diff options
Diffstat (limited to 'autoload/vimtex/parser')
-rw-r--r-- | autoload/vimtex/parser/auxiliary.vim | 58 | ||||
-rw-r--r-- | autoload/vimtex/parser/bib.vim | 370 | ||||
-rw-r--r-- | autoload/vimtex/parser/fls.vim | 19 | ||||
-rw-r--r-- | autoload/vimtex/parser/tex.vim | 205 | ||||
-rw-r--r-- | autoload/vimtex/parser/toc.vim | 782 |
5 files changed, 0 insertions, 1434 deletions
diff --git a/autoload/vimtex/parser/auxiliary.vim b/autoload/vimtex/parser/auxiliary.vim deleted file mode 100644 index b8805ebd..00000000 --- a/autoload/vimtex/parser/auxiliary.vim +++ /dev/null @@ -1,58 +0,0 @@ -if !exists('g:polyglot_disabled') || index(g:polyglot_disabled, 'latex') == -1 - -" vimtex - LaTeX plugin for Vim -" -" Maintainer: Karl Yngve Lervåg -" Email: karl.yngve@gmail.com -" - -function! vimtex#parser#auxiliary#parse(file) abort " {{{1 - return s:parse_recurse(a:file, []) -endfunction - -" }}}1 - -function! s:parse_recurse(file, parsed) abort " {{{1 - if !filereadable(a:file) || index(a:parsed, a:file) >= 0 - return [] - endif - call add(a:parsed, a:file) - - let l:lines = [] - for l:line in readfile(a:file) - call add(l:lines, l:line) - - if l:line =~# '\\@input{' - let l:file = s:input_line_parser(l:line, a:file) - call extend(l:lines, s:parse_recurse(l:file, a:parsed)) - endif - endfor - - return l:lines -endfunction - -" }}}1 - -function! s:input_line_parser(line, file) abort " {{{1 - let l:file = matchstr(a:line, '\\@input{\zs[^}]\+\ze}') - - " Remove extension to simplify the parsing (e.g. for "my file name".aux) - let l:file = substitute(l:file, '\.aux', '', '') - - " Trim whitespaces and quotes from beginning/end of string, append extension - let l:file = substitute(l:file, '^\(\s\|"\)*', '', '') - let l:file = substitute(l:file, '\(\s\|"\)*$', '', '') - let l:file .= '.aux' - - " Use absolute paths - if l:file !~# '\v^(\/|[A-Z]:)' - let l:file = fnamemodify(a:file, ':p:h') . '/' . l:file - endif - - " Only return filename if it is readable - return filereadable(l:file) ? l:file : '' -endfunction - -" }}}1 - -endif diff --git a/autoload/vimtex/parser/bib.vim b/autoload/vimtex/parser/bib.vim deleted file mode 100644 index 7ea2c238..00000000 --- a/autoload/vimtex/parser/bib.vim +++ /dev/null @@ -1,370 +0,0 @@ -if !exists('g:polyglot_disabled') || index(g:polyglot_disabled, 'latex') == -1 - -" vimtex - LaTeX plugin for Vim -" -" Maintainer: Karl Yngve Lervåg -" Email: karl.yngve@gmail.com -" - -function! vimtex#parser#bib#parse(file, opts) abort " {{{1 - if !filereadable(a:file) | return [] | endif - - let l:backend = get(a:opts, 'backend', g:vimtex_parser_bib_backend) - - if l:backend ==# 'bibtex' - if !executable('bibtex') | let l:backend = 'vim' | endif - elseif l:backend ==# 'bibparse' - if !executable('bibparse') | let l:backend = 'vim' | endif - else - let l:backend = 'vim' - endif - - return s:parse_with_{l:backend}(a:file) -endfunction - -" }}}1 - - -function! s:parse_with_bibtex(file) abort " {{{1 - call s:parse_with_bibtex_init() - if s:bibtex_not_executable | return [] | endif - - " Define temporary files - let tmp = { - \ 'aux' : 'tmpfile.aux', - \ 'bbl' : 'tmpfile.bbl', - \ 'blg' : 'tmpfile.blg', - \ } - - " Write temporary aux file - call writefile([ - \ '\citation{*}', - \ '\bibstyle{' . s:bibtex_bstfile . '}', - \ '\bibdata{' . fnamemodify(a:file, ':r') . '}', - \ ], tmp.aux) - - " Create the temporary bbl file - call vimtex#process#run('bibtex -terse ' . fnameescape(tmp.aux), { - \ 'background' : 0, - \ 'silent' : 1, - \}) - - " Parse temporary bbl file - let lines = join(readfile(tmp.bbl), "\n") - let lines = substitute(lines, '\n\n\@!\(\s\=\)\s*\|{\|}', '\1', 'g') - let lines = vimtex#util#tex2unicode(lines) - let lines = split(lines, "\n") - - let l:entries = [] - for line in lines - let matches = split(line, '||') - if empty(matches) || empty(matches[0]) | continue | endif - - let l:entry = { - \ 'key': matches[0], - \ 'type': matches[1], - \} - - if !empty(matches[2]) - let l:entry.author = matches[2] - endif - if !empty(matches[3]) - let l:entry.year = matches[3] - endif - if !empty(get(matches, 4, '')) - let l:entry.title = get(matches, 4, '') - endif - - call add(l:entries, l:entry) - endfor - - " Clean up - call delete(tmp.aux) - call delete(tmp.bbl) - call delete(tmp.blg) - - return l:entries -endfunction - -" }}}1 -function! s:parse_with_bibtex_init() abort " {{{1 - if exists('s:bibtex_init_done') | return | endif - - " Check if bibtex is executable - let s:bibtex_not_executable = !executable('bibtex') - if s:bibtex_not_executable - call vimtex#log#warning( - \ 'bibtex is not executable and may not be used to parse bib files!') - endif - - " Check if bstfile contains whitespace (not handled by vimtex) - if stridx(s:bibtex_bstfile, ' ') >= 0 - let l:oldbst = s:bibtex_bstfile . '.bst' - let s:bibtex_bstfile = tempname() - call writefile(readfile(l:oldbst), s:bibtex_bstfile . '.bst') - endif - - let s:bibtex_init_done = 1 -endfunction - -let s:bibtex_bstfile = expand('<sfile>:p:h') . '/vimcomplete' - -" }}}1 - -function! s:parse_with_bibparse(file) abort " {{{1 - call s:parse_with_bibparse_init() - if s:bibparse_not_executable | return [] | endif - - call vimtex#process#run('bibparse ' . fnameescape(a:file) - \ . ' >_vimtex_bibparsed.log', {'background' : 0, 'silent' : 1}) - let l:lines = readfile('_vimtex_bibparsed.log') - call delete('_vimtex_bibparsed.log') - - let l:current = {} - let l:entries = [] - for l:line in l:lines - if l:line[0] ==# '@' - if !empty(l:current) - call add(l:entries, l:current) - let l:current = {} - endif - - let l:index = stridx(l:line, ' ') - if l:index > 0 - let l:type = l:line[1:l:index-1] - let l:current.type = l:type - let l:current.key = l:line[l:index+1:] - endif - elseif !empty(l:current) - let l:index = stridx(l:line, '=') - if l:index < 0 | continue | endif - - let l:key = l:line[:l:index-1] - let l:value = l:line[l:index+1:] - let l:current[tolower(l:key)] = l:value - endif - endfor - - if !empty(l:current) - call add(l:entries, l:current) - endif - - return l:entries -endfunction - -" }}}1 -function! s:parse_with_bibparse_init() abort " {{{1 - if exists('s:bibparse_init_done') | return | endif - - " Check if bibtex is executable - let s:bibparse_not_executable = !executable('bibparse') - if s:bibparse_not_executable - call vimtex#log#warning( - \ 'bibparse is not executable and may not be used to parse bib files!') - endif - - let s:bibparse_init_done = 1 -endfunction - -" }}}1 - -function! s:parse_with_vim(file) abort " {{{1 - " Adheres to the format description found here: - " http://www.bibtex.org/Format/ - - if !filereadable(a:file) - return [] - endif - - let l:current = {} - let l:strings = {} - let l:entries = [] - for l:line in filter(readfile(a:file), 'v:val !~# ''^\s*\%(%\|$\)''') - if empty(l:current) - if s:parse_type(l:line, l:current, l:strings) - let l:current = {} - endif - continue - endif - - if l:current.type ==# 'string' - if s:parse_string(l:line, l:current, l:strings) - let l:current = {} - endif - else - if s:parse_entry(l:line, l:current, l:entries) - let l:current = {} - endif - endif - endfor - - return map(l:entries, 's:parse_entry_body(v:val, l:strings)') -endfunction - -" }}}1 - -function! s:parse_type(line, current, strings) abort " {{{1 - let l:matches = matchlist(a:line, '\v^\@(\w+)\s*\{\s*(.*)') - if empty(l:matches) | return 0 | endif - - let l:type = tolower(l:matches[1]) - if index(['preamble', 'comment'], l:type) >= 0 | return 0 | endif - - let a:current.level = 1 - let a:current.body = '' - - if l:type ==# 'string' - return s:parse_string(l:matches[2], a:current, a:strings) - else - let a:current.type = l:type - let a:current.key = matchstr(l:matches[2], '.*\ze,\s*') - return 0 - endif -endfunction - -" }}}1 -function! s:parse_string(line, string, strings) abort " {{{1 - let a:string.level += s:count(a:line, '{') - s:count(a:line, '}') - if a:string.level > 0 - let a:string.body .= a:line - return 0 - endif - - let a:string.body .= matchstr(a:line, '.*\ze}') - - let l:matches = matchlist(a:string.body, '\v^\s*(\w+)\s*\=\s*"(.*)"\s*$') - if !empty(l:matches) && !empty(l:matches[1]) - let a:strings[l:matches[1]] = l:matches[2] - endif - - return 1 -endfunction - -" }}}1 -function! s:parse_entry(line, entry, entries) abort " {{{1 - let a:entry.level += s:count(a:line, '{') - s:count(a:line, '}') - if a:entry.level > 0 - let a:entry.body .= a:line - return 0 - endif - - let a:entry.body .= matchstr(a:line, '.*\ze}') - - call add(a:entries, a:entry) - return 1 -endfunction - -" }}}1 - -function! s:parse_entry_body(entry, strings) abort " {{{1 - unlet a:entry.level - - let l:key = '' - let l:pos = matchend(a:entry.body, '^\s*') - while l:pos >= 0 - if empty(l:key) - let [l:key, l:pos] = s:get_key(a:entry.body, l:pos) - else - let [l:value, l:pos] = s:get_value(a:entry.body, l:pos, a:strings) - let a:entry[l:key] = l:value - let l:key = '' - endif - endwhile - - unlet a:entry.body - return a:entry -endfunction - -" }}}1 -function! s:get_key(body, head) abort " {{{1 - " Parse the key part of a bib entry tag. - " Assumption: a:body is left trimmed and either empty or starts with a key. - " Returns: The key and the remaining part of the entry body. - - let l:matches = matchlist(a:body, '^\v(\w+)\s*\=\s*', a:head) - return empty(l:matches) - \ ? ['', -1] - \ : [tolower(l:matches[1]), a:head + strlen(l:matches[0])] -endfunction - -" }}}1 -function! s:get_value(body, head, strings) abort " {{{1 - " Parse the value part of a bib entry tag, until separating comma or end. - " Assumption: a:body is left trimmed and either empty or starts with a value. - " Returns: The value and the remaining part of the entry body. - " - " A bib entry value is either - " 1. A number. - " 2. A concatenation (with #s) of double quoted strings, curlied strings, - " and/or bibvariables, - " - if a:body[a:head] =~# '\d' - let l:value = matchstr(a:body, '^\d\+', a:head) - let l:head = matchend(a:body, '^\s*,\s*', a:head + len(l:value)) - return [l:value, l:head] - else - return s:get_value_string(a:body, a:head, a:strings) - endif - - return ['s:get_value failed', -1] -endfunction - -" }}}1 -function! s:get_value_string(body, head, strings) abort " {{{1 - if a:body[a:head] ==# '{' - let l:sum = 1 - let l:i1 = a:head + 1 - let l:i0 = l:i1 - - while l:sum > 0 - let [l:match, l:_, l:i1] = matchstrpos(a:body, '[{}]', l:i1) - if l:i1 < 0 | break | endif - - let l:i0 = l:i1 - let l:sum += l:match ==# '{' ? 1 : -1 - endwhile - - let l:value = a:body[a:head+1:l:i0-2] - let l:head = matchend(a:body, '^\s*', l:i0) - elseif a:body[a:head] ==# '"' - let l:index = match(a:body, '\\\@<!"', a:head+1) - if l:index < 0 - return ['s:get_value_string failed', ''] - endif - - let l:value = a:body[a:head+1:l:index-1] - let l:head = matchend(a:body, '^\s*', l:index+1) - return [l:value, l:head] - elseif a:body[a:head:] =~# '^\w' - let l:value = matchstr(a:body, '^\w\+', a:head) - let l:head = matchend(a:body, '^\s*', a:head + strlen(l:value)) - let l:value = get(a:strings, l:value, '@(' . l:value . ')') - else - let l:head = a:head - endif - - if a:body[l:head] ==# '#' - let l:head = matchend(a:body, '^\s*', l:head + 1) - let [l:vadd, l:head] = s:get_value_string(a:body, l:head, a:strings) - let l:value .= l:vadd - endif - - return [l:value, matchend(a:body, '^,\s*', l:head)] -endfunction - -" }}}1 - -function! s:count(container, item) abort " {{{1 - " Necessary because in old Vim versions, count() does not work for strings - try - let l:count = count(a:container, a:item) - catch /E712/ - let l:count = count(split(a:container, '\zs'), a:item) - endtry - - return l:count -endfunction - -" }}}1 - -endif diff --git a/autoload/vimtex/parser/fls.vim b/autoload/vimtex/parser/fls.vim deleted file mode 100644 index 46b0db2f..00000000 --- a/autoload/vimtex/parser/fls.vim +++ /dev/null @@ -1,19 +0,0 @@ -if !exists('g:polyglot_disabled') || index(g:polyglot_disabled, 'latex') == -1 - -" vimtex - LaTeX plugin for Vim -" -" Maintainer: Karl Yngve Lervåg -" Email: karl.yngve@gmail.com -" - -function! vimtex#parser#fls#parse(file) abort " {{{1 - if !filereadable(a:file) - return [] - endif - - return readfile(a:file) -endfunction - -" }}}1 - -endif diff --git a/autoload/vimtex/parser/tex.vim b/autoload/vimtex/parser/tex.vim deleted file mode 100644 index 6259b5fa..00000000 --- a/autoload/vimtex/parser/tex.vim +++ /dev/null @@ -1,205 +0,0 @@ -if !exists('g:polyglot_disabled') || index(g:polyglot_disabled, 'latex') == -1 - -" vimtex - LaTeX plugin for Vim -" -" Maintainer: Karl Yngve Lervåg -" Email: karl.yngve@gmail.com -" - -function! vimtex#parser#tex#parse(file, opts) abort " {{{1 - let l:opts = extend({ - \ 'detailed': 1, - \ 'root' : exists('b:vimtex.root') ? b:vimtex.root : '', - \}, a:opts) - - let l:cache = vimtex#cache#open('texparser', { - \ 'local': 1, - \ 'persistent': 0, - \ 'default': {'ftime': -2}, - \}) - - let l:parsed = s:parse(a:file, l:opts, l:cache) - - if !l:opts.detailed - call map(l:parsed, 'v:val[2]') - endif - - return l:parsed -endfunction - -" }}}1 -function! vimtex#parser#tex#parse_files(file, opts) abort " {{{1 - let l:opts = extend({ - \ 'root' : exists('b:vimtex.root') ? b:vimtex.root : '', - \}, a:opts) - - let l:cache = vimtex#cache#open('texparser', { - \ 'local': 1, - \ 'persistent': 0, - \ 'default': {'ftime': -2}, - \}) - - return vimtex#util#uniq_unsorted( - \ s:parse_files(a:file, l:opts, l:cache)) -endfunction - -" }}}1 -function! vimtex#parser#tex#parse_preamble(file, opts) abort " {{{1 - let l:opts = extend({ - \ 'inclusive' : 0, - \ 'root' : exists('b:vimtex.root') ? b:vimtex.root : '', - \}, a:opts) - - return s:parse_preamble(a:file, l:opts, []) -endfunction - -" }}}1 - -function! s:parse(file, opts, cache) abort " {{{1 - let l:current = a:cache.get(a:file) - let l:ftime = getftime(a:file) - if l:ftime > l:current.ftime - let l:current.ftime = l:ftime - call s:parse_current(a:file, a:opts, l:current) - endif - - let l:parsed = [] - - for l:val in l:current.lines - if type(l:val) == type([]) - call add(l:parsed, l:val) - else - call extend(l:parsed, s:parse(l:val, a:opts, a:cache)) - endif - endfor - - return l:parsed -endfunction - -" }}}1 -function! s:parse_files(file, opts, cache) abort " {{{1 - let l:current = a:cache.get(a:file) - let l:ftime = getftime(a:file) - if l:ftime > l:current.ftime - let l:current.ftime = l:ftime - call s:parse_current(a:file, a:opts, l:current) - endif - - " Only include existing files - if !filereadable(a:file) | return [] | endif - - let l:files = [a:file] - for l:file in l:current.includes - let l:files += s:parse_files(l:file, a:opts, a:cache) - endfor - - return l:files -endfunction - -" }}}1 -function! s:parse_current(file, opts, current) abort " {{{1 - let a:current.lines = [] - let a:current.includes = [] - - " Also load includes from glsentries - let l:re_input = g:vimtex#re#tex_input . '|^\s*\\loadglsentries' - - if filereadable(a:file) - let l:lnum = 0 - for l:line in readfile(a:file) - let l:lnum += 1 - call add(a:current.lines, [a:file, l:lnum, l:line]) - - " Minor optimization: Avoid complex regex on "simple" lines - if stridx(l:line, '\') < 0 | continue | endif - - if l:line =~# l:re_input - let l:file = s:input_parser(l:line, a:file, a:opts.root) - call add(a:current.lines, l:file) - call add(a:current.includes, l:file) - endif - endfor - endif -endfunction - -" }}}1 -function! s:parse_preamble(file, opts, parsed_files) abort " {{{1 - if !filereadable(a:file) || index(a:parsed_files, a:file) >= 0 - return [] - endif - call add(a:parsed_files, a:file) - - let l:lines = [] - for l:line in readfile(a:file) - if l:line =~# '\\begin\s*{document}' - if a:opts.inclusive - call add(l:lines, l:line) - endif - break - endif - - call add(l:lines, l:line) - - if l:line =~# g:vimtex#re#tex_input - let l:file = s:input_parser(l:line, a:file, a:opts.root) - call extend(l:lines, s:parse_preamble(l:file, a:opts, a:parsed_files)) - endif - endfor - - return l:lines -endfunction - -" }}}1 - -function! s:input_parser(line, current_file, root) abort " {{{1 - " Handle \space commands - let l:file = substitute(a:line, '\\space\s*', ' ', 'g') - - " Handle import package commands - if l:file =~# g:vimtex#re#tex_input_import - let l:root = l:file =~# '\\sub' - \ ? fnamemodify(a:current_file, ':p:h') - \ : a:root - - let l:candidate = s:input_to_filename( - \ substitute(copy(l:file), '}\s*{', '', 'g'), l:root) - if !empty(l:candidate) - return l:candidate - else - return s:input_to_filename( - \ substitute(copy(l:file), '{.{-}}', '', ''), l:root) - endif - else - return s:input_to_filename(l:file, a:root) - endif -endfunction - -" }}}1 -function! s:input_to_filename(input, root) abort " {{{1 - let l:file = matchstr(a:input, '\zs[^{}]\+\ze}\s*\%(%\|$\)') - - " Trim whitespaces and quotes from beginning/end of string - let l:file = substitute(l:file, '^\(\s\|"\)*', '', '') - let l:file = substitute(l:file, '\(\s\|"\)*$', '', '') - - " Ensure that the file name has extension - if empty(fnamemodify(l:file, ':e')) - let l:file .= '.tex' - endif - - if vimtex#paths#is_abs(l:file) - return l:file - endif - - let l:candidate = a:root . '/' . l:file - if filereadable(l:candidate) - return l:candidate - endif - - let l:candidate = vimtex#kpsewhich#find(l:file) - return filereadable(l:candidate) ? l:candidate : l:file -endfunction - -" }}}1 - -endif diff --git a/autoload/vimtex/parser/toc.vim b/autoload/vimtex/parser/toc.vim deleted file mode 100644 index a518ce53..00000000 --- a/autoload/vimtex/parser/toc.vim +++ /dev/null @@ -1,782 +0,0 @@ -if !exists('g:polyglot_disabled') || index(g:polyglot_disabled, 'latex') == -1 - -" vimtex - LaTeX plugin for Vim -" -" Maintainer: Karl Yngve Lervåg -" Email: karl.yngve@gmail.com -" -" -" Parses tex project for ToC-like entries. Each entry is a dictionary -" similar to the following: -" -" entry = { -" title : "Some title", -" number : "3.1.2", -" file : /path/to/file.tex, -" line : 142, -" rank : cumulative line number, -" level : 2, -" type : [content | label | todo | include], -" link : [0 | 1], -" } -" - -function! vimtex#parser#toc#parse(file) abort " {{{1 - let l:entries = [] - let l:content = vimtex#parser#tex(a:file) - - let l:max_level = 0 - for [l:file, l:lnum, l:line] in l:content - if l:line =~# s:matcher_sections.re - let l:max_level = max([ - \ l:max_level, - \ s:sec_to_value[matchstr(l:line, s:matcher_sections.re_level)] - \]) - endif - endfor - - call s:level.reset('preamble', l:max_level) - - " No more parsing if there is no content - if empty(l:content) | return l:entries | endif - - " - " Begin parsing LaTeX files - " - let l:lnum_total = 0 - let l:matchers = s:matchers_preamble - for [l:file, l:lnum, l:line] in l:content - let l:lnum_total += 1 - let l:context = { - \ 'file' : l:file, - \ 'line' : l:line, - \ 'lnum' : l:lnum, - \ 'lnum_total' : l:lnum_total, - \ 'level' : s:level, - \ 'max_level' : l:max_level, - \ 'entry' : get(l:entries, -1, {}), - \ 'num_entries' : len(l:entries), - \} - - " Detect end of preamble - if s:level.preamble && l:line =~# '\v^\s*\\begin\{document\}' - let s:level.preamble = 0 - let l:matchers = s:matchers_content - continue - endif - - " Handle multi-line entries - if exists('s:matcher_continue') - call s:matcher_continue.continue(l:context) - continue - endif - - " Apply prefilter - this gives considerable speedup for large documents - if l:line !~# s:re_prefilter | continue | endif - - " Apply the matchers - for l:matcher in l:matchers - if l:line =~# l:matcher.re - let l:entry = l:matcher.get_entry(l:context) - if type(l:entry) == type([]) - call extend(l:entries, l:entry) - elseif !empty(l:entry) - call add(l:entries, l:entry) - endif - endif - endfor - endfor - - for l:matcher in s:matchers - try - call l:matcher.filter(l:entries) - catch /E716/ - endtry - endfor - - return l:entries -endfunction - -" }}}1 -function! vimtex#parser#toc#get_topmatters() abort " {{{1 - let l:topmatters = s:level.frontmatter - let l:topmatters += s:level.mainmatter - let l:topmatters += s:level.appendix - let l:topmatters += s:level.backmatter - - for l:level in get(s:level, 'old', []) - let l:topmatters += l:level.frontmatter - let l:topmatters += l:level.mainmatter - let l:topmatters += l:level.appendix - let l:topmatters += l:level.backmatter - endfor - - return l:topmatters -endfunction - -" }}}1 -function! vimtex#parser#toc#get_entry_general(context) abort dict " {{{1 - return { - \ 'title' : self.title, - \ 'number' : '', - \ 'file' : a:context.file, - \ 'line' : a:context.lnum, - \ 'rank' : a:context.lnum_total, - \ 'level' : 0, - \ 'type' : 'content', - \} -endfunction - -" }}}1 - -" IMPORTANT: The following defines a prefilter for optimizing the toc parser. -" Any line that should be parsed has to be matched by this regexp! -" {{{1 let s:re_prefilter = ... -let s:re_prefilter = '\v%(\\' . join([ - \ '%(front|main|back)matter', - \ 'add%(global|section)?bib', - \ 'appendix', - \ 'begin', - \ 'bibliography', - \ 'chapter', - \ 'documentclass', - \ 'import', - \ 'include', - \ 'includegraphics', - \ 'input', - \ 'label', - \ 'part', - \ 'printbib', - \ 'printindex', - \ 'paragraph', - \ 'section', - \ 'subfile', - \ 'tableofcontents', - \ 'todo', - \], '|') . ')' - \ . '|\%\s*%(' . join(keys(g:vimtex_toc_todo_labels), '|') . ')' - \ . '|\%\s*vimtex-include' -for s:m in g:vimtex_toc_custom_matchers - if has_key(s:m, 'prefilter') - let s:re_prefilter .= '|' . s:m.prefilter - endif -endfor - -" }}}1 - -" Adds entries for included files -let s:matcher_include = { - \ 're' : vimtex#re#tex_input . '\zs\f{-}\s*\ze\}', - \ 'in_preamble' : 1, - \ 'priority' : 0, - \} -function! s:matcher_include.get_entry(context) abort dict " {{{1 - let l:file = matchstr(a:context.line, self.re) - if !vimtex#paths#is_abs(l:file[0]) - let l:file = b:vimtex.root . '/' . l:file - endif - let l:file = fnamemodify(l:file, ':~:.') - if !filereadable(l:file) - let l:file .= '.tex' - endif - return { - \ 'title' : 'tex incl: ' . (strlen(l:file) < 70 - \ ? l:file - \ : l:file[0:30] . '...' . l:file[-36:]), - \ 'number' : '', - \ 'file' : l:file, - \ 'line' : 1, - \ 'level' : a:context.max_level - a:context.level.current, - \ 'rank' : a:context.lnum_total, - \ 'type' : 'include', - \ } -endfunction - -" }}}1 - -" Adds entries for included graphics files (filetype tikz, tex) -let s:matcher_include_graphics = { - \ 're' : '\v^\s*\\includegraphics\*?%(\s*\[[^]]*\]){0,2}\s*\{\zs[^}]*', - \ 'priority' : 1, - \} -function! s:matcher_include_graphics.get_entry(context) abort dict " {{{1 - let l:file = matchstr(a:context.line, self.re) - if !vimtex#paths#is_abs(l:file) - let l:file = vimtex#misc#get_graphicspath(l:file) - endif - let l:file = fnamemodify(l:file, ':~:.') - let l:ext = fnamemodify(l:file, ':e') - - return !filereadable(l:file) || index(['asy', 'tikz'], l:ext) < 0 - \ ? {} - \ : { - \ 'title' : 'fig incl: ' . (strlen(l:file) < 70 - \ ? l:file - \ : l:file[0:30] . '...' . l:file[-36:]), - \ 'number' : '', - \ 'file' : l:file, - \ 'line' : 1, - \ 'level' : a:context.max_level - a:context.level.current, - \ 'rank' : a:context.lnum_total, - \ 'type' : 'include', - \ 'link' : 1, - \ } -endfunction - -" }}}1 - -" Adds entries for included files through vimtex specific syntax (this allows -" to add entries for any filetype or file) -let s:matcher_include_vimtex = { - \ 're' : '^\s*%\s*vimtex-include:\?\s\+\zs\f\+', - \ 'in_preamble' : 1, - \ 'priority' : 1, - \} -function! s:matcher_include_vimtex.get_entry(context) abort dict " {{{1 - let l:file = matchstr(a:context.line, self.re) - if !vimtex#paths#is_abs(l:file) - let l:file = b:vimtex.root . '/' . l:file - endif - let l:file = fnamemodify(l:file, ':~:.') - return { - \ 'title' : 'vtx incl: ' . (strlen(l:file) < 70 - \ ? l:file - \ : l:file[0:30] . '...' . l:file[-36:]), - \ 'number' : '', - \ 'file' : l:file, - \ 'line' : 1, - \ 'level' : a:context.max_level - a:context.level.current, - \ 'rank' : a:context.lnum_total, - \ 'type' : 'include', - \ 'link' : 1, - \ } -endfunction - -" }}}1 - -let s:matcher_include_bibtex = { - \ 're' : '\v^\s*\\bibliography\s*\{\zs[^}]+\ze\}', - \ 'in_preamble' : 1, - \ 'priority' : 0, - \} -function! s:matcher_include_bibtex.get_entry(context) abort dict " {{{1 - let l:entries = [] - - for l:file in split(matchstr(a:context.line, self.re), ',') - " Ensure that the file name has extension - if l:file !~# '\.bib$' - let l:file .= '.bib' - endif - - call add(l:entries, { - \ 'title' : printf('bib incl: %-.67s', fnamemodify(l:file, ':t')), - \ 'number' : '', - \ 'file' : vimtex#kpsewhich#find(l:file), - \ 'line' : 1, - \ 'level' : 0, - \ 'rank' : a:context.lnum_total, - \ 'type' : 'include', - \ 'link' : 1, - \}) - endfor - - return l:entries -endfunction - -" }}}1 - -let s:matcher_include_biblatex = { - \ 're' : '\v^\s*\\add(bibresource|globalbib|sectionbib)\s*\{\zs[^}]+\ze\}', - \ 'in_preamble' : 1, - \ 'in_content' : 0, - \ 'priority' : 0, - \} -function! s:matcher_include_biblatex.get_entry(context) abort dict " {{{1 - let l:file = matchstr(a:context.line, self.re) - - return { - \ 'title' : printf('bib incl: %-.67s', fnamemodify(l:file, ':t')), - \ 'number' : '', - \ 'file' : vimtex#kpsewhich#find(l:file), - \ 'line' : 1, - \ 'level' : 0, - \ 'rank' : a:context.lnum_total, - \ 'type' : 'include', - \ 'link' : 1, - \} -endfunction - -" }}}1 - -let s:matcher_preamble = { - \ 're' : '\v^\s*\\documentclass', - \ 'in_preamble' : 1, - \ 'in_content' : 0, - \ 'priority' : 0, - \} -function! s:matcher_preamble.get_entry(context) abort " {{{1 - return g:vimtex_toc_show_preamble - \ ? { - \ 'title' : 'Preamble', - \ 'number' : '', - \ 'file' : a:context.file, - \ 'line' : a:context.lnum, - \ 'level' : 0, - \ 'rank' : a:context.lnum_total, - \ 'type' : 'content', - \ } - \ : {} -endfunction - -" }}}1 - -let s:matcher_parts = { - \ 're' : '\v^\s*\\\zs((front|main|back)matter|appendix)>', - \ 'priority' : 0, - \} -function! s:matcher_parts.get_entry(context) abort dict " {{{1 - call a:context.level.reset( - \ matchstr(a:context.line, self.re), - \ a:context.max_level) - return {} -endfunction - -" }}}1 - -let s:matcher_sections = { - \ 're' : '\v^\s*\\%(part|chapter|%(sub)*section|%(sub)?paragraph)\*?\s*(\[|\{)', - \ 're_starred' : '\v^\s*\\%(part|chapter|%(sub)*section)\*', - \ 're_level' : '\v^\s*\\\zs%(part|chapter|%(sub)*section|%(sub)?paragraph)', - \ 'priority' : 0, - \} -let s:matcher_sections.re_title = s:matcher_sections.re . '\zs.{-}\ze\%?\s*$' -function! s:matcher_sections.get_entry(context) abort dict " {{{1 - let level = matchstr(a:context.line, self.re_level) - let type = matchlist(a:context.line, self.re)[1] - let title = matchstr(a:context.line, self.re_title) - let number = '' - - let [l:end, l:count] = s:find_closing(0, title, 1, type) - if l:count == 0 - let title = self.parse_title(strpart(title, 0, l:end+1)) - else - let self.type = type - let self.count = l:count - let s:matcher_continue = deepcopy(self) - endif - - if a:context.line !~# self.re_starred - call a:context.level.increment(level) - if a:context.line !~# '\v^\s*\\%(sub)?paragraph' - let number = deepcopy(a:context.level) - endif - endif - - return { - \ 'title' : title, - \ 'number' : number, - \ 'file' : a:context.file, - \ 'line' : a:context.lnum, - \ 'level' : a:context.max_level - a:context.level.current, - \ 'rank' : a:context.lnum_total, - \ 'type' : 'content', - \ } -endfunction - -" }}}1 -function! s:matcher_sections.parse_title(title) abort dict " {{{1 - let l:title = substitute(a:title, '\v%(\]|\})\s*$', '', '') - return s:clear_texorpdfstring(l:title) -endfunction - -" }}}1 -function! s:matcher_sections.continue(context) abort dict " {{{1 - let [l:end, l:count] = s:find_closing(0, a:context.line, self.count, self.type) - if l:count == 0 - let a:context.entry.title = self.parse_title(a:context.entry.title . strpart(a:context.line, 0, l:end+1)) - unlet! s:matcher_continue - else - let a:context.entry.title .= a:context.line - let self.count = l:count - endif -endfunction - -" }}}1 - -let s:matcher_table_of_contents = { - \ 'title' : 'Table of contents', - \ 're' : '\v^\s*\\tableofcontents', - \ 'priority' : 0, - \} - -let s:matcher_index = { - \ 'title' : 'Alphabetical index', - \ 're' : '\v^\s*\\printindex\[?', - \ 'priority' : 0, - \} - -let s:matcher_titlepage = { - \ 'title' : 'Titlepage', - \ 're' : '\v^\s*\\begin\{titlepage\}', - \ 'priority' : 0, - \} - -let s:matcher_bibliography = { - \ 'title' : 'Bibliography', - \ 're' : '\v^\s*\\%(' - \ . 'printbib%(liography|heading)\s*(\{|\[)?' - \ . '|begin\s*\{\s*thebibliography\s*\}' - \ . '|bibliography\s*\{)', - \ 're_biblatex' : '\v^\s*\\printbib%(liography|heading)', - \ 'priority' : 0, - \} -function! s:matcher_bibliography.get_entry(context) abort dict " {{{1 - let l:entry = call('vimtex#parser#toc#get_entry_general', [a:context], self) - - if a:context.line !~# self.re_biblatex - return l:entry - endif - - let self.options = matchstr(a:context.line, self.re_biblatex . '\s*\[\zs.*') - - let [l:end, l:count] = s:find_closing( - \ 0, self.options, !empty(self.options), '[') - if l:count == 0 - let self.options = strpart(self.options, 0, l:end) - call self.parse_options(a:context, l:entry) - else - let self.count = l:count - let s:matcher_continue = deepcopy(self) - endif - - return l:entry -endfunction - -" }}}1 -function! s:matcher_bibliography.continue(context) abort dict " {{{1 - let [l:end, l:count] = s:find_closing(0, a:context.line, self.count, '[') - if l:count == 0 - let self.options .= strpart(a:context.line, 0, l:end) - unlet! s:matcher_continue - call self.parse_options(a:context, a:context.entry) - else - let self.options .= a:context.line - let self.count = l:count - endif -endfunction - -" }}}1 -function! s:matcher_bibliography.parse_options(context, entry) abort dict " {{{1 - " Parse the options - let l:opt_pairs = map(split(self.options, ','), 'split(v:val, ''='')') - let l:opts = {} - for [l:key, l:val] in l:opt_pairs - let l:key = substitute(l:key, '^\s*\|\s*$', '', 'g') - let l:val = substitute(l:val, '^\s*\|\s*$', '', 'g') - let l:val = substitute(l:val, '{\|}', '', 'g') - let l:opts[l:key] = l:val - endfor - - " Check if entry should appear in the TOC - let l:heading = get(l:opts, 'heading') - let a:entry.added_to_toc = l:heading =~# 'intoc\|numbered' - - " Check if entry should be numbered - if l:heading =~# '\v%(sub)?bibnumbered' - if a:context.level.chapter > 0 - let l:levels = ['chapter', 'section'] - else - let l:levels = ['section', 'subsection'] - endif - call a:context.level.increment(l:levels[l:heading =~# '^sub']) - let a:entry.level = a:context.max_level - a:context.level.current - let a:entry.number = deepcopy(a:context.level) - endif - - " Parse title - try - let a:entry.title = remove(l:opts, 'title') - catch /E716/ - let a:entry.title = l:heading =~# '^sub' ? 'References' : 'Bibliography' - endtry -endfunction - -" }}}1 -function! s:matcher_bibliography.filter(entries) abort dict " {{{1 - if !empty( - \ filter(deepcopy(a:entries), 'get(v:val, "added_to_toc")')) - call filter(a:entries, 'get(v:val, "added_to_toc", 1)') - endif -endfunction - -" }}}1 - -let s:matcher_todos = { - \ 're' : g:vimtex#re#not_bslash . '\%\s+(' - \ . join(keys(g:vimtex_toc_todo_labels), '|') . ')[ :]+\s*(.*)', - \ 'in_preamble' : 1, - \ 'priority' : 2, - \} -function! s:matcher_todos.get_entry(context) abort dict " {{{1 - let [l:type, l:text] = matchlist(a:context.line, self.re)[1:2] - let l:label = g:vimtex_toc_todo_labels[toupper(l:type)] - - return { - \ 'title' : l:label . l:text, - \ 'number' : '', - \ 'file' : a:context.file, - \ 'line' : a:context.lnum, - \ 'level' : a:context.max_level - a:context.level.current, - \ 'rank' : a:context.lnum_total, - \ 'type' : 'todo', - \ } -endfunction - -" }}}1 - -let s:matcher_todonotes = { - \ 're' : g:vimtex#re#not_comment . '\\\w*todo\w*%(\[[^]]*\])?\{\zs.*', - \ 'priority' : 2, - \} -function! s:matcher_todonotes.get_entry(context) abort dict " {{{1 - let title = matchstr(a:context.line, self.re) - - let [l:end, l:count] = s:find_closing(0, title, 1, '{') - if l:count == 0 - let title = strpart(title, 0, l:end) - else - let self.count = l:count - let s:matcher_continue = deepcopy(self) - endif - - let l:label = get(g:vimtex_toc_todo_labels, 'TODO', 'TODO: ') - - return { - \ 'title' : l:label . title, - \ 'number' : '', - \ 'file' : a:context.file, - \ 'line' : a:context.lnum, - \ 'level' : a:context.max_level - a:context.level.current, - \ 'rank' : a:context.lnum_total, - \ 'type' : 'todo', - \ } -endfunction - -" }}}1 -function! s:matcher_todonotes.continue(context) abort dict " {{{1 - let [l:end, l:count] = s:find_closing(0, a:context.line, self.count, '{') - if l:count == 0 - let a:context.entry.title .= strpart(a:context.line, 0, l:end) - unlet! s:matcher_continue - else - let a:context.entry.title .= a:context.line - let self.count = l:count - endif -endfunction - -" }}}1 - -let s:matcher_labels = { - \ 're' : g:vimtex#re#not_comment . '\\label\{\zs.{-}\ze\}', - \ 'priority' : 1, - \} -function! s:matcher_labels.get_entry(context) abort dict " {{{1 - return { - \ 'title' : matchstr(a:context.line, self.re), - \ 'number' : '', - \ 'file' : a:context.file, - \ 'line' : a:context.lnum, - \ 'level' : a:context.max_level - a:context.level.current, - \ 'rank' : a:context.lnum_total, - \ 'type' : 'label', - \ } -endfunction -" }}}1 - -let s:matcher_beamer_frame = { - \ 're' : '^\s*\\begin{frame}', - \ 'priority' : 0, - \} -function! s:matcher_beamer_frame.get_entry(context) abort dict " {{{1 - let l:title = vimtex#util#trim( - \ matchstr(a:context.line, self.re . '\s*{\zs.*\ze}\s*$')) - - return { - \ 'title' : 'Frame' . (empty(l:title) ? '' : ': ' . l:title), - \ 'number' : '', - \ 'file' : a:context.file, - \ 'line' : a:context.lnum, - \ 'level' : a:context.max_level - a:context.level.current, - \ 'rank' : a:context.lnum_total, - \ 'type' : 'content', - \ } -endfunction -" }}}1 - -" -" Utility functions -" -function! s:clear_texorpdfstring(title) abort " {{{1 - let l:i1 = match(a:title, '\\texorpdfstring') - if l:i1 < 0 | return a:title | endif - - " Find start of included part - let [l:i2, l:dummy] = s:find_closing( - \ match(a:title, '{', l:i1+1), a:title, 1, '{') - let l:i2 = match(a:title, '{', l:i2+1) - if l:i2 < 0 | return a:title | endif - - " Find end of included part - let [l:i3, l:dummy] = s:find_closing(l:i2, a:title, 1, '{') - if l:i3 < 0 | return a:title | endif - - return strpart(a:title, 0, l:i1) - \ . strpart(a:title, l:i2+1, l:i3-l:i2-1) - \ . s:clear_texorpdfstring(strpart(a:title, l:i3+1)) -endfunction - -" }}}1 -function! s:find_closing(start, string, count, type) abort " {{{1 - if a:type ==# '{' - let l:re = '{\|}' - let l:open = '{' - else - let l:re = '\[\|\]' - let l:open = '[' - endif - let l:i2 = a:start-1 - let l:count = a:count - while l:count > 0 - let l:i2 = match(a:string, l:re, l:i2+1) - if l:i2 < 0 | break | endif - - if a:string[l:i2] ==# l:open - let l:count += 1 - else - let l:count -= 1 - endif - endwhile - - return [l:i2, l:count] -endfunction - -" }}}1 -function! s:sort_by_priority(d1, d2) abort " {{{1 - let l:p1 = get(a:d1, 'priority') - let l:p2 = get(a:d2, 'priority') - return l:p1 >= l:p2 ? l:p1 > l:p2 : -1 -endfunction - -" }}}1 - -" -" Section level counter -" -let s:level = {} -function! s:level.reset(part, level) abort dict " {{{1 - if a:part ==# 'preamble' - let self.old = [] - else - let self.old += [copy(self)] - endif - - let self.preamble = 0 - let self.frontmatter = 0 - let self.mainmatter = 0 - let self.appendix = 0 - let self.backmatter = 0 - let self.part = 0 - let self.chapter = 0 - let self.section = 0 - let self.subsection = 0 - let self.subsubsection = 0 - let self.subsubsubsection = 0 - let self.paragraph = 0 - let self.subparagraph = 0 - let self.current = a:level - let self[a:part] = 1 -endfunction - -" }}}1 -function! s:level.increment(level) abort dict " {{{1 - let self.current = s:sec_to_value[a:level] - - let self.part_toggle = 0 - - if a:level ==# 'part' - let self.part += 1 - let self.part_toggle = 1 - elseif a:level ==# 'chapter' - let self.chapter += 1 - let self.section = 0 - let self.subsection = 0 - let self.subsubsection = 0 - let self.subsubsubsection = 0 - let self.paragraph = 0 - let self.subparagraph = 0 - elseif a:level ==# 'section' - let self.section += 1 - let self.subsection = 0 - let self.subsubsection = 0 - let self.subsubsubsection = 0 - let self.paragraph = 0 - let self.subparagraph = 0 - elseif a:level ==# 'subsection' - let self.subsection += 1 - let self.subsubsection = 0 - let self.subsubsubsection = 0 - let self.paragraph = 0 - let self.subparagraph = 0 - elseif a:level ==# 'subsubsection' - let self.subsubsection += 1 - let self.subsubsubsection = 0 - let self.paragraph = 0 - let self.subparagraph = 0 - elseif a:level ==# 'subsubsubsection' - let self.subsubsubsection += 1 - let self.paragraph = 0 - let self.subparagraph = 0 - elseif a:level ==# 'paragraph' - let self.paragraph += 1 - let self.subparagraph = 0 - elseif a:level ==# 'subparagraph' - let self.subparagraph += 1 - endif -endfunction - -" }}}1 - -let s:sec_to_value = { - \ '_' : 0, - \ 'subparagraph' : 1, - \ 'paragraph' : 2, - \ 'subsubsubsection' : 3, - \ 'subsubsection' : 4, - \ 'subsection' : 5, - \ 'section' : 6, - \ 'chapter' : 7, - \ 'part' : 8, - \ } - -" -" Create the lists of matchers -" -let s:matchers = map( - \ filter(items(s:), 'v:val[0] =~# ''^matcher_'''), - \ 'v:val[1]') - \ + g:vimtex_toc_custom_matchers -call sort(s:matchers, function('s:sort_by_priority')) - -for s:m in s:matchers - if !has_key(s:m, 'get_entry') - let s:m.get_entry = function('vimtex#parser#toc#get_entry_general') - endif -endfor -unlet! s:m - -let s:matchers_preamble = filter( - \ deepcopy(s:matchers), "get(v:val, 'in_preamble')") -let s:matchers_content = filter( - \ deepcopy(s:matchers), "get(v:val, 'in_content', 1)") - -endif |