Write code with Emacs org-mode
Introduction
In this post, I aim to demonstrate the use of Emacs org-mode for taking notes that incorporate programming code. The goal was to not only display code with proper formatting and syntax highlighting but also to execute it and show the results of specific code blocks, allowing readers to reproduce the entire analysis if desired. The challenge was to sift through available examples and documentation to put together a summary that brings something new to what’s already been written about this topic. Initially, I intended to focus solely on customizing the PDF output, but as I explored the documentation, I discovered other aspects that I believe are worth sharing, most notably, customization of HTML output. As a result, however, the post ended up being quite long thus, here is a brief overview of each main section:
- Examples of how to work with code: Provides a simple description of the structure of a code block and how its main parts can influence the processing of code blocks.
- Setup and configuration: Provide instructions for installed all the required packages and third-party software to reproduce the examples.
- Processing code blocks: Provides a more detailed description of how code blocks are processed.
- Customization: Provides examples of how to customize the processing and export of org files mainly for HTML and PDF export
Examples of how to work with code
Code in an org file can be written either as in-line or in blocks. Among the various block types (e.g., example, comment, quote), the one that allows formatting and evaluation is known as code block or live code block. The term ‘live code blocks’ reflects their ability to modify the document content dynamically.
Blocks are defined using the keywords #+begin_src and #+end_src. Additional keywords and arguments can be used to control formatting, evaluation, and export. These include:
- A
namekeyword - A
languageargument - One or more
switchesarguments - One or more
header arguments
Name keyword
The name keyword allows to insert a reference to a block within the org file. For code blocks, this means one can evaluate a block like a function and insert its results directly into the text (which is the main purpose of inline code blocks). In the example below, a dataset gets processed in an R code block, but the results (a table) appear later in the document after the dataset and selection process are described.
#+name: AirQuality on line 1), which gets referenced later to insert the results (#+RESULTS: AirQuality on line 11) near the end of the text.Language argument
The language argument tells Emacs how to format, edit, and evaluate the code—provided that an interpreter for the language is available on the system, it is supported by Org, and it is properly configured in Emacs. Fortunately, Org provides out-of-the-box support for dozens of languages (a list that can be further extended through third-party packages). This is a powerful feature that allows combining the results of code blocks—written in different languages—all within a single document.
In the following example, Python is used to scrape a web page and transform a table (describing the list of languages with built-in support in Org-mode) from HTML into a pandas DataFrame. This result is then passed to an R block, where the DataFrame is filtered based on a list of computer languages of interest. Finally, the resulting table is inserted later in the document using the #+RESULTS: keyword.
Since this process requires several external packages, the example include an introductory section listing the commands for creating a virtual environment and installing all the required packages and language dependencies.
Table 1 shows a copy of the final output and Snippet 3 shows parts of the code blocks that produce it.
| Lang_N | Language | Identifier | Documentation | Maintainer |
|---|---|---|---|---|
| 3 | C++ | cpp | ob-doc-c | Thierry Banel |
| 5 | CLI | shell | ob-doc-shell | Matthew Trzcinski |
| 22 | LaTeX | latex | ob-doc-LaTeX | nan |
| 36 | Python | python | ob-doc-python | Jack Kamm |
| 37 | R | R | ob-doc-R | Jeremie Juste |
#+begin_src python, on line 3). Notably, in the second code block (line 10), the results of the first block are assigned to the variable df using the header argument :var df=code:ScrapTable(). Note the use of parentheses around the code block name; this indicates that it should be processed as a function call.Switch argument
The switch argument gives you more control over how code blocks are executed and exported. In the example below, the switches -n and -r were used to add line numbers to the code and remove any labels, respectively.
(ref:upd-upg) is included. Due to the -r switch, this label is not exported to the HTML file but can still be used to reference a specific line in the text. Interestingly, if the output format is HTML and the variable org-html-head-include-scripts is set to true, hovering over the link will highlight the cited line.Other switches include, -k which will keep the labels, +n which will continue the line numbers from previous blocks or, if a number is passed along (e.g. +n 5) it will set the line number at that value.Header arguments
Header arguments are probably the most important setting for controlling how code blocks behave, including how they’re evaluated or exported. In the examples above, a number of header arguments modified the code blocks locally. These arguments use a colon followed by the lowercase argument name (e.g. :session), though they can be specified in other ways (see the Header arguments section). Table 2 describes the ones we used in the examples.
| Argument | Used in | Description |
|---|---|---|
:session | Snippets 2 and 3 | Allows two or more code blocks wot share the same environment including packages (and their functions) and variables. |
:exports both | Snippets 2 and 3 | Exports the code and the results of the evaluation |
:colnames yes | Snippets 2 and 3 | Treats the first row of a table as a heading, also preservs the first row in the output. |
:results table | Snippet 3 | Try to interpret the output as an org-table. |
:var df=code:ScrapTable() | Snippet 3 | Assigns the returned value of the block code:ScrapTable to the variable df. The parenthesis at the end of the block name are needed to interpret the block as a function and not verbatim. |
:cache yes | Snippet 3 | Caches the results of the code block evaluation in cases where the inputs have not changed. |
org-babel-default-header-args and org-babel-default-inline-header-args which control how code blocks and in-line code are interpreted, respectively. Table 3 describes the default values.| Argument | Block | In-line | Description |
|---|---|---|---|
| :session | none | none | Every code block is evaluated in a new environment |
| :results | replace | replace | Every time a block is evaluated the newest results replace the previous one |
| :exports | code | results | The former exports only code, the latter only the results of the evaluation. |
| :cache | no | - | No caching is attempted.Blocks are evaluated each time. |
| :noweb | no | - | References to other blocks <<ref>> are not expanded. |
| :hlines | no | yes | Preserves horizontal lines in a table. |
| :tangle | no | - | Code is not exported to an external file. |
Setup and configuration
To reproduce the examples presented in the previous section, it is necessary to install some Emacs packages and third-party software, most notably.
- Emacs 30+
- Org
- Git
- Python with the following packages
numpypandasbs4lxmlrequestspygments
- R with the following packages
dplyr
- A TeX distribution with some specific engines, scripts and packages
inputencfontencgeometryfontspeculemxcoloramsmath
amssymbbooktabslongtablecolortblrotatingsidecapgraphicx
wrapfighyperrefplaceinscaptionmintedfancyvrbnewfloat
Emacs
Packages
Sample configuration files are available on a cloud storage service ( ). A description of the configuration is provided in the sections Org configuration and Emacs customization. Additionally, I maintain a GitHub repository with my Emacs configuration ( ) , which includes further customizations.
Org configuration
Babel
At the core of Org’s ability to process code is Babel. Originally an extension, it is now part of core Org and defines which languages can be executed. By default, only emacs-lisp is enabled for evaluation, but additional languages can be enabled by extending the variable org-babel-load-languages. Snippet 5 shows an example of how to include R and Python in this list. This code should be placed in your Emacs configuration file.
Header arguments
Header arguments can be configured at different levels: globally, per file, per section, and per block (locally). Local settings take precedence over higher-level ones. Snippet 6 shows an example from the official documentation to set the :noweb header argument to yes globally, affecting both blocks and in-line code.
#+PROPERTY: keyword at the top of the file. For example, #+PROPERTY: header-args :exports both sets all code blocks to export both the code and the evaluation results by default. Language-specific configurations can also be set, such as #+PROPERTY: header-args:R :session *R*, which sets the default session for R code to *R*.At the section level, customization is specified inside a PROPERTIES drawer and applies to all child subsections (Snippet 7). Language-specific settings are applied using :HEADER-ARGS:<lang>.
#+HEADER: keyword.As for in-line code, header arguments are specified within square brackets for example, src_R[:exports both]{nrow(mytable)}.
Third-party software
The third-party software required can be installed inside an Anaconda virtual environment for easier management and removal. Snippets 8, 9 and 10 list all the commands needed to set up a virtual environment and install the required software.
Then, on an R session install the required R packages.For exporting to PDF, the following LaTeX packages should be installed:Processing code blocks
Having all the required software properly configured, we can discuss with more detail how code blocks are processed by Org. The official documentation identifies three operations: evaluation, export, and tangling. Evaluation determines whether code gets executed and how the results are handled. Export controls whether code and results appear in different output formats (like HTML or PDF) and how they’re presented. Tangling extracts code from blocks to create standalone source files that can run as executable programs. Together, these three operations make Emacs a powerful literate programming environment
Evaluate
#+RESULTS keyword. Also, code blocks can be evaluated and explicitly place their results elsewhere in the document using the #+RESULTS: or #+CALL: keywords. The latter is particularly useful, as it allows you to pass header arguments and specify variables for example, #+CALL: my-Rblock[:session *R*](n=5). For in-line code the equivalent will be call_my-Rblock[:session *R*](n=5).Code block evaluation can be further customized using the header argument, :eval which accepts the values listed in Table 4
:eval argument.| Value | Description |
|---|---|
| yes | Allows evaluation |
| no | Never evaluates |
| query | Prompts for permission during export and interactively. |
| never-export | Do not evaluate during export but it can be done interactively. |
| query-export | Prompts for permission during export. |
org-confirm-babel-evaluate.For longer documents with multiple code blocks, evaluation can be time-consuming. To optimize performance, you can cache results using the :cache yes header argument. This is particularly useful when certain blocks consistently return the same output for the same input. Behind the scenes, Org determines whether a block needs re-evaluation by checking a generated SHA1 hash.
Handling the results of the evaluation
The handling of results is controlled by several header arguments, the most important of which is :results. This argument accepts multiple values, categorized into four distinct types:
- Collection: How are the results collected
- Type: What kind of results are expected.
- Format: How Org process the results.
- Handling: How the results are inserted after processing.
Collection
Controlled by the values value or output. The former is the default for most languages and interprets the code as a function that returns the last evaluated expression. The later (also known as scripting mode) sends the code to an interpreter and returns the same values that would be printed to standard output.
Type
Code blocks can return various types of output, including:
table/vector: Attempts to interpret the result as an org-table. Horizontal lines are not included unless the argument:hlines yesis included. By default, tabs are used as field separator but this can be customized with the:separgument.list: Attempts to interpret the result as an org-list.scalar/verbatim: Returns the results as raw text wrapped in anexampleblock.file: Saves the result to a filename and inserts a link after the code block. The name of the file is specified with the:file <name>argument. The description used for the link can be customized with the:file-desc <description>argument. The directory where to save the file can be specified with the:output-dir <path>argument. File permissions are set with the:file-mode (identity #oXYZ)whereXYZwill define the file permission for the owner, group and others as it is normally do in Unix systems.
Format
The results of a code block evaluation are inserted below a #+RESULTS keyword. Additionally, these results can be wrapped in a block of a particular format, controlled by one of the following values:
raw: The output is interpreted as raw text.code: The output is wrapped in a code block with the language being the same as the code block evaluated.drawer: The output is interpreted as raw text but wrapped inside a:results:drawer.html: The output is wrapped in an export block of the html type,#+begin_export htmllatex: The output is wrapped in an export block of the latex type,#+begin_export latexorg: The output is wrapped in a code block of the languageorgpp: In the case of Emacs-Lisp, Python or Ruby, the output is converted to ‘pretty-print’ code.
:wrap <string1> <string2> will wrap the results in a block, #+begin_<string1> <string2>Handling
These values control whether and how the results are inserted into the buffer.
replace: The default value, it causes the results to replace any previous output.silent: The results are not inserted but echoed in the minibuffer.none: The results are computed but not inserted nor echoed. These can still be referenced.discard: No results are inserted or exported. This value can be useful for the side-effects a code can have such as downloading files or setting environmental variables.appendandprepend. Adds the latest results to the bottom and the top of the#+RESULTSkeyword, respectively.
Export
Exporting an Org file is useful for sharing content with individuals who do not use Emacs, for example, by printing it or publishing it on the web. For code blocks, Org provides a header argument to control whether the code, its results, both, or neither are included in the exported output. This argument is :exports, and its possible values are:
code: The default value, it exports only the code.results: Exports the results of the evaluation but not the code.both: Code and the results are included in the exported file.none: Nothing is exported. The evaluation of the code would depend on other arguments such as:eval(see )
Tangle
While exporting produces a file where code and documentation are combined (woven) together, it is also possible to generate one or more files containing only code, which can then be used as executable files. This process is known as tangling. The following header arguments control various aspects of this process:
:tangle: Default valueno. If ayesvalue is passed, the code will be written to a file whose name is derived from the name of the org file. If another string is passed that would be used as the file name for the source code. In this case an extension should be provided, for example:tangle update_cmds.sh.:mkpdir: Default valueno. If ayesvalue is passed, it allows the creation of directories, provided that these are included in the:tangleargument.:comments: Allow the inclusion of comments outside those in the code. Possible values include:no: Default, no extra comments are includedlink: Include org-links that point to the org heading where the code is located. In the tangled file one can jump back to the source org file using the functionorg-babel-tangle-jump-to-orgorg: Include the name of the org heading and all the text between it and the code block. However, no link to the source file are provided.both: Combines the information oforgandlinkcombined in that order. That is the link to the org file is above the first line of the tangled code. If more than one code block tangles to the same output file and the code blocks are tangled using the:comments linkorboththen extra comment lines will be included to mark the end of the code from each blocknoweb: If noweb is enabled (e.g.:noweb yes), then it will expand the references, adding a link to their definition.
:shebang: Used to insert the path to the executable for the tangled file, for example,:shebang "#!/bin/bash"; it will also change the tangled file’s permission to allow execution.:tangle-mode: It can further customize the file’s permission. For example, to set the tangled file to be read and write only by the owner one could write,:tangle-mode (identity #o600)or simply:tangle-mode o600. Note that if the tangled file has been previously created with different permissions this argument will not change them.
Customization
Having described the processing of code blocks, we now explore how to customize key aspects of this process, specifically evaluation and export.
Code evaluation
For trusted files, confirmation for the evaluation of code blocks can be skipped by setting the variable org-confirm-babel-evaluate to nil inside a local variable box. Snippet 11 shows how can this be done inside a heading that would not be exported.
HTML export
Exporting an org-file to HTML is a straightforward process. Emacs has a default exporter that you can access using the keybinding C-c C-e followed by h h. This sequence calls the function org-export-dispatch, which opens a menu with various export options typically selected by typing two letters in sequence. Here, h h selects Export to HTML followed by As HTML file. The examples shown in the section Examples of how to work with code were produced this way. Note that titles and headings have larger font sizes and weights, and code has a different font, a border, and syntax highlighting. Importantly, all these customizations are embedded in the HTML file (no extra CSS or JS files).
While the default output is generally sufficient, there are areas that could be improved, such as table formatting or code highlighting. Customization options include using packages, theme files, and even creating custom backends for export. A few of these options are described below but before that a brief description of how Org highlights code is provided.
Org’s default highlighting system
Org includes a built-in system for highlighting code snippets when exporting to HTML, controlled by the variable org-html-htmlize-output-type. Table 5 lists the three possible values for this variable. By default, Org generates CSS styling based on the current theme’s faces and includes these styles inline in the exported HTML file. If the value is set to css, Org will include only the CSS selectors in the output; the corresponding style definitions can then be generated using the function org-html-htmlize-generate-css. Finally, if the value is set to plain, the code will be exported as plain text without any styling.
org-html-htmlize-output-type variable.| Value | Description |
|---|---|
inline-css | Default. Includes CSS attributes in the HTML file. |
css | Includes CSS selectors only. |
plain | No CSS definitions or selectors are included. |
org-src-fontify-natively. This is enabled by default (t), meaning Org applies syntax highlighting to source blocks. Syntax highlighting depends on the configuration of the corresponding major mode, which defines language-specific rules for highlighting, indentation, and editing. When a major mode is available for the language used in a source block, Org activates it in the background and applies its font-lock rules for styling.In addition to the CSS used for syntax highlighting, Org provides a default CSS stylesheet that applies to other elements such as headlines, tables, and code blocks. This is controlled by the variable org-html-head-include-default-style. This is also enabled by default (t), meaning the default stylesheet will be included alongside any syntax highlighting styles.
Using a theme
As will be discussed in the section Using the custom css, orgcss, Org provides a way to include CSS and JS files in the exported HTML file. Many users have created custom files to personalize the output, and these have been collected in a GitLab repository and embedded in ‘theme’ files. These files allow you to load all configurations using the #+SETUPFILE keyword. Snippet 12 shows two examples of how themes are loaded, and below it, the HTML files produced are presented. The first theme (bigblow_inline) adds a navigation bar with all sections and clickable elements that can collapse and expand all subsections (if any). The second theme (stylish_white) is simpler, changing mainly the font face. In both cases, the tables are better formatted that the original, while the code has the same colors as the original but without the background.
Using the custom css, orgcss
Org provides two keywords for adding HTML code to the <head> element of the exported HTML file: #+HTML_HEAD: and #+HTML_HEAD_EXTRA:. Snippet 13 demonstrates how to include a local CSS file in the exported HTML using the #+HTML_HEAD: keyword. This keyword is intended for essential information, while #+HTML_HEAD_EXTRA: is meant for additional content, such as a JavaScript (JS) file.
#+HTML_HEAD: keyword (line 1).Importantly, to produce the HTML file, two Org variables must be changed from their default values. First, the variable org-html-head-include-default-style should be set to nil (as in line 5) to prevent the inclusion of the CSS definitions in the variable org-html-style-default. Second, the variable org-html-htmlize-output-type should be set to either css or nil to prevent the definition of classes within the HTML file.
Using highlight.js
The CSS file provided by orgcss doesn’t include definitions for highlighting code blocks. However, it provides instructions for using the dedicated syntax highlighter highlight.js, which supports for over 190 languages. By ‘support’ it is meant that different code elements (i.e. tokens) are colorized according to their specific semantic meaning, rather than the more generic approach that Org’s default highlighting system uses.This shouldn’t be confused with the support for evaluating code discussed in the Language argument section.
Snippet 15 shows how to use highlight.js via CDN (e.g. cdnjs). Line 1 loads a CSS file for a specific color theme (see https://highlightjs.org/examples for more examples). Line 3 loads a minified version of the highlight.js library with support for about 40 languages (see the official list of supported languages). Since this doesn’t include R, line 5 will load an additional library for that language. Finally, line 7 defines a custom function that modifies the code blocks and applies the styling above. The result of exporting to HTML appears below the code block.
Using the package, nice-org-html
The nice-org-html package offers a straightforward way to customize HTML exports using any Emacs theme available on the system. A dark and a light themes should be specified for export, with optional additions of headers, footers, and custom CSS/JS files. The official repository provides installation and configuration instructions. Notably, the package adds an export option to the org-export-dispatch menu (accessed via keys H h) which will produce an HTML file. Snippet 16 shows a sample configuration using two non-default themes (See the online theme gallery for additional theme options). Below the code is the resulting HTML export.
Use Pygments with a custom backend
Pygments is a Python-based syntax highlighter that supports more languages than highlight.js, including Emacs Lisp. It is easily applied when exporting to PDF (see next section). For HTML, however, it requires defining a custom backend exporter.
Snippet 17 is a modification of the code described in Anton Linevych’s blog post. It sets up three variables: one for the CSS filename, another for the style, and a third for the Pygments binary path (see Settings for pygments). A custom function then creates the CSS file and adds a linking line to the exported HTML. Next, another custom function processes code blocks using Pygments. An export function is then defined to specify which buffer elements will be processed. Finally, the custom backend combines these functions and adds a menu entry to org-export-dispatch.
org-html-htmlize-output-type should be set to either css or nil whereas, the variable org-html-head-include-default-style can remain at its default value (t), as in the example below. This will keep the background color and left margin of code blocks.PDF export
Exporting to PDF uses the same base function as HTML (org-export-dispatch) with the only difference being the last two keys, l p. These will select the options, Export to LaTeX followed by Export as PDF file. As for the style, similar to the HTML export, the default PDF output is quite simple: the title and headings appear in a bold, larger font than the regular text, which itself differs from the font used for code (see below).
