Org-Mode: Run Code Live in a Reveal slideshow with Klipse

Slide-based presentations are pretty useful and in any case de rigeur for contemporary lecturing, but it can be a REAL pain to switch form a slideshow to a code editor and then back again to a web browser to show the results of the code… Much better would be to have all the code embedded directly in your presentation. This also makes it easier to check over your code when you’re rewriting your lectures from year to year, and also helps with literate programming.

It turns out that there are actually several solutions for doing this with Org. They are:

  • directly embed JSBin in your slideshow
  • Use RevealEditor for HTML, CSS, and JS that you want to render in a web page.
  • Use the new klipse plugin for code that you want to evaluate (rather than render)

Each method has advantages and disadvantages, and each requires a certain amount of setup in your config. In this post I’m talking about using Klipse because I’ve bene able to make it actually work!

The Basics: ox-reveal

To start with, you will need to install org-reveal as well as its dependencies (org-mode and reveal.js). I install org-reveal from github instead of from ELPA, in part because I need to make modifications to both it and reveal.js.

Follow the detailed instructions until you have a working export to reveal. Make sure that you have activated the “highlight” plugin in org-reveal-plugins, which you can do with M-x customize-variable org-reveal-plugins. None of the tricks described below will work without that plugin!

Running Code in Klipse

Klipse is a web repl that supports a handful of languages, and can be extended to support more. Code is embedded in a CodeMirror instance and evaluated as you type (pretty cool).

Unfortunately CodeMirror and Reveal don’t get along very well so it’s not completely straightforward to embed Klipse directly into a reveal.js slideshow. The workaround is to replace a code-snippet tag with an iframe element containing the code in a form that klipse can read. Klipse’s author @viebel has kindly provided an example of how to do this here.

The next trick is to set up org-reveal to use this snippet. After messing around for a bit it became obvious that org-mode’s standard Export filters were going to be pretty hard to use, so I decided to rewrite some of org-reveal’s functionality:

Add Support for Third-Party Plugins

Org-reveal has excellent support for reveal.js’s “build-in” plugins, but using third-party plugins requires you to use the or-reveal~postamble variable~, which didn’t seem right to me. So I added a defcustom and a few small generic tricks to support loading third-party plugins. Then I downloaded @viebel’s klipse_reveal.js script and saved it to the plugin folder of my reveal.js installation. Finally, I set org-reveal-external-plugins to ((klipse . "{src: '%splugin/klipse_reveal.js'}")) using the customize-variable interface.

Rewrite org-reveal-src-block

At this point, the plugin loads, but it couldn’t find the exported src blocks. As I mentioned above, I was unable to find a satisfactory solution using export filters, so in the end I just rewrote the function org-reveal-src-block, which ox-reveal uses to process src blocks. I decided to keep the original <pre><code> syntax and simply add on an additional <klipse-snippet block, using the display:none CSS property for the old code block. This ensures compatibility with the reveal-editor solution (which isn’t currently working for me, but offers some other features.

Here’s my modified function:

(defun org-reveal-src-block (src-block contents info)
  "Transcode a SRC-BLOCK element from Org to Reveal.
CONTENTS holds the contents of the item.  INFO is a plist holding
contextual information."
  (if (org-export-read-attribute :attr_html src-block :textarea)
      (org-html--textarea-block src-block)
    (let* ((use-highlight (org-reveal--using-highlight.js info))
           (lang (org-element-property :language src-block))
           (caption (org-export-get-caption src-block))
           (code (if (not use-highlight)
                     (org-html-format-code src-block info)
                   (cl-letf (((symbol-function 'org-html-htmlize-region-for-paste)
                              #'buffer-substring))
                     (org-html-format-code src-block info))))
           (frag (org-export-read-attribute :attr_reveal src-block :frag))
           (code-attribs (or (org-export-read-attribute
                         :attr_reveal src-block :code_attribs) ""))
           (label (let ((lbl (org-element-property :name src-block)))
                    (if (not lbl) ""
                      (format " id=\"%s\"" lbl))))
           (klipsify  (and (assoc 'klipse org-reveal-external-plugins)
                           (member lang '("javascript" "ruby" "scheme" "clojure" "php"))))
                      )
      (if (not lang)
          (format "<pre %s%s>\n%s</pre>"
                  (or (frag-class frag info) " class=\"example\"")
                  label
                  code)
        (format
         "<div %sclass=\"org-src-container\">\n%s%s\n</div>%s"
         (if klipsify "style=\"display:none;\" " "")
         (if (not caption) ""
           (format "<label class=\"org-src-name\">%s</label>"
                   (org-export-data caption info)))
         (if use-highlight
             (format "\n<pre%s%s><code class=\"%s\" %s>%s</code></pre>"
                     (or (frag-class frag info) "")
                     label lang code-attribs code)
           (format "\n<pre %s%s>%s</pre>"
                   (or (frag-class frag info)
                       (format " class=\"src src-%s\"" lang))
                   label code)
           )
         (if klipsify (format "<klipse-snippet data-language=\"%s\">%s</klipse-snippet>"
                              lang code) ""))))))

As you can see, it now decides whether or not to “klipsify” the code aafter checking if the klipse plugin is loaded, and if it supports the language of the src block. Klipsifying code just means hiding the original code block and instead showing the klipse-snippet, which will be replaced by an iframe in javascript. I guess I could just do that work right in this same elisp function! Oh well, maybe next time.

So far, this seems to work great, though I don’t have access to the DOM of the larger document ,which is sort of a drag.

[UPDATE 2016-12-11 Sun]: However, @viebel pointed out to me that you can access the parent frame with:

var d = parent.document

Pretty cool! Note: klipse will hang if you try to type this in directly. It will only work if you pass the completed line in as part of the initial code block.

Also, this is not a solution that works for rendering HTML pages. For this we need something else. I would like to use RevealEditor, but am not quite there yet (can’t get its code to load properly). So for now I’m embedding a JSBin instance with preoaded code – I just add an iframe tag directly in my org-mode file. It’s not a s pretty or robust, and I can’t see my code when I’m writing, which is too bad, but it’s still pretty useful.

I would really like to hear what other people think! I’d especially like to hear about alternatives or improvements.