literate-elisp is an Emacs lisp library to provide an easy way to use literal programming in Emacs lisp.
It extends the Emacs load mechanism so Emacs can load org files as lisp source files directly.
The implementation details of literate-elisp is in file ./literate-elisp.org (pdf version).
This library export an Emacs Lisp function literate-elisp-load
and this function can load Emacs Lisp codes in all Emacs Lisp code blocks
surrounded by #+begin_src elisp
and #+end_src
in an org file directly.
For example, if you have an org file and it contains such Emacs Lisp code block:
#+BEGIN_SRC elisp :load yes (defun test () (message "this is a test from org file.~%")) #+END_SRC
Then you can load this org file like this:
(load "~/projects/literate-elisp/literate-elisp.el")
(literate-elisp-load "test.org")
Now the Emacs Lisp function test
is ready to use,
and you can jump to the definition of test
in the org file by using Emacs library find-func directly
without tangling them to an Emacs Lisp file(.el).
You can also open an org file by polymode when edit, which will switch Emacs Lisp code block to Emacs Lisp mode.
So you will have a perfect literate environment which can write Emacs Lisp codes in an org file, and load it directly without tangling an org file to an Emacs Lisp file. Emacs lisp’s source code editing and navigation feature work well to this org file.
This library contains the following files:
- ./literate-elisp.org
The implementation and documentation of literate Emacs Lisp reader. - ./literate-elisp.el
The tangled codes of literate Emacs Lisp reader, generated from ./literate-elisp.org. - ./literate-elisp.pdf
The weaved documentation, generated from ./literate-elisp.org by org mode’s publish feature. - ./readme.org
This file contains introduction and demo codes for how to do literate Emacs Lisp in an org file. - ./.travis.yml
The config file used by Web service travis ci to test this library.
The org file can open as polymode,the following Emacs lisp scripts should add in .emacs
.
(use-package poly-org
:ensure t)
literate-lisp
is available on the two major package.el
community maintained repos - MELPA Stable and MELPA.
You can install literate-elisp
, or more commonly a specific literate-elisp
, interactively
M-x package-install [RET] literate-elisp [RET]
Or you can install it from source directly
(load "~/projects/literate-elisp/literate-elisp.el")
Please have a look of the section How to insert code block in org file.
To load an org file, we can use Emacs interactive command literate-elisp-load
.
For example, This org file can load directly with the following code.
(literate-elisp-load "readme.org")
If you want to load an org file with a Emacs command, please press “Alt-X” and type literate-elisp-load-file
.
If you want to load an org file in batch mode, please use function literate-elisp-batch-load
.
To byte compile an org file to an elc
file, we can use Emacs interactive command literate-elisp-byte-compile-file
.
For example, This org file can be compiled with the following code.
(literate-elisp-byte-compile-file "readme.org")
Now the target file readme.org.elc
is ready to use.
Source blocks in a literate program can serve a variety of
purposes—implementation, examples, testing, and so on—so we define a
load
Org code block header argument to decide whether to read them
or not, which accepts the following values -
- yes
The code block should be loaded normally. This is the default when the header argumentload
is not provided.#+BEGIN_SRC elisp :load yes (defun a-function-to-load () (message "this function will be loaded by literate-elisp.~%")) #+END_SRC
- no
The code block should be ignored by the Emacs Lisp reader.#+BEGIN_SRC elisp :load no (defun a-function-to-ignore () (message "this function will be ingored by literate-elisp.~%")) #+END_SRC
- test
The code block should be loaded only when the variableliterate-elisp-test-p
is true.#+BEGIN_SRC elisp :load test (defun a-function-to-test () (message "this function will be loaded by literate-elisp only if literate-elisp-test-p is true.~%")) #+END_SRC
- the name of a variable or function
The code block is loaded if the value of the variable or the return value of the function is non-nil.
We have some extension to keep compatibility with other libraries, the following sections show how to setup them.
;; ;; To make `elisp-refs' work with `literate-elisp', we need to add an advice to `elisp-refs--read-all-buffer-forms'.
(eval-after-load "elisp-refs"
'(advice-add 'elisp-refs--read-all-buffer-forms :around #'literate-elisp-refs--read-all-buffer-forms))
;; To make `elisp-refs' work with `literate-elisp', we need to add an advice to `elisp-refs--loaded-paths'.
(eval-after-load "elisp-refs"
'(advice-add 'elisp-refs--loaded-paths :filter-return #'literate-elisp-refs--loaded-paths))
;; To make `helpful' work with `literate-elisp', we need to add an advice to `helpful--find-by-macroexpanding'.
(with-eval-after-load 'helpful
(advice-add 'helpful--find-by-macroexpanding :around #'literate-elisp-helpful--find-by-macroexpanding))
- try to use Emacs command
check-parens
. - toggle on the debug mode of literate-elisp
(setf literate-elisp-debug-p t)
Yes you can, just like the original function in a elisp source file.
As a demo org file, we write a simple demo macro aif
here.
Sometimes we want to use the expression value of if
condition form when it yields non-nil.
That’s the purpose of aif
which will bind variable it
to the value of if
condition form.
We will use some common lisp macros, so let’s load this library now.
(require 'cl)
Let’s implement if-bind
firstly,
which can bind the value of if
condition form to any specified variable.
(defmacro if-bind (var test &rest then/else)
"Anaphoric IF control structure.
VAR (a symbol) will be bound to the primary value of TEST. If
TEST returns a true value then THEN will be executed, otherwise
ELSE will be executed."
(cl-assert (car then/else)
(then/else)
"IF-BIND missing THEN clause.")
(cl-destructuring-bind (then &optional else)
then/else
`(lexical-let ((,var ,test))
(if ,var ,then ,else))))
Now aif
is easy to finish.
(defmacro aif (test then &optional else)
"Just like IF-BIND but the var is always IT."
`(if-bind it ,test ,then ,else))
You can use it like this
(aif (and (y-or-n-p "Try it")
10)
(message "it is %s" it))
After loading this org file by function literate-elisp-load
,
you can use macro aif
directly in your other Emacs Lisp files.
Of course the one purpose of this library is to write Emacs configuration directly in an org file.
Here we give a demo configuration and the way to load such org config file.
(add-to-list 'auto-mode-alist '("\\.\\(org\\|org_archive\\)$" . org-mode))
Then to load routines and configurations in this org file, I add the following codes in my .emacs
(load "~/projects/literate-elisp/literate-elisp.el")
(literate-elisp-load "~/projects/literate-elisp/readme.org")
- helm-q Manage remote q sessions with Helm and q-mode
We use ERT library to define and run tests. Web service travis ci will load config file ./.travis.yml to run these tests automatically every time there is a new git change.
(ert-deftest literate-demo-aif ()
"A spec of macro aif."
(should (equal (aif 10 it 9) 10)))
(ert-deftest literate-demo-org-mode ()
"A spec of macro aif."
(should (equal (cl-loop for (x . y) in auto-mode-alist
if (eq y 'org-mode)
return x)
"\\.\\(org\\|org_archive\\)$")))
Code and documentation copyright 2018-2019 Jingtao Xu.
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 of the License, 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 this program. If not, see http://www.gnu.org/licenses/.