How do I add auto-completion to my custom comint mode in Emacs?

679 Views Asked by At

I'm writing a comint mode for a legacy command-line tool. I'd like to add basic auto-completion to it.

Suppose I have the following list of keywords used by the tool:

(defconst my-keywords '("export" "extract" "display"))

How would I add auto-completion to my mode, based on this list?


What I found so far: I know there are examples of this in shell.el or in comint.el, but I did not manage to understand the code well enough to answer to this basic question. I did understand though that I could build a regexp list out of my-keywords, like this:

(regexp-opt my-keywords)
;; output:
"\\(?:display\\|ex\\(?:\\(?:por\\|trac\\)t\\)\\)"

Other than that, I gathered that I could use pcomplete, or company, or both – I'm fine with any solution actually, but how do I do it?

2

There are 2 best solutions below

0
On

Thanks to ergoemacs I found a first solution :

  1. Define a completion function which returns a list of the form (start end my-keywords . nil), with start and end delimiting the entity to complete at point. I added a tweak to take into account the program prompt, for the first keyword.
  2. Add this function to completion-at-point-functions, in the definition of the mode;
  3. Add a tab shortcut to completion-at-point, in the mode-map.
(defconst my-keywords '("export" "extract" "display"))

;; 1 - custom completion function
(defun my-completion-at-point ()
  "This is the function to be used for the hook `completion-at-point-functions'."
  (interactive)
  (let* (
         (bds (bounds-of-thing-at-point 'symbol))
         (start (max (car bds) (comint-line-beginning-position)))
         (end (cdr bds)))
    (list start end xyz-keywords . nil )))

;; 2 - adding it to my-completion-at-point 
(define-derived-mode my-comint-mode comint-mode "My comint mode"
;; your  code....
(add-hook 'completion-at-point-functions 'my-completion-at-point nil 'local))

;; 3 - add a tab shortcut in the map of the mode
(defvar my-mode-map
  (let ((map (nconc (make-sparse-keymap) comint-mode-map)))
    ;; your code...
    (define-key map "\t" 'completion-at-point)
    map))

3
On

Comint also calls customizable comint-dynamic-complete-functions from its comint-completion-at-point function. Derived modes will often add functions to this hook (see shell-dynamic-complete-functions), eg.

(defconst my-keywords '("export" "extract" "display"))
(defun my-comint-dynamic-completion-function ()
  (when-let* ((bds (bounds-of-thing-at-point 'symbol))
              (beg (car bds))
              (end (cdr bds)))
    (when (> end beg)
      (list beg end my-keywords :annotation-function (lambda (_) "my-keywords")))))

(define-derived-mode my-comint-mode comint-mode "my mode"
  (add-hook 'comint-dynamic-complete-functions
            #'my-comint-dynamic-completion-function nil 'local)
  (make-local-variable 'company-backends)
  (cl-pushnew 'company-capf company-backends))

By adding company-capf to your company-backends you will additionally get company support from your completion at point function (see elisp-completion-at-point for example of additional company-specific symbols for showing help/location/etc. of completion candidates).