Modo Daemon: ¿aplazar las indicaciones interactivas al inicio?

16

(Tenga en cuenta que, a título de lo contrario, esta cuestión no es la misma que Cómo iniciar en modo demonio y suprimir diálogos interactivos? , Ya que esa pregunta fue "respondió" por el presentador de la eliminación de lo que estaba causando una determinada pronta a aparecer.)

Me gustaría saber si hay una forma general de evitar que se emacs --daemoncuelgue para siempre esperando una respuesta a un mensaje que se muestra en un minibúfer que aún no existe.

Es imposible conectarse con un cliente de emacs para responder estas preguntas, porque el servidor no se inicia hasta que Emacs finaliza la secuencia de inicio. (Esto significa que si tiene ALTERNATE_EDITOR configurado en la cadena vacía, lo que hace emacsclientque un servidor que no puede encontrar inicie un nuevo demonio, puede terminar con varios demonios de Emacs todos atascados y esperando). Tengo que killall emacssolucionar el problema. Antes de continuar.

Puedo jugar whack-a-mole con cada cosa que causa un mensaje de inicio cuando lo identifico (iniciando Emacs en modo no demonio y viendo lo que está pidiendo), pero no es una solución porque no puede detener el próximo demonio de colgar en el inicio por una nueva razón.

Para dar un ejemplo: una razón común por la que se colgaría fue después de un reinicio del sistema o un bloqueo de Emacs, cuando el primer Emacs posterior al reinicio quería saber si estaba bien robar archivos de bloqueo de los Emacs desaparecidos. Podría arreglar eso creando consejos para hacer que ese mensaje siempre responda "sí" sin interacción. Pero luego, uno de los archivos que estaba abierto en la sesión anterior fue un archivo TRAMP que requería una contraseña sudo o SSH, por lo que el demonio está atascado esperando una solicitud de contraseña. Así que lo arreglo editando manualmente el archivo de sesión (con vio emacs -q!) Para eliminar los archivos ofensivos, pero eso no evita que suceda la próxima vez.

Entonces, puedo dejar de cargar mi sesión automáticamente al inicio y cambiarlo a un comando que debo ejecutar manualmente desde mi primer cliente de correo electrónico. Pero si no está cargando mi sesión en segundo plano para que esté lista para cuando esté lista para usarla, ¡se pierde todo el propósito del demonio!

Entonces, lo que me gustaría es:

  • (Mejor) Alguna forma de diferir las indicaciones del minibúfer hasta que abra un cliente de correo electrónico, mientras sigo completando el resto de la inicialización.
  • (OK) Alguna forma de hacer todas las solicitudes de minibúfer que no he aconsejado de otra manera, como se describió anteriormente, solo devuelve a nomenos que se esté ejecutando un cliente de correo electrónico. Puedo vivir con mis buffers TRAMP con errores, siempre y cuando funcione principalmente.

¿Hay alguna forma de lograr alguno de estos objetivos?

Trey
fuente
¿Hay alguna manera de reproducir este tipo de problemas mediante programación para que la comunidad pueda solucionar problemas?
Melioratus
1
Bueno, como escribí en la primera línea, es bastante fácil arreglar un ejemplo dado ... "Doctor, me duele cuando hago esto ..." "Entonces no hagas eso". El problema es el caso general. Pero una forma simple de crear el problema es hacer que el escritorio de restauración de inicio (read-desktop), luego, antes de ejecutarlo emacs --daemon, cree un archivo de bloqueo falso colocando un número entero en .emacs.desktop.lock (dónde colocar ese archivo, desafortunadamente, depende de su configuración , pero probablemente sea tu homedir o ~ / .emacs.d / .
Trey
1
Este es un caso frecuentemente mencionado aquí, por ejemplo: emacs.stackexchange.com/questions/8147/… o emacs.stackexchange.com/questions/31621/… pueden proporcionar contexto.
Trey
Este error parece estar relacionado: Error # 13697: una forma de saber si Emacs puede interactuar con el usuario , pero hasta donde yo sé, nadie ha trabajado en ello.
npostavs
@npostavs Gracias por el enlace: he anotado el error, ¡aunque tuve un comienzo falso que comenté aquí (desde que lo eliminé) antes de resolverlo!
Trey

Respuestas:

2

Nuestra discusión aclaró que no tiene ningún servidor X funcionando, esto hace que mi primera solución sea inútil para usted.

A continuación presento una segunda solución que funciona con marcos de terminal de texto.

Cuando su inicialización requiere la entrada del usuario a través de uno, las funciones recomendadas con avoid-initial-terminalEmacs esperan hasta que abra un marco de terminal de texto. El mensaje aparece en el minibúfer de ese marco y puede dar su respuesta interactiva.

La información relacionada con el código se proporciona como comentarios en el código. Hay TODOmarcadores con descripciones que le muestran dónde insertar su propia configuración. Actualmente hay formularios de prueba que validan el código.

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

Prueba: versión Emacs: 26.1

1º) Ejecutar emacs --daemonen una consola.

2º) Ejecutar emacsclient --ttyen otra consola. Allí se le solicita una contraseña y una cadena. Posteriormente, también debe responder una consulta y-or-np.

Tobias
fuente
3

No es exactamente lo que está pidiendo, pero tal vez una solución a su problema original:

Me gustaría saber si hay una forma general de evitar que emacs: el demonio se cuelgue para siempre esperando la respuesta a un mensaje que se muestra en un minibúfer que aún no existe.

Si el demonio le proporciona un marco gráfico para responder las preguntas que surgen en su fase de inicio, ya no se queda atascado.

El siguiente código define un consejo general my-with-initial-frameque abre un marco en la primera pantalla disponible (por ejemplo, :0.0).

Ese consejo se puede agregar fácilmente a los comandos de consulta como y-or-n-po read-passwd, como se demuestra a continuación.

Solo abrir un marco le brinda una posibilidad bastante grosera de responder las consultas en la interfaz de usuario. También se podría usar un cuadro de diálogo, y-or-n-ppero eso requeriría soluciones especiales para comandos de consulta específicos. Quería evitar eso.

Si prueba ese código en su archivo init, asegúrese de que sea lo primero que haya allí.

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

Prueba:

Suposiciones

Tenga un xserver en ejecución al que se puedan conectar los programas a través de la DISPLAYvariable de entorno.

Entrada en xterm:

emacs --daemon

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

Allí se abre un marco con el y-or-n-pmensaje de consulta A (y or n). Responda esa consulta e intente nuevamente:

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

Nueva consulta con solicitud B (y or n)en el mismo marco. Cierre ese marco, por ejemplo, con C-x 5 0e intente nuevamente:

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

Se abre un nuevo marco con el mensaje de consulta C (y or n).

Lo mismo funciona para la entrada de contraseña.

Tobias
fuente
@Trey Tuve algún problema con mi código (en realidad con las pruebas). Iniciar el servidor x no funcionó la primera vez. No me di cuenta inicialmente ya que no reinicié el demonio. Lo corregí ahora. Por favor prueba de nuevo. Gracias.
Tobias
Mi Linux virtual no tiene un terminal gráfico conectado, por lo que no puedo ejecutarlo xterm. Además, no creo que esta solución pueda funcionar para aquellos que sí lo hacen: si tiene el demonio configurado para ejecutarse en el inicio, intentaría abrir un marco en la parte superior de la pantalla de inicio de sesión, lo que no está permitido, por lo que se bloquea
Trey
Tobias, disculpas si lo anterior sonaba brusco: respondí en mi teléfono y puede que me haya interrumpido, así que déjame tratar de explicarlo: la única ventaja que puedo ver de lo que describes sobre no usar el demonio y ejecutarlo server-startal final del inicio en cambio, si tiene un inicio limpio, no tendrá que esperar. Pero ... tendrá que esperar, porque a menos que lo malinterprete, no puede poner la tarea de iniciar el demonio Emacs en el script de inicio de sesión del sistema ya que una GUI no estará disponible en ese momento. (Y en un caso como el mío, nunca será más tarde tampoco.)
Trey
@Trey ¿Podrías unirte a un chat ?
Tobias
1

Creo que diferir las indicaciones será difícil en general, pero debería ser bastante fácil cambiar Emacs para que tales indicaciones indiquen inmediatamente un error.

No solo eso, sino que si no puede responder a esas indicaciones sin mucha gimnasia, creo que califica como un error, por lo que le recomiendo que envíe un informe de error para eso.

Stefan
fuente
Creo que necesito un poco más de detalle. Considere el bloqueo del archivo de guardado del escritorio que menciono en el comentario de la recompensa anterior. ¿Cómo se podría cambiar la Warning: desktop file appears to be in use by PID xxx. Using it may cause conflicts. Use it anyway? (y or n)solicitud en un error, sin referirse específicamente al "escritorio" de alguna manera (porque de esa forma, al no ser general, se encuentra whack-a-mole)?
Trey
Stefan, también hay un tema que no se molesta conmigo porque yo no ejecute el daemon Emacs esta manera, pero para hacer una respuesta útil en general puede ser que necesite hacer frente a: erroring cabo fatalmente hará que Emacs se puso en marcha a través de organismos de control systemd u otros para reiniciar en un bucle Pero ignorar los errores y simplemente iniciar sesión *Messages*es probablemente un aviso insuficiente en la primera conexión del cliente de que algo puede estar seriamente mal y necesita atención inmediata antes de que el usuario intente cualquier operación con estado.
Trey
(Para aclarar a aquellos que no usan el demonio: si lo inicia manualmente, ya sea a través emacs --daemono comenzando emacsclientcon la ALTERNATE_EDITORvariable de entorno establecida en la cadena vacía, verá la salida que normalmente se *Messages*repite en el terminal hasta que el demonio completa la inicialización y Emacs está listo Pero muchos tienen Emacs iniciar el demonio al iniciar el sistema o el tiempo de inicio de sesión y la salida es conectado o tirado..
Trey
1
@Trey: la señalización de error no debería estar en desktopsino en la y-or-n-pfunción (o aún más baja). Tenemos algún mecanismo para retrasar la visualización de errores que ocurrieron durante el inicio, por lo que podríamos usar eso para mostrarlos cuando el primer cliente de correo electrónico se conecte al demonio.
Stefan
En cualquier caso, sin embargo, la mayoría de los usuarios no examina detenidamente, *Messages*y si bien el *Warnings*sistema poco utilizado abre una ventana al búfer si existe un marco activo cuando se genera la advertencia, en este caso, no existe un marco, y no Parece fácil aplazar la ventana emergente hasta el primer cliente de correo electrónico que sigue al problema de la advertencia. Si eso pudiera hacerse, su sugerencia de hacer una yes-or-no-padvertencia previa al cliente sería ideal. (¡Dudo que los usuarios se peinen *Messages*en el inicio!)
Trey