Ctags IDE on the True Editor!
What is it?
(If you are already familiar with Citre, see changelog for the news.)
Citre is an advanced Ctags (or actually, readtags) frontend for Emacs. It offers:
completion-at-point
, xref and imenu integration.citre-jump
: Acompleting-read
UI for jumping to definition.citre-peek
: A powerful code reading tool that lets you go down the rabbit hole without leaving current buffer.
Let's see them in action!
-
completion-at-point
, with the UI of company and Vertico:Notice the rich annotations. Candidates are annotated by
(kind/type@scope)
, so you know "it's a member of structthread
, withpid_t
type", etc. This is because Ctags "tags" format records much more abundant info than the etags "TAGS" format.Also, notice that candidates with the "member" kind are put above the others because we are in a C source file, and the current symbol is after a dot. Citre guesses that you want a struct member.
-
citre-jump
, withcompleting-read
UI provided by Selectrum: -
citre-peek
. It opens a "peek window" to show the definition of a symbol:And there's more. Notice the code reading history at the bottom of the peek window. Do you hate having to switch between a lot of buffers while reading code? With
citre-peek
, you can peek a symbol in the peek window. This allows a tree-like code reading history, that you can browse and edit, without leaving current buffer!
All above screenshots were taken in a huge project (the Linux kernel), and Citre is still fast, because readtags performes binary search on the tags file.
Besides, Citre has a GNU Global plugin that can find completions, definitions and references using xref or the above UIs. See this user manual to know about it. Citre has a pluggable backend design so you could forget about ctags and only use Citre as a GNU Global frontend.
Quick start
Prerequisites
readtags
Citre uses readtags program to read from tags files.
Citre requires readtags program provided by Universal Ctags. The minimal version is:
- commit
31d13e85
, or - weekly release p5.9.20200124.0
It's recommended to get the latest version, as Citre actively takes advantage of its latest features.
-
For GNU/Linux users: If you install ctags from your software repository, run
$ ctags --version
to see if you are using Universal Ctags. The version is a little hard to inspect since Universal Ctags doesn't have a formal version number yet. If it's compiled before Jan 21 2021, it will probably not work. You can build it yourself, or try the snap package. -
For macOS users: Follow the instructions here to install the latest version.
-
For Windows users: Download the binary here. Ctags in cygwin (or msys repo of msys2) won't work since it doesn't come with readtags. Ctags in the mingw64 repo of msys2 is Universal Ctags, but by the time of writing, it doesn't meet the version requirement.
If you don't have readtags executable in your PATH, customize
citre-readtags-program
to the path of it.
ctags
If you use Citre's built-in tools to create tags file, you need a ctags program.
The ctags program provided by Universal Ctags is still recommended. If you
don't have it in your PATH, customize citre-ctags-program
to the path of it.
You can also use other program that outputs a tags file, like
hasktags,
gotags and
ripper-tags. You don't need to customize
citre-ctags-program
when using these tools.
Installation
You can install citre
from MELPA. Below are instructions
to manually install Citre.
-
Clone this repository:
$ git clone https://github.com/universal-ctags/citre.git /path/to/citre
-
Add the path to your
load-path
in your Emacs configuration:(add-to-list 'load-path "/path/to/citre")
-
Require
citre
andcitre-config
in your configuration:(require 'citre) (require 'citre-config)
Or, you can read citre-config.el, and write your own config.
Create tags file
The customizable way
Open a file in your project, type M-x citre-update-this-tags-file
. If it
can't find a tags file for the current file, it'll guide you to generate one.
I'll take you through the simplest situation here. To know more, read this
user manual.
You are asked to:
-
Pick a place to save the tags file, type
1
. -
Pick a directory in which to use the tags file. This means when you visit a file in that directory, this tags file is used for it.
Let's genearte a tags file for the whole project and use it in the whole project, so choose
/path/to/project/
. -
Pick a tags file name. It'll be saved inside
/path/to/project/
. Pick one you like. -
Pick a root dir to run ctags. This is the working directory when running Ctags. Let's pick the project root.
-
You are taken to a command editing buffer, and a help message is shown in the buffer. Read it.
If you use Universal Ctags, you may write a command like:
ctags
-o
%TAGSFILE%
--languages=C,C++
--kinds-all=*
--fields=*
--extras=*
-R
./
/external/lib/used/in/the/project/
You can also use other ctags program, for example, gotags:
~/go/bin/gotags
-R=true
-f=%TAGSFILE%
./
/external/lib/used/in/the/project/
You get the idea.
Once you've created such a file, run M-x citre-update-this-tags-file
again to
update it. The recipe for updating a tags file is stored in the tags file
itself, so no more configuration file or buffer-local variables are needed!
You can edit the updating recipe later by citre-edit-tags-file-recipe
.
The simpler way
Citre searches the tags file in different locations, like in the directory that uses it, in global/project cache dir, etc. See this documentation to know the details.
citre-default-create-tags-file-location
lets you choose a default location.
For example, if you always want to use the global cache dir, set it to
'global-cache
.
Most of the time, people just create a tags file for the whole project and use
it in the whole project. If you want Citre to do this by default, rather than
ask you a lot of questions, set citre-use-project-root-when-creating-tags
to
t
. This uses citre-project-root-function
to detect the project root.
If you don't want to edit the command line manually, set
citre-prompt-language-for-ctags-command
to t
. Then, instead of giving you a
buffer to edit the command, Citre lets you choose the languages to scan, and
generates a command that should work for most projects. This requires the ctags
program from Universal Ctags.
In any situations, you could further edit the tags file updating recipe by
citre-edit-tags-file-recipe
later.
The command line way
You don't have to create a tags file using Citre. You can just cd
to the
project root directory and run:
$ ctags --languages=c,c++,... --kinds-all='*' --fields='*' --extras='*' -R
This creates a tags
file in the project root, and Citre could find it. To
know how Citre finds a tags file for the current buffer, see this
documentation
Tags file created this way can't be updated by Citre.
Notes
See this documentation to know more about tags file format, how to tweak the command line, how to specify which dir uses which tags file, etc.
Note: Emacs users are more familiar with the TAGS format. TAGS format is
generated by etags
or $ ctags -e
, and Citre doesn't support it.
Citre supports the tags format, which is the default format used by Ctags. Simply puts it, tags format is much more informative than TAGS format, making Citre a much more powerful tool. See this user manual for details.
Note for Windows and macOS users: Windows and macOS uses case-insensitive file system by default, so this may happen:
- You create a tags file named
tags
. - Some plugins like
projectile
andcompany
(when using thecompany-etags
backend) tries find and load aTAGS
file, which is the default file used by Emacs etags. - Since
tags
andTAGS
are the same to the file system, they tries to load thetags
file, which can't be recognised byetags.el
. - You'll see a "TAGS is not valid tags table" error.
To avoid this problem, you could configure those plugins to not use a tags
file, or simply avoid creating a tags file named tags
on Windows and macOS.
Use Citre
Use citre-mode
to enable completion-at-point
, xref and imenu integration.
If you also use company
, make sure company-capf
is in company-backends
.
By default, when you open a file, and a tags file can be found for it,
citre-mode
is automatically enabled. If you don't use citre-config
, you can
put this in your configuration:
(add-hook 'find-file-hook #'citre-auto-enable-citre-mode)
citre-jump
and citre-peek
works without citre-mode
. Type M-x citre-jump
on a symbol to jump to its definition, M-x citre-jump-back
to go back in the
jump history. About citre-peek
, See this user
manual to know how to use it.
Here's a example configuration using
use-package
. Be sure to read it
and tweak it to your own need.
(use-package citre
:defer t
:init
;; This is needed in `:init' block for lazy load to work.
(require 'citre-config)
;; Bind your frequently used commands. Alternatively, you can define them
;; in `citre-mode-map' so you can only use them when `citre-mode' is enabled.
(global-set-key (kbd "C-x c j") 'citre-jump)
(global-set-key (kbd "C-x c J") 'citre-jump-back)
(global-set-key (kbd "C-x c p") 'citre-ace-peek)
(global-set-key (kbd "C-x c u") 'citre-update-this-tags-file)
:config
(setq
;; Set these if readtags/ctags is not in your PATH.
citre-readtags-program "/path/to/readtags"
citre-ctags-program "/path/to/ctags"
;; Set these if gtags/global is not in your PATH (and you want to use the
;; global backend)
citre-gtags-program "/path/to/gtags"
citre-global-program "/path/to/global"
;; Set this if you use project management plugin like projectile. It's
;; used for things like displaying paths relatively, see its docstring.
citre-project-root-function #'projectile-project-root
;; Set this if you want to always use one location to create a tags file.
citre-default-create-tags-file-location 'global-cache
;; See the "Create tags file" section above to know these options
citre-use-project-root-when-creating-tags t
citre-prompt-language-for-ctags-command t
;; By default, when you open any file, and a tags file can be found for it,
;; `citre-mode' is automatically enabled. If you only want this to work for
;; certain modes (like `prog-mode'), set it like this.
citre-auto-enable-citre-mode-modes '(prog-mode)))
These user options are for customizing enabled backends:
citre-completion-backends
citre-find-definition-backends
citre-find-reference-backends
citre-tags-in-buffer-backends
citre-auto-enable-citre-mode-backends
Currently we have tags
and global
backends. See this
documentation to know more customizable options.
Documentations
This chapter in the developer manual talks about the strengths/weaknesses of ctags, and the design principle of Citre. Non-developers are also encouraged to read it to know more about these tools.
FAQ
-
Q: What are the advantages of Citre & Ctags over etags, gtags, language servers...
A: See this documentation.
-
Q: How to use Citre over TRAMP?
A: Make sure you've installed readtags on the remote machine, and everything will just work. Tags file generating/updating also works if you have ctags program on the remote machine.
-
Q: What to do if Citre didn't grab the right symbol for me, e.g., I want to find the definition of
foo.bar
, but can only getfoo
orbar
?A: You can select
foo.bar
first (by an active region), then find its definitions. -
Q: Why doesn't Citre support automatically update tags file?
A: Citre uses both line number and a search pattern to locate a tag. When the file containing the tag is edited, Citre could still locate the tag using the search pattern. Citre even tries to locate the tag when the line containing the tag itself is edited.
So, jumping to definition is still useable when the file is edited. There's no need to frequently update the tags file.
You may ask "what if I add new definitions, or modify/delete existing ones?" The truth is, if your codebase is reasonably large that you have to index them by Ctags, then small edits won't cause much trouble. You can just update the tags file when needed.
-
Q: How many languages does Citre support?
A: Citre supports all languages that Ctags support. The latest Universal Ctags support 134(!) languages:
$ ctags --list-languages | wc -l 134
Besides, you could define your own parser using regex to support more languages.
-
Q: But seems for now Citre only has support code for C...
A: No matter what's the language, as long as you have a tags file for it, then Citre works out of the box. Language-specific support is for extra minor goodies, see the "Commentary" section in each language-support code file.
Current status
Below are the status of tools provided by Citre:
Tool | Description | Status | Note |
---|---|---|---|
Ctags | Create/update tags files | alpha | 1 |
capf, xref, imenu | Integration with built-in mechanisms | stable | |
citre-jump |
Jump to the definition | stable | |
citre-peek |
Deep code reading in a peek window | beta | 2 |
citre-global |
GNU Global plugin | alpha |
"alpha" means the tool is likely to go through breaking changes. "beta" means new features and improvements may happen. "stable" means the tool is basically finished.
Below are new tools I have in mind, and may come in the future:
-
citre-diagnostics
: Check if you have the right version of readtags, ctags; show the project root, the tags file being used, and things like that. -
A tool that lets you interactively filter a tags file (find a tag whose name contains "foo", the path contains "bar", with the kind "function", etc). Then you can use the results as completion (insert in current bufffer), visit their definition, or convert them into a
citre-peek
or xref session. I consider this to be the "ultimate weapon" of Citre.
Donation
If Citre makes you happy, please consider buying me (@AmaiKinono) a beer to make me happy ;)
Footnotes
-
Universal Ctags is exploring concepts like incremental updating, multi-pass parsing, and more. Citre may follow the changes happen in Universal Ctags.
↩ -
I plan to implement a feature that lets you further filter the definitions in a peek window.
↩