emacs प्रमुख मोड को स्विच करते समय कैसे डायर-लोकल चर रखने के लिए?




elisp (3)

मैं एक ऐसी परियोजना के लिए तैयार हूं जहां मानक इंडेंटेशन और टैब 3-वर्ण विस्तृत हैं, और यह एचटीएमएल, पीएचपी, और जावास्क्रिप्ट के मिश्रण का उपयोग कर रहा है। चूंकि मैं सब कुछ के लिए इमाक्स का उपयोग करता हूं, और केवल इस प्रोजेक्ट के लिए 3-चार इंडेंटेशन चाहता हूं, मैंने परियोजना के रूट पर ".dir-localals.el" फ़ाइल की स्थापना की है, जिसके अंतर्गत सभी फ़ाइलों / सभी विधियों पर लागू होता है:

; Match projets's default indent of 3 spaces per level- and don't add tabs
(
 (nil .
        (
         (tab-width . 3)
         (c-basic-offset . 3)
         (indent-tabs-mode . nil)
         ))
 )

जब मैं पहली बार फ़ाइल खोलता हूं, तो ठीक काम करता है समस्या तब होती है जब प्रमुख तरीकों को स्विच करना होता है- उदाहरण के लिए PHP फ़ाइल के अंदर एक शाब्दिक HTML के हिस्से पर काम करना। तब मैं सभी डायर-लोकल वैरिएबल खो देता हूं।

मैंने स्पष्ट रूप से ".dir-localals.el" में उपयोग किए जाने वाले सभी मोडों को बताते हुए प्रयास किया है, और मेरी .emac फाइल "dir-localals-set-class-variables / dir-locals-set-directory-class "। मुझे यह कहते हुए प्रसन्नता हो रही है कि वे सभी लगातार व्यवहार करते हैं, शुरू में डायर-लोकल चर सेट करते हैं, और फिर उन्हें खो देते हैं क्योंकि मैं प्रमुख मोड को स्विच करता हूं।

मैं जीएनयू एमैक्स 24.3.1 का उपयोग कर रहा हूं।

बफर के प्रमुख-मोड को स्विच करने पर डायर-लोकल वैरिएबल को पुनः लोड करने का एक शानदार तरीका क्या है?

- संपादित करें - उत्कृष्ट उत्तर और कमेंटरी के लिए धन्यवाद हारून और एफ़िल! यहां पोस्ट करने के बाद, मैंने सोचा कि "बग की तरह" बदबू आती है, इसलिए जीएनयू को एक रिपोर्ट में प्रवेश किया- इन चर्चाओं के संदर्भ में उन्हें भेज दिया जाएगा


मेरी इस पर:

(add-hook 'after-change-major-mode-hook #'hack-local-variables)

और या तो

(defun my-normal-mode-advice
    (function &rest ...)
  (let ((after-change-major-mode-hook
         (remq #'hack-local-variables after-change-major-mode-hook)))
    (apply function ...)))

यदि आप कष्टप्रद साथ रह सकते हैं

बाद-परिवर्तन-प्रमुख-मोड-हुक बफर-स्थानीय बनाने के लिए स्थानीय रूप से चलें!

संदेश या

(defun my-normal-mode-advice
    (function &rest ...)
  (remove-hook 'after-change-major-mode-hook #'hack-local-variables)
  (unwind-protect
      (apply function ...)
    (add-hook 'after-change-major-mode-hook #'hack-local-variables)))

अन्यथा और अंत में

(advice-add #'normal-mode :around #'my-normal-mode-advice)

हारून मिलर के उत्तर के लिए टिप्पणी के अनुसार, यहां एक संक्षिप्त अवलोकन है, जब एक मोड फ़ंक्शन को कहा जाता है (व्युत्पन्न तरीके से स्पष्टीकरण के साथ); कैसे एक मोड को मैन्युअल रूप से एमएसीएस से अलग ढंग से बुला रहा है, यह स्वत: कॉल करता है; और जहां after-change-major-mode-hook और hack-local-variables फिट होते हैं, निम्न सुझाए गए कोड के संदर्भ में:

(add-hook 'after-change-major-mode-hook 'hack-local-variables)

किसी फ़ाइल पर जाने के बाद, एएमएसी normal-mode कॉल करता normal-mode जो बफ़र के लिए "उचित प्रमुख मोड और बफर-स्थानीय वेरिएबल बाइंडिंग को स्थापित करता है"। यह पहली कॉलिंग set-auto-mode द्वारा करता set-auto-mode , और तुरंत ही hack-local-variables वेरिएल्स को बुलाता है, जो बफर के लिए सभी निर्देशिका-स्थानीय और फ़ाइल-स्थानीय चर को निर्धारित करता है, और उसके अनुसार उनके मान सेट करता है।

set-auto-mode कैसे कॉल करने का set-auto-mode चुनता है, इसके विवरण के लिए, Ch i g (elisp) Auto Major Mode RET देखें । यह वास्तव में कुछ प्रारंभिक स्थानीय-वैरिएबल इंटरैक्शन शामिल है (इसे एक mode वैरिएबल की जांच करने की आवश्यकता है, इसलिए उस तरीके के लिए एक विशिष्ट लुकअप है जो मोड सेट होने से पहले होता है), लेकिन बाद में 'उचित' स्थानीय चर प्रसंस्करण होता है

जब चयनित मोड फ़ंक्शन को वास्तव में कहा जाता है, तो घटनाओं का एक चतुर अनुक्रम होता है जो ब्योरा देता है। इसके लिए हमें "व्युत्पन्न मोड" और "विलंब मोड हुक" के बारे में कुछ समझने की आवश्यकता है ...

व्युत्पन्न मोड, और मोड हुक

अधिकांश प्रमुख तरीकों को मैक्रो define-derived-mode साथ परिभाषित किया गया define-derived-mode । (बेशक, बस आपको लिखने से रोकना (defun foo-mode ...) और जो कुछ भी आप चाहते हैं, लेकिन अगर आप यह सुनिश्चित करना चाहते हैं कि आपका प्रमुख मोड पूरी तरह से एमाक्स के साथ अच्छी तरह से चलाता है, तो आप मानक मैक्रोज़ का उपयोग करेंगे ।)

जब आप किसी व्युत्पन्न विधि को परिभाषित करते हैं, तो आपको उस मूल मोड को निर्दिष्ट करना होगा जो इसे से प्राप्त होता है । यदि मोड में कोई तार्किक अभिभावक नहीं है, तो आप इसे परिभाषित करने के लिए अभी भी इस मैक्रो का उपयोग करते हैं (सभी मानक लाभ प्राप्त करने के लिए), और आप केवल माता-पिता के लिए nil निर्दिष्ट करते हैं वैकल्पिक रूप से आप fundamental-mode को अभिभावक के रूप में निर्दिष्ट कर सकते हैं, क्योंकि प्रभाव बहुत ही nil के समान है, क्योंकि हम क्षणिक रूप से देखेंगे।

define-derived-mode तब मानक टेम्पलेट का उपयोग करने के लिए आपके लिए विधा फ़ंक्शन को परिभाषित करता है, और सबसे पहली बात यह होती है कि जब मोड फ़ंक्शन कहा जाता है:

(delay-mode-hooks
  (PARENT-MODE)
  ,@body
  ...)

या अगर कोई माता पिता सेट नहीं है:

(delay-mode-hooks
  (kill-all-local-variables)
  ,@body
  ...)

fundamental-mode ही कॉल (kill-all-local-variables) और तब तत्काल देता है जब इस स्थिति में बुलाया जाता है, माता-पिता के रूप में निर्दिष्ट करने का प्रभाव समतुल्य है यदि माता-पिता nil

ध्यान दें कि kill-all-local-variables कुछ भी करने से पहले change-major-mode-hook चलाता है, ताकि यह पहला हुक हो जो इस पूरे अनुक्रम के दौरान चलाया जाता है (और यह तब होता है जब पिछले प्रमुख मोड अभी भी सक्रिय है, इससे पहले नए मोड के लिए किसी भी कोड का मूल्यांकन किया गया है)।

तो ऐसा पहली बात है जो होता है। बहुत ही आखिरी बात यह है कि मोड फ़ंक्शन, अपने MODE-HOOK वैरिएबल के लिए (run-mode-hooks MODE-HOOK) कॉल करने के लिए है (यह चर नाम शाब्दिक रूप से मोड फ़ंक्शन के प्रतीक का नाम -hook प्रत्यय के साथ होता है)।

इसलिए यदि हम child-mode मोड नामक एक मोड को मानते हैं जो parent-mode से उत्पन्न होता parent-mode जो कि grandparent-mode से प्राप्त होता grandparent-mode , हम कॉल करते समय घटनाओं की पूरी श्रृंखला (child-mode) कुछ ऐसा दिखता है:

(delay-mode-hooks
  (delay-mode-hooks
    (delay-mode-hooks
      (kill-all-local-variables) ;; runs change-major-mode-hook
      ,@grandparent-body)
    (run-mode-hooks 'grandparent-mode-hook)
    ,@parent-body)
  (run-mode-hooks 'parent-mode-hook)
  ,@child-body)
(run-mode-hooks 'child-mode-hook)

delay-mode-hooks क्या करता है? यह केवल चर delay-mode-hooks बांधता है, जिसे run-mode-hooks द्वारा चेक किया जाता है जब यह चर गैर- nil , तो run-mode-hooks किसी भविष्य के समय में चलने वाले हुकों की सूची में अपनी तर्क को दबा देता है, और तुरंत वापस देता है

केवल जब delay-mode-hooks nil run-mode-hooks वास्तव में हुक चलाएंगे। उपरोक्त उदाहरण में, यह तब तक नहीं है जब तक (run-mode-hooks 'child-mode-hook) कहा जाता है।

(run-mode-hooks HOOKS) के सामान्य मामले के लिए, निम्नलिखित हुक क्रम में चलते हैं:

  • change-major-mode-after-body-hook
  • delayed-mode-hooks (अनुक्रम में जिसमें वे अन्यथा चलेंगे)
  • run-mode-hooks ( run-mode-hooks का तर्क है)
  • after-change-major-mode-hook

इसलिए जब हम (child-mode) कॉल करते हैं, तो पूर्ण अनुक्रम होता है:

(run-hooks 'change-major-mode-hook) ;; actually the first thing done by
(kill-all-local-variables)          ;; <-- this function
,@grandparent-body
,@parent-body
,@child-body
(run-hooks 'change-major-mode-after-body-hook)
(run-hooks 'grandparent-mode-hook)
(run-hooks 'parent-mode-hook)
(run-hooks 'child-mode-hook)
(run-hooks 'after-change-major-mode-hook)

स्थानीय चर पर वापस ...

जो हमें after-change-major-mode-hook वापस लाता है और इसे hack-local-variables कॉल करने के लिए उपयोग करता है:

(add-hook 'after-change-major-mode-hook 'hack-local-variables)

अब हम स्पष्ट रूप से देख सकते हैं कि अगर हम ऐसा करते हैं, तो नोट के दो संभावित अनुक्रम हैं:

  1. हम मैन्युअल रूप से foo-mode बदल जाते हैं:

    (foo-mode)
     => (kill-all-local-variables)
     => [...]
     => (run-hooks 'after-change-major-mode-hook)
         => (hack-local-variables)
  2. हम उस फ़ाइल पर जाते हैं जिसके लिए foo-mode स्वचालित पसंद है:

    (normal-mode)
     => (set-auto-mode)
         => (foo-mode)
             => (kill-all-local-variables)
             => [...]
             => (run-hooks 'after-change-major-mode-hook)
                 => (hack-local-variables)
     => (hack-local-variables)

क्या यह एक समस्या है कि hack-local-variables दो बार चलाता है? शायद शायद नहीं। कम से कम यह थोड़ा अक्षम है, लेकिन संभवतः ज्यादातर लोगों के लिए यह एक महत्वपूर्ण चिंता नहीं है मेरे लिए, मुख्य बात यह है कि मैं हमेशा इस परिस्थिति पर भरोसा नहीं करना चाहता कि सभी परिस्थितियों में हमेशा अच्छा रहा, क्योंकि यह निश्चित रूप से अपेक्षित व्यवहार नहीं है

(निजी तौर पर मैं वास्तव में इसे कुछ विशिष्ट मामलों में होने के कारण होता है, और यह सिर्फ ठीक काम करता है, लेकिन निश्चित रूप से उन मामलों को आसानी से परीक्षण किया जाता है - जबकि यह मानक के रूप में करते हुए सभी मामलों पर असर पड़ता है, और परीक्षण अव्यवहारिक है।)

इसलिए मैं तकनीक के लिए एक छोटे से ट्वीक का प्रस्ताव दूंगा, ताकि फ़ंक्शन में हमारी अतिरिक्त कॉल न हो तो normal-mode निष्पादित हो रहा हो:

(defvar my-hack-local-variables-after-major-mode-change t
  "Whether to process local variables after a major mode change.
Disabled by advice if the mode change is triggered by `normal-mode',
as local variables are processed automatically in that instance.")

(defadvice normal-mode (around my-do-not-hack-local-variables-twice)
  "Prevents `after-change-major-mode-hook' from processing local variables.
See `my-after-change-major-mode-hack-local-variables'."
  (let ((my-hack-local-variables-after-major-mode-change nil))
    ad-do-it))
(ad-activate 'normal-mode)

(add-hook 'after-change-major-mode-hook 
          'my-after-change-major-mode-hack-local-variables)

(defun my-after-change-major-mode-hack-local-variables ()
  "Callback function for `after-change-major-mode-hook'."
  (when my-hack-local-variables-after-major-mode-change
    (hack-local-variables)))

इससे नुकसान?

सबसे महत्वपूर्ण बात यह है कि आप किसी बफर के मोड को बदल नहीं सकते जो एक स्थानीय चर का उपयोग करके इसका प्रमुख मोड सेट करता है। या बल्कि, स्थानीय चर प्रसंस्करण के परिणामस्वरूप इसे तुरंत वापस बदला जाएगा।

यह मुकाबला करना असंभव नहीं है, लेकिन मैं इस क्षण के दायरे से इसे कॉल करने जा रहा हूं :)


चेतावनी दी है कि मैंने यह कोशिश नहीं की है , इसलिए यह आपके डीआईआर-लोकल वेरिएबल्स से लेकर अवांछित परिणाम उत्पन्न कर सकता है, जो आपकी बिल्ली को गड़बड़ने का प्रयास करने के लिए इमैक को लागू नहीं किया जा रहा है; एएमएसीएस को कैसे व्यवहार करना चाहिए, इसकी किसी भी समझदार परिभाषा से, यह लगभग निश्चित रूप से धोखाधड़ी है दूसरी ओर, यह सभी मानक पुस्तकालय में है, इसलिए यह बहुत अधिक पाप नहीं हो सकता। (मुझे उम्मीद है।)

निम्नलिखित का मूल्यांकन करें:

(add-hook 'after-change-major-mode-hook
          'hack-dir-local-variables-non-file-buffer)

तब से, जब आप बड़े मोड बदलते हैं, तो डीआईआर-लोकल वेरिएबल्स (मुझे लगता है) को बदलने के तुरंत बाद पुनः उपयोग किया जाना चाहिए।

अगर यह काम नहीं कर रहा है या आपको इसे पसंद नहीं है, तो आप 'एडे-हुक' को 'हटा-हुक' के साथ बदलकर और फॉर्म का मूल्यांकन करने के लिए इंपिक्स को पुन: प्रारंभ किए बिना इसे पूर्ववत कर सकते हैं।





elisp