PhaultLines

Slide deck syntax highlighting made easy with Alfred

I’ve spent a lot of time lately creating slide decks for my MontageJS presentations. When I include source code in a slide, I like to have syntax highlighting to help make it easier for the audience to read. Unfortunately, syntax highlighting isn’t a built-in feature in Powerpoint or Keynote. There are some great browser-based presentation frameworks like reveal.js that offer native syntax highlighting, but I currently need to use a standard tool so that other people can easily edit and consume my decks.

After giving the matter some thought, I decided to solve my problem by building an Alfred workflow. My new workflow (which you can download here) applies RTF syntax highlighting to the contents of the clipboard, making it easy to paste the colored text into Keynote and other applications. The workflow includes several simple scripts written in Python. It uses the popular Pygments library to perform the highlighting.

A slide I made for a recent MontageJS presentation

When the user types the highlight keyword in Alfred, the workflow uses a simple script filter to let the user select the desired programming language. After a language is selected, the workflow runs a script that extracts the current contents of the clipboard, colorizes it with the lexer for the desired language, and then replaces the contents of the clipboard with the RTF output.

Unfortunately, the standard “Copy to Clipboard” mechanism provided by Alfred’s workflow system doesn’t appear to support anything other than plain text. When I used it to place Pygment’s output into the clipboard, pasting would produce a plain text string with all of the RTF escape sequences. To work around that limitation, I ended up handling the clipboard interaction programmatically in my workflow’s Python script.

The PyObjC bridge provides direct access to NSPasteboard, which you can use to manually put RTF content into the clipboard as rich text. The following code is my simple colorization script:

import AppKit
from pygments import highlight
from pygments.lexers import get_lexer_by_name
from pygments.styles import get_style_by_name
from pygments.formatters import RtfFormatter

clip = AppKit.NSPasteboard.generalPasteboard()
content = clip.stringForType_(AppKit.NSStringPboardType)

lexer = get_lexer_by_name("{query}", stripall=True)
result = highlight(content, lexer, RtfFormatter(style="solarized"))

arr = AppKit.NSArray.arrayWithObject_(AppKit.NSPasteboardTypeRTF)

clip.clearContents()
clip.declareTypes_owner_(arr, None)
clip.setString_forType_(result, AppKit.NSPasteboardTypeRTF)

As you can see, all you have to do to indicate that the clipboard data is rich text is use the NSPasteboardTypeRTF option. In my workflow, the “{query}” value passed into the script is the name of the programming language selected by the user. If you want to use the script by itself without Alfred, you can just replace that string so that it specifies the language that you want. I threw in nice a Solarized Dark style for Pygments that I found on GitHub, but you can change the style string in the script if you want a different color scheme.

There are command line tools like Highlight that provide similar functionality, but I find it very convenient to have this feature available through Alfred. The workflow is entirely self-contained, so it works seamlessly on every Mac where I have my Alfred workflows synchronized.