company-mode (short for “complete anything”) is a framework for performing completion in buffers.1 It’s an alternative to the popular
company-mode supports extension via backends which provide the framework with lists of possible completions in various contexts. So, for example, there’s a backend that provides completion support for Emacs lisp and one that does the same for Python. Backends can use very different technologies as long as they conform to the backend interface specified by the mode.
I recently decided to write a
company-mode backend for ycmd, a completion server for languages including C/C++/Objective-C and Python.2 All in all it was a relatively pain-free experience, but the process isn’t as well documented as I would have liked. So I want to use this series to describe how it’s done with the hope of making it easier for others and of helping me remember how to do it in the future.
I won’t be covering all of the details of
company-mode backends (partially because I don’t know them all), but this series should tell you what you need to know to create your own fully-armed and operational backend.3 In this article we’ll define the simplest possible backend in order to familiarize you with the concepts and infrastructure involved. In the next article we’ll add some sophistication to that backend to improve the user experience.
The simplest possible backend
For our example we need to define a source of completion candidates. Ultimately, any completion source is just a sequence of strings that meet some criteria. Examples might include:
- A list of English words starting with some prefix
- Methods for a particular object in Java
- Modules available for import in Python program
company-mode doesn’t care about the nature of these strings. It just takes them and makes it easy for the user to select from the available options.
In this case, we’ll just define a fixed list of strings:
(defconst sample-completions '("alan" "john" "ada" "don"))
That’s it.4 Completion sources don’t need to (though they generally will) be more complex than that.
Defining the backend
Backends take the form of a function which takes a command as its first argument. This command can take any of a number of values, and backends are required to respond to a handful of them. Before we get into those details, let’s look at our very basic backend implementation:
1 (require 'cl-lib) 2 (require 'company) 3 4 (defun company-sample-backend (command &optional arg &rest ignored) 5 (interactive (list 'interactive)) 6 7 (cl-case command 8 (interactive (company-begin-backend 'company-sample-backend)) 9 (prefix (and (eq major-mode 'fundamental-mode) 10 (company-grab-symbol))) 11 (candidates 12 (cl-remove-if-not 13 (lambda (c) (string-prefix-p arg c)) 14 sample-completions))))
The signature of this function is mandated by company-mode. Line 5 makes the function interactive so that you can easily drive your backend without invoking
company-mode, something we’ll do in a bit. The
cl-case statement on line 7 is where we decide what to do based on
command. In this case, we respond to
interactive command is passed once, before the other commands are used, and it is used to initialize the
company-mode infrastructure. All you need to do as a backend developer is pass your backend to
company-begin-backend as in this example.
The prefix command
prefix command is probably the most complex command to handle. This command should return the text that is to be completed. Determining this text can be complex depending on what you’re trying to complete, but
company-grab-symbol often does “the right thing” if your completion context is space-delimited.
prefix command returns
nil, this tells
company-mode that the backend is not suitable for doing completion on this context. On line 9 of our example we check to see if we’re in
fundamental-mode and, if not, return
nil. In other words, we’re saying here that our backend only applies to
fundamental-mode. Programming language-oriented backends can make a similar check for their specific modes. When a backend responds to
nil, other backends are given a chance to do the completion.
On the other hand, if a backend is appropriate for the current completion but it can’t provide any completions for some reason, the backend should return
'stop. This tells company-mode that no other backends should be used for this completion.
So our backend is effectively saying that it can do completion for anything in fundamental mode. There are more details to
prefix, but that’s covers the important parts.
The candidates commands
The response to the
candidates command is where you actually generate a list of possible completions at a point in a buffer. When this command is passed in, the
arg argument holds the value returned by
prefix. In other words, you construct your candidates based on the text that you previously indicated was to be completed.
In our case, the prefix we indicated was whatever came before
point in the buffer. To calculate our possible completions, we filter the
sample-completions values with that prefix using
remove-if-not, returning only those candidates which begin with the prefix.
As with prefix calculations, real candidate calculations can be much more complex. But if you understand how the data is piped around, then constructing these complex candidate lists should be fairly straightforward.
Test-driving the backend
To test out our backend, first enter all of the code into a buffer and evaluate it (e.g. with
M-x eval-buffer.) Then create a new buffer and run
M-x fundamental-mode and
If that works correctly, then you’ve done almost everything you need to for a fully working backend.
Plugging the backend into company-mode
The final thing you need to do to make your backend available to
company-mode is to add it the list
company-backends. One simple way to do that is with
add-to-list list this:
(add-to-list 'company-backends 'company-sample-backend)
Once you’ve done this, you can use the command
company-complete to do completions, and your new backend will be used in concert with all of the other backends in that list. Generally speaking,
company-complete is the command you’ll use for completion with
company-mode, and it’ll often be bound to a simple keystroke.
A complete company-mode backend
That’s all there is to writing a basic
company-mode backend. In the next article in this series we’ll look at adding a few more details to what we have already.
Here’s a complete listing of the code used in this article:
(require 'company) (defconst sample-completions '("alan" "john" "ada" "don")) (defun company-sample-backend (command &optional arg &rest ignored) (interactive (list 'interactive)) (case command (interactive (company-begin-backend 'company-sample-backend)) (prefix (and (eq major-mode 'fundamental-mode) (company-grab-symbol))) (candidates (remove-if-not (lambda (c) (string-prefix-p arg c)) sample-completions)))) (add-to-list 'company-backends 'company-sample-backend)