Modalità demone: rinviare i prompt interattivi all'avvio?


16

(Si noti che, al contrario, questa domanda non è la stessa di Come iniziare in modalità demone e sopprimere le finestre di dialogo interattive?, Poiché quella domanda è stata "risposta" dal mittente eliminando ciò che stava causando la visualizzazione di un particolare prompt.)

Vorrei sapere se esiste un modo generale per evitare di rimanere emacs --daemonin sospeso per sempre in attesa di una risposta a un prompt visualizzato in un minibuffer che non esiste ancora.

È impossibile connettersi con un emacsclient per rispondere a questi prompt, perché il server non si avvia fino a quando Emacs non completa la sequenza di avvio. (Ciò significa che se ALTERNATE_EDITOR è impostato su una stringa vuota, il che fa sì emacsclientche un server che non trova un server possa avviare un nuovo demone, puoi finire con più demoni Emacs tutti bloccati e in attesa.) Devo killall emacse risolvere il problema prima di continuare.

Posso giocare a squarciagola con ogni cosa che provoca un prompt all'avvio quando lo identifico (avviando Emacs in modalità non demone e vedendo quello che sta chiedendo), ma non è una soluzione perché non può fermare il demone successivo dall'attesa all'avvio per un nuovo motivo.

Per fare un esempio: un motivo comune per cui si bloccava era dopo un riavvio del sistema o un arresto anomalo di Emacs, quando il primo post-riavvio di Emacs voleva sapere se andava bene rubare i file di blocco dai defunti Emacs. Potrei risolverlo creando consigli per fare in modo che quel prompt risponda sempre "sì" senza interazione. Ma poi, uno dei file che era stato aperto durante il salvataggio della sessione precedente era un file TRAMP che richiedeva una password sudo o SSH, quindi il demone era bloccato in attesa di una richiesta di password. Quindi lo risolvo modificando manualmente il file di sessione (con vio emacs -q!) Per rimuovere i file offensivi, ma ciò non impedisce che accada la prossima volta.

Quindi, posso interrompere il caricamento automatico della mia sessione all'avvio e cambiarlo in un comando che devo eseguire manualmente dal mio primo emacsclient. Ma se non sta caricando la mia sessione in background, quindi è pronta quando sono pronto per usarla, l'intero scopo del demone è perso!

Quindi quello che mi piacerebbe è:

  • (Migliore) In qualche modo per rinviare le istruzioni del minibuffer fino a quando apro un emacsclient, pur continuando il resto dell'inizializzazione.
  • (OK) In qualche modo per eseguire tutte le istruzioni relative al minibuffer non ho già avvisato altrimenti, come descritto sopra, è sufficiente tornare a nomeno che non sia in esecuzione un emacsclient. Posso vivere con i miei buffer TRAMP in errore fino a quando funziona principalmente.

C'è un modo per raggiungere uno di questi obiettivi?


Esiste un modo per riprodurre questo tipo di problemi a livello di codice in modo che la community possa risolvere i problemi?
Melioratus,

1
Bene, come ho scritto nella prima riga, è abbastanza facile risolvere un dato esempio ... "Dottore, fa male quando faccio questo ..." "Allora non farlo." Il problema è il caso generale. Ma un modo semplice per creare il problema è quello di avere l'avvio ripristinare desktop tramite (read-desktop), quindi, prima di eseguire emacs --daemon, creare un file di blocco fasullo mettendo un intero in .emacs.desktop.lock (dove mettere quel file, purtroppo, dipende dalla configurazione , ma probabilmente o il tuo homedir o ~ / .emacs.d / .
Trey

1
Questo è un caso frequentemente menzionato qui, ad esempio: emacs.stackexchange.com/questions/8147/… o emacs.stackexchange.com/questions/31621/… può fornire contesto.
Trey,

Questo bug sembra correlato: Bug # 13697 - Un modo per dire se Emacs può interagire con l'utente , ma nessuno ci ha lavorato, per quanto ne so.
npostavs

@npostavs Grazie per il link: ho annotato il bug, anche se ha preso una falsa partenza che ho commentato qui (dal momento che è stato eliminato) prima di averlo capito!
Trey,

Risposte:


2

La nostra discussione ha chiarito che non hai alcun X-server in esecuzione questo rende la mia prima soluzione inutile per te.

Di seguito presento una seconda soluzione che funziona con i frame dei terminali di testo.

Quando l'inizializzazione richiede l'immissione da parte dell'utente tramite una, le funzioni consigliate con avoid-initial-terminalEmacs attendono di aprire una cornice di terminale di testo. Il prompt appare nel minibuffer di quel frame e puoi dare la tua risposta interattiva.

Le informazioni relative al codice sono fornite come commenti nel codice. Ci sono TODOmarcatori con descrizioni che mostrano dove inserire la propria configurazione. Attualmente ci sono moduli di test che convalidano il codice.

;; 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")

Test: versione Emacs: 26.1

1) Esegui emacs --daemonsu una console.

2 °) Esegui emacsclient --ttysu un'altra console. Vi viene richiesta una password e una stringa. Successivamente viene richiesto anche di rispondere a una query y-o-np.


3

Non esattamente quello che stai chiedendo, ma forse una soluzione al tuo problema originale:

Mi piacerebbe sapere se esiste un modo generale per impedire a emacs --daemon di rimanere sospeso per sempre in attesa di una risposta a un prompt visualizzato in un minibuffer che non esiste ancora.

Se il demone ti dà una cornice grafica per rispondere alle domande che sorgono nella sua fase di avvio, non rimani più bloccato.

Il codice seguente definisce un consiglio generale my-with-initial-frameche apre una cornice sul primo display disponibile (ad es :0.0.).

Tale consiglio può essere aggiunto facilmente ai comandi di query come y-or-n-po read-passwd, come è dimostrato di seguito.

L'apertura di un frame ti dà una possibilità piuttosto grossolana di rispondere alle domande sull'interfaccia utente. Si potrebbe anche usare una finestra di dialogo per y-or-n-pma ciò richiederebbe soluzioni speciali per comandi di query specifici. Volevo evitarlo.

Se provi quel codice nel tuo file init assicurati che sia la prima cosa lì.

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

Test:

ipotesi:

Avere un xserver in esecuzione a cui i programmi possono connettersi tramite la DISPLAYvariabile di ambiente.

Ingresso a xterm:

emacs --daemon

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

Si apre un frame con il y-or-n-pprompt delle query A (y or n). Rispondi a questa domanda e riprova:

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

Nuova query con prompt B (y or n)nello stesso frame. Chiudi quel frame, ad esempio, con C-x 5 0e riprova:

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

Viene aperto un nuovo frame con il prompt delle query C (y or n).

Lo stesso vale per l'immissione della password.


@Trey Ho avuto qualche problema con il mio codice (in realtà con il test). L'avvio di x-server non ha funzionato la prima volta. Inizialmente non ho notato che non ho riavviato il demone. L'ho corretto ora. Per favore prova di nuovo. Grazie.
Tobias,

Il mio Linux virtuale non ha un terminale grafico collegato, quindi non posso eseguire xterm. Inoltre, non credo che questa soluzione possa funzionare per coloro che lo fanno: se hai il demone impostato per essere eseguito all'avvio, proverebbe ad aprire un frame nella parte superiore della schermata di accesso, che non è permesso, quindi si blocca.
Trey,

Tobias, mi scuso se il suono di cui sopra sembrava brusco— Ho risposto sul mio telefono e potrei essermi interrotto, quindi lasciami provare a elaborare: l'unico vantaggio che posso vedere di ciò che descrivi sul non usare il demone e correre server-startalla fine dell'avvio invece è che, se ti capita di avere un avvio pulito, non dovrai aspettare. Ma ... dovrai aspettare, perché a meno che non fraintenda, non puoi mettere il compito di avviare il demone Emacs nello script di accesso del tuo sistema poiché una GUI non sarà disponibile allora. (E in un caso come il mio, non lo sarà mai più tardi.)
Trey,

@Trey Potresti unirti a una chat ?
Tobias,

1

Penso che rinviare i prompt sarà difficile in generale, ma dovrebbe essere abbastanza facile cambiare Emacs in modo che tali prompt segnalino immediatamente un errore.

Non solo, ma se non puoi rispondere a questi suggerimenti senza molta ginnastica, penso che si qualifichi come un bug, quindi ti consiglio di inviare una segnalazione di bug per questo.


Penso di aver bisogno di un po 'più di dettagli. Considera il blocco del file di salvataggio sul desktop che menziono nel commento alla taglia sopra. Come si potrebbe cambiare il Warning: desktop file appears to be in use by PID xxx. Using it may cause conflicts. Use it anyway? (y or n)prompt in un errore, senza fare riferimento in particolare al "desktop" in qualche modo (perché in questo modo, essendo non generale, si trova una talpa)?
Trey,

Stefan, anche c'è un problema che non si preoccupa di me perché io non correre l'Emacs demone in questo modo, ma per fare una risposta generalmente utile potrebbe essere necessario affrontare: erroring fuori fatalmente causerà Emacs avviato tramite systemd o di altri cani da guardia per riavviare in un ciclo. Ma ignorare gli errori e accedere semplicemente *Messages*è probabilmente insufficiente all'heads-up al primo collegamento client che qualcosa potrebbe essere seriamente sbagliato e richiedere attenzione immediata prima che l'utente tenti qualsiasi operazione con stato.
Trey,

(Per chiarire per coloro che non usano il demone, se lo avvii manualmente, tramite emacs --daemono iniziando emacsclientcon la ALTERNATE_EDITORvariabile d'ambiente impostata su una stringa vuota, vedrai l'output che normalmente va *Messages*echo nel terminale fino a quando il demone completa l'inizializzazione ed Emacs è pronto, ma molti hanno Emacs avviare il demone all'avvio del sistema o al momento dell'accesso e l'output viene registrato o eliminato.
Trey

1
@Trey: la segnalazione di errori non dovrebbe essere presente desktopma nella y-or-n-pfunzione (o ancora inferiore). Abbiamo qualche meccanismo per ritardare la visualizzazione degli errori che si sono verificati durante l'avvio, quindi potremmo usarli per visualizzarli quando il primo emacsclient si collega al demone.
Stefan,

In entrambi i casi, tuttavia, la maggior parte degli utenti non esamina, *Messages*e mentre il *Warnings*sistema poco utilizzato fa apparire una finestra nel buffer se esiste un frame attivo quando viene generato l'avviso, in questo caso non esiste alcun frame e non sembra facile rimandare il pop-up fino al primo emacsclient a seguito del problema di avviso. Se ciò potesse essere fatto, il tuo suggerimento di fare un yes-or-no-pavviso pre-cliente sarebbe invece l'ideale. (Dubito che gli utenti si pettinino *Messages*all'avvio!)
Trey
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.