ggtags
package – [config].
This post still has value if you are interesting in configuring
ctags
only.
This posts shows how to set up ctags
to parse SystemVerilog code and
how to access that tag database in emacs.
ctags
is an awesome application which crawls through all your code
and indexes everything you want, as you want as all of that can be
controlled using regular expressions. Here’s a scenario where ctags
comes helpful. Say you are in pqr.v file in which a function xyz is
called. Now that function may neither be defined in the same file nor
in some other file in the same directory. The function xyz could be
defined in some other folder in a file called abc.v. But with the help
of ctags, you can jump directly to the function xyz definition from
the place where it is called!
I code in Verilog and ctags helps be jump to the point where a variable/module/task/function/interface/define(or macro)/class/.. etc. is defined.
Custom configuration for ctags is usually stored in ~/.ctags
.
--exclude=.SOS
--exclude=.git
--extra=+q
# Hide the warning: ctags: Warning: xcmd recognizes
# /home/kmodi/usr_local2/libexec/ctags/drivers/coffeetags is not available
# https://github.com/fishman/ctags/issues/131#issuecomment-69467247
--quiet
--langmap=SystemVerilog:.sv.v.svh.vg.tv.vinc
--languages=SystemVerilog,C,C++,HTML,Lisp,Make,Matlab,Perl,Python,Sh,Tex
--regex-SystemVerilog=/^\s*`define\b\s*(\w+)/`\1/d,define/
Here are some notes about my customizations in .ctags
file:
--langdef=systemverilog
--langmap=systemverilog:.v.vg.sv.svh.tv.vinc
--regex-LANGUAGE=/REGEX/REGEX_GROUP/CTAGS_KIND_ABBREV, CTAGS_KIND_NAME/
^\s*\bfunction\b.*(\b\w+\b)
xyz
. The REGEX_GROUP defined for the Verilog defines
or macros
is a special case because I wanted to also prefix
the define/macro string with backtick `
. The reason is
that in emacs when I have the cursor on a define like `XYZ
,
etags-select-find-tag-at-point function uses that whole string
including the backtick for searching in the TAGS file.t,task
f,function
m,module
. Specifying the kind is important
because you can later specify which kinds of matches
you want to log in the TAGS file. This is done using
--systemverilog-kinds=+ctfmpied
.--languages=systemverilog,C,C++,HTML,Lisp,Make,Matlab,Perl,Python,Sh,Tex
.
NOTE: I learnt that specifying the language files you want to parse
is better because if a particular extension is defined for more than
1 language, then they result in duplicate tag entries in the TAGS
file. I am not sure if that duplication is done by ctags or emacs, but
once I specified the languages I wanted to parse, I stopped getting
duplicate entries when using the etags-select package in emacs. In my
case, the .v extension was associated with Verilog language predefined
in ctags, and it was also defined for systemverilog language in my
.ctags
.Once you have your .ctags file ready, generate the TAGS file using
the command, ctags -Re -f /project/root/dir/TAGS /project/root/dir
.
Example: ctags -Re -f ~/.emacs.d/TAGS ~/.emacs.d
I use Exuberant ctags 5.8 with emacs 24.3. My .ctags
is heavily
inspired from a verificationguild.com forum.
Ensure that you are using Exuberant ctags and not the ctags
that’s installed along with emacs by checking the output of
ctags --version
. Usually you would need to install ctags AFTER
installing emacs so that the ctags binary in /usr/local/bin
or
$HOME/local/bin
is the Exuberant version and not emacs.
Here is my emacs configuration for ctags: [github].
My emacs ctags config starts by setting few variables to avoid any annoyances:
(setq tags-revert-without-query t)
This prevents emacs from asking
you every time if you want to reread that updated TAGS file. Of
course you would want to!(setq large-file-warning-threshold 30000000)
In most of the cases,
TAGS files will be large (> 10MB). I didn’t want emacs warning me
about that every time it accessed the TAGS files. So I increased the
threshold to 30MB. So set the threshold as per your needs. You can
also disable that warning completely by setting the value to nil
.(setq tags-case-fold-search nil)
I like the searches to be
case-insensitive. It is useful when I manually search for a tag. But
usually the way I use tags is: I put my cursor on the name of
function/task/.. etc I want to jump to and hit my key-binding for
tag search.I rely on few packages to makes the emacs and TAGS files' interaction seemless: etags-table, ctags-update, etags-select. All are available through MELPA.
etags-table will help you load the correct TAGS file based on your
file path. But you have to load all the project path possibilities
into etags-table-alist
first! Let’s say one of the project roots
entered in that list is $PRJ. If your TAGS path is $PRJ/TAGS and you
search a tag in $PRJ/any/nested/path/file.c, etags-table will figure
out that you want to search in $PRJ/TAGS.
In my ctags setup file I check for a project-root
var and load that
into ’etags-table-list’ if available. I update the project-root var
using a shell env var. I haven’t committed that project-root var
assignment to github. But you can update that using projectile or any
other mechanism.
The beauty is that etags-table won’t load the TAGS files from ALL
the paths in ’etags-table-alist’. It will load only the relevant
one(s). Note that each entry in ’etags-table-alist’ is another
list. Each of those lists is of the nature '( PROJECT_PATH, TAGS_FILE_1, [OPTIONAL_TAGS_FILE_2, ..] )
.
(require 'etags-table)
(setq etags-table-alist
(list
`(,(concat user-emacs-directory "/.*") ,(concat user-emacs-directory "/TAGS"))
))
(setq etags-table-search-up-depth 15) ;; Max depth to search up for a tags file. nil means don't search.
;; Below function comes useful when you change the project-root symbol to a
;; different value (when switching projects)
(defun update-etags-table-then-find-tag ()
"Update etags-table based on the current value of project-root and then do
tag find"
(interactive)
(when (boundp 'project-root) ;; add-to-list if project-root symbol is defined
(add-to-list 'etags-table-alist
`(,(concat project-root "/.*") ,(concat project-root "/TAGS")) t))
(etags-select-find-tag-at-point)
)
ctags-update will update the first TAGS file that is found while searching up the parent directories from the path of the file that gets modified. You can configure how frequent you want the update frequency to be.
(require 'ctags-update)
(setq ctags-update-delay-seconds (* 30 60)) ;; every 1/2 hour
(autoload 'turn-on-ctags-auto-update-mode "ctags-update" "turn on `ctags-auto-update-mode'." t)
(add-hook 'verilog-mode-hook 'turn-on-ctags-auto-update-mode)
(add-hook 'emacs-lisp-mode-hook 'turn-on-ctags-auto-update-mode)
You can use etags-select
to pick one of
multiple tag matches. It is useful when a same function/task/.. has
multiple definitions and you need to pick the definition to jump
to. If multiple matches don’t exist, finding a tag at point will make
you jump directly to the definition file.
I prefer etags-select as helm-etags+ doesn’t play well with tags
that have a `
prefix (which is very crucial for jumping to
define/macro definitions in Verilog).
(require 'etags-select)
(define-key etags-select-mode-map (kbd "C-g") 'etags-select-quit)
;; Also quit etags-select when cursor moves to another window
(define-key etags-select-mode-map (kbd "C-x o") 'etags-select-quit)
(define-key etags-select-mode-map (kbd "C-x O") 'etags-select-quit)
(define-key etags-select-mode-map (kbd "C-p") 'etags-select-previous-tag)
(define-key etags-select-mode-map (kbd "C-n") 'etags-select-next-tag)
Finally here is the key-binding I have set to my quick hyper-space jumps to definitions of any kind.
(global-set-key (kbd "M-.") 'update-etags-table-then-find-tag)