Le contexte
J'utilise le after-make-frame-functions
hook pour charger correctement les thèmes dans une configuration client / serveur emacs . Plus précisément, voici l'extrait de code que j'utilise pour le faire (basé sur cette réponse SO ):
(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)))
Le problème
Lorsqu'une nouvelle emacsclient -c/t
session est démarrée, le hook est exécuté non seulement dans le nouveau cadre, mais dans tous les cadres existants existants (autres sessions emacsclient ) et cela fait un effet visuel très ennuyeux (les thèmes sont chargés à nouveau dans tous ces cadres) . Pire encore, dans le terminal, les clients déjà ouverts, la couleur du thème est complètement gâchée. Évidemment, cela ne se produit que sur les clients emacs connectés au même serveur emacs. La raison de ce comportement est claire, le hook est exécuté sur le serveur et tous ses clients sont affectés.
La question
Existe-t-il un moyen d'exécuter cette fonction une seule fois ou d'obtenir le même résultat sans utiliser le hook?
Une solution partielle
J'ai maintenant ce code, grâce à la réponse de @ Drew. Mais il y a toujours un problème, une fois que vous démarrez une session client dans le terminal, l'interface graphique ne charge pas les thèmes correctement et vice-versa. Après beaucoup de tests, j'ai réalisé que le comportement dépend de quel emacsclient commence en premier, et après avoir jeté diverses choses, je pense que c'est peut-être lié à la palette de couleurs qui est chargée. Si vous rechargez manuellement le thème, tout fonctionne bien et c'est la raison pour laquelle ce comportement n'apparaît pas lorsque la fonction est appelée par le hook à chaque fois comme dans ma configuration initiale.
(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)))
La solution finale
Enfin, j'ai un code totalement fonctionnel qui résout le comportement vu dans la solution partielle, pour y parvenir, j'exécute la fonction une fois par mode (terminal ou gui) lorsque le client pertinent est démarré pour la première fois, puis supprimez la fonction du crochet car est plus besoin. Maintenant, je suis heureux! :) Merci encore @Drew!
Le 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)))
Réponses:
Je suppose que vous ne cherchez pas vraiment à " exécuter le hook une seule fois ". Je suppose que vous cherchez un moyen d'exécuter cette fonction particulière une seule fois, chaque fois que le hook est exécuté.
La réponse conventionnelle et simple à cette question est que votre fonction se retire du crochet, après avoir effectué l'action unique que vous souhaitez. En d'autres termes, utilisez
add-hook
dans un contexte où vous savez que la fonction doit être exécutée lorsque le hook est exécuté et que la fonction elle-même se supprime du hook, une fois que la fonction a fait son travail.Si je devine correctement ce que vous voulez vraiment, envisagez de modifier votre question en quelque chose comme " Existe-t-il un moyen d'exécuter une fonction de raccordement une seule fois?
Voici un exemple, de la bibliothèque standard
facemenu.el
:la source
Voici une macro que vous pouvez utiliser à la place de
add-hook
(non largement testée):Remarque:
make-symbol
crée un symbole non interne avec le nom donné. J'ai inclus un#
dans le nom pour signaler le symbole comme quelque peu inhabituel, au cas où vous le rencontreriez en regardant une variable de crochet.la source
(void-function gensym)
cl
paquet. Désolé - j'oublie que tout le monde ne l'utilise pas. Vous pouvez utiliser à la(make-symbol "#once")
place. Je mettrai à jour la réponse.vous pouvez créer une hyper fonction
gen-once
pour transposer une fonction normale en une fonction qui ne peut s'exécuter qu'une seule fois:puis, utilisez
(add-hook 'xxx-mode-hook (gen-once your-function-need-to-run-once))
la source