โหมด Daemon: เลื่อนการโต้ตอบให้แจ้งเมื่อเริ่มต้น


16

(โปรดทราบว่าชื่อตรงข้ามคำถามนี้ไม่เหมือนกับวิธีเริ่มต้นในโหมด daemon และระงับการโต้ตอบโต้ตอบ?เนื่องจากคำถามดังกล่าวถูก "ตอบ" โดยผู้ส่งกำจัดสิ่งที่ทำให้พรอมต์ปรากฏขึ้นโดยเฉพาะ)

ฉันต้องการทราบว่ามีวิธีทั่วไปในการหลีกเลี่ยงการemacs --daemonแขวนตลอดไปรอคำตอบสำหรับการแจ้งให้แสดงใน minibuffer ที่ยังไม่มี

เป็นไปไม่ได้ที่จะเชื่อมต่อกับ emacsclient เพื่อตอบคำถามเหล่านี้เนื่องจากเซิร์ฟเวอร์ไม่เริ่มทำงานจนกว่า Emacs จะเสร็จสิ้นการเริ่มต้นตามลำดับ (ซึ่งหมายความว่าหากคุณตั้งค่า ALTERNATE_EDITOR เป็นสตริงว่างซึ่งทำให้emacsclientไม่สามารถหาเซิร์ฟเวอร์เริ่มต้นภูตใหม่คุณสามารถจบลงด้วย Emacs daemons หลายตัวที่ติดอยู่และรอ) ฉันต้องkillall emacsแก้ไขปัญหา ก่อนดำเนินการต่อ

ฉันสามารถเล่น whack-a-mole กับแต่ละสิ่งที่ก่อให้เกิดพรอมต์เมื่อเริ่มต้นเมื่อฉันระบุมัน (โดยการเริ่มต้น Emacs ในโหมดที่ไม่ใช่ daemon และเห็นสิ่งที่มันขอ) แต่มันไม่ใช่วิธีแก้ปัญหาเพราะมันไม่สามารถหยุด daemon ถัดไปได้ จากการหยุดทำงานเมื่อเริ่มต้นด้วยเหตุผลใหม่

เพื่อยกตัวอย่าง: เหตุผลทั่วไปที่มันจะหยุดทำงานหลังจากรีบูตระบบหรือ Emacs หยุดทำงานเมื่อ Emacs ที่ทำการบู๊ตครั้งแรกต้องการทราบว่าการขโมยล็อคไฟล์จาก Emacs ที่หมดอายุแล้วนั้นเป็นอย่างไร ฉันสามารถแก้ไขได้โดยสร้างคำแนะนำเพื่อให้พร้อมท์นั้นตอบ "ใช่" โดยไม่มีการโต้ตอบ แต่แล้วหนึ่งในไฟล์ที่ถูกเปิดในการบันทึกเซสชั่นก่อนหน้าคือไฟล์ TRAMP ที่ต้องใช้รหัสผ่าน sudo หรือ SSH ดังนั้น daemon จึงติดรอพรอมต์รหัสผ่าน ดังนั้นฉันจึงแก้ไขด้วยการแก้ไขไฟล์เซสชั่น (ด้วยviหรือemacs -q!) เพื่อลบไฟล์ที่ละเมิด - แต่นั่นไม่ได้เกิดขึ้นในครั้งต่อไป

ดังนั้นฉันสามารถหยุดการโหลดเซสชันของฉันโดยอัตโนมัติเมื่อเริ่มต้นและเปลี่ยนเป็นคำสั่งฉันต้องดำเนินการด้วยตนเองจาก emacsclient แรกของฉัน แต่ถ้ามันไม่ได้โหลดเซสชั่นของฉันในพื้นหลังดังนั้นจึงพร้อมตามเวลาที่ฉันพร้อมที่จะใช้ daemon ทั้งหมดจะหายไป!

ดังนั้นสิ่งที่ฉันต้องการคือ:

  • (ดีที่สุด) บางวิธีในการเลื่อนมินิบัสเกอร์แจ้งเตือนจนกว่าฉันจะเปิด emacsclient ในขณะที่ยังคงการเตรียมใช้งานส่วนที่เหลือให้เสร็จสิ้น
  • (ตกลง) บางวิธีที่จะทำให้ minibuffer ทั้งหมดแจ้งให้ฉันไม่ได้แนะนำอย่างอื่นตามที่อธิบายไว้ข้างต้นเพียงแค่ส่งคืนnoเว้นแต่ว่า emacsclient กำลังทำงานอยู่ ฉันสามารถใช้ชีวิตกับบัฟเฟอร์ TRAMP ของฉันเพื่อแก้ไขข้อผิดพลาดตราบใดที่ใช้งานได้เป็นส่วนใหญ่

มีวิธีใดในการบรรลุเป้าหมายทั้งสองนี้หรือไม่?


มีวิธีทำซ้ำปัญหาประเภทนี้โดยทางโปรแกรมหรือไม่เพื่อให้ชุมชนสามารถแก้ไขปัญหาได้
Melioratus

1
อย่างที่ฉันเขียนในบรรทัดแรกมันค่อนข้างง่ายที่จะแก้ไขตัวอย่างที่ให้ ... "หมอมันเจ็บเมื่อฉันทำอย่างนี้ ... " "จากนั้นอย่าทำอย่างนั้น" ปัญหาเป็นกรณีทั่วไป แต่วิธีง่ายๆในการสร้างปัญหาคือให้เริ่มต้นการกู้คืนเดสก์ท็อปผ่าน(read-desktop)จากนั้นก่อนที่จะเรียกใช้emacs --daemonให้สร้างไฟล์ล็อคปลอมโดยใส่จำนวนเต็มลงใน. emacs.desktop.lock (ที่จะวางไฟล์นั้น แต่อาจเป็น homedir ของคุณหรือ~ / .emacs.d / .
Trey

1
นี่เป็นกรณีที่มีการกล่าวถึงบ่อยครั้งที่นี่ตัวอย่างเช่น: emacs.stackexchange.com/questions/8147/ …หรือemacs.stackexchange.com/questions/31621/ ......อาจมีบริบท
แต้ม

ข้อผิดพลาดนี้ดูเหมือนว่าเกี่ยวข้อง: Bug # 13697 - วิธีที่จะบอกว่า Emacs สามารถโต้ตอบกับผู้ใช้ได้หรือไม่ แต่ไม่มีใครทำงานได้เลยตราบใดที่ฉันรู้
npostavs

@npostavs ขอบคุณสำหรับลิงค์ - ฉันได้อธิบายข้อผิดพลาดถึงแม้ว่ามันจะเป็นจุดเริ่มต้นที่ผิดพลาดที่ฉันได้แสดงความคิดเห็นที่นี่ (ตั้งแต่ถูกลบ) ก่อนที่ฉันจะหามัน!
แต้ม

คำตอบ:


2

การสนทนาของเราได้เคลียร์ว่าคุณไม่มีเซิร์ฟเวอร์ X ใด ๆ ที่ใช้วิธีนี้ทำให้โซลูชันแรกของฉันไม่มีประโยชน์สำหรับคุณ

ในต่อไปนี้ฉันนำเสนอโซลูชั่นที่สองที่ทำงานกับกรอบข้อความ

เมื่อการกำหนดค่าเริ่มต้นของคุณต้องการอินพุตของผู้ใช้ผ่านฟังก์ชันที่แนะนำโดยavoid-initial-terminalEmacs จะรอจนกว่าคุณจะเปิดเฟรมเทอร์มินัลข้อความ พรอมต์ปรากฏขึ้นใน minibuffer ของเฟรมนั้นและคุณสามารถให้การตอบสนองแบบโต้ตอบของคุณ

ข่าวสารที่เกี่ยวข้องกับรหัสจะได้รับเป็นความคิดเห็นในรหัส มีTODOเครื่องหมายพร้อมคำอธิบายที่แสดงตำแหน่งที่จะแทรกการกำหนดค่าของคุณเอง ขณะนี้มีแบบฟอร์มการทดสอบที่มีการตรวจสอบรหัส

;; TODO: Do here configure the server if needed.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Startup the server:
;; Analysis of read_from_minibuffer in src/minibuf.c and daemon_type in src/emacs.c
;; shows that daemon-initialized must have run before read-passwd / read-string
;; works on frames. Before it only works on stdin & stdout.
(server-start) ;;< early start
(let ((after-init-time before-init-time))
  (daemon-initialized)) ;; Finalize the daemon, 

(advice-add 'daemon-initialized :override #'ignore)
;;< Ignore `daemon-initialized' after initialization. It may only run once!
;; Now the background emacs is no longer marked as daemon. It just runs the server.

(defun prevent-server-start (&rest _ignore)
  "Prevent starting a server one time after `server-start' has been advised with this."
  (advice-remove 'server-start #'prevent-server-start))

(advice-add 'server-start :override #'prevent-server-start)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Prepare waiting for a real terminal frame when user input is required:

(defun avoid-initial-terminal (fun &rest args)
  "Wait until we are no longer on \"intial-terminal\".
Afterwards run `fun' with frame on the other terminal selected."
  (message "Avoiding initial terminal. Terminal: %S" (get-device-terminal nil))
  (while (string-equal
      (terminal-name (get-device-terminal nil))
      "initial_terminal")
    (sleep-for 1))
  ;; (message "Selected frame: %S; Running %S with %S." (selected-frame) fun args)
  (apply fun args))

(advice-add 'read-string :around #'avoid-initial-terminal)

(advice-add 'read-passwd :around #'avoid-initial-terminal)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; TODO: Your initialization that is not daemon related
;; and may require user input:

;; Currently this is just a test.
(read-passwd "Passwd: ")

(read-string "String: ")

(y-or-n-p "y-or-n query")

ทดสอบ: Emacs-version: 26.1

ขั้นแรก) เรียกใช้emacs --daemonบนคอนโซล

2nd) เรียกใช้emacsclient --ttyบนคอนโซลอื่น คุณจะถูกขอให้ใส่รหัสผ่านและสตริง หลังจากนั้นคุณจะต้องตอบคำถาม y-or-np ด้วย


3

ไม่ใช่สิ่งที่คุณขออย่างแน่นอน แต่อาจเป็นทางออกสำหรับปัญหาดั้งเดิมของคุณ:

ฉันต้องการทราบว่ามีวิธีทั่วไปในการป้องกันไม่ให้ emacs - ภูตผีปีศาจลอยไปตลอดกาลโดยรอคำตอบจากข้อความแจ้งที่แสดงในรถมินิบัสที่ยังไม่มี

หาก daemon ให้เฟรมกราฟิกสำหรับตอบคำถามที่เกิดขึ้นในเฟสเริ่มต้นคุณจะไม่ติดขัดอีกต่อไป

รหัสด้านล่างกำหนดคำแนะนำทั่วไปmy-with-initial-frameที่เปิดเฟรมบนจอแสดงผลแรกที่มี (เช่น, :0.0)

คำแนะนำนั้นสามารถเพิ่มได้อย่างง่ายดายในการสอบถามคำสั่งเช่นy-or-n-pหรือread-passwdตามที่แสดงด้านล่าง

เพียงเปิดเฟรมให้คุณมีความเป็นไปได้ค่อนข้างที่จะตอบคำถามในส่วนติดต่อผู้ใช้ หนึ่งสามารถใช้กล่องโต้ตอบสำหรับy-or-n-pแต่ที่จะต้องมีโซลูชั่นพิเศษสำหรับคำสั่งสอบถามเฉพาะ ฉันต้องการหลีกเลี่ยงสิ่งนั้น

หากคุณลองใช้รหัสนั้นในไฟล์ init ของคุณตรวจสอบให้แน่ใจว่าเป็นสิ่งแรกที่มี

(when (daemonp)

  (defun my-with-initial-frame (&rest _)
    "Ensure a frame on display :0.0 and ignore args."
    (let* ((display-list (x-display-list))
           (display-re (and display-list (regexp-opt display-list)))
           (term (and display-re (cl-some (lambda (term) (and (string-match display-re (terminal-name term)) term)) (terminal-list))))
           (frame (and term (cl-some (lambda (frame) (and (frame-live-p frame) frame)) (frames-on-display-list term)))))
      (select-frame (or frame (make-frame-on-display (getenv "DISPLAY"))))))

  (message "Advising querying functions with `my-with-initial-frame'.")
  (advice-add 'y-or-n-p :before #'my-with-initial-frame)
  (advice-add 'read-passwd :before #'my-with-initial-frame))

ทดสอบ:

สมมติฐาน:

มี xserver ที่ทำงานซึ่งโปรแกรมสามารถเชื่อมต่อผ่านDISPLAYตัวแปรสภาพแวดล้อม

อินพุตที่ xterm:

emacs --daemon

emacsclient --eval '(y-or-n-p "A")'

มีกรอบกับเปิดพร้อมท์แบบสอบถามy-or-n-p A (y or n)ตอบคำถามนั้นแล้วลองอีกครั้ง:

emacsclient --eval '(y-or-n-p "B")'

แบบสอบถามใหม่ที่มีพรอมต์B (y or n)ในเฟรมเดียวกัน ปิดเฟรมนั้นเช่นด้วยC-x 5 0แล้วลองอีกครั้ง:

emacsclient --eval '(y-or-n-p "C")'

C (y or n)กรอบใหม่เปิดขึ้นพร้อมกับพรอมต์แบบสอบถาม

การทำงานเช่นเดียวกันสำหรับการป้อนรหัสผ่าน


@Trey ฉันมีปัญหากับรหัสของฉัน (จริง ๆ แล้วมีการทดสอบ) การเริ่มต้นเซิร์ฟเวอร์ x ไม่ทำงานในครั้งแรก ฉันไม่ได้สังเกตเห็นตั้งแต่แรกเพราะฉันไม่ได้เริ่มดีมอนใหม่ ฉันแก้ไขให้ถูกต้องแล้ว กรุณาทดสอบอีกครั้ง ขอบคุณ
โทเบียส

Linux เสมือนของฉันไม่มีเทอร์มินัลกราฟิกประกอบอยู่ดังนั้นฉันจึงไม่สามารถทำงานxtermได้ นอกจากนี้ฉันไม่คิดว่าโซลูชันนี้สามารถทำงานได้สำหรับผู้ที่ทำถ้าคุณตั้งค่า daemon ให้ทำงานเมื่อเริ่มต้นระบบจะพยายามเปิดเฟรมที่ด้านบนของหน้าจอเข้าสู่ระบบซึ่งไม่ได้รับอนุญาต เกิดความผิดพลาด
แต้ม

โทเบียสขอโทษถ้าเสียงดังเอร็ดอร่อย - ฉันตอบโทรศัพท์ของฉันและอาจทำให้ตัวเองสั้นดังนั้นให้ฉันลองทำอย่างละเอียด: ข้อได้เปรียบเดียวที่ฉันสามารถเห็นสิ่งที่คุณอธิบายไม่ใช้ภูตและทำงานserver-startเมื่อสิ้นสุดการเริ่มต้น หากคุณมีการเริ่มต้นระบบแบบสะอาดคุณจะไม่ต้องรอ แต่ ... คุณจะต้องรอเพราะถ้าฉันเข้าใจผิดคุณไม่สามารถเริ่มงาน Emacs daemon ในสคริปต์การเข้าสู่ระบบของคุณได้เนื่องจาก GUI จะไม่สามารถใช้งานได้ (และในกรณีเช่นของฉันมันจะไม่เป็นเช่นนี้ในภายหลัง)
Trey

@Trey คุณสามารถเข้าร่วมการแชทได้หรือไม่?
โทเบียส

1

ฉันคิดว่าการเพิกถอนพรอมต์จะเป็นเรื่องยากโดยทั่วไป แต่มันก็ค่อนข้างง่ายที่จะเปลี่ยน Emacs เพื่อให้พรอมต์ดังกล่าวส่งสัญญาณข้อผิดพลาดทันที

ไม่เพียงแค่นั้น แต่ถ้าคุณไม่สามารถตอบคำถามเหล่านั้นโดยไม่ต้องใช้ยิมนาสติกจำนวนมากฉันคิดว่ามันมีคุณสมบัติเป็นข้อบกพร่องดังนั้นฉันขอแนะนำให้คุณส่งรายงานข้อผิดพลาดสำหรับเรื่องนั้น


ฉันคิดว่าฉันต้องการรายละเอียดเพิ่มเติมเล็กน้อย พิจารณาล็อคไฟล์บันทึกเดสก์ท็อปที่ฉันพูดถึงในความคิดเห็นเพื่อความโปรดปรานด้านบน หนึ่งจะไปเกี่ยวกับการเปลี่ยนWarning: desktop file appears to be in use by PID xxx. Using it may cause conflicts. Use it anyway? (y or n)พรอมต์เป็นข้อผิดพลาดโดยไม่ต้องอ้างถึง "เดสก์ท็อป" ในบางวิธี (เพราะวิธีการที่เป็น nongeneral โกหก whack-a-โมล)?
แต้ม

สเตฟานยังมีปัญหาที่ไม่รบกวนฉันเพราะฉันไม่ได้ใช้ Emacs daemon ด้วยวิธีนี้ แต่เพื่อให้ได้คำตอบที่เป็นประโยชน์โดยทั่วไปอาจต้องมีการจัดการกับ: การแก้ไขข้อผิดพลาดร้ายแรงจะทำให้ Emacs เริ่มทำงานผ่าน systemd หรือสุนัขเฝ้าบ้านอื่น ๆ ในวง แต่การเพิกเฉยข้อผิดพลาดและการเข้าสู่ระบบ*Messages*อาจเป็นเรื่องที่ไม่เพียงพอในการเชื่อมต่อไคลเอนต์แรกว่ามีบางสิ่งที่อาจผิดพลาดอย่างรุนแรงและต้องการความสนใจทันทีก่อนที่ผู้ใช้จะพยายามดำเนินการใด ๆ
แต้ม

(หากต้องการอธิบายให้ชัดเจนสำหรับผู้ที่ไม่ได้ใช้ daemon - หากคุณเริ่มต้นด้วยตนเองไม่ว่าจะผ่านemacs --daemonหรือโดยเริ่มต้นemacsclientด้วยALTERNATE_EDITORตัวแปรสภาพแวดล้อมที่ตั้งค่าเป็นสตริงว่างคุณจะเห็นผลลัพธ์ที่ตามปกติจะ*Messages*สะท้อนไปที่เทอร์มินัลจนกระทั่งดีมอน เสร็จสิ้นการกำหนดค่าเริ่มต้นและ Emacs พร้อมแล้ว แต่หลายคนมี Emacs เริ่ม daemon เมื่อระบบเริ่มต้นหรือเวลาเข้าสู่ระบบและผลลัพธ์จะถูกบันทึกหรือโยนออกไป
Trey

1
@Trey: การส่งสัญญาณข้อผิดพลาดไม่ควรอยู่desktopแต่อยู่ในy-or-n-pฟังก์ชั่น (หรือต่ำกว่า) เรามีกลไกบางอย่างในการชะลอการแสดงข้อผิดพลาดที่เกิดขึ้นในระหว่างการเริ่มต้นดังนั้นเราสามารถใช้เพื่อแสดงข้อผิดพลาดเมื่อ emacsclient ตัวแรกเชื่อมต่อกับ daemon
สเตฟาน

ในกรณีใดกรณีหนึ่งผู้ใช้ส่วนใหญ่ไม่อ่าน*Messages*- และในขณะที่*Warnings*ระบบที่ใช้น้อยจะปรากฏขึ้นที่หน้าต่างเพื่อบัฟเฟอร์หากมีเฟรมที่ใช้งานอยู่เมื่อมีการสร้างคำเตือนในกรณีนี้ไม่มีเฟรมและไม่มี ไม่ปรากฏขึ้นเพื่อเลื่อนป๊อปอัปจนกว่า emacsclient ตัวแรกจะติดตามปัญหาของคำเตือน หากสามารถทำได้ข้อเสนอแนะของคุณในการyes-or-no-pแจ้งเตือนลูกค้าล่วงหน้าจะค่อนข้างสมบูรณ์แบบ (ฉันสงสัยผู้ใช้หวี*Messages*ในการเริ่มต้น!)
แต้ม
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.