我正在编写基于comint-mode的派生模式。该模式是命令行程序(GRASS gis)的接口,并且comint模式完成适用于程序。我正试图通过completion-at-point-functions
添加对完成程序参数的支持。一个玩具的例子是:
(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
,则按Tab键插入my-completion-
并且不提供这两个选项。再次点击标签再次插入my-completion-
,以便我现在有ls my-completion-mycompletion-
。
我的实际代码包含几行来检查多行命令,但不会对完成代码进行任何更改。使用此版本的代码,我在my-commands
中以某个程序名称开头的行上按Tab键,我会看到一个可能的参数列表,用于完成命令,但没有任何内容插入缓冲区,并且不会通过键入参数的前几个字母来缩小列表。
我已经阅读了手册,但我无法找出编写completion-at-point
函数的正确方法。我缺少什么想法?
我简要地看过pcomplete
,但是他们并没有真正了解'文档',也没有取得任何进展。
答案 0 :(得分:7)
问题似乎在于您找到start
和end
以返回参数边界的方式。我没有花太多时间调试它以确保细节,但我认为如果你以交互方式调用该函数,你会看到它返回start
和end
的相同值,这个表示完成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
而不是regexp搜索,我觉得这种事情稍微有点惯用Elisp。它只是说要向后移动任何不在语法类“空白”中的东西。 skip-syntax函数返回移动的距离而不是point的值,因此我们必须在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缓冲区中使用这些完成。