Emacs Lisp: How to Write Keyword Completion Command

By Xah Lee. Date: . Last updated: .

This page shows you how to implement keyword completion in emacs.

Problem

xlsl-keyword completion
Keyword completion in emacs.

Solution

Suppose your language xyz has the following list of keywords.

;; this is your lang's keywords
(setq xyz-kwdList
      '("touch"
       "touch_start"
       "touch_end"
       "for"
       "foreach"
       "forall"
       ))

The following is the code that does the completion.

(defun xyz-complete-symbol ()
  "Perform keyword completion on word before cursor."
  (interactive)
  (let ((posEnd (point))
        (meat (thing-at-point 'symbol))
        maxMatchResult)

    ;; when nil, set it to empty string, so user can see all lang's keywords.
    ;; if not done, try-completion on nil result lisp error.
    (when (not meat) (setq meat ""))
    (setq maxMatchResult (try-completion meat xyz-kwdList))

    (cond ((eq maxMatchResult t))
          ((null maxMatchResult)
           (message "Can't find completion for “%s”" meat)
           (ding))
          ((not (string= meat maxMatchResult))
           (delete-region (- posEnd (length meat)) posEnd)
           (insert maxMatchResult))
          (t (message "Making completion list…")
             (with-output-to-temp-buffer "*Completions*"
               (display-completion-list
                (all-completions meat xyz-kwdList)
                meat))
             (message "Making completion list…%s" "done")))))

Now, to test it, just set a temp key for easy call:

(global-set-key (kbd "<f7>") 'xyz-complete-symbol)

Then, open a new buffer, type any letter, say “t”, then press F7, type some more letter, press F7 again.

The above code is very easy to understand. First, you grab the word before cursor, save it as “meat”. Then, you find the maximal match, save it as maxMatchResult. Then, we have a few cases:

Lucky for us, emacs does most of the tedious job. The core functions that do the job is try-completion, all-completions, display-completion-list.

In the above, we used a simple list for our keywords, and fed them to emacs's completion functions. Emacs's completion functions can also take keyword argument in the form of a alist or hashtable. A alist looks like this:

(setq xyz-kwdList
 '(("touch" . nil)
   ("touch_start" . nil)
   ("touch_end" . nil)))

The keyword list can also be a hash table. See: Emacs Lisp Tutorial: Hash Table.

(info "(elisp) Completion")

Using ido for Completion

A alternative mechanism of completion is to use ido-mode's interface.

emacs keyword completion ido
emacs keyword completion with ido

Here's a example.

;; this is your lang's keywords
(setq abc-kwdList
      '("touch"
       "touch_start"
       "touch_end"
       "for"
       "foreach"
       "forall"
       ))

(defun abc-complete-symbol ()
  "Perform keyword completion on current symbol.
This uses `ido-mode' user interface for completion."
  (interactive)
  (let* (
         (-bds (bounds-of-thing-at-point 'symbol))
         (-p1 (car -bds))
         (-p2 (cdr -bds))
         (-current-sym
          (if  (or (null -p1) (null -p2) (equal -p1 -p2))
              ""
            (buffer-substring-no-properties -p1 -p2)))
         -result-sym)
    (when (not -current-sym) (setq -current-sym ""))
    (setq -result-sym
          (ido-completing-read "" abc-kwdList nil nil -current-sym ))
    (delete-region -p1 -p2)
    (insert -result-sym)))

This is much more convenient.

Like it? Buy Xah Emacs Tutorial. Thanks.

or, buy something from my keyboard store.