Navigating Julia source code files using Xref in Emacs

In recent versions of Emacs, two packages (maybe more) have been incorporated to help with writing code. These are Eglot (aka Emacs Polyglot) and tree-sitter (Emacs specific description). I've not used either of these so will not try to explain what they can do.

Xref

I'm old skool and use a system that has been part of Emacs for at least a decade1, xref. The xref code describes itself as

[providing] a somewhat generic infrastructure for cross referencing commands, in particular "find-definition".

This capability is pretty much all that I need an editor to give me when editing source code. If you want more, the packages mentioned above may be more suitable.

Xref allows you to find definitions of variables, types, and functions. For instance, consider the code in this screenshot:

20250627-xref-starting-point.png

Point is indicated by the solid light blue block cursor on the first line and is on the Julia data type Layout. The numbers on the left are line numbers, displayed by Emacs using display-line-numbers-mode with display-line-numbers-type set to 'visual which shows the current line number with other lines relative to it.

Invoking xref-find-definitions pops up a buffer which looks like

20250627-xref-popup-buffer.png

This buffer shows to possible definitions for Layout, one as a struct in a file called QTHEN.jl and the other as a module in a file called Layout.jl. Hitting RET on any of the specific lines of code in this buffer will navigate to the actual line in the source file:

20250627-xref-definition.png

At this point, xref-go-back will go back to where xref-find-definitions was invoked. The history of jumps is recorded and both xref-go-back and xref-go-forward will allow you to traverse this history.2 This feature is key to the use of Xref: being able to jump back and forward in references means you do not lose your place in whatever code you are writing. See this recent post by Chris Maiorana.

Tags

For xref to work, you have to define tags for all of the source code you want considered by xref. For a description of tags, check out the Xref part of the Emacs manual.

Emacs comes with the etags program which extracts information from source files to create a tags table, usually in a file called TAGS. Unfortunately, etags does not have specifications for the Julia language. Luckily, there is an extended version of etags, called exuberant-ctags (at least on Debian) which can be made to understand Julia code by creating a language file using the julia-ctags package3.

Most documentation assumes that you have a single tags table for all your code. This can be useful, in that you can find anything in your code base with it, but sometimes you would rather have a more discriminating search. For instance, when looking for the definition of a symbol in a Julia package, I would like Emacs to first look for such a symbol in the current package and only look in other packages if the symbol is not present locally. Luckily, Emacs has a variable, tags-table-list, which specifies the set of directories in which to look for tags files, with the search considering the directories in turn until the desired symbol is found.

I use the following shell script to periodically (via cron) update all my tags tables. I have a tags table for each Julia package, all of which are siblings of each other in a [...]/research/julia directory, a tags table the combines all the symbols from all of my Julia packages, and then a tags table that also includes the symbols defined in the base Julia system.

#!/bin/sh -f
#
# generate tags, especially for Julia projects.  For each project, we
# generate a project specific TAGS file.  We also generate one for all
# projects together.  These different tags files will be used by Emacs
# by defining the `tags-table-list` variable.

ctags="${HOME}/synced/emacs/julia-ctags/ctags"
myjuliapackages="${HOME}/synced/research/julia"
#
# generate full set of tags:
echo "Generating full set of tags in ${myjuliapackages}"
ctags-exuberant -R -e --options=${ctags} --totals=yes -f ${myjuliapackages}/TAGS ${myjuliapackages}
# now for each project
for file in $(ls -1 ${myjuliapackages})
do
    project="${myjuliapackages}/${file}"
    echo "Checking project ${project}"
    if [ -d ${project} ]
    then
        echo "Processing project ${project}:"
        ctags-exuberant -R -e --options=${ctags} --totals=yes -f ${project}/TAGS ${project}
    fi
done
# generate set of tags for the full Julia system
echo "Processing full Julia base package:"
juliabase="${HOME}/git/julia/base"
ctags-exuberant -R -e --options=${ctags} --totals=yes -f ${juliabase}/TAGS ${juliabase}

The default value of tags-table-list for me is

("~/synced/research/julia/TAGS" "~/git/julia/base/TAGS")

which includes first the tags file for all of my Julia packages combined and then the tags for the Julia system itself. For any project where I would like the search to consider symbols in that package initially before looking in all the other packages, I define a local variable:

# Local Variables:
# tags-table-list: ("../TAGS" "../../TAGS" "~/git/julia/base/TAGS")
# End:

noting that the tags file for a specific package is in the parent directory for the actual source code due to Julia coding conventions.

Blog navigation

Previous: Calling python code from Julia for objective function in optimization
Index

You can find me on Mastodon should you wish to comment on this entry or the whole blog.

References

Footnotes:

1

probably longer but the oldest entry in the git revision history for xref.el is from December 2014

2

In general, I avoid specifying the key bindings for commands as my own Emacs configuration is highly customised. Most of the commands I mention do have default bindings and you can use the where-is command (bound by default to C-h w) to find where any command is bound to.

Author: Professor Eric S Fraga

Created: 2025-07-17 Thu 11:00

Validate