2012-04-23 58 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