1
0
mirror of https://github.com/SpaceVim/SpaceVim.git synced 2025-01-24 05:20:04 +08:00
SpaceVim/bundle/vim-asciidoc/ftplugin/asciidoc.vim

406 lines
11 KiB
VimL
Raw Normal View History

2020-09-06 21:56:12 +08:00
" 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