For quite some time now, I only used markdown for either note-taking or making revealjs(Fr) presentations. And this blog of course…

Anyway, while looking for my next employer, I recently had to produce a document and I wanted to use my usual tools: nvim to write markdown and compile it using pandoc. The thing is, my nvim configuration was kind of hard-coded for revealjs.

It was time for me to address this issue and have it work for every type of pandoc use case.

The issue

Say, you open a markdown file with the .md extension with nvim. The filetype is automatically detected to markdown and you’re happy.

But, how to instruct nvim on what to do with it? How to tell whether it’s a regular markdown file containing notes, a blog post, or a pandoc and maybe revealjs oriented redaction?

And of course, what to do with this information?

The solution

The approach I’ve taken here is two-fold:

  1. Set some information about the file’s content using the modeline feature,
  2. Define a custom makeprg based on this information.


Setting the correct filetype is as easy as using a modeline.

A modeline is a commented piece of instruction for nvim at the start or end of a file. When the file is (re)loaded in a buffer, the editor executes those commands.

There are some restrictions such as it cannot run code that may break your machine. For example, it’s impossible to set a custom makeprg which would have been of great use here. But this blog post is all about finding a way to overcome this limitation.

As said before, a modeline is one or more commented lines. There are no comments in markdown… But pandoc documents have this yaml header which may contain comments. So there is no issue here: a regular markdown will be detected as such and any pandoc source will be able to set its own filetype.

I picked two filetypes for the occasion: pandoc and revealjs. I use the former to generate basic HTML files automatically and the latter to generate a presentation manually.

# vim: set filetype=pandoc:
# vim: set filetype=revealjs:


While nvim forbid the redefinition of makeprg through the modeline for security reasons, it allows us to define filetype-based behavior.

It’s as easy as writing a small bit of lua in the ~/.config/nvim/after/ftplugin/pandoc.lua and ~/.config/nvim/after/ftplugin/revealjs.lua files.


1vim.opt_local.makeprg = "pandoc % --output %:r.html --standalone"
3local pandoc = vim.api.nvim_create_augroup("pandoc", { clear = true })
5vim.api.nvim_create_autocmd({ "BufWritePost" }, {
6 group = pandoc,
7 command = [[ silent make ]]

The first line defines the makeprg. Using pandoc to generate an (almost) self-contained HTML file.

And the seventh line calls this makeprg every time I save the buffer to disc. It’s a really fast process so I allow this often.


vim.opt_local.makeprg = "pandoc %                                                   \
                        --output %:r.html                                           \
                        --from markdown+emoji                                       \
                        --to revealjs                                               \
                        --slide-level 4                                             \
                        --standalone                                                \
                        --variable revealjs-url= \
                        --variable slideNumber=true                                 \
                        --variable theme=solarized

This time, the makeprg is more complex and there is no automatic call on BufWritePost. Generating a revealjs presentation takes more time so I manually call :make<CR> every now and then when I want to see the output.


NVim does not allow for file defined makeprg. But it does allow for filetype defined one.

What we get here is a fine-grained configuration while remaining safe: there is no way for someone to send us an ill-intentioned file that’ll arm our system when opened with nvim.


When I’m ready to share the final output, whether it’s a simple HTML or a revealjs presentation, I add the --embed-resources flag to pandoc. It’ll output the file while embedding every asset in it, making it fully self-contained so no external resources (images, revealjs library) will be loaded on opening.

:make --embed-resources<CR> 👌