O contexto
Estou usando o after-make-frame-functions
gancho para carregar corretamente os temas em uma configuração de cliente / servidor do emacs . Especificamente, este é o trecho de código que eu uso para fazer isso (com base nesta resposta do 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)))
O problema
Quando uma nova emacsclient -c/t
sessão é iniciada, o gancho é executado não apenas no novo quadro, mas em todos os quadros existentes anteriores (outras sessões do emacsclient ) e produz um efeito visual muito irritante (os temas são carregados novamente em todos esses quadros) . Pior ainda, nos clientes do terminal já abertos, a cor do tema fica completamente bagunçada. Obviamente, isso acontece apenas nos clientes emacs conectados ao mesmo servidor emacs. O motivo desse comportamento é claro, o gancho é executado no servidor e todos os seus clientes são afetados.
A questão
Existe alguma maneira de executar essa função apenas uma vez ou obter o mesmo resultado sem usar o gancho?
Uma solução parcial
Agora tenho esse código, graças à resposta do @ Drew. Mas ainda tem um problema: depois de iniciar uma sessão do cliente no terminal, a GUI não carrega os temas corretamente e vice-versa. Depois de muitos testes, percebi que o comportamento depende de qual emacsclient é iniciado primeiro e, depois de descartar várias coisas, acho que talvez esteja relacionado à paleta de cores carregada. Se você recarregar manualmente, o tema funcionará bem e essa é a razão pela qual esse comportamento não aparece quando a função é chamada pelo gancho sempre, como na minha configuração inicial.
(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)))
A solução final
Finalmente, tenho um código totalmente funcional que resolve o comportamento visto na solução parcial. Para conseguir isso, eu executo a função uma vez por modo (terminal ou gui), quando o emacsclient pertinente é iniciado pela primeira vez e, em seguida, remova a função do gancho, porque é não é mais necessário. Agora estou feliz! :) Mais uma vez obrigado @Drew!
O código:
(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)))
Respostas:
Eu estou supondo que você realmente não está procurando uma maneira de " executar o gancho apenas uma vez ". Suponho que você esteja procurando uma maneira de executar essa função específica apenas uma vez, sempre que o gancho for executado.
A resposta convencional e simples a essa pergunta é que sua função se retire do gancho, depois de executar a ação única que você deseja. Em outras palavras, use
add-hook
em um contexto em que você saiba que a função deve ser executada quando o gancho for executado e faça com que a própria função se remova do gancho, depois que a função fizer sua coisa.Se estou adivinhando corretamente o que você realmente deseja, considere editar sua pergunta para algo como " Existe alguma maneira de executar uma função de gancho apenas uma vez?
Aqui está um exemplo da biblioteca padrão
facemenu.el
:fonte
Aqui está uma macro que você pode usar em vez de
add-hook
(não amplamente testada):Nota:
make-symbol
cria um símbolo não interno com o nome fornecido. Eu incluí um#
no nome para sinalizar o símbolo como algo incomum, caso você o encontre enquanto olha para uma variável de gancho.fonte
(void-function gensym)
cl
pacote. Desculpe por isso - esqueço que nem todo mundo usa. Você pode usar em seu(make-symbol "#once")
lugar. Vou atualizar a resposta.você pode criar uma hiper função
gen-once
para transformar uma função normal em uma função que só pode ser executada uma vez:então use
(add-hook 'xxx-mode-hook (gen-once your-function-need-to-run-once))
fonte