Skip to content

Evaluate Markdown code blocks within Vim

License

Notifications You must be signed in to change notification settings

lukejahnke/vim-medieval

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

45 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

vim-medieval

Evaluate Markdown code blocks within Vim.

asciicast

Table of Contents

Description

Medieval allows you to evaluate code blocks in Markdown buffers of the following form:

```bash
echo "Hello world!"
```

By placing your cursor anywhere in the code block above and running :EvalBlock, Medieval will print the result of evaluating the block (in this case, it will echo "Hello world!")

You can also redirect the output of the evaluation into a register using :EvalBlock @{0-9a-z".=*+}.

You can send the output of evaluation into another code block, allowing you to do a primitive style of literate programming. You can accomplish this by adding a "target" parameter to your code block and creating a second code block with a "name" parameter. The output of the evaluation of your code block will be redirected to the targeted block. For example:

<!-- target: squares -->
```python
print([x*x for x in range(5)])
```

<!-- name: squares -->
```
```

If you run :EvalBlock in the first code block, the second block will become

<!-- name: squares -->
```
[0, 1, 4, 9, 16]
```

The target of a block can also be a file. If the target name contains a / character, it is assumed to be a file path. File paths can contain environment variables and tilde expansion. Example:

<!-- target: $HOME/squares.txt -->
```python
print([x*x for x in range(5)])
```

Note that the following will write to a code block named squares.txt (and create it if it doesn't exist) instead of writing to a file called squares.txt:

<!-- target: squares.txt -->

To write to a file called squares.txt, use

<!-- target: ./squares.txt -->

You can manually specify a target block using :EvalBlock {target}. With [!], :EvalBlock will cause the evaluated code block to replace its own contents with the result of its evaluation:

```sh
fortune
```

After :EvalBlock!:

```sh
The difference between art and science is that science is what we
understand well enough to explain to a computer.  Art is everything else.
                -- Donald Knuth, "Discover"
```

The language of the block being executed is detected through the text next to the opening code fence (known as the "info string"). There is no formal specification for how the info string should be formatted; however, Medieval can detect info strings in any of the following formats:

```lang
```

```{.lang}
```

```{lang}
```

Whitespace is allowed before the info string. The closing } is not required for the latter two styles, meaning you can use info strings such as

``` {.python .numberLines #my-id}
```

Note, however, that when using this kind of info string the language name must be first for Medieval to correctly detect it.

The target block can be either another code block (delimited by ``` or ~~~) or a LaTeX math block (delimited by $$):

<!-- target: math -->
```python
print(r"\text{Hello LaTeX!}")
```

<!-- name: math -->
$$
\text{Hello LaTeX!}
$$

The block labels must be of the form <!-- OPTION: VALUE[,] [OPTION: VALUE[,] [...]] where OPTION is one of name, target, require, or tangle. The label can be preceeded by whitespace, but no other characters. The option values can be composed of the following characters: 0-9A-Za-z_+.$#&-. Note that the closing tag of the HTML comment is not required. This allows you to embed the code block within an HTML block comment so that the block will not be rendered in the final output. For example:

<!-- target: example
```sh
echo '$ ls -1'
ls -1
```
-->

<!-- name: example -->
```sh
$ ls -1
LICENSE
README.md
autoload
doc
ftplugin
```

In this example, only the second block will be rendered, since the first block is nested within an HTML comment.

Block Dependencies

Code blocks can be combined using the require option. The argument to the require option is the name of another code block which will be evaluated before the contents of the block itself. Required blocks must use the same language as the requiring block.

For example,

<!-- name: numpy -->
```python
import numpy as np
```

<!-- target: output, require: numpy -->
```python
print(np.arange(1, 5))
```

<!-- name: output -->
```
```

Running :EvalBlock in the second code block produces:

<!-- name: output -->
```
[1 2 3 4]
```

Blocks can have recursive dependencies:

<!-- name: first_name -->
```sh
first_name="Gregory"
```

<!-- name: full_name, require: first_name -->
```sh
full_name="$first_name Anders"
```

<!-- target: greeting, require: full_name -->
```sh
echo "Hi, my name is $full_name"
```

After running :EvalBlock in the block above...

<!-- name: greeting -->
```
Hi, my name is Gregory Anders
```

Code Tangling

The source code in a code block can be written to a given file before executing by using the "tangle" option. This can be used in conjunction with the "require" keyword to combine multiple blocks together into a single combined source file.

Example:

<!-- name: numpy -->
```python
import numpy as np
```

<!-- require: numpy tangle: script.py -->
```python
x = np.arange(5)
print(x)
```

When you run :EvalBlock on the second code block above, a new file called "script.py" will be generated in your current working directory with the contents

import numpy as np
x = np.arange(5)
print(x)

The file specified as the "tangle" option can be a relative or absolute path and may use tilde expansion and environment variables.

If you only wish to use the tangling feature without printing the output of the code block, you can use /dev/null as the block target:

<!-- target: /dev/null tangle: script.py -->

Configuration

Medieval will only attempt to execute code blocks in languages explicitly listed in the variable g:medieval_langs. The structure of this variable is a list of strings corresponding to whitelisted languages that can be interpreted. If a language's interpreter has a different name than the language itself, you can use the form {lang}={interpreter} to specify what interpreter should be used.

For example, to allow Medieval to run Python, Ruby, and shell scripts, use

let g:medieval_langs = ['python=python3', 'ruby', 'sh', 'console=bash']

By default, g:medieval_langs is empty, so you must specify this variable yourself.

You can also define custom code fence delimiters using the variable g:medieval_fences. This variable is a List of Dicts containing a start key that defines a pattern for the opening delimiter of the code block and an optional end key that defines a pattern for the closing delimiter of the code block. If end is omitted, then the closing delimiter is assumed to be the same as the opening delimiter.

For example, a Hugo shortcode has the following form:

{{< katex >}}
Some content here
{{< /katex >}}

You can use Medieval with blocks like this by setting g:medieval_fences to the following:

let g:medieval_fences = [{'start': '{{<\s\+\(\S\+\)\s\+>}}', 'end': '{{<\s\+/\1\s\+>}}'}]

Note the use of a capture group in the start pattern and the use of \1 in the end pattern. In this example, the \1 in the end pattern will be replaced by whatever matches the capture group in the start pattern (katex in our example above).

Create a mapping

Medieval does not create any mappings by default, but you can easily create one yourself by adding the following to the file ~/.vim/after/ftplugin/markdown.vim (create it if it does not yet exist):

nnoremap <buffer> Z! :<C-U>EvalBlock<CR>

Limitations

For now, Medieval only works in Markdown buffers. If you'd like to see support added for other file types, please see the Contributing section.

Contributing

Please feel free to contribute changes or bug fixes. You can send patches to [email protected] or submit a pull request on GitHub.

About

Evaluate Markdown code blocks within Vim

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Vim Script 100.0%