Exporting Formatted Code Snippets from Emacs

rayo - 13 Jun 2014

I wanted a way to create formatted code snippets (syntax highlighting, etc.) for pasting into GMail or Google Slides. The catch was I didn't want to use a web service (e.g. pastie, hilite.me) or switch from Emacs to another text editor (e.g. Sublime with the SublimeHighlight plugin).

Learning from Markdown-Mode

I remembered that Emacs markdown-mode lets you preview, in a browser, the Markdown text rendered as HTML.

Basically, when you install markdown-mode, you also install a Markdown executable like Python-Markdown. When you invoke markdown-preview in Emacs, markdown-mode runs the Markdown executable in a shell. The input is the Markdown text in the current Emacs region, and the HTML output goes to a temporary Emacs buffer. The buffer's contents, in turn, are opened in a browser by the Emacs command browse-url-of-buffer.

Pygments to the Rescue

We could use a similar strategy to markdown-mode's for exporting formatted code snippets. We just need a command-line tool that will receive code from stdin and send it formatted to stdout.

Pygments fits the bill. Pygments is a syntax higlighter written in Python, with many lexers and formatting options. It can be installed using pip (e.g. pip install pygments).

Lisp Isn't So Painful

After installing Pygments, you get a script called pygmentize. Of course to call it from Emacs, you'll need some Lisp. Here's a helper that constructs the desired invocation:

26 (defun pygmentize-html-command (beginning-line-number)
27   (let ((lexer (gethash major-mode pygmentize-lexers))
28         (linenostart (number-to-string beginning-line-number)))
29     (if (not lexer) (error (concat "error: no lexer for " (symbol-name major-mode))))
30     (concat "pygmentize -f html" 
31             " -l " lexer 
32             " -O style=autumn,linenos=inline,noclasses=true,linenostart=" linenostart)))

A lookup table maps Emacs major-modes to their lexer argument for pygmentize:

16 (setq pygmentize-lexers (make-hash-table))
17 (puthash 'emacs-lisp-mode "common-lisp" pygmentize-lexers)
18 (puthash 'scala-mode "scala" pygmentize-lexers)
19 (puthash 'java-mode "java" pygmentize-lexers)
20 (puthash 'ruby-mode "rb" pygmentize-lexers)
21 (puthash 'python-mode "py" pygmentize-lexers)
22 (puthash 'sh-mode "sh" pygmentize-lexers)
23 (puthash 'diff-mode "diff" pygmentize-lexers)

The Emacs clip region is passed via stdin to pygmentize, with stdout redirected to a temporary buffer:

38 (defun pygmentize-html (&optional output-buffer-name)
39   (interactive)
40   (save-window-excursion
41     (unless output-buffer-name
42       (setq output-buffer-name pygmentize-html-output-buffer-name))
43     (let* ((beginning-line-number (line-number-at-pos (region-beginning)))
44            (shell-command (pygmentize-html-command beginning-line-number)))
45       (shell-command-on-region (region-beginning) 
46                                (region-end) 
47                                shell-command
48                                output-buffer-name))
49     (switch-to-buffer output-buffer-name)
50     (clipboard-kill-ring-save (point-min) (point-max))
51     output-buffer-name))

The temporary buffer's syntax-highlighted, HTML-formatted contents will be opened in a browser, using Emacs's browse-url-of-buffer function:

53 (defun pygmentize-html-preview (&optional output-buffer-name)
54   (interactive)
55   (unless output-buffer-name
56     (setq output-buffer-name pygmentize-html-output-buffer-name))
57   (browse-url-of-buffer (pygmentize-html output-buffer-name)))

Gotta have a keybinding:

59 (global-set-key (kbd "C-c h p") 'pygmentize-html-preview)

Exporting the Snippets

This gist contains the full Emacs-Lisp code. (And it easily embeds into a .emacs file.)

To export a snippet, highlight some code, and invoke pygmentize-html-preview:


In the launched browser window, select all and copy:


Paste into GMail, Google Slides, or anything that will accept formatted HTML:


comments powered by Disqus