2013-10-11 67 views
3

在emacs 24中,set-temporary-overlay-map激活一個鍵映射,只要用戶按下該鍵映射中未定義的鍵,該鍵映射就會失效。手動退出臨時覆蓋圖

我需要手動鈍化疊加鍵盤映射,但沒有提供特定的功能。我偷看到源代碼:

(defun set-temporary-overlay-map (map &optional keep-pred) 
    "Set MAP as a temporary keymap taking precedence over most other keymaps. 
Note that this does NOT take precedence over the \"overriding\" maps 
`overriding-terminal-local-map' and `overriding-local-map' (or the 
`keymap' text property). Unlike those maps, if no match for a key is 
found in MAP, the normal key lookup sequence then continues. 

Normally, MAP is used only once. If the optional argument 
KEEP-PRED is t, MAP stays active if a key from MAP is used. 
KEEP-PRED can also be a function of no arguments: if it returns 
non-nil then MAP stays active." 
    (let* ((clearfunsym (make-symbol "clear-temporary-overlay-map")) 
     (overlaysym (make-symbol "t")) 
     (alist (list (cons overlaysym map))) 
     (clearfun 
      ;; FIXME: Use lexical-binding. 
      `(lambda() 
      (unless ,(cond ((null keep-pred) nil) 
          ((eq t keep-pred) 
          `(eq this-command 
            (lookup-key ',map 
               (this-command-keys-vector)))) 
          (t `(funcall ',keep-pred))) 
       (set ',overlaysym nil) ;Just in case. 
       (remove-hook 'pre-command-hook ',clearfunsym) 
       (setq emulation-mode-map-alists 
        (delq ',alist emulation-mode-map-alists)))))) 
    (set overlaysym overlaysym) 
    (fset clearfunsym clearfun) 
    (add-hook 'pre-command-hook clearfunsym) 
    ;; FIXME: That's the keymaps with highest precedence, except for 
    ;; the `keymap' text-property ;-(
    (push alist emulation-mode-map-alists))) 

我推測,滅活當前覆蓋鍵盤映射的機制如下:

  1. 函數clearfun定義每個命令之前運行,檢查是否之前調用的命令在地圖中。
  2. 如果在地圖上沒有,下面的代碼被執行:

(爲什麼沒有這種格式正確好了,現在呢?)

(set ',overlaysym nil) ;Just in case. 
       (remove-hook 'pre-command-hook ',clearfunsym) 
       (setq emulation-mode-map-alists 
        (delq ',alist emulation-mode-map-alists)) 

因此,我真的想要使用適當的變量來執行上面的代碼。但是這個代碼是閉包的一部分,並且我在確定閉包內部的值如overlaysym,clearfunsym,alist時遇到問題。我試着尋找clearfunsymeval -ing pre-command-hook,但奇怪的是沒有任何東西(除了另一個無關的鉤子)。

我試着重新評估函數defintion和edebugging它,並且在(add-hook 'pre-command-hook clearfunsym),pre-command-hook仍然是nil之後我忘記了,這讓我很困惑。我將繼續深入研究源代碼,也許我會重寫自己的這個函數版本,以便生成force-clear函數,稍後可以調用它,但也許有人可以看到更清晰的解決方案。

+0

大問題(S)。期待來自@Stefan(或者其他人)的有趣和翔實的答案。期待更多地瞭解這一點,我自己。 – Drew

+0

謝謝,德魯,雖然我很失望,因爲我認爲可以回答所有與emacs/elisp相關的問題。我現在要回到這個問題上來,我可能會重寫這個函數,給我一個我可以稍後使用的'force-clear',但我絕對認爲可以對標準版本進行一些改進。 – erjoalgo

+0

請解釋你的意思是「我需要手動停用疊加鍵盤映射表」。 – Stefan

回答

1

您寫道:「我在確定像overlaysym這樣的值時遇到問題」 但是,會評估overlaysym。它具有值(make-symbol「t」)。 這是一個名字爲t的符號。這使得很難訪問它,但並非不可能。下列行 評價給出了註釋的結果:

(setq mysym (make-symbol "t")) 
;; t 
(set mysym 'test) 
;; test 
(symbol-value mysym) 
;; test 

這同樣適用於clearfunsym其評估以清除-臨時覆蓋地圖。

還有一條評論:當你調試set-temporary-overlay-map時,你正在按鍵。可能這些擊鍵會調用clear-temporary-overlay-map並清除預命令掛鉤?

試一下:

(defadvice set-temporary-overlay-map (after test activate) 
    (setq test-pre-command-hook pre-command-hook)) 

然後輸入文本擴展模式(C- +),並期待在測試前命令掛機。爲了在我的電腦上評估test-pre-command-hook給我以下清單:

清除臨時覆蓋圖工具提示)。

讓我們與emulation-mode-map-alists一樣。然後我們得到:

(((t keymap (67108912 . #[0 "\301\302\300!!\207" [1 text-scale-adjust abs] 3 " 
... 

(fn)" nil]) (45 . #[0 "\301\302\300!!\207" [1 text-scale-adjust abs] 3 " 
(fn)" nil])))) 

特別注意t開頭。這意味着您可以通過在開頭搜索帶有符號t的列表來查找覆蓋地圖。

類似下面的代碼片段應足以刪除疊加圖:

(when (assoc-string "t" (car emulation-mode-map-alists)) 
    (setq emulation-mode-map-alists (cdr emulation-mode-map-alists))) 

when僅僅是一個保護。 (也許,別的什麼東西已經殺死了地圖?)臨時地圖應該始終在前面(因爲set-temporary-overlay-map中的push)。有沒有什麼機會把另一個鍵盤映射到它的前面?也許,有時間控制?然後,您將需要搜索emulation-mode-map-alists爲alist與(make-symbol "t")鍵盤映射。

+0

謝謝你的回答,我很抱歉我不能我沒有早點看過,最後我重寫了這個函數以消除黑客和缺乏可讀性,另外還爲用戶提供了一個很好的'force-overlay-clear',如果它存在,它將清除當前的覆蓋。會讓一段時間過去,以便允許可能討論的問題和提供的解決方案 – erjoalgo

0

原始set-temporary-overlay-map是非常混亂,無法讀取,並依靠大量不必要的骯髒的黑客和伎倆。使用詞法綁定,我用一種更加清晰和模塊化的方式重寫了函數。

  1. 修改後的函數使用詞法綁定來代替急切懶惰的評估黑客(在這種情況下,他們是完全不必要的)。
  2. 原始功能不必要地創建用於每個功能的兩個符號,(clearfunsymclear-fun基本上是相同的東西,後者被唯一用作前者的功能細胞)
  3. 訂正功能提供了一個force-overlay-clear,這被稱爲通過clear-temporary-overlay-map預條件符合條件時(即,最後一個鍵不在疊加映射中)。如果他想手動清除這張地圖,它也可以被用戶調用。函數void自己的函數單元格,所以如果調用兩次它將會出錯。
  4. 代碼測試清楚是否適用簡化。

我無法消除極其奇怪的(overlaysym (make-symbol "t")),擔心其他代碼可能依賴於此t符號。因此,修訂後的版本幾乎可以肯定與原始版本相同。我測試了這個,它很好地工作。

(defun set-temporary-overlay-map (map &optional keep-pred) 
    (lexical-let* (
    (map map) 
    (keep-pred keep-pred) 
    (clear-temporary-overlay-map nil) 
    (force-overlay-clear nil) 
    (overlaysym (make-symbol "t")) 
    (alist (list (cons overlaysym map)))) 

    (fset 'force-overlay-clear (lambda() 
      (message "clearing overlay")    
      ;this is a copy of the original code to clear 
      (set overlaysym nil) ;Just in case. 
      (remove-hook 'pre-command-hook 'clear-temporary-overlay-map) 
      (setq emulation-mode-map-alists 
      (delq alist emulation-mode-map-alists)) 
      ;void the function cell of 'force-overlay-clear', an attempt to clear overlay twice will err 
      (fset 'force-overlay-clear nil) 
    )) 
    (fset 'clear-temporary-overlay-map (lambda() 
      (unless (cond 
     ((null keep-pred) nil) 
     (keep-pred 
      (lookup-key map (this-command-keys-vector))) 
     (t (funcall keep-pred))) 
     (force-overlay-clear) 
    ))) 

    (set overlaysym overlaysym) 
    (add-hook 'pre-command-hook 'clear-temporary-overlay-map) 
    ;; FIXME: That's the keymaps with highest precedence, except for 
    ;; the `keymap' text-property ;- 
    (push alist emulation-mode-map-alists)) 
) 
+0

@Drew,Stefan我已經看到你在這些主題中都非常活躍,所以也許你對此有一些評論 – erjoalgo

+0

'fset'全局更改該符號的定義,所以它會破壞misera當兩個'set-temporary-overlay-map'被同時使用時。 – Stefan

0

你可以做到以下幾點:

(defun my-exit-command() 
    (do-what-the-q-key-should-do)) 

....(set-temporary-overlay-map 
    my-overlay-map 
    (lambda() 
     (and (eq this-command (lookup-key my-overlay-map 
             (this-single-command-keys))) 
      (not (eq this-command 'my-exit-command))))) 
....