From a60e299a3ce55b4a84a48b240d35c84e6e5a8746 Mon Sep 17 00:00:00 2001 From: Adam Stankiewicz Date: Tue, 31 Dec 2019 14:08:15 +0100 Subject: Switch back to plasticboy for markdown --- README.md | 2 +- after/ftplugin/markdown.vim | 205 ++++++++ autoload/markdown.vim | 532 --------------------- build | 2 +- ftdetect/polyglot.vim | 11 +- ftplugin/markdown.vim | 903 ++++++++++++++++++++++++++++------- indent/markdown.vim | 79 ++++ syntax/markdown.vim | 1085 +++++++------------------------------------ syntax/markdown_jekyll.vim | 34 -- 9 files changed, 1206 insertions(+), 1647 deletions(-) create mode 100644 after/ftplugin/markdown.vim delete mode 100644 autoload/markdown.vim create mode 100644 indent/markdown.vim delete mode 100644 syntax/markdown_jekyll.vim diff --git a/README.md b/README.md index e41ef2ad..52e2776b 100644 --- a/README.md +++ b/README.md @@ -119,7 +119,7 @@ If you need full functionality of any plugin, please use it directly with your p - [log](https://github.com/MTDL9/vim-log-highlighting) (syntax) - [lua](https://github.com/tbastos/vim-lua) (syntax, indent) - [mako](https://github.com/sophacles/vim-bundle-mako) (syntax, indent, ftplugin) -- [markdown](https://github.com/gabrielelana/vim-markdown) (syntax, autoload, ftplugin) +- [markdown](https://github.com/plasticboy/vim-markdown) (syntax, indent, ftplugin) - [mathematica](https://github.com/voldikss/vim-mma) (syntax, ftplugin) - [mdx](https://github.com/jxnblk/vim-mdx-js) (syntax) - [meson](https://github.com/mesonbuild/meson) (syntax, indent, ftplugin) diff --git a/after/ftplugin/markdown.vim b/after/ftplugin/markdown.vim new file mode 100644 index 00000000..1d1e410c --- /dev/null +++ b/after/ftplugin/markdown.vim @@ -0,0 +1,205 @@ +if !exists('g:polyglot_disabled') || index(g:polyglot_disabled, 'markdown') == -1 + +" vim: ts=4 sw=4: +" folding for Markdown headers, both styles (atx- and setex-) +" http://daringfireball.net/projects/markdown/syntax#header +" +" this code can be placed in file +" $HOME/.vim/after/ftplugin/markdown.vim +" +" original version from Steve Losh's gist: https://gist.github.com/1038710 + +function! s:is_mkdCode(lnum) + let name = synIDattr(synID(a:lnum, 1, 0), 'name') + return (name =~ '^mkd\%(Code$\|Snippet\)' || name != '' && name !~ '^\%(mkd\|html\)') +endfunction + +if get(g:, "vim_markdown_folding_style_pythonic", 0) + function! Foldexpr_markdown(lnum) + let l1 = getline(a:lnum) + "~~~~~ keep track of fenced code blocks ~~~~~ + "If we hit a code block fence + if l1 =~ '````*' || l1 =~ '\~\~\~\~*' + " toggle the variable that says if we're in a code block + if b:fenced_block == 0 + let b:fenced_block = 1 + elseif b:fenced_block == 1 + let b:fenced_block = 0 + endif + " else, if we're caring about front matter + elseif g:vim_markdown_frontmatter == 1 + " if we're in front matter and not on line 1 + if b:front_matter == 1 && a:lnum > 2 + let l0 = getline(a:lnum-1) + " if the previous line fenced front matter + if l0 == '---' + " we must not be in front matter + let b:front_matter = 0 + endif + " else, if we're on line one + elseif a:lnum == 1 + " if we hit a front matter fence + if l1 == '---' + " we're in the front matter + let b:front_matter = 1 + endif + endif + endif + + " if we're in a code block or front matter + if b:fenced_block == 1 || b:front_matter == 1 + if a:lnum == 1 + " fold any 'preamble' + return '>1' + else + " keep previous foldlevel + return '=' + endif + endif + + let l2 = getline(a:lnum+1) + " if the next line starts with two or more '=' + " and is not code + if l2 =~ '^==\+\s*' && !s:is_mkdCode(a:lnum+1) + " next line is underlined (level 1) + return '>0' + " else, if the nex line starts with two or more '-' + " and is not code + elseif l2 =~ '^--\+\s*' && !s:is_mkdCode(a:lnum+1) + " next line is underlined (level 2) + return '>1' + endif + + "if we're on a non-code line starting with a pound sign + if l1 =~ '^#' && !s:is_mkdCode(a:lnum) + " set the fold level to the number of hashes -1 + " return '>'.(matchend(l1, '^#\+') - 1) + " set the fold level to the number of hashes + return '>'.(matchend(l1, '^#\+')) + " else, if we're on line 1 + elseif a:lnum == 1 + " fold any 'preamble' + return '>1' + else + " keep previous foldlevel + return '=' + endif + endfunction + + function! Foldtext_markdown() + let line = getline(v:foldstart) + let has_numbers = &number || &relativenumber + let nucolwidth = &fdc + has_numbers * &numberwidth + let windowwidth = winwidth(0) - nucolwidth - 6 + let foldedlinecount = v:foldend - v:foldstart + let line = strpart(line, 0, windowwidth - 2 -len(foldedlinecount)) + let line = substitute(line, '\%("""\|''''''\)', '', '') + let fillcharcount = windowwidth - len(line) - len(foldedlinecount) + 1 + return line . ' ' . repeat("-", fillcharcount) . ' ' . foldedlinecount + endfunction +else " vim_markdown_folding_style_pythonic == 0 + function! Foldexpr_markdown(lnum) + if (a:lnum == 1) + let l0 = '' + else + let l0 = getline(a:lnum-1) + endif + + " keep track of fenced code blocks + if l0 =~ '````*' || l0 =~ '\~\~\~\~*' + if b:fenced_block == 0 + let b:fenced_block = 1 + elseif b:fenced_block == 1 + let b:fenced_block = 0 + endif + elseif g:vim_markdown_frontmatter == 1 + if b:front_matter == 1 + if l0 == '---' + let b:front_matter = 0 + endif + elseif a:lnum == 2 + if l0 == '---' + let b:front_matter = 1 + endif + endif + endif + + if b:fenced_block == 1 || b:front_matter == 1 + " keep previous foldlevel + return '=' + endif + + let l2 = getline(a:lnum+1) + if l2 =~ '^==\+\s*' && !s:is_mkdCode(a:lnum+1) + " next line is underlined (level 1) + return '>1' + elseif l2 =~ '^--\+\s*' && !s:is_mkdCode(a:lnum+1) + " next line is underlined (level 2) + if s:vim_markdown_folding_level >= 2 + return '>1' + else + return '>2' + endif + endif + + let l1 = getline(a:lnum) + if l1 =~ '^#' && !s:is_mkdCode(a:lnum) + " fold level according to option + if s:vim_markdown_folding_level == 1 || matchend(l1, '^#\+') > s:vim_markdown_folding_level + if a:lnum == line('$') + return matchend(l1, '^#\+') - 1 + else + return -1 + endif + else + " headers are not folded + return 0 + endif + endif + + if l0 =~ '^#' && !s:is_mkdCode(a:lnum-1) + " previous line starts with hashes + return '>'.matchend(l0, '^#\+') + else + " keep previous foldlevel + return '=' + endif + endfunction +endif + + +let b:fenced_block = 0 +let b:front_matter = 0 +let s:vim_markdown_folding_level = get(g:, "vim_markdown_folding_level", 1) + +function! s:MarkdownSetupFolding() + if !get(g:, "vim_markdown_folding_disabled", 0) + if get(g:, "vim_markdown_folding_style_pythonic", 0) + if get(g:, "vim_markdown_override_foldtext", 1) + setlocal foldtext=Foldtext_markdown() + endif + endif + setlocal foldexpr=Foldexpr_markdown(v:lnum) + setlocal foldmethod=expr + endif +endfunction + +function! s:MarkdownSetupFoldLevel() + if get(g:, "vim_markdown_folding_style_pythonic", 0) + " set default foldlevel + execute "setlocal foldlevel=".s:vim_markdown_folding_level + endif +endfunction + +call s:MarkdownSetupFoldLevel() +call s:MarkdownSetupFolding() + +augroup Mkd + " These autocmds need to be kept in sync with the autocmds calling + " s:MarkdownRefreshSyntax in ftplugin/markdown.vim. + autocmd BufWinEnter,BufWritePost call s:MarkdownSetupFolding() + autocmd InsertEnter,InsertLeave call s:MarkdownSetupFolding() + autocmd CursorHold,CursorHoldI call s:MarkdownSetupFolding() +augroup END + +endif diff --git a/autoload/markdown.vim b/autoload/markdown.vim deleted file mode 100644 index 3db667e1..00000000 --- a/autoload/markdown.vim +++ /dev/null @@ -1,532 +0,0 @@ -if !exists('g:polyglot_disabled') || index(g:polyglot_disabled, 'markdown') == -1 - - -" {{{ FOLDING - -function! markdown#FoldLevelOfLine(lnum) - let currentline = getline(a:lnum) - let nextline = getline(a:lnum + 1) - - " an empty line is not going to change the indentation level - if match(currentline, '^\s*$') >= 0 - return '=' - endif - - " folding lists - if s:SyntaxGroupOfLineIs(a:lnum, '^markdownListItem') - if s:SyntaxGroupOfLineIs(a:lnum - 1, '^markdownListItem') - return 'a1' - endif - if s:SyntaxGroupOfLineIs(a:lnum + 1, '^markdownListItem') - return 's1' - endif - return '=' - endif - - " we are not going to fold things inside list items, too hairy - let is_inside_a_list_item = s:SyntaxGroupOfLineIs(a:lnum, '^markdownListItem') - if is_inside_a_list_item - return '=' - endif - - " folding atx headers - if match(currentline, '^#\{1,6}\s') >= 0 - let header_level = strlen(substitute(currentline, '^\(#\{1,6}\).*', '\1', '')) - return '>' . header_level - endif - - " folding fenced code blocks - let next_line_syntax_group = synIDattr(synID(a:lnum + 1, 1, 1), 'name') - if match(currentline, '^\s*```') >= 0 - if next_line_syntax_group ==# 'markdownFencedCodeBlock' - return 'a1' - endif - return 's1' - endif - - " folding code blocks - let current_line_syntax_group = synIDattr(synID(a:lnum, 1, 1), 'name') - let prev_line_syntax_group = synIDattr(synID(a:lnum - 1, 1, 1), 'name') - if match(currentline, '^\s\{4,}') >= 0 - if current_line_syntax_group ==# 'markdownCodeBlock' - if prev_line_syntax_group !=# 'markdownCodeBlock' - return 'a1' - endif - if next_line_syntax_group !=# 'markdownCodeBlock' - return 's1' - endif - endif - return '=' - endif - - " folding setex headers - if (match(currentline, '^.*$') >= 0) - if (match(nextline, '^=\+$') >= 0) - return '>1' - endif - if (match(nextline, '^-\+$') >= 0) - return '>2' - endif - endif - - return '=' -endfunction - -function! s:SyntaxGroupOfLineIs(lnum, pattern) - let stack = synstack(a:lnum, a:cnum) - if len(stack) > 0 - return synIDattr(stack[0], 'name') =~# a:pattern - endif - return 0 -endfunction - -" }}} - -" {{{ EDIT - -function! markdown#EditBlock() range abort - if exists('b:markdown_temporary_buffer') && b:markdown_temporary_buffer - echo 'Sorry, you cannot edit a code block inside a temporary buffer' - return - endif - " Github fenced code blocks like ```ruby - let code_block = s:LocateFencedCodeBlock(a:firstline, - \ '^\s*`\{3,}\(\w\+\)\%(\s.*$\|$\)', - \ '^\s*`\{3,}\s*$' - \ ) - if code_block['from'] == 0 || code_block['to'] == 0 - " Github fenced code blocks with metadata like ```{ruby, } - let code_block = s:LocateFencedCodeBlock(a:firstline, - \ '^\s*`\{3,}{\(\w\+\),[^}]\+}\%(\s.*$\|$\)', - \ '^\s*`\{3,}\s*$' - \ ) - endif - if code_block['from'] == 0 || code_block['to'] == 0 - " Github fenced code blocks alternate style like ~~~ruby - let code_block = s:LocateFencedCodeBlock(a:firstline, - \ '^\s*\~\{3,}\(\w\+\)\%(\s.*$\|$\)', - \ '^\s*\~\{3,}\s*$' - \ ) - endif - if code_block['from'] == 0 || code_block['to'] == 0 - " Liquid fenced code blocks {% highlight ruby %} - " (since we support some liquid/jekyll tags, why not?) - let code_block = s:LocateFencedCodeBlock(a:firstline, - \ '^\s*{%\s*highlight\s\+\(\w\+\)\s*%}\%(\s.*$\|$\)', - \ '^\s*{%\s*endhighlight\s*%}\%(\s.*$\|$\)' - \ ) - endif - if code_block['from'] == 0 || code_block['to'] == 0 - let code_block = s:LocateRangeBlock(a:firstline, a:lastline) - endif - if code_block['from'] == 0 || code_block['to'] == 0 - echo 'Sorry, I did not find any suitable code block to edit or create' - return - endif - - let code_block['file_extension'] = '.' . code_block['language'] - if has_key(s:known_file_extensions, code_block['language']) - let code_block['file_extension'] = s:known_file_extensions[code_block['language']] - endif - let code_block['file_path'] = tempname() . code_block['file_extension'] - let code_block['content'] = getline(code_block['from'], code_block['to']) - let code_block['content'] = s:UnindentBlock(code_block['content'], code_block['indentation']) - - call writefile(code_block['content'], code_block['file_path']) - augroup MarkdownReplaceEditedBlock - autocmd BufEnter call s:ReplaceEditedBlock() - augroup END - - let b:code_block = code_block - execute 'split ' . code_block['file_path'] - let b:markdown_temporary_buffer = 1 - autocmd BufLeave wq -endfunction - -function! s:ReplaceEditedBlock() - augroup MarkdownReplaceEditedBlock - autocmd! - augroup END - augroup! MarkdownReplaceEditedBlock - - if b:code_block['to'] - b:code_block['from'] >= 0 - execute b:code_block['from'] . ',' b:code_block['to'] . ' delete _' - endif - let content = readfile(b:code_block['file_path']) - let content = s:IndentBlock(l:content, b:code_block['indentation']) - let content = s:SurroundWithFencedCodeBlock(l:content, b:code_block) - call append(b:code_block['from']-1, content) - call setpos('.', b:code_block['back_to_position']) - - execute 'silent bwipeout! ' . b:code_block['file_path'] - call delete(b:code_block['file_path']) - unlet! b:code_block -endfunction - -function! s:UnindentBlock(content, indentation) - return map(a:content, 'substitute(v:val, ''^' . a:indentation . ''', '''', ''g'')') -endfunction - -function! s:IndentBlock(content, indentation) - return map(a:content, 'substitute(v:val, ''^'', ''' . a:indentation . ''', ''g'')') -endfunction - -function! s:SurroundWithFencedCodeBlock(code, editing) - if !a:editing['surround'] | return a:code | endif - if a:editing['language'] =~# 'markdown' | return a:code | endif - let before = - \ (a:editing['make_room_before'] ? [''] : []) + - \ [a:editing['indentation'] . '```' . a:editing['language']] - let after = - \ [a:editing['indentation'] . '```'] + - \ (a:editing['make_room_after'] ? [''] : []) - return l:before + a:code + l:after -endfunction - -function! s:LocateRangeBlock(from, to) - " TODO: extract initialize_code_block - let code_block = {'from': 0, 'to': 0, 'language': 'txt', 'indentation': '', 'surround': 0} - if a:to >= a:from - let code_block['from'] = a:from - let code_block['to'] = a:to - let code_block['back_to_position'] = getpos('.') - let code_block['language'] = 'markdown' - - if a:from == a:to && getline(a:from) =~ '^\s*$' - let code_block['surround'] = 1 - let code_block['make_room_before'] = getline(a:from - 1) !~ '^\s*$' - let code_block['make_room_after'] = getline(a:to + 1) !~ '^\s*$' - let code_block['language'] = input('filetype? (default: markdown) ', '', 'filetype') - if code_block['language'] =~ '^\s*$' - let code_block['language'] = 'markdown' - endif - endif - endif - return code_block -endfunction - -function! s:LocateFencedCodeBlock(starting_from, upper_delimiter, lower_delimiter) - " TODO: extract initialize_code_block - let code_block = {'from': 0, 'to': 0, 'language': 'txt', 'indentation': '', 'surround': 0} - let initial_position = getpos('.') - let search_position = copy(initial_position) - let search_position[1] = a:starting_from - let search_position[2] = 0 - cal setpos('.', search_position) - - let start_code_block_backward = search(a:upper_delimiter, 'cbnW') - let end_code_block_backward = search(a:lower_delimiter, 'cbnW') - let end_code_block_forward = search(a:lower_delimiter, 'cnW') - - let found_code_block = - \ start_code_block_backward > 0 && - \ end_code_block_forward > 0 - let between_two_code_blocks = - \ start_code_block_backward < end_code_block_backward && - \ end_code_block_backward <= a:starting_from - let starting_inside_code_block = - \ found_code_block && - \ !between_two_code_blocks && - \ start_code_block_backward <= a:starting_from && - \ end_code_block_forward >= a:starting_from - - if starting_inside_code_block - let code_block['language'] = s:ExtractLanguage(start_code_block_backward, a:upper_delimiter) - let code_block['indentation'] = s:ExtractIndentation(start_code_block_backward) - let code_block['back_to_position'] = initial_position - let code_block['back_to_position'][1] = start_code_block_backward - let code_block['back_to_position'][2] = 0 - let code_block['from'] = start_code_block_backward + 1 - let code_block['to'] = end_code_block_forward - 1 - endif - return code_block -endfunction - -function! s:ExtractLanguage(start_at, upper_delimiter) - return substitute(getline(a:start_at), a:upper_delimiter, '\1', '') -endfunction - -function! s:ExtractIndentation(start_at) - return substitute(getline(a:start_at), '\(^\s*\).\+$', '\1', '') -endfunction - -let s:known_file_extensions = { - \ 'abap': '.abap', - \ 'antlr': '.g4', - \ 'asp': '.asp', - \ 'ats': '.dats', - \ 'actionscript': '.as', - \ 'ada': '.adb', - \ 'agda': '.agda', - \ 'apacheconf': '.apacheconf', - \ 'apex': '.cls', - \ 'applescript': '.applescript', - \ 'arc': '.arc', - \ 'arduino': '.ino', - \ 'asciidoc': '.asciidoc', - \ 'assembly': '.asm', - \ 'augeas': '.aug', - \ 'autohotkey': '.ahk', - \ 'autoit': '.au3', - \ 'awk': '.awk', - \ 'batchfile': '.bat', - \ 'befunge': '.befunge', - \ 'blitzbasic': '.bb', - \ 'blitzmax': '.bmx', - \ 'bluespec': '.bsv', - \ 'boo': '.boo', - \ 'brainfuck': '.b', - \ 'brightscript': '.brs', - \ 'bro': '.bro', - \ 'c': '.c', - \ 'c++': '.cpp', - \ 'cpp': '.cpp', - \ 'clips': '.clp', - \ 'cmake': '.cmake', - \ 'cobol': '.cob', - \ 'css': '.css', - \ 'ceylon': '.ceylon', - \ 'chuck': '.ck', - \ 'cirru': '.cirru', - \ 'clean': '.icl', - \ 'clojure': '.clj', - \ 'coffeescript': '.coffee', - \ 'coldfusion': '.cfm', - \ 'coq': '.coq', - \ 'creole': '.creole', - \ 'crystal': '.cr', - \ 'cucumber': '.feature', - \ 'cuda': '.cu', - \ 'cython': '.pyx', - \ 'd': '.d', - \ 'dm': '.dm', - \ 'dot': '.dot', - \ 'dart': '.dart', - \ 'diff': '.diff', - \ 'dylan': '.dylan', - \ 'ecl': '.ecl', - \ 'eiffel': '.e', - \ 'elixir': '.ex', - \ 'elm': '.elm', - \ 'erlang': '.erl', - \ 'flux': '.fx', - \ 'fortran': '.f90', - \ 'factor': '.factor', - \ 'fancy': '.fy', - \ 'fantom': '.fan', - \ 'forth': '.fth', - \ 'gas': '.s', - \ 'glsl': '.glsl', - \ 'genshi': '.kid', - \ 'glyph': '.glf', - \ 'go': '.go', - \ 'gosu': '.gs', - \ 'groff': '.man', - \ 'groovy': '.groovy', - \ 'html': '.html', - \ 'http': '.http', - \ 'haml': '.haml', - \ 'handlebars': '.handlebars', - \ 'harbour': '.hb', - \ 'haskell': '.hs', - \ 'haxe': '.hx', - \ 'hy': '.hy', - \ 'idl': '.pro', - \ 'ini': '.ini', - \ 'idris': '.idr', - \ 'io': '.io', - \ 'ioke': '.ik', - \ 'j': '.ijs', - \ 'json': '.json', - \ 'json5': '.json5', - \ 'jsonld': '.jsonld', - \ 'jade': '.jade', - \ 'java': '.java', - \ 'javascript': '.js', - \ 'julia': '.jl', - \ 'krl': '.krl', - \ 'kotlin': '.kt', - \ 'lfe': '.lfe', - \ 'llvm': '.ll', - \ 'lasso': '.lasso', - \ 'less': '.less', - \ 'lilypond': '.ly', - \ 'livescript': '.ls', - \ 'logos': '.xm', - \ 'logtalk': '.lgt', - \ 'lua': '.lua', - \ 'm': '.mumps', - \ 'makefile': '.mak', - \ 'mako': '.mako', - \ 'markdown': '.md', - \ 'mask': '.mask', - \ 'matlab': '.matlab', - \ 'max': '.maxpat', - \ 'mediawiki': '.mediawiki', - \ 'mirah': '.druby', - \ 'monkey': '.monkey', - \ 'moocode': '.moo', - \ 'moonscript': '.moon', - \ 'myghty': '.myt', - \ 'nsis': '.nsi', - \ 'nemerle': '.n', - \ 'netlogo': '.nlogo', - \ 'nginx': '.nginxconf', - \ 'nimrod': '.nim', - \ 'nu': '.nu', - \ 'numpy': '.numpy', - \ 'ocaml': '.ml', - \ 'objdump': '.objdump', - \ 'omgrofl': '.omgrofl', - \ 'opa': '.opa', - \ 'opencl': '.cl', - \ 'org': '.org', - \ 'oxygene': '.oxygene', - \ 'pawn': '.pwn', - \ 'php': '.php', - \ 'parrot': '.parrot', - \ 'pascal': '.pas', - \ 'perl': '.pl', - \ 'perl6': '.p6', - \ 'pike': '.pike', - \ 'pod': '.pod', - \ 'pogoscript': '.pogo', - \ 'postscript': '.ps', - \ 'powershell': '.ps1', - \ 'processing': '.pde', - \ 'prolog': '.prolog', - \ 'puppet': '.pp', - \ 'python': '.py', - \ 'qml': '.qml', - \ 'r': '.r', - \ 'rdoc': '.rdoc', - \ 'realbasic': '.rbbas', - \ 'rhtml': '.rhtml', - \ 'rmarkdown': '.rmd', - \ 'racket': '.rkt', - \ 'rebol': '.rebol', - \ 'redcode': '.cw', - \ 'robotframework': '.robot', - \ 'rouge': '.rg', - \ 'ruby': '.rb', - \ 'rust': '.rs', - \ 'scss': '.scss', - \ 'sql': '.sql', - \ 'sage': '.sage', - \ 'sass': '.sass', - \ 'scala': '.scala', - \ 'scaml': '.scaml', - \ 'scheme': '.scm', - \ 'scilab': '.sci', - \ 'self': '.self', - \ 'shell': '.sh', - \ 'shen': '.shen', - \ 'slash': '.sl', - \ 'smalltalk': '.st', - \ 'smarty': '.tpl', - \ 'squirrel': '.nut', - \ 'stylus': '.styl', - \ 'supercollider': '.scd', - \ 'toml': '.toml', - \ 'txl': '.txl', - \ 'tcl': '.tcl', - \ 'tcsh': '.tcsh', - \ 'tex': '.tex', - \ 'tea': '.tea', - \ 'textile': '.textile', - \ 'turing': '.t', - \ 'twig': '.twig', - \ 'typescript': '.ts', - \ 'unrealscript': '.uc', - \ 'vhdl': '.vhdl', - \ 'vala': '.vala', - \ 'verilog': '.v', - \ 'viml': '.vim', - \ 'volt': '.volt', - \ 'xc': '.xc', - \ 'xml': '.xml', - \ 'xproc': '.xpl', - \ 'xquery': '.xquery', - \ 'xs': '.xs', - \ 'xslt': '.xslt', - \ 'xtend': '.xtend', - \ 'yaml': '.yml', - \ 'ec': '.ec', - \ 'edn': '.edn', - \ 'fish': '.fish', - \ 'mupad': '.mu', - \ 'nesc': '.nc', - \ 'ooc': '.ooc', - \ 'restructuredtext': '.rst', - \ 'wisp': '.wisp', - \ 'xbase': '.prg', -\ } - -" }}} - -" {{{ FORMAT -function! markdown#FormatTable() - let p = '^\s*|\s.*\s|\s*$' - if exists(':Tabularize') && getline('.') =~# '^\s*|' && (getline(line('.')-1) =~# p || getline(line('.')+1) =~# p) - let column = strlen(substitute(getline('.')[0:col('.')],'[^|]','','g')) - let position = strlen(matchstr(getline('.')[0:col('.')],'.*|\s*\zs.*')) - let separator_line_number = search('^\s*|\s*-\{3,}', 'cbnW') - - call s:ShrinkTableHeaderSeparator(separator_line_number) - Tabularize/|/l1 - call s:ExpandTableHeaderSeparator(separator_line_number) - normal! 0 - - call search(repeat('[^|]*|',column).'\s\{-\}'.repeat('.',position),'ce',line('.')) - endif -endfunction - -function! s:ShrinkTableHeaderSeparator(separator_line_number) - if a:separator_line_number > 0 - let separator_line = getline(a:separator_line_number) - let separator_line = substitute(separator_line, '-\+', '---', 'g') - call setline(a:separator_line_number, separator_line) - endif -endfunction - -function! s:ExpandTableHeaderSeparator(separator_line_number) - if a:separator_line_number > 0 - let separator_line = getline(a:separator_line_number) - let separator_line = substitute( - \ separator_line, - \ '|\([^|]*\)', - \ '\="| " . repeat("-", strlen(submatch(1)) - 2) . " "', - \ 'g') - let separator_line = substitute(separator_line, '\s*$', '', '') - call setline(a:separator_line_number, separator_line) - endif -endfunction -" }}} - -" {{{ SWITCH STATUS -function! markdown#SwitchStatus() - let current_line = getline('.') - if match(current_line, '^\s*[*\-+] \[ \]') >= 0 - call setline('.', substitute(current_line, '^\(\s*[*\-+]\) \[ \]', '\1 [x]', '')) - return - endif - if match(current_line, '^\s*[*\-+] \[x\]') >= 0 - call setline('.', substitute(current_line, '^\(\s*[*\-+]\) \[x\]', '\1', '')) - return - endif - if match(current_line, '^\s*[*\-+] \(\[[x ]\]\)\@!') >= 0 - call setline('.', substitute(current_line, '^\(\s*[*\-+]\)', '\1 [ ]', '')) - return - endif - if match(current_line, '^\s*#\{1,5}\s') >= 0 - call setline('.', substitute(current_line, '^\(\s*#\{1,5}\) \(.*$\)', '\1# \2', '')) - return - endif - if match(current_line, '^\s*#\{6}\s') >= 0 - call setline('.', substitute(current_line, '^\(\s*\)#\{6} \(.*$\)', '\1# \2', '')) - return - endif -endfunction -" }}} - -endif diff --git a/build b/build index 67ad3c50..7e4b5221 100755 --- a/build +++ b/build @@ -233,7 +233,7 @@ PACKS=" log:MTDL9/vim-log-highlighting lua:tbastos/vim-lua mako:sophacles/vim-bundle-mako - markdown:gabrielelana/vim-markdown + markdown:plasticboy/vim-markdown mathematica:voldikss/vim-mma mdx:jxnblk/vim-mdx-js meson:mesonbuild/meson:_ALL:/data/syntax-highlighting/vim/ diff --git a/ftdetect/polyglot.vim b/ftdetect/polyglot.vim index e2452071..cd389fdd 100644 --- a/ftdetect/polyglot.vim +++ b/ftdetect/polyglot.vim @@ -816,8 +816,15 @@ endif if !exists('g:polyglot_disabled') || index(g:polyglot_disabled, 'markdown') == -1 augroup filetypedetect - " markdown, from markdown.vim in gabrielelana/vim-markdown -au BufRead,BufNewFile *.{md,mdown,mkd,mkdn,markdown,mdwn} set filetype=markdown + " markdown, from markdown.vim in plasticboy/vim-markdown +if !has('patch-7.4.480') + " Before this patch, vim used modula2 for .md. + au! filetypedetect BufRead,BufNewFile *.md +endif + +" markdown filetype file +au BufRead,BufNewFile *.{md,mdown,mkd,mkdn,markdown,mdwn} setfiletype markdown +au BufRead,BufNewFile *.{md,mdown,mkd,mkdn,markdown,mdwn}.{des3,des,bf,bfa,aes,idea,cast,rc2,rc4,rc5,desx} setfiletype markdown augroup end endif diff --git a/ftplugin/markdown.vim b/ftplugin/markdown.vim index 8d327f61..43c9773a 100644 --- a/ftplugin/markdown.vim +++ b/ftplugin/markdown.vim @@ -1,202 +1,795 @@ if !exists('g:polyglot_disabled') || index(g:polyglot_disabled, 'markdown') == -1 -if exists('b:did_ftplugin') | finish | endif - -" {{{ CONFIGURATION - -if !exists('g:markdown_flavor') - let g:markdown_flavor = 'github' -endif - -if !exists('g:markdown_enable_folding') - let g:markdown_enable_folding = 0 -endif - -if !exists('g:markdown_enable_mappings') - " make it compatible with previous configuration value - if exists('g:markdown_include_default_mappings') - let g:markdown_enable_mappings = g:markdown_include_default_mappings - else - let g:markdown_enable_mappings = 1 - endif -endif - -if !exists('g:markdown_enable_insert_mode_mappings') - " make it compatible with previous configuration value - if exists('g:markdown_include_insert_mode_default_mappings') - let g:markdown_enable_insert_mode_mappings = g:markdown_include_insert_mode_default_mappings - else - let g:markdown_enable_insert_mode_mappings = 1 - endif -endif +"TODO print messages when on visual mode. I only see VISUAL, not the messages. + +" Function interface phylosophy: +" +" - functions take arbitrary line numbers as parameters. +" Current cursor line is only a suitable default parameter. +" +" - only functions that bind directly to user actions: +" +" - print error messages. +" All intermediate functions limit themselves return `0` to indicate an error. +" +" - move the cursor. All other functions do not move the cursor. +" +" This is how you should view headers for the header mappings: +" +" |BUFFER +" | +" |Outside any header +" | +" a-+# a +" | +" |Inside a +" | +" a-+ +" b-+## b +" | +" |inside b +" | +" b-+ +" c-+### c +" | +" |Inside c +" | +" c-+ +" d-|# d +" | +" |Inside d +" | +" d-+ +" e-|e +" |==== +" | +" |Inside e +" | +" e-+ + +" For each level, contains the regexp that matches at that level only. +" +let s:levelRegexpDict = { + \ 1: '\v^(#[^#]@=|.+\n\=+$)', + \ 2: '\v^(##[^#]@=|.+\n-+$)', + \ 3: '\v^###[^#]@=', + \ 4: '\v^####[^#]@=', + \ 5: '\v^#####[^#]@=', + \ 6: '\v^######[^#]@=' +\ } + +" Maches any header level of any type. +" +" This could be deduced from `s:levelRegexpDict`, but it is more +" efficient to have a single regexp for this. +" +let s:headersRegexp = '\v^(#|.+\n(\=+|-+)$)' + +" Returns the line number of the first header before `line`, called the +" current header. +" +" If there is no current header, return `0`. +" +" @param a:1 The line to look the header of. Default value: `getpos('.')`. +" +function! s:GetHeaderLineNum(...) + if a:0 == 0 + let l:l = line('.') + else + let l:l = a:1 + endif + while(l:l > 0) + if join(getline(l:l, l:l + 1), "\n") =~ s:headersRegexp + return l:l + endif + let l:l -= 1 + endwhile + return 0 +endfunction -if !exists('g:markdown_enable_insert_mode_leader_mappings') - let g:markdown_enable_insert_mode_leader_mappings = 0 -endif +" - if inside a header goes to it. +" Return its line number. +" +" - if on top level outside any headers, +" print a warning +" Return `0`. +" +function! s:MoveToCurHeader() + let l:lineNum = s:GetHeaderLineNum() + if l:lineNum != 0 + call cursor(l:lineNum, 1) + else + echo 'outside any header' + "normal! gg + endif + return l:lineNum +endfunction -if !exists('g:markdown_drop_empty_blockquotes') - let g:markdown_drop_empty_blockquotes = 0 -endif +" Move cursor to next header of any level. +" +" If there are no more headers, print a warning. +" +function! s:MoveToNextHeader() + if search(s:headersRegexp, 'W') == 0 + "normal! G + echo 'no next header' + endif +endfunction -if !exists('g:markdown_mapping_switch_status') - let g:markdown_mapping_switch_status = '' -endif +" Move cursor to previous header (before current) of any level. +" +" If it does not exist, print a warning. +" +function! s:MoveToPreviousHeader() + let l:curHeaderLineNumber = s:GetHeaderLineNum() + let l:noPreviousHeader = 0 + if l:curHeaderLineNumber <= 1 + let l:noPreviousHeader = 1 + else + let l:previousHeaderLineNumber = s:GetHeaderLineNum(l:curHeaderLineNumber - 1) + if l:previousHeaderLineNumber == 0 + let l:noPreviousHeader = 1 + else + call cursor(l:previousHeaderLineNumber, 1) + endif + endif + if l:noPreviousHeader + echo 'no previous header' + endif +endfunction -if !exists('g:markdown_enable_spell_checking') - let g:markdown_enable_spell_checking = 1 -endif +" - if line is inside a header, return the header level (h1 -> 1, h2 -> 2, etc.). +" +" - if line is at top level outside any headers, return `0`. +" +function! s:GetHeaderLevel(...) + if a:0 == 0 + let l:line = line('.') + else + let l:line = a:1 + endif + let l:linenum = s:GetHeaderLineNum(l:line) + if l:linenum != 0 + return s:GetLevelOfHeaderAtLine(l:linenum) + else + return 0 + endif +endfunction -if !exists('g:markdown_enable_input_abbreviations') - let g:markdown_enable_input_abbreviations = 1 -endif +" Returns the level of the header at the given line. +" +" If there is no header at the given line, returns `0`. +" +function! s:GetLevelOfHeaderAtLine(linenum) + let l:lines = join(getline(a:linenum, a:linenum + 1), "\n") + for l:key in keys(s:levelRegexpDict) + if l:lines =~ get(s:levelRegexpDict, l:key) + return l:key + endif + endfor + return 0 +endfunction -" }}} +" Move cursor to parent header of the current header. +" +" If it does not exit, print a warning and do nothing. +" +function! s:MoveToParentHeader() + let l:linenum = s:GetParentHeaderLineNumber() + if l:linenum != 0 + call cursor(l:linenum, 1) + else + echo 'no parent header' + endif +endfunction +" Return the line number of the parent header of line `line`. +" +" If it has no parent, return `0`. +" +function! s:GetParentHeaderLineNumber(...) + if a:0 == 0 + let l:line = line('.') + else + let l:line = a:1 + endif + let l:level = s:GetHeaderLevel(l:line) + if l:level > 1 + let l:linenum = s:GetPreviousHeaderLineNumberAtLevel(l:level - 1, l:line) + return l:linenum + endif + return 0 +endfunction -" {{{ OPTIONS +" Return the line number of the previous header of given level. +" in relation to line `a:1`. If not given, `a:1 = getline()` +" +" `a:1` line is included, and this may return the current header. +" +" If none return 0. +" +function! s:GetNextHeaderLineNumberAtLevel(level, ...) + if a:0 < 1 + let l:line = line('.') + else + let l:line = a:1 + endif + let l:l = l:line + while(l:l <= line('$')) + if join(getline(l:l, l:l + 1), "\n") =~ get(s:levelRegexpDict, a:level) + return l:l + endif + let l:l += 1 + endwhile + return 0 +endfunction -setlocal textwidth=0 -setlocal ts=2 sw=2 expandtab smarttab -setlocal comments=b:*,b:-,b:+,n:>,se:``` commentstring=>\ %s -setlocal formatoptions=tron -setlocal formatlistpat=^\\s*\\d\\+\\.\\s\\+\\\\|^\\s*[+-\\*]\\s\\+ -setlocal nolisp -setlocal autoindent +" Return the line number of the previous header of given level. +" in relation to line `a:1`. If not given, `a:1 = getline()` +" +" `a:1` line is included, and this may return the current header. +" +" If none return 0. +" +function! s:GetPreviousHeaderLineNumberAtLevel(level, ...) + if a:0 == 0 + let l:line = line('.') + else + let l:line = a:1 + endif + let l:l = l:line + while(l:l > 0) + if join(getline(l:l, l:l + 1), "\n") =~ get(s:levelRegexpDict, a:level) + return l:l + endif + let l:l -= 1 + endwhile + return 0 +endfunction -" Enable spelling and completion based on dictionary words -if &spelllang !~# '^\s*$' && g:markdown_enable_spell_checking - setlocal spell -endif +" Move cursor to next sibling header. +" +" If there is no next siblings, print a warning and don't move. +" +function! s:MoveToNextSiblingHeader() + let l:curHeaderLineNumber = s:GetHeaderLineNum() + let l:curHeaderLevel = s:GetLevelOfHeaderAtLine(l:curHeaderLineNumber) + let l:curHeaderParentLineNumber = s:GetParentHeaderLineNumber() + let l:nextHeaderSameLevelLineNumber = s:GetNextHeaderLineNumberAtLevel(l:curHeaderLevel, l:curHeaderLineNumber + 1) + let l:noNextSibling = 0 + if l:nextHeaderSameLevelLineNumber == 0 + let l:noNextSibling = 1 + else + let l:nextHeaderSameLevelParentLineNumber = s:GetParentHeaderLineNumber(l:nextHeaderSameLevelLineNumber) + if l:curHeaderParentLineNumber == l:nextHeaderSameLevelParentLineNumber + call cursor(l:nextHeaderSameLevelLineNumber, 1) + else + let l:noNextSibling = 1 + endif + endif + if l:noNextSibling + echo 'no next sibling header' + endif +endfunction -" Custom dictionary for emoji -execute 'setlocal dictionary+=' . shellescape(expand(':p:h:h')) . '/dict/emoticons.dict' -setlocal iskeyword+=:,+,- -setlocal complete+=k - -if g:markdown_enable_input_abbreviations - " Replace common ascii emoticons with supported emoji - iabbrev :-) :smile: - iabbrev :-D :laughing: - iabbrev :-( :disappointed: - - " Replace common punctuation - iabbrev ... … - iabbrev << « - iabbrev >> » -endif +" Move cursor to previous sibling header. +" +" If there is no previous siblings, print a warning and do nothing. +" +function! s:MoveToPreviousSiblingHeader() + let l:curHeaderLineNumber = s:GetHeaderLineNum() + let l:curHeaderLevel = s:GetLevelOfHeaderAtLine(l:curHeaderLineNumber) + let l:curHeaderParentLineNumber = s:GetParentHeaderLineNumber() + let l:previousHeaderSameLevelLineNumber = s:GetPreviousHeaderLineNumberAtLevel(l:curHeaderLevel, l:curHeaderLineNumber - 1) + let l:noPreviousSibling = 0 + if l:previousHeaderSameLevelLineNumber == 0 + let l:noPreviousSibling = 1 + else + let l:previousHeaderSameLevelParentLineNumber = s:GetParentHeaderLineNumber(l:previousHeaderSameLevelLineNumber) + if l:curHeaderParentLineNumber == l:previousHeaderSameLevelParentLineNumber + call cursor(l:previousHeaderSameLevelLineNumber, 1) + else + let l:noPreviousSibling = 1 + endif + endif + if l:noPreviousSibling + echo 'no previous sibling header' + endif +endfunction -" Folding -if g:markdown_enable_folding - setlocal foldmethod=expr - setlocal foldexpr=markdown#FoldLevelOfLine(v:lnum) -endif +function! s:Toc(...) + if a:0 > 0 + let l:window_type = a:1 + else + let l:window_type = 'vertical' + endif -" }}} + let l:bufnr = bufnr('%') + let l:cursor_line = line('.') + let l:cursor_header = 0 + let l:fenced_block = 0 + let l:front_matter = 0 + let l:header_list = [] + let l:header_max_len = 0 + let l:vim_markdown_toc_autofit = get(g:, "vim_markdown_toc_autofit", 0) + let l:vim_markdown_frontmatter = get(g:, "vim_markdown_frontmatter", 0) + for i in range(1, line('$')) + let l:lineraw = getline(i) + let l:l1 = getline(i+1) + let l:line = substitute(l:lineraw, "#", "\\\#", "g") + if l:line =~ '````*' || l:line =~ '\~\~\~\~*' + if l:fenced_block == 0 + let l:fenced_block = 1 + elseif l:fenced_block == 1 + let l:fenced_block = 0 + endif + elseif l:vim_markdown_frontmatter == 1 + if l:front_matter == 1 + if l:line == '---' + let l:front_matter = 0 + endif + elseif i == 1 + if l:line == '---' + let l:front_matter = 1 + endif + endif + endif + if l:line =~ '^#\+' || (l:l1 =~ '^=\+\s*$' || l:l1 =~ '^-\+\s*$') && l:line =~ '^\S' + let l:is_header = 1 + else + let l:is_header = 0 + endif + if l:is_header == 1 && l:fenced_block == 0 && l:front_matter == 0 + " append line to location list + let l:item = {'lnum': i, 'text': l:line, 'valid': 1, 'bufnr': l:bufnr, 'col': 1} + let l:header_list = l:header_list + [l:item] + " set header number of the cursor position + if l:cursor_header == 0 + if i == l:cursor_line + let l:cursor_header = len(l:header_list) + elseif i > l:cursor_line + let l:cursor_header = len(l:header_list) - 1 + endif + endif + " keep track of the longest header size (heading level + title) + let l:total_len = stridx(l:line, ' ') + strdisplaywidth(l:line) + if l:total_len > l:header_max_len + let l:header_max_len = l:total_len + endif + endif + endfor + call setloclist(0, l:header_list) + if len(l:header_list) == 0 + echom "Toc: No headers." + return + endif -" {{{ FUNCTIONS + if l:window_type ==# 'horizontal' + lopen + elseif l:window_type ==# 'vertical' + vertical lopen + " auto-fit toc window when possible to shrink it + if (&columns/2) > l:header_max_len && l:vim_markdown_toc_autofit == 1 + execute 'vertical resize ' . (l:header_max_len + 1) + else + execute 'vertical resize ' . (&columns/2) + endif + elseif l:window_type ==# 'tab' + tab lopen + else + lopen + endif + setlocal modifiable + for i in range(1, line('$')) + " this is the location-list data for the current item + let d = getloclist(0)[i-1] + " atx headers + if match(d.text, "^#") > -1 + let l:level = len(matchstr(d.text, '#*', 'g'))-1 + let d.text = substitute(d.text, '\v^#*[ ]*', '', '') + let d.text = substitute(d.text, '\v[ ]*#*$', '', '') + " setex headers + else + let l:next_line = getbufline(d.bufnr, d.lnum+1) + if match(l:next_line, "=") > -1 + let l:level = 0 + elseif match(l:next_line, "-") > -1 + let l:level = 1 + endif + endif + call setline(i, repeat(' ', l:level). d.text) + endfor + setlocal nomodified + setlocal nomodifiable + execute 'normal! ' . l:cursor_header . 'G' +endfunction -function! s:JumpToHeader(forward, visual) - let cnt = v:count1 - let save = @/ - let pattern = '\v^#{1,6}.*$|^.+\n%(\-+|\=+)$' - if a:visual - normal! gv - endif - if a:forward - let motion = '/' . pattern - else - let motion = '?' . pattern - endif - while cnt > 0 - silent! execute motion - let cnt = cnt - 1 - endwhile - call histdel('/', -1) - let @/ = save +" Convert Setex headers in range `line1 .. line2` to Atx. +" +" Return the number of conversions. +" +function! s:SetexToAtx(line1, line2) + let l:originalNumLines = line('$') + execute 'silent! ' . a:line1 . ',' . a:line2 . 'substitute/\v(.*\S.*)\n\=+$/# \1/' + execute 'silent! ' . a:line1 . ',' . a:line2 . 'substitute/\v(.*\S.*)\n-+$/## \1/' + return l:originalNumLines - line('$') endfunction -function! s:Indent(indent) - if getline('.') =~ '\v^\s*%([-*+]|\d\.)\s*$' - if a:indent - normal >> +" If `a:1` is 0, decrease the level of all headers in range `line1 .. line2`. +" +" Otherwise, increase the level. `a:1` defaults to `0`. +" +function! s:HeaderDecrease(line1, line2, ...) + if a:0 > 0 + let l:increase = a:1 else - normal << + let l:increase = 0 endif - call setline('.', substitute(getline('.'), '\([-*+]\|\d\.\)\s*$', '\1 ', '')) - normal $ - elseif getline('.') =~ '\v^\s*(\s?\>)+\s*$' - if a:indent - call setline('.', substitute(getline('.'), '>\s*$', '>> ', '')) + if l:increase + let l:forbiddenLevel = 6 + let l:replaceLevels = [5, 1] + let l:levelDelta = 1 else - call setline('.', substitute(getline('.'), '\s*>\s*$', ' ', '')) - call setline('.', substitute(getline('.'), '^\s\+$', '', '')) + let l:forbiddenLevel = 1 + let l:replaceLevels = [2, 6] + let l:levelDelta = -1 endif - normal $ - endif + for l:line in range(a:line1, a:line2) + if join(getline(l:line, l:line + 1), "\n") =~ s:levelRegexpDict[l:forbiddenLevel] + echomsg 'There is an h' . l:forbiddenLevel . ' at line ' . l:line . '. Aborting.' + return + endif + endfor + let l:numSubstitutions = s:SetexToAtx(a:line1, a:line2) + let l:flags = (&gdefault ? '' : 'g') + for l:level in range(replaceLevels[0], replaceLevels[1], -l:levelDelta) + execute 'silent! ' . a:line1 . ',' . (a:line2 - l:numSubstitutions) . 'substitute/' . s:levelRegexpDict[l:level] . '/' . repeat('#', l:level + l:levelDelta) . '/' . l:flags + endfor +endfunction + +" Format table under cursor. +" +" Depends on Tabularize. +" +function! s:TableFormat() + let l:pos = getpos('.') + normal! { + " Search instead of `normal! j` because of the table at beginning of file edge case. + call search('|') + normal! j + " Remove everything that is not a pipe, colon or hyphen next to a colon othewise + " well formated tables would grow because of addition of 2 spaces on the separator + " line by Tabularize /|. + let l:flags = (&gdefault ? '' : 'g') + execute 's/\(:\@)+\s*$' +" Map in both normal and visual modes. +" +function! s:MapNormVis(rhs,lhs) + execute 'nn ' . a:rhs . ' :call ' . a:lhs . '()' + execute 'vn ' . a:rhs . ' :call VisMove(''' . a:lhs . ''')' endfunction -" }}} +" Parameters: +" +" - step +1 for right, -1 for left +" +" TODO: multiple lines. +" +function! s:FindCornerOfSyntax(lnum, col, step) + let l:col = a:col + let l:syn = synIDattr(synID(a:lnum, l:col, 1), 'name') + while synIDattr(synID(a:lnum, l:col, 1), 'name') ==# l:syn + let l:col += a:step + endwhile + return l:col - a:step +endfunction + +" Return the next position of the given syntax name, +" inclusive on the given position. +" +" TODO: multiple lines +" +function! s:FindNextSyntax(lnum, col, name) + let l:col = a:col + let l:step = 1 + while synIDattr(synID(a:lnum, l:col, 1), 'name') !=# a:name + let l:col += l:step + endwhile + return [a:lnum, l:col] +endfunction +function! s:FindCornersOfSyntax(lnum, col) + return [FindLeftOfSyntax(a:lnum, a:col), FindRightOfSyntax(a:lnum, a:col)] +endfunction -" {{{ MAPPINGS +function! s:FindRightOfSyntax(lnum, col) + return FindCornerOfSyntax(a:lnum, a:col, 1) +endfunction -" Commands -command! -nargs=0 -range MarkdownEditBlock :,call markdown#EditBlock() +function! s:FindLeftOfSyntax(lnum, col) + return FindCornerOfSyntax(a:lnum, a:col, -1) +endfunction -if g:markdown_enable_mappings - " Jumping around - noremap