diff --git a/autoload/wiki/rx.vim b/autoload/wiki/rx.vim index 73ddb32..731e985 100644 --- a/autoload/wiki/rx.vim +++ b/autoload/wiki/rx.vim @@ -23,8 +23,12 @@ let wiki#rx#list_define = '::\%(\s\|$\)' let wiki#rx#comment = '^\s*%%.*$' let wiki#rx#todo = '\C\<\%(TODO\|STARTED\|FIXME\)\>:\?' let wiki#rx#done = '\C\<\%(OK\|DONE\|FIXED\)\>:\?' -let wiki#rx#header = '^#\{1,6}\s*[^#].*' -let wiki#rx#header_items = '^\(#\{1,6}\)\s*\([^#].*\)\s*$' +let wiki#rx#header_md_atx = '^#\{1,6}\s*[^#].*' +let wiki#rx#header_md_atx_items = '^\(#\{1,6}\)\s*\([^#].*\)\s*$' +let wiki#rx#header_org = '^\*\{1,6}\s*[^\*].*' +let wiki#rx#header_org_items = '^\(\*\{1,6}\)\s*\([^\*].*\)\s*$' +let wiki#rx#header_adoc = '^=\{1,6}\s*[^=].*' +let wiki#rx#header_adoc_items = '^\(=\{1,6}\)\s*\([^=].*\)\s*$' let wiki#rx#bold = wiki#rx#surrounded( \ '[^*`[:space:]]\%([^*`]*[^*`[:space:]]\)\?', '*') let wiki#rx#italic = wiki#rx#surrounded( diff --git a/autoload/wiki/toc.vim b/autoload/wiki/toc.vim index b2e0b5f..e4c5e9b 100644 --- a/autoload/wiki/toc.vim +++ b/autoload/wiki/toc.vim @@ -5,90 +5,12 @@ " function! wiki#toc#create(local) abort " {{{1 - let l:entries = wiki#toc#gather_entries() - if empty(l:entries) | return | endif - - if a:local - let [l:entries, l:local] = s:get_local_toc(l:entries, line('.')) - if empty(l:entries) | return | endif - - let l:level = l:local.level - let l:lnum_top = l:local.lnum_top - let l:lnum_bottom = l:local.lnum_bottom - let l:print_depth = get(g:, 'wiki_toc_depth', 6) + l:level - 1 - else - let l:level = 1 - let l:lnum_top = 1 - let l:lnum_bottom = get(get(l:entries, 1, {}), 'lnum', line('$')) - let l:print_depth = get(g:, 'wiki_toc_depth', 6) - endif - - " Only print entries to the desired depth - call filter(l:entries, 'v:val.level <= l:print_depth') - - let l:start = max([l:entries[0].lnum, 0]) - let l:title = '*' . g:wiki_toc_title . '*' - let l:re = printf( - \ '\v^%(%s %s|\*%s\*)$', - \ repeat('#', l:level), g:wiki_toc_title, g:wiki_toc_title) - - " Save the window view and syntax setting and disable syntax (makes things - " much faster) - let l:winsave = winsaveview() - let l:syntax = &l:syntax - setlocal syntax=off - - " Delete TOC if it exists - for l:lnum in range(l:lnum_top, l:lnum_bottom) - if getline(l:lnum) =~# l:re - let l:title = getline(l:lnum) - let l:start = l:lnum - let l:end = l:start + (getline(l:lnum+1) =~# '^\s*$' ? 2 : 1) - while l:end <= l:lnum_bottom && getline(l:end) =~# '^\s*[*-] ' - let l:end += 1 - endwhile - - let l:foldenable = &l:foldenable - setlocal nofoldenable - silent execute printf('%d,%ddelete _', l:start, l:end - 1) - let &l:foldenable = l:foldenable - - break - endif - endfor - - " Remove the first entry if it is "trivial" - if !a:local - let l:count = -1 - for l:e in l:entries - let l:count += 1 - if (l:e.level == 1 && l:e.lnum > l:start) || l:count > 1 | break | endif - endfor - if l:count == 1 - let l:entries = l:entries[1:] - endif - endif - - " Add updated TOC - call append(l:start - 1, l:title) - let l:i = 0 - for l:e in l:entries - call append(l:start + l:i, - \ repeat(' ', shiftwidth()*(l:e.level - l:level)) - \ . '* ' . wiki#link#template(l:e.anchor, l:e.header)) - let l:i += 1 - endfor - let l:length = len(l:entries) - if getline(l:start + l:length + 1) !=# '' - call append(l:start + l:length, '') - endif - if l:title =~# '^#' - call append(l:start, '') - endif - - " Restore syntax and view - let &l:syntax = l:syntax - call winrestview(l:winsave) + try + let l:filetype = !empty(&filetype) ? &filetype : 'wiki' + call s:toc_create_{l:filetype}(a:local) + catch /E117:/ + call wiki#log#error("No TOC support for filetype: " . &filetype . "!") + endtry endfunction " }}}1 @@ -106,7 +28,6 @@ function! wiki#toc#get_page_title(...) abort " {{{1 return empty(l:toc) ? '' : l:toc[0].header endfunction -" }}}1 function! wiki#toc#get_section_at(lnum) abort " {{{1 let l:toc = wiki#toc#gather_entries(#{ at_lnum: a:lnum }) return empty(l:toc) ? {} : l:toc[-1] @@ -123,6 +44,13 @@ function! wiki#toc#gather_entries(...) abort " {{{1 " at_lnum: Return the entry that covers specified line " Output: ToC entries + let l:filetype = !empty(&filetype) ? &filetype : 'wiki' + let l:hd = get(s:header_spec, l:filetype, {}) + if empty(l:hd) + call wiki#log#error("No TOC support for filetype: " . l:filetype . "!") + return [] + endif + let l:opts = extend(a:0 > 0 ? a:1 : {}, #{ \ first_only: v:false \}, 'keep') @@ -147,17 +75,17 @@ function! wiki#toc#gather_entries(...) abort " {{{1 endif if l:preblock | continue | endif - " Get line - check for header - if l:line !~# g:wiki#rx#header | continue | endif + " Get line - check for hd + if l:line !~# l:hd.regex | continue | endif - " Parse current header - let l:level = len(matchstr(l:line, '^#*')) - let l:header = matchlist(l:line, g:wiki#rx#header_items)[2] + " Parse current hd + let l:level = len(matchstr(l:line, '^' . l:hd.anchor_re . '*')) + let l:header = matchlist(l:line, l:hd.items)[2] let l:anchors[l:level] = l:header " Add the new entry call add(l:entries, { - \ 'anchor' : join(l:anchors[:l:level], '#'), + \ 'anchor' : join(l:anchors[:l:level], l:hd.anchor_char), \ 'anchors' : copy(l:anchors[1:l:level]), \ 'header': l:header, \ 'level' : l:level, @@ -173,6 +101,33 @@ function! wiki#toc#gather_entries(...) abort " {{{1 return l:entries endfunction +let s:header_spec = { + \ 'asciidoc': { + \ 'anchor_char': '=', + \ 'anchor_re': '=', + \ 'regex': g:wiki#rx#header_adoc, + \ 'items': g:wiki#rx#header_adoc_items, + \ }, + \ 'markdown': { + \ 'anchor_char': '#', + \ 'anchor_re': '#', + \ 'regex': g:wiki#rx#header_md_atx, + \ 'items': g:wiki#rx#header_md_atx_items, + \ }, + \ 'wiki': { + \ 'anchor_char': '#', + \ 'anchor_re': '#', + \ 'regex': g:wiki#rx#header_md_atx, + \ 'items': g:wiki#rx#header_md_atx_items, + \ }, + \ 'org': { + \ 'anchor_char': '*', + \ 'anchor_re': '\*', + \ 'regex': g:wiki#rx#header_org, + \ 'items': g:wiki#rx#header_org_items, + \ }, + \} + " }}}1 function! wiki#toc#gather_anchors(...) abort " {{{1 let l:cache = wiki#cache#open('anchors', { @@ -197,6 +152,165 @@ endfunction " }}}1 +function! s:toc_create_asciidoc(...) abort " {{{1 + let l:entries = wiki#toc#gather_entries() + if empty(l:entries) | return | endif + + if l:entries[0].level != 1 + let l:page_name = expand('%:t:r') + call append(0, "= " . l:page_name) + let l:toc = {'exists' : 0, 'lnum' : 1} + else + let l:toc = {'exists' : 0, 'lnum' : l:entries[0].lnum} + endif + + let l:toctitle = {'exists' : 0, 'lnum' : l:toc.lnum + 1} + let l:toclevels = {'exists' : 0, 'lnum' : l:toc.lnum + 2} + + " Loops over the first two heading if there are already toc keywords + for l:lnum in range(l:entries[0].lnum, l:entries[1].lnum) + let l:line = getline(l:lnum) + if match(l:line, ':toc:') > -1 + let l:toc.exists = 1 + let l:toc.lnum = l:lnum + elseif match(l:line, ':toc-title:') > -1 + let l:toctitle.exists = 1 + let l:toctitle.lnum = l:lnum + elseif match(l:line, ':toclevels:') > -1 + let l:toclevels.exists = 1 + let l:toclevels.lnum = l:lnum + endif + endfor + + if !l:toc.exists + call append(l:toc.lnum, ':toc:') + let l:toc.lnum += 1 + endif + if !l:toctitle.exists + call append(l:toc.lnum, ':toc-title: ' . g:wiki_toc_title) + let l:toc.lnum += 1 + endif + if !l:toclevels.exists + call append(l:toc.lnum, ':toclevels: ' . g:wiki_toc_depth) + endif +endfunction + +" }}}1 +function! s:toc_create_org(...) abort " {{{1 + let l:entries = wiki#toc#gather_entries() + if empty(l:entries) | return | endif + + let l:found = 0 + for l:lnum in range(1, l:entries[0].lnum) + let l:line = getline(l:lnum) + if match(l:line, 'toc') > -1 + let l:found = 1 + endif + endfor + + if !l:found + if match(getline(1), '^\*') > -1 + call append(0, '') + endif + call append(0, '#+OPTIONS: toc: ' . g:wiki_toc_depth) + endif +endfunction + +" }}}1 +function! s:toc_create_markdown(local) abort " {{{1 + let l:entries = wiki#toc#gather_entries() + if empty(l:entries) | return | endif + + if a:local + let [l:entries, l:local] = s:get_local_toc(l:entries, line('.')) + if empty(l:entries) | return | endif + + let l:level = l:local.level + let l:lnum_top = l:local.lnum_top + let l:lnum_bottom = l:local.lnum_bottom + let l:print_depth = g:wiki_toc_depth + l:level - 1 + else + let l:level = 1 + let l:lnum_top = 1 + let l:lnum_bottom = get(get(l:entries, 1, {}), 'lnum', line('$')) + let l:print_depth = g:wiki_toc_depth + endif + + " Only print entries to the desired depth + call filter(l:entries, 'v:val.level <= l:print_depth') + + let l:start = max([l:entries[0].lnum, 0]) + let l:title = '*' . g:wiki_toc_title . '*' + let l:re = printf( + \ '\v^%(%s %s|\*%s\*)$', + \ repeat('\=', l:level), g:wiki_toc_title, g:wiki_toc_title) + + " Save the window view and syntax setting and disable syntax (makes things + " much faster) + let l:winsave = winsaveview() + let l:syntax = &l:syntax + setlocal syntax=off + + " Delete TOC if it exists + for l:lnum in range(l:lnum_top, l:lnum_bottom) + if getline(l:lnum) =~# l:re + let l:title = getline(l:lnum) + let l:start = l:lnum + let l:end = l:start + (getline(l:lnum+1) =~# '^\s*$' ? 2 : 1) + while l:end <= l:lnum_bottom && getline(l:end) =~# '^\s*[*-] ' + let l:end += 1 + endwhile + + let l:foldenable = &l:foldenable + setlocal nofoldenable + silent execute printf('%d,%ddelete _', l:start, l:end - 1) + let &l:foldenable = l:foldenable + + break + endif + endfor + + " Remove the first entry if it is "trivial" + if !a:local + let l:count = -1 + for l:e in l:entries + let l:count += 1 + if (l:e.level == 1 && l:e.lnum > l:start) || l:count > 1 | break | endif + endfor + if l:count == 1 + let l:entries = l:entries[1:] + endif + endif + + " Add updated TOC + call append(l:start - 1, l:title) + let l:i = 0 + for l:e in l:entries + call append(l:start + l:i, + \ repeat(' ', shiftwidth()*(l:e.level - l:level)) + \ . '* ' . wiki#link#template(l:e.anchor, l:e.header)) + let l:i += 1 + endfor + let l:length = len(l:entries) + if getline(l:start + l:length + 1) !=# '' + call append(l:start + l:length, '') + endif + if l:title =~# '^#' + call append(l:start, '') + endif + + " Restore syntax and view + let &l:syntax = l:syntax + call winrestview(l:winsave) +endfunction + +" }}}1 +function! s:toc_create_wiki(local) abort " {{{1 + call s:toc_create_markdown(a:local) +endfunction + +" }}}1 + function! s:get_local_toc(entries, lnum_current) abort " {{{1 " Get ToC for the section for lnum_current " diff --git a/plugin/wiki.vim b/plugin/wiki.vim index 09337c2..a9f3970 100644 --- a/plugin/wiki.vim +++ b/plugin/wiki.vim @@ -81,6 +81,7 @@ call wiki#init#option('wiki_template_title_month', call wiki#init#option('wiki_template_title_week', \ '# Summary, %(year) week %(week)') call wiki#init#option('wiki_toc_title', 'Contents') +call wiki#init#option('wiki_toc_depth', 6) call wiki#init#option('wiki_viewer', { \ '_' : get({ \ 'linux' : 'xdg-open',