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 ```python print([x*x for x in range(5)]) ``` ``` ``` ```` If you run `:EvalBlock` in the first code block, the second block will become ````markdown ``` [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 Z! (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 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 = '' let s:optionpat = '^\s* " ``` " ``` 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) =~# '\ 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) =~# '\ 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 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: > ```python print([x*x for x in range(5)]) ``` ``` ``` < If you run |:EvalBlock| in the first code block, the second block will become > ``` [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: > ```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": > To write to a file called "squares.txt", use: > < 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 "$$"): > ```python print(r"\text{Hello LaTeX!}") ``` $$ $$ < *medieval-labels* By default, the block labels must be of the form " ```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, > ```python import numpy as np ``` ```python print(np.arange(1, 5)) ``` ``` ``` < Running |:EvalBlock| in the second code block produces: > ``` [1 2 3 4] ``` < Blocks can have recursive dependencies: > ```sh first_name="Gregory" ``` ```sh full_name="$first_name Anders" ``` ```sh echo "Hi, my name is $full_name" ``` < After running :EvalBlock in the block above... > ``` 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: > ```python import numpy as np ``` ```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: > < *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: > {{< 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*", 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 > 0 | \ call medieval#evalrange(, , 0 ? 'self' : ) | \ elseif 0 | \ call medieval#eval('self') | \ else | \ call medieval#eval() | \ endif nnoremap (medieval-eval) :call medieval#eval() ================================================ 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): ```sh echo "computed" ``` ``` ``` 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): ```sh echo "from source" ``` ``` 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" ``` ``` ``` 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): ```sh first_name="Gregory" ``` ```sh full_name="$first_name Anders" ``` ```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): ```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): ```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): ```sh echo "first" ``` ```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): ```sh echo "in range" ``` ```sh echo "also in range" ``` ```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): ```sh echo "result" ``` ``` ``` 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): ```sh echo "source" ``` ```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