When writing Markdown it is common to have code blocks within your document
that look something like this:

This is a code block:
```bash
echo "Hello, world!"
```

One of the great features of Emacs’ Org mode is its
ability to evaluate blocks of code from within the editing environment. I
always appreciated this feature about Org mode which got me to thinking about
implementing something similar in Vim.

Update (2020-03-02): I’ve packaged this into a Vim plugin called
Medieval. The remainder of this
post contains the original implementation, which is now out-of-date.

The solution ended up being fairly straightforward. I created the following
autoloaded
function in ~/.vim/autoload/ft/markdown.vim:

Basically, the function looks backward from the current cursor position for any
line that matches the start of a code block (three or more back ticks followed
by a language name) and then looks forward for the close of the code block. If
the current cursor position is between those two locations, then we know we’re
in a code block. At that point, it’s as simple as copying the text within the
code block to a temp file and sourcing it using the interpreter given by the
syntax of the code block (e.g. bash, ruby, etc.).

Obviously, not all languages can be run in an interpreter, so there is also a
check to make sure the language of the code block is in a user-specified
variable g:markdown_interp_languages. I set this in my Markdown ftplugin file
(~/.vim/after/ftplugin/markdown.vim):

The = syntax lets me specify that certain syntaxes use the same interpreter
(e.g. sh and console syntaxes should both use bash as their interpreter).
Adding more languages is as simple as extending this global variable.

Finally, I map this function to a key binding in my ftplugin file:

nnoremap<silent><buffer>Z! :<C-U>callft#markdown#eval()<CR>

Now I can press Z! in a code block in Markdown and have the block evaluated.

This is a very lightweight solution and doesn’t have anywhere near the full
features of Org mode (which includes things like inline results, persistent
contexts, mangling, and more), but it’s a good first cut solution which I’m
happy with.

As always, you can find the most up-to-date version of the function above, as
well as all of my Vim files, here.