UP | HOME

Ping's Tech Notes

Markdown in Emacs: Live Preview, Math and Images

Ping Zhou, 2020-11-11

Introduction

Emacs has a Markdown mode, but I need a more comprehensive setup with features like:

  • Live preview window
  • Rendering of math equations
  • Rendering of local and remote images

Basically I need a setup that provides similar functionality as VS Code + Markdown extensions.

Install dependencies

Emacs packages needed:

  • markdown-mode
  • markdown-mode
  • simple-httpd
  • impatient-mode

Install pandoc:

brew install pandoc

Get MathJax:

git clone https://github.com/mathjax/MathJax.git mathjax

Configure Emacs

Markdown settings

Add these customization to Emacs config:

  • markdown-command: "/usr/local/bin/pandoc --mathjax" (assuming pandoc is installed at /usr/local/bin)
  • markdown-display-remote-images: t
  • markdown-enable-math: t

This can be done with Emacs Customization UI (M-x customize), or by manually editing custom-set-variables:

(custom-set-variables
 ;;
 ;; Other custom values...
 ;;
 '(markdown-command "/usr/local/bin/pandoc --mathjax")
 '(markdown-display-remote-images t)
 '(markdown-enable-math t)
 ;;
 ;; ...
 )

Simple HTTPD

Add this to Emacs config file to enable simple-httpd (a simple Web server inside Emacs):

(require 'simple-httpd)

(use-package simple-httpd
  :ensure t
  :config
  (setq httpd-port 7070))

This will configure the simple-httpd to run on localhost:7070.

Default root folder of simple-httpd is ~/public_html. This can also be customized by changing the httpd-root variable. For example, I set it to ~/www:

;; Add this to custom-set-variables:
;;
  '(httpd-root "~/www")

Impatient Mode

Add these to Emacs config file to configure markdown filter for impatient-mode:

(defun markdown-filter (buffer)
  (princ
   (with-temp-buffer
     (let ((tmp (buffer-name)))
       (set-buffer buffer)
       (set-buffer (markdown tmp))
       (format "<!DOCTYPE html><html><title>Markdown preview</title>
<body><article class=\"markdown-body\" style=\"box-sizing: border-box;min-width: 200px;max-width: 800px;margin: 0 auto;padding: 45px;\">%s</article></body><script src=\"/mathjax/es5/tex-chtml-full.js\"></script></html>" (buffer-string))))
   (current-buffer)))

(defun markdown-live-preview ()
  "Preview markdown."
  (interactive)
  (unless (process-status "httpd")
    (httpd-start))
  (impatient-mode)
  (imp-set-user-filter 'my-markdown-filter)
  (imp-visit-buffer))

The filter will insert MathJax script in the generated HTML so that math symbols/equations will be properly rendered.

You can point it to any URL that hosts MathJax (if you trust the source), but I decide to host MathJax locally in simple-httpd:

<script src=\"/mathjax/es5/tex-chtml-full.js\"></script>

So I need to copy MathJax repo over to the root folder of simple-httpd:

cp -rf ~/mathjax ~/www

Now MathJax script should be available to impatient-mode.

(Optional) Add GitHub Style

The rendered HTML seems to be plain. So I added a GitHub-style CSS to the impatient-mode filter:

git clone https://github.com/sindresorhus/github-markdown-css.git
cp github-markdown-css/github-markdown.css ~/www

The CSS file is also hosted locally in simple-httpd.

Add this line to my filter:

<link rel=\"stylesheet\" href = \"/github-markdown.css\"/>

Now the filter is like this:

(defun markdown-filter (buffer)
  (princ
   (with-temp-buffer
     (let ((tmp (buffer-name)))
       (set-buffer buffer)
       (set-buffer (markdown tmp))
       (format "<!DOCTYPE html><html><title>Markdown preview</title><link rel=\"stylesheet\" href = \"/github-markdown.css\"/>
<body><article class=\"markdown-body\" style=\"box-sizing: border-box;min-width: 200px;max-width: 800px;margin: 0 auto;padding: 45px;\">%s</article></body><script src=\"/mathjax/es5/tex-chtml-full.js\"></script></html>" (buffer-string))))
   (current-buffer)))

Test Run

Live Preview

Open a Markdown file, and then start live preview:

M-x markdown-live-preview

A new page will be opened in browser, showing the live preview.

As you type in the Markdown file, live preview also changes accordingly.

Images

To add images, just insert the <img> tag in Markdown:

<img src="./test.png" width="600" />

This relative path works for impatient-mode too (i.e. it will look for test.png in the same folder as current Markdown file).

Math Equations

Try math equations in Markdown:

$$
\begin{pmatrix}
aa & bb \\
cc & dd \\
\end{pmatrix}
\otimes
\begin{pmatrix}
ee & ff \\
gg & hh \\
\end{pmatrix}
$$

Rendered like this in live preview:

\begin{matrix} \begin{pmatrix} aa & bb \\ cc & dd \\ \end{pmatrix} \otimes \begin{pmatrix} ee & ff \\ gg & hh \\ \end{pmatrix} \end{matrix}

Looking good!