Tryb demona: odroczyć interaktywne monity podczas uruchamiania?

16

(Zwróć uwagę, że przeciwnie, to pytanie nie jest takie samo jak Jak uruchomić w trybie demona i ukryć interaktywne okna dialogowe? Ponieważ na to pytanie „odpowiedział” autor, który wyeliminował to, co spowodowało pojawienie się konkretnego monitu).

Chciałbym wiedzieć, czy istnieje ogólny sposób, aby nie emacs --daemonzawieszać się na zawsze i czekać na odpowiedź na monit wyświetlany w minibuforze, który jeszcze nie istnieje.

Nie można połączyć się z emacsclient, aby odpowiedzieć na te monity, ponieważ serwer nie uruchomi się, dopóki Emacs nie zakończy sekwencji startowej. (Oznacza to, że jeśli ALTERNATE_EDITOR jest ustawiony na pusty ciąg, co sprawia, emacsclientże serwer , który nie może znaleźć serwera, uruchamia nowego demona, możesz skończyć z wieloma demonami Emacsa, które są zablokowane i czekają.) Muszę killall emacsrozwiązać problem przed kontynuowaniem.

Mogę grać w walenie w kret z każdą rzeczą, która powoduje wyświetlenie monitu podczas uruchamiania, gdy ją zidentyfikuję (uruchamiając Emacsa w trybie innym niż demon i sprawdzając, o co pyta), ale nie jest to rozwiązanie, ponieważ nie może zatrzymać następnego demona od zawieszenia się przy starcie z nowego powodu.

Podam przykład: częstym powodem zawieszania się było po ponownym uruchomieniu systemu lub awarii Emacsa, gdy pierwszy po ponownym uruchomieniu Emacs chciał wiedzieć, czy można ukraść pliki blokujące od nieistniejącego Emacsa. Mógłbym to naprawić, tworząc porady, które sprawią, że monit zawsze odpowie „tak” bez interakcji. Ale jednym z plików, które były otwarte podczas poprzedniej sesji składowania, był plik TRAMP wymagający hasła sudo lub SSH, więc demon utknął, czekając na monit o hasło. Naprawiam to, ręcznie edytując plik sesji (za pomocą vilub emacs -q!), Aby usunąć szkodliwe pliki - ale to nie powstrzymuje go przed następnym razem.

Mogę więc zatrzymać ładowanie sesji automatycznie podczas uruchamiania i zmienić ją na polecenie, które muszę ręcznie wykonać z pierwszego programu emacsclient. Ale jeśli nie ładuje mojej sesji w tle, więc jest gotowa, zanim będę gotowa z niej korzystać, cały cel demona zostanie utracony!

Chciałbym więc:

  • (Najlepsze) Jakiś sposób na odroczenie monitów minibufora, dopóki nie otworzę emacsclient, jednocześnie kończąc resztę inicjalizacji.
  • (OK) W jakiś sposób, aby wszystkie monity w minibuforze nie były jeszcze zalecane, jak opisano powyżej, po prostu wróć, nochyba że uruchomiony jest emacsclient. Mogę żyć z błędami buforów TRAMP, o ile w większości działają.

Czy jest jakiś sposób na osiągnięcie któregokolwiek z tych celów?

Trey
źródło
Czy istnieje sposób programowego odtworzenia tego typu problemów, aby społeczność mogła je rozwiązać?
Melioratus
1
Cóż, jak napisałem w pierwszym wierszu, dość łatwo jest naprawić dany przykład ... „Doktorze, to boli, kiedy to robię ...” „Więc nie rób tego”. Problemem jest sprawa ogólna. Ale prostym sposobem na rozwiązanie tego problemu jest przywrócenie pulpitu podczas uruchamiania (read-desktop), a następnie, przed uruchomieniem emacs --daemon, utwórz fałszywy plik blokady, umieszczając liczbę całkowitą w .emacs.desktop.lock (gdzie umieszczenie tego pliku niestety zależy od konfiguracji , ale prawdopodobnie albo twój homedir, albo ~ / .emacs.d / .
Trey,
1
Jest to tutaj często wspominany przypadek, na przykład: emacs.stackexchange.com/questions/8147/… lub emacs.stackexchange.com/questions/31621/... może zawierać kontekst.
Trey,
Ten błąd wydaje się powiązany: Bug # 13697 - sposób, aby stwierdzić, czy Emacs może wchodzić w interakcje z użytkownikiem , ale o ile wiem, nikt nie pracował nad tym.
npostavs
@npostavs Dzięki za link - zanotowałem błąd, ale skomentowałem go tutaj, ponieważ skomentowałem go (odkąd go usunąłem), zanim go rozgryzłem!
Trey,

Odpowiedzi:

2

Nasza dyskusja wyjaśniła, że ​​nie masz uruchomionego żadnego serwera X, przez co moje pierwsze rozwiązanie jest bezużyteczne.

Poniżej przedstawiam drugie rozwiązanie, które działa z ramkami terminali tekstowych.

Gdy inicjalizacja wymaga wprowadzenia danych przez użytkownika, funkcje zalecane przez avoid-initial-terminalEmacsa czekają, aż otworzysz ramkę terminala tekstowego. Monit pojawia się w minibuforze tej ramki i możesz udzielić interaktywnej odpowiedzi.

Informacje związane z kodem są podawane jako komentarze w kodzie. Istnieją TODOznaczniki z opisami, które pokazują, gdzie wstawić własną konfigurację. Obecnie istnieją tam formularze testowe, które sprawdzają poprawność kodu.

;; 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: wersja Emacsa: 26.1

1.) Uruchom emacs --daemonna konsoli.

2.) Uruchom emacsclient --ttyna innej konsoli. Zostaniesz tam poproszony o hasło i ciąg znaków. Następnie musisz również odpowiedzieć na zapytanie typu y-or-np.

Tobiasz
źródło
3

Nie dokładnie to, o co prosisz, ale może rozwiązanie pierwotnego problemu:

Chciałbym wiedzieć, czy istnieje ogólny sposób na powstrzymanie emacsa - demona od zawieszenia się na zawsze i czekania na odpowiedź na monit wyświetlany w minibuforze, który jeszcze nie istnieje.

Jeśli demon da ci graficzną ramkę do odpowiedzi na pytania pojawiające się w fazie rozruchu, nie utkniesz.

Poniższy kod definiuje ogólne porady, my-with-initial-framektóre otwierają ramkę na pierwszym dostępnym wyświetlaczu (np :0.0.).

Poradę tę można łatwo dodać do zapytań dotyczących poleceń, takich jak y-or-n-plub read-passwd, jak pokazano poniżej.

Samo otwarcie ramki daje dość zgrubną możliwość odpowiedzi na zapytania w interfejsie użytkownika. Można również użyć okna dialogowego, y-or-n-pale wymagałoby to specjalnych rozwiązań dla określonych poleceń zapytań. Chciałem tego uniknąć.

Jeśli spróbujesz tego kodu w pliku init, upewnij się, że jest to pierwsza rzecz.

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

Założenia:

Posiadaj działający serwer xserver, z którym programy mogą się łączyć za pośrednictwem DISPLAYzmiennej środowiskowej.

Dane wejściowe w xterm:

emacs --daemon

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

Otwiera ramkę z y-or-n-pmonitem zapytania A (y or n). Odpowiedz na to zapytanie i spróbuj ponownie:

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

Nowe zapytanie z monitem B (y or n)w tej samej ramce. Zamknij tę ramkę, np. Za pomocą C-x 5 0i spróbuj ponownie:

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

Nowa ramka otwiera się z pytaniem C (y or n).

To samo działa w przypadku wprowadzania hasła.

Tobiasz
źródło
@Trey Miałem problem z kodem (właściwie z testowaniem). Uruchamianie serwera x nie działało za pierwszym razem. Początkowo nie zauważyłem, ponieważ nie uruchomiłem ponownie demona. Poprawiłem to teraz. Proszę przetestuj ponownie. Dzięki.
Tobias
Mój Linux Virtual nie ma podłączonego graficznego terminalu, więc nie mogę uruchomić xterm. Nie sądzę też, aby to rozwiązanie działało dla tych, którzy to robią - jeśli masz skonfigurowanego demona do uruchamiania podczas uruchamiania, próbowałby otworzyć ramkę na ekranie logowania, co jest niedozwolone, więc ulega awarii
Trey,
Tobiaszu, przepraszam, jeśli powyższe zabrzmiało szorstko - odpowiedziałem na swój telefon i być może skróciłem się, więc pozwólcie, że spróbuję rozwinąć: jedyną zaletą, jaką widzę na temat tego, co opisujesz, niż nieużywanie demona i uruchamianie server-startpo zakończeniu uruchamiania zamiast tego jest tak, że jeśli masz czysty start, nie będziesz musiał czekać. Ale ... będziesz musiał poczekać, ponieważ jeśli nie zrozumiem źle, nie możesz umieścić zadania uruchomienia demona Emacs w skrypcie logowania do systemu, ponieważ GUI nie będzie wtedy dostępny. (A w przypadku takim jak mój, nigdy też nie będzie później.)
Trey
@Trey Czy możesz dołączyć do czatu ?
Tobias
1

Myślę, że odroczenie podpowiedzi będzie ogólnie trudne, ale zmiana Emacsa powinna być dość łatwa, aby takie podpowiedzi natychmiast sygnalizowały błąd.

Mało tego, ale jeśli nie potrafisz odpowiedzieć na te pytania bez dużej ilości ćwiczeń, myślę, że kwalifikuje się to jako błąd, więc zalecam przesłanie raportu o błędzie.

Stefan
źródło
Myślę, że potrzebuję trochę więcej szczegółów. Rozważ blokadę plików zapisywania na pulpicie, o której wspomniałem w komentarzu do powyższej nagrody. Jak przejść do zamiany Warning: desktop file appears to be in use by PID xxx. Using it may cause conflicts. Use it anyway? (y or n)monitu na błąd, nie odnosząc się konkretnie do „pulpitu” (ponieważ w ten sposób, będąc nieogólnym, kłamie jak kret)?
Trey,
Stefan, również nie jest problem, że nie przeszkadza mi, bo nie uruchomić Emacs demon ten sposób, ale aby ogólnie użyteczne odpowiedź może wymagać adresowania: erroring się śmiertelnie spowoduje Emacs uruchomiony poprzez Systemd lub innych strażników, aby ponownie uruchomić w pętli. Ale zignorowanie błędów i samo zalogowanie się *Messages*jest prawdopodobnie niewystarczającym heads-upem przy pierwszym połączeniu klienta, że ​​coś może być naprawdę nie tak i wymaga natychmiastowej uwagi, zanim użytkownik podejmie jakiekolwiek stanowe operacje.
Trey,
(Aby to wyjaśnić dla tych, którzy nie używają demona - jeśli uruchomisz go ręcznie, albo za pomocą, emacs --daemonalbo zaczynając emacsclientod ALTERNATE_EDITORzmiennej środowiskowej ustawionej na pusty ciąg znaków, zobaczysz dane wyjściowe, które zwykle trafiają do *Messages*echa w terminalu aż do demona finalizuje Inicjalizacja i Emacs jest gotowy, ale wiele z nich Emacs uruchomić demona przy starcie systemu lub czasie logowania, a wyjście jest albo zalogować lub wyrzucić..
Trey
1
@Trey: sygnalizacja błędu nie powinna być włączona, desktopale w y-or-n-pfunkcji (lub niższej). Mamy mechanizm opóźniający wyświetlanie błędów, które wystąpiły podczas uruchamiania, więc możemy go użyć, aby wyświetlić je, gdy pierwszy emacsclient połączy się z demonem.
Stefan
W obu przypadkach jednak większość użytkowników nie spostrzega *Messages*- i podczas gdy mało używany *Warnings*system wyświetla okno bufora, jeśli aktywna ramka istnieje, gdy generowane jest ostrzeżenie, w tym przypadku nie istnieje żadna ramka, a nie wydaje się łatwe, aby odłożyć wyskakujące okienko do pierwszego emacsclient po problemie z ostrzeżeniem. Jeśli to można zrobić, Twoja sugestia yes-or-no-postrzeżenia przed klientem byłaby idealna. (Wątpię, żeby użytkownicy czesali się *Messages*przy starcie!)
Trey