Full Code of gpanders/vim-medieval for AI

master 3bcb574c2050 cached
9 files
33.4 KB
9.5k tokens
1 requests
Download .txt
Repository: gpanders/vim-medieval
Branch: master
Commit: 3bcb574c2050
Files: 9
Total size: 33.4 KB

Directory structure:
gitextract_t68na24_/

├── .github/
│   └── workflows/
│       └── test.yml
├── LICENSE
├── README.md
├── autoload/
│   └── medieval.vim
├── doc/
│   └── medieval.txt
├── plugin/
│   └── medieval.vim
├── run-test.sh
└── test/
    ├── medieval.vader
    └── vimrc

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/workflows/test.yml
================================================
name: Test
on:
  push:
    branches: [master]
  pull_request:
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Check Vim
        run: which vim && vim --version | head -1
      - name: Run tests
        run: ./run-test.sh


================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2020 Greg Anders

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================
# vim-medieval

Evaluate Markdown code blocks within Vim.

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

## Description

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

````markdown
```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 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:

````markdown
<!-- 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

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

Medieval can do a lot more. Read `:h medieval` for the full documentation.

## 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):

```vim
nmap <buffer> Z! <Plug>(medieval-eval)
```

## 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](#contributing)
section.

## Contributing

Please feel free to contribute changes or bug fixes. You can [send patches][]
to <git@gpanders.com> or submit a pull request on [GitHub][].

[send patches]: https://git-send-email.io/
[Github]: https://github.com/gpanders/vim-medieval


================================================
FILE: autoload/medieval.vim
================================================
const s:fences = [#{start: '\([`~]\{3,}\)\s*\%({\s*\.\?\)\?\(\a\+\)\?', end: '\1', lang: 2,}, #{start: '\$\$'}]
let s:opts = ['name', 'target', 'require', 'tangle']
let s:optspat = '\(' . join(s:opts, '\|') . '\):\s*\([0-9A-Za-z_+.$#&/-]\+\)'
let s:optionfmt = '<!-- %s -->'
let s:optionpat = '^\s*<!--\s*'

function! s:error(msg) abort
    if empty(a:msg)
        return
    endif

    echohl ErrorMsg
    echom 'medieval: ' . a:msg
    echohl None
endfunction

" Check the v:register variable for a valid value to see if the user wants to
" copy output to a register
function! s:validreg(reg) abort
    if a:reg ==# ''
        return v:false
    endif

    if a:reg ==# '"'
        return v:false
    endif

    if &clipboard =~# '^unnamed' && (a:reg ==# '*' || a:reg ==# '+')
        return v:false
    endif

    return v:true
endfunction

" Generate search pattern to match the start of any valid fence
function! s:fencepat(fences) abort
    return join(map(copy(a:fences), 'v:val.start'), '\|')
endfunction

" Find a code block with the given name and return the start and end lines.
" For example, s:findblock('foo') will find the following block:
"
"     <!-- name: foo -->
"     ```
"     ```
function! s:findblock(ft, name) abort
    let fences = s:fences + get(g:, 'medieval_fences', [])
    let fencepat = s:fencepat(fences)

    let curpos = getcurpos()[1:]

    call cursor(1, 1)

    let pat = get(get(g:, 'medieval_option_pat', {}), a:ft, s:optionpat)
    while 1
        let start = search(pat . s:optspat, 'cW')
        if !start || start == line('$')
            call cursor(curpos)
            return [0, 0]
        endif

        " Move the cursor so that we don't match on the current line again
        call cursor(start + 1, 1)

        if getline(start) =~# '\<name:\s*' . a:name
            if getline('.') =~# '^\s*\%(' . fencepat . '\)'
                break
            endif
        endif
    endwhile

    let endpat = ''
    for fence in fences
        let matches = matchlist(getline('.'), fence.start)
        if !empty(matches)
            " If 'end' pattern is not defined, copy the opening
            " delimiter
            let endpat = get(fence, 'end', fence.start)

            " Replace any instances of \0, \1, \2, ... with the
            " submatch from the opening delimiter
            let endpat = substitute(endpat, '\\\(\d\)', '\=matches[submatch(1)]', 'g')
            break
        endif
    endfor

    let end = search('^\s*' . endpat . '\s*$', 'nW')

    call cursor(curpos)

    return [start, end]
endfunction

function! s:createblock(ft, start, name, fence) abort
    let opt = printf('name: %s', a:name)
    let marker = printf(get(get(g:, 'medieval_option_fmt', {}), a:ft, s:optionfmt), opt)
    call append(a:start, ['', marker, a:fence.start, a:fence.end])
endfunction

function! s:extend(list, val)
    let data = a:val
    if data[-1] == ''
        let data = data[:-2]
    end
    return extend(a:list, data)
endfunction

" Wrapper around job start functions for both neovim and vim
function! s:jobstart(cmd, cb) abort
    let output = []
    if !get(g:, 'medieval_sync') && exists('*jobstart')
        call jobstart(a:cmd, {
                    \ 'on_stdout': {_, data, ... -> s:extend(output, data)},
                    \ 'on_stderr': {_, data, ... -> s:extend(output, data)},
                    \ 'on_exit': {... -> a:cb(output)},
                    \ 'stdout_buffered': 1,
                    \ 'stderr_buffered': 1,
                    \ })
    elseif !get(g:, 'medieval_sync') && exists('*job_start')
        call job_start(a:cmd, {
                    \ 'callback': {_, data -> add(output, data)},
                    \ 'exit_cb': {... -> a:cb(output)},
                    \ })
    elseif exists('*systemlist')
        let output = systemlist(join(a:cmd))
        call a:cb(output)
    else
        call s:error('Unable to start job')
    endif
endfunction

" Parse an options string on the given line number
function! s:parseopts(ft, lnum) abort
    let opts = {}
    let line = getline(a:lnum)
    let pat = get(get(g:, 'medieval_option_pat', {}), a:ft, s:optionpat)
    if line =~# pat . s:optspat
        let cnt = 0
        while 1
            let matches = matchlist(line, s:optspat, 0, cnt)
            if empty(matches)
                break
            endif
            let opts[matches[1]] = matches[2]
            let cnt += 1
        endwhile
    endif

    return opts
endfunction

function! s:require(ft, name) abort
    let [start, end] = s:findblock(a:ft, a:name)
    if !end
        return []
    endif

    let block = getline(start + 2, end - 1)

    let opts = s:parseopts(a:ft, start)
    if has_key(opts, 'require')
        return s:require(a:ft, opts.require) + block
    endif

    return block
endfunction

function! s:callback(context, output) abort
    let opts = a:context.opts
    if !has_key(opts, 'tangle')
        call delete(a:context.fname)
    endif

    if empty(a:output)
        return
    endif

    if has_key(opts, 'complete')
        call opts.complete(a:context, a:output)
    endif

    let start = a:context.start
    let end = a:context.end

    if get(opts, 'target', '') !=# ''
        if opts.target ==# 'self'
            call deletebufline('%', start + 1, end - 1)
            call append(start, a:output)
        elseif opts.target =~# '^@'
            call setreg(opts.target[1], a:output)
        elseif expand(opts.target) =~# '/'
            let f = fnamemodify(expand(opts.target), ':p')
            call writefile(a:output, f)
            echo 'Output written to ' . f
        else
            let [tstart, tend] = s:findblock(a:context.filetype, opts.target)
            if !tstart
                call s:createblock(a:context.filetype, end, opts.target, #{start: getline(start), end: getline(end)})
                let tstart = end + 2
                let tend = tstart + 1
            endif

            if !tend
                return s:error('Block "' . opts.target . '" doesn''t have a closing fence')
            endif

            call deletebufline('%', tstart + 2, tend - 1)
            call append(tstart + 1, a:output)
        endif
    else
        " Open result in scratch buffer
        if &splitbelow
            botright new
        else
            topleft new
        endif

        call append(0, a:output)
        call deletebufline('%', '$')
        exec 'resize' &previewheight
        setlocal buftype=nofile bufhidden=delete nobuflisted noswapfile winfixheight
        wincmd p
    endif

    if has_key(opts, 'after')
        call opts.after(a:context, a:output)
    endif
endfunction

function! medieval#evalrange(line1, line2, target) abort
    if !exists('g:medieval_langs')
        call s:error('g:medieval_langs is unset')
        return
    endif

    let fences = filter((s:fences + get(g:, 'medieval_fences', [])), 'has_key(v:val, "lang")')
    let view = winsaveview()

    " Collect opening fence lines with a language within the range
    let blocks = []
    let lnum = a:line1
    while lnum <= a:line2
        let line = getline(lnum)
        for fence in fences
            let matches = matchlist(line, fence.start)
            if !empty(matches) && matches[fence.lang] !=# ''
                " Skip named blocks without a target — these are output
                " destinations or dependency blocks, not source blocks
                let opts = s:parseopts(&filetype, lnum - 1)
                if !has_key(opts, 'name') || has_key(opts, 'target')
                    call add(blocks, lnum)
                endif
                break
            endif
        endfor
        let lnum += 1
    endwhile

    " Evaluate each block
    for blnum in blocks
        call cursor(blnum, 1)
        call medieval#eval(a:target)
    endfor

    call winrestview(view)
endfunction

function! medieval#eval(...) abort
    if !exists('g:medieval_langs')
        call s:error('g:medieval_langs is unset')
        return
    endif

    let view = winsaveview()
    let line = line('.')
    let fences = filter((s:fences + get(g:, 'medieval_fences', [])), 'has_key(v:val, "lang")')
    let fencepat = s:fencepat(fences)
    let start = search(fencepat, 'bcnW')
    if !start
        return
    endif

    " If cursor is in a named destination block, find and evaluate its source
    let opts = s:parseopts(&filetype, start - 1)
    if has_key(opts, 'name') && !has_key(opts, 'target')
        call cursor(1, 1)
        let pat = get(get(g:, 'medieval_option_pat', {}), &filetype, s:optionpat)
        while 1
            let srcline = search(pat . s:optspat, 'cW')
            if !srcline
                call winrestview(view)
                return s:error('No source block targeting "' . opts.name . '"')
            endif
            call cursor(srcline + 1, 1)
            if getline(srcline) =~# '\<target:\s*' . opts.name
                break
            endif
        endwhile
        call call('medieval#eval', a:000)
        call winrestview(view)
        return
    endif

    call cursor(start, 1)

    let lang = ''
    let endpat = ''
    for fence in fences
        let matches = matchlist(getline(start), fence.start)
        if !empty(matches)
            let lang = matches[fence.lang]
            let endpat = get(fence, 'end', fence.start)
            let endpat = substitute(endpat, '\\\(\d\)', '\=matches[submatch(1)]', 'g')
            break
        endif
    endfor

    if empty(lang)
        call winrestview(view)
        return s:error('Could not determine language for block')
    endif

    if empty(endpat)
        call winrestview(view)
        return s:error('No end pattern')
    endif

    let end = search('^\s*' . endpat . '\s*$', 'nW')
    if end < line
        call winrestview(view)
        return s:error('Closing fence not found')
    endif

    let langidx = index(map(copy(g:medieval_langs), 'split(v:val, "=", 1)[0]'), lang)
    if langidx < 0
        call winrestview(view)
        echo '''' . lang . ''' not found in g:medieval_langs'
        return
    endif

    let opts = s:parseopts(&filetype, start - 1)

    if a:0 && a:1 !=# ''
        let opts.target = a:1
    elseif s:validreg(v:register)
        let opts.target = '@' . v:register
    endif

    if g:medieval_langs[langidx] =~# '='
        let lang = split(g:medieval_langs[langidx], '=')[-1]
    endif

    if !executable(lang)
        call winrestview(view)
        return s:error('Command not found: ' . lang)
    endif

    if has_key(opts, 'tangle')
        let fname = expand(opts.tangle)
        echo 'Tangled source code written to ' . fname
    else
        let fname = tempname()
        if lang == "cmd"
            let fname .= ".bat"
        endif
    endif

    if a:0 > 1
        call extend(opts, a:2, 'error')
    endif

    let context = {'opts': opts, 'start': start, 'end': end, 'fname': fname, 'filetype': &filetype}

    let block = getline(start + 1, end - 1)
    if has_key(opts, 'require')
        let block = s:require(&filetype, opts.require) + block
    endif
    if has_key(opts, 'setup')
        call opts.setup(context, block)
    endif
    call writefile(block, fname)
    if lang == "cmd"
        call s:jobstart([fname], function('s:callback', [context]))
    else
        call s:jobstart([lang, fname], function('s:callback', [context]))
    endif
    call winrestview(view)
endfunction


================================================
FILE: doc/medieval.txt
================================================
*medieval.txt*  Evaluate Markdown code blocks in Vim

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

						*:EvalBlock* *medieval*

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 open the result of evaluating the block in the
|preview-window| (in this case, it will contain the text "Hello world!")

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

You can also use a range to evaluate multiple blocks at once. For example: >

	:%EvalBlock
<
evaluates all code blocks in the entire buffer. Similarly: >

	:10,50EvalBlock
<
evaluates all code blocks between lines 10 and 50. A visual selection also
works: select lines, then run |:EvalBlock|. Each block uses its own options
(target, require, etc.).

							*medieval-target*
You can also 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 -->
	$$
	$$
<
							*medieval-labels*

By default, 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
	after
	autoload
	doc
	```
<
In this example, only the second block will be rendered, since the first block
is nested within an HTML comment.

The label pattern can be changed on a per-filetype basis, if needed. See
|g:medieval_option_pat|.

							*medieval-require*

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
	```
<
							*medieval-tangle*
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)
<
Note that the value of the "tangle" option is always interpreted as the name
of a file, regardless of whether or not it contains a / character.

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 -->
<
							*medieval#eval()*
medieval#eval({target}[, {opts})
		Evaluate the block under the cursor. To replace the contents of
		the block (like |:EvalBlock!|) use "self" for {target}. If
		{target} is |v:null| or an empty string then it uses the
		"target" field from the block header |medieval-target| if it
		exists; otherwise, output is written to the |preview-window|.

		{opts} is an optional |Dict| accepting the following keys:
		  setup:	(function) A function to be called just before
				evaluating the code block. The function
				accepts two arguments: a "context" |Dict|
				containing the parameters used to evaluate the
				block (such as the start and end line number
				of the block) and the text withing the block
				as a list of lines. Modifications to the block
				text will affect what is evaluated.
		  complete:	(function) A function to be called when
				evaluation completes, before the output is
				written to the target block. The function
				accepts two arguments: a "context" |Dict|
				containing the parameters used to evaluate the
				block and the result of the block evaluation
				as a list of lines. Modifications to the
				output list will affect what is written to the
				target block.
		  after:	(function) A function to be called when
				evaluation completes, but after the output is
				written to the target block. The function
				accepts two arguments: a "context" |Dict|
				containing the parameters used to evaluate the
				block and the result of the block evaluation
				as a list of lines.

		Example: >

			function! s:complete(ctx, output)
			    let elapsed = reltimestr(reltime(a:ctx.start_time))
			    call add(a:output, 'Evaluation finished in ' . elapsed . 's')
			endfunction

			function! s:setup(ctx, input)
			    let a:ctx.start_time = reltime()
			endfunction

			function! s:after(ctx, input)
			    echo "Target has been updated."
			endfunction

			call medieval#eval('',
				\ #{setup: function('s:setup'),
				\   complete: function('s:complete'),
				\   after: function('s:after')})
<
							*medieval#evalrange()*
medieval#evalrange({line1}, {line2}, {target})
		Evaluate all code blocks between {line1} and {line2}.
		{target} is passed to |medieval#eval()| for each block. Use
		an empty string to let each block use its own target. Use
		"self" to replace each block's contents with its output.

						*g:medieval_langs*
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.

							*g:medieval_fences*
You can define custom code fence delimiters using the variable
|g:medieval_fences|. This variable is a |List| of |Dict|s 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, to use a block of the following form:
>
	<!-- name: katex -->
	{{< katex >}}
	{{< /katex >}}
<
You can set |g:medieval_fences| to
>
	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. The "\1" in the end pattern will be replaced by whatever
matches the capture group in the "start" pattern ("katex" in our example
above).

						*g:medieval_option_pat*
Medieval finds labeled blocks using an "option pattern". The default is
"^\s*<!--\s*" which matches HTML comments as described in this document. This
pattern can be overriden on a per-filetype basis by adding entries to the
|g:medieval_option_pat| variable. Example: >

	let g:medieval_option_pat = {}
	let g:medieval_option_pat.vimwiki = '^%%\s*'
<
						*g:medieval_option_fmt*
When Medieval creates a new block it will insert an option label
automatically. By default, the label will be "<!-- name: {name} -->", but this
can be overridden on a per-filetype basis by setting the
|g:medieval_option_fmt| variable. This variable is a |Dict| mapping filetype
to a |printf()| style pattern. Example: >

	let g:medieval_option_fmt = {}
	let g:medieval_option_fmt.vimwiki = '%%%% %s'
<
This example will instead insert "%% name: {name}" for new blocks. Note that
in the example above, the "%" characters are escaped with a 2nd "%" character.

 vim:tw=78:ts=8:noet:ft=help:norl:


================================================
FILE: plugin/medieval.vim
================================================
if get(g:, 'loaded_medieval')
    finish
endif
let g:loaded_medieval = 1

command! -bang -range -nargs=? EvalBlock
            \ if <range> > 0 |
            \   call medieval#evalrange(<line1>, <line2>, <bang>0 ? 'self' : <q-args>) |
            \ elseif <bang>0 |
            \   call medieval#eval('self') |
            \ else |
            \   call medieval#eval(<q-args>) |
            \ endif

nnoremap <silent> <Plug>(medieval-eval) :<C-U>call medieval#eval()<CR>


================================================
FILE: run-test.sh
================================================
#!/bin/sh
cd "$(dirname "$0")"

if [ ! -d test/vader.vim ]; then
  git clone --depth 1 https://github.com/junegunn/vader.vim test/vader.vim
fi

vim --not-a-term -Nu test/vimrc '+Vader! test/*.vader' 2>&1 | \
  perl -pe 's/\e[\[\]>][0-9;?]*[a-zA-Z]//g; s/\e[>=]//g' | \
  grep -E '(^Vader|^Starting|^  Starting|^    \(|^  Success|^Success|^Elapsed)'


================================================
FILE: test/medieval.vader
================================================
Before:
  let g:medieval_langs = ['sh']

" === :EvalBlock! — target self (doc line 65-79) ===

Given markdown (EvalBlock! replaces block content):
  ```sh
  echo "hello"
  ```

Execute (EvalBlock! replaces block in-place):
  2
  EvalBlock!

Then (Output replaces code block content):
  AssertEqual 'hello', getline(2)
  AssertEqual '```', getline(3)

" === Named target block (doc line 23-44) ===

Given markdown (Output sent to named target block):
  <!-- target: result -->
  ```sh
  echo "computed"
  ```

  <!-- name: result -->
  ```
  ```

Execute (Eval source block with named target):
  3
  EvalBlock

Then (Output written to named target block):
  AssertEqual 'computed', getline(8)

" === EvalBlock from destination block redirects to source ===

Given markdown (EvalBlock in destination block evaluates its source):
  <!-- target: dest -->
  ```sh
  echo "from source"
  ```

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

Execute (Cursor in destination block):
  8
  EvalBlock

Then (Source block was evaluated, output written to destination):
  AssertEqual 'from source', getline(8)

" === :EvalBlock {target} — explicit target argument (doc line 65) ===

Given markdown (Explicit target argument overrides header):
  ```sh
  echo "override"
  ```

  <!-- name: dest -->
  ```
  ```

Execute (Explicit target argument):
  2
  EvalBlock dest

Then (Output sent to explicit target, not header target):
  AssertEqual 'override', getline(7)

" === Info string formats (doc line 81-102) ===

Given markdown (Info string with curly-dot: {.lang}):
  ```{.sh}
  echo "dotlang"
  ```

Execute (EvalBlock! with {.lang} fence):
  2
  EvalBlock!

Then (Language parsed from {.lang} format):
  AssertEqual 'dotlang', getline(2)

Given markdown (Info string with curly: {lang}):
  ```{sh}
  echo "curlylang"
  ```

Execute (EvalBlock! with {lang} fence):
  2
  EvalBlock!

Then (Language parsed from {lang} format):
  AssertEqual 'curlylang', getline(2)

Given markdown (Info string with extra attributes):
  ``` {.sh .numberLines #my-id}
  echo "attrs"
  ```

Execute (EvalBlock! with extra attributes):
  2
  EvalBlock!

Then (Language parsed despite extra attributes):
  AssertEqual 'attrs', getline(2)

" === Tilde fences (doc line 104) ===
" SKIPPED: ~~~sh resolves end pattern to ~~~, but ~ is a special regex atom
" in Vim (last substitute string). With no prior :s, this gives E33.
" This is a pre-existing bug in the fence end-pattern handling.

" === Require — recursive dependencies (doc line 179-201) ===

Given markdown (Recursive require chain):
  <!-- 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"
  ```

Execute (Eval block with recursive require chain):
  13
  EvalBlock

Then (All dependencies resolved recursively):
  AssertEqual 'Hi, my name is Gregory Anders', getline(18)

" === Language alias (doc line 296-302) ===

Given markdown (Language alias sh=bash):
  ```sh
  echo "aliased"
  ```

Before:
  let g:medieval_langs = ['sh=bash']

Execute (EvalBlock! with aliased language):
  2
  EvalBlock!

Then (sh executed via bash alias):
  AssertEqual 'aliased', getline(2)

After:
  let g:medieval_langs = ['sh']

" === Register target (doc line 20-21) ===

Before:
  let g:medieval_langs = ['sh']

Given markdown (Register target stores output in register):
  ```sh
  echo "in register"
  ```

Execute (EvalBlock to register @a):
  2
  EvalBlock @a

Then (Output stored in register a):
  AssertEqual "in register\n", getreg('a')

" === File target (doc line 46-54) ===

Before:
  let g:medieval_langs = ['sh']
  if filereadable('_medieval_test_output.txt')
    throw 'Refusing to run: _medieval_test_output.txt already exists in working directory'
  endif

Given markdown (File target writes output to file):
  <!-- target: ./_medieval_test_output.txt -->
  ```sh
  echo "file output"
  ```

Execute (EvalBlock with file target):
  3
  EvalBlock

Then (Output written to file):
  Assert filereadable('_medieval_test_output.txt'), 'Output file should exist'
  AssertEqual ['file output'], readfile('_medieval_test_output.txt')

After:
  call delete('_medieval_test_output.txt')

" === Tangle (doc line 203-236) ===

Before:
  let g:medieval_langs = ['sh']
  if filereadable('_medieval_test_tangle.sh')
    throw 'Refusing to run: _medieval_test_tangle.sh already exists in working directory'
  endif

Given markdown (Tangle writes source to file before executing):
  <!-- tangle: _medieval_test_tangle.sh -->
  ```sh
  echo "tangled"
  ```

Execute (EvalBlock! with tangle option):
  3
  EvalBlock!

Then (Source tangled to file and executed):
  Assert filereadable('_medieval_test_tangle.sh'), 'Tangle file should exist'
  AssertEqual ['echo "tangled"'], readfile('_medieval_test_tangle.sh')
  AssertEqual 'tangled', getline(3)

After:
  call delete('_medieval_test_tangle.sh')

" === Range support ===

Before:
  let g:medieval_langs = ['sh']

Given markdown (Two blocks both targeting self):
  <!-- target: self -->
  ```sh
  echo "first"
  ```

  <!-- target: self -->
  ```sh
  echo "second"
  ```

Execute (Range eval replaces all blocks):
  %EvalBlock!


Then (Both blocks replaced):
  AssertEqual 'first', getline(3)
  AssertEqual 'second', getline(8)

Given markdown (Partial range evaluates only covered blocks):
  <!-- target: self -->
  ```sh
  echo "in range"
  ```

  <!-- target: self -->
  ```sh
  echo "also in range"
  ```

  <!-- target: self -->
  ```sh
  echo "out of range"
  ```

Execute (Range covers only first two blocks):
  1,10EvalBlock!


Then (First two blocks evaluated, third unchanged):
  AssertEqual 'in range', getline(3)
  AssertEqual 'also in range', getline(8)
  AssertEqual 'echo "out of range"', getline(13)

Given markdown (No blocks in range):
  This is just text.
  No code blocks here.

  ```sh
  echo "hello"
  ```

Execute (Range covers only text lines):
  1,2EvalBlock


Then (Buffer is unchanged):
  AssertEqual 'This is just text.', getline(1)
  AssertEqual 'No code blocks here.', getline(2)
  AssertEqual 'echo "hello"', getline(5)

Given markdown (Named target block):
  <!-- target: output -->
  ```sh
  echo "result"
  ```

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

Execute (Range eval with named target):
  %EvalBlock


Then (Named target block populated with output):
  AssertEqual 'result', getline(8)

Given markdown (Closing fences not treated as blocks):
  ```sh
  echo "one"
  ```

  ```sh
  echo "two"
  ```

Execute (Range eval only evaluates opening fences):
  %EvalBlock!


Then (Both blocks evaluated correctly):
  AssertEqual 'one', getline(2)
  AssertEqual '```', getline(3)
  AssertEqual 'two', getline(6)
  AssertEqual '```', getline(7)

" === Range skips named output blocks ===

Given markdown (Range skips named blocks that are output destinations):
  <!-- target: out -->
  ```sh
  echo "source"
  ```

  <!-- name: out -->
  ```sh
  old output
  ```

Execute (Range eval should only run the source block):
  %EvalBlock

Then (Source block output written to named block, named block not executed):
  AssertEqual 'source', getline(8)


================================================
FILE: test/vimrc
================================================
set nocompatible
filetype off
set runtimepath+=test/vader.vim
set runtimepath+=.
filetype plugin indent on
let g:medieval_langs = ['sh']
let g:medieval_sync = 1
Download .txt
gitextract_t68na24_/

├── .github/
│   └── workflows/
│       └── test.yml
├── LICENSE
├── README.md
├── autoload/
│   └── medieval.vim
├── doc/
│   └── medieval.txt
├── plugin/
│   └── medieval.vim
├── run-test.sh
└── test/
    ├── medieval.vader
    └── vimrc
Condensed preview — 9 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (37K chars).
[
  {
    "path": ".github/workflows/test.yml",
    "chars": 273,
    "preview": "name: Test\non:\n  push:\n    branches: [master]\n  pull_request:\njobs:\n  test:\n    runs-on: ubuntu-latest\n    steps:\n      "
  },
  {
    "path": "LICENSE",
    "chars": 1068,
    "preview": "MIT License\n\nCopyright (c) 2020 Greg Anders\n\nPermission is hereby granted, free of charge, to any person obtaining a cop"
  },
  {
    "path": "README.md",
    "chars": 1887,
    "preview": "# vim-medieval\n\nEvaluate Markdown code blocks within Vim.\n\n[![asciicast](https://asciinema.org/a/306995.svg)](https://as"
  },
  {
    "path": "autoload/medieval.vim",
    "chars": 11397,
    "preview": "const s:fences = [#{start: '\\([`~]\\{3,}\\)\\s*\\%({\\s*\\.\\?\\)\\?\\(\\a\\+\\)\\?', end: '\\1', lang: 2,}, #{start: '\\$\\$'}]\nlet s:op"
  },
  {
    "path": "doc/medieval.txt",
    "chars": 11383,
    "preview": "*medieval.txt*  Evaluate Markdown code blocks in Vim\n\nAuthor:  Gregory Anders <greg@gpanders.com>\nRepo:    https://githu"
  },
  {
    "path": "plugin/medieval.vim",
    "chars": 471,
    "preview": "if get(g:, 'loaded_medieval')\n    finish\nendif\nlet g:loaded_medieval = 1\n\ncommand! -bang -range -nargs=? EvalBlock\n     "
  },
  {
    "path": "run-test.sh",
    "chars": 349,
    "preview": "#!/bin/sh\ncd \"$(dirname \"$0\")\"\n\nif [ ! -d test/vader.vim ]; then\n  git clone --depth 1 https://github.com/junegunn/vader"
  },
  {
    "path": "test/medieval.vader",
    "chars": 7221,
    "preview": "Before:\n  let g:medieval_langs = ['sh']\n\n\" === :EvalBlock! — target self (doc line 65-79) ===\n\nGiven markdown (EvalBlock"
  },
  {
    "path": "test/vimrc",
    "chars": 161,
    "preview": "set nocompatible\nfiletype off\nset runtimepath+=test/vader.vim\nset runtimepath+=.\nfiletype plugin indent on\nlet g:medieva"
  }
]

About this extraction

This page contains the full source code of the gpanders/vim-medieval GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 9 files (33.4 KB), approximately 9.5k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!