" 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 0 :call asciidoc#set_section_title_level(1) " nnoremap 1 :call asciidoc#set_section_title_level(2) " nnoremap 2 :call asciidoc#set_section_title_level(3) " nnoremap 3 :call asciidoc#set_section_title_level(4) " nnoremap 4 :call asciidoc#set_section_title_level(5) " TODO: Make simple 'j/k' offsets honour setext style sections nnoremap [[ asciidoc#find_prior_section_title() nnoremap [] asciidoc#find_prior_section_title() . 'j' nnoremap ]] asciidoc#find_next_section_title() nnoremap ][ asciidoc#find_next_section_title() . 'k' xnoremap [[ asciidoc#find_prior_section_title() xnoremap [] asciidoc#find_prior_section_title() . 'j' xnoremap ]] asciidoc#find_next_section_title() xnoremap ][ asciidoc#find_next_section_title() . 'k' " xnoremap lu :call asciidoc#make_list('*')gv " xnoremap lo :call asciidoc#make_list('.')gv " xnoremap l< :call asciidoc#dent_list('in')gv " xnoremap l> :call asciidoc#dent_list('out')gv " nmap lu viplu`` " nmap lo viplo`` 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 Q :call Q(0) nnoremap gQ :call Q(1) 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