mirror of
https://github.com/SpaceVim/SpaceVim.git
synced 2025-01-24 02:20:03 +08:00
406 lines
11 KiB
VimL
406 lines
11 KiB
VimL
" Asciidoc
|
|
" Barry Arthur
|
|
" 1.1, 2014-08-26
|
|
|
|
" 'atx' or 'setext'
|
|
if !exists('g:asciidoc_title_style')
|
|
let g:asciidoc_title_style = 'atx'
|
|
endif
|
|
|
|
" 'asymmetric' or 'symmetric'
|
|
if !exists('g:asciidoc_title_style_atx')
|
|
let g:asciidoc_title_style_atx = 'asymmetric'
|
|
endif
|
|
|
|
compiler asciidoc
|
|
|
|
setlocal foldmethod=marker
|
|
|
|
if &spelllang == ''
|
|
setlocal spelllang=en
|
|
endif
|
|
|
|
|
|
setlocal autoindent expandtab softtabstop=2 shiftwidth=2 wrap
|
|
|
|
if &textwidth == 0
|
|
setlocal textwidth=70
|
|
endif
|
|
|
|
setlocal comments=://
|
|
setlocal commentstring=//\ %s
|
|
|
|
setlocal formatoptions+=tcroqln2
|
|
setlocal indentkeys=!^F,o,O
|
|
setlocal nosmartindent nocindent
|
|
setlocal isk-=_
|
|
|
|
" headings
|
|
" nnoremap <buffer> <leader>0 :call asciidoc#set_section_title_level(1)<cr>
|
|
" nnoremap <buffer> <leader>1 :call asciidoc#set_section_title_level(2)<cr>
|
|
" nnoremap <buffer> <leader>2 :call asciidoc#set_section_title_level(3)<cr>
|
|
" nnoremap <buffer> <leader>3 :call asciidoc#set_section_title_level(4)<cr>
|
|
" nnoremap <buffer> <leader>4 :call asciidoc#set_section_title_level(5)<cr>
|
|
|
|
" TODO: Make simple 'j/k' offsets honour setext style sections
|
|
nnoremap <buffer> <expr><silent> [[ asciidoc#find_prior_section_title()
|
|
nnoremap <buffer> <expr><silent> [] asciidoc#find_prior_section_title() . 'j'
|
|
nnoremap <buffer> <expr><silent> ]] asciidoc#find_next_section_title()
|
|
nnoremap <buffer> <expr><silent> ][ asciidoc#find_next_section_title() . 'k'
|
|
|
|
xnoremap <buffer> <expr><silent> [[ asciidoc#find_prior_section_title()
|
|
xnoremap <buffer> <expr><silent> [] asciidoc#find_prior_section_title() . 'j'
|
|
xnoremap <buffer> <expr><silent> ]] asciidoc#find_next_section_title()
|
|
xnoremap <buffer> <expr><silent> ][ asciidoc#find_next_section_title() . 'k'
|
|
|
|
" xnoremap <buffer> <silent> lu :call asciidoc#make_list('*')<cr>gv
|
|
" xnoremap <buffer> <silent> lo :call asciidoc#make_list('.')<cr>gv
|
|
" xnoremap <buffer> <silent> l< :call asciidoc#dent_list('in')<cr>gv
|
|
" xnoremap <buffer> <silent> l> :call asciidoc#dent_list('out')<cr>gv
|
|
|
|
" nmap <buffer> <leader>lu viplu<c-\><c-n>``
|
|
" nmap <buffer> <leader>lo viplo<c-\><c-n>``
|
|
|
|
let s:asciidoc = {}
|
|
let s:asciidoc.delimited_block_pattern = '^[-.~_+^=*\/]\{4,}\s*$'
|
|
let s:asciidoc.heading_pattern = '^[-=~^+]\{4,}\s*$'
|
|
|
|
let s:asciidoc.list_pattern = ERex.parse('
|
|
\ \%(\_^\|\n\) # explicitly_numbered
|
|
\ \s*
|
|
\ \d\+
|
|
\ \.
|
|
\ \s\+
|
|
\ \|
|
|
\ \%(\_^\|\n\) # explicitly_alpha
|
|
\ \s*
|
|
\ [a-zA-Z]
|
|
\ \.
|
|
\ \s\+
|
|
\ \|
|
|
\ \%(\_^\|\n\) # explicitly_roman
|
|
\ \s*
|
|
\ [ivxIVX]\+ # (must_end_in_")"
|
|
\ )
|
|
\ \s\+
|
|
\ \|
|
|
\ \%(\_^\|\n\) # definition_list
|
|
\ \%(\_^\|\n\)
|
|
\ \%(\S\+\s\+\)\+
|
|
\ ::\+
|
|
\ \s\+
|
|
\ \%(\S\+\)\@=
|
|
\ \|
|
|
\ \%(\_^\|\n\) # implicit
|
|
\ \s*
|
|
\ [-*+.]\+
|
|
\ \s\+
|
|
\ \%(\S\+\)\@=
|
|
\')
|
|
|
|
" DEPRECATED after accurate list_pattern definition above
|
|
" let s:asciidoc.itemization_pattern = '^\s*[-*+.]\+\s'
|
|
|
|
" allow multi-depth list chars (--, ---, ----, .., ..., ...., etc)
|
|
exe 'syn match asciidocListBullet /' . s:asciidoc.list_pattern . '/'
|
|
let &l:formatlistpat=s:asciidoc.list_pattern
|
|
|
|
"Typing "" in insert mode inserts a pair of smart quotes and places the
|
|
"cursor between them. Depends on asciidoc/asciidoctor flavour. Off by default.
|
|
|
|
if ! exists('g:asciidoc_smartquotes')
|
|
let g:asciidoc_smartquotes = 0
|
|
endif
|
|
if ! exists('g:asciidoctor_smartquotes')
|
|
let g:asciidoctor_smartquotes = 0
|
|
endif
|
|
|
|
" indent
|
|
" ------
|
|
setlocal indentexpr=GetAsciidocIndent()
|
|
|
|
" stolen from the RST equivalent
|
|
function! GetAsciidocIndent()
|
|
let lnum = prevnonblank(v:lnum - 1)
|
|
if lnum == 0
|
|
return 0
|
|
endif
|
|
|
|
let [lnum, line] = s:asciidoc.skip_back_until_white_line(lnum)
|
|
let ind = indent(lnum)
|
|
|
|
" echom 'lnum=' . lnum
|
|
" echom 'ind=' . ind
|
|
" echom 'line=' . line
|
|
|
|
" Don't auto-indent within lists
|
|
if line =~ s:asciidoc.itemization_pattern
|
|
let ind = 0
|
|
endif
|
|
|
|
let line = getline(v:lnum - 1)
|
|
|
|
return ind
|
|
endfunction
|
|
|
|
" format
|
|
" ------
|
|
|
|
" The following object and its functions is modified from Yukihiro Nakadaira's
|
|
" autofmt example.
|
|
|
|
" Easily reflow text
|
|
" the Q form (badly) tries to keep cursor position
|
|
" the gQ form subsequently jumps over the reformatted block
|
|
nnoremap <silent> <buffer> Q :call <SID>Q(0)<cr>
|
|
nnoremap <silent> <buffer> gQ :call <SID>Q(1)<cr>
|
|
|
|
function! s:Q(skip_block_after_format)
|
|
if ! a:skip_block_after_format
|
|
let save_clip = @*
|
|
let save_reg = @@
|
|
let tos = line('w0')
|
|
let pos = getpos('.')
|
|
norm! v{y
|
|
call setpos('.', pos)
|
|
let word_count = len(split(@@, '\_s\+'))
|
|
endif
|
|
|
|
norm! gqap
|
|
|
|
if a:skip_block_after_format
|
|
normal! }
|
|
else
|
|
let scrolloff = &scrolloff
|
|
set scrolloff=0
|
|
call setpos('.', pos)
|
|
exe 'norm! {' . word_count . 'W'
|
|
let pos = getpos('.')
|
|
call cursor(tos, 1)
|
|
norm! zt
|
|
call setpos('.', pos)
|
|
let &scrolloff = scrolloff
|
|
let @* = save_clip
|
|
let @@ = save_reg
|
|
endif
|
|
endfunction
|
|
|
|
setlocal formatexpr=AsciidocFormatexpr()
|
|
|
|
function! AsciidocFormatexpr()
|
|
return s:asciidoc.formatexpr()
|
|
endfunction
|
|
|
|
function s:asciidoc.formatexpr()
|
|
" echom 'formatter called'
|
|
if mode() =~# '[iR]' && &formatoptions =~# 'a'
|
|
return 1
|
|
elseif mode() !~# '[niR]' || (mode() =~# '[iR]' && v:count != 1) || v:char =~# '\s'
|
|
echohl ErrorMsg
|
|
echomsg "Assert(formatexpr): Unknown State: " mode() v:lnum v:count string(v:char)
|
|
echohl None
|
|
return 1
|
|
endif
|
|
if mode() == 'n'
|
|
return self.format_normal_mode(v:lnum, v:count - 1)
|
|
else
|
|
" We don't actually do anything in insert mode yet
|
|
" return self.format_insert_mode(v:char)
|
|
endif
|
|
endfunction
|
|
|
|
function s:asciidoc.format_normal_mode(lnum, count)
|
|
" echom "normal formatexpr(lnum,count): " . a:lnum . ", " . a:count
|
|
let lnum = a:lnum
|
|
let last_line = lnum + a:count
|
|
let lnum = self.skip_white_lines(lnum)
|
|
let [lnum, line] = self.skip_fixed_lines(lnum)
|
|
let last_line = max([last_line, lnum])
|
|
let last_line = self.find_last_line(last_line)
|
|
|
|
" echom "normal formatexpr(first,last): " . lnum . ", " . last_line
|
|
" echom 'line = ' . line
|
|
" echom 'lnum = ' . lnum
|
|
" echom 'last_line = ' . last_line
|
|
|
|
call self.reformat_text(lnum, last_line)
|
|
return 0
|
|
endfunction
|
|
|
|
function s:asciidoc.reformat_chunk(chunk)
|
|
" echom 'reformat_chunk: ' . a:chunk[0]
|
|
return Asif(a:chunk, 'asciidoc', ['setlocal textwidth=' . &tw, 'setlocal indentexpr=', 'setlocal formatexpr=', 'normal! gqap'])
|
|
endfunction
|
|
|
|
function s:asciidoc.replace_chunk(chunk, lnum, last_line)
|
|
exe a:lnum . ',' . a:last_line . 'd'
|
|
undojoin
|
|
call append(a:lnum - 1, a:chunk)
|
|
endfunction
|
|
|
|
function s:asciidoc.reformat_text(lnum, last_line)
|
|
" echom 'reformat_text: ' . a:lnum . ', ' . a:last_line
|
|
let lnum = a:lnum
|
|
let last_line = a:last_line
|
|
let lines = getline(lnum, a:last_line)
|
|
|
|
let block = s:asciidoc.identify_block(lines[0])
|
|
echom 'block=' . block
|
|
|
|
if block == 'literal'
|
|
" nothing to do
|
|
elseif block == 'para'
|
|
let formatted = s:asciidoc.reformat_chunk(lines)
|
|
if formatted != lines
|
|
call s:asciidoc.replace_chunk(formatted, lnum, last_line)
|
|
endif
|
|
elseif block == 'list'
|
|
let formatted = []
|
|
|
|
let elems = list#partition(
|
|
\ string#scanner(lines).split(
|
|
\ '\n\?\zs\(\(+\n\)\|\(' . s:asciidoc.list_pattern . '\)\)'
|
|
\ , 1)[1:], 2)
|
|
let elems = (type(elems[0]) == type([]) ? elems : [elems])
|
|
for chunk in map(elems
|
|
\ , 'v:val[0] . string#trim(substitute(v:val[1], "\\n\\s\\+", " ", "g"))')
|
|
if chunk =~ "^+\n"
|
|
call extend(formatted, ['+'])
|
|
call extend(formatted, s:asciidoc.reformat_chunk(matchstr(chunk, "^+\n\\zs.*")))
|
|
else
|
|
call extend(formatted, s:asciidoc.reformat_chunk(chunk))
|
|
endif
|
|
endfor
|
|
if formatted != lines
|
|
call s:asciidoc.replace_chunk(formatted, lnum, last_line)
|
|
endif
|
|
else
|
|
echohl Comment
|
|
echom 'vim-asciidoc: unknown block on ' . lnum . ": don't know how to format: " . strpart(lines[0], 0, 20) . '...'
|
|
echohl None
|
|
endif
|
|
endfunction
|
|
|
|
function s:asciidoc.identify_block(line)
|
|
let line = a:line
|
|
if line =~ self.list_pattern
|
|
return 'list'
|
|
elseif line =~ '^[*_`+]\{0,2}\S'
|
|
return 'para'
|
|
elseif line =~ '^\s\+'
|
|
return 'literal'
|
|
else
|
|
return 'unknown'
|
|
endif
|
|
endfunction
|
|
|
|
function s:asciidoc.get_line(lnum)
|
|
return [a:lnum, getline(a:lnum)]
|
|
endfunction
|
|
|
|
function s:asciidoc.get_next_line(lnum)
|
|
return s:asciidoc.get_line(a:lnum + 1)
|
|
endfunction
|
|
|
|
function s:asciidoc.get_prev_line(lnum)
|
|
return s:asciidoc.get_line(a:lnum - 1)
|
|
endfunction
|
|
|
|
function s:asciidoc.skip_fixed_lines(lnum)
|
|
let [lnum, line] = s:asciidoc.get_line(a:lnum)
|
|
let done = 0
|
|
|
|
while done == 0
|
|
let done = 1
|
|
" skip optional block title
|
|
if line =~ '^\.\a'
|
|
let [lnum, line] = self.get_next_line(lnum)
|
|
let done = 0
|
|
endif
|
|
" " skip list joiner
|
|
" if line =~ '^+$'
|
|
" let [lnum, line] = self.get_next_line(lnum)
|
|
" let done = 0
|
|
" endif
|
|
" skip optional attribute or blockid
|
|
if line =~ '^\['
|
|
let [lnum, line] = self.get_next_line(lnum)
|
|
let done = 0
|
|
endif
|
|
" skip possible one-line heading
|
|
if line =~ '^=\+\s\+\a'
|
|
let [lnum, line] = self.get_next_line(lnum)
|
|
let done = 0
|
|
endif
|
|
" skip possible table
|
|
if line =~ '^|'
|
|
let [lnum, line] = self.get_next_line(lnum)
|
|
let done = 0
|
|
endif
|
|
" skip possible start of delimited block
|
|
if line =~ self.delimited_block_pattern
|
|
let [lnum, line] = self.get_next_line(lnum)
|
|
let done = 0
|
|
endif
|
|
" skip possible two-line heading
|
|
let [next_lnum, next_line] = self.get_next_line(lnum)
|
|
if (line =~ '^\a') && (next_line =~ self.heading_pattern)
|
|
let [lnum, line] = self.get_next_line(next_lnum)
|
|
let done = 0
|
|
endif
|
|
|
|
endwhile
|
|
return [lnum, line]
|
|
endfunction
|
|
|
|
function s:asciidoc.find_last_line(lnum)
|
|
let [lnum, line] = s:asciidoc.get_line(a:lnum)
|
|
let done = 0
|
|
|
|
while done == 0
|
|
let done = 1
|
|
" skip until blank line
|
|
if line !~ '^\s*$'
|
|
let [lnum, line] = self.get_next_line(lnum)
|
|
let done = 0
|
|
endif
|
|
endwhile
|
|
let done = 0
|
|
|
|
while done == 0
|
|
let done = 1
|
|
" skip possible blank lines
|
|
if line =~ '^\s*$'
|
|
let [lnum, line] = self.get_prev_line(lnum)
|
|
let done = 0
|
|
endif
|
|
" skip possible one-line heading
|
|
if line =~ self.delimited_block_pattern
|
|
let [lnum, line] = self.get_prev_line(lnum)
|
|
let done = 0
|
|
endif
|
|
endwhile
|
|
return lnum
|
|
endfunction
|
|
|
|
function s:asciidoc.format_insert_mode(char)
|
|
endfunction
|
|
|
|
function s:asciidoc.skip_white_lines(lnum)
|
|
let [lnum, line] = s:asciidoc.get_line(a:lnum)
|
|
while line =~ '^\s*$'
|
|
let [lnum, line] = self.get_next_line(lnum)
|
|
endwhile
|
|
return lnum
|
|
endfunction
|
|
|
|
function s:asciidoc.skip_back_until_white_line(lnum)
|
|
let [lnum, line] = s:asciidoc.get_line(a:lnum)
|
|
while line !~ '^\s*$'
|
|
let [pn, pl] = [lnum, line]
|
|
let [lnum, line] = self.get_prev_line(lnum)
|
|
endwhile
|
|
return [pn, pl]
|
|
endfunction
|
|
|