2012-04-23 70 views
4

我正在寫基於comint模式的派生模式。該模式是一個命令行程序(GRASSIS)的接口,並且comint模式完成對程序起作用。我試圖通過completion-at-point-functions添加對完成程序參數的支持。一個玩具的例子是:Emacs完成點功能

(setq my-commands 
     '(("ls" 
     ("my-completion-1") 
     ("my-completion-2")) 
     ("mv" 
     ("my-completion-3") 
     ("my-completion-4")))) 


(defun my-completion-at-point() 
    (interactive) 
    (let ((pt (point)) ;; collect point 
     start end) 

    (save-excursion ;; collect the program name 
     (comint-bol) 
     (re-search-forward "\\(\\S +\\)\\s ?")) 
    (if (and (>= pt (match-beginning 1)) 
      (<= pt (match-end 1))) 
     () ;; if we're still entering the command, pass completion on to 
     ;; comint-completion-at-point by returning nil 

     (let ((command (match-string-no-properties 1))) 
     (when (member* command my-commands :test 'string= :key 'car) 
      ;; If the command is one of my-commands, use the associated completions 
      (goto-char pt) 
      (re-search-backward "\\S *") 
      (setq start (point)) 
      (re-search-forward "\\S *") 
      (setq end (point)) 
      (list start end (cdr (assoc command my-commands)) :exclusive 'no)))))) 

(push 'my-completion-at-point completion-at-point-functions) 

這幾乎奏效。我正常完成程序名稱。但是,如果我在命令行中輸入了ls,那麼按標籤插入my-completion-並且不提供這兩個選項。再次點擊標籤第二次插入my-completion-,以便我現在有ls my-completion-mycompletion-

我的實際代碼包含幾行代碼來檢查多行命令,但不會更改完成代碼。使用這個版本的代碼,我打開一個以my-commands中的程序名稱之一開頭的行上的選項卡,向其提供可能的參數列表以完成該命令,但沒有任何內容插入到緩衝區中,並且列表通過輸入參數的前幾個字母不會縮小。

我已經完成了手冊,但我無法弄清楚編寫completion-at-point函數的正確方法。任何想法我失蹤?

我已經簡單地看過pcomplete,但是沒有真正理解'文檔',並沒有取得任何進展。

回答

7

該問題似乎與您發現startend的方式在點處返回參數的邊界。我沒有花足夠多的時間調試它以確保細節,但是我認爲如果您以交互方式調用函數,則會看到它返回的值爲startend,這意味着完成UI不會知道在點使用參數從完成表中選擇您已通過它。

改變你的函數下面的最後一部分似乎是一個修復:

(when (member* command my-commands :test 'string= :key 'car) 
    ;; If the command is one of my-commands, use the associated completions 
    (goto-char pt) 
    (let ((start 
     (save-excursion 
      (skip-syntax-backward "^ ") 
      (point)))) 

    (list start pt (cdr (assoc command my-commands)) :exclusive 'no))))))) 

時爲completion-at-point-functions元素添加這給了預期的效果。

在這裏,我用skip-syntax-backward而不是正則表達式搜索,我認爲這是比較慣用的Elisp這種東西。它只是說將點向後移動到不在語法類「空白」中的任何東西。 skip-syntax函數返回移動的距離而不是點的值,所以我們必須在save-excursion結尾添加一個調用point

如果確實在這樣的函數中使用了regexp搜索,通常會將t作爲第四個參數noerror,以便它在用戶匹配失敗時不會將錯誤傳遞給用戶。這確實意味着你必須自己檢查返回值是否爲nil

最後,push,而不是添加完成功能,您可能需要使用add-hook如下:

(add-hook 'completion-at-point-functions 'my-completion-at-point nil t) 

這確實兩個有用的東西:它會檢查你的功能是否已經在鉤加入之前,和(通過傳遞t作爲第四個參數,local),它僅將該函數添加到完成點掛接的緩衝區本地值。這幾乎可以肯定是你想要的,因爲當你按下TAB鍵時,你不希望在所有其他Emacs緩衝區中使用這些完成項。

+0

如果我有一個以上的投票,你會得到一個!感謝您的回答。在我閱讀文檔時,向後搜索應該在與skip-syntax-back相同的位置留下點,但顯然它不是。我也開始使用加載,但功能名稱反轉和不正確的引用。現在一切正常。再次感謝! – Tyler 2012-04-23 11:46:58

+0

很高興這有幫助!考慮到這一點,我懷疑「向後搜索」的問題與反向搜索時正則表達式操作符的貪婪有關。當然''\\ S *「'與空字符串匹配,但即使用'+'替換'*',它仍然只會返回一個非空格字符並停止。 – 2012-04-23 13:06:20