Compilation mode is a major mode that allow us to run a command and see its output result in a special buffer, this resulting buffer show the errors and allow us to navigate through them, you can check the documentation for more details.
It's a "simple mode" but it can be used for many things like compile and run a program, run tests, and so on.
compile is an interactive function so we can call it with
M-x compile and enter the command we want to execute.
In the following example we're compiling this blog using hugo binary:
compile is a emacs-lisp function so we call it from our code, we just need to take care about the
default-directory when we call it, for example if we call it from
default-directory will be
lib and in some cases we want to use our project root, or a different directory, to run our command.
To fix this we need to setup
default-directory before we call
compile, for example let's build a custom function to run
hlint in our entire project and then show its results in a
(defun my/run-hlint () "Run hlint over the current project." (interactive) (let ((default-directory (projectile-project-root))) (compile "hlint .")))
In this case we're setting up
default-directory with our project root (using projectile to get the root) and then when we call
compile it will take
These modifications to default behaviour of
compilation-mode should be made after the mode was loaded so we need to use with-eval-after-load otherwise these changes won't be applied correctly.
compilation buffer has some preset key bindings that conflict with
evil-mode, for example when we press
g in a compilation buffer this will re-run the command, but this key binding is also used by
evil-mode, to fix this we can disable the default key binding with:
(with-eval-after-load 'compile (define-key compilation-mode-map (kbd "g") nil) (define-key compilation-mode-map (kbd "r") 'recompile) (define-key compilation-mode-map (kbd "h") nil))
In this case
h key binding is also disabled (also used by evil) and
r is remapped to
recompile for easy access now that we disabled
g default key binding.
Follow compilation output
compilation-mode doesn't follow the output of the command so if our command result has a large output we'll need to scroll manually, to fix this we can change
(with-eval-after-load 'compile ;; set cursor to follow compilation output (setq compilation-scroll-output t))
Enable ANSI colors
Some tools show results with colors for easy reading but
compilation-mode won't show them by default, you can make them look better with:
(require 'ansi-color) (defun colorize-compilation-buffer () (let ((inhibit-read-only t)) (ansi-color-apply-on-region (point-min) (point-max)))) (add-hook 'compilation-filter-hook 'colorize-compilation-buffer)
This was taken from this Stack Overflow answer
Re run compilation from another buffer
When we're making changes in our code we want to re-run our compilation process right after we save the changes but to do this we have to move to the compilation buffer to be able to re-run the compilation, a better approach to do this could be just call
recompile by using a key binding, I use
evil-leader to make this:
(evil-leader/set-key "R" 'recompile)
But it can be attached to any key binding, for example:
(global-set-key (kbd "C-c C-r") 'recompile)
Bonus: run parrot mode animation when a compilation is successful
I configured parrot-mode to animate the little parrot every time the compilation process is a success, to make this we need a small function that check if it was a success and then we need to attach it to
'compilation-finish-functions, this is a variable defined in
(defun my/parrot-animate-when-compile-success (buffer result) (if (string-match "^finished" result) (parrot-start-animation))) (use-package parrot :ensure t :config (parrot-mode) (add-to-list 'compilation-finish-functions 'my/parrot-animate-when-compile-success))
As we can see
compilation-mode is a simple but powerful mode that allow us to build our own tools, we can create an automatic build system in case there is not something already existing for the technology we're using or if we just want to run some tasks in an easier way.