├── README.asciidoc ├── UltiSnips └── asciidoc.snippets ├── autoload └── asciidoc.vim ├── compiler ├── asciidoc.vim └── asciidoctor.vim ├── ftdetect └── asciidoc.vim ├── ftplugin └── asciidoc.vim └── syntax └── asciidoc.vim /README.asciidoc: -------------------------------------------------------------------------------- 1 | == vim-asciidoc 2 | 3 | __Enhanced editing support for http://asciidoc.org[Asciidoc] files in Vim__ 4 | 5 | === Dependencies 6 | 7 | * https://github.com/dahu/vimple[] 8 | * https://github.com/dahu/Asif[] 9 | * https://github.com/Raimondi/VimRegStyle[] 10 | * (optional) https://github.com/vim-scripts/SyntaxRange[] 11 | 12 | === Compilers 13 | 14 | vim-asciidoc provides: 15 | 16 | [style="horizontal"] 17 | `:compiler asciidoc` :: To use the original Python Asciidoc 18 | `:compiler asciidoctor` :: To use the newer Ruby Asciidoctor 19 | 20 | Each compiler provides a `:Theme` command for changing the style-sheet. 21 | 22 | .Asciidoc 23 | 24 | [style="horizontal"] 25 | `g:asciidoc_themes` :: The list of installed themes, defaulting to the 26 | two themes that come with standard asciidoc, _flask_ and _volnitsky_. 27 | 28 | `g:asciidoc_theme` :: The name of your preferred default theme, 29 | defaulting to the asciidoc default blue stylesheet. 30 | 31 | .Asciidoctor 32 | 33 | `g:asciidoctor_themes_dir` :: The location of your CSS stylesheets 34 | `g:asciidoctor_theme` :: The name of your preferred default theme, 35 | defaulting to the asciidoctor default red stylesheet. 36 | 37 | ==== Live Reload 38 | 39 | - The https://wiki.gnome.org/Apps/Web[Epiphany browser] automatically 40 | refreshes when it detects that the source has been updated. This makes 41 | for a wonderful previewer when editing asciidoc files. Just run 42 | `:make` and switch to Epiphany and watch it auto-refresh. 43 | - Firefox has the plugin 44 | (https://addons.mozilla.org/fr/firefox/addon/auto-reload[AutoReload]), and 45 | - Chrome has the plugins 46 | (https://chrome.google.com/webstore/detail/livepage/pilnojpmdoofaelbinaeodfpjheijkbh[LivePage] 47 | and 48 | https://chrome.google.com/webstore/detail/livereload/jnihajbhpnppcggbcgedagnkighmdlei[LiveReload]) 49 | to the same effect. 50 | 51 | === Headings 52 | 53 | The key sequence `1` will turn the current line into a level 1 54 | heading using the current heading style as defined in 55 | `g:asciidoc_title_style` (_setext_, or the default, _atx_). Similar 56 | mappings exist for heading levels 2-5. 57 | 58 | The Vim _section keys_ (`[[ ]] [] ][`) will move you between headings. 59 | 60 | ==== Heading Styles 61 | 62 | By default, vim-asciidoc uses asymmetric _atx_ style headings, like: 63 | 64 | .... 65 | == Level 2 Heading 66 | .... 67 | 68 | You can force symmetric _atx_ style headings with `let 69 | g:asciidoc_title_style_atx = "symmetric"` which will result in 70 | headings like: 71 | 72 | .... 73 | == Level 2 Heading == 74 | .... 75 | 76 | _Setext_ style headings are also supported with `let 77 | g:asciidoc_title_style = "setext"` which results in headings like: 78 | 79 | .... 80 | Level 2 Heading 81 | --------------- 82 | .... 83 | 84 | === Lists 85 | 86 | .Nested bullet and number lists are supported using the following syntax: 87 | 88 | .... 89 | * item 90 | ** sub-item 91 | *** sub-sub-item 92 | .... 93 | 94 | .And numbered lists: 95 | 96 | .... 97 | . item 98 | .. sub-item 99 | ... sub-sub-item 100 | .... 101 | 102 | ==== List Building Commands 103 | 104 | Use the following visual mode list building commands: 105 | 106 | [style="horizontal"] 107 | `lu` :: Create unordered lists 108 | `lo` :: Create ordered lists 109 | `l>` :: Increase list depth 110 | `l<` :: Decrease list depth 111 | 112 | === Reformatting 113 | 114 | By default, Vim does the wrong thing by reformatting Asciidoc's block 115 | delimiters as part of the paragraph. This plugin fixes that problem, 116 | correctly formatting blocks, lists and normal paragraphs. 117 | 118 | Use the `Q` key to reformat the current _block_. 119 | 120 | === Syntax Highlighting 121 | 122 | vim-asciidoc merely bundles Stuart Rackham's original asciidoc syntax 123 | file. 124 | 125 | If you have the 126 | https://github.com/vim-scripts/SyntaxRange[SyntaxRange] plugin 127 | installed, source blocks within ++[=+-]++ blocks will be highlighted 128 | according to the named language. Currently only C, Python and VimL are 129 | highlighted by default. Submit a PR if you want to extend this set, or just 130 | add your own to ++~/.vim/after/syntax/asciidoc.vim++ 131 | 132 | === Snippets 133 | 134 | vim-asciidoc provides several convenient snippets in the 135 | https://github.com/SirVer/ultisnips[UltiSnips] format. 136 | -------------------------------------------------------------------------------- /UltiSnips/asciidoc.snippets: -------------------------------------------------------------------------------- 1 | priority -50 2 | 3 | snippet version "Document header version string" 4 | v1.0, `!v strftime('%Y-%m-%d', localtime())`:${1: Revision remark} 5 | endsnippet 6 | 7 | snippet author "Document header author string" 8 | `!v g:username` <`!v g:email`> 9 | endsnippet 10 | 11 | snippet header "Document Blockheader" 12 | ${1:`!v expand('%:t:r')`} 13 | `!p snip.rv="=" * len(t[1])` 14 | `!v g:username` <`!v g:email`> 15 | v1.0, `!v strftime('%Y-%m-%d', localtime())`:${2: Revision remark} 16 | endsnippet 17 | 18 | snippet quote "Quote Block" b 19 | [quote,${1:author},${2:source}] 20 | ____ 21 | $0 22 | ____ 23 | endsnippet 24 | 25 | snippet verse "Verse Block" b 26 | [verse,${1:author},${2:source}] 27 | ____ 28 | $0 29 | ____ 30 | endsnippet 31 | 32 | snippet comment "Comment Block" b 33 | //// 34 | $0 35 | //// 36 | endsnippet 37 | 38 | snippet passthrough "Passthrough Block" b 39 | ++++ 40 | $0 41 | ++++ 42 | endsnippet 43 | 44 | snippet listing "Listing Block" b 45 | ---- 46 | $0 47 | ---- 48 | endsnippet 49 | 50 | snippet literal "Literal Block" b 51 | .... 52 | $0 53 | .... 54 | endsnippet 55 | 56 | snippet sidebar "Sidebar Block" b 57 | **** 58 | $0 59 | **** 60 | endsnippet 61 | 62 | snippet quote "Quote Block" b 63 | ____ 64 | $0 65 | ____ 66 | endsnippet 67 | 68 | snippet example "Example Block" b 69 | ==== 70 | $0 71 | ==== 72 | endsnippet 73 | 74 | snippet note "Note admonition" b 75 | [NOTE] 76 | .${1:Title} 77 | ==== 78 | $0 79 | ==== 80 | endsnippet 81 | 82 | snippet footnote "Footnote" 83 | footnote:[${1:text}] 84 | endsnippet 85 | 86 | snippet anchor "Hypertext link target" 87 | anchor:${1:id}[${2:label}] 88 | endsnippet 89 | 90 | snippet xref "Link to hypertext anchor" 91 | xref:${1:id}[${2:caption}] 92 | endsnippet 93 | 94 | snippet image "Image" 95 | image:${1:target}[${2:alt="$3"}] 96 | endsnippet 97 | 98 | snippet table "Table" b 99 | .${1:Title} 100 | [${2:width="50%",cols=">s,^m,e",frame="topbot",options="header,footer"}] 101 | |==== 102 | |$0 103 | |==== 104 | endsnippet 105 | 106 | # Two line headers 107 | snippet l1 "Level 1 Section" b 108 | ${1:Level 1 Section} 109 | `!p snip.rv="-" * len(t[1])` 110 | $0 111 | endsnippet 112 | 113 | snippet l2 "Level 2 Section" b 114 | ${1:Level 2 Section} 115 | `!p snip.rv="~" * len(t[1])` 116 | $0 117 | endsnippet 118 | 119 | snippet l3 "Level 3 Section" b 120 | ${1:Level 3 Section} 121 | `!p snip.rv="^" * len(t[1])` 122 | $0 123 | endsnippet 124 | 125 | snippet l4 "Level 4 Section" b 126 | ${1:Level 4 Section} 127 | `!p snip.rv="+" * len(t[1])` 128 | $0 129 | endsnippet 130 | -------------------------------------------------------------------------------- /autoload/asciidoc.vim: -------------------------------------------------------------------------------- 1 | " TODO: create T title text-object 2 | " TODO: make section jumps [[ ]] etc work 3 | 4 | let s:atx_title = '\_^=*\s\+\(\S\%(.\%(\s\+=\+\s*\_$\)\@!\)*.\)\(\s\+=\+\)\?\s*$' 5 | let s:setext_title_underline = '[-=~^+]\+\s*$' 6 | let s:setext_title = '\_^\(\S.\+\)\s*\n' . s:setext_title_underline 7 | let s:setext_levels = ['=','-', '~', '^', '+'] 8 | 9 | function! asciidoc#find_prior_section_title() 10 | let old_pos = getpos('.') 11 | let pos = old_pos 12 | let pos[2] = 0 13 | call setpos('.', pos) 14 | let prior_atx = search(s:atx_title, 'Wbn') 15 | let prior_setext = search(s:setext_title, 'Wbn') 16 | call setpos('.', old_pos) 17 | let prior = max([prior_atx, prior_setext]) 18 | if prior == 0 19 | return 20 | endif 21 | return prior . 'G' 22 | endfunction 23 | 24 | function! asciidoc#find_next_section_title() 25 | let next_atx = search(s:atx_title, 'Wn') 26 | let next_setext = search(s:setext_title, 'Wn') 27 | let next = min(filter([next_atx, next_setext], 'v:val != 0')) 28 | if next == 0 29 | return 30 | endif 31 | return next . 'G' 32 | endfunction 33 | 34 | function! asciidoc#get_atx_section_title(line_number) 35 | let line = getline(a:line_number) 36 | let match = matchlist(line, s:atx_title) 37 | if !empty(match) 38 | let title = match[1] 39 | let symmetric = len(match[2]) != 0 40 | let level = len(matchstr(line, '^=*')) 41 | return {'line' : a:line_number, 'type' : 'atx', 'symmetric' : symmetric, 'level' : level, 'title' : title} 42 | else 43 | return {} 44 | endif 45 | endfunction 46 | 47 | function! asciidoc#get_setext_section_title(line_number) 48 | let line = getline(a:line_number) 49 | if line =~ '^' . s:setext_title_underline 50 | let underline = line 51 | let line_number = a:line_number - 1 52 | let line = getline(line_number) 53 | else 54 | let line_number = a:line_number 55 | let underline = getline(line_number + 1) 56 | endif 57 | let level = 1 + index(s:setext_levels, underline[0]) 58 | if (line . "\n" . underline) =~ s:setext_title 59 | return {'line' : line_number, 'type' : 'setext', 'level' : level, 'title' : line} 60 | else 61 | return {} 62 | endif 63 | endfunction 64 | 65 | function! asciidoc#get_section_title(line_number) 66 | let atx = asciidoc#get_atx_section_title(a:line_number) 67 | if !empty(atx) 68 | return atx 69 | else 70 | return asciidoc#get_setext_section_title(a:line_number) 71 | endif 72 | endfunction 73 | 74 | function! asciidoc#set_atx_section_title(line_number, level, title, symmetric) 75 | let level_marks = repeat('=', a:level) 76 | call setline(a:line_number, level_marks . ' ' . a:title . (a:symmetric ? (' ' . level_marks) : '')) 77 | endfunction 78 | 79 | function! asciidoc#set_setext_section_title(line_number, level, title) 80 | let line_number = a:line_number + 1 81 | let level_marks = repeat(s:setext_levels[a:level - 1], len(a:title)) 82 | if getline(line_number) =~ '^$' 83 | call append(line_number - 1, level_marks) 84 | else 85 | call setline(line_number, level_marks) 86 | endif 87 | endfunction 88 | 89 | function! asciidoc#set_section_title_level(level) 90 | let line = line('.') 91 | let section_title = asciidoc#get_section_title(line) 92 | if !empty(section_title) 93 | if section_title.type == 'atx' 94 | call asciidoc#set_atx_section_title(section_title.line, a:level, section_title.title, section_title.symmetric) 95 | else 96 | call asciidoc#set_setext_section_title(section_title.line, a:level, section_title.title) 97 | endif 98 | else 99 | let title = getline('.') 100 | if g:asciidoc_title_style == 'atx' 101 | call asciidoc#set_atx_section_title(line, a:level, title, g:asciidoc_title_style_atx != 'asymmetric') 102 | else 103 | call asciidoc#set_setext_section_title(line, a:level, title) 104 | endif 105 | endif 106 | endfunction 107 | 108 | function! asciidoc#make_list(type) range 109 | let old_search = @/ 110 | exe a:firstline . ',' . a:lastline . 's/^\s*\([*.]*\)\s*/\=repeat("' . a:type . '", max([1, len(submatch(1))]))." "/' 111 | let @/ = old_search 112 | endfunction 113 | 114 | function! asciidoc#dent_list(in_out) range 115 | let old_search = @/ 116 | if a:in_out == 'in' 117 | silent! exe a:firstline . ',' . a:lastline . 's/^[*.]//' 118 | else 119 | silent! exe a:firstline . ',' . a:lastline . 's/^\([*.]\)/&&/' 120 | endif 121 | let @/ = old_search 122 | endfunction 123 | -------------------------------------------------------------------------------- /compiler/asciidoc.vim: -------------------------------------------------------------------------------- 1 | " Asciidoc compiler settings for Vim 2 | 3 | if exists("b:current_compiler") 4 | finish 5 | endif 6 | let b:current_compiler = "asciidoc" 7 | 8 | if exists(":CompilerSet") != 2 9 | command! -nargs=* CompilerSet setlocal 10 | endif 11 | 12 | " errorformat stolen from syntastic 13 | 14 | let &l:errorformat = '' 15 | \. '%Easciidoc: %tRROR: %f: line %l: %m,' 16 | \. '%Easciidoc: %tRROR: %f: %m,' 17 | \. '%Easciidoc: FAILED: %f: line %l: %m,' 18 | \. '%Easciidoc: FAILED: %f: %m,' 19 | \. '%Wasciidoc: %tARNING: %f: line %l: %m,' 20 | \. '%Wasciidoc: %tARNING: %f: %m,' 21 | \. '%Wasciidoc: DEPRECATED: %f: line %l: %m,' 22 | \. '%Wasciidoc: DEPRECATED: %f: %m' 23 | 24 | function! s:set_makeprg() 25 | let &l:makeprg = '' 26 | \. 'asciidoc' 27 | \. ' -a urldata' 28 | \. ' -a icons' 29 | \. ' ' . get(b:, 'asciidoc_theme', '') 30 | \. ' ' . get(b:, 'asciidoc_icons_dir', '-a iconsdir=./images/icons/') 31 | \. ' ' . get(b:, 'asciidoc_backend', '') 32 | \. ' %' 33 | endfunction 34 | 35 | 36 | let s:asciidoc_default_themes = ['default', 'flask', 'volnitsky'] 37 | 38 | if ! exists('g:asciidoc_themes') 39 | let g:asciidoc_themes = s:asciidoc_default_themes 40 | endif 41 | 42 | if ! exists('g:asciidoc_theme') 43 | let g:asciidoc_theme = 'default' 44 | endif 45 | 46 | function! s:available_themes_completer(ArgLead, CmdLine, CursorPos) 47 | return filter(copy(g:asciidoc_themes), 'v:val =~ a:ArgLead') 48 | endfunction 49 | 50 | function! s:update_theme(theme) 51 | if a:theme == 'default' 52 | let b:asciidoc_theme = '' 53 | else 54 | let b:asciidoc_theme = '-a theme=' . a:theme 55 | endif 56 | call s:set_makeprg() 57 | endfunction 58 | 59 | command! -nargs=1 -complete=customlist,s:available_themes_completer Theme call update_theme() 60 | 61 | call s:set_makeprg() 62 | call s:update_theme(g:asciidoc_theme) 63 | -------------------------------------------------------------------------------- /compiler/asciidoctor.vim: -------------------------------------------------------------------------------- 1 | " Asciidoc compiler settings for Vim 2 | 3 | if exists("b:current_compiler") 4 | finish 5 | endif 6 | let b:current_compiler = "asciidoc" 7 | 8 | if exists(":CompilerSet") != 2 9 | command! -nargs=* CompilerSet setlocal 10 | endif 11 | 12 | " errorformat stolen from syntastic 13 | 14 | let &l:errorformat = '' 15 | \. '%Easciidoc: %tRROR: %f: line %l: %m,' 16 | \. '%Easciidoc: %tRROR: %f: %m,' 17 | \. '%Easciidoc: FAILED: %f: line %l: %m,' 18 | \. '%Easciidoc: FAILED: %f: %m,' 19 | \. '%Wasciidoc: %tARNING: %f: line %l: %m,' 20 | \. '%Wasciidoc: %tARNING: %f: %m,' 21 | \. '%Wasciidoc: DEPRECATED: %f: line %l: %m,' 22 | \. '%Wasciidoc: DEPRECATED: %f: %m' 23 | 24 | function! s:set_makeprg() 25 | let &l:makeprg = '' 26 | \. 'asciidoctor' 27 | \. ' -a urldata' 28 | \. ' -a icons' 29 | \. ' ' . get(b:, 'asciidoctor_theme', '') 30 | \. ' ' . get(b:, 'asciidoctor_icons_dir', '-a iconsdir=./images/icons/') 31 | \. ' ' . get(b:, 'asciidoctor_backend', '') 32 | \. ' %' 33 | endfunction 34 | 35 | if ! exists('g:asciidoctor_themes_dir') 36 | echohl Warning 37 | echom 'Set g:asciidoctor_themes_dir for theme support.' 38 | echohl None 39 | endif 40 | 41 | if ! exists('g:asciidoctor_theme') 42 | let g:asciidoctor_theme = 'default' 43 | endif 44 | 45 | function! s:asciidoctor_themes() 46 | if ! exists('g:asciidoctor_themes_dir') 47 | echohl Warning 48 | echom 'Set g:asciidoctor_themes_dir for theme support.' 49 | echohl None 50 | return ['default'] 51 | endif 52 | return map(glob(g:asciidoctor_themes_dir . '/*.css', 0, 1) 53 | \, 'fnamemodify(v:val, ":p:t")') 54 | endfunction 55 | 56 | function! s:available_themes_completer(ArgLead, CmdLine, CursorPos) 57 | return filter(s:asciidoctor_themes(), 'v:val =~ a:ArgLead') 58 | endfunction 59 | 60 | function! s:update_theme(theme) 61 | if a:theme == 'default' 62 | let b:asciidoctor_theme = '' 63 | else 64 | let b:asciidoctor_theme = '-a stylesheet=' . a:theme . ' -a stylesdir=' . g:asciidoctor_themes_dir 65 | endif 66 | call s:set_makeprg() 67 | endfunction 68 | 69 | command! -nargs=1 -complete=customlist,s:available_themes_completer Theme call update_theme() 70 | 71 | call s:set_makeprg() 72 | call s:update_theme(g:asciidoctor_theme) 73 | -------------------------------------------------------------------------------- /ftdetect/asciidoc.vim: -------------------------------------------------------------------------------- 1 | " Vim filetype detection file 2 | " Language: AsciiDoc 3 | " Maintainer: Barry Arthur 4 | " URL: http://asciidoc.org/ 5 | " https://github.com/dahu/vim-asciidoc 6 | " Licence: Licensed under the same terms as Vim itself 7 | " Remarks: Vim 6 or greater 8 | 9 | augroup Asciidoc 10 | au! 11 | au BufRead *.txt,README,TODO,CHANGELOG,NOTES call s:FTasciidoc() 12 | au BufRead,BufNewFile *.asciidoc,*.adoc,*.ad set filetype=asciidoc 13 | augroup END 14 | 15 | " Checks for a valid AsciiDoc document title after first skipping any 16 | " leading comments. 17 | " Original code by Stuart Rackham 18 | function! s:FTasciidoc() 19 | " this check doesn't play well with 20 | " some CMake files and should be skipped 21 | if expand('%:t') == "CMakeLists.txt" 22 | return 23 | endif 24 | let in_comment_block = 0 25 | let n = 1 26 | while n < 50 27 | let line = getline(n) 28 | let n += 1 29 | if line =~ '^/\{4,}$' 30 | if ! in_comment_block 31 | let in_comment_block = 1 32 | else 33 | let in_comment_block = 0 34 | endif 35 | continue 36 | endif 37 | if in_comment_block 38 | continue 39 | endif 40 | if line !~ '\(^//\)\|\(^\s*$\)' 41 | break 42 | endif 43 | endwhile 44 | if line =~ '^[=#]\+\s\+\w' 45 | set filetype=asciidoc 46 | return 47 | endif 48 | let len = len(line) 49 | if len < 3 50 | return 51 | endif 52 | let nextline = getline(n) 53 | if nextline !~ '[-=]\{3,}' 54 | return 55 | endif 56 | let nextlen = len(nextline) 57 | if len < (nextlen - 3) || len > (nextlen + 3) 58 | return 59 | endif 60 | set filetype=asciidoc 61 | endfunction 62 | -------------------------------------------------------------------------------- /ftplugin/asciidoc.vim: -------------------------------------------------------------------------------- 1 | " Asciidoc 2 | " Barry Arthur 3 | " 1.1, 2014-08-26 4 | 5 | " 'atx' or 'setext' 6 | if !exists('g:asciidoc_title_style') 7 | let g:asciidoc_title_style = 'atx' 8 | endif 9 | 10 | " 'asymmetric' or 'symmetric' 11 | if !exists('g:asciidoc_title_style_atx') 12 | let g:asciidoc_title_style_atx = 'asymmetric' 13 | endif 14 | 15 | compiler asciidoc 16 | 17 | setlocal foldmethod=marker 18 | if &spelllang == '' 19 | setlocal spelllang=en 20 | endif 21 | 22 | setlocal spell 23 | setlocal autoindent expandtab softtabstop=2 shiftwidth=2 wrap 24 | if &textwidth == 0 25 | setlocal textwidth=70 26 | endif 27 | setlocal comments=:// 28 | setlocal commentstring=//\ %s 29 | 30 | setlocal formatoptions+=tcroqln2 31 | setlocal indentkeys=!^F,o,O 32 | setlocal nosmartindent nocindent 33 | setlocal isk-=_ 34 | 35 | " headings 36 | nnoremap 0 :call asciidoc#set_section_title_level(1) 37 | nnoremap 1 :call asciidoc#set_section_title_level(2) 38 | nnoremap 2 :call asciidoc#set_section_title_level(3) 39 | nnoremap 3 :call asciidoc#set_section_title_level(4) 40 | nnoremap 4 :call asciidoc#set_section_title_level(5) 41 | 42 | " TODO: Make simple 'j/k' offsets honour setext style sections 43 | nnoremap [[ asciidoc#find_prior_section_title() 44 | nnoremap [] asciidoc#find_prior_section_title() . 'j' 45 | nnoremap ]] asciidoc#find_next_section_title() 46 | nnoremap ][ asciidoc#find_next_section_title() . 'k' 47 | 48 | xnoremap [[ asciidoc#find_prior_section_title() 49 | xnoremap [] asciidoc#find_prior_section_title() . 'j' 50 | xnoremap ]] asciidoc#find_next_section_title() 51 | xnoremap ][ asciidoc#find_next_section_title() . 'k' 52 | 53 | xnoremap lu :call asciidoc#make_list('*')gv 54 | xnoremap lo :call asciidoc#make_list('.')gv 55 | xnoremap l< :call asciidoc#dent_list('in')gv 56 | xnoremap l> :call asciidoc#dent_list('out')gv 57 | 58 | nmap lu viplu`` 59 | nmap lo viplo`` 60 | 61 | let s:asciidoc = {} 62 | let s:asciidoc.delimited_block_pattern = '^[-.~_+^=*\/]\{4,}\s*$' 63 | let s:asciidoc.heading_pattern = '^[-=~^+]\{4,}\s*$' 64 | 65 | let s:asciidoc.list_pattern = ERex.parse(' 66 | \ \%(\_^\|\n\) # explicitly_numbered 67 | \ \s* 68 | \ \d\+ 69 | \ \. 70 | \ \s\+ 71 | \ \| 72 | \ \%(\_^\|\n\) # explicitly_alpha 73 | \ \s* 74 | \ [a-zA-Z] 75 | \ \. 76 | \ \s\+ 77 | \ \| 78 | \ \%(\_^\|\n\) # explicitly_roman 79 | \ \s* 80 | \ [ivxIVX]\+ # (must_end_in_")" 81 | \ ) 82 | \ \s\+ 83 | \ \| 84 | \ \%(\_^\|\n\) # definition_list 85 | \ \%(\_^\|\n\) 86 | \ \%(\S\+\s\+\)\+ 87 | \ ::\+ 88 | \ \s\+ 89 | \ \%(\S\+\)\@= 90 | \ \| 91 | \ \%(\_^\|\n\) # implicit 92 | \ \s* 93 | \ [-*+.]\+ 94 | \ \s\+ 95 | \ \%(\S\+\)\@= 96 | \') 97 | 98 | " DEPRECATED after accurate list_pattern definition above 99 | " let s:asciidoc.itemization_pattern = '^\s*[-*+.]\+\s' 100 | 101 | " allow multi-depth list chars (--, ---, ----, .., ..., ...., etc) 102 | exe 'syn match asciidocListBullet /' . s:asciidoc.list_pattern . '/' 103 | let &l:formatlistpat=s:asciidoc.list_pattern 104 | 105 | "Typing "" in insert mode inserts a pair of smart quotes and places the 106 | "cursor between them. Depends on asciidoc/asciidoctor flavour. Off by default. 107 | 108 | if ! exists('g:asciidoc_smartquotes') 109 | let g:asciidoc_smartquotes = 0 110 | endif 111 | if ! exists('g:asciidoctor_smartquotes') 112 | let g:asciidoctor_smartquotes = 0 113 | endif 114 | 115 | if g:asciidoc_smartquotes 116 | inoremap "" ``''hi 117 | elseif g:asciidoctor_smartquotes 118 | inoremap "" "``"hi 119 | endif 120 | 121 | " indent 122 | " ------ 123 | setlocal indentexpr=GetAsciidocIndent() 124 | 125 | " stolen from the RST equivalent 126 | function! GetAsciidocIndent() 127 | let lnum = prevnonblank(v:lnum - 1) 128 | if lnum == 0 129 | return 0 130 | endif 131 | 132 | let [lnum, line] = s:asciidoc.skip_back_until_white_line(lnum) 133 | let ind = indent(lnum) 134 | 135 | " echom 'lnum=' . lnum 136 | " echom 'ind=' . ind 137 | " echom 'line=' . line 138 | 139 | " Don't auto-indent within lists 140 | if line =~ s:asciidoc.itemization_pattern 141 | let ind = 0 142 | endif 143 | 144 | let line = getline(v:lnum - 1) 145 | 146 | return ind 147 | endfunction 148 | 149 | " format 150 | " ------ 151 | 152 | " The following object and its functions is modified from Yukihiro Nakadaira's 153 | " autofmt example. 154 | 155 | " Easily reflow text 156 | " the Q form (badly) tries to keep cursor position 157 | " the gQ form subsequently jumps over the reformatted block 158 | nnoremap Q :call Q(0) 159 | nnoremap gQ :call Q(1) 160 | 161 | function! s:Q(skip_block_after_format) 162 | if ! a:skip_block_after_format 163 | let save_clip = @* 164 | let save_reg = @@ 165 | let tos = line('w0') 166 | let pos = getpos('.') 167 | norm! v{y 168 | call setpos('.', pos) 169 | let word_count = len(split(@@, '\_s\+')) 170 | endif 171 | 172 | norm! gqap 173 | 174 | if a:skip_block_after_format 175 | normal! } 176 | else 177 | let scrolloff = &scrolloff 178 | set scrolloff=0 179 | call setpos('.', pos) 180 | exe 'norm! {' . word_count . 'W' 181 | let pos = getpos('.') 182 | call cursor(tos, 1) 183 | norm! zt 184 | call setpos('.', pos) 185 | let &scrolloff = scrolloff 186 | let @* = save_clip 187 | let @@ = save_reg 188 | endif 189 | endfunction 190 | 191 | setlocal formatexpr=AsciidocFormatexpr() 192 | 193 | function! AsciidocFormatexpr() 194 | return s:asciidoc.formatexpr() 195 | endfunction 196 | 197 | function s:asciidoc.formatexpr() 198 | " echom 'formatter called' 199 | if mode() =~# '[iR]' && &formatoptions =~# 'a' 200 | return 1 201 | elseif mode() !~# '[niR]' || (mode() =~# '[iR]' && v:count != 1) || v:char =~# '\s' 202 | echohl ErrorMsg 203 | echomsg "Assert(formatexpr): Unknown State: " mode() v:lnum v:count string(v:char) 204 | echohl None 205 | return 1 206 | endif 207 | if mode() == 'n' 208 | return self.format_normal_mode(v:lnum, v:count - 1) 209 | else 210 | return self.format_insert_mode(v:char) 211 | endif 212 | endfunction 213 | 214 | function s:asciidoc.format_normal_mode(lnum, count) 215 | " echom "normal formatexpr(lnum,count): " . a:lnum . ", " . a:count 216 | let lnum = a:lnum 217 | let last_line = lnum + a:count 218 | let lnum = self.skip_white_lines(lnum) 219 | let [lnum, line] = self.skip_fixed_lines(lnum) 220 | let last_line = max([last_line, lnum]) 221 | let last_line = self.find_last_line(last_line) 222 | 223 | " echom "normal formatexpr(first,last): " . lnum . ", " . last_line 224 | " echom 'line = ' . line 225 | " echom 'lnum = ' . lnum 226 | " echom 'last_line = ' . last_line 227 | 228 | call self.reformat_text(lnum, last_line) 229 | return 0 230 | endfunction 231 | 232 | function s:asciidoc.reformat_chunk(chunk) 233 | " echom 'reformat_chunk: ' . a:chunk[0] 234 | return Asif(a:chunk, 'asciidoc', ['setlocal textwidth=' . &tw, 'setlocal indentexpr=', 'setlocal formatexpr=', 'normal! gqap']) 235 | endfunction 236 | 237 | function s:asciidoc.replace_chunk(chunk, lnum, last_line) 238 | exe a:lnum . ',' . a:last_line . 'd' 239 | undojoin 240 | call append(a:lnum - 1, a:chunk) 241 | endfunction 242 | 243 | function s:asciidoc.reformat_text(lnum, last_line) 244 | " echom 'reformat_text: ' . a:lnum . ', ' . a:last_line 245 | let lnum = a:lnum 246 | let last_line = a:last_line 247 | let lines = getline(lnum, a:last_line) 248 | 249 | let block = s:asciidoc.identify_block(lines[0]) 250 | echom 'block=' . block 251 | 252 | if block == 'literal' 253 | " nothing to do 254 | elseif block == 'para' 255 | let formatted = s:asciidoc.reformat_chunk(lines) 256 | if formatted != lines 257 | call s:asciidoc.replace_chunk(formatted, lnum, last_line) 258 | endif 259 | elseif block == 'list' 260 | let formatted = [] 261 | 262 | let elems = list#partition( 263 | \ string#scanner(lines).split( 264 | \ '\n\?\zs\(\(+\n\)\|\(' . s:asciidoc.list_pattern . '\)\)' 265 | \ , 1)[1:], 2) 266 | let elems = (type(elems[0]) == type([]) ? elems : [elems]) 267 | for chunk in map(elems 268 | \ , 'v:val[0] . string#trim(substitute(v:val[1], "\\n\\s\\+", " ", "g"))') 269 | if chunk =~ "^+\n" 270 | call extend(formatted, ['+']) 271 | call extend(formatted, s:asciidoc.reformat_chunk(matchstr(chunk, "^+\n\\zs.*"))) 272 | else 273 | call extend(formatted, s:asciidoc.reformat_chunk(chunk)) 274 | endif 275 | endfor 276 | if formatted != lines 277 | call s:asciidoc.replace_chunk(formatted, lnum, last_line) 278 | endif 279 | else 280 | echohl Comment 281 | echom 'vim-asciidoc: unknown block on ' . lnum . ": don't know how to format: " . strpart(lines[0], 0, 20) . '...' 282 | echohl None 283 | endif 284 | endfunction 285 | 286 | function s:asciidoc.identify_block(line) 287 | let line = a:line 288 | if line =~ self.list_pattern 289 | return 'list' 290 | elseif line =~ '^[*_`+]\{0,2}\S' 291 | return 'para' 292 | elseif line =~ '^\s\+' 293 | return 'literal' 294 | else 295 | return 'unknown' 296 | endif 297 | endfunction 298 | 299 | function s:asciidoc.get_line(lnum) 300 | return [a:lnum, getline(a:lnum)] 301 | endfunction 302 | 303 | function s:asciidoc.get_next_line(lnum) 304 | return s:asciidoc.get_line(a:lnum + 1) 305 | endfunction 306 | 307 | function s:asciidoc.get_prev_line(lnum) 308 | return s:asciidoc.get_line(a:lnum - 1) 309 | endfunction 310 | 311 | function s:asciidoc.skip_fixed_lines(lnum) 312 | let [lnum, line] = s:asciidoc.get_line(a:lnum) 313 | let done = 0 314 | 315 | while done == 0 316 | let done = 1 317 | " skip optional block title 318 | if line =~ '^\.\a' 319 | let [lnum, line] = self.get_next_line(lnum) 320 | let done = 0 321 | endif 322 | " " skip list joiner 323 | " if line =~ '^+$' 324 | " let [lnum, line] = self.get_next_line(lnum) 325 | " let done = 0 326 | " endif 327 | " skip optional attribute or blockid 328 | if line =~ '^\[' 329 | let [lnum, line] = self.get_next_line(lnum) 330 | let done = 0 331 | endif 332 | " skip possible one-line heading 333 | if line =~ '^=\+\s\+\a' 334 | let [lnum, line] = self.get_next_line(lnum) 335 | let done = 0 336 | endif 337 | " skip possible table 338 | if line =~ '^|' 339 | let [lnum, line] = self.get_next_line(lnum) 340 | let done = 0 341 | endif 342 | " skip possible start of delimited block 343 | if line =~ self.delimited_block_pattern 344 | let [lnum, line] = self.get_next_line(lnum) 345 | let done = 0 346 | endif 347 | " skip possible two-line heading 348 | let [next_lnum, next_line] = self.get_next_line(lnum) 349 | if (line =~ '^\a') && (next_line =~ self.heading_pattern) 350 | let [lnum, line] = self.get_next_line(next_lnum) 351 | let done = 0 352 | endif 353 | 354 | endwhile 355 | return [lnum, line] 356 | endfunction 357 | 358 | function s:asciidoc.find_last_line(lnum) 359 | let [lnum, line] = s:asciidoc.get_line(a:lnum) 360 | let done = 0 361 | 362 | while done == 0 363 | let done = 1 364 | " skip until blank line 365 | if line !~ '^\s*$' 366 | let [lnum, line] = self.get_next_line(lnum) 367 | let done = 0 368 | endif 369 | endwhile 370 | let done = 0 371 | 372 | while done == 0 373 | let done = 1 374 | " skip possible blank lines 375 | if line =~ '^\s*$' 376 | let [lnum, line] = self.get_prev_line(lnum) 377 | let done = 0 378 | endif 379 | " skip possible one-line heading 380 | if line =~ self.delimited_block_pattern 381 | let [lnum, line] = self.get_prev_line(lnum) 382 | let done = 0 383 | endif 384 | endwhile 385 | return lnum 386 | endfunction 387 | 388 | function s:asciidoc.format_insert_mode(char) 389 | " We don't actually do anything special in insert mode yet. 390 | " A non-zero return code here uses Vim's internal formatters based on the 391 | " options set. 392 | return 1 393 | endfunction 394 | 395 | function s:asciidoc.skip_white_lines(lnum) 396 | let [lnum, line] = s:asciidoc.get_line(a:lnum) 397 | while line =~ '^\s*$' 398 | let [lnum, line] = self.get_next_line(lnum) 399 | endwhile 400 | return lnum 401 | endfunction 402 | 403 | function s:asciidoc.skip_back_until_white_line(lnum) 404 | let [lnum, line] = s:asciidoc.get_line(a:lnum) 405 | while line !~ '^\s*$' 406 | let [pn, pl] = [lnum, line] 407 | let [lnum, line] = self.get_prev_line(lnum) 408 | endwhile 409 | return [pn, pl] 410 | endfunction 411 | 412 | -------------------------------------------------------------------------------- /syntax/asciidoc.vim: -------------------------------------------------------------------------------- 1 | " Vim syntax file 2 | " Language: AsciiDoc 3 | " Author: Stuart Rackham (inspired by Felix 4 | " Obenhuber's original asciidoc.vim script). 5 | " URL: http://www.methods.co.nz/asciidoc/ 6 | " https://github.com/dahu/vim-asciidoc 7 | " Licence: GPL (http://www.gnu.org) 8 | " Remarks: Vim 6 or greater 9 | " Limitations: See 'Appendix E: Vim Syntax Highlighter' in the AsciiDoc 'User 10 | " Guide'. 11 | 12 | if exists("b:current_syntax") 13 | finish 14 | endif 15 | 16 | syn clear 17 | syn sync fromstart 18 | syn sync linebreaks=1 19 | 20 | function! AsciidocEnableSyntaxRanges() 21 | " source block syntax highlighting 22 | if exists('g:loaded_SyntaxRange') 23 | for lang in ['c', 'python', 'vim'] 24 | call SyntaxRange#Include( 25 | \ '^\c\[source\s*,\s*' . lang . '.*\]\s*$' 26 | \, '\(\]\s*\n\)\@/ 37 | syn match asciidocListBlockDelimiter /^--$/ 38 | syn match asciidocLineBreak /[ \t]+$/ 39 | syn match asciidocRuler /^'\{3,}$/ 40 | syn match asciidocPagebreak /^<\{3,}$/ 41 | syn match asciidocEntityRef /\\\@\?[0-9A-Za-z_]\@!/ 45 | syn match asciidocAttributeRef /\\\@.]\{,3}\)\?\([a-z]\)\?\)\?|/ containedin=asciidocTableBlock contained 88 | syn region asciidocTableBlock matchgroup=asciidocTableDelimiter start=/^|=\{3,}$/ end=/^|=\{3,}$/ keepend contains=ALL 89 | syn match asciidocTablePrefix /\(\S\@.]\{,3}\)\?\([a-z]\)\?\)\?!/ containedin=asciidocTableBlock contained 90 | syn region asciidocTableBlock2 matchgroup=asciidocTableDelimiter2 start=/^!=\{3,}$/ end=/^!=\{3,}$/ keepend contains=ALL 91 | 92 | syn match asciidocListContinuation /^+$/ 93 | syn region asciidocExampleBlock start=/^=\{4,}$/ end=/^=\{4,}$/ contains=asciidocCallout,asciidocToDo keepend 94 | syn region asciidocLiteralBlock start=/^\.\{4,}$/ end=/^\.\{4,}$/ contains=asciidocCallout,asciidocToDo keepend 95 | syn region asciidocListingBlock start=/^-\{4,}$/ end=/^-\{4,}$/ contains=asciidocCallout,asciidocToDo keepend 96 | syn region asciidocCommentBlock start="^/\{4,}$" end="^/\{4,}$" contains=asciidocToDo 97 | syn region asciidocPassthroughBlock start="^+\{4,}$" end="^+\{4,}$" 98 | 99 | 100 | " Allowing leading \w characters in the filter delimiter is to accomodate 101 | " the pre version 8.2.7 syntax and may be removed in future releases. 102 | syn region asciidocFilterBlock start=/^\w*\~\{4,}$/ end=/^\w*\~\{4,}$/ 103 | 104 | syn region asciidocMacroAttributes matchgroup=asciidocRefMacro start=/\\\@>\)\|^$/ contains=asciidocQuoted.* keepend 105 | syn region asciidocMacroAttributes matchgroup=asciidocAnchorMacro start=/\\\@