Der Kontext
Ich verwende den after-make-frame-functions
Haken, um die Themen in einer Emacs-Client / Server-Konfiguration richtig zu laden . Im Einzelnen ist dies das Code-Snippet, mit dem ich das mache (basierend auf dieser SO-Antwort ):
(if (daemonp)
(add-hook 'after-make-frame-functions
(lambda (frame)
(select-frame frame)
(load-theme 'monokai t)
;; setup the smart-mode-line and its theme
(sml/setup)))
(progn (load-theme 'monokai t)
(sml/setup)))
Das Problem
Wenn eine neue emacsclient -c/t
Sitzung gestartet wird, wird der Hook nicht nur in dem neuen Frame ausgeführt, sondern in allen vorherigen vorhandenen Frames (andere Emacsclient- Sitzungen), und es entsteht ein sehr ärgerlicher visueller Effekt (die Themes werden in all diesen Frames erneut geladen) . Schlimmer noch, in den bereits geöffneten Terminal-Clients wird das Thema Farbe komplett durcheinander gebracht. Dies geschieht natürlich nur auf den Emacs-Clients, die mit demselben Emacs-Server verbunden sind. Der Grund für dieses Verhalten ist klar, der Hook wird auf dem Server ausgeführt und alle seine Clients sind betroffen.
Die Frage
Gibt es eine Möglichkeit, diese Funktion nur einmal auszuführen oder dasselbe Ergebnis zu erhalten, ohne den Hook zu verwenden?
Eine Teillösung
Ich habe jetzt diesen Code, dank der Antwort von @ Drew. Wenn Sie jedoch eine Client-Sitzung im Terminal starten, wird die GUI die Designs nicht richtig laden und umgekehrt. Nach vielen Tests stellte ich fest, dass das Verhalten davon abhängt, welcher Emacsclient zuerst startet, und nach dem Verwerfen verschiedener Dinge denke ich, dass dies möglicherweise mit der geladenen Farbpalette zusammenhängt. Wenn Sie das Design manuell neu laden, funktioniert alles einwandfrei. Aus diesem Grund tritt dieses Verhalten nicht immer auf, wenn die Funktion wie in meiner ursprünglichen Konfiguration vom Hook aufgerufen wird.
(defun emacsclient-setup-theme-function (frame)
(progn
(select-frame frame)
(load-theme 'monokai t)
;; setup the smart-mode-line and its theme
(sml/setup)
(remove-hook 'after-make-frame-functions 'emacsclient-setup-theme-function)))
(if (daemonp)
(add-hook 'after-make-frame-functions 'emacsclient-setup-theme-function)
(progn (load-theme 'monokai t)
(sml/setup)))
Das finale Resultat
Schließlich habe ich voll funktionsfähigen Code, der das in der Teillösung beobachtete Verhalten behebt. Um dies zu erreichen, führe ich die Funktion einmal nach Modus (Terminal oder GUI) aus, wenn der betreffende Emacsclient zum ersten Mal gestartet wird, und entferne dann die Funktion aus dem Hook, weil nicht mehr benötigt. Jetzt bin ich glücklich! :) Nochmals vielen Dank @Drew!
Der Code:
(setq myGraphicModeHash (make-hash-table :test 'equal :size 2))
(puthash "gui" t myGraphicModeHash)
(puthash "term" t myGraphicModeHash)
(defun emacsclient-setup-theme-function (frame)
(let ((gui (gethash "gui" myGraphicModeHash))
(ter (gethash "term" myGraphicModeHash)))
(progn
(select-frame frame)
(when (or gui ter)
(progn
(load-theme 'monokai t)
;; setup the smart-mode-line and its theme
(sml/setup)
(sml/apply-theme 'dark)
(if (display-graphic-p)
(puthash "gui" nil myGraphicModeHash)
(puthash "term" nil myGraphicModeHash))))
(when (not (and gui ter))
(remove-hook 'after-make-frame-functions 'emacsclient-setup-theme-function)))))
(if (daemonp)
(add-hook 'after-make-frame-functions 'emacsclient-setup-theme-function)
(progn (load-theme 'monokai t)
(sml/setup)))
Antworten:
Ich vermute, dass Sie nicht wirklich nach einer Möglichkeit suchen, " den Hook nur einmal auszuführen ". Ich vermute, dass Sie nach einer Möglichkeit suchen, diese bestimmte Funktion nur einmal auszuführen, wenn der Hook ausgeführt wird.
Die konventionelle und einfache Antwort auf diese Frage lautet, dass Ihre Funktion sich selbst vom Haken löst, nachdem Sie die gewünschte einmalige Aktion ausgeführt haben. Mit anderen Worten, verwenden
add-hook
Sie diese Option in einem Kontext, in dem Sie wissen, dass die Funktion ausgeführt werden soll, wenn der Hook ausgeführt wird, und lassen Sie die Funktion selbst aus dem Hook entfernen, nachdem die Funktion ihre Aufgabe erfüllt hat.Wenn ich richtig einschätze, was Sie wirklich wollen, dann überlegen Sie bitte, ob Sie Ihre Frage auf etwas wie " Gibt es eine Möglichkeit, eine Hook- Funktion nur einmal auszuführen ?
Hier ist ein Beispiel aus der Standardbibliothek
facemenu.el
:quelle
Hier ist ein Makro, das Sie anstelle von
add-hook
(nicht ausführlich getestet) verwenden können:Hinweis:
make-symbol
Erstellt ein nicht internes Symbol mit dem angegebenen Namen. Ich habe ein#
in den Namen eingefügt, um das Symbol als etwas ungewöhnlich zu kennzeichnen, falls Sie es beim Betrachten einer Hook-Variablen finden.quelle
(void-function gensym)
cl
Paket. Tut mir leid - ich vergesse, dass nicht jeder es benutzt. Sie können(make-symbol "#once")
stattdessen verwenden. Ich werde die Antwort aktualisieren.Sie können eine Hyperfunktion
gen-once
erstellen, um eine normale Funktion in eine Funktion umzuwandeln, die nur einmal ausgeführt werden kann:dann benutze
(add-hook 'xxx-mode-hook (gen-once your-function-need-to-run-once))
quelle