Wolin Labs :: fun toys for your iThingy & Droid

Articles

Copying a Single File with a Yocto Recipe

Semihosting Debugging on STM32F4

Realtime Audio DSP with the STM32F4

Tuning the Moog Etherwave Theremin

STM32 Discovery Development on Linux

STM32F4 USB Virtual COM Port (VCP)

Editing GoPro Hero2 Video with Cinelerra

Code Browsing with Emacs, GLOBAL, and Speedbar

Android Activity Bar Framework

Generating an Audio Sine Wave with Java

Android Simple Yes/No MessageBox

Fox Talas 32 Oil/Seal Change Checklist

Creating a Bootloader Environment (Freescale ColdFire Example)

Pentomino Smackdown: Names That Didn't Make the Cut

Code Browsing with
Emacs, GLOBAL, and Speedbar

Ross Wolin - last updated 2013.03.05


I am a big fan of Source Insight for code browsing... when I'm working on Windows. I have also heard people use newer versions of Visual Studio w/ intellisense. Unfortunately neither program runs on Linux.

My goal was to find a simple source browsing solution for C/C++ and Java that runs roughly the same on Linux, Windows, and OSX.

    The basic functions I look for in a code browser are:
  • Jump to function or symbol declaration (i.e where is this defined?)
  • Jump back to where I was
  • Show all references to a function (i.e. where is this function called?), display a list, and let me click on the list to jump to a reference
  • Show all the functions in the current file
  • Show which function cursor is in
  • Find in files (i.e. string search through all files in project or recursive grep)

I'm partial to Emacs as a universal platform editor.

Yes, you with your hand up in the back? Yes, using ECB and CEDET/Sematic/EDE/Senator I think you can probably do everything in my list. However, when I set up ECB+CEDET, I found CEDET overwhelmingly complex, and a tad slow. It's very possible I didn't have them set up correctly, didn't spend enough time with the configuration, etc. Couple that with the problem that the included version of CEDET didn't seem to work right with ECB, forcing me to disable the included version, then download/set up ECB and CEDET manually... I spent more time trying to make that setup work than I'd like to admit, I did get it to work somewhat, but ultimately I wasn't that satisfied with the results. =)

This article details a simpler solution, using Emacs in combination with GLOBAL and Speedbar for code browsing.

 

I'm going to assume you already have Emacs installed, and recent versions of Emacs include Speedbar as part of CEDET. If you don't have GNU Global, on a Debian based Linux system you can install that via:

apt-get install global


Init.el


Start by adding a few lines to your init.el to do a basic setup for Global.

First, tell Emacs where your gtags.el file, if it is someplace other than the standard macro directory. (I installed Global via apt-get on Debian, and the gtags.el was in /usr/share/emacs/site-lisp/global, so I didn't need to modify the load-path)

(setq load-path (cons "/path/to/gtags.el" load-path))

Next, tell Emacs to load gtags on start-up:

autoload 'gtags-mode "gtags" "" t)

The next part is preference. If you would like Emacs to go into gtags mode whenever you enter c mode, add the following section. (If instead you would rather control when gtags mode starts, omit this section and turn on gtags mode when you want it, via M-x gtags-mode)

(add-hook 'c-mode-hook 
   '(lambda () 
      (gtags-mode t)
))

Note: I have actually set Emacs to use c++-mode even when I am editing C files. If you've done that, then you want to add-hook to c++-mode-hook rather than c-mode-hook)



Additional, I like to have Speedbar start when I run Emacs. To do that, add the following section to init.el. (If you want to start Speedbar manually instead, omit this and use M-x speedbar

; Start speedbar automatically if we're using a window system like X, etc
(when window-system 
   (speedbar t))



Generate Tags


The simplest way to make a set of tags is to go to the top directory in your source tree and run

gtags -v

This will recursively tag all the files in the tree, including C, C++, Java, PHP, assembler, and YACC source files.


If you wish to limit the files Global tags (or add more) look in the Global documentation, including the settings in the ~/.globalrc config file.

This simple script is something I used for an ARM Linux kernel source project, to specify which parts of the source tree I want to tag. It uses gtag's -f option to read the list of files to tag from a file.

#!/bin/bash

rm .files2gtag

for f in block crypto Documentation drivers firmware fs include init ipc kernel lib \
         mm net samples scripts security sound tools usr virt
do
   find $f -iregex '.*\(c\|h\)'  >>.files2gtag
done

for f in boot common configs include kernel lib mach-dove mm nwfpe oprofile \
         plat-orion tools
do
   find arch/arm/$f -iregex '.*\(c\|h\)'  >>.files2gtag
done

gtags -v -f .files2gtag


Jump to Function or Symbol Definition


Place the cursor on the function name and run M-x gtags-find-tag to jump to the function's defintion.
This function is also bound to M-. key combination.

Place the cursor on a symbol name and run M-x gtags-find-symbol to jump to the symbol's defintion.
This function is not bound by default, you could bind it a key in your init.el, etc.


The middle mouse button is somewhat of a context sensitive version, it runs an Emacs function called gtags-find-tag-by-event which either runs gtags-find-tag or gtags-find-symbol, depending on if you click on a function name or a symbol. (I might be tempted to bind this to a key combination also...)


TO DO: would be nice to know how to limit symbol definition by scope, if possible



Jump Back to Previous Spot


After jumping to a function, symbol, or references buffer, you can return back to where you jumped from using the M-x gtags-pop-stack function.

This function is bound to M-* key combination, as well as the right mouse button.



Show All References to a Function


Place the insertion point (i.e. the cursor) on the function name in the code, and run
M-x gtags-find-rtag


    A new buffer appears, with all the references in it:
  • Clicking a reference with the middle mouse button jumps to it.
  • M-* will jump back to the references buffer.



Show All Functions in Current File


Speedbar will do this for you (click image to expand)



To start Speedbar, use: M-x speedbar

or you can start Speedbar in your init.el with:

(when window-system       ;start speedbar if we're using a window system like X, etc
   (speedbar t)) 

(Probably a good idea to not start Speedbar if you are running console Emacs =)


Once Speedbar is running, you might want to make sure "Auto Update" is turned on. (Right click in Speedbar, it's on the menu)


    Now with Speedbar running:
  • Press the center mouse button on a filename to open it in the Emacs window.
  • Press center mouse button on one of the file icons to the left of the filename to expand the entry and show all the functions within it.


TO DO: It would be cool to have Speedbar automatically expand only the current file's functions...



Show Which Function Cursor is in


Emacs will show the current function name within a file when "which function mode" is enabled. (The name is displayed on the right side of the status bar.)

Toggle this mode from the command line with: M-x which-function-mode

Enable it at startup via your init.el with:

(which-function-mode 1)


Find in Files


Run find in files with the Emacs command: M-x find-grep

You could also bind find-grep to an Emacs key (i.e. Shift+F3 in this case) in your init.el with:

(global-set-key [S-f3] 'find-grep)


I tweaked the find-grep command to work more to my liking, by adding this line to init.el:

(setq grep-find-command 
      "grep -rnH --exclude=.hg --include=\*.{c,cpp,h} --include=-e 'pattern' ~/src_top/*")


    This does a couple of things.
  • No matter where I am in the source tree, it starts every recursive search from ~/src_top.
  • It only searchs through .c, .cpp, and .h files
  • It ignores the Mercurial source control database in .hg


When you invoke find-grep, you cursor to where 'pattern' is and change it to your search regex. You can also change any of the other parameters. Next time you run the command, you can arrow up through the command history if desired.

While the grep results buffer exists, you can jump forward (M-g M-n) and backward (M-g M-p) among the items in the list.


This section binds find-grep to Shift+F3 all the time. You can obviously do more sophisticated stuff like in a mode hook limit the bind the key based on mode, write a function that pulls the selection from the point into the pattern, asks for the pattern, etc.



TO DO: How to update tags as files are changed in emacs? Might be possible to run with -u for incremental update?



References

Speedbar/CEDET
Speedbar - Emacs Wiki
GNU Global Source Code Tag System, section 3.6, Extended Emacs using GLOBAL




Send comments, questions, money in large denominations, etc to android at wolinlabs.com