Adding custom completions to Company mode in Spacemacs
Inspired by A Beginner's Guide to Extending Emacs, I decided to implement my own in Spacemacs. This post navigates through the odessey of getting the capf to work with company-mode auto-completion in Spacemacs.
Writing a custom capf
My Python code often contains variables with names input_prefix
and output_prefix
. It would be super convenient to, say, type just input
and have it expanded to input_prefix
. This is quite simple to implement (as adequately described in aforementioned Beginner's Guide to Extending Emacs).
;; just define a thing
(define-thing-chars py-path-var "\\(input\\|output\\)")
;; our custom completion func
(defun my-python-prefix-capf ()
(let* ((bounds (bounds-of-thing-at-point 'py-path-var))
(start (car bounds))
(end (cdr bounds)))
(if start
(list start end (list "input_prefix" "output_prefix") '()))))
;; and make it available to the completion framework
(add-hook 'completion-at-point-functions 'my-python-prefix-capf)
Evaluate all three of these, and then M-x completion-at-point
will complete your text.
Integrating into Spacemacs's Company auto-completion
People on the interwebs say that Company interops with completion functions. This should be simple, this idea couldn't be described more succintly than karthinks:
Add the CAPF to the list of active completion-at-point-functions, then add company-capf to your company backends.
Our code goes into dotspacemacs/user-config
(defun dotspacemacs/user-config ()
(define-thing-chars py-path-var "\\(input\\|output\\)")
(defun my-python-prefix-capf ()
(let* ((bounds (bounds-of-thing-at-point 'py-path-var))
(start (car bounds))
(end (cdr bounds)))
(if start
(list start end (list "input_prefix" "output_prefix") '()))))
(add-hook 'python-mode-hook
(lambda ()
(add-hook 'completion-at-point-functions #'my-python-prefix-capf 0 t))))
and we configure the auto-completion
layer as described in Spacemacs docs
dotspacemacs-configuration-layers
'(
(auto-completion
:variables
spacemacs-default-company-backends '(company-capf))
(python
:variables
python-backend 'anaconda))
But it doesn't work auto-magically. Apparently, the custom capf needs to come first in the list for both backends to work together.
(add-hook 'python-mode-hook
(lambda ()
(add-hook 'completion-at-point-functions #'my-python-prefix-capf 0 t)
(add-hook 'company-mode-hook (lambda () (setq-local company-backends '((company-capf company-anaconda)))))))
With this, you can use completions from both the custom capf and the anaconda python backend. You can view your company config with M-x company-diag
Using more in-built completion funcs
We can combine with in-built company backends, like company-files. Company does recommend some conventions on grouping backends, but does not automatically switches to the next group when one does not provide any completions. This can be done manually via M-x company-other-backend
. I prefer to put all of them in a single group. This will have it's own disadvantages but I'll cross that bridge when I get there, I guess?
(add-hook 'python-mode-hook
(lambda ()
(add-hook 'completion-at-point-functions #'my-python-prefix-capf 0 t)
(add-hook 'company-mode-hook (lambda () (setq-local company-backends '((company-capf company-anaconda company-files)))))))