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=pygmentsto create gtags. In order to access them from the Emacs, I installed helm-gtags. - Before using
helm-gtagsover 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 myinit.el. If you have multiple severs, then install gnu-global in each of them and add the path to thetramp-remote-pathas 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-globalin one of the server and will complain about thegnu-globalpath for all others. It is because thetramp-remote-pathneeds to be forced updated when you switch the server. It can be done by callingtramp-cleanup-all-connectionsfromM-xand then usinghelm-gtags - Another elegant solution is to switch the
tramp-remote-pathautomatically when we switch to the buffer of another server. I have given an example for two servers with the namespuhtiandnarviand 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)