Writing Technical Documentation with Emacs

Writing technical documentation using Emacs Org mode.

Writing Tech Doc with Emacs

Overview

Developers normally dislike documentation. However, it is inevitable that you have to spend time documenting the applications, libraries, or SDK that you developed. For project documentation, we have tools like MkDocs, Jekyll, Sphinx, Hugo, Gatsby, and many others that can be used.

However, one major issue with using these tools is that your application code and documentation are separated. E.g. if you need to document a code snippet, you have to run it separately and then paste the code and results into the document. Over time this is going to be a maintenance issue to keep your documentation updated.

In my previous article, I talked about literate programming using Jupyter notebook. Using this approach, your code and documentation will always be in sync. In this article, I am going to use Emacs Org mode to write technical documentation.

Setup

Emacs comes default with Org mode installed. However, if you want to use the latest version, just type M-x package-install RET org RET (Make sure you configure Org ELPA as one of your package repositories). For this article, I am going to use Org mode 9.3. You can check your Org mode version by typing M-x org-version RET.

If you are just started with Emacs, do go through my previous articles. You can refer to my dotfiles in this repository.

Org-Babel

Let’s start by creating a file with .org extension. Below I create a file called writing_tech_docs.org with the following content.

#+TITLE: Writing Technical Documentation using Emacs* Shell
#+begin_src shell
echo "Writing technical documentation using Emacs"
#+end_src

Now either press

  • M-x org-export-dispatch
  • C-c C-e

and you should see the following screen.

org-export-dispatch

Press h (Export to HTML) and then o(As HTML file and open).

Export HTML

Org-HTML Themes

The generated HTML is not so appealing. Let’s change it now.

Create a file called setup.conf with the following content.

# -*- mode: org; -*-#+HTML_HEAD: <link rel="stylesheet" type="text/css" href="https://fniessen.github.io/org-html-themes/src/readtheorg_theme/css/htmlize.css"/>
#+HTML_HEAD: <link rel="stylesheet" type="text/css" href="https://fniessen.github.io/org-html-themes/src/readtheorg_theme/css/readtheorg.css"/>
#+HTML_HEAD: <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
#+HTML_HEAD: <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/js/bootstrap.min.js"></script>
#+HTML_HEAD: <script type="text/javascript" src="https://fniessen.github.io/org-html-themes/src/lib/js/jquery.stickytableheaders.min.js"></script>
#+HTML_HEAD: <script type="text/javascript" src="https://fniessen.github.io/org-html-themes/src/readtheorg_theme/js/readtheorg.js"></script>
#+HTML_HEAD: <style type="text/css">
#+HTML_HEAD: pre.src:hover:before { display: none; }
#+HTML_HEAD: </style>

I am going to use the HTML theme from this repository.

I also do not want to remove the numbering in the headers. Let’s add the lines below to the top of the file.

#+TITLE: Writing Technical Documentation using Emacs
#+OPTIONS: num:nil
#+SETUPFILE: setup.conf

Now press C-c C-e to export the file to HTML.

Themed Documentation

Change Export and Working Directory

Now the exported HTML is in the same directory as our org file. Let’s change the working directory and also specify the exported file name.

#+TITLE: Writing Technical Documentation using Emacs
#+OPTIONS: num:nil
#+SETUPFILE: setup.conf
#+PROPERTY: header-args :mkdirp yes :dir ~/output
#+EXPORT_FIle_NAME: ~/output/writing_tech_docs.html

Now export again and the file should be exported in the specified directory.

Change Working Directory

Startup Script

For the working directory, I want to clean up the files before I generate them. Let’s define a function to remove all files in the output folder.

Create a file called code.inc

# -*- mode: org; -*-#+NAME: init
#+BEGIN_SRC shell :exports none
rm -rf ~/output
#+END_SRC

Now add the headers to the org file.

#+TITLE: Writing Technical Documentation using Emacs
#+OPTIONS: num:nil
#+SETUPFILE: setup.conf
#+PROPERTY: header-args :mkdirp yes :dir ~/output
#+EXPORT_FIle_NAME: ~/output/writing_tech_docs.html
#+INCLUDE: code.inc
#+CALL: init()

Invoking Code Block in Text

Let’s add a shell and Python code block in code.inc

# -*- mode: org; -*-#+NAME: init
#+BEGIN_SRC shell :exports none
rm -rf ~/output
#+END_SRC
#+NAME: today
#+BEGIN_SRC shell :exports both :results output
echo `date`
#+END_SRC
#+NAME: increment
#+begin_src python :noweb yes :var num=0
def incr(num):
return num + 1

return incr(num)
#+end_src

In the org file, I can then invoke the function.

* Evaluating Code BlockToday is “call_today[:results raw]()Increment of 87 is "call_increment[:results raw](num=87)"#+CALL: today()
#+CALL: increment(167)
Evaluating Code Block

Reusable Code Block

I can also define a reusable code block and then embed it using noweb.

* Reusable function
#+NAME: init_block
#+BEGIN_SRC python
PI=3.142
def some_function(r):
return 2 * PI * r
#+END_SRC
* Call the function on an integer
#+BEGIN_SRC python :noweb yes :exports both
<<init_block>>
return some_function(888)
#+END_SRC
#+RESULTS:
#+begin_example
5580.192
#+end_example
Reusable Code Block

Session

I can also use a session to persist data objects across different code blocks.

* Session#+begin_src python :session mysession
PI=3.14159
#+end_src
#+RESULTS:#+begin_src python :results output :session mysession
print(PI)
#+end_src
#+RESULTS:
#+begin_example
3.14159
#+end_example

Diagram

I can also draw diagrams using plantuml.

#+begin_src plantuml :file flow.png :exports results
title Authentication Sequence
Alice->Bob: Authentication Request
note right of Bob: Bob thinks about it
Bob->Alice: Authentication Response
#+end_src

This is the configuration I used.

(setq org-plantuml-jar-path
(expand-file-name "~/workspace/software/plantuml/plantuml.jar"))
(setq plantuml-default-exec-mode 'jar);; org-babel configuration
(org-babel-do-load-languages
'org-babel-load-languages
'((emacs-lisp . t)
(shell . t)
(plantuml . t)
(python . t)))
(push '("plantuml" . plantuml) org-src-lang-modes)
PlantUML Diagram

Python Mode

While editing a Python code block, I can go into Python mode by pressing C-c ‘

Python Mode

Summary

And below is the final generated output.

Technical Documentation using Emacs

The files I used can be found in this repository.

References

Programmer and occasional blogger.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store