-- encoding:utf-8 Mode: POLY-ORG; -- ---

May 28, 2026 ยท View on GitHub

#+Startup: noindent #+PROPERTY: literate-lang lisp #+PROPERTY: literate-load yes #+PROPERTY: literate-insert-header no

[[http://quickdocs.org/literate-lisp/][file:http://quickdocs.org/badge/literate-lisp.svg]] [[https://github.com/jingtaozf/literate-lisp/actions][file:https://github.com/jingtaozf/literate-lisp/workflows/Continous%20Integration/badge.svg]]

By using this package ([[https://github.com/jingtaozf/literate-lisp][literate-lisp]]), Emacs [[https://orgmode.org/][Org mode]], and Emacs Lisp library [[https://polymode.github.io/][polymode]], literate programming can be very easy, with one Org file containing both documentation and source code, and this Org file can interact well with [[https://common-lisp.net/project/slime/][SLIME]].

The implementation detail of [[https://github.com/jingtaozf/literate-lisp][literate-lisp]] is in file [[./literate-lisp.org]] ([[./literate-lisp.pdf][pdf version]]).

This library contains the following files:

  • [[./literate-lisp.org]] \ The implementation and documentation of literate lisp reader.
  • [[./lisp]] \ This directory contains the tangled code of literate lisp reader, generated from [[./literate-lisp.org]]
  • [[./literate-lisp.pdf]] \ The weaved documentation, generated from [[./literate-lisp.org]] by Org mode's [[https://orgmode.org/manual/Triggering-publication.html#Triggering-publication][publish feature]].
  • [[./readme.org]] \ This file contains introduction and demo code for how to do literate lisp in an Org file.
  • [[./puzzle.org]] \ This file contains a puzzle solver to show how to do literate lisp in an Org file.
  • [[./.github/workflows/continuous-integration.yml][continuous-integration.yml]] \ The config file used by Web service [[https://github.com/jingtaozf/literate-lisp/actions][GitHub actions]] to test this library.
  • [[./literate-lisp.asd]] \ The ASDF definition for literate-lisp project.
  • [[./literate-demo.asd]] \ The ASDF definition for literate demo project.
  • Tutorial ** The restriction to the Org file The Org file should start with a comment character and a space character("# "), to drive lisp reader into Org syntax. Actually it can be a convenient way for us to specify some local variables, for example I often put them in the first line of an Org file: #+BEGIN_SRC org

-- encoding:utf-8 Mode: POLY-ORG; -- ---

#+END_SRC Which make Emacs open file with utf-8 encoding and [[https://github.com/polymode/poly-org][poly-org-mode]]. ** Installing polymode in Emacs It's better to edit the Org file with [[https://polymode.github.io/][polymode]], which will make code block use its native file mode. The following Emacs Lisp scripts in .emacs will install it. #+BEGIN_SRC elisp (use-package poly-org :ensure t) #+END_SRC ** How to insert code block quickly Please have a look of the section [[https://github.com/jingtaozf/literate-elisp/blob/master/literate-elisp.org#how-to-insert-code-block-in-org-file][How to insert code block in Org file]] in library [[https://github.com/jingtaozf/literate-elisp][literate-elisp]].

Please note that =literate-lisp= now support parsing Org property values ([[https://orgmode.org/manual/Property-Syntax.html][property syntax]]), so there is no need to insert =:load no= in header argument now, you can set them as Org properties.

To disable insertion of unnecessary header argument, you can set Org property =literate-insert-header= to =no=. ** The load code block header argument Please have a look of the section [[./literate-lisp.org#new-defined-header-argument-load][new defined header argument load]] in [[./literate-lisp.org]]. ** Reference named blocks as global parameters If a [[https://orgmode.org/manual/Blocks.html][block]] has a named for it, that is, with a =#+NAME:= before it like this: #+begin_example ,#+NAME: js-demo-code ,#+BEGIN_SRC js document.getElementById("demo").innerHTML = "Hello JavaScript"; ,#+END_SRC #+end_example Then after loading, a global parameter =js-demo-code= will contain the string in above block.

It is more friendly than write this string in lisp directly, because =org-mode= can provide syntax for it and =poly-mode= can even enable us edit this code block in =js-mode=.

You can visit these named blocks by Emacs Lisp function [[https://orgmode.org/worg/orgcard.html#org11fbe72][org-babel-goto-named-src-block]], or by hacking [[https://github.com/joaotavora/sly][sly]] like this: #+BEGIN_SRC elisp :load no (defun sly-edit-definition-of-named-block (&optional name method) (when (string-prefix-p "#+END_" (string-trim (buffer-substring (line-beginning-position) (line-end-position))) t) (let ((case-fold-search t)) (search-forward-regexp (format "#\+NAME:\s+%s" name)) (forward-line 2) (goto-char (line-beginning-position))))) (eval-after-load "sly" '(advice-add 'sly-edit-definition :after #'sly-edit-definition-of-named-block)) #+END_SRC You can hack [[https://common-lisp.net/project/slime/][slime]] the same way.

NOTE You have to install literate-lisp to active =readtable= in [[https://github.com/joaotavora/sly][sly]] when using SBCL to make above patch work, because the SBCL backend in =sly= will [[https://github.com/joaotavora/sly/blob/master/slynk/backend/sbcl.lisp#L423][read the source code inside file]] when find definitions. #+BEGIN_SRC lisp :load no (literate-lisp:install-globally) #+END_SRC ** How to debug Org file in LispWorks IDE You have to add the following code in your .lispworks to enable the debug facility in Lispworks Editor. #+BEGIN_SRC lisp :load no (defun check-org-mode (buffer truename) (when (and truename (equal (pathname-type truename) "org")) (setf (editor:buffer-major-mode buffer) "Lisp"))) (editor:add-global-hook editor::read-file-hook 'check-org-mode) #+END_SRC Thanks for Martin Simmons in [[http://www.lispworks.com/][LispWorks]] to support the above configuration code. ** How to integrate with namded-readtables You may find that [[https://github.com/melisgl/named-readtables][named-readtables]] is friendly to define the syntax for literate-lisp in your code [[https://github.com/jingtaozf/literate-lisp/issues/12#issuecomment-710256276][like this]]: #+BEGIN_SRC lisp :load no (named-readtables:defreadtable literate-lisp (:merge :standard) (:dispatch-macro-char ## #\space #'literate-lisp::sharp-space) (:dispatch-macro-char ## #+ #'literate-lisp::sharp-plus)) #+END_SRC

** How to write user initialization file with literate programming style You can put all initialization code in an Org source file, all you need is to load literate-lisp firstly. For example, you can put the following code in file [[http://www.sbcl.org/manual/#Initialization-Files][$HOME/.sbclrc]] for SBCL. #+BEGIN_SRC lisp :load no (require :asdf) #-quicklisp (let ((quicklisp-init "/quicklisp/setup.lisp") (quicklisp-install "/quicklisp.lisp")) (cond ((probe-file quicklisp-init) (format terminal-io "loading quicklisp...%") (load quicklisp-init) (format terminal-io "loading quicklisp...done%")) ((probe-file quicklisp-install) (load quicklisp-install) (funcall (intern "INSTALL" :quicklisp-quickstart)))))

(load "/projects/common-lisp/literate-lisp/literate-lisp.asd") (ql:quickload :literate-lisp) (literate-lisp:with-literate-syntax (load "/projects/common-lisp/config/init-lisp.org")) #+END_SRC I find it useful for various Lisp vendors so all initialization code for them can be in just one file.

** How to include Org code with ASDF package-inferred-system extension The [[https://common-lisp.net/project/asdf/asdf.html#The-package_002dinferred_002dsystem-extension][ASDF package-inferred-system extension]] is wonderful, in which each file is its own system, and dependencies are deduced from the defpackage form or its variant, uiop:define-package. You can also use literate-lisp to make a package inferred system by writing an ASD definition like this: #+BEGIN_SRC lisp :load no (asdf:defsystem literate-libraries :serial t :defsystem-depends-on (:literate-lisp) :default-component-class :org :class :package-inferred-system) #+END_SRC Here =:class :package-inferred-system= enables the package-inferred-system extension, and =:default-component-class :org= means that ASDF will look for all Org files to find out a system and load it.

For example, you can create an Org file in the same directory of above ASD definition file named as utilities.org and contains the following code #+begin_example

-- encoding:utf-8 Mode: POLY-ORG; -- ---

  • Create a package for this package inferred system ,#+BEGIN_SRC lisp (defpackage literate-libraries/utilities (:use :cl) (:import-from :flexi-streams :octet :make-flexi-stream) (:import-from :log4cl :log-config) (:documentation "a utility module.")) ,#+END_SRC
  • implementation ... ... #+end_example After loading the above ASD definition file, you can load system literate-libraries/utilities in your REPL. #+BEGIN_SRC lisp :load no (load "/some/path/literate-libraries.asd") (ql:quickload :literate-libraries/utilities) #+END_SRC

Please upgrade to ASDF 3.3.4.5 or later, it is not supported in earlier ASDF versions.

** How to tangle to a bundle of lisp files from one Org file Yes, now you can tangle one Org file to a bundle of lisp files, so to share it to team members with more clear interface.

Please have a look of [[./literate-lisp.org#tangle-to-multiple-files-for-one-org-file][tangle to multiple files for one Org file]] or the usage of Org property =LITERATE_EXPORT_PACKAGE= and =LITERATE_EXPORT_NAME= in file [[./literate-lisp.org]].

** Packages written by literate-lisp

In a short word, we should load literate-lisp by ASDF keyword :defsystem-depends-on and declare the Org source file with new ASDF keyword :org.

Now let's define the ASDF system file [[./literate-demo.asd]] for this demo package #+BEGIN_SRC elisp :load no (asdf:defsystem literate-demo :author "Xu Jingtao jingtaozf@gmail.com" :version "0.1" :licence "MIT" :serial t :description "a demo project of literate-lisp" :defsystem-depends-on ("literate-lisp") :depends-on (:iterate #+dev :clgplot) :components ((:module :demo :pathname "./" :components ((:org "puzzle") (:org "readme")))) :properties ((version "0.1"))) #+END_SRC Which will load [[./puzzle.org]] and this file directly as a lisp source file.

The whole content of ASDF definition file is in [[./literate-demo.asd]]. *** A demo package for this file #+BEGIN_SRC lisp (defpackage :literate-demo (:use :cl) (:export )) (in-package :literate-demo) #+END_SRC *** Test cases :PROPERTIES: :literate-load: test :END: **** Preparation The [[https://common-lisp.net/project/fiveam/][FiveAM]] library is used to test. #+BEGIN_SRC lisp (eval-when (:compile-toplevel :load-toplevel :execute) (unless (find-package :fiveam) #+quicklisp (ql:quickload :fiveam) #-quicklisp (asdf:load-system :fiveam))) (5am:def-suite literate-demo-suite :description "The test suite of literate-demo.") (5am:in-suite literate-demo-suite) #+END_SRC **** test case for named block Let's define a named code block for some javascript code: #+NAME: js-demo-code-1 #+begin_src js { console.log("Hello"); } #+end_src Then try to read it in our test case #+BEGIN_SRC lisp (5am:test named-block (5am:is (stringp js-demo-code-1)) (5am:is (not (null (position #" js-demo-code-1 :test #'char=))))) #+END_SRC **** run all tests in this library This function is the entry point to run all tests and return true if all test cases pass. #+BEGIN_SRC lisp (defun run-test () (5am:run! 'literate-demo-suite)) #+END_SRC