[
  {
    "path": ".github/workflows/test.yml",
    "content": "name: Test\non:\n  push:\n    branches: [master]\n  pull_request:\njobs:\n  test:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n      - name: Check Vim\n        run: which vim && vim --version | head -1\n      - name: Run tests\n        run: ./run-test.sh\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2020 Greg Anders\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# vim-medieval\n\nEvaluate Markdown code blocks within Vim.\n\n[![asciicast](https://asciinema.org/a/306995.svg)](https://asciinema.org/a/306995)\n\n## Description\n\nMedieval allows you to evaluate code blocks in Markdown buffers of the\nfollowing form:\n\n````markdown\n```bash\necho \"Hello world!\"\n```\n````\n\nBy placing your cursor anywhere in the code block above and running\n`:EvalBlock`, Medieval will print the result of evaluating the block (in this\ncase, it will echo \"Hello world!\")\n\nYou can send the output of evaluation into another code block, allowing\nyou to do a primitive style of literate programming. You can accomplish this\nby adding a \"target\" parameter to your code block and creating a second code\nblock with a \"name\" parameter. The output of the evaluation of your code block\nwill be redirected to the targeted block. For example:\n\n````markdown\n<!-- target: squares -->\n```python\nprint([x*x for x in range(5)])\n```\n\n<!-- name: squares -->\n```\n```\n````\n\nIf you run `:EvalBlock` in the first code block, the second block will become\n\n````markdown\n<!-- name: squares -->\n```\n[0, 1, 4, 9, 16]\n```\n````\n\nMedieval can do a lot more. Read `:h medieval` for the full documentation.\n\n## Create a mapping\n\nMedieval does not create any mappings by default, but you can easily create one\nyourself by adding the following to the file\n`~/.vim/after/ftplugin/markdown.vim` (create it if it does not yet exist):\n\n```vim\nnmap <buffer> Z! <Plug>(medieval-eval)\n```\n\n## Limitations\n\nFor now, Medieval only works in Markdown buffers. If you'd like to see support\nadded for other file types, please see the [Contributing](#contributing)\nsection.\n\n## Contributing\n\nPlease feel free to contribute changes or bug fixes. You can [send patches][]\nto <git@gpanders.com> or submit a pull request on [GitHub][].\n\n[send patches]: https://git-send-email.io/\n[Github]: https://github.com/gpanders/vim-medieval\n"
  },
  {
    "path": "autoload/medieval.vim",
    "content": "const s:fences = [#{start: '\\([`~]\\{3,}\\)\\s*\\%({\\s*\\.\\?\\)\\?\\(\\a\\+\\)\\?', end: '\\1', lang: 2,}, #{start: '\\$\\$'}]\nlet s:opts = ['name', 'target', 'require', 'tangle']\nlet s:optspat = '\\(' . join(s:opts, '\\|') . '\\):\\s*\\([0-9A-Za-z_+.$#&/-]\\+\\)'\nlet s:optionfmt = '<!-- %s -->'\nlet s:optionpat = '^\\s*<!--\\s*'\n\nfunction! s:error(msg) abort\n    if empty(a:msg)\n        return\n    endif\n\n    echohl ErrorMsg\n    echom 'medieval: ' . a:msg\n    echohl None\nendfunction\n\n\" Check the v:register variable for a valid value to see if the user wants to\n\" copy output to a register\nfunction! s:validreg(reg) abort\n    if a:reg ==# ''\n        return v:false\n    endif\n\n    if a:reg ==# '\"'\n        return v:false\n    endif\n\n    if &clipboard =~# '^unnamed' && (a:reg ==# '*' || a:reg ==# '+')\n        return v:false\n    endif\n\n    return v:true\nendfunction\n\n\" Generate search pattern to match the start of any valid fence\nfunction! s:fencepat(fences) abort\n    return join(map(copy(a:fences), 'v:val.start'), '\\|')\nendfunction\n\n\" Find a code block with the given name and return the start and end lines.\n\" For example, s:findblock('foo') will find the following block:\n\"\n\"     <!-- name: foo -->\n\"     ```\n\"     ```\nfunction! s:findblock(ft, name) abort\n    let fences = s:fences + get(g:, 'medieval_fences', [])\n    let fencepat = s:fencepat(fences)\n\n    let curpos = getcurpos()[1:]\n\n    call cursor(1, 1)\n\n    let pat = get(get(g:, 'medieval_option_pat', {}), a:ft, s:optionpat)\n    while 1\n        let start = search(pat . s:optspat, 'cW')\n        if !start || start == line('$')\n            call cursor(curpos)\n            return [0, 0]\n        endif\n\n        \" Move the cursor so that we don't match on the current line again\n        call cursor(start + 1, 1)\n\n        if getline(start) =~# '\\<name:\\s*' . a:name\n            if getline('.') =~# '^\\s*\\%(' . fencepat . '\\)'\n                break\n            endif\n        endif\n    endwhile\n\n    let endpat = ''\n    for fence in fences\n        let matches = matchlist(getline('.'), fence.start)\n        if !empty(matches)\n            \" If 'end' pattern is not defined, copy the opening\n            \" delimiter\n            let endpat = get(fence, 'end', fence.start)\n\n            \" Replace any instances of \\0, \\1, \\2, ... with the\n            \" submatch from the opening delimiter\n            let endpat = substitute(endpat, '\\\\\\(\\d\\)', '\\=matches[submatch(1)]', 'g')\n            break\n        endif\n    endfor\n\n    let end = search('^\\s*' . endpat . '\\s*$', 'nW')\n\n    call cursor(curpos)\n\n    return [start, end]\nendfunction\n\nfunction! s:createblock(ft, start, name, fence) abort\n    let opt = printf('name: %s', a:name)\n    let marker = printf(get(get(g:, 'medieval_option_fmt', {}), a:ft, s:optionfmt), opt)\n    call append(a:start, ['', marker, a:fence.start, a:fence.end])\nendfunction\n\nfunction! s:extend(list, val)\n    let data = a:val\n    if data[-1] == ''\n        let data = data[:-2]\n    end\n    return extend(a:list, data)\nendfunction\n\n\" Wrapper around job start functions for both neovim and vim\nfunction! s:jobstart(cmd, cb) abort\n    let output = []\n    if !get(g:, 'medieval_sync') && exists('*jobstart')\n        call jobstart(a:cmd, {\n                    \\ 'on_stdout': {_, data, ... -> s:extend(output, data)},\n                    \\ 'on_stderr': {_, data, ... -> s:extend(output, data)},\n                    \\ 'on_exit': {... -> a:cb(output)},\n                    \\ 'stdout_buffered': 1,\n                    \\ 'stderr_buffered': 1,\n                    \\ })\n    elseif !get(g:, 'medieval_sync') && exists('*job_start')\n        call job_start(a:cmd, {\n                    \\ 'callback': {_, data -> add(output, data)},\n                    \\ 'exit_cb': {... -> a:cb(output)},\n                    \\ })\n    elseif exists('*systemlist')\n        let output = systemlist(join(a:cmd))\n        call a:cb(output)\n    else\n        call s:error('Unable to start job')\n    endif\nendfunction\n\n\" Parse an options string on the given line number\nfunction! s:parseopts(ft, lnum) abort\n    let opts = {}\n    let line = getline(a:lnum)\n    let pat = get(get(g:, 'medieval_option_pat', {}), a:ft, s:optionpat)\n    if line =~# pat . s:optspat\n        let cnt = 0\n        while 1\n            let matches = matchlist(line, s:optspat, 0, cnt)\n            if empty(matches)\n                break\n            endif\n            let opts[matches[1]] = matches[2]\n            let cnt += 1\n        endwhile\n    endif\n\n    return opts\nendfunction\n\nfunction! s:require(ft, name) abort\n    let [start, end] = s:findblock(a:ft, a:name)\n    if !end\n        return []\n    endif\n\n    let block = getline(start + 2, end - 1)\n\n    let opts = s:parseopts(a:ft, start)\n    if has_key(opts, 'require')\n        return s:require(a:ft, opts.require) + block\n    endif\n\n    return block\nendfunction\n\nfunction! s:callback(context, output) abort\n    let opts = a:context.opts\n    if !has_key(opts, 'tangle')\n        call delete(a:context.fname)\n    endif\n\n    if empty(a:output)\n        return\n    endif\n\n    if has_key(opts, 'complete')\n        call opts.complete(a:context, a:output)\n    endif\n\n    let start = a:context.start\n    let end = a:context.end\n\n    if get(opts, 'target', '') !=# ''\n        if opts.target ==# 'self'\n            call deletebufline('%', start + 1, end - 1)\n            call append(start, a:output)\n        elseif opts.target =~# '^@'\n            call setreg(opts.target[1], a:output)\n        elseif expand(opts.target) =~# '/'\n            let f = fnamemodify(expand(opts.target), ':p')\n            call writefile(a:output, f)\n            echo 'Output written to ' . f\n        else\n            let [tstart, tend] = s:findblock(a:context.filetype, opts.target)\n            if !tstart\n                call s:createblock(a:context.filetype, end, opts.target, #{start: getline(start), end: getline(end)})\n                let tstart = end + 2\n                let tend = tstart + 1\n            endif\n\n            if !tend\n                return s:error('Block \"' . opts.target . '\" doesn''t have a closing fence')\n            endif\n\n            call deletebufline('%', tstart + 2, tend - 1)\n            call append(tstart + 1, a:output)\n        endif\n    else\n        \" Open result in scratch buffer\n        if &splitbelow\n            botright new\n        else\n            topleft new\n        endif\n\n        call append(0, a:output)\n        call deletebufline('%', '$')\n        exec 'resize' &previewheight\n        setlocal buftype=nofile bufhidden=delete nobuflisted noswapfile winfixheight\n        wincmd p\n    endif\n\n    if has_key(opts, 'after')\n        call opts.after(a:context, a:output)\n    endif\nendfunction\n\nfunction! medieval#evalrange(line1, line2, target) abort\n    if !exists('g:medieval_langs')\n        call s:error('g:medieval_langs is unset')\n        return\n    endif\n\n    let fences = filter((s:fences + get(g:, 'medieval_fences', [])), 'has_key(v:val, \"lang\")')\n    let view = winsaveview()\n\n    \" Collect opening fence lines with a language within the range\n    let blocks = []\n    let lnum = a:line1\n    while lnum <= a:line2\n        let line = getline(lnum)\n        for fence in fences\n            let matches = matchlist(line, fence.start)\n            if !empty(matches) && matches[fence.lang] !=# ''\n                \" Skip named blocks without a target — these are output\n                \" destinations or dependency blocks, not source blocks\n                let opts = s:parseopts(&filetype, lnum - 1)\n                if !has_key(opts, 'name') || has_key(opts, 'target')\n                    call add(blocks, lnum)\n                endif\n                break\n            endif\n        endfor\n        let lnum += 1\n    endwhile\n\n    \" Evaluate each block\n    for blnum in blocks\n        call cursor(blnum, 1)\n        call medieval#eval(a:target)\n    endfor\n\n    call winrestview(view)\nendfunction\n\nfunction! medieval#eval(...) abort\n    if !exists('g:medieval_langs')\n        call s:error('g:medieval_langs is unset')\n        return\n    endif\n\n    let view = winsaveview()\n    let line = line('.')\n    let fences = filter((s:fences + get(g:, 'medieval_fences', [])), 'has_key(v:val, \"lang\")')\n    let fencepat = s:fencepat(fences)\n    let start = search(fencepat, 'bcnW')\n    if !start\n        return\n    endif\n\n    \" If cursor is in a named destination block, find and evaluate its source\n    let opts = s:parseopts(&filetype, start - 1)\n    if has_key(opts, 'name') && !has_key(opts, 'target')\n        call cursor(1, 1)\n        let pat = get(get(g:, 'medieval_option_pat', {}), &filetype, s:optionpat)\n        while 1\n            let srcline = search(pat . s:optspat, 'cW')\n            if !srcline\n                call winrestview(view)\n                return s:error('No source block targeting \"' . opts.name . '\"')\n            endif\n            call cursor(srcline + 1, 1)\n            if getline(srcline) =~# '\\<target:\\s*' . opts.name\n                break\n            endif\n        endwhile\n        call call('medieval#eval', a:000)\n        call winrestview(view)\n        return\n    endif\n\n    call cursor(start, 1)\n\n    let lang = ''\n    let endpat = ''\n    for fence in fences\n        let matches = matchlist(getline(start), fence.start)\n        if !empty(matches)\n            let lang = matches[fence.lang]\n            let endpat = get(fence, 'end', fence.start)\n            let endpat = substitute(endpat, '\\\\\\(\\d\\)', '\\=matches[submatch(1)]', 'g')\n            break\n        endif\n    endfor\n\n    if empty(lang)\n        call winrestview(view)\n        return s:error('Could not determine language for block')\n    endif\n\n    if empty(endpat)\n        call winrestview(view)\n        return s:error('No end pattern')\n    endif\n\n    let end = search('^\\s*' . endpat . '\\s*$', 'nW')\n    if end < line\n        call winrestview(view)\n        return s:error('Closing fence not found')\n    endif\n\n    let langidx = index(map(copy(g:medieval_langs), 'split(v:val, \"=\", 1)[0]'), lang)\n    if langidx < 0\n        call winrestview(view)\n        echo '''' . lang . ''' not found in g:medieval_langs'\n        return\n    endif\n\n    let opts = s:parseopts(&filetype, start - 1)\n\n    if a:0 && a:1 !=# ''\n        let opts.target = a:1\n    elseif s:validreg(v:register)\n        let opts.target = '@' . v:register\n    endif\n\n    if g:medieval_langs[langidx] =~# '='\n        let lang = split(g:medieval_langs[langidx], '=')[-1]\n    endif\n\n    if !executable(lang)\n        call winrestview(view)\n        return s:error('Command not found: ' . lang)\n    endif\n\n    if has_key(opts, 'tangle')\n        let fname = expand(opts.tangle)\n        echo 'Tangled source code written to ' . fname\n    else\n        let fname = tempname()\n        if lang == \"cmd\"\n            let fname .= \".bat\"\n        endif\n    endif\n\n    if a:0 > 1\n        call extend(opts, a:2, 'error')\n    endif\n\n    let context = {'opts': opts, 'start': start, 'end': end, 'fname': fname, 'filetype': &filetype}\n\n    let block = getline(start + 1, end - 1)\n    if has_key(opts, 'require')\n        let block = s:require(&filetype, opts.require) + block\n    endif\n    if has_key(opts, 'setup')\n        call opts.setup(context, block)\n    endif\n    call writefile(block, fname)\n    if lang == \"cmd\"\n        call s:jobstart([fname], function('s:callback', [context]))\n    else\n        call s:jobstart([lang, fname], function('s:callback', [context]))\n    endif\n    call winrestview(view)\nendfunction\n"
  },
  {
    "path": "doc/medieval.txt",
    "content": "*medieval.txt*  Evaluate Markdown code blocks in Vim\n\nAuthor:  Gregory Anders <greg@gpanders.com>\nRepo:    https://github.com/gpanders/vim-medieval\nLicense: Same terms as Vim itself (see |license|)\n\n\t\t\t\t\t\t*:EvalBlock* *medieval*\n\nMedieval allows you to evaluate code blocks in Markdown buffers of the\nfollowing form:\n>\n\t```bash\n\techo \"Hello world!\"\n\t```\n<\nBy placing your cursor anywhere in the code block above and running\n|:EvalBlock|, Medieval will open the result of evaluating the block in the\n|preview-window| (in this case, it will contain the text \"Hello world!\")\n\nYou can also redirect the output of the evaluation into a register using\n|:EvalBlock| @{0-9a-z\".=*+}.\n\nYou can also use a range to evaluate multiple blocks at once. For example: >\n\n\t:%EvalBlock\n<\nevaluates all code blocks in the entire buffer. Similarly: >\n\n\t:10,50EvalBlock\n<\nevaluates all code blocks between lines 10 and 50. A visual selection also\nworks: select lines, then run |:EvalBlock|. Each block uses its own options\n(target, require, etc.).\n\n\t\t\t\t\t\t\t*medieval-target*\nYou can also send the output of evaluation into another code block, allowing\nyou to do a primitive style of literate programming. You can accomplish this\nby adding a \"target\" parameter to your code block and creating a second code\nblock with a \"name\" parameter. The output of the evaluation of your code block\nwill be redirected to the targeted block. For example:\n>\n\t<!-- target: squares -->\n\t```python\n\tprint([x*x for x in range(5)])\n\t```\n\n\t<!-- name: squares -->\n\t```\n\t```\n<\nIf you run |:EvalBlock| in the first code block, the second block will become\n>\n\t<!-- name: squares -->\n\t```\n\t[0, 1, 4, 9, 16]\n\t```\n<\nThe target of a block can also be a file. If the target name contains a \"/\"\ncharacter, it is assumed to be a file path. File paths can contain environment\nvariables and tilde expansion. Example:\n>\n\t<!-- target: $HOME/squares.txt -->\n\t```python\n\tprint([x*x for x in range(5)])\n\t```\n<\nNote that the following will write to a code block named \"squares.txt\" (and\ncreate it if it doesn't exist) instead of writing to a file called\n\"squares.txt\":\n>\n\t<!-- target: squares.txt -->\n\nTo write to a file called \"squares.txt\", use: >\n\n\t<!-- target: ./squares.txt -->\n<\nYou can manually specify a target block using |:EvalBlock| {target}. With\n[!], |:EvalBlock| will cause the evaluated code block to replace its own\ncontents with the result of its evaluation:\n>\n\t```sh\n\tfortune\n\t```\n<\nAfter |:EvalBlock!|:\n>\n\t```sh\n\tThe difference between art and science is that science is what we\n\tunderstand well enough to explain to a computer.  Art is everything else.\n\t                -- Donald Knuth, \"Discover\"\n\t```\n<\nThe language of the block being executed is detected through the text next to\nthe opening code fence (known as the \"info string\"). There is no formal\nspecification for how the info string should be formatted; however, Medieval\ncan detect info strings in any of the following formats:\n>\n\t```lang\n\t```\n\n\t```{.lang}\n\t```\n\n\t```{lang}\n\t```\n<\nWhitespace is allowed before the info string. The closing \"}\" is not required\nfor the latter two styles, meaning you can use info strings such as\n>\n\t``` {.python .numberLines #my-id}\n\t```\n<\nNote, however, that when using this kind of info string the language name must\nbe first for Medieval to correctly detect it.\n\nThe target block can be either another code block (delimited by \"```\" or\n\"~~~\") or a LaTeX math block (delimited by \"$$\"):\n>\n\t<!-- target: math -->\n\t```python\n\tprint(r\"\\text{Hello LaTeX!}\")\n\t```\n\n\t<!-- name: math -->\n\t$$\n\t$$\n<\n\t\t\t\t\t\t\t*medieval-labels*\n\nBy default, the block labels must be of the form \"<!-- {option}: {value}[,]\n[{option}: {value}[,] [...]]\" where {option} is one of \"name\", \"target\",\n\"require\", or \"tangle\". The label can be preceeded by whitespace, but no other\ncharacters. The option values can be composed of the following characters:\n\"0-9A-Za-z_+.$#&/-\". Note that the closing tag of the HTML comment is not\nrequired. This allows you to embed the code block within an HTML block comment\nso that the block will not be rendered in the final output. For example:\n>\n\t<!-- target: example\n\t```sh\n\techo '$ ls -1'\n\tls -1\n\t```\n\t-->\n\n\t<!-- name: example -->\n\t```sh\n\t$ ls -1\n\tLICENSE\n\tREADME.md\n\tafter\n\tautoload\n\tdoc\n\t```\n<\nIn this example, only the second block will be rendered, since the first block\nis nested within an HTML comment.\n\nThe label pattern can be changed on a per-filetype basis, if needed. See\n|g:medieval_option_pat|.\n\n\t\t\t\t\t\t\t*medieval-require*\n\nCode blocks can be combined using the \"require\" option. The argument to the\n\"require\" option is the name of another code block which will be evaluated\nbefore the contents of the block itself. Required blocks must use the same\nlanguage as the requiring block.\n\nFor example,\n>\n\t<!-- name: numpy -->\n\t```python\n\timport numpy as np\n\t```\n\n\t<!-- target: output, require: numpy -->\n\t```python\n\tprint(np.arange(1, 5))\n\t```\n\n\t<!-- name: output -->\n\t```\n\t```\n<\nRunning |:EvalBlock| in the second code block produces:\n>\n\t<!-- name: output -->\n\t```\n\t[1 2 3 4]\n\t```\n<\nBlocks can have recursive dependencies:\n>\n\t<!-- name: first_name -->\n\t```sh\n\tfirst_name=\"Gregory\"\n\t```\n\n\t<!-- name: full_name, require: first_name -->\n\t```sh\n\tfull_name=\"$first_name Anders\"\n\t```\n\n\t<!-- target: greeting, require: full_name -->\n\t```sh\n\techo \"Hi, my name is $full_name\"\n\t```\n<\nAfter running :EvalBlock in the block above...\n>\n\t<!-- name: greeting -->\n\t```\n\tHi, my name is Gregory Anders\n\t```\n<\n\t\t\t\t\t\t\t*medieval-tangle*\nThe source code in a code block can be written to a given file before\nexecuting by using the \"tangle\" option. This can be used in conjunction with\nthe \"require\" keyword to combine multiple blocks together into a single\ncombined source file.\n\nExample:\n>\n\t<!-- name: numpy -->\n\t```python\n\timport numpy as np\n\t```\n\n\t<!-- require: numpy tangle: script.py -->\n\t```python\n\tx = np.arange(5)\n\tprint(x)\n\t```\n<\nWhen you run |:EvalBlock| on the second code block above, a new file called\n\"script.py\" will be generated in your current working directory with the\ncontents\n>\n\timport numpy as np\n\tx = np.arange(5)\n\tprint(x)\n<\nNote that the value of the \"tangle\" option is always interpreted as the name\nof a file, regardless of whether or not it contains a / character.\n\nIf you only wish to use the tangling feature without printing the output\nof the code block, you can use `/dev/null` as the block target:\n>\n\t<!-- target: /dev/null tangle: script.py -->\n<\n\t\t\t\t\t\t\t*medieval#eval()*\nmedieval#eval({target}[, {opts})\n\t\tEvaluate the block under the cursor. To replace the contents of\n\t\tthe block (like |:EvalBlock!|) use \"self\" for {target}. If\n\t\t{target} is |v:null| or an empty string then it uses the\n\t\t\"target\" field from the block header |medieval-target| if it\n\t\texists; otherwise, output is written to the |preview-window|.\n\n\t\t{opts} is an optional |Dict| accepting the following keys:\n\t\t  setup:\t(function) A function to be called just before\n\t\t\t\tevaluating the code block. The function\n\t\t\t\taccepts two arguments: a \"context\" |Dict|\n\t\t\t\tcontaining the parameters used to evaluate the\n\t\t\t\tblock (such as the start and end line number\n\t\t\t\tof the block) and the text withing the block\n\t\t\t\tas a list of lines. Modifications to the block\n\t\t\t\ttext will affect what is evaluated.\n\t\t  complete:\t(function) A function to be called when\n\t\t\t\tevaluation completes, before the output is\n\t\t\t\twritten to the target block. The function\n\t\t\t\taccepts two arguments: a \"context\" |Dict|\n\t\t\t\tcontaining the parameters used to evaluate the\n\t\t\t\tblock and the result of the block evaluation\n\t\t\t\tas a list of lines. Modifications to the\n\t\t\t\toutput list will affect what is written to the\n\t\t\t\ttarget block.\n\t\t  after:\t(function) A function to be called when\n\t\t\t\tevaluation completes, but after the output is\n\t\t\t\twritten to the target block. The function\n\t\t\t\taccepts two arguments: a \"context\" |Dict|\n\t\t\t\tcontaining the parameters used to evaluate the\n\t\t\t\tblock and the result of the block evaluation\n\t\t\t\tas a list of lines.\n\n\t\tExample: >\n\n\t\t\tfunction! s:complete(ctx, output)\n\t\t\t    let elapsed = reltimestr(reltime(a:ctx.start_time))\n\t\t\t    call add(a:output, 'Evaluation finished in ' . elapsed . 's')\n\t\t\tendfunction\n\n\t\t\tfunction! s:setup(ctx, input)\n\t\t\t    let a:ctx.start_time = reltime()\n\t\t\tendfunction\n\n\t\t\tfunction! s:after(ctx, input)\n\t\t\t    echo \"Target has been updated.\"\n\t\t\tendfunction\n\n\t\t\tcall medieval#eval('',\n\t\t\t\t\\ #{setup: function('s:setup'),\n\t\t\t\t\\   complete: function('s:complete'),\n\t\t\t\t\\   after: function('s:after')})\n<\n\t\t\t\t\t\t\t*medieval#evalrange()*\nmedieval#evalrange({line1}, {line2}, {target})\n\t\tEvaluate all code blocks between {line1} and {line2}.\n\t\t{target} is passed to |medieval#eval()| for each block. Use\n\t\tan empty string to let each block use its own target. Use\n\t\t\"self\" to replace each block's contents with its output.\n\n\t\t\t\t\t\t*g:medieval_langs*\nMedieval will only attempt to execute code blocks in languages explicitly\nlisted in the variable |g:medieval_langs|. The structure of this variable is a\nlist of strings corresponding to whitelisted languages that can be\ninterpreted. If a language's interpreter has a different name than the\nlanguage itself, you can use the form \"{lang}={interpreter}\" to specify what\ninterpreter should be used.\n\nFor example, to allow Medieval to run Python, Ruby, and shell scripts, use\n>\n\tlet g:medieval_langs = ['python=python3', 'ruby', 'sh', 'console=bash']\n<\nBy default, |g:medieval_langs| is empty, so you must specify this variable\nyourself.\n\n\t\t\t\t\t\t\t*g:medieval_fences*\nYou can define custom code fence delimiters using the variable\n|g:medieval_fences|. This variable is a |List| of |Dict|s containing a \"start\"\nkey that defines a pattern for the opening delimiter of the code block and an\noptional \"end\" key that defines a pattern for the closing delimiter of the\ncode block. If \"end\" is omitted, then the closing delimiter is assumed to be\nthe same as the opening delimiter.\n\nFor example, to use a block of the following form:\n>\n\t<!-- name: katex -->\n\t{{< katex >}}\n\t{{< /katex >}}\n<\nYou can set |g:medieval_fences| to\n>\n\tlet g:medieval_fences = [{\n\t\t\\ 'start': '{{<\\s\\+\\(\\S\\+\\)\\s\\+>}}',\n\t\t\\ 'end': '{{<\\s\\+/\\1\\s\\+>}}',\n\t\t\\ }]\n<\nNote the use of a capture group in the \"start\" pattern and the use of \"\\1\" in\nthe end pattern. The \"\\1\" in the end pattern will be replaced by whatever\nmatches the capture group in the \"start\" pattern (\"katex\" in our example\nabove).\n\n\t\t\t\t\t\t*g:medieval_option_pat*\nMedieval finds labeled blocks using an \"option pattern\". The default is\n\"^\\s*<!--\\s*\" which matches HTML comments as described in this document. This\npattern can be overriden on a per-filetype basis by adding entries to the\n|g:medieval_option_pat| variable. Example: >\n\n\tlet g:medieval_option_pat = {}\n\tlet g:medieval_option_pat.vimwiki = '^%%\\s*'\n<\n\t\t\t\t\t\t*g:medieval_option_fmt*\nWhen Medieval creates a new block it will insert an option label\nautomatically. By default, the label will be \"<!-- name: {name} -->\", but this\ncan be overridden on a per-filetype basis by setting the\n|g:medieval_option_fmt| variable. This variable is a |Dict| mapping filetype\nto a |printf()| style pattern. Example: >\n\n\tlet g:medieval_option_fmt = {}\n\tlet g:medieval_option_fmt.vimwiki = '%%%% %s'\n<\nThis example will instead insert \"%% name: {name}\" for new blocks. Note that\nin the example above, the \"%\" characters are escaped with a 2nd \"%\" character.\n\n vim:tw=78:ts=8:noet:ft=help:norl:\n"
  },
  {
    "path": "plugin/medieval.vim",
    "content": "if get(g:, 'loaded_medieval')\n    finish\nendif\nlet g:loaded_medieval = 1\n\ncommand! -bang -range -nargs=? EvalBlock\n            \\ if <range> > 0 |\n            \\   call medieval#evalrange(<line1>, <line2>, <bang>0 ? 'self' : <q-args>) |\n            \\ elseif <bang>0 |\n            \\   call medieval#eval('self') |\n            \\ else |\n            \\   call medieval#eval(<q-args>) |\n            \\ endif\n\nnnoremap <silent> <Plug>(medieval-eval) :<C-U>call medieval#eval()<CR>\n"
  },
  {
    "path": "run-test.sh",
    "content": "#!/bin/sh\ncd \"$(dirname \"$0\")\"\n\nif [ ! -d test/vader.vim ]; then\n  git clone --depth 1 https://github.com/junegunn/vader.vim test/vader.vim\nfi\n\nvim --not-a-term -Nu test/vimrc '+Vader! test/*.vader' 2>&1 | \\\n  perl -pe 's/\\e[\\[\\]>][0-9;?]*[a-zA-Z]//g; s/\\e[>=]//g' | \\\n  grep -E '(^Vader|^Starting|^  Starting|^    \\(|^  Success|^Success|^Elapsed)'\n"
  },
  {
    "path": "test/medieval.vader",
    "content": "Before:\n  let g:medieval_langs = ['sh']\n\n\" === :EvalBlock! — target self (doc line 65-79) ===\n\nGiven markdown (EvalBlock! replaces block content):\n  ```sh\n  echo \"hello\"\n  ```\n\nExecute (EvalBlock! replaces block in-place):\n  2\n  EvalBlock!\n\nThen (Output replaces code block content):\n  AssertEqual 'hello', getline(2)\n  AssertEqual '```', getline(3)\n\n\" === Named target block (doc line 23-44) ===\n\nGiven markdown (Output sent to named target block):\n  <!-- target: result -->\n  ```sh\n  echo \"computed\"\n  ```\n\n  <!-- name: result -->\n  ```\n  ```\n\nExecute (Eval source block with named target):\n  3\n  EvalBlock\n\nThen (Output written to named target block):\n  AssertEqual 'computed', getline(8)\n\n\" === EvalBlock from destination block redirects to source ===\n\nGiven markdown (EvalBlock in destination block evaluates its source):\n  <!-- target: dest -->\n  ```sh\n  echo \"from source\"\n  ```\n\n  <!-- name: dest -->\n  ```\n  old output\n  ```\n\nExecute (Cursor in destination block):\n  8\n  EvalBlock\n\nThen (Source block was evaluated, output written to destination):\n  AssertEqual 'from source', getline(8)\n\n\" === :EvalBlock {target} — explicit target argument (doc line 65) ===\n\nGiven markdown (Explicit target argument overrides header):\n  ```sh\n  echo \"override\"\n  ```\n\n  <!-- name: dest -->\n  ```\n  ```\n\nExecute (Explicit target argument):\n  2\n  EvalBlock dest\n\nThen (Output sent to explicit target, not header target):\n  AssertEqual 'override', getline(7)\n\n\" === Info string formats (doc line 81-102) ===\n\nGiven markdown (Info string with curly-dot: {.lang}):\n  ```{.sh}\n  echo \"dotlang\"\n  ```\n\nExecute (EvalBlock! with {.lang} fence):\n  2\n  EvalBlock!\n\nThen (Language parsed from {.lang} format):\n  AssertEqual 'dotlang', getline(2)\n\nGiven markdown (Info string with curly: {lang}):\n  ```{sh}\n  echo \"curlylang\"\n  ```\n\nExecute (EvalBlock! with {lang} fence):\n  2\n  EvalBlock!\n\nThen (Language parsed from {lang} format):\n  AssertEqual 'curlylang', getline(2)\n\nGiven markdown (Info string with extra attributes):\n  ``` {.sh .numberLines #my-id}\n  echo \"attrs\"\n  ```\n\nExecute (EvalBlock! with extra attributes):\n  2\n  EvalBlock!\n\nThen (Language parsed despite extra attributes):\n  AssertEqual 'attrs', getline(2)\n\n\" === Tilde fences (doc line 104) ===\n\" SKIPPED: ~~~sh resolves end pattern to ~~~, but ~ is a special regex atom\n\" in Vim (last substitute string). With no prior :s, this gives E33.\n\" This is a pre-existing bug in the fence end-pattern handling.\n\n\" === Require — recursive dependencies (doc line 179-201) ===\n\nGiven markdown (Recursive require chain):\n  <!-- name: first_name -->\n  ```sh\n  first_name=\"Gregory\"\n  ```\n\n  <!-- name: full_name, require: first_name -->\n  ```sh\n  full_name=\"$first_name Anders\"\n  ```\n\n  <!-- target: greeting, require: full_name -->\n  ```sh\n  echo \"Hi, my name is $full_name\"\n  ```\n\nExecute (Eval block with recursive require chain):\n  13\n  EvalBlock\n\nThen (All dependencies resolved recursively):\n  AssertEqual 'Hi, my name is Gregory Anders', getline(18)\n\n\" === Language alias (doc line 296-302) ===\n\nGiven markdown (Language alias sh=bash):\n  ```sh\n  echo \"aliased\"\n  ```\n\nBefore:\n  let g:medieval_langs = ['sh=bash']\n\nExecute (EvalBlock! with aliased language):\n  2\n  EvalBlock!\n\nThen (sh executed via bash alias):\n  AssertEqual 'aliased', getline(2)\n\nAfter:\n  let g:medieval_langs = ['sh']\n\n\" === Register target (doc line 20-21) ===\n\nBefore:\n  let g:medieval_langs = ['sh']\n\nGiven markdown (Register target stores output in register):\n  ```sh\n  echo \"in register\"\n  ```\n\nExecute (EvalBlock to register @a):\n  2\n  EvalBlock @a\n\nThen (Output stored in register a):\n  AssertEqual \"in register\\n\", getreg('a')\n\n\" === File target (doc line 46-54) ===\n\nBefore:\n  let g:medieval_langs = ['sh']\n  if filereadable('_medieval_test_output.txt')\n    throw 'Refusing to run: _medieval_test_output.txt already exists in working directory'\n  endif\n\nGiven markdown (File target writes output to file):\n  <!-- target: ./_medieval_test_output.txt -->\n  ```sh\n  echo \"file output\"\n  ```\n\nExecute (EvalBlock with file target):\n  3\n  EvalBlock\n\nThen (Output written to file):\n  Assert filereadable('_medieval_test_output.txt'), 'Output file should exist'\n  AssertEqual ['file output'], readfile('_medieval_test_output.txt')\n\nAfter:\n  call delete('_medieval_test_output.txt')\n\n\" === Tangle (doc line 203-236) ===\n\nBefore:\n  let g:medieval_langs = ['sh']\n  if filereadable('_medieval_test_tangle.sh')\n    throw 'Refusing to run: _medieval_test_tangle.sh already exists in working directory'\n  endif\n\nGiven markdown (Tangle writes source to file before executing):\n  <!-- tangle: _medieval_test_tangle.sh -->\n  ```sh\n  echo \"tangled\"\n  ```\n\nExecute (EvalBlock! with tangle option):\n  3\n  EvalBlock!\n\nThen (Source tangled to file and executed):\n  Assert filereadable('_medieval_test_tangle.sh'), 'Tangle file should exist'\n  AssertEqual ['echo \"tangled\"'], readfile('_medieval_test_tangle.sh')\n  AssertEqual 'tangled', getline(3)\n\nAfter:\n  call delete('_medieval_test_tangle.sh')\n\n\" === Range support ===\n\nBefore:\n  let g:medieval_langs = ['sh']\n\nGiven markdown (Two blocks both targeting self):\n  <!-- target: self -->\n  ```sh\n  echo \"first\"\n  ```\n\n  <!-- target: self -->\n  ```sh\n  echo \"second\"\n  ```\n\nExecute (Range eval replaces all blocks):\n  %EvalBlock!\n\n\nThen (Both blocks replaced):\n  AssertEqual 'first', getline(3)\n  AssertEqual 'second', getline(8)\n\nGiven markdown (Partial range evaluates only covered blocks):\n  <!-- target: self -->\n  ```sh\n  echo \"in range\"\n  ```\n\n  <!-- target: self -->\n  ```sh\n  echo \"also in range\"\n  ```\n\n  <!-- target: self -->\n  ```sh\n  echo \"out of range\"\n  ```\n\nExecute (Range covers only first two blocks):\n  1,10EvalBlock!\n\n\nThen (First two blocks evaluated, third unchanged):\n  AssertEqual 'in range', getline(3)\n  AssertEqual 'also in range', getline(8)\n  AssertEqual 'echo \"out of range\"', getline(13)\n\nGiven markdown (No blocks in range):\n  This is just text.\n  No code blocks here.\n\n  ```sh\n  echo \"hello\"\n  ```\n\nExecute (Range covers only text lines):\n  1,2EvalBlock\n\n\nThen (Buffer is unchanged):\n  AssertEqual 'This is just text.', getline(1)\n  AssertEqual 'No code blocks here.', getline(2)\n  AssertEqual 'echo \"hello\"', getline(5)\n\nGiven markdown (Named target block):\n  <!-- target: output -->\n  ```sh\n  echo \"result\"\n  ```\n\n  <!-- name: output -->\n  ```\n  ```\n\nExecute (Range eval with named target):\n  %EvalBlock\n\n\nThen (Named target block populated with output):\n  AssertEqual 'result', getline(8)\n\nGiven markdown (Closing fences not treated as blocks):\n  ```sh\n  echo \"one\"\n  ```\n\n  ```sh\n  echo \"two\"\n  ```\n\nExecute (Range eval only evaluates opening fences):\n  %EvalBlock!\n\n\nThen (Both blocks evaluated correctly):\n  AssertEqual 'one', getline(2)\n  AssertEqual '```', getline(3)\n  AssertEqual 'two', getline(6)\n  AssertEqual '```', getline(7)\n\n\" === Range skips named output blocks ===\n\nGiven markdown (Range skips named blocks that are output destinations):\n  <!-- target: out -->\n  ```sh\n  echo \"source\"\n  ```\n\n  <!-- name: out -->\n  ```sh\n  old output\n  ```\n\nExecute (Range eval should only run the source block):\n  %EvalBlock\n\nThen (Source block output written to named block, named block not executed):\n  AssertEqual 'source', getline(8)\n"
  },
  {
    "path": "test/vimrc",
    "content": "set nocompatible\nfiletype off\nset runtimepath+=test/vader.vim\nset runtimepath+=.\nfiletype plugin indent on\nlet g:medieval_langs = ['sh']\nlet g:medieval_sync = 1\n"
  }
]