Support via Liberapay

Org-Docco

The docco tool (see http://ashkenas.com/docco/) generates HTML from JavaScript source code providing an attractive side-by-side display of source code and comments. This file (see index.org) generates the same type of output from Org-mode documents with code embedded in code blocks.

The way this works is an Org-mode document with embedded code blocks is exported to html using the standard Org-mode export functions. This file defines a new function named org-docco-buffer which, when added to the org-export-html-final-hook, will be run automatically as part of the Org-mod export process doccoizing your Org-mode document.

A pure source code file can be extracted (or "tangled") from the Org-mode document using the normal org-babel-tangle function. See Working With Source Code chapter of the Org-mode manual for more information on using code blocks in Org-mode files.

Disclaimer: this currently only works on very simple Org-mode files which have no headings but rather are just a collection of alternating text and code blocks. It wouldn't be difficult to generalize the following code so that it could be run in particular sub-trees but I simply don't have the time to do so myself, and this version perfectly satisfies my own limit needs. I make no promises to support this code moving forward. Caveat Emptor

;;; org-docco.el --- docco type html generation from Org-mode

;; Copyright (C) 2012 Eric Schulte

;; Author: Eric Schulte
;; Keywords: org-mode, literate programming, html
;; Homepage: https://orgmode.org/worg/org-contrib/org-mime.php
;; Version: 0.01

;; This file is not part of GNU Emacs.

;;; License:

;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 3, or (at your option)
;; any later version.
;;
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING.  If not, write to the
;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
;; Boston, MA 02110-1301, USA.

;;; Commentary:

;; <- look over there

The cl package provides all of the state-changing functions used below e.g., push and incf. It looks like a namespace-safe version of cl may soon be permissible for use in official Emacs packages.

;;; Code:
(require 'cl)

This is a function which returns the buffer positions of matching regular expressions. It has two special features…

  1. It only counts matched instances of beg-re and end-re which are properly nested, so for example if beg-re and end-re are set to ( and ) respectively and we run this against the following,

    1    2       3 4   5     6
    |    |       | |   |     |
    v    v       v v   v     v
    (foo (bar baz) (qux) quux)
    

    it will return 1 and 6 rather than 1 and 3.

  2. It uses markers which save their position in a buffer even as the buffer is changed (e.g., by me adding in extra HTML text).
(defun org-docco-balanced-re (beg-re end-re)
  "Return the beginning and of a balanced regexp."
  (save-excursion
    (save-match-data
      (let ((both-re (concat "\\(" beg-re "\\|" end-re "\\)"))
            (beg-count 0) (end-count 0)
            beg end)
        (when (re-search-forward beg-re nil t)
          (goto-char (match-beginning 0))
          (setq beg (point-marker))
          (cl-incf beg-count)
          (goto-char (match-end 0))
          (while (and (not end) (re-search-forward both-re nil t))
            (goto-char (match-beginning 0))
            (cond ((looking-at beg-re) (cl-incf beg-count))
                  ((looking-at end-re) (cl-incf end-count))
                  (:otherwise (error "miss-matched")))
            (goto-char (match-end 0))
            (when (= beg-count end-count) (setq end (point-marker))))
          (when end (cons beg end)))))))

This ugly large function does the actual conversion. It wraps the entire main content div of the exported Org-mode html into a single large table. Each row of the table has documentation on the left side and code on the right side. This function has two parts.

  1. We use (org-docco-balanced-re "<div" "</div>") to find the beginning and end of the main content div. We then break up this div at <pre></pre> boundaries with multiple calls to (org-docco-balanced-re "<pre class\"src" "</pre>").
  2. With all documentation/code boundaries in hand we step through the buffer inserting the table html code at boundary locations.
(defun org-docco-buffer ()
  "Call from within an HTML buffer to doccoize it."
  (interactive)
  (let ((table-start "<table>\n")
        (doc-row-start  "<tr><th class=\"docs\">\n") (doc-row-end  "</th>\n")
        (code-row-start "    <td class=\"code\">\n") (code-row-end "</td></tr>\n")
        (table-end "</table>" )
        pair transition-points next)
    (save-excursion
      (save-match-data
        (goto-char (point-min))
        (when (re-search-forward "<div id=\"content\">" nil t)
          (goto-char (match-end 0))
          (push (point-marker) transition-points)
          (goto-char (match-beginning 0))
          (setq pair (org-docco-balanced-re "<div" "</div>"))
          (while (setq next (org-docco-balanced-re "<pre class=\"src" "</pre>"))
            (goto-char (cdr next))
            (push (car next) transition-points)
            (push (cdr next) transition-points))
          (goto-char (cdr pair))
          (push (and (re-search-backward "</div>" nil t) (point-marker))
                transition-points)
          ;; collected transitions, so build the table
          (setq transition-points (nreverse transition-points))
          (goto-char (pop transition-points))
          (insert table-start doc-row-start)
          (while (> (length transition-points) 1)
            (goto-char (pop transition-points))
            (insert doc-row-end code-row-start)
            (goto-char (pop transition-points))
            (insert code-row-end doc-row-start))
          (goto-char (pop transition-points))
          (insert code-row-end table-end)
          (unless (null transition-points)
            (error "leftover points")))))))

We'll use Emacs File Local Variables and the org-export-html-final-hook to control which buffers have org-docco-buffer run as part of their export process.

(defvar org-docco-doccoize-me nil
  "File local variable controlling if html export should be doccoized.")
(make-local-variable 'org-docco-doccoize-me)

A simple function will conditionally process HTML output based on the value of this variable.

(defun org-docco-buffer-maybe ()
  (when org-docco-doccoize-me (org-docco-buffer)))

Finally this function is added to the org-export-html-final-hook.

(add-hook 'org-export-html-final-hook #'org-docco-buffer-maybe)

That's it. To use this simply;

  1. Checkout this file from https://github.com/eschulte/org-docco,

    git clone git://github.com/eschulte/org-docco.git
    

    and open it using Emacs.

  2. Tangle org-docco.el out of this file by calling org-babel-tangle or C-c C-v t.
  3. Load the resulting Emacs Lisp file.
  4. Execute the following in any Org-mode buffer to add file local variable declarations which will enable post-processed with org-docco-buffer.

    (add-file-local-variable 'org-export-html-postamble nil)
    (add-file-local-variable 'org-export-html-style-include-default nil)
    (add-file-local-variable 'org-docco-doccoize-me t)
    

    And add the following style declaration to make use of the docco.css style sheet taken directly from https://github.com/jashkenas/docco.

    #+Style: <link rel="stylesheet" href="docco.css" type="text/css">
    
(provide 'org-docco)
;;; org-docco.el ends here

Documentation from the orgmode.org/worg/ website (either in its HTML format or in its Org format) is licensed under the GNU Free Documentation License version 1.3 or later. The code examples and css stylesheets are licensed under the GNU General Public License v3 or later.