Emacs完成点功能

Tyl*_*ler 4 emacs elisp tab-completion

我正在编写基于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)
Run Code Online (Sandbox Code Playgroud)

这几乎可行.我正常完成程序名称.但是,如果我ls在命令行输入,则按Tab键插入my-completion-并且不提供两个选项.点击标签再次插入my-completion-第二次,所以我现在有ls my-completion-mycompletion-.

我的实际代码包含几行来检查多行命令,但不会对完成代码进行任何更改.使用这个版本的代码,我点击了一个以一个程序名称开头的行上的选项卡,我看到了一个my-commands完成命令的可能参数列表,但没有在缓冲区中插入,列表也是不要通过输入参数的前几个字母来缩小范围.

我已经阅读了手册,但我无法弄清楚编写completion-at-point函数的正确方法.我缺少什么想法?

我已经简要地看了一下pcomplete,但是并没有真正理解'文档',并没有取得任何进展.

Jon*_* O. 7

问题似乎在于你找到的方式,startend在点上返回参数的边界.我没有花太多时间来调试它以确保细节,但我认为如果你以交互方式调用该函数,你会看到它为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)))))))
Run Code Online (Sandbox Code Playgroud)

当作为元素添加时,这给出了预期的结果completion-at-point-functions.

在这里,我使用的skip-syntax-backward不是正则表达式搜索,我认为这种情况稍微更为惯用的Elisp.它只是说要向后移动任何不在语法类"空白"中的东西.skip-syntax函数返回移动的距离而不是point的值,因此我们必须point在save-excursion的末尾添加一个调用.

如果你在这样的函数中使用regexp搜索,那么传递t第四个参数通常是个好主意noerror,这样如果它不匹配就不会将错误传递给用户.这确实意味着您必须自己检查返回值是否为nil.

最后,push您可能希望使用add-hook以下内容来添加完成功能:

(add-hook 'completion-at-point-functions 'my-completion-at-point nil t)
Run Code Online (Sandbox Code Playgroud)

这有两个有用的东西:它在添加它之前检查你的函数是否已经在钩子中,并且(通过传递t第四个参数local)它只将函数添加到完成点挂钩的缓冲区本地值.这几乎可以肯定你想要的,因为你不想在按TAB键时在每个其他Emacs缓冲区中使用这些完成.