Skip to content

Commit 735dd7a

Browse files
committed
Add optional opts dict which accepts a callback
This callback can be used to do something when evaluation completes.
1 parent 4b429f9 commit 735dd7a

File tree

4 files changed

+46
-284
lines changed

4 files changed

+46
-284
lines changed

README.md

Lines changed: 1 addition & 278 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,6 @@ Evaluate Markdown code blocks within Vim.
44

55
[![asciicast](https://asciinema.org/a/306995.svg)](https://asciinema.org/a/306995)
66

7-
## Table of Contents
8-
9-
- [Description](#description)
10-
- [Block Dependencies](#block-dependencies)
11-
- [Code Tangling](#code-tangling)
12-
- [Configuration](#configuration)
13-
- [Create a mapping](#create-a-mapping)
14-
- [Limitations](#limitations)
15-
- [Contributing](#contributing)
16-
177
## Description
188

199
Medieval allows you to evaluate code blocks in Markdown buffers of the
@@ -29,9 +19,6 @@ By placing your cursor anywhere in the code block above and running
2919
`:EvalBlock`, Medieval will print the result of evaluating the block (in this
3020
case, it will echo "Hello world!")
3121

32-
You can also redirect the output of the evaluation into a register using
33-
`:EvalBlock @{0-9a-z".=*+}`.
34-
3522
You can send the output of evaluation into another code block, allowing
3623
you to do a primitive style of literate programming. You can accomplish this
3724
by adding a "target" parameter to your code block and creating a second code
@@ -58,271 +45,7 @@ If you run `:EvalBlock` in the first code block, the second block will become
5845
```
5946
````
6047

61-
The target of a block can also be a file. If the target name contains a `/`
62-
character, it is assumed to be a file path. File paths can contain environment
63-
variables and tilde expansion. Example:
64-
65-
````markdown
66-
<!-- target: $HOME/squares.txt -->
67-
```python
68-
print([x*x for x in range(5)])
69-
```
70-
````
71-
72-
Note that the following will write to a code block named `squares.txt` (and
73-
create it if it doesn't exist) instead of writing to a file called
74-
`squares.txt`:
75-
76-
```markdown
77-
<!-- target: squares.txt -->
78-
```
79-
80-
To write to a file called `squares.txt`, use
81-
82-
```markdown
83-
<!-- target: ./squares.txt -->
84-
```
85-
86-
You can manually specify a target block using `:EvalBlock {target}`. With
87-
`[!]`, `:EvalBlock` will cause the evaluated code block to replace its own
88-
contents with the result of its evaluation:
89-
90-
````markdown
91-
```sh
92-
fortune
93-
```
94-
````
95-
96-
After `:EvalBlock!`:
97-
98-
````markdown
99-
```sh
100-
The difference between art and science is that science is what we
101-
understand well enough to explain to a computer. Art is everything else.
102-
-- Donald Knuth, "Discover"
103-
```
104-
````
105-
106-
The language of the block being executed is detected through the text next to
107-
the opening code fence (known as the "info string"). There is no formal
108-
specification for how the info string should be formatted; however, Medieval
109-
can detect info strings in any of the following formats:
110-
111-
````markdown
112-
```lang
113-
```
114-
115-
```{.lang}
116-
```
117-
118-
```{lang}
119-
```
120-
````
121-
122-
Whitespace is allowed before the info string. The closing `}` is not required
123-
for the latter two styles, meaning you can use info strings such as
124-
125-
````markdown
126-
``` {.python .numberLines #my-id}
127-
```
128-
````
129-
130-
Note, however, that when using this kind of info string the language name must
131-
be first for Medieval to correctly detect it.
132-
133-
The target block can be either another code block (delimited by `` ``` `` or
134-
`~~~`) or a LaTeX math block (delimited by `$$`):
135-
136-
````markdown
137-
<!-- target: math -->
138-
```python
139-
print(r"\text{Hello LaTeX!}")
140-
```
141-
142-
<!-- name: math -->
143-
$$
144-
\text{Hello LaTeX!}
145-
$$
146-
````
147-
148-
The block labels must be of the form `<!-- OPTION: VALUE[,] [OPTION: VALUE[,]
149-
[...]]` where `OPTION` is one of `name`, `target`, `require`, or `tangle`. The
150-
label can be preceeded by whitespace, but no other characters. The option
151-
values can be composed of the following characters: `0-9A-Za-z_+.$#&-`. Note
152-
that the closing tag of the HTML comment is not required. This allows you to
153-
embed the code block within an HTML block comment so that the block will not be
154-
rendered in the final output. For example:
155-
156-
````markdown
157-
<!-- target: example
158-
```sh
159-
echo '$ ls -1'
160-
ls -1
161-
```
162-
-->
163-
164-
<!-- name: example -->
165-
```sh
166-
$ ls -1
167-
LICENSE
168-
README.md
169-
autoload
170-
doc
171-
ftplugin
172-
```
173-
````
174-
175-
In this example, only the second block will be rendered, since the first block
176-
is nested within an HTML comment.
177-
178-
## Block Dependencies
179-
180-
Code blocks can be combined using the `require` option. The argument to the
181-
`require` option is the name of another code block which will be evaluated
182-
before the contents of the block itself. Required blocks must use the same
183-
language as the requiring block.
184-
185-
For example,
186-
187-
````markdown
188-
<!-- name: numpy -->
189-
```python
190-
import numpy as np
191-
```
192-
193-
<!-- target: output, require: numpy -->
194-
```python
195-
print(np.arange(1, 5))
196-
```
197-
198-
<!-- name: output -->
199-
```
200-
```
201-
````
202-
203-
Running `:EvalBlock` in the second code block produces:
204-
205-
````markdown
206-
<!-- name: output -->
207-
```
208-
[1 2 3 4]
209-
```
210-
````
211-
212-
Blocks can have recursive dependencies:
213-
214-
````markdown
215-
<!-- name: first_name -->
216-
```sh
217-
first_name="Gregory"
218-
```
219-
220-
<!-- name: full_name, require: first_name -->
221-
```sh
222-
full_name="$first_name Anders"
223-
```
224-
225-
<!-- target: greeting, require: full_name -->
226-
```sh
227-
echo "Hi, my name is $full_name"
228-
```
229-
230-
After running :EvalBlock in the block above...
231-
232-
<!-- name: greeting -->
233-
```
234-
Hi, my name is Gregory Anders
235-
```
236-
````
237-
238-
## Code Tangling
239-
240-
The source code in a code block can be written to a given file before executing
241-
by using the "tangle" option. This can be used in conjunction with the
242-
"require" keyword to combine multiple blocks together into a single combined
243-
source file.
244-
245-
Example:
246-
247-
````markdown
248-
<!-- name: numpy -->
249-
```python
250-
import numpy as np
251-
```
252-
253-
<!-- require: numpy tangle: script.py -->
254-
```python
255-
x = np.arange(5)
256-
print(x)
257-
```
258-
````
259-
260-
When you run `:EvalBlock` on the second code block above, a new file called
261-
"script.py" will be generated in your current working directory with the
262-
contents
263-
264-
```python
265-
import numpy as np
266-
x = np.arange(5)
267-
print(x)
268-
```
269-
270-
The file specified as the "tangle" option can be a relative or absolute path
271-
and may use tilde expansion and environment variables.
272-
273-
If you only wish to use the tangling feature without printing the output of the
274-
code block, you can use `/dev/null` as the block target:
275-
276-
```markdown
277-
<!-- target: /dev/null tangle: script.py -->
278-
```
279-
280-
## Configuration
281-
282-
Medieval will only attempt to execute code blocks in languages explicitly
283-
listed in the variable `g:medieval_langs`. The structure of this variable is a
284-
list of strings corresponding to whitelisted languages that can be
285-
interpreted. If a language's interpreter has a different name than the
286-
language itself, you can use the form `{lang}={interpreter}` to specify what
287-
interpreter should be used.
288-
289-
For example, to allow Medieval to run Python, Ruby, and shell scripts, use
290-
291-
```vim
292-
let g:medieval_langs = ['python=python3', 'ruby', 'sh', 'console=bash']
293-
```
294-
295-
By default, `g:medieval_langs` is empty, so you **must** specify this variable
296-
yourself.
297-
298-
You can also define custom code fence delimiters using the variable
299-
`g:medieval_fences`. This variable is a List of Dicts containing a `start` key
300-
that defines a pattern for the opening delimiter of the code block and an
301-
optional `end` key that defines a pattern for the closing delimiter of the code
302-
block. If `end` is omitted, then the closing delimiter is assumed to be the
303-
same as the opening delimiter.
304-
305-
For example, a [Hugo shortcode][shortcodes] has the following form:
306-
307-
```markdown
308-
{{< katex >}}
309-
Some content here
310-
{{< /katex >}}
311-
```
312-
313-
You can use Medieval with blocks like this by setting `g:medieval_fences` to
314-
the following:
315-
316-
```vim
317-
let g:medieval_fences = [{'start': '{{<\s\+\(\S\+\)\s\+>}}', 'end': '{{<\s\+/\1\s\+>}}'}]
318-
```
319-
320-
Note the use of a capture group in the `start` pattern and the use of `\1` in
321-
the end pattern. In this example, the `\1` in the end pattern will be replaced
322-
by whatever matches the capture group in the `start` pattern (`katex` in our
323-
example above).
324-
325-
[shortcodes]: https://gohugo.io/content-management/shortcodes/
48+
Medieval can do a lot more. Read `:h medieval` for the full documentation.
32649

32750
## Create a mapping
32851

autoload/medieval.vim

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,10 @@ function! s:callback(context, output) abort
162162
return
163163
endif
164164

165+
if has_key(opts, 'callback')
166+
call opts.callback(a:context, a:output)
167+
endif
168+
165169
let start = a:context.start
166170
let end = a:context.end
167171

@@ -207,7 +211,7 @@ function! s:callback(context, output) abort
207211
endif
208212
endfunction
209213

210-
function! medieval#eval(bang, ...) abort
214+
function! medieval#eval(...) abort
211215
if !exists('g:medieval_langs')
212216
call s:error('g:medieval_langs is unset')
213217
return
@@ -238,9 +242,7 @@ function! medieval#eval(bang, ...) abort
238242

239243
let opts = s:parseopts(start - 1)
240244

241-
if a:bang
242-
let opts.target = 'self'
243-
elseif a:0
245+
if a:0 && a:1 !=# ''
244246
let opts.target = a:1
245247
elseif s:validreg(v:register)
246248
let opts.target = '@' . v:register
@@ -268,6 +270,10 @@ function! medieval#eval(bang, ...) abort
268270
endif
269271
call writefile(block, fname)
270272

273+
if a:0 > 1
274+
call extend(opts, a:2, 'error')
275+
endif
276+
271277
let context = {'opts': opts, 'start': start, 'end': end, 'fname': fname}
272278
call s:jobstart([lang, fname], function('s:callback', [context]))
273279
call winrestview(view)

doc/medieval.txt

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
*medieval.txt* Evaluate Markdown code blocks in Vim
22

3-
Author: Greg Anders <greg@gpanders.com>
3+
Author: Gregory Anders <greg@gpanders.com>
44
Repo: https://github.com/gpanders/vim-medieval
55
License: Same terms as Vim itself (see |license|)
66

@@ -232,6 +232,33 @@ of the code block, you can use `/dev/null` as the block target:
232232
<!-- target: /dev/null tangle: script.py -->
233233
<
234234

235+
*medieval#eval()*
236+
medieval#eval({target}[, {opts})
237+
Evaluate the block under the cursor. To replace the contents of
238+
the block (like |:EvalBlock!|) use "self" for {target}.
239+
240+
{opts} is an optional |Dict| accepting the following keys:
241+
callback: (function) A function to be called when
242+
evaluation completes, before the output is
243+
written to the target block. The function is
244+
passed two arguments: a "context" |Dict|
245+
containing the parameters used in the
246+
evaluation and the result of the block
247+
evaluation as a list of lines. Modifications to
248+
the output list will effect what is written to
249+
the target block.
250+
251+
Example: >
252+
253+
function! s:cb(ctx, output)
254+
let elapsed = reltimestr(reltime(s:start))
255+
unlet! s:start
256+
call add(a:output, 'Evaluation finished in ' . elapsed . 's')
257+
endfunction
258+
259+
let s:start = reltime()
260+
call medieval#eval('', #{callback: function('s:cb')})
261+
<
235262
*g:medieval_langs*
236263
Medieval will only attempt to execute code blocks in languages explicitly
237264
listed in the variable |g:medieval_langs|. The structure of this variable is a

ftplugin/markdown/medieval.vim

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,8 @@
1-
command! -bang -buffer -nargs=? EvalBlock call medieval#eval(<bang>0, <f-args>)
1+
command! -bang -buffer -nargs=? EvalBlock
2+
\ if <bang>0 |
3+
\ call medieval#eval('self') |
4+
\ else |
5+
\ call medieval#eval(<q-args>) |
6+
\ endif
7+
28
let b:undo_ftplugin = get(b:, 'undo_ftplugin', '') . '|delc EvalBlock'

0 commit comments

Comments
 (0)