How To Implement Keyword Completion in Emacs

Perm url: http://xahlee.org/emacs/elisp_keyword_completion.html

How To Implement Keyword Completion in Emacs

Xah Lee, 2009-03-06

This page shows you how to implement computer language keyword completion in emacs. You should know the basics of writing a major mode. If not, see: How To Write A Emacs Major Mode For Syntax Coloring.

The Problem

You are writing a emacs major mode for your own language. You want to have a keyword completion feature, so that user can press a key and have the word under cursor automatically expanded to the possible keywords of the language.

xlsl-keyword completion

Keyword completion in emacs.

Solution

The basic concept of keyword completion is pretty simple. You begin with a list of keywords, and you are given a string that you want to complete. You match the string against the keywords, find the maximal match, then replace the current word with tha max match. However, you will also need to popup a list of possible completions for the user to choose, and allow user some user interface conveniences such as clicking on one of the choices.

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 completion on word under cursor."
  (interactive)
  (let* (
         (cusorPoint (point))
         (meat (thing-at-point 'symbol))
         (maxMatchResult (try-completion meat xyz-kwdList))
         )
    (when meat
      (cond ((eq maxMatchResult t))
            ((null maxMatchResult)
             (message "Can't find completion for “%s”" meat)
             (ding))
            ((not (string= meat maxMatchResult))
             (delete-region (- cusorPoint (length meat)) cusorPoint)
             (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"))))
    ))

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:

  • (1) If the max match is the same as the word under cursor, then do nothing, because the word is already complete.
  • (2) If the max match is empty, then tell user there is no completion.
  • (3) If not the above two cases, then expand the current word to max match.
  • (4) Otherwise, pop up a dialog to list possible completions.

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”.

  • The “try-completion” returns the maximal match.
  • The “all-completions” returns all possible completions.
  • The “display-completion-list” takes care of the user interface for displaying the possible completions, and making them clickable.

Set a keyboard shortcut for your completion function, so that you can easily test it. e.g. “(global-set-key (kbd "<f1>") 'xyz-complete-symbol)”.

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)))

For hash, see: Elisp Lesson: Hash Table.

(info "(elisp)Completion")

Emacs is beautiful.

Popular posts from this blog

11 Years of Writing About Emacs

does md5 creates more randomness?

Google Code shutting down, future of ErgoEmacs