क्या केवल एक बार हुक फ़ंक्शन चलाने का कोई तरीका है?


15

प्रसंग

मैं emacs क्लाइंट / सर्वर कॉन्फ़िगरेशनafter-make-frame-functions में थीम को ठीक से लोड करने के लिए हुक का उपयोग कर रहा हूं । विशेष रूप से यह कोड स्निपेट है जिसे मैं बनाने के लिए उपयोग करता हूं (इस एसओ उत्तर में आधारित ):

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

समस्या

जब एक नया emacsclient -c/tसत्र शुरू किया जाता है तो हुक को न केवल नए फ्रेम में निष्पादित किया जाता है, बल्कि पिछले सभी मौजूदा फ़्रेमों (अन्य इमैसेक्लिएंट सत्र) में और यह बहुत कष्टप्रद दृश्य प्रभाव बनाता है (उन सभी फ़्रेमों में थीम फिर से लोड किए जाते हैं) । इससे भी बदतर, टर्मिनल ग्राहकों में पहले से ही खोला विषय रंग पूरी तरह से गड़बड़ हो। जाहिर है कि केवल emacs क्लाइंट पर ही emacs सर्वर से जुड़ा होता है। इस व्यवहार का कारण स्पष्ट है, हुक सर्वर पर चलाया जाता है और इसके सभी क्लाइंट प्रभावित होते हैं।

प्रश्न

क्या इस फ़ंक्शन को केवल एक बार निष्पादित करने या हुक का उपयोग किए बिना एक ही परिणाम प्राप्त करने का कोई तरीका है?


एक आंशिक समाधान

मेरे पास अब यह कोड है, @ ड्रू के उत्तर के लिए धन्यवाद। लेकिन अभी भी एक समस्या है, एक बार जब आप टर्मिनल में एक ग्राहक सत्र शुरू करते हैं, तो जीयूआई विषयों को ठीक से लोड नहीं करता है और इसके विपरीत। बहुत सारे परीक्षणों के बाद, मुझे एहसास हुआ कि व्यवहार इस बात पर निर्भर करता है कि कौन सा क्षीणता पहले शुरू होती है, और विभिन्न चीजों को त्यागने के बाद, मुझे लगता है कि यह शायद रंग पैलेट से संबंधित है जो भरी हुई है। यदि आप मैन्युअल रूप से थीम को पुनः लोड करते हैं, तो सभी ठीक काम करते हैं और यही कारण है कि जब मेरे प्रारंभिक कॉन्फ़िगरेशन में फ़ंक्शन द्वारा हर बार हुक द्वारा कॉल किया जाता है तो यह व्यवहार प्रकट नहीं होता है।

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

अंतिम समाधान

अंत में मेरे पास पूरी तरह से काम करने वाला कोड है जो आंशिक समाधान में देखे गए व्यवहार को हल करता है, इसे प्राप्त करने के लिए मैं फ़ंक्शन को एक बार मोड (टर्मिनल या गुई) से चलाता हूं, जब पहली बार पर्टिकेंट इमैसेक्लाइंट शुरू होता है, तो हुक से फ़ंक्शन को हटा दें क्योंकि यह है किसी और की जरूरत नहीं। अब में खुश हूँ! :) फिर से धन्यवाद @ आकर्षित!

कोड:

(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
मैंने सुझाव के अनुसार शीर्षक संपादित किया है। कृपया, बेझिझक रोलबैक करें यदि यह वह नहीं है जो आप मूल रूप से चाहते थे।
मलबर्बा

यह ठीक है @Malabarba! मैं
जो डी कास्त्रो

जवाबों:


11

मैं अनुमान लगा रहा हूं कि आप वास्तव में " केवल एक बार हुक निष्पादित करने" का रास्ता नहीं तलाश रहे हैं । मैं अनुमान लगा रहा हूं कि आप उस विशेष फ़ंक्शन को निष्पादित करने का एक तरीका खोज रहे हैं, जब भी हुक चलाया जाता है।

पारंपरिक, और सरल, उस प्रश्न का उत्तर आपके कार्य को हुक से हटाने के लिए है, जो आप चाहते हैं एक बार की कार्रवाई को पूरा करने के बाद। दूसरे शब्दों add-hookमें, एक संदर्भ में उपयोग करते हैं जहां आप जानते हैं कि हुक चलने पर फ़ंक्शन को निष्पादित किया जाना चाहिए, और फ़ंक्शन अपने आप को हुक से हटा देता है, जब फ़ंक्शन अपनी चीज़ करता है।

यदि मैं सही ढंग से अनुमान लगा रहा हूं कि आप वास्तव में क्या चाहते हैं, तो कृपया अपने प्रश्न को कुछ इस तरह संपादित करने पर विचार करें " क्या एक बार हुक फ़ंक्शन चलाने का कोई तरीका है ?

यहाँ एक उदाहरण है, मानक पुस्तकालय से 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))

हां, यह उस तरह से काम कर सकता है, और मुझे लगता है कि यह एक कम समस्याग्रस्त और त्रुटि-रहित समाधान होगा। धन्यवाद।
जो डीआई कास्त्रो

3
+1। यह एक फ़ंक्शन का 3-लाइन उदाहरण के लिए चोट नहीं करेगा जो हुक से खुद को हटा देता है।
मलाबारबा

अंत में मेरे पास आपके उत्तर में आंशिक रूप से आधारित कुल कार्य समाधान है (अंत में मुझे एहसास हुआ कि मैं हुक से फ़ंक्शन को हटाए बिना इसे डॉट कर सकता हूं)। आपका बहुत बहुत धन्यवाद!
जो डि कास्त्रो

3

यहां एक मैक्रो है जिसे आप add-hook(बड़े पैमाने पर परीक्षण नहीं) के बजाय उपयोग कर सकते हैं :

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

नोट: make-symbolदिए गए नाम के साथ एक निर्जन प्रतीक बनाता है। मैंने #प्रतीक को चिह्नित करने के लिए एक नाम को कुछ असामान्य के रूप में शामिल किया, यदि आप एक हुक चर को देखते हुए इसमें आते हैं।


यह मेरे लिए काम नहीं करता है, यह इस त्रुटि को फेंकता है:(void-function gensym)
जो डी कास्त्रो

@joedicastro आह, हाँ, यह clपैकेज से है। इसके बारे में क्षमा करें - मैं भूल गया कि हर कोई इसका उपयोग नहीं करता है। आप (make-symbol "#once")इसके बजाय उपयोग कर सकते हैं । मैं जवाब अपडेट कर दूंगा।
हेराल्ड हैन्च-ऑलसेन

1
मैंने फिर से कोशिश की, और मेरे लिए काम नहीं किया, और ईमानदारी से, क्योंकि मेरे पास ड्रू से आंशिक रूप से काम करने वाला समाधान था, मैं इसके बजाय उस अधिक आशाजनक पथ की तलाश करता हूं। वैसे भी आप जवाब के लिए धन्यवाद!
जो डी कास्त्रो

@joedicastro: यह आपका निर्णय है, निश्चित रूप से, और वास्तव में ड्रू का समाधान काम करेगा। और इसे करने का पारंपरिक तरीका है। मुख्य दोष हुक फ़ंक्शन में हुक का नाम कोड करने की आवश्यकता है, जिससे फ़ंक्शन को एक से अधिक हुक में पुन: उपयोग करना मुश्किल हो जाता है, यदि आवश्यक हो। साथ ही, यदि आप स्वयं को एक अलग संदर्भ में उपयोग के लिए समाधान की नकल करते हुए पाते हैं, तो आपको हुक के नाम को संपादित करना भी याद रखना होगा। मेरा समाधान निर्भरता को तोड़ता है, आपको टुकड़ों को पुन: उपयोग करने देता है और उन्हें इच्छानुसार घुमाता है। मुझे इस बात की उत्सुकता है कि यह आपके लिए काम क्यों नहीं करता है, लेकिन अगर ...
हैराल्ड हैन्च-ऑलसेन

... लेकिन अगर आप इसके बारे में जानने के लिए समय नहीं निकालेंगे, तो मैं पूरी तरह समझ सकता हूं। जीवन छोटा है - आप के लिए क्या काम करता है के साथ जाओ।
हेराल्ड हैन्च-ऑलसेन

0

आप gen-onceएक सामान्य फ़ंक्शन को एक फ़ंक्शन को ट्रांस करने के लिए एक हाइपर फ़ंक्शन कर सकते हैं जो केवल एक बार चल सकता है:

(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

तो, का उपयोग करें (add-hook 'xxx-mode-hook (gen-once your-function-need-to-run-once))

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.