Bir kanca işlevini yalnızca bir kez çalıştırmanın bir yolu var mı?


15

Bağlam

after-make-frame-functionsBir emacs istemci / sunucu yapılandırmasında temaları düzgün yüklemek için kanca kullanıyorum . Özellikle bu yapmak için kullandığım kod snippet (bu SO cevabına dayanarak ):

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

Sorun

Yeni bir emacsclient -c/toturum başlatıldığında, kanca yalnızca yeni çerçevede değil, önceki tüm karelerde (diğer emacclient oturumları) yürütülür ve çok can sıkıcı bir görsel efekt oluşturur (temalar tüm bu karelere yeniden yüklenir) . Daha da kötüsü, terminal istemcilerinde zaten açılan tema rengi tamamen berbat oluyor. Açıkçası bu sadece aynı emacs sunucusuna bağlı emacs istemcilerinde olur. Bu davranışın nedeni açıktır, kanca sunucuda çalıştırılır ve tüm istemcileri etkilenir.

Soru

Kanca kullanmadan bu işlevi yalnızca bir kez yürütmenin veya aynı sonucu almanın herhangi bir yolu var mı?


Kısmi bir çözüm

@ Drew'un cevabı sayesinde şimdi bu kod var. Ancak yine de bir sorun var, terminalde bir istemci oturumu başlattığınızda, GUI temaları düzgün yüklemez ve bunun tersi de geçerlidir. Birçok testten sonra, davranışın ilk önce hangi emacsclient'in başladığına bağlı olduğunu fark ettim ve çeşitli şeyleri attıktan sonra, bunun yüklü renk paleti ile ilgili olduğunu düşünüyorum. Elle yeniden yüklerseniz, tema iyi çalışır ve bu nedenle, ilk yapılandırmamda olduğu gibi işlev her seferinde kanca tarafından çağrıldığında bu davranışın görünmemesinin nedeni budur.

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

Son çözüm

Sonunda, kısmi çözümde görülen davranışı çözen tamamen çalışma kodum var, bunu başarmak için işlevi bir kez mod (terminal veya gui) ile çalıştırıyorum, sonra ilgili emacclient ilk kez başlatıldığında, daha sonra işlevi kancadan çıkarın çünkü artık gerek yok. Şimdi mutluyum! :) Tekrar teşekkürler @Drew!

Kod:

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

1
Başlığı önerildiği gibi düzenledim. Lütfen, başlangıçta kastettiğiniz şey değilse, geri dönmekten çekinmeyin.
Malabarba

İyi @Malabarba! Katılıyorum @drew
joe di castro

Yanıtlar:


11

Gerçekten " kanca sadece bir kez yürütmek " için bir yol aramıyorum tahmin ediyorum . Kanca çalıştırıldığında bu işlevi yalnızca bir kez yürütmenin bir yolunu aradığınızı tahmin ediyorum.

Geleneksel ve basit cevabı bu soruya İstediğiniz kerelik işlemi gerçekleştirdikten sonra, kanca kendisini kaldırmak için işlevi içindir. Başka bir deyişle add-hook, kanca çalıştırıldığında işlevin yürütülmesi gerektiğini bildiğiniz bir bağlamda kullanın ve işlev kendi işini yaptıktan sonra işlevin kendisini kancadan kaldırmasını sağlayın.

Gerçekten ne istediğinizi doğru tahmin ediyorsanız, lütfen sorunuzu " Bir kanca işlevini yalnızca bir kez çalıştırmanın bir yolu var mı ?

Standart kütüphaneden bir örnek facemenu.el:

(defun facemenu-set-self-insert-face (face)
  "Arrange for the next self-inserted char to have face `face'."
  (setq facemenu-self-insert-data (cons face this-command))
  (add-hook 'post-self-insert-hook 'facemenu-post-self-insert-function))

(defun facemenu-post-self-insert-function ()
  (when (and (car facemenu-self-insert-data)
             (eq last-command (cdr facemenu-self-insert-data)))
    (put-text-property (1- (point)) (point)
                       'face (car facemenu-self-insert-data))
    (setq facemenu-self-insert-data nil))
  (remove-hook 'post-self-insert-hook 'facemenu-post-self-insert-function))

Evet, bu şekilde çalışabilir ve sanırım daha az sorunlu ve hataya açık bir çözüm olacaktır. Teşekkürler.
joe di castro

3
+1. Kancadan kendisini kaldıran bir fonksiyonun 3 satırlı bir örneğine sahip olmak acı vermez.
Malabarba

Sonunda kısmen cevabınıza dayanan toplam bir çalışma çözümüm var (sonunda işlevi kancadan çıkarmadan noktalayabileceğimi fark ettim). Çok teşekkür ederim!
joe di castro

3

İşte add-hook(kapsamlı olarak test edilmemiştir) yerine kullanabileceğiniz bir makro :

(defmacro add-hook-run-once (hook function &optional append local)
  "Like add-hook, but remove the hook after it is called"
  (let ((sym (make-symbol "#once")))
    `(progn
       (defun ,sym ()
         (remove-hook ,hook ',sym ,local)
         (funcall ,function))
       (add-hook ,hook ',sym ,append ,local))))

Not: make-symbolverilen ada sahip, belirsiz bir sembol oluşturur. Ben dahil #bir kanca değişkeni bakarken durumunda bunun rastlamak, bayrağa isim biraz alışılmadık şekilde sembolü.


Benim için çalışmıyor, bu hatayı atar:(void-function gensym)
joe di castro

@joedicastro Ah, evet, bu clpaketten. Bunun için üzgünüm - Herkesin kullanmadığını unutuyorum. Bunun (make-symbol "#once")yerine kullanabilirsiniz . Cevabı güncelleyeceğim.
Harald Hanche-Olsen

1
Tekrar denedim ve benim için çalışmadı ve dürüst olmak gerekirse, Drew'dan kısmi bir çalışma çözümüm olduğu için, bunun yerine daha umut verici bir yol arıyorum. Cevap verdiğiniz için teşekkürler!
joe di castro

@joedicastro: Elbette bu senin kararın ve gerçekten de Drew'un çözümü işe yarayacak. Ve bunu yapmanın geleneksel yolu. Ana dezavantaj, kanca işlevindeki kanca adının sabit kodlanması gereksinimidir, bu da gerektiğinde işlevi birden fazla kancada yeniden kullanmayı zorlaştırır. Ayrıca, çözümü farklı bir bağlamda kullanmak için kopyalarken kendinizi kancanın adını da düzenlemeyi unutmayın. Benim çözümüm bağımlılığı kırarak parçaları tekrar kullanmanıza ve onları istediğiniz gibi hareket ettirmenize izin veriyor. Neden senin için işe yaramadığını merak ediyorum, ama eğer ...
Harald Hanche-Olsen

… Ama bunun sonuna kadar zaman ayırmak istemiyorsanız, tamamen anlıyorum. Hayat kısa - sizin için uygun olana gidin.
Harald Hanche-Olsen

0

gen-oncenormal bir işlevi yalnızca bir kez çalışabilecek bir işleve aktarmak için hiper işlev yapabilirsiniz :

(setq lexical-binding t)

(defun gen-once (fn)
  (let ((done nil))
    (lambda () (if (not done)
                   (progn (setq done t)
                          (funcall fn))))))
(setq test (gen-once (lambda () (message "runs"))))

;;;;---following is test----;;;;
((lambda () (funcall test))) ;; first time, message "runs"
((lambda () (funcall test))) ;; nil
((lambda () (funcall test))) ;; nil

sonra kullan (add-hook 'xxx-mode-hook (gen-once your-function-need-to-run-once))

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.