デーモンモード:起動時に対話型プロンプトを延期しますか?


16

(逆に、この質問はデーモンモードで起動して対話型ダイアログを非表示にする方法と同じではないことに注意してくださいその質問は特定のプロンプトを表示させる原因を排除して提出者によって「回答」されたためです。)

まだ存在しないミニバッファに表示されるプロンプトへの回答を待って永遠にハングアップしないようにする一般的な方法があるかどうかを知りたいemacs --daemonです。

Emacsが起動シーケンスを完了するまでサーバーは起動しないため、emacsclientに接続してこれらのプロンプトに答えることはできません。(つまり、ALTERNATE_EDITORが空の文字列に設定されている場合emacsclient、サーバーが新しいデーモンを開始できないことがわかると、複数のEmacsデーモンがすべてスタックして待機killall emacsすることになります。)問題を解決する必要があります続行する前に。

Emacsを非デーモンモードで起動し、何を要求しているかを確認することで、起動時にプロンプ​​トを表示する各問題でwhack-a-moleを再生できますが、次のデーモンを停止できないため、解決策ではありません新しい理由でスタートアップにハングアップすることから。

例を挙げると、最初の再起動後のEmacsが古いEmacsからロックファイルを盗むことができるかどうかを知りたいときに、システムが再起動またはEmacsがクラッシュした後にハングする一般的な理由です。そのプロンプトが対話なしで常に「はい」と答えるようにアドバイスを作成することで、それを修正できました。ただし、前回のセッションの保存時に開いていたファイルの1つは、sudoまたはSSHパスワードを必要とするTRAMPファイルであったため、デーモンはパスワードプロンプトで待機しています。そこで、問題のあるファイルを削除するためにセッションファイルを(viまたはemacs -q!を使用して)手動で編集することで修正します。

そのため、起動時にセッションのロードを自動的に停止し、最初のemacsclientから手動で実行する必要があるコマンドに変更できます。しかし、バックグラウンドでセッションをロードしておらず、使用する準備が整うまでに準備が整っている場合、デーモンの目的はすべて失われます!

だから私が欲しいのは:

  • (最良)残りの初期化を完了しながら、emacsclientを開くまでミニバッファープロンプトを延期する方法。
  • (OK)noemacsclientが実行されていない限り、上記で説明したように、私がまだアドバイスしていないすべてのミニバッファープロンプトを返す方法があります。TRAMPバッファーがほとんど機能する限り、私はTRAMPバッファーでエラーが発生しても生き続けることができます。

これらの目標のいずれかを達成する方法はありますか?


コミュニティがトラブルシューティングできるように、これらのタイプの問題をプログラムで再現する方法はありますか?
Melioratus

1
さて、最初の行で書いたように、与えられた例を修正するのは非常に簡単です... 問題は一般的なケースです。しかし、問題を作成する簡単な方法は、スタートアップを介してデスクトップを復元し(read-desktop)、実行emacs --daemonする前に、.emacs.desktop.lockに整数を入れて偽のロックファイルを作成することです(残念ながら、そのファイルを置く場所は設定によって異なります、おそらくどちらかあなたのホームディレクトリまたは〜/ .emacs.d /
トレイ・

1
これは、ここで頻繁に言及されているケースです。たとえばemacs.stackexchange.com / questions / 8147 / またはemacs.stackexchange.com/questions/31621/…はコンテキストを提供します。
トレイ

このバグは関連しているようです:Bug#13697-Emacsがユーザー対話できるかどうかを確認する方法ですが、私が知る限り、誰もそれに取り組んできません。
npostavs

@npostavsリンクをお寄せいただきありがとうございます—バグに注釈を付けましたが、それを理解する前に、ここでコメントした(削除されたため)誤った開始を要しました!
トレイ

回答:


2

私たちの議論でこれを実行しているXサーバーがないため、私の最初のソリューションは役に立たないことが明らかになりました。

以下では、テキストターミナルフレームで機能する2番目のソリューションを紹介します。

初期化にユーザー入力が必要な場合、avoid-initial-terminalEmacsで推奨される機能は、テキストターミナルフレームを開くまで待機します。そのフレームのミニバッファにプロンプ​​トが表示され、インタラクティブな応答を行うことができます。

コード関連の情報は、コード内のコメントとして提供されます。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バージョン:26.1

1)emacs --daemonコンソールで実行します。

2)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))

テスト:

仮定:

DISPLAY環境変数を介してプログラムが接続できる実行中のxserverがあります。

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-serverの起動は最初は機能しませんでした。デーモンを再起動しなかったため、最初は気付きませんでした。今すぐ修正しました。もう一度テストしてください。ありがとう。
トバイアス

私のLinux仮想にはグラフィカル端末が接続されていないため、実行できませんxterm。また、このソリューションはそうする人にはうまくいかないと思います。起動時にデーモンを実行するように設定している場合、ログイン画面の上部にフレームを開こうとしますが、許可されていません。クラッシュします。
トレイ

トビアス、謝罪もし私は自分の携帯電話に答えたと私は短いので、私は詳しく説明してみましょうカットがありbrusque-打診上:だけ私はあなたがオーバーデーモンを使用して実行していない記述するものの見ることができるという利点server-startスタートアップの終わりを代わりに、クリーンなスタートアップが発生した場合、待つ必要はありません。しかし、...誤解しない限り、GUIが使用できないため、Emacsデーモンを起動するタスクをシステムログインスクリプトに入れることができないため、待つ必要があります。(そして、私のようなケースでは、それは決して後になることはありません。)
トレイ

@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)プロンプトをエラーに変更するにはどうすればよいですか(その方法は、一般的ではないため、モグラたたきになります)。
トレイ

ステファンは、また、気にしない問題があります私を出しerroring:私はこのようデーモンEmacsを実行していないので、しかし、一般的に有用な答えが対処する必要があるかもしれません作るために致命的なことは、Emacsが再起動するようにsystemdまたは他の番犬を経由して起動が発生しますループで。しかし、エラーを無視してログインするだけで*Messages*は、おそらく最初のクライアント接続でのヘッズアップが不十分であるため、ユーザーがステートフルな操作を試みる前に、何かがひどくひどく、すぐに注意が必要です。
トレイ

(デーモンを使用しない人のために明確にするために、空の文字列に設定された環境変数を介して、emacs --daemonまたは手動で起動するemacsclientと、デーモンまでALTERNATE_EDITOR通常*Messages*端末にエコーされる出力が表示されます初期化が完了し、Emacsの準備が完了しますが、多くの場合、システムの起動時またはログイン時にEmacsがデーモンを起動し、出力がログに記録されるか
Trey

1
@Trey:エラーシグナルは関数desktop内にあるのではなく、y-or-n-p関数内にあるべきです(またはそれ以下)。起動中に発生したエラーの表示を遅らせるメカニズムがあるため、最初のemacsclientがデーモンに接続するときにエラーを表示できます。
ステファン

いずれの場合も、しかし、ほとんどのユーザーがいない熟読ん*Messages*少し使われながら-と*Warnings*、システムがバッファにウィンドウをポップアップしない警告が発生したときにアクティブフレームは、存在する場合、この場合には、何のフレームが存在しない、それはdoesnの警告の問題に続く最初のemacsclientまでポップアップを延期するのは簡単ではないようです。それが可能であれば、yes-or-no-p代わりにクライアント前に警告を出すというあなたの提案は非常に理想的です。(ユーザー*Messages*が起動時にくしを疑う!)
Trey
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.