How to use ctags with Emacs effectively

  |   Source

UPDATE on <2018-02-24 Sat>: I released counsel-etags, a complete solution for Ctags/Emacs.

ORIGINAL CONTENT:

See http://emacswiki.org/emacs/EmacsTags for general usage of tags file. A file name of tags files is TAGS by default. But in Emacs manual the tags file is named tags table. Either tags file or tags table or TAGS is only different name for the same thing.

According wiki page, people usually use ctags to do two things,

  • Code navigation
  • Auto-complete/IntelliSense

In my opinion, ctags is OK for code navigation because it's integrated into Emacs well and ctags supports more programming language. But ctags only uses regular expression to analyze the code. So it's not as precise as professional tools like cscope or GNU Global. According to my own experience, ctags plus grep is actually good enough for code navigation in big enterprise projects with mixed environments. If you are among a few talented geeks who are developing Linux Kernel in the most efficient way, you should use better tools like GNU Global. See the head up from Nick Alcock. His summary of the Pro/Con of GNU Global is great.

IntelliSense is a different story. For programming languages like Javascript, ctags provide some IntelliSense with is better than nothing. C++ is a more complex language which is a litter harder for ctags to handle unless you are not using those "advanced" features of C++.

I found one missing piece from EmacsWiki is the tip on how to manage tags file easily when the TAGS is built from some code of third party libraries (QT, wxWidgets, GTK, Boost, just name a few).

My typical work flow is:

  • Load the tags file for my own code.
  • Load other tags files built from third party libraries related to my current task.
  • Unload third party tags immediately if its not needed.

The purpose to load tags files is to look up API easily. For example, I use C++ library wxWidgets to develop my application. I want to use its API called wxWindow::Maximize. I type M-x find-tag and input the keyword Maximize. So I can check the function definition and know what parameters I need fill in to use "Maximize".

The reason to do the unload thing is to avoid function name conflict from some tags files. EmacsWiki has a section "Choosing Among Multiple Tags for the Same Name" to solve this problem. But unloading is more convenient. In theory, it would be annoying if I need repeat load and unload the same tags files. In reality, it seldom happens because if I need work on Javascript (so I unload C++ related TAGS), I will usually keep working on Javascript until the end of the day.

Produce tags files for all the environments

For example, I produce tags files of Library A for Mac and Linux. I put tags file from Mac at directory "/home/cb/tags/Library_A/mac" and file from Linux at /home/cb/tags/Library_A/linux.

Use git and github to manage the tags files

So I can have copies of the tags files at github server and my local computers. That's also a part of my strategy to setup the development environment. My projects on different computers usually use same external library.

EmacsWiki discusses how to update your tags file frequently because your own code are updated frequently. But the tags file from a big library like wxWidgets need not be updated to often because I only use limited old APIs in that library.

Write emacs lisp code to load/unload these tags files

Here is the code. In this example, if I need load tags file for wxWidgets, I call the function "add-wx-tags". If I need unload the tags file, I press "Ctrl-u" and call the same function "add-wx-tags". Since I use Smex, calling function doesn't mean pressing many keys.

; Set up tags built from third party libraries ===begin
; define tags alias like "wx-tags" here
(setq wx-tags (expand-file-name "~/tags/wx/osx/TAGS"))

; @see <Selecting a Tags Table> in Emacs manual for details.
; We only change the list "tags-table-list". It is documented officialy.
(defun insert-into-tags-table-list(e)
  (add-to-list 'tags-table-list e t))
(defun delete-from-tags-table-list (e)
  (setq tags-table-list (delete e tags-table-list)))
; This is a sample command, all you need is copy/paste this template
; for other new commands
(defun add-wx-tags (&optional del)
  "Add or delete(C-u) wxWidgets tags into tags-table-list"
  (interactive "P")
  (let (mytags)
    ; here add your third party tags files
    ; Usually you need load/unload tags files combination in one command
    ; change below line to add them
    (setq mytags (list wx-tags))
    (if del (mapc 'delete-from-tags-table-list mytags)
      (mapc 'insert-into-tags-table-list mytags))))

This solution only uses one standard variable tags-table-list in Emacs. So it will always work with any other Emacs plugins you install. And you can use all the old hotkeys and functions ("find-tag", for example) without any problem.

See Emacs manual for technical details.

Comments powered by Disqus