Source code review with Emacs [Part 2]
- Last Modified:Series: "Source code review with Emacs"
Now that we can configure packages in an organized way, the first step is to setup the completion framework. This will allow us to quickly search for files, text, etc. Helm integrates nicely with all the needed tools.
You are expected to have followed Source code review with Emacs: Part 1
Content
Helm
Helm is a completion framework that can be used to build completion packages. We don't care about the complexity of the framework, the needed completion packages already exist. We just need to download and configure them.
The following code will setup the needed packages. Add it to the Emacs init file.
;;
;; HELM
;;
(use-package helm
:ensure t
:diminish helm-mode
;; Helm behavior
:bind (:map helm-map
("<tab>" . 'helm-execute-persistent-action)
("C-i" . 'helm-execute-persistent-action)
("C-z" . 'helm-select-action)
("A-v" . 'helm-previous-page))
;; Global shorcuts7
:bind (("<f2>" . 'helm-occur)
("<f3>" . 'helm-resume)
("M-x" . 'helm-M-x)
("M-y" . 'helm-show-kill-ring)
("M-s o" . 'helm-occur)
("C-'" . 'helm-semantic-or-imenu)
("C-c h" . 'helm-command-prefix)
("C-x b" . 'helm-mini)
("C-x C-f" . 'helm-find-files)
("C-c f" . 'helm-recentf)
("C-x C-b" . 'helm-buffers-list))
:config
(setq helm-bookmark-show-location t)
(global-unset-key (kbd "C-x c"))
;; fuzzy matching
(setq helm-mode-fuzzy-match t)
(setq helm-completion-in-region-fuzzy-match t)
(setq helm-M-x-fuzzy-match t
helm-buffers-fuzzy-matching t
helm-recentf-fuzzy-match t)
(helm-mode 1))
This code is self explanatory(?). It enables fuzzy search, sets the
shortcuts, and enable the mode globally. In the code, C means the
Control key, M the Alt key, and s the Super key (windows key)
e.g. C-f
means Control+f and M-s-o
means Alt+Super+o.
The main shortcuts are aimed towards code navigation:
- When using helm, complete the selection with
TAB
- Select the action to be executed with the selection with
C-z
- Use fuzzy search for file search with
C-x C-f
- Use fuzzy search to find recent visited files with
C-c f
Try opening a file with C-x C-f
and test the fuzzy
search is working by entering "em d". The emacs.d folder should match.
"rc ba" should match at least the .bashrc file.
Code navigation
The previous code in tandem with the following code, makes it possible to start navigating code:
;; ggtags can be used to navigate the code and genearte TAG files
;; It supports universal-ctags and pygments backend
(use-package ggtags
:defer t
:ensure t)
(use-package helm-gtags
:defer t
:ensure t
:after (helm ggtags)
:bind (("M-." . 'helm-gtags-dwim)
("M-," . 'helm-gtags-pop-stack)))
(use-package helm-ag
:ensure t
:defer t
:after (helm ag)
:config
(setq helm-ag-insert-at-point 'symbol))
So far the basic shortcuts to navigate code are:
- Go to symbol definition by placing the cursor over the symbol and
pressing
C-.
. Go back withM-,
- List functions defined in the current file with
C-'
- Quickly search for the text under cursor with
F2
Although there are smarter ways to find references to symbols, I prefer to use helm-occur to find symbols in the same file. In order to find symbols in other files, I use helm-ag (not properly set yet).
The reason I prefer grepping over smart navigators, is that I usually don't have enough code to make IDEs/navigators work. Furthermore, highly modular code such as code that uses dependency injection cannot be interpreted properly, as the IDE does not have a proper understanding of the dependency injection framework, or there are missing Makefiles. This is of course common with code generated and injected at compile time. Every big enough project will have macros and code generation.
So far we can navigate code by simple grepping, but we cannot navigate using symbols. We will set what is missing in the section below.
C and Java modes
The Java mode is derived from CC mode. Hence, we will configure them at the same time. The following code needs to be added to the init file:
(use-package ggtags
:defer t
:ensure t)
Gtags works with GNU global. The usage is pretty simple. TAG files are created by parsing the source code, and then these TAG files can be used by helm-gtags and ggtags mode to navigate the code. The current configuration prefers helm-gtags for code navigation.
When the M-.
command is used for the first time ,
helm-gtags will try to locate the TAG files. If the files are not
found, it will ask for the source code root folder, whether to use
gtags to generate the TAG files, and what backend to use to generate
the TAG files.
If you want to use Pygments as parsing backend, you can set the
environment GTAGSLABEL
variable to "Pygments", and use
helm-gtags-create-tags to select between backends.
;; Source Code review
;;
;; C mode
;;
(use-package ag)
(use-package cc-mode
:defer t
:config
(setq c-default-style "linux")
;; Change font for functions as I want them
;; different than other identifiers.
(make-face 'font-lock-unnecessary-function)
(set-face-foreground 'font-lock-unnecessary-function "pink")
(font-lock-add-keywords 'c-mode
'(("\\(\\w+\\)\\s-*\("
(1 'font-lock-unnecessary-function)))
t)
;; Open files read-only
(add-hook 'c-mode-common-hook
(lambda ()
(when (derived-mode-p 'c-mode 'c++-mode 'java-mode)
(ggtags-mode t)
;; Prefer helm-gtags over ggtags
(define-key ggtags-mode-map (kbd "M-.") nil)
(define-key ggtags-mode-map (kbd "M-,") nil)
(setq buffer-read-only t))))
;; Highlight whitespace at the end of a line
(add-hook 'find-file-hook (lambda () (setq show-trailing-whitespace t))))
;; Comment to use ctags as backend. Pygments is way better.
(setenv "GTAGSLABEL" "pygments")
This is what the code does:
- All C, C++, and Java files, are opened in read-only mode. This is because the main use of the setup is for code review.
- Function calls will be highlighted in pink color. This is a little crude, but needed to facilitate source code review.
- It removes the ggtags key bindings in favor of helm-ggtags ones.