etags as xref backend functions
About
I built the TAGS file in some open source projects but Emacs's
xref-find-definitions was failing me.
I remembered that I had encountered this issue a few weeks ago and completely forgot my learnings.
I'm going to be documenting them here now in hopes that I'll remember for next time.
Debugging
I authored a very bare c program and built the TAGS file. Emacs was successfully finding the definitions. So what's up?
I found out that it was still finding the definitions when the TAGS file was not present!
When I spun up Emacs without any configuration, it was only working when the TAGS file was present. This is the expected behavior!
Findings
There's a variable, xref-backend-functions, that configures the choice of
backends for Emacs.
On Spacemacs, it seems that it's default value is (dumb-jump-xref-activate
etags--xref-backend).
The dumb-jump-xref-activate function is being matched instead of the etags–xref-backend one!
Configuration
This can be fixed by reconfiguring xref-backend-functions. When
emacs-lisp-mode is used, that variable is locally configured:
(define-derived-mode emacs-lisp-mode lisp-data-mode
;; ...
(add-hook 'xref-backend-functions #'elisp--xref-backend nil t)
;; ...
)
In the end, we end up with this value: (elisp--xref-backend t).
Roadblocks
When we run the same function but with etags--xref-backend as an argument, we
end up with a list where etags--xref-backend is at the end: (t
etags--xref-backend).
Consequently, t will be matched first and t acts as a flag to use the global
xref-backend-functions variable. Why is this?
It's because etags--xref-backend is already added to the hooks, and cached a
depth of 90. Hint: The depth acts as a way to order the functions.
;; xref.el
(add-hook 'xref-backend-functions #'etags--xref-backend t) ;; `t' configures depth.We can confirm it's depth by getting the properties of the symbol:
(symbol-value (get 'xref-backend-functions 'hook--depth-alist))Solution #1
How can we solve this then? Add a hook with an earlier depth!?
(add-hook 'c-mode-hook (lambda ()
(add-hook 'xref-backend-functions #'etags--xref-backend -1 t)))But this will override it's depth for everyone:
(symbol-value (get 'xref-backend-functions 'hook--depth-alist)) ;; we will see -1 even outside of c-mode.Solution #2
Just configure it authoratively in a local buffer to avoid add-hook injecting
a t flag.
Also, I'm configuring c-mode using a personal spacemacs layer. Since the
c-mode is not a package by itself, I create an init function for cc-mode.
(defun augustfengd/init-cc-mode ()
(add-hook 'c-mode-hook (lambda ()
(make-local-variable 'xref-backend-functions)
(set 'xref-backend-functions '(etags--xref-backend)))))