I’ve been using company mode, which is a great completion framework for years now, and in genreal, it’s phenomenal. For a while, however, I’ve had the feeling that I’m not getting completion options at exactly the right frequency that I’d like. And a completion framework that’s a bit sluggish or that won’t offer completions that you’d expect is a drag. I dug in a bit, and got a much better, because of some poor configuration choices I’d made, and I thought I write up my configuration.
Backend Configuration
Company allows for configurable backends, which are just functions that
provide completions, many of which are provided in the main company
package, but also provided by many third (fourth?) party packages. These
backends, then, are in a list which is stored in the company-backends
,
that company uses to try and find completions. When you get to a moment
when you might want to complete things, emacs+company iterate through
this list and build a list of expansions. This is pretty straight
forward, at least in principle.
Now company is pretty good at making these backends fast, or trying,
particularly when the backend might be irrelevant to whatever you’re
currently editing--except in some special cases--but it means that the
order of things in the list matters sometimes. The convention for
configuring company backends is to load the module that provides the
backend and then push
the new backend onto the list. This mostly works
fine, but there are some backends that either aren’t very fast or have
the effect of blocking backends that come later (because they’re
theoretically applicable to all modes.) These backends to be careful of
are: company-yasnippet, company-ispell, and company-dabbrev.
I’ve never really gotten company-ispell to work (you have to configure
a wordlist,) and I’ve never been a dabbrev user, but I’ve definitely
made the mistake to putting the snippet expansion near the front of the
list rather than the end. I’ve been tweaking things recently, and have
settled on the following value for company-backends
: :
(setq company-backends '(company-capf
company-keywords
company-semantic
company-files
company-etags
company-elisp
company-clang
company-irony-c-headers
company-irony
company-jedi
company-cmake
company-ispell
company-yasnippet))
The main caveat is that everything has to be loaded or have autoloads registered appropriately, particularly for things like jedi (python,) clang, and irony. The “capf” backend is the integration with emacs' default completion-at-point facility, and is the main mechanism by which lap-mode interacts with company, so it’s good to keep that at the top.
Make it Fast
I think there’s some fear that a completion framework like company could impact the perceived responsiveness of emacs as a whole, and as a result there are a couple of knobs for how to tweak things. Having said that, I’ve always run things more aggressively, because I like seeing possible completions fast, and I’ve never seen any real impact on apparent performance or battery utilization. use these settings: :
(setq company-tooltip-limit 20)
(setq company-show-numbers t)
(setq company-idle-delay 0)
(setq company-echo-delay 0)
Configure Prompts
To be honest, I mostly like the default popup, but it’s nice to be able to look at more completions and spill over to helm when needed. It’s a sometimes thing, but it’s quite nice: :
(use-package helm-company
:ensure t
:after (helm company)
:bind (("C-c C-;" . helm-company))
:commands (helm-company)
:init
(define-key company-mode-map (kbd "C-;") 'helm-company)
(define-key company-active-map (kbd "C-;") 'helm-company))
Full Configuration
Some of the following is duplicated above, but here’s the full configuration that I run with: :
(use-package company
:ensure t
:delight
:bind (("C-c ." . company-complete)
("C-c C-." . company-complete)
("C-c s s" . company-yasnippet)
:map company-active-map
("C-n" . company-select-next)
("C-p" . company-select-previous)
("C-d" . company-show-doc-buffer)
("M-." . company-show-location))
:init
(add-hook 'c-mode-common-hook 'company-mode)
(add-hook 'sgml-mode-hook 'company-mode)
(add-hook 'emacs-lisp-mode-hook 'company-mode)
(add-hook 'text-mode-hook 'company-mode)
(add-hook 'lisp-mode-hook 'company-mode)
:config
(eval-after-load 'c-mode
'(define-key c-mode-map (kbd "[tab]") 'company-complete))
(setq company-tooltip-limit 20)
(setq company-show-numbers t)
(setq company-dabbrev-downcase nil)
(setq company-idle-delay 0)
(setq company-echo-delay 0)
(setq company-ispell-dictionary (f-join tychoish-config-path "aspell-pws"))
(setq company-backends '(company-capf
company-keywords
company-semantic
company-files
company-etags
company-elisp
company-clang
company-irony-c-headers
company-irony
company-jedi
company-cmake
company-ispell
company-yasnippet))
(global-company-mode))
(use-package company-quickhelp
:after company
:config
(setq company-quickhelp-idle-delay 0.1)
(company-quickhelp-mode 1))
(use-package company-irony
:ensure t
:after (company irony)
:commands (company-irony)
:config
(add-hook 'irony-mode-hook 'company-irony-setup-begin-commands))
(use-package company-irony-c-headers
:ensure t
:commands (company-irony-c-headers)
:after company-irony)
(use-package company-jedi
:ensure t
:commands (company-jedi)
:after (company python-mode))
(use-package company-statistics
:ensure t
:after company
:config
(company-statistics-mode))