simfish:

Litdoc

Litdoc is a command-line utility useful for authoring literate programs using Emacs org-mode and GNU/Guix.

cover image

Installation

A Linux system equipped with Git and GNU/Guix See the official GNU Guix installation instructions. is assumed.

To get started, clone the litdoc project repository to some directory of choice, for example ~/opt/litdoc.

 mkdir -p ~/opt
 cd ~/opt
 git clone https://gitlab.com/aarongile/projects/litdoc

Interaction with Litdoc is based on its command-line interface litdoc. Integrate the command to a shell environment of choice by sourcing .litrc file. For Bash users, for example, add the line below to ~/.bashrc.

# assuming /op/litdoc is the install path:
. /opt/litdoc/.litrc

Optionally add one or more search paths and export is as LITDOC_PATH, an environment variable litdoc inspects when searching org files by name.

# if more than one path is available,
# use `:` to seperate each
export LITDOC_PATH=#/path/first:/path/second:/path/third: ... /pathN

That's it, Litdoc is installed and ready to go. To verify the installation run: litdoc a shell output of the form shown below should be displayed:

Usage:

 litdoc <org-name>
 litdoc site <build|load|ls|lsdeps|find|inspect> <org-name>

Software requirements:
- GNU/Guix
- Git

Report bugs to: https://gitlab.com/arongile/litdoc/issues
Home page: https://simfish.dev/projects/litdoc

Getting Started

Literate programming with Emacs org-mode involves authoring documents that mix prose and code. In org, code is expressed using source blocks that can be executed using Babel Babel is Org's ability to execute source code within Org documents. .

When using LitDoc, in addition to mixing code with prose and executing it in-place, it's also possible to specify per document software dependency and execute code from within the command-line without even using Emacs.

This is made possible by,

To get a better sense of how these work together, consider the following hello world document, hello.org

#+title: hello world

This is a hello world literate document. It uses
Python to print /"hello world."/

#+begin_src shell :tangle main
 #! /usr/bin/env python3

 print("Hello World!")

#+end_src

To run this document =python= has to be available locally.
The dependency can be specified using a scheme manifest
source block
#+begin_src scheme  :tangle manifest

(specifications->manifest '("python"))

#+end_src

To run the document switch to a directory where, hello.org is located and execute and run,

# verbose is optional
litdoc hello --verbose

The command should produce the expected output:

> Hello World!

Behind the scene, Litdoc first parses the document, if it has not already, and extracts code blocks. It then inspects the extracted blocks to setup correctly an isolated Guix profile, which is activated whenever the document is executed.

Managing Dependencies

A Litdoc document can specify within document software dependency using specialized Guile/Scheme or Emacs/Lisp source block.

A Guile/Scheme based source block uses Guix's manifest specification syntax This approach is useful for listing software dependencies in bulk, See Basic Setup with Manifests .

,#+begin_src  scheme :tangle manifest

 (specifications->manifest '("emacs-next" "emacs-helm"))

,#+end_src

Notice the block's tangle property is set to the keyword manifest. This pseudo tangle target instructs Litdoc the document dependencies are specified in this one block.

Alternatively, instead of just specifying multiple dependencies in a single manifest, each dependency can be specified separately using an Emacs/Lisp block and a use-package syntax In contrast to a Guile/Scheme based manifest, an Emacs/Lisp use-package based package specification is not required to have a tangle target. Instead the keyword :guix is applied on a per package basis .

,#+begin_src emacs-lisp

;; Helm
(use-package helm
  :guix (:name emacs-helm)
  :config
  (helm-mode 1))

,#+end_src

This approach is particularly useful when managing Emacs packages. As it is customary to place package specification along with its configuration for readability.

In either of the two case, emacs.org document can be made available in the current shell environment Both package specification approaches do not require the existence of Emacs.

# notice we drop .org extension
litdoc build emacs --verbose

Running Code

Litdoc provides a command-line interface, litdoc, for interacting with org documents. Given an input org file ORGNAME.org litdoc can tangle, build, load, and run the file.

litdoc site tangle /ORGNAME/ tangle org document ORGNAME.org
litdoc site build /ORGNAME/ build a Guix profile after parsing source blocks in /ORGNAME.org/
litdoc site load /ORGNAME/ load Guix profile for /ORGNAME.org/ in the current shell
litdoc site run /ORGNAME/ run shell script specified in the main block

To execute an document, ORGNAME.org, use

# short form
litdoc ORGNAME
# long form
litdoc site run ORGNAME
# long form with additional paramaters
litdoc site run ORGNAME -- param1 param2 --opt=1 -o2 ...

This works as long as ORGNAME.org contains a main shell block. That is, a shell source block with its :tangle property set to main. Adding to the emacs.org example, the code below adds a daemon based Emacs launcher script:

,* Running Emacs

  Emacs can be run as a daemon using the =--daemon= option.
  Once the daemon is started a light weight Emacs instance
  can be used to connect the daemon using =emacsclinet=.
  This approach will significantly improve Emacs startup time
  by applying load Emacs only once policy.

 ,#+begin_src shell :tangle main
 ,#!/bin/env bash

  if ! emacsclient -e 0 >&/dev/null; then
    emacs --daemon &
  fi
  emacsclient -c "$@"

 ,#+end_src

The main block can be run from the command-line with:

# verbose is optional
litdoc emacs --verbose

Setting up a Shell Environment

In addition to making org document runnable, it is also possible to provide code block that enables the documents to be to hooked into the host machine's shell environment. This is achieved using host.env code block.

A host.env block is useful for setting up shell variables, functions, and aliases. Any entry that goes into host.env block is equivalent to a code block that gets sourced from ~/.bashrc or the like.

The code snippet below Following example developed in sections 1,2 creates the alias command e, that introduces a shortcut for launching Emacs using Litdoc:

,* Startup Settings

 Settings in this section is intended to be sourced
 as part of the host's interactive shell session.

,#+begin_src  shell :tangle host.env

  alias e="litdoc emacs"

,#+end_src

Once this configuration is applied by running litdoc site load emacs, typing in e on the terminal should launch Emacs.

Setting up Container Environment

By default org code blocks runs in the host machine. To run code in a GNU/Guix container environment include a container.opt shell block. The block contains a list of Guix container shell options See Invoking Guix Shell ,which instruct litdoc to run code in a containerized environment.

Building on the emacs.org example, and adding the org snippet below will instruct Litdoc to run Emacs in a GNU/Guix container environment:

,* Containerization

This code snippet below configures Emacs to run
in a Guix container environment.

,#+begin_src shell :tangle container.opt

# enable access to network
 --network

# working directory
--share="${HOME}/Documents/dev=/home/user/dev

# X11 client/server communication
--expose=/tmp/.X11-unix

,#+end_src

Further, the container.opt block configures the container's access to several system resources. The --expose option, for example, is used to grant read-only access to selected file system entries. In this case, to /tmp/.X11-unix, which enables Emacs to connect with an X11 server.

In contrast, the --share option is used to grant both read and write access to file system entries. In the example above, it grants the container read-write access to ${HOME}/Documents/dev directory, which maps to /home/user/dev within the container environment This is a powerful feature and enables composing custom file system layout by combining several paths .

In general, container.opt options configure access to system resources such as the file system and network. Some useful resource access configuration options are summarized in table-1 For a complete list of available options see Invoking Guix shell .

Resource Access Control Comment
--network allow containers to have network access
--timeout=T Signal error if the container does not load after T second.
--max-jobs=N specify the number of build jobs during container construction.
--cores=N specify the number of CPU core to use.
--expose=SPEC allow containers to have read-only access to host
file or directory specified with SPEC
--share=SPEC allow containers to have both read and write access to
host file system specified with SPEC
SPEC has the form:
--share /tmp/t1=/tmp: map host /tmp/t1 => container /tmp
--share /tmp/t1: map host /tmp/t1 => =/tmp/t1