GTags for Python in Emacs

Prologue

I usually code in Python and when I have a lot of functions in different files in a project then it is desirable to jump to the function definition from its identifier at point. In Emacs, Elpy has (elpy-goto-definition) with keybinding Meta-., that sometimes works on local files and doesn't most of the time on remote files. There are some other good options like elpy-rgrep-symbol, rgrep but they are not that efficient. I have also tried lsp-mode with mspyls which works good in local but always complains about server-time-elapsed over tramp. As I work mostly on my servers, it is really important for me to get a reliable tool for this feature.

I came across gnu-global on some online platform which is widely used by C-programmers for similar use-cases as of mine. The main idea is to create TAG files of your source codes and feed them to Emacs for navigation around the project. This tool works for python too but with some extra plugins that I have discussed below. I followed the gtags-spacemacs guide which works for my vanilla-Emacs too. To make it work over tramp I had to install gnu-global on the server and this article summerizes those steps.

Install gnu-global on server

If you have the admin rights on the server then you can follow the same instruction as of gtags-spacemacs and everything works fine. Without the sudo rights, you need to do the following steps to install it properly. Before installing gnu-global, first I had to install all the dependencies like,

ctags:

- git clone https://github.com/universal-ctags/ctags.git
- cd ctags
- bash autogen.sh
- ./configure --prefix=/path/to/install (keep the path-to-install same for all the rest of the packages)
- make
- make install
- cd ..

gperf:

- wget https://ftp.gnu.org/pub/gnu/gperf/gperf-3.1.tar.gz
- tar -xf gperf-3.1.tar.gz
- cd gperf-3.1
- ./configure --prefix=/path/to/install (keep it same as of ctags)
- make
- make install
- cd ..

libtool:

- wget https://ftp.gnu.org/gnu/libtool/libtool-2.4.2.tar.gz (this version is important)
- tar -xf libtool-2.4.2.tar.gz
- cd libtool-2.4.2
- ./configure --prefix=/path/to/install (keep it same as of ctags)
- make
- make install
- cd ..

texinfo:

- wget https://ftp.gnu.org/gnu/texinfo/texinfo-6.7.tar.xz
- tar -xf texinfo-6.7.tar.xz
- cd texinfo-6.7
- ./configure --prefix=/path/to/install (keep it same as of ctags)
- make
- make install
- cd ..

I added the installation path (path/to/install/bin) to the .bashrc to access them from the terminal directly.

export PATH="/path/to/install/bin:$PATH"

Finally, install the gnu-global with the following three commands.

- wget https://ftp.gnu.org/pub/gnu/global/global-6.6.tar.gz
- tar -xf global-6.6.tar.gz
- cd global-6.6
- ./configure --with-exuberant-ctags=/path/to/install/bin/ctags --prefix=/path/to/install/  (keep it same as ctags)
- make
- make install
- cp gtags.conf ~/.globalrc (copy the config to the globalrc)
- echo export GTAGSLABEL=pygments >> .profile
- cd ..

Creating tags and accessing them from Emacs

  • Once gnu-global is installed, I can go to any project directory and type gtags --gtagslabel=pygments to create gtags. In order to access them from the Emacs, I installed helm-gtags.
  • Before using helm-gtags over tramp, I need to tell the tramp the path of gnu-global on the server. I have done that by adding (add-to-list 'tramp-remote-path "/path/to/install/bin") to my init.el. If you have multiple severs, then install gnu-global in each of them and add the path to the tramp-remote-path as mention earlier. Now you are ready to go. The screenshot shows it in action. We can go to any function identifier and then it can take us to the function definition even defined in another file.

Using helm-gtags in multiple server simulataneously

  • When you have multiple servers open, helm-gtags can use gnu-global in one of the server and will complain about the gnu-global path for all others. It is because the tramp-remote-path needs to be forced updated when you switch the server. It can be done by calling tramp-cleanup-all-connections from M-x and then using helm-gtags
  • Another elegant solution is to switch the tramp-remote-path automatically when we switch to the buffer of another server. I have given an example for two servers with the names puhti and narvi and can be extended for more than two.
(connection-local-set-profile-variables 'remote-path-puhti
   '((tramp-remote-path . ("/projappl/project_2000936/miniconda3/bin"  "/projappl/project_2001838/bin" tramp-default-remote-path)))) ;"/projappl/project_2001838/bin": gnu_global is installed here

(connection-local-set-profile-variables 'remote-path-narvi
   '((tramp-remote-path . ("/home/tripathy/miniconda3/bin" "/home/tripathy/applications/bin" tramp-default-remote-path)))) ; "/home/tripathy/applications/bin": gnu_global installed here
;"/home/tripathy/miniconda3/bin":  anaconda path for other packages
(connection-local-set-profiles
   '(:application tramp :machine "puhti") 'remote-path-puhti)
(connection-local-set-profiles
   '(:application tramp :machine "narvi") 'remote-path-narvi)

demo.gif

Author: Soumya Tripathy

Created: 2021-03-25 Thu 18:42

Validate