summaryrefslogtreecommitdiffstats
path: root/autoload/vimtex/doc.vim
diff options
context:
space:
mode:
Diffstat (limited to 'autoload/vimtex/doc.vim')
-rw-r--r--autoload/vimtex/doc.vim251
1 files changed, 251 insertions, 0 deletions
diff --git a/autoload/vimtex/doc.vim b/autoload/vimtex/doc.vim
new file mode 100644
index 00000000..dd42a6ed
--- /dev/null
+++ b/autoload/vimtex/doc.vim
@@ -0,0 +1,251 @@
+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#doc#init_buffer() abort " {{{1
+ command! -buffer -nargs=? VimtexDocPackage call vimtex#doc#package(<q-args>)
+
+ nnoremap <buffer> <plug>(vimtex-doc-package) :VimtexDocPackage<cr>
+endfunction
+
+" }}}1
+
+function! vimtex#doc#package(word) abort " {{{1
+ let l:context = empty(a:word)
+ \ ? s:packages_get_from_cursor()
+ \ : {
+ \ 'type': 'word',
+ \ 'candidates': [a:word],
+ \ }
+ if empty(l:context) | return | endif
+
+ call s:packages_remove_invalid(l:context)
+
+ for l:handler in g:vimtex_doc_handlers
+ if exists('*' . l:handler)
+ if call(l:handler, [l:context]) | return | endif
+ endif
+ endfor
+
+ call s:packages_open(l:context)
+endfunction
+
+" }}}1
+function! vimtex#doc#make_selection(context) abort " {{{1
+ if has_key(a:context, 'selected') | return | endif
+
+ if len(a:context.candidates) == 0
+ if exists('a:context.name')
+ echohl ErrorMsg
+ echo 'Sorry, no doc for '.a:context.name
+ echohl NONE
+ endif
+ let a:context.selected = ''
+ return
+ endif
+
+ if len(a:context.candidates) == 1
+ let a:context.selected = a:context.candidates[0]
+ return
+ endif
+
+ call vimtex#echo#echo('Multiple candidates detected, please select one:')
+ let l:count = 0
+ for l:package in a:context.candidates
+ let l:count += 1
+ call vimtex#echo#formatted([
+ \ ' [' . string(l:count) . '] ',
+ \ ['VimtexSuccess', l:package]
+ \])
+ endfor
+
+ call vimtex#echo#echo('Type number (everything else cancels): ')
+ let l:choice = nr2char(getchar())
+ if l:choice !~# '\d'
+ \ || l:choice == 0
+ \ || l:choice > len(a:context.candidates)
+ echohl VimtexWarning
+ echon l:choice =~# '\d' ? l:choice : '-'
+ echohl NONE
+ let a:context.selected = ''
+ else
+ echon l:choice
+ let a:context.selected = a:context.candidates[l:choice-1]
+ let a:context.ask_before_open = 0
+ endif
+endfunction
+
+" }}}1
+
+function! s:packages_get_from_cursor() abort " {{{1
+ let l:cmd = vimtex#cmd#get_current()
+ if empty(l:cmd) | return {} | endif
+
+ if l:cmd.name ==# '\usepackage'
+ return s:packages_from_usepackage(l:cmd)
+ elseif l:cmd.name ==# '\documentclass'
+ return s:packages_from_documentclass(l:cmd)
+ else
+ return s:packages_from_command(strpart(l:cmd.name, 1))
+ endif
+endfunction
+
+" }}}1
+function! s:packages_from_usepackage(cmd) abort " {{{1
+ try
+ " Gather and clean up candidate list
+ let l:candidates = substitute(a:cmd.args[0].text, '%.\{-}\n', '', 'g')
+ let l:candidates = substitute(l:candidates, '\s*', '', 'g')
+ let l:candidates = split(l:candidates, ',')
+
+ let l:context = {
+ \ 'type': 'usepackage',
+ \ 'candidates': l:candidates,
+ \}
+
+ let l:cword = expand('<cword>')
+ if len(l:context.candidates) > 1 && index(l:context.candidates, l:cword) >= 0
+ let l:context.selected = l:cword
+ endif
+
+ return l:context
+ catch
+ call vimtex#log#warning('Could not parse the package from \usepackage!')
+ return {}
+ endtry
+endfunction
+
+" }}}1
+function! s:packages_from_documentclass(cmd) abort " {{{1
+ try
+ return {
+ \ 'type': 'documentclass',
+ \ 'candidates': [a:cmd.args[0].text],
+ \}
+ catch
+ call vimtex#log#warning('Could not parse the package from \documentclass!')
+ return {}
+ endtry
+endfunction
+
+" }}}1
+function! s:packages_from_command(cmd) abort " {{{1
+ let l:packages = [
+ \ 'default',
+ \ 'class-' . get(b:vimtex, 'documentclass', ''),
+ \] + keys(b:vimtex.packages)
+ call filter(l:packages, 'filereadable(s:complete_dir . v:val)')
+
+ let l:queue = copy(l:packages)
+ while !empty(l:queue)
+ let l:current = remove(l:queue, 0)
+ let l:includes = filter(readfile(s:complete_dir . l:current), 'v:val =~# ''^\#\s*include:''')
+ if empty(l:includes) | continue | endif
+
+ call map(l:includes, 'matchstr(v:val, ''include:\s*\zs.*\ze\s*$'')')
+ call filter(l:includes, 'filereadable(s:complete_dir . v:val)')
+ call filter(l:includes, 'index(l:packages, v:val) < 0')
+
+ let l:packages += l:includes
+ let l:queue += l:includes
+ endwhile
+
+ let l:candidates = []
+ let l:filter = 'v:val =~# ''^' . a:cmd . '\>'''
+ for l:package in l:packages
+ let l:cmds = filter(readfile(s:complete_dir . l:package), l:filter)
+ if empty(l:cmds) | continue | endif
+
+ if l:package ==# 'default'
+ call extend(l:candidates, ['latex2e', 'lshort'])
+ else
+ call add(l:candidates, substitute(l:package, '^class-', '', ''))
+ endif
+ endfor
+
+ return {
+ \ 'type': 'command',
+ \ 'name': a:cmd,
+ \ 'candidates': l:candidates,
+ \}
+endfunction
+
+" }}}1
+function! s:packages_remove_invalid(context) abort " {{{1
+ let l:invalid_packages = filter(copy(a:context.candidates),
+ \ 'empty(vimtex#kpsewhich#find(v:val . ''.sty'')) && '
+ \ . 'empty(vimtex#kpsewhich#find(v:val . ''.cls''))')
+
+ call filter(l:invalid_packages,
+ \ 'index([''latex2e'', ''lshort''], v:val) < 0')
+
+ " Warn about invalid candidates
+ if !empty(l:invalid_packages)
+ if len(l:invalid_packages) == 1
+ call vimtex#log#warning(
+ \ 'Package not recognized: ' . l:invalid_packages[0])
+ else
+ call vimtex#log#warning(
+ \ 'Packages not recognized:',
+ \ map(copy(l:invalid_packages), "'- ' . v:val"))
+ endif
+ endif
+
+ " Remove invalid candidates
+ call filter(a:context.candidates, 'index(l:invalid_packages, v:val) < 0')
+
+ " Reset the selection if the selected candidate is not valid
+ if has_key(a:context, 'selected')
+ \ && index(a:context.candidates, a:context.selected) < 0
+ unlet a:context.selected
+ endif
+endfunction
+
+" }}}1
+function! s:packages_open(context) abort " {{{1
+ if !has_key(a:context, 'selected')
+ call vimtex#doc#make_selection(a:context)
+ endif
+
+ if empty(a:context.selected) | return | endif
+
+ if get(a:context, 'ask_before_open', 1)
+ call vimtex#echo#formatted([
+ \ 'Open documentation for ',
+ \ ['VimtexSuccess', a:context.selected], ' [y/N]? '
+ \])
+
+ let l:choice = nr2char(getchar())
+ if l:choice ==# 'y'
+ echon 'y'
+ else
+ echohl VimtexWarning
+ echon l:choice =~# '\w' ? l:choice : 'N'
+ echohl NONE
+ return
+ endif
+ endif
+
+ let l:os = vimtex#util#get_os()
+ let l:url = 'http://texdoc.net/pkg/' . a:context.selected
+
+ silent execute (l:os ==# 'linux'
+ \ ? '!xdg-open'
+ \ : (l:os ==# 'mac'
+ \ ? '!open'
+ \ : '!start'))
+ \ . ' ' . l:url
+ \ . (l:os ==# 'win' ? '' : ' &')
+
+ redraw!
+endfunction
+
+" }}}1
+
+let s:complete_dir = fnamemodify(expand('<sfile>'), ':h') . '/complete/'
+
+endif