For many tasks in a developer’s day, a well-tuned compilation mode is more efficient than reaching for a shell (fewer keystrokes, less context switching) and lighter on the brain than specialized major modes (no new key bindings).
Emacs’s “compilation” mode is not limited to running compilers. You can run bundlers for front-end development, test runners for red-green-refactor TDD, project-wide linters, dev servers for back-end code, Makefile targets, and more.
These notes cover:
run-command
packageColors are often the only way to notice errors in a flurry of output. Also, commands with a long startup time sometimes offer a “watch” mode to do several runs for a single invocation, and clear the screen between runs.
Compilation mode by default handles neither the control characters that produce colors, nor those that clear the screen. A simple solution that covers many cases is to watch out for a few sequences in the command output using a “compilation filter”, and apply them:
(defun local/postprocess-compilation-buffer ()
(goto-char compilation-filter-start)
(when (looking-at "\033c")
(delete-region (point-min) (match-end 0)))
(ansi-color-apply-on-region (point) (point-max))))
(add-hook 'compilation-filter-hook 'local/postprocess-compilation-buffer)
Some commands produce even richer output (progress bars, simple animations) or clear the screen by other means (ANSI codes), requiring more complete terminal capabilities. The run-command
package (described later) can run such commands in Emacs’s term-mode
, providing those capabilities, while maintaining compilation-mode
functionality.
The basic way to run a command in compilation mode is M-x compile RET <command> RET
, e.g. M-x compile RET npm start RET
. Next time you invoke compile
, <command>
defaults to the previous command, so recompiling effectively becomes M-x compile RET RET
.
However, recompiling being the more common case, Emacs also provides M-x recompile
, which reuses the last command right away, without prompting. (You can still edit the command with C-u M-x recompile
.) So, recompile
is the better candidate for quick access via a key binding, e.g.:
(global-set-key (kbd "C-c c") 'recompile)
With that, C-c c
will re-run the last command, and C-u C-c c
will let you edit the command first.
There’s an even shorter binding for when the cursor is already in the compilation window: g
. This is useful when you need to alternate among several complex commands: leave their buffers open, then switch back to them and press g
as needed.
To stop a command, move the cursor to the compilation buffer and press C-c k
.
Unless you need to copy a command’s output, or re-run a previous command, there’s little reason to move the cursor to the command output window; you can more efficiently browse it without leaving the window where you’re writing using these key bindings:
M-home
M-end
M-next
(where “next” means PageDown
) or C-M-v
M-prior
(where “prior” means PageUp
) or C-M-S-v
These bindings are not specific to compilation mode, the work in every situation where there is an “other window”. They may seem cumbersome, especially on laptop keyboards which require an additional Fn
press, but are well worth the effort.
Another flow saver, provided that the command prints errors in a format that compilation mode understands, is C-x `
, which jumps to errors in the output, in succession. For example, given a TypeScript project with a file src/App.tsx
:
import React from 'react'
import ReactDOM from 'react-dom'
console.helloWorld()
Running M-x compile RET npxj tsc -P . --noEmit --watch
will launch the type checker and print the following:
[8:13:36 PM] File change detected. Starting incremental compilation...
src/App.tsx:3:9 - error TS2339: Property 'helloWorld' does not exist on type 'Console'.
3 console.helloWorld()
~~~~~~~~~~
[8:13:36 PM] Found 1 error. Watching for file changes.
If you now hit C-x `
, the App.tsx
buffer will appear, with the cursor on helloWorld
.
run-command
packageIf, in a given context, you tend to run the same small subset of commands over and over, (e.g. npm test:watch
in a JavaScript project, make clean
in a C project, hugo server --buildDrafts
in a Hugo blog), that’s a prime case for automation.
The run-command
Emacs package (by the author) lets you write simple command lists, bring them up depending on the context, and select them with autocompletion:
See the run-command GitHub page for setup and usage instructions.
compilation-scroll-output
to t
.
scroll-conservatively
to 101
(see M-x describe-variable scroll-conservatively RET
for why).compilation-scroll-output
to error
.