Quantcast
Channel: Mastering Emacs » Dired Shell Commands: The find & xargs replacement
Viewing all articles
Browse latest Browse all 10

Compiling and running scripts in Emacs

$
0
0

I’ve talked about running shells and executing shell commands in Emacs before, but that’s mostly used for ad hoc commands or spelunking; what if you want to compile or run your scripts, code or unit tests?

There’s a command for that…

Not surprisingly, Emacs has its own compilation feature, complete with an error message parser that adds syntax highlighting and “go to next/prev error” so you can walk up and down a traceback in Python, or jump to a syntax error, warning or hint in GCC.

There are two ways of using the compilation feature in Emacs: the easiest is invoking M-x compile followed by the compile command you want Emacs to run. So, for Python, you can type M-x compile RET python myfile.py RET and a new *compilation* buffer will appear with the output of the command. Emacs will parse the output and look for certain patterns — stored in the variables compilation-error-regexp-alist[-alist] — and then highlight the output in the compilation buffer with “hyperlinks” that jump to the file and line (and column, if the tool outputs it) where the error occurred.

One annoying thing about compile is its insistence on wanting to save every. single. unsaved buffer before continuing. It’s there to keep you from accidentally compiling a mix of newly saved and old, stale files, which would lead to unexpected behavior, and the only reason it’s still there — and why it insists on saving files unrelated to what you are compiling — is the complete lack of a proper, integrated project management suite in Emacs. But I’ll save that rant for later.

Anyway, if it offends you as much as it offends me, you can add this to your emacs file to shut it up:

;;; Shut up compile saves
(setq compilation-ask-about-save nil)
;;; Don't save *anything*
(setq compilation-save-buffers-predicate '(lambda () nil))

There is nothing stopping you from customizing compilation-save-buffers-predicate to take into account the files or directories so its save mechanism is cleverer, but I personally never bothered.

If compilation is successful — and like most things in the UNIX world, this is governed by the exit code — the modeline and compilation buffer will say so; likewise, if an error occurred, this is also displayed.

You can jump to the next or previous error ([next/previous]-error) with M-g M-n and M-g M-p — they’re bound to more keys, but I think those are the easiest ones to use. Similarly, in the compilation buffer itself (only), you can go to the next/previous file (compilation-[next/previous]-file) with M-g M-} and M-g M-{, respectively, and RET jumps to the location of the error point is on.

There is an undocumented convention in Emacs that commands like dired, grep, and compile can be rerun, reverted or redisplayed by typing g in the buffer.

By default the compilation buffer is just a dumb display and you cannot communicate with the background process. If you pass the universal argument (C-u) you can; the buffer is switched to comint mode, and you can converse with the process as though you were running it in the shell.

Python Debugging with Compile

I work mostly in Python, and quite often I have to debug the script I am writing with pdb, the Python debugger. I use breakpoints a lot, and I have a command bound to F5 that imports pdb and calls pdb.set_trace() — a standard Python debugging idiom. What it also does is highlight the line in bright red so I don’t miss it. And if I call compile from a Python buffer, it checks if there is a breakpoint present in the file: if there is, it switches to the interactive comint mode automagically; if there isn’t, it defaults to the “dumb” display mode. Having it switch automagically is perhaps a slightly pointless flourish, but screw it I’m using Emacs, not vim :)

This is the code I use. Feel free to adapt it for other languages. Send me an e-mail if you do something neat with it.

(require 'python)
(defun python--add-debug-highlight ()
  "Adds a highlighter for use by `python--pdb-breakpoint-string'"
  (highlight-lines-matching-regexp "## DEBUG ##\\s-*$" 'hi-red-b))
 
(add-hook 'python-mode-hook 'python--add-debug-highlight)
 
(defvar python--pdb-breakpoint-string "import pdb; pdb.set_trace() ## DEBUG ##"
  "Python breakpoint string used by `python-insert-breakpoint'")
 
(defun python-insert-breakpoint ()
  "Inserts a python breakpoint using `pdb'"
  (interactive)
  (back-to-indentation)
  ;; this preserves the correct indentation in case the line above
  ;; point is a nested block
  (split-line)
  (insert python--pdb-breakpoint-string))
(define-key python-mode-map (kbd "<f5>") 'python-insert-breakpoint)
 
(defadvice compile (before ad-compile-smart activate)
  "Advises `compile' so it sets the argument COMINT to t
if breakpoints are present in `python-mode' files"
  (when (derived-mode-p major-mode 'python-mode)
    (save-excursion
      (save-match-data
        (goto-char (point-min))
        (if (re-search-forward (concat "^\\s-*" python--pdb-breakpoint-string "$")
                               (point-max) t)
            ;; set COMINT argument to `t'.
            (ad-set-arg 1 t))))))

There’s a Minor Mode for that…

The compile workflow isn’t for everyone; some people want everything in one place: their shell. Well, good news then — you can have your cake and eat it. The minor mode M-x compilation-shell-minor-mode is designed for comint buffers of all sizes and works well with M-x shell and most modes that use comint. You get the same benefits offered by compile without altering your workflow. If you compile or run interpreted stuff in your Emacs shell you’ll feel like a modern-day Prometheus with this minor mode enabled!

Add this to your emacs file and the compilation minor mode will start when shell does.

(add-hook 'shell-mode-hook 'compilation-shell-minor-mode)

Share


Viewing all articles
Browse latest Browse all 10

Trending Articles