commit 7808e59239fa6d0eeacad99fd86366225c14632b Author: Karl Yngve Lervåg Date: Sat Oct 5 13:53:42 2013 +0200 Initial revision diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..926ccaa --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +doc/tags diff --git a/README.md b/README.md new file mode 100644 index 0000000..22b8150 --- /dev/null +++ b/README.md @@ -0,0 +1,47 @@ +# vim-latex +## Introduction + +There exists several LaTeX plugins for vim, for instance: +- LaTeX-Suite: vimscript#475 +- AutomaticTexPlugin: vimscript#2945 +- LaTeX-Box: vimscript#3109 + +I have been using both LaTeX-Suite and LaTeX-Box myself, but I found both of +these to be relatively bulky and difficult to manage and extend. LaTeX-Box +was supposed to be simple and lightweight, and I think it was close to being +just that. However, after having worked on it for some time, I felt that much +of the simplicity could be improved by a complete restructuring. + +Enter vim-latex, which is a lightweight and simple plugin that provides LaTeX +support for vim. It has most of the functionality of LaTeX-Box, but the idea +is to combine vim-latex with the strength of other plugins. I personally +recommend [UltiSnips](https://github.com/SirVer/ultisnips) for snippets and +[neocomplete](https://github.com/Shougo/neocomplete.vim) for completion. + +Read the documentation for a more thorough introduction. + +## Installation +### With gmarik vundle +_https://github.com/gmarik/vundle_ + +Add `Bundle 'lervag/vim-latex'` to your ~/.vimrc and run +`:BundleInstall` in a vim buffer. Add `!` to the command to update. + +### With neobundle +_https://github.com/Shougo/neobundle.vim_ + +Add `NeoBundle 'lervag/vim-latex'` to your ~/.vimrc and run +`:NeoBundleInstall` in a vim buffer. Add `!` to the command to update. + +### With pathogen +_https://github.com/tpope/vim-pathogen_ + +Add the vim-latex bundle to your bundle directory, for instance with `git +clone`. This will typically be enough: + + cd ~/.vim/bundle + git clone git://github.com/lervag/vim-latex + +### Without a plugin manager + +Copy the directories to your `.vim/` folder. diff --git a/after/syntax/tex.vim b/after/syntax/tex.vim new file mode 100644 index 0000000..450a044 --- /dev/null +++ b/after/syntax/tex.vim @@ -0,0 +1,5 @@ +" Add support for cleverref package (`\cref` and `\Cref`) +syn region texRefZone matchgroup=texStatement + \ start="\\\(c\|C\)ref{" + \ end="}\|%stopzone\>" + \ contains=@texRefGroup diff --git a/autoload/latex.vim b/autoload/latex.vim new file mode 100644 index 0000000..d7a4e33 --- /dev/null +++ b/autoload/latex.vim @@ -0,0 +1,263 @@ +" {{{1 latex#init +let s:initialized = 0 +function! latex#init() + call s:init_environment() + call s:init_errorformat() + + call latex#toc#init(s:initialized) + call latex#fold#init(s:initialized) + call latex#motion#init(s:initialized) + call latex#change#init(s:initialized) + call latex#latexmk#init(s:initialized) + call latex#complete#init(s:initialized) + + " + " This variable is used to allow a distinction between global and buffer + " initialization + " + let s:initialized = 1 +endfunction + +" {{{1 latex#info +function! latex#info() + echo "Buffer data" + echo printf(' id: %-s', b:latex.id) + echo printf('fold sections: %-s', string(b:latex.fold_sections)) + echo "\n" + + echo "Latex blobs" + let n = -1 + for data in g:latex#data + let n += 1 + if n > 0 + echo "\n" + endif + let d = copy(data) + let d.aux = d.aux() + let d.out = d.out() + let d.log = d.log() + echo printf('%6s: %-s', 'id', n) + for [key, val] in sort(items(d), "s:info_sort_func") + if key =~ '\vaux|out|root|log|tex' + let val = s:truncate(val) + endif + echo printf('%6s: %-s', key, val) + endfor + endfor +endfunction + +" {{{1 latex#help +function! latex#help() + if g:latex_default_mappings + echo "Latex mappings" + nmap + vmap + omap + endif +endfunction + +" {{{1 latex#reinit +function! latex#reinit() + " + " Stop latexmk processes (if running) + " + call latex#latexmk#stop_all() + + " + " Reset variables + " + let s:initialized = 0 + unlet g:latex#data + bufdo unlet b:notbslash + bufdo unlet b:notcomment + bufdo unlet b:latex + + " + " Reinitialize + " + bufdo call latex#init() +endfunction + +" {{{1 latex#view +function! latex#view() + let outfile = g:latex#data[b:latex.id].out() + if !filereadable(outfile) + echomsg "Can't view: Output file is not readable!" + return + endif + + silent execute '!' . g:latex_viewer . ' ' . outfile . ' &>/dev/null &' + if !has("gui_running") + redraw! + endif +endfunction +" }}}1 + +" {{{1 s:init_environment +function! s:init_environment() + " + " Initialize global and local data blobs + " + call latex#util#set_default('g:latex#data', []) + call latex#util#set_default('b:latex', {}) + + " + " Initialize some common patterns + " + call latex#util#set_default('b:notbslash', '\%(\\\@= 0 + let b:latex.id = id + else + let data = {} + let data.tex = main + let data.root = fnamemodify(data.tex, ':h') + let data.base = fnamemodify(data.tex, ':t') + let data.name = fnamemodify(data.tex, ':t:r') + function data.aux() dict + return s:get_main_ext(self, 'aux') + endfunction + function data.log() dict + return s:get_main_ext(self, 'log') + endfunction + function data.out() dict + return s:get_main_ext(self, g:latex_latexmk_output) + endfunction + + call add(g:latex#data, data) + let b:latex.id = len(g:latex#data) - 1 + endif + + if g:latex_default_mappings + nnoremap li :call latex#info() + nnoremap lh :call latex#help() + nnoremap lv :call latex#view() + nnoremap lR :call latex#reinit() + endif +endfunction + +" {{{1 s:init_errorformat +function! s:init_errorformat() + " + " Note: The error formats assume we're using the -file-line-error with + " [pdf]latex. For more info, see |errorformat-LaTeX|. + " + + " Push file to file stack + setlocal efm+=%+P**%f + + " Match errors + setlocal efm=%E!\ LaTeX\ %trror:\ %m + setlocal efm+=%E%f:%l:\ %m + setlocal efm+=%E!\ %m + + " More info for undefined control sequences + setlocal efm+=%Z\ %m + + " Show warnings + if g:latex_errorformat_show_warnings + " Ignore some warnings + for w in g:latex_errorformat_ignore_warnings + let warning = escape(substitute(w, '[\,]', '%\\\\&', 'g'), ' ') + exe 'setlocal efm+=%-G%.%#'. warning .'%.%#' + endfor + setlocal efm+=%+WLaTeX\ %.%#Warning:\ %.%#line\ %l%.%# + setlocal efm+=%+W%.%#\ at\ lines\ %l--%*\\d + setlocal efm+=%+WLaTeX\ %.%#Warning:\ %m + setlocal efm+=%+W%.%#%.%#Warning:\ %m + endif + + " Ignore unmatched lines + setlocal efm+=%-G%.%# +endfunction +" }}}1 + +" {{{1 s:get_id +function! s:get_id(main) + if exists('g:latex#data') && !empty(g:latex#data) + let id = 0 + while id < len(g:latex#data) + if g:latex#data[id].tex == a:main + return id + endif + let id += 1 + endwhile + endif + + return -1 +endfunction + +" {{{1 s:get_main_tex +function! s:get_main_tex() + if !search('\C\\begin\_\s*{document}', 'nw') + let tex_files = glob('*.tex', 0, 1) + glob('../*.tex', 0, 1) + call filter(tex_files, + \ "count(g:latex_main_tex_candidates, fnamemodify(v:val,':t:r'))") + if !empty(tex_files) + return fnamemodify(tex_files[0], ':p') + endif + endif + + return expand('%:p') +endfunction + +" {{{1 s:get_main_ext +function! s:get_main_ext(texdata, ext) + " Create set of candidates + let candidates = [ + \ a:texdata.name, + \ g:latex_build_dir . '/' . a:texdata.name, + \ ] + + " Search through the candidates + for f in map(candidates, + \ 'a:texdata.root . ''/'' . v:val . ''.'' . a:ext') + if filereadable(f) + return fnamemodify(f, ':p') + endif + endfor + + " Return empty string if no entry is found + return '' +endfunction + +" {{{1 s:info_sort_func +function! s:info_sort_func(a, b) + if a:a[1][0] == "!" + " Put cmd's way behind + return 1 + elseif a:b[1][0] == "!" + " Put cmd's way behind + return -1 + elseif a:a[1][0] == "/" && a:b[1][0] != "/" + " Put full paths behind + return 1 + elseif a:a[1][0] != "/" && a:b[1][0] == "/" + " Put full paths behind + return -1 + elseif a:a[1][0] == "/" && a:b[1][0] == "/" + " Put full paths behind + return -1 + else + return a:a[1] > a:b[1] ? 1 : -1 + endif +endfunction + +" {{{1 s:truncate +function! s:truncate(string) + if len(a:string) >= winwidth('.') - 9 + return a:string[0:10] . "..." . a:string[-winwidth('.')+23:] + else + return a:string + endif +endfunction +" }}}1 + +" vim:fdm=marker:ff=unix diff --git a/autoload/latex/change.vim b/autoload/latex/change.vim new file mode 100644 index 0000000..04abf7d --- /dev/null +++ b/autoload/latex/change.vim @@ -0,0 +1,153 @@ +" {{{1 latex#change#init +function! latex#change#init(initialized) + if g:latex_default_mappings + nnoremap + \ :call latex#change#environment_prompt() + nnoremap + \ :call latex#change#environment_toggle_star() + endif +endfunction + +" {{{1 latex#change#environment +function! latex#change#environment(new_env) + let [env, l1, c1, l2, c2] = latex#util#get_env(1) + + if a:new_env == '\[' || a:new_env == '[' + let beg = '\[' + let end = '\]' + let n1 = 1 + let n2 = 1 + elseif a:new_env == '\(' || a:new_env == '(' + let beg = '\(' + let end = '\)' + let n1 = 1 + let n2 = 1 + else + let beg = '\begin{' . a:new_env . '}' + let end = '\end{' . a:new_env . '}' + let n1 = len(env) + 7 + let n2 = len(env) + 5 + endif + + let line = getline(l1) + let line = strpart(line, 0, c1 - 1) . l:beg . strpart(line, c1 + n1) + call setline(l1, line) + let line = getline(l2) + let line = strpart(line, 0, c2 - 1) . l:end . strpart(line, c2 + n2) + call setline(l2, line) +endfunction + +" {{{1 latex#change#environment_prompt +function! latex#change#environment_prompt() + let new_env = input('Change ' . latex#util#get_env() . ' for: ', '', + \ 'customlist,' . s:sidwrap('input_complete')) + if empty(new_env) + return + else + call latex#change#environment(new_env) + endif +endfunction + +" {{{1 latex#change#environment_toggle_star +function! latex#change#environment_toggle_star() + let env = latex#util#get_env() + + if env == '\(' + return + elseif env == '\[' + let new_env = equation + elseif env[-1:] == '*' + let new_env = env[:-2] + else + let new_env = env . '*' + endif + + call latex#change#environment(new_env) +endfunction + +" {{{1 latex#change#wrap_selection +function! latex#change#wrap_selection(wrapper) + keepjumps normal! `>a} + execute 'keepjumps normal! `o\end{' . env . '}' + execute 'keepjumps normal! ` + normal! gvgq + endif + else + execute 'keepjumps normal! `>a\end{' . env . '}' + execute 'keepjumps normal! `'), '\zs\d\+_\ze.*$') +function! s:sidwrap(func) + return s:SID . a:func +endfunction + +" {{{1 s:input_complete +function! s:input_complete(lead, cmdline, pos) + let suggestions = [] + for entry in g:latex_complete_environments + let env = entry.word + if env =~ '^' . a:lead + call add(suggestions, env) + endif + endfor + return suggestions +endfunction + +" {{{1 s:search_and_skip_comments +function! s:search_and_skip_comments(pat, ...) + " Usage: s:search_and_skip_comments(pat, [flags, stopline]) + let flags = a:0 >= 1 ? a:1 : '' + let stopline = a:0 >= 2 ? a:2 : 0 + let saved_pos = getpos('.') + + " search once + let ret = search(a:pat, flags, stopline) + + if ret + " do not match at current position if inside comment + let flags = substitute(flags, 'c', '', 'g') + + " keep searching while in comment + while latex#util#in_comment() + let ret = search(a:pat, flags, stopline) + if !ret + break + endif + endwhile + endif + + if !ret + " if no match found, restore position + call setpos('.', saved_pos) + endif + + return ret +endfunction +" }}}1 Modeline + +" vim:fdm=marker:ff=unix diff --git a/autoload/latex/complete.vim b/autoload/latex/complete.vim new file mode 100644 index 0000000..8bb47fd --- /dev/null +++ b/autoload/latex/complete.vim @@ -0,0 +1,312 @@ +" {{{1 latex#complete#init +function! latex#complete#init(initialized) + if g:latex_complete_enabled + setlocal omnifunc=latex#complete#omnifunc + endif +endfunction + +" {{{1 latex#complete#omnifunc +let s:completion_type = '' +function! latex#complete#omnifunc(findstart, base) + if a:findstart + " + " First call: Find start of text to be completed + " + " Note: g:latex_complete_patterns is a dictionary where the keys are the + " types of completion and the values are the patterns that must match for + " the given type. Currently, it completes labels (e.g. \ref{...), bibtex + " entries (e.g. \cite{...) and commands (e.g. \...). + " + let line = getline('.') + let pos = col('.') - 1 + for [type, pattern] in items(g:latex_complete_patterns) + if line =~ pattern . '$' + let s:completion_type = type + while pos > 0 && line[pos - 1] !~ '{\|,' + let pos -= 1 + endwhile + return pos > 0 ? pos : -2 + endif + endfor + else + " + " Second call: Find list of matches + " + if s:completion_type == 'ref' + return latex#complete#labels(a:base) + elseif s:completion_type == 'bib' + return latex#complete#bibtex(a:base) + endif + endif +endfunction + +" {{{1 latex#complete#labels +function! latex#complete#labels(regex) + let labels = s:labels_get(g:latex#data[b:latex.id].aux()) + let matches = filter(copy(labels), 'v:val[0] =~ ''' . a:regex . '''') + + " Try to match label and number + if empty(matches) + let regex_split = split(a:regex) + if len(regex_split) > 1 + let base = regex_split[0] + let number = escape(join(regex_split[1:], ' '), '.') + let matches = filter(copy(labels), + \ 'v:val[0] =~ ''' . base . ''' &&' . + \ 'v:val[1] =~ ''' . number . '''') + endif + endif + + " Try to match number + if empty(matches) + let matches = filter(copy(labels), 'v:val[1] =~ ''' . a:regex . '''') + endif + + let suggestions = [] + for m in matches + let entry = { + \ 'word': m[0], + \ 'menu': printf("%7s [p. %s]", '('.m[1].')', m[2]) + \ } + if g:latex_complete_close_braces && !s:next_chars_match('^\s*[,}]') + let entry = copy(entry) + let entry.abbr = entry.word + let entry.word = entry.word . '}' + endif + call add(suggestions, entry) + endfor + + return suggestions +endfunction + +" {{{1 latex#complete#bibtex +function! latex#complete#bibtex(regexp) + let res = [] + + for m in s:bibtex_search(a:regexp) + let type = m['type'] == '' ? '[-]' : '[' . m['type'] . '] ' + let auth = m['author'] == '' ? '' : m['author'][:20] . ' ' + let year = m['year'] == '' ? '' : '(' . m['year'] . ')' + let w = { + \ 'word': m['key'], + \ 'abbr': type . auth . year, + \ 'menu': m['title'] + \ } + + " Close braces if desired + if g:latex_complete_close_braces && !s:next_chars_match('^\s*[,}]') + let w.word = w.word . '}' + endif + + call add(res, w) + endfor + + return res +endfunction +" }}}1 + +" {{{1 s:bibtex_search +let s:bstfile = expand(':p:h') . '/vimcomplete' +function! s:bibtex_search(regexp) + let res = [] + + " Find data from external bib files + let bibdata = join(s:bibtex_find_bibs(), ',') + if bibdata != '' + let tmp = { + \ 'aux' : 'tmpfile.aux', + \ 'bbl' : 'tmpfile.bbl', + \ 'blg' : 'tmpfile.blg', + \ } + + " Write temporary aux file + call writefile([ + \ '\citation{*}', + \ '\bibstyle{' . s:bstfile . '}', + \ '\bibdata{' . bibdata . '}', + \ ], tmp.aux) + + " Create temporary bbl file + silent execute '!bibtex -terse ' . tmp.aux . ' >/dev/null' + if !has('gui_running') + redraw! + endif + + " Parse temporary bbl file + let lines = split(substitute(join(readfile(tmp.bbl), "\n"), + \ '\n\n\@!\(\s\=\)\s*\|{\|}', '\1', 'g'), "\n") + + for line in filter(lines, 'v:val =~ a:regexp') + let matches = matchlist(line, + \ '^\(.*\)||\(.*\)||\(.*\)||\(.*\)||\(.*\)') + if !empty(matches) && !empty(matches[1]) + call add(res, { + \ 'key': matches[1], + \ 'type': matches[2], + \ 'author': matches[3], + \ 'year': matches[4], + \ 'title': matches[5], + \ }) + endif + endfor + + call delete(tmp.aux) + call delete(tmp.bbl) + call delete(tmp.blg) + endif + + " Find data from 'thebibliography' environments + let lines = readfile(g:latex#data[b:latex.id].tex) + if match(lines, '\C\\begin{thebibliography}') + for line in filter(filter(lines, 'v:val =~ ''\C\\bibitem'''), + \ 'v:val =~ a:regexp') + let match = matchlist(line, '\\bibitem{\([^}]*\)')[1] + call add(res, { + \ 'key': match, + \ 'type': '', + \ 'author': '', + \ 'year': '', + \ 'title': match, + \ }) + endfor + endif + + return res +endfunction + +" {{{1 s:bibtex_find_bibs +function! s:bibtex_find_bibs(...) + if a:0 + let file = a:1 + else + let file = g:latex#data[b:latex.id].tex + endif + + if !filereadable(file) + return '' + endif + let lines = readfile(file) + let bibdata_list = [] + + " + " Search for added bibliographies + " + let bibliography_cmds = [ + \ '\\bibliography', + \ '\\addbibresource', + \ '\\addglobalbib', + \ '\\addsectionbib', + \ ] + for cmd in bibliography_cmds + let filter = 'v:val =~ ''\C' . cmd . '\s*{[^}]\+}''' + let map = 'matchstr(v:val, ''\C' . cmd . '\s*{\zs[^}]\+\ze}'')' + let bibdata_list += map(filter(copy(lines), filter), map) + endfor + + " + " Also search included files + " + for input in filter(lines, 'v:val =~ ''\C\\\%(input\|include\)\s*{[^}]\+}''') + let bibdata_list += s:bibtex_find_bibs(latex#util#kpsewhich( + \ matchstr(input, '\C\\\%(input\|include\)\s*{\zs[^}]\+\ze}'))) + endfor + + " + " Make all entries full paths + " + return bibdata_list +endfunction + +" {{{1 s:labels_cache +" +" s:label_cache is a dictionary that maps filenames to tuples of the form +" +" [ time, labels, inputs ] +" +" where time is modification time of the cache entry, labels is a list like +" returned by extract_labels, and inputs is a list like returned by +" s:extract_inputs. +" + +let s:label_cache = {} + +" {{{1 s:labels_get +function! s:labels_get(file) + " + " s:labels_get compares modification time of each entry in the label cache + " and updates it if necessary. During traversal of the label cache, all + " current labels are collected and returned. + " + if !filereadable(a:file) + return [] + endif + + " Open file in temporary split window for label extraction. + if !has_key(s:label_cache , a:file) + \ || s:label_cache[a:file][0] != getftime(a:file) + let s:label_cache[a:file] = [ + \ getftime(a:file), + \ s:labels_extract(a:file), + \ s:labels_extract_inputs(a:file), + \ ] + endif + + " We need to create a copy of s:label_cache[fid][1], otherwise all inputs' + " labels would be added to the current file's label cache upon each + " completion call, leading to duplicates/triplicates/etc. and decreased + " performance. Also, because we don't anything with the list besides + " matching copies, we can get away with a shallow copy for now. + let labels = copy(s:label_cache[a:file][1]) + + for input in s:label_cache[a:file][2] + let labels += s:labels_get(input) + endfor + + return labels +endfunction + +" {{{1 s:labels_extract +function! s:labels_extract(file) + " + " Searches file for commands of the form + " + " \newlabel{name}{{number}{page}.*}.* + " + " and returns a list of [name, number, page] tuples. + " + let matches = [] + let lines = readfile(a:file) + let lines = filter(lines, 'v:val =~ ''\\newlabel{''') + let lines = filter(lines, 'v:val !~ ''@cref''') + let lines = map(lines, 'latex#util#convert_back(v:val)') + for line in lines + let tree = latex#util#tex2tree(line) + call add(matches, [ + \ latex#util#tree2tex(tree[1][0]), + \ latex#util#tree2tex(tree[2][0][0]), + \ latex#util#tree2tex(tree[2][1][0]), + \ ]) + endfor + return matches +endfunction + +" {{{1 s:labels_extract_inputs +function! s:labels_extract_inputs(file) + " + " Searches file for \@input{file} entries and returns list of all files. + " + let matches = [] + for line in filter(readfile(a:file), 'v:val =~ ''\\@input{''') + call add(matches, matchstr(line, '{\zs.*\ze}')) + endfor + return matches +endfunction +" }}}1 + +" {{{1 s:next_chars_match +function! s:next_chars_match(regex) + return strpart(getline('.'), col('.') - 1) =~ a:regex +endfunction +" }}}1 + +" vim:fdm=marker:ff=unix diff --git a/autoload/latex/fold.vim b/autoload/latex/fold.vim new file mode 100644 index 0000000..4088c06 --- /dev/null +++ b/autoload/latex/fold.vim @@ -0,0 +1,291 @@ +" {{{1 latex#fold#init +function! latex#fold#init(initialized) + if g:latex_fold_enabled + setl foldmethod=expr + setl foldexpr=latex#fold#level(v:lnum) + setl foldtext=latex#fold#text() + call latex#fold#refresh() + + if g:latex_default_mappings + nnoremap zx :call latex#fold#refresh()zx + endif + + " + " The foldexpr function returns "=" for most lines, which means it can + " become slow for large files. The following is a hack that is based on + " this reply to a discussion on the Vim Developer list: + " http://permalink.gmane.org/gmane.editors.vim.devel/14100 + " + if !a:initialized + augroup latex_fold + autocmd! + autocmd InsertEnter *.tex setlocal foldmethod=manual + autocmd InsertLeave *.tex setlocal foldmethod=expr + augroup END + endif + endif +endfunction + +" {{{1 latex#fold#refresh +function! latex#fold#refresh() + " Parse tex file to dynamically set the sectioning fold levels + let b:latex.fold_sections = s:find_fold_sections() +endfunction + +" {{{1 latex#fold#level +function! latex#fold#level(lnum) + " Check for normal lines first (optimization) + let line = getline(a:lnum) + if line !~ '\(% Fake\|\\\(document\|begin\|end\|' + \ . 'front\|main\|back\|app\|sub\|section\|chapter\|part\)\)' + return "=" + endif + + " Fold preamble + if g:latex_fold_preamble + if line =~# '\s*\\documentclass' + return ">1" + elseif line =~# '^\s*\\begin\s*{\s*document\s*}' + return "0" + endif + endif + + " Fold parts (\frontmatter, \mainmatter, \backmatter, and \appendix) + if line =~# '^\s*\\\%(' . join(g:latex_fold_parts, '\|') . '\)' + return ">1" + endif + + " Fold chapters and sections + for [part, level] in b:latex.fold_sections + if line =~# part + return ">" . level + endif + endfor + + " Never fold \end{document} + if line =~# '^\s*\\end{document}' + return 0 + endif + + " Fold environments + if g:latex_fold_envs + if line =~# b:notcomment . b:notbslash . '\\begin\s*{.\{-}}' + return "a1" + elseif line =~# b:notcomment . b:notbslash . '\\end\s*{.\{-}}' + return "s1" + endif + endif + + " Return foldlevel of previous line + return "=" +endfunction + +" {{{1 latex#fold#text +function! latex#fold#text() + " Initialize + let line = getline(v:foldstart) + let nlines = v:foldend - v:foldstart + 1 + let level = '' + let title = 'Not defined' + + " Fold level + let level = strpart(repeat('-', v:foldlevel-1) . '*',0,3) + if v:foldlevel > 3 + let level = strpart(level, 1) . v:foldlevel + endif + let level = printf('%-3s', level) + + " Preamble + if line =~ '\s*\\documentclass' + let title = "Preamble" + endif + + " Parts, sections and fakesections + let sections = '\(\(sub\)*section\|part\|chapter\)' + let secpat1 = '^\s*\\' . sections . '\*\?\s*{' + let secpat2 = '^\s*\\' . sections . '\*\?\s*\[' + if line =~ '\\frontmatter' + let title = "Frontmatter" + elseif line =~ '\\mainmatter' + let title = "Mainmatter" + elseif line =~ '\\backmatter' + let title = "Backmatter" + elseif line =~ '\\appendix' + let title = "Appendix" + elseif line =~ secpat1 . '.*}' + let title = matchstr(line, secpat1 . '\zs.*\ze}') + elseif line =~ secpat1 + let title = matchstr(line, secpat1 . '\zs.*') + elseif line =~ secpat2 . '.*\]' + let title = matchstr(line, secpat2 . '\zs.*\ze\]') + elseif line =~ secpat2 + let title = matchstr(line, secpat2 . '\zs.*') + elseif line =~ 'Fake' . sections . ':' + let title = matchstr(line,'Fake' . sections . ':\s*\zs.*') + elseif line =~ 'Fake' . sections + let title = matchstr(line, 'Fake' . sections) + endif + + " Environments + if line =~ '\\begin' + " Capture environment name + let env = matchstr(line,'\\begin\*\?{\zs\w*\*\?\ze}') + + " Set caption/label based on type of environment + if env == 'frame' + let label = '' + let caption = s:parse_caption_frame(line) + elseif env == 'table' + let label = s:parse_label() + let caption = s:parse_caption_table(line) + else + let label = s:parse_label() + let caption = s:parse_caption(line) + endif + + " Add paranthesis to label + if label != '' + let label = substitute(strpart(label,0,54), '\(.*\)', '(\1)','') + endif + + " Set size of label and caption part of string + let nl = len(label) > 56 ? 56 : len(label) + let nc = 56 - (nl + 1) + let caption = strpart(caption, 0, nc) + + " Create title based on env, caption and label + let title = printf('%-12s%-' . nc . 's %' . nl . 's', + \ env, caption, label) + endif + + let title = strpart(title, 0, 68) + return printf('%-3s %-68S #%5d', level, title, nlines) +endfunction +" }}}1 + +" {{{1 s:find_fold_sections +function! s:find_fold_sections() + " + " This function parses the tex file to find the sections that are to be + " folded and their levels, and then predefines the patterns for optimized + " folding. + " + " Initialize + let level = 1 + let foldsections = [] + + " If we use two or more of the *matter commands, we need one more foldlevel + let nparts = 0 + for part in g:latex_fold_parts + let i = 1 + while i < line("$") + if getline(i) =~ '^\s*\\' . part . '\>' + let nparts += 1 + break + endif + let i += 1 + endwhile + if nparts > 1 + let level = 2 + break + endif + endfor + + " Combine sections and levels, but ignore unused section commands: If we + " don't use the part command, then chapter should have the highest + " level. If we don't use the chapter command, then section should be the + " highest level. And so on. + let ignore = 1 + for part in g:latex_fold_sections + " For each part, check if it is used in the file. We start adding the + " part patterns to the fold sections array whenever we find one. + let partpattern = '^\s*\(\\\|% Fake\)' . part . '\>' + if ignore + let i = 1 + while i < line("$") + if getline(i) =~# partpattern + call insert(foldsections, [partpattern, level]) + let level += 1 + let ignore = 0 + break + endif + let i += 1 + endwhile + else + call insert(foldsections, [partpattern, level]) + let level += 1 + endif + endfor + + return foldsections +endfunction + +" {{{1 s:parse_label +function! s:parse_label() + let i = v:foldend + while i >= v:foldstart + if getline(i) =~ '^\s*\\label' + return matchstr(getline(i), '^\s*\\label{\zs.*\ze}') + end + let i -= 1 + endwhile + return "" +endfunction + +" {{{1 s:parse_caption +function! s:parse_caption(line) + let i = v:foldend + while i >= v:foldstart + if getline(i) =~ '^\s*\\caption' + return matchstr(getline(i), + \ '^\s*\\caption\(\[.*\]\)\?{\zs.\{-1,}\ze\(}\s*\)\?$') + end + let i -= 1 + endwhile + + " If no caption found, check for a caption comment + return matchstr(a:line,'\\begin\*\?{.*}\s*%\s*\zs.*') +endfunction + +" {{{1 s:parse_caption_table +function! s:parse_caption_table(line) + let i = v:foldstart + while i <= v:foldend + if getline(i) =~ '^\s*\\caption' + return matchstr(getline(i), + \ '^\s*\\caption\(\[.*\]\)\?{\zs.\{-1,}\ze\(}\s*\)\?$') + end + let i += 1 + endwhile + + " If no caption found, check for a caption comment + return matchstr(a:line,'\\begin\*\?{.*}\s*%\s*\zs.*') +endfunction + +" {{{1 s:parse_caption_frame +function! s:parse_caption_frame(line) + " Test simple variants first + let caption1 = matchstr(a:line,'\\begin\*\?{.*}{\zs.\+\ze}') + let caption2 = matchstr(a:line,'\\begin\*\?{.*}{\zs.\+') + + if len(caption1) > 0 + return caption1 + elseif len(caption2) > 0 + return caption2 + else + let i = v:foldstart + while i <= v:foldend + if getline(i) =~ '^\s*\\frametitle' + return matchstr(getline(i), + \ '^\s*\\frametitle\(\[.*\]\)\?{\zs.\{-1,}\ze\(}\s*\)\?$') + end + let i += 1 + endwhile + + " If no caption found, check for a caption comment + return matchstr(a:line,'\\begin\*\?{.*}\s*%\s*\zs.*') + endif +endfunction +" }}}1 + +" vim:fdm=marker:ff=unix diff --git a/autoload/latex/latexmk.vim b/autoload/latex/latexmk.vim new file mode 100644 index 0000000..9480d1b --- /dev/null +++ b/autoload/latex/latexmk.vim @@ -0,0 +1,226 @@ +" {{{1 latex#latexmk#init +function! latex#latexmk#init(initialized) + if !g:latex_latexmk_enabled | return | endif + + " + " Initialize pid for current tex file + " + if !has_key(g:latex#data[b:latex.id], 'pid') + let g:latex#data[b:latex.id].pid = 0 + endif + + " + " Set default mappings + " + if g:latex_default_mappings + nnoremap ll :call latex#latexmk#compile() + nnoremap lc :call latex#latexmk#clean() + nnoremap lC :call latex#latexmk#clean(1) + nnoremap lg :call latex#latexmk#status() + nnoremap lG :call latex#latexmk#status(1) + nnoremap lk :call latex#latexmk#stop(1) + nnoremap lK :call latex#latexmk#stop_all() + nnoremap le :call latex#latexmk#errors() + endif + + " + " Ensure that all latexmk processes are stopped when vim exits + " Note: Only need to define this once, globally. + " + if !a:initialized + augroup latex_latexmk + autocmd! + autocmd VimLeave *.tex call latex#latexmk#stop_all() + augroup END + endif + + " + " If all buffers for a given latex project are closed, kill latexmk + " Note: This must come after the above so that the autocmd group is properly + " refreshed if necessary + " + augroup latex_latexmk + autocmd BufUnload call s:stop_buffer() + augroup END +endfunction + +" {{{1 latex#latexmk#clean +function! latex#latexmk#clean(...) + let full = a:0 > 0 + + let data = g:latex#data[b:latex.id] + if data.pid + echomsg "latexmk is already running" + return + endif + + " + " Run latexmk clean process + " + let cmd = '!cd ' . data.root . ';' + if full + let cmd .= 'latexmk -C ' + else + let cmd .= 'latexmk -c ' + endif + let cmd .= data.base . ' &>/dev/null' + let g:latex#data[b:latex.id].clean_cmd = cmd + + call s:execute(cmd) + if full + echomsg "latexmk full clean finished" + else + echomsg "latexmk clean finished" + endif +endfunction + +" {{{1 latex#latexmk#compile +function! latex#latexmk#compile() + let data = g:latex#data[b:latex.id] + if data.pid + echomsg "latexmk is already running for `" . data.base . "'" + return + endif + + " + " Set latexmk command with options + " + let cmd = '!cd ' . data.root . ' && ' + let cmd .= 'max_print_line=2000 latexmk' + let cmd .= ' -' . g:latex_latexmk_output + let cmd .= ' -quiet ' + let cmd .= ' -pvc' + let cmd .= g:latex_latexmk_options + let cmd .= ' -e ' . shellescape('$pdflatex =~ s/ / -file-line-error /') + let cmd .= ' -e ' . shellescape('$latex =~ s/ / -file-line-error /') + let cmd .= ' ' . data.base + let cmd .= ' &>/dev/null &' + let g:latex#data[b:latex.id].cmd = cmd + + " + " Start latexmk and save PID + " + call s:execute(cmd) + let g:latex#data[b:latex.id].pid = system('pgrep -nf latexmk')[:-2] + echomsg 'latexmk started successfully' +endfunction + +" {{{1 latex#latexmk#errors +function! latex#latexmk#errors() + let log = g:latex#data[b:latex.id].log() + + cclose + + if g:latex_latexmk_autojump + execute 'cfile ' . log + else + execute 'cgetfile ' . log + endif + + botright copen +endfunction + +" {{{1 latex#latexmk#status +function! latex#latexmk#status(...) + let detailed = a:0 > 0 + + if detailed + let running = 0 + for data in g:latex#data + if data.pid + if !running + echo "latexmk is running" + let running = 1 + endif + + let name = data.tex + if len(name) >= winwidth('.') - 20 + let name = "..." . name[-winwidth('.')+23:] + endif + + echom printf('pid: %6s, file: %-s', data.pid, name) + endif + endfor + + if !running + echo "latexmk is not running" + endif + else + if g:latex#data[b:latex.id].pid + echo "latexmk is running" + else + echo "latexmk is not running" + endif + endif +endfunction + +" {{{1 latex#latexmk#stop +function! latex#latexmk#stop(...) + let l:verbose = a:0 > 0 + + let pid = g:latex#data[b:latex.id].pid + let base = g:latex#data[b:latex.id].base + if pid + call s:execute('!kill ' . pid) + let g:latex#data[b:latex.id].pid = 0 + if l:verbose + echo "latexmk stopped for `" . base . "'" + endif + elseif l:verbose + echo "latexmk is not running for `" . base . "'" + endif +endfunction + +" {{{1 latex#latexmk#stop_all +function! latex#latexmk#stop_all() + for data in g:latex#data + if data.pid + call s:execute('!kill ' . data.pid) + let data.pid = 0 + endif + endfor +endfunction +" }}}1 + +" {{{1 s:stop_buffer +function! s:stop_buffer() + " + " Only run if latex variables are set + " + if !exists('b:latex') | return | endif + let id = b:latex.id + let pid = g:latex#data[id].pid + + " + " Only stop if latexmk is running + " + if pid + " + " Count the number of buffers that point to current latex blob + " + let n = 0 + for b in filter(range(1, bufnr("$")), 'buflisted(v:val)') + if id == getbufvar(b, 'latex', {'id' : -1}).id + let n += 1 + endif + endfor + + " + " Only stop if current buffer is the last for current latex blob + " + if n == 1 + call latex#latexmk#stop(0) + endif + endif +endfunction + +" {{{1 s:execute +function! s:execute(cmd) + silent execute a:cmd + if !has('gui_running') + redraw! + endif +endfunction +" }}}1 + +" vim:fdm=marker:ff=unix diff --git a/autoload/latex/motion.vim b/autoload/latex/motion.vim new file mode 100644 index 0000000..d7ab9ec --- /dev/null +++ b/autoload/latex/motion.vim @@ -0,0 +1,275 @@ +" {{{1 latex#motion#init +function! latex#motion#init(initialized) + if !g:latex_motion_enabled | return | endif + + if g:latex_default_mappings + nnoremap % :call latex#motion#find_matching_pair('n') + vnoremap % + \ :call latex#motion#find_matching_pair('v') + onoremap % :call latex#motion#find_matching_pair('o') + + nnoremap ]] :call latex#motion#next_sec(0,0,0) + nnoremap ][ :call latex#motion#next_sec(1,0,0) + nnoremap [] :call latex#motion#next_sec(1,1,0) + nnoremap [[ :call latex#motion#next_sec(0,1,0) + vnoremap ]] :call latex#motion#next_sec(0,0,1) + vnoremap ][ :call latex#motion#next_sec(1,0,1) + vnoremap [] :call latex#motion#next_sec(1,1,1) + vnoremap [[ :call latex#motion#next_sec(0,1,1) + onoremap ]] :normal v]] + onoremap ][ :normal v][ + onoremap [] :normal v[] + onoremap [[ :normal v[[ + + vnoremap ie :latex#motion#select_current_env('inner') + vnoremap ae :latex#motion#select_current_env('outer') + onoremap ie :normal vie + onoremap ae :normal vae + + vnoremap i$ :latex#motion#select_inline_math('inner') + vnoremap a$ :latex#motion#select_inline_math('outer') + onoremap i$ :normal vi$ + onoremap a$ :normal va$ + endif + + " + " Highlight matching parens ($, (), ...) + " + if !a:initialized && g:latex_motion_matchparen + augroup latex_motion + autocmd! + " Disable matchparen autocommands + autocmd BufEnter *.tex + \ if !exists("g:loaded_matchparen") || !g:loaded_matchparen + \ | runtime plugin/matchparen.vim + \ | endif + autocmd BufEnter *.tex + \ 3match none | unlet! g:loaded_matchparen | au! matchparen + + " Enable latex matchparen functionality + autocmd! CursorMoved *.tex call latex#motion#find_matching_pair('h') + autocmd! CursorMovedI *.tex call latex#motion#find_matching_pair('i') + augroup END + endif +endfunction + +" {{{1 latex#motion#find_matching_pair +function! latex#motion#find_matching_pair(mode) + " + " Note: This code is ugly, but it seems to work. + " + if a:mode =~ 'h\|i' + 2match none + elseif a:mode == 'v' + normal! gv + endif + + if latex#util#in_comment() | return | endif + + let lnum = line('.') + let cnum = searchpos('\A', 'cbnW', lnum)[1] + + " Check if previous char is a backslash + if strpart(getline(lnum), cnum-2, 1) == '\' + let cnum = cnum-1 + endif + + " Make pattern to combine all open/close pats + let all_pats = join(g:latex_motion_open_pats+g:latex_motion_close_pats, '\|') + let all_pats = '\(' . all_pats . '\|\$\)' + + let delim = matchstr(getline(lnum), '\C^'. all_pats, cnum-1) + if empty(delim) || strlen(delim)+cnum-1 < col('.') + if a:mode =~ 'n\|v\|o' + " if not found, search forward + let cnum = match(getline(lnum), '\C'. all_pats, col('.') - 1) + 1 + if cnum == 0 | return | endif + call cursor(lnum, cnum) + let delim = matchstr(getline(lnum), '\C^'. all_pats, cnum - 1) + elseif a:mode =~ 'i' + " if not found, move one char bacward and search + let cnum = searchpos('\A', 'bnW', lnum)[1] + " if the previous char is a backslash + if strpart(getline(lnum), cnum-2, 1) == '\' + let cnum = cnum-1 + endif + let delim = matchstr(getline(lnum), '\C^'. all_pats, cnum - 1) + if empty(delim) || strlen(delim)+cnum< col('.') + return + endif + elseif a:mode =~ 'h' + return + endif + endif + + if delim =~ '^\$' + " match $-pairs + " check if next character is in inline math + let [lnum0, cnum0] = searchpos('.', 'nW') + if lnum0 && latex#util#has_syntax('texMathZoneX', lnum0, cnum0) + let [lnum2, cnum2] = searchpos(b:notcomment . b:notbslash . '\$', + \ 'nW', line('w$')*(a:mode =~ 'h\|i'), 200) + else + let [lnum2, cnum2] = searchpos('\%(\%'. lnum . 'l\%' + \ . cnum . 'c\)\@!'. b:notcomment . b:notbslash . '\$', + \ 'bnW', line('w0')*(a:mode =~ 'h\|i'), 200) + endif + + if a:mode =~ 'h\|i' + execute '2match MatchParen /\%(\%' . lnum . 'l\%' + \ . cnum . 'c\$' . '\|\%' . lnum2 . 'l\%' . cnum2 . 'c\$\)/' + elseif a:mode =~ 'n\|v\|o' + call cursor(lnum2,cnum2) + endif + else + " match other pairs + for i in range(len(g:latex_motion_open_pats)) + let open_pat = b:notbslash . g:latex_motion_open_pats[i] + let close_pat = b:notbslash . g:latex_motion_close_pats[i] + + if delim =~# '^' . open_pat + " if on opening pattern, search for closing pattern + let [lnum2, cnum2] = searchpairpos('\C' . open_pat, '', '\C' + \ . close_pat, 'nW', 'latex#util#in_comment()', + \ line('w$')*(a:mode =~ 'h\|i'), 200) + if a:mode =~ 'h\|i' + execute '2match MatchParen /\%(\%' . lnum . 'l\%' . cnum + \ . 'c' . g:latex_motion_open_pats[i] . '\|\%' + \ . lnum2 . 'l\%' . cnum2 . 'c' + \ . g:latex_motion_close_pats[i] . '\)/' + elseif a:mode =~ 'n\|v\|o' + call cursor(lnum2,cnum2) + if strlen(close_pat)>1 && a:mode =~ 'o' + call cursor(lnum2, matchend(getline('.'), '\C' + \ . close_pat, col('.')-1)) + endif + endif + break + elseif delim =~# '^' . close_pat + " if on closing pattern, search for opening pattern + let [lnum2, cnum2] = searchpairpos('\C' . open_pat, '', + \ '\C\%(\%'. lnum . 'l\%' . cnum . 'c\)\@!' + \ . close_pat, 'bnW', 'latex#util#in_comment()', + \ line('w0')*(a:mode =~ 'h\|i'), 200) + if a:mode =~ 'h\|i' + execute '2match MatchParen /\%(\%' . lnum2 . 'l\%' . cnum2 + \ . 'c' . g:latex_motion_open_pats[i] . '\|\%' + \ . lnum . 'l\%' . cnum . 'c' + \ . g:latex_motion_close_pats[i] . '\)/' + elseif a:mode =~ 'n\|v\|o' + call cursor(lnum2,cnum2) + endif + break + endif + endfor + endif +endfunction + +" {{{1 latex#motion#next_sec +function! latex#motion#next_sec(type, backwards, visual) + " Restore visual mode if desired + if a:visual + normal! gv + endif + + " For the [] and ][ commands we move up or down before the search + if a:type == 1 + if a:backwards + normal! k + else + normal! j + endif + endif + + " Define search pattern and do the search while preserving "/ + let save_search = @/ + let flags = 'W' + if a:backwards + let flags = 'b' . flags + endif + + " Define section pattern + let sec_pat = join([ + \ '(sub)*section', + \ 'chapter', + \ 'part', + \ 'appendix', + \ '(front|back|main)matter', + \ ], '|') + let sec_pat = b:notcomment . '\v\s*\\(' . sec_pat . ')>' + + " Perform the search + call search(sec_pat, flags) + let @/ = save_search + + " For the [] and ][ commands we move down or up after the search + if a:type == 1 + if a:backwards + normal! j + else + normal! k + endif + endif +endfunction + +" {{{1 latex#motion#select_current_env +function! latex#motion#select_current_env(seltype) + let [env, lnum, cnum, lnum2, cnum2] = latex#util#get_env(1) + call cursor(lnum, cnum) + if a:seltype == 'inner' + if env =~ '^\' + call search('\\.\_\s*\S', 'eW') + else + call search('}\(\_\s*\[\_[^]]*\]\)\?\_\s*\S', 'eW') + endif + endif + if visualmode() ==# 'V' + normal! V + else + normal! v + endif + call cursor(lnum2, cnum2) + if a:seltype == 'inner' + call search('\S\_\s*', 'bW') + else + if env =~ '^\' + normal! l + else + call search('}', 'eW') + endif + endif +endfunction + +" {{{1 latex#motion#select_inline_math +function! latex#motion#select_inline_math(seltype) + " seltype is either 'inner' or 'outer' + + let dollar_pat = '\\\@ lt :call latex#toc#open() + nnoremap lT :call latex#toc#toggle() + endif +endfunction + +" {{{1 latex#toc#open +function! latex#toc#open() + " Check if buffer exists + let winnr = bufwinnr(bufnr('LaTeX TOC')) + if winnr >= 0 + silent execute winnr . 'wincmd w' + return + endif + + " Get file names + let auxfile = g:latex#data[b:latex.id].aux() + let texfile = g:latex#data[b:latex.id].tex + + " Create TOC window + let calling_buf = bufnr('%') + let calling_file = expand('%:p') + if g:latex_toc_resize + silent exe "set columns+=" . g:latex_toc_width + endif + silent exe g:latex_toc_split_side g:latex_toc_width . 'vnew LaTeX\ TOC' + + " Parse TOC data + if auxfile == "" + call append('$', "TeX file not compiled") + else + let toc = s:read_toc(auxfile, texfile) + let closest_index = s:find_closest_section(toc, calling_file) + let b:toc = toc.data + let b:toc_numbers = 1 + let b:calling_win = bufwinnr(calling_buf) + + " Add TOC entries and jump to the closest section + for entry in toc.data + call append('$', entry['number'] . "\t" . entry['text']) + endfor + + execute 'normal! ' . (closest_index + 1) . 'G' + endif + + " Add help info + if !g:latex_toc_hide_help + call append('$', "") + call append('$', "/q: close") + call append('$', ": jump") + call append('$', ": jump and close") + call append('$', "s: hide numbering") + endif + 0delete _ + + " Set filetype and lock buffer + setlocal filetype=latextoc + setlocal nomodifiable +endfunction + +" {{{1 latex#toc#toggle +function! latex#toc#toggle() + if bufwinnr(bufnr('LaTeX TOC')) >= 0 + if g:latex_toc_resize + silent exe "set columns-=" . g:latex_toc_width + endif + silent execute 'bwipeout' . bufnr('LaTeX TOC') + else + call latex#toc#open() + silent execute 'wincmd w' + endif +endfunction +" }}}1 + +" {{{1 s:read_toc +function! s:read_toc(auxfile, texfile, ...) + let texfile = a:texfile + let prefix = fnamemodify(a:auxfile, ':p:h') + + if a:0 != 2 + let toc = [] + let fileindices = { texfile : [] } + else + let toc = a:1 + let fileindices = a:2 + let fileindices[texfile] = [] + endif + + for line in readfile(a:auxfile) + " Check for included files, include if found + let included = matchstr(line, '^\\@input{\zs[^}]*\ze}') + if included != '' + let newaux = prefix . '/' . included + let newtex = fnamemodify(newaux, ':r') . '.tex' + call s:read_toc(newaux, newtex, toc, fileindices) + continue + endif + + " Parse TOC statements from aux files + let line = matchstr(line, + \ '\\@writefile{toc}{\\contentsline\s*\zs.*\ze}\s*$') + if empty(line) + continue + endif + + let tree = latex#util#tex2tree(latex#util#convert_back(line)) + + if len(tree) < 3 + " unknown entry type: just skip it + continue + endif + + " Parse level + let level = tree[0][0] + " parse page + if !empty(tree[2]) + let page = tree[2][0] + else + let page = '' + endif + + " Parse number + if len(tree[1]) > 3 && empty(tree[1][1]) + call remove(tree[1], 1) + endif + if len(tree[1]) > 1 + if !empty(tree[1][1]) + let secnum = latex#util#tree2tex(tree[1][1]) + let secnum = substitute(secnum, '\\\S\+\s', '', 'g') + let secnum = substitute(secnum, '\\\S\+{\(.\{-}\)}', '\1', 'g') + let secnum = substitute(secnum, '^{\+\|}\+$', '', 'g') + endif + let tree = tree[1][2:] + else + let secnum = '' + let tree = tree[1] + endif + + " Parse title + let text = latex#util#tree2tex(tree) + let text = substitute(text, '^{\+\|}\+$', '', 'g') + + " Add TOC entry + call add(fileindices[texfile], len(toc)) + call add(toc, + \ { + \ 'file': texfile, + \ 'level': level, + \ 'number': secnum, + \ 'text': text, + \ 'page': page, + \ }) + endfor + + return {'data': toc, 'fileindices': fileindices} +endfunction + +" {{{1 s:find_closest_section +" +" 1. Binary search for the closest section +" 2. Return the index of the TOC entry +" +function! s:find_closest_section(toc, file) + if !has_key(a:toc.fileindices, a:file) + echoerr 'File not included in main tex file!' + return + endif + + let imax = len(a:toc.fileindices[a:file]) + if imax > 0 + let imin = 0 + while imin < imax - 1 + let i = (imax + imin) / 2 + let tocindex = a:toc.fileindices[a:file][i] + let entry = a:toc.data[tocindex] + let titlestr = entry['text'] + let titlestr = escape(titlestr, '\') + let titlestr = substitute(titlestr, ' ', '\\_\\s\\+', 'g') + let [lnum, cnum] + \ = searchpos('\\'.entry['level'].'\_\s*{'.titlestr.'}', 'nW') + if lnum + let imax = i + else + let imin = i + endif + endwhile + return a:toc.fileindices[a:file][imin] + else + return 0 + endif +endfunction + +" }}}1 + +" vim:fdm=marker:ff=unix diff --git a/autoload/latex/util.vim b/autoload/latex/util.vim new file mode 100644 index 0000000..19dcde7 --- /dev/null +++ b/autoload/latex/util.vim @@ -0,0 +1,231 @@ +" +" Utility functions sorted by name +" +" {{{1 latex#util#convert_back +function! latex#util#convert_back(line) + " + " Substitute stuff like '\IeC{\"u}' to corresponding unicode symbols + " + let line = a:line + for [pat, symbol] in s:convert_back_list + let line = substitute(line, pat, symbol, 'g') + endfor + + " + " There might be some missing conversions, which might be fixed by the last + " substitution + " + return substitute(line, '\C\(\\IeC\s*{\)\?\\.\(.\)}', '\1', 'g') +endfunction + +let s:convert_back_list = map([ + \ ['\\''A}' , 'Á'], + \ ['\\`A}' , 'À'], + \ ['\\^A}' , 'À'], + \ ['\\¨A}' , 'Ä'], + \ ['\\"A}' , 'Ä'], + \ ['\\''a}' , 'á'], + \ ['\\`a}' , 'à'], + \ ['\\^a}' , 'à'], + \ ['\\¨a}' , 'ä'], + \ ['\\"a}' , 'ä'], + \ ['\\''E}' , 'É'], + \ ['\\`E}' , 'È'], + \ ['\\^E}' , 'Ê'], + \ ['\\¨E}' , 'Ë'], + \ ['\\"E}' , 'Ë'], + \ ['\\''e}' , 'é'], + \ ['\\`e}' , 'è'], + \ ['\\^e}' , 'ê'], + \ ['\\¨e}' , 'ë'], + \ ['\\"e}' , 'ë'], + \ ['\\''I}' , 'Í'], + \ ['\\`I}' , 'Î'], + \ ['\\^I}' , 'Ì'], + \ ['\\¨I}' , 'Ï'], + \ ['\\"I}' , 'Ï'], + \ ['\\''i}' , 'í'], + \ ['\\`i}' , 'î'], + \ ['\\^i}' , 'ì'], + \ ['\\¨i}' , 'ï'], + \ ['\\"i}' , 'ï'], + \ ['\\''{\?\\i }' , 'í'], + \ ['\\''O}' , 'Ó'], + \ ['\\`O}' , 'Ò'], + \ ['\\^O}' , 'Ô'], + \ ['\\¨O}' , 'Ö'], + \ ['\\"O}' , 'Ö'], + \ ['\\''o}' , 'ó'], + \ ['\\`o}' , 'ò'], + \ ['\\^o}' , 'ô'], + \ ['\\¨o}' , 'ö'], + \ ['\\"o}' , 'ö'], + \ ['\\o }' , 'ø'], + \ ['\\''U}' , 'Ú'], + \ ['\\`U}' , 'Ù'], + \ ['\\^U}' , 'Û'], + \ ['\\¨U}' , 'Ü'], + \ ['\\"U}' , 'Ü'], + \ ['\\''u}' , 'ú'], + \ ['\\`u}' , 'ù'], + \ ['\\^u}' , 'û'], + \ ['\\¨u}' , 'ü'], + \ ['\\"u}' , 'ü'], + \ ['\\`N}' , 'Ǹ'], + \ ['\\\~N}' , 'Ñ'], + \ ['\\''n}' , 'ń'], + \ ['\\`n}' , 'ǹ'], + \ ['\\\~n}' , 'ñ'], + \], '[''\C\(\\IeC\s*{\)\?'' . v:val[0], v:val[1]]') + +" {{{1 latex#util#get_env +function! latex#util#get_env(...) + " latex#util#get_env([with_pos]) + " Returns: + " - environment + " if with_pos is not given + " - [environment, lnum_begin, cnum_begin, lnum_end, cnum_end] + " if with_pos is nonzero + if a:0 > 0 + let with_pos = a:1 + else + let with_pos = 0 + endif + + let begin_pat = '\C\\begin\_\s*{[^}]*}\|\\\@ 1 && line[cnum - 1] != '\' + let cnum -= 1 + endwhile + call cursor(lnum, cnum) + + " match begin/end pairs but skip comments + let flags = 'bnW' + if strpart(getline('.'), col('.') - 1) =~ '^\%(' . begin_pat . '\)' + let flags .= 'c' + endif + let [lnum1, cnum1] = searchpairpos(begin_pat, '', end_pat, flags, + \ 'latex#util#in_comment()') + + let env = '' + + if lnum1 + let line = strpart(getline(lnum1), cnum1 - 1) + + if empty(env) + let env = matchstr(line, '^\C\\begin\_\s*{\zs[^}]*\ze}') + endif + if empty(env) + let env = matchstr(line, '^\\\[') + endif + if empty(env) + let env = matchstr(line, '^\\(') + endif + endif + + if with_pos == 1 + let flags = 'nW' + if !(lnum1 == lnum && cnum1 == cnum) + let flags .= 'c' + endif + + let [lnum2, cnum2] = searchpairpos(begin_pat, '', end_pat, flags, + \ 'latex#util#in_comment()') + + call setpos('.', saved_pos) + return [env, lnum1, cnum1, lnum2, cnum2] + else + call setpos('.', saved_pos) + return env + endif +endfunction + +" {{{1 latex#util#has_syntax +function! latex#util#has_syntax(name, ...) + " Usage: latex#util#has_syntax(name, [line], [col]) + let line = a:0 >= 1 ? a:1 : line('.') + let col = a:0 >= 2 ? a:2 : col('.') + return 0 <= index(map(synstack(line, col), + \ 'synIDattr(v:val, "name") == "' . a:name . '"'), 1) +endfunction + +" {{{1 latex#util#in_comment +function! latex#util#in_comment(...) + let line = a:0 >= 1 ? a:1 : line('.') + let col = a:0 >= 2 ? a:2 : col('.') + return synIDattr(synID(line, col, 0), "name") =~# '^texComment' +endfunction + +" {{{1 latex#util#kpsewhich +function! latex#util#kpsewhich(file, ...) + let cmd = 'kpsewhich ' + let cmd .= a:0 > 0 ? a:1 : '' + let cmd .= ' "' . a:file . '"' + let out = system(cmd) + + " If kpsewhich has found something, it returns a non-empty string with a + " newline at the end; otherwise the string is empty + if len(out) + " Remove the trailing newline + let out = fnamemodify(out[:-2], ':p') + endif + + return out +endfunction + +" {{{1 latex#util#set_default +function! latex#util#set_default(variable, default) + if !exists(a:variable) + let {a:variable} = a:default + endif +endfunction + +" {{{1 latex#util#tex2tree +function! latex#util#tex2tree(str) + let tree = [] + let i1 = 0 + let i2 = -1 + let depth = 0 + while i2 < len(a:str) + let i2 = match(a:str, '[{}]', i2 + 1) + if i2 < 0 + let i2 = len(a:str) + endif + if i2 >= len(a:str) || a:str[i2] == '{' + if depth == 0 + let item = substitute(strpart(a:str, i1, i2 - i1), + \ '^\s*\|\s*$', '', 'g') + if !empty(item) + call add(tree, item) + endif + let i1 = i2 + 1 + endif + let depth += 1 + else + let depth -= 1 + if depth == 0 + call add(tree, latex#util#tex2tree(strpart(a:str, i1, i2 - i1))) + let i1 = i2 + 1 + endif + endif + endwhile + return tree +endfunction + +" {{{1 latex#util#tree2tex +function! latex#util#tree2tex(tree) + if type(a:tree) == type('') + return a:tree + else + return '{' . join(map(a:tree, 'latex#util#tree2tex(v:val)'), '') . '}' + endif +endfunction + +" }}}1 + +" vim:fdm=marker:ff=unix diff --git a/autoload/latex/vimcomplete.bst b/autoload/latex/vimcomplete.bst new file mode 100644 index 0000000..0d7e96a --- /dev/null +++ b/autoload/latex/vimcomplete.bst @@ -0,0 +1,306 @@ +ENTRY + { address author booktitle chapter doi edition editor eid howpublished institution isbn issn journal key month note number organization pages publisher school series title type volume year } + {} + { label } +STRINGS { s t} + +FUNCTION {output} +{ 's := + %purify$ + %"}{" * write$ + "||" * write$ + s +} +FUNCTION {fin.entry} +%{ "}" * write$ +{ write$ + newline$ +} + +FUNCTION {not} +{ { #0 } + { #1 } + if$ +} +FUNCTION {and} +{ 'skip$ + { pop$ #0 } + if$ +} +FUNCTION {or} +{ { pop$ #1 } + 'skip$ + if$ +} +FUNCTION {field.or.null} +{ duplicate$ empty$ + { pop$ "" } + 'skip$ + if$ +} + +FUNCTION {capitalize} +{ "u" change.case$ "t" change.case$ } + +FUNCTION {space.word} +{ " " swap$ * " " * } + +FUNCTION {bbl.and} { "&"} +FUNCTION {bbl.etal} { "et al." } + +INTEGERS { nameptr namesleft numnames } + +STRINGS { bibinfo} + +FUNCTION {format.names} +{ duplicate$ empty$ 'skip$ { + 's := + "" 't := + #1 'nameptr := + s num.names$ 'numnames := + numnames 'namesleft := + { namesleft #0 > } + { s nameptr + %"{vv~}{ll}{, f.}{, jj}" + "{vv }{ll}{}{}" + format.name$ + 't := + nameptr #1 > + { + namesleft #1 > + { ", " * t * } + { + s nameptr "{ll}" format.name$ duplicate$ "others" = + { 't := } + { pop$ } + if$ + t "others" = + { + " " * bbl.etal * + } + { + bbl.and + space.word * t * + } + if$ + } + if$ + } + 't + if$ + nameptr #1 + 'nameptr := + namesleft #1 - 'namesleft := + } + while$ + } if$ +} + +FUNCTION {format.authors} +{ author empty$ + {editor format.names} {author format.names} + if$ +} + +FUNCTION {format.title} +{ title + duplicate$ empty$ 'skip$ + { "t" change.case$ } + if$ +} +FUNCTION {output.label} +{ newline$ + %"{" cite$ * write$ + cite$ write$ + "" +} + + +FUNCTION {format.date} +{ + "" + duplicate$ empty$ + year duplicate$ empty$ + { swap$ 'skip$ + { "there's a month but no year in " cite$ * warning$ } + if$ + * + } + { swap$ 'skip$ + { + swap$ + " " * swap$ + } + if$ + * + } + if$ +} + +FUNCTION {output.entry} +{ 's := + output.label + s output + format.authors output + format.date output + format.title output + fin.entry +} + +FUNCTION {default.type} {"?" output.entry} + +FUNCTION {article} {"a" output.entry} +FUNCTION {book} {"B" output.entry} +FUNCTION {booklet} {"k" output.entry} +FUNCTION {conference} {"f" output.entry} +FUNCTION {inbook} {"b" output.entry} +FUNCTION {incollection} {"c" output.entry} +FUNCTION {inproceedings} {"p" output.entry} +FUNCTION {manual} {"m" output.entry} +FUNCTION {mastersthesis} {"Master" output.entry} +FUNCTION {misc} {"-" output.entry} +FUNCTION {phdthesis} {"PhD" output.entry} +FUNCTION {proceedings} {"P" output.entry} +FUNCTION {techreport} {"r" output.entry} +FUNCTION {unpublished} {"u" output.entry} + + +READ +FUNCTION {sortify} +{ purify$ + "l" change.case$ +} +INTEGERS { len } +FUNCTION {chop.word} +{ 's := + 'len := + s #1 len substring$ = + { s len #1 + global.max$ substring$ } + 's + if$ +} +FUNCTION {sort.format.names} +{ 's := + #1 'nameptr := + "" + s num.names$ 'numnames := + numnames 'namesleft := + { namesleft #0 > } + { s nameptr + "{vv{ } }{ll{ }}{ f{ }}{ jj{ }}" + format.name$ 't := + nameptr #1 > + { + " " * + namesleft #1 = t "others" = and + { "zzzzz" * } + { t sortify * } + if$ + } + { t sortify * } + if$ + nameptr #1 + 'nameptr := + namesleft #1 - 'namesleft := + } + while$ +} + +FUNCTION {sort.format.title} +{ 't := + "A " #2 + "An " #3 + "The " #4 t chop.word + chop.word + chop.word + sortify + #1 global.max$ substring$ +} +FUNCTION {author.sort} +{ author empty$ + { key empty$ + { "to sort, need author or key in " cite$ * warning$ + "" + } + { key sortify } + if$ + } + { author sort.format.names } + if$ +} +FUNCTION {author.editor.sort} +{ author empty$ + { editor empty$ + { key empty$ + { "to sort, need author, editor, or key in " cite$ * warning$ + "" + } + { key sortify } + if$ + } + { editor sort.format.names } + if$ + } + { author sort.format.names } + if$ +} +FUNCTION {author.organization.sort} +{ author empty$ + { organization empty$ + { key empty$ + { "to sort, need author, organization, or key in " cite$ * warning$ + "" + } + { key sortify } + if$ + } + { "The " #4 organization chop.word sortify } + if$ + } + { author sort.format.names } + if$ +} +FUNCTION {editor.organization.sort} +{ editor empty$ + { organization empty$ + { key empty$ + { "to sort, need editor, organization, or key in " cite$ * warning$ + "" + } + { key sortify } + if$ + } + { "The " #4 organization chop.word sortify } + if$ + } + { editor sort.format.names } + if$ +} +FUNCTION {presort} +{ type$ "book" = + type$ "inbook" = + or + 'author.editor.sort + { type$ "proceedings" = + 'editor.organization.sort + { type$ "manual" = + 'author.organization.sort + 'author.sort + if$ + } + if$ + } + if$ + " " + * + year field.or.null sortify + * + " " + * + title field.or.null + sort.format.title + * + #1 entry.max$ substring$ + 'sort.key$ := +} +ITERATE {presort} +SORT +ITERATE {call.type$} diff --git a/autoload/toc.vim b/autoload/toc.vim new file mode 100644 index 0000000..c98f596 --- /dev/null +++ b/autoload/toc.vim @@ -0,0 +1,41 @@ +" {{{1 latextoc#fold_level +function! latextoc#fold_level(lnum) + let line = getline(a:lnum) + let match_s1 = line =~# '^\w\+\s' + let match_s2 = line =~# '^\w\+\.\w\+\s' + let match_s3 = line =~# '^\w\+\.\w\+\.\w\+\s' + + if g:latex_toc_fold_levels >= 3 + if match_s3 + return ">3" + endif + endif + + if g:latex_toc_fold_levels >= 2 + if match_s2 + return ">2" + endif + endif + + if match_s1 + return ">1" + endif + + " Don't fold options + if line =~# '^\s*$' + return 0 + endif + + " Return previous fold level + return "=" +endfunction + +" {{{1 latextoc#fold_text +function! latextoc#fold_text() + let parts = matchlist(getline(v:foldstart), '^\(.*\)\t\(.*\)$') + return printf('%-8s%-72s', parts[1], parts[2]) +endfunction + +" }}}1 + +" vim:fdm=marker:ff=unix diff --git a/doc/latex.txt b/doc/latex.txt new file mode 100644 index 0000000..21be13e --- /dev/null +++ b/doc/latex.txt @@ -0,0 +1,745 @@ +*latex.txt* LaTeX plugin for Vim version 7.3 (and above) +*vim-latex* + +Author: Karl Yngve Lervåg +License: MIT license {{{ + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to + deal in the Software without restriction, including without limitation the + rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + sell copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +}}} + +============================================================================== +CONTENTS *vim-latex-contents* + + 1. Intro ................................ |vim-latex-intro| + 2. Main interface ....................... |vim-latex-main| + 3. Default mappings ..................... |vim-latex-mappings| + 4. Options .............................. |vim-latex-options| + 5. Omni completion ...................... |vim-latex-completion| + 6. Folding .............................. |vim-latex-folding| + 7. Latexmk .............................. |vim-latex-latexmk| + 8. Motion ............................... |vim-latex-motion| + 9. Table of contents .................... |vim-latex-toc| + 10. Function reference ................... |vim-latex-functions| + 11. Limitations .......................... |vim-latex-limitations| + +============================================================================== +INTRO *vim-latex-intro* + +This vim plugin provides some convenience functionality for editing LaTeX +documents. The goal has been to create a minimalistic and functional plugin +that is easy to customize and evolve. Most of the functionality provided is +turned on by default, but the user may turn off features that are not desired. +The plugin will hereafter be referred to as |vim-latex|. + +============================================================================== +MAIN INTERFACE *vim-latex-main* + +The |vim-latex| interface is based on the |autoload| feature of vim. For each +new latex buffer, the function *latex#init* initializes the variables and +functionalities based on the desired options |vim-latex-options|. The first +run performs some global initialization, which is mainly to define +autocommands. Other functionality is provided by additional autoloaded +scripts, such as |vim-latex-latexmk|. Every additional script should have an +initialization script that sets default mappings, creates autocommands and +performs other necessary initialization. This initialization script is then +called from |latex#init|. + +The main interface provides some basic functionality. |latex#view|, by +default mapped to 'lv', opens the current output file in +a viewer. |latex#help| lists all mappings that are defined specifically for +the current buffer, by default mapped to 'lh'. If the default +mappings are used, |latex#help| will display them. |latex#info| echoes the +contents of |g:latex#data| and |b:latex|. This is useful mainly for +debugging. Finally, |latex#reinit| clears the current data in |g:latex#data| +and |b:latex|, stops running latexmk processes |latex#latexmk#stop_all|, and +then performs a new initialization with |latex#init|. + +For each latex project that is opened, a |Dictionary| is created and added to +the list |g:latex#data|. The dictionary is initialized with information tied +to the current project, mainly the different associated file names. In +addition, a dictonary is created for every buffer |b:latex|. This contains +data that is specific to the current buffer, most importantly it contains the +ID of the main latex project. This combination of local and global data +enables |vim-latex| to work properly for multi-file latex projects. It also +allows the editing of different latex projects simultaneously in different +buffers. + + *g:latex#data* +A |List| of |Dictionaries|, each containing data specific to a given latex +project. The ID of a latex project is the index of its dictonary in the list. +An example of a dictionary is given here: > + + { + 'pid': 0, + 'name': 'main', + 'base': 'main.tex', + 'root': '/some/path', + 'tex': '/some/path/main.tex', + 'aux': function('202'), + 'log': function('203'), + 'out': function('204'), + } + +Which entries are present in a given dictionary depends on the current +settings. For example, the entry `pid` is only available if +|vim-latex-latexmk| is enabled |g:latex_latexmk_enabled|. Note that some of +the file entries are functions. The functions are used to find the files when +necessary, in case they do not exist when the latex file is opened. All +defined dictionaries can be printed with the function |latex#info|, by default +mapped to 'li'. + + *b:latex* +For each buffer, |vim-latex| defines a |Dictionary| that contains buffer local +information. An example of such a dictonary is: > + + { + 'id': 0, + 'fold_sections': [], + } + +The most important entry is `id`, which is the index of the corresponding +entry in the list of latex projects |g:latex#data|. Other entries may also be +exist, such as `fold_sections`. The dictionary can be printed with the +function |latex#info|, by default mapped to 'li'. + +Functions: + |latex#info| + |latex#help| + |latex#reinit| + |latex#view| + +============================================================================== +DEFAULT MAPPINGS *vim-latex-mappings* + +The default mappings for |vim-latex| are given below. See 'map-listing' for +an explanation of the format. The function |latex#help| is provided for +convenience to list all the defined mappings, and it is by default mapped to +'lh'. The default mappings may be disabled with the option +|g:latex_default_mappings|, if the user wishes to create his own mappings. + +n % *@:call latex#motion#find_matching_pair('n') +v % *@:call latex#motion#find_matching_pair('v') +o % *@:call latex#motion#find_matching_pair('o') + +v a$ *@:latex#motion#select_inline_math('outer') +v i$ *@:latex#motion#select_inline_math('inner') +o a$ *@:normal va$ +o i$ *@:normal vi$ + +v ae *@:latex#motion#select_current_env('outer') +v ie *@:latex#motion#select_current_env('inner') +o ae *@:normal vae +o ie *@:normal vie + +n [[ *@:call latex#motion#next_sec(0,1,0) +n [] *@:call latex#motion#next_sec(1,1,0) +n ][ *@:call latex#motion#next_sec(1,0,0) +n ]] *@:call latex#motion#next_sec(0,0,0) +v [[ *@:call latex#motion#next_sec(0,1,1) +v [] *@:call latex#motion#next_sec(1,1,1) +v ][ *@:call latex#motion#next_sec(1,0,1) +v ]] *@:call latex#motion#next_sec(0,0,1) +o [[ *@:normal v[[ +o [] *@:normal v[] +o ][ *@:normal v][ +o ]] *@:normal v]] + +n li *@:call latex#info() +n lh *@:call latex#help() +n lv *@:call latex#view() +n lR *@:call latex#reinit() +n lt *@:call latex#toc#open() +n lT *@:call latex#toc#toggle() +n ll *@:call latex#latexmk#compile() +n lk *@:call latex#latexmk#stop(1) +n lK *@:call latex#latexmk#stop_all() +n le *@:call latex#latexmk#errors() +n lg *@:call latex#latexmk#status() +n lG *@:call latex#latexmk#status(1) +n lc *@:call latex#latexmk#clean() +n lC *@:call latex#latexmk#clean(1) + +n zx *@:call latex#fold#refresh()zx +n *@:call latex#change#environment_toggle_star() +n *@:call latex#change#environment_prompt() + +============================================================================== +OPTIONS *vim-latex-options* + +This section describes the options for |vim-latex|. The options are first +listed in alphabetical order, before they are described in more detail. The +descriptions also list the default values. + +Overview:~ + + |g:latex_build_dir| + |g:latex_complete_close_braces| + |g:latex_complete_enabled| + |g:latex_complete_patterns| + |g:latex_default_mappings| + |g:latex_errorformat_ignore_warnings| + |g:latex_errorformat_show_warnings| + |g:latex_fold_enabled| + |g:latex_fold_envs| + |g:latex_fold_parts| + |g:latex_fold_preamble| + |g:latex_fold_sections| + |g:latex_latexmk_autojump| + |g:latex_latexmk_enabled| + |g:latex_latexmk_options| + |g:latex_latexmk_output| + |g:latex_main_tex_candidates| + |g:latex_motion_close_pats| + |g:latex_motion_enabled| + |g:latex_motion_matchparen| + |g:latex_motion_open_pats| + |g:latex_toc_enabled| + |g:latex_toc_fold_levels| + |g:latex_toc_fold| + |g:latex_toc_hide_help| + |g:latex_toc_resize| + |g:latex_toc_split_side| + |g:latex_toc_width| + |g:latex_viewer| + +------------------------------------------------------------------------------ +Detailed descriptions and default values:~ + + *g:latex_build_dir* +Set this variable in case a dedicated build dir is used with latexmk/latex +compilations. > + let g:latex_build_dir = '.' +< + + *g:latex_complete_close_braces* +When a label or a cite has been completed, this option controls whether it +will be followed by a closing brace. > + let g:latex_complete_close_braces = 0 +< + + *g:latex_complete_enabled* +Use this option to prevent the plugin from setting the 'omnifunc': > + let g:latex_complete_enabled = 1 +< + + *g:latex_complete_patterns* +Define patterns that control when the label and citation completion is +triggered. > + let g:latex_complete_patterns = { + \ 'ref' : '\C\\v\?\(eq\|page\|[cC]\)\?ref\*\?\_\s*{[^{}]*', + \ 'bib' : '\C\\\a*cite\a*\*\?\(\[[^\]]*\]\)*\_\s*{[^{}]*', + \ }) +< + + *g:latex_default_mappings* +Whether to load the default mappings. If this is set to 0, then no mappings +will be defined. Since all of the functionality is available as functions, +this allows the user to define his or her own mappings. > + let g:latex_default_mappings = 1 +< + + *g:latex_errorformat_ignore_warnings* +List of warning messages that should be ignored. > + let g:latex_errorformat_ignore_warnings = [ + \ 'Underfull', + \ 'Overfull', + \ 'specifier changed to', + \ ] +< + + *g:latex_errorformat_show_warnings* +Show warnings in quickfix window. > + let g:latex_errorformat_show_warnings = 1 +< + + *g:latex_fold_enabled* +Use this option to disable/enable folding. > + let g:latex_fold_enabled = 1 +< + + *g:latex_fold_envs* +Decide whether environments should be folded or not. > + let g:latex_fold_envs = 1 +< + + *g:latex_fold_parts* +List of parts that should be folded. > + let g:latex_fold_parts = [ + \ "appendix", + \ "frontmatter", + \ "mainmatter", + \ "backmatter", + \ ] +< + + *g:latex_fold_preamble* +Decide whether preamble should be folded or not. > + let g:latex_fold_preamble = 1 +< + + *g:latex_fold_sections* +List of section constructs that should be folded. > + let g:latex_fold_sections = [ + \ "part", + \ "chapter", + \ "section", + \ "subsection", + \ "subsubsection", + \ ] +< + + *g:latex_latexmk_autojump* +Whether to automatically jump to the first error when the error window is +opened with the default mapping or |latex#latexmk#errors|. > + let g:latex_latexmk_autojump = '0' +< + + *g:latex_latexmk_enabled* +Whether to enable the latexmk interface or not. Note that even if it is not +enabled, the autoload functions will be available. However, the +necessary variables and autocommands will not be defined, and the mappings +will not be created. > + let g:latex_latexmk_enabled = 1 +< + + *g:latex_latexmk_options* +Set extra options for latexmk compilation. > + let g:latex_latexmk_options = '' +< + + *g:latex_latexmk_output* +Set desired output for latexmk compilation. > + let g:latex_latexmk_output = 'pdf' +< + + *g:latex_main_tex_candidates* +A list of common names for main tex-file candidates. This is used to find the +main tex file in multi-file projects. > + let g:latex_main_tex_candidates = [ + \ 'main', + \ 'memo', + \ 'note', + \ 'report', + \ 'thesis', + \] +< + + *g:latex_motion_close_pats* +Define a list of patterns that match closing braces and environments. > + let g:latex_motion_close_pats = [ + \ '}', + \ ')', + \ '\]', + \ '\\}', + \ '\\)', + \ '\\\]', + \ '\\end\s*{.\{-}}', + \ '\\right\s*\%([^\\]\|\\.\|\\\a*\)', + \ ] +< + + *g:latex_motion_enabled* +Whether to enable the motion interface. If it is disabled, then neither the +default mappings nor the autocommands that enable highlighting of matching +parens will be defined. > + let g:latex_motion_enabled = 1 +< + + *g:latex_motion_matchparen* +Enable highlighting of matching parens. This gives better support for LaTeX, +but disables the builtin |matchparen|. > + let g:latex_motion_matchparen = 1 +< + + *g:latex_motion_open_pats* +Define a list of patterns that match opening braces and environments. > + let g:latex_motion_open_pats = [ + \ '{', + \ '(', + \ '\[', + \ '\\{', + \ '\\(', + \ '\\\[', + \ '\\begin\s*{.\{-}}', + \ '\\left\s*\%([^\\]\|\\.\|\\\a*\)', + \ ] +< + + *g:latex_toc_enabled* +Enable interface for TOC. If it is disabled, then mappings for the TOC will +not be created. > + let g:latex_toc_enabled = 0 +< + + *g:latex_toc_fold_levels* +If TOC entries are folded, this option controls the number of fold levels that +will be used. > + let g:latex_toc_fold_levels = 0 +< + + *g:latex_toc_fold* +Turn on folding of TOC entries. > + let g:latex_toc_fold = 0 +< + + *g:latex_toc_hide_help* +Allow the TOC help text to be hidden. > + let g:latex_toc_hide_help = 0 +< + + *g:latex_toc_resize* +Automatically resize vim when the TOC window is opened. > + let g:latex_toc_resize = 1 +< + + *g:latex_toc_split_side* +Define where the TOC window is opened. > + let g:latex_toc_split_side = 'leftabove' +< + + *g:latex_toc_width* +Set width of TOC window. > + let g:latex_toc_width = 30 +< + + *g:latex_viewer* +Set default viewer application. > + let g:latex_viewer = 'xdg-open' +< + +============================================================================== +OMNI COMPLETION *vim-latex-completion* + +|vim-latex| provides an 'omnifunc' for omni completion, see |compl-omni| and +|latex#complete#omnifunc|. The function is enabled by default, but may be +disabled with |g:latex_complete_enabled|. Omni completion is accessible with +'i_'. + +The omni function completes labels and citations. The completion candidates +are gathered with the functions |latex#complete#labels| and +|latex#complete#bibtex|. If |g:latex_complete_close_braces| is set to 1, then +the completion includes closing braces. + +Associated settings: + |g:latex_complete_enabled| + |g:latex_complete_patterns.ref| + |g:latex_complete_patterns.bib| + |g:latex_complete_close_braces| + +Functions: + |latex#complete#omnifunc| + |latex#complete#labels| + |latex#complete#bibtex| + +------------------------------------------------------------------------------ +Complete labels~ + +Label completion is triggered by '\ref{' commands as defined with +|g:latex_complete_patterns.ref|. The completion parses every relevant aux +file to gather the completion candidates. This is important to note, because +it means that the completion only works when the latex document has been +compiled. + +As an example: > + + \ref{sec: + +offers a list of all matching labels, with their associated value and page +number. The label completion matches: + + 1. labels: > + \ref{sec: +< 2. numbers: > + \eqref{2 +< 3. labels and numbers together (separated by whitespace): > + \eqref{eq 2 + +------------------------------------------------------------------------------ +Complete citations~ + +Citation completion is triggered by '\cite{' commands as defined with +|g:latex_complete_patterns.bib|. The completion parses included bibliography +files (`*.bib`) and `thebibliography` environments to gather the completion +candidates. + +As an example, assume that a bibliography file is included with the following +entry: > + + @book { knuth1981, + author = "Donald E. Knuth", + title = "Seminumerical Algorithms", + publisher = "Addison-Wesley", + year = "1981" } + +Then the bibliography key `knuth1981` will be completed with e.g.: > + + \cite{Knuth 1981 + \cite{algo + \cite{Don.*Knuth + +In particular, note that regular expressions (or vim patterns) can be used +after '\cite{' to search for candidates. + +============================================================================== +FOLDING *vim-latex-folding* + +LatexBox can fold texts according to LaTeX structure (part, chapter, section +and subsection). Folding is turned on by default, but it can be disabled if +desired |g:latex_fold_enabled|. + +When folding is turned on and a latex document is opened, the document is +parsed once in order to define the highest fold level based on which parts +(such as frontmatter, backmatter, and appendix) and section types (chapter, +section, etc.) are present. If the document has been edited and a new fold +level is required (or has become redundant), then |latex#fold#refresh| can be +used to refresh the fold level settings. This function is mapped by default +to `zx`. + +Associated settings: + |g:latex_fold_enabled| + |g:latex_fold_preamble| + |g:latex_fold_parts| + |g:latex_fold_sections| + |g:latex_fold_envs| + +Functions: + |latex#fold#level| + |latex#fold#text| + |latex#fold#refresh| + +============================================================================== +LATEXMK *vim-latex-latexmk* + +|vim-latex| provides a basic interface to `latexmk` for background +compilation. The interface may be disabled with |g:latex_latexmk_enabled|. +The default mappings are: > + + nnoremap ll :call latex#latexmk#compile() + nnoremap lk :call latex#latexmk#stop(1) + nnoremap lK :call latex#latexmk#stop_all() + nnoremap le :call latex#latexmk#errors() + nnoremap lg :call latex#latexmk#status(0) + nnoremap lG :call latex#latexmk#status(1) + nnoremap lc :call latex#latexmk#clean(0) + nnoremap lC :call latex#latexmk#clean(1) + +The background compilation is started with |latex#latexmk#compile|, and relies +on the preview continuous mode of `latexmk`. Compilation errors are not +parsed automatically, since there is no way of knowing when the document has +been compiled. To view errors, use |latex#latexmk#errors|. To check if +compilation is running in the background, use |latex#latexmk#status|. + +Associated settings: + |g:latex_latexmk_enabled| + |g:latex_latexmk_autojump| + |g:latex_latexmk_options| + |g:latex_latexmk_output| + +Functions: + |latex#latexmk#clean| + |latex#latexmk#compile| + |latex#latexmk#errors| + |latex#latexmk#status| + |latex#latexmk#stop| + |latex#latexmk#stop_all| + +============================================================================== +MOTION *vim-latex-motion* + +|vim-latex| provides some functions that can be used to give improved motions +and text objects. |latex#motion#find_matching_pair| is also used to enable +highlighting of matching parens or tags (such as begin/end structures). The +functionality is enabled by default, but may be disabled if desired. + +Associated settings: + |g:latex_motion_enabled| + |g:latex_motion_matchparen| + |g:latex_motion_open_pats| + |g:latex_motion_close_pats| + +Functions: + |latex#motion#find_matching_pair| + |latex#motion#next_sec| + |latex#motion#select_current_env| + |latex#motion#select_inline_math| + +============================================================================== +TABLE OF CONTENTS *vim-latex-toc* + +|vim-latex| provides a table of contents (TOC) window that can be opened +|latex#toc#open| or toggled |latex#toc#toggle|. In the TOC, one can use +'' on a selected entry to navigate. + +Associated settings: + |g:latex_toc_enabled| + |g:latex_toc_fold_levels| + |g:latex_toc_fold| + |g:latex_toc_hide_help| + |g:latex_toc_resize| + |g:latex_toc_split_side| + |g:latex_toc_width| + +Functions: + |latex#toc#open| + |latex#toc#toggle| + +============================================================================== +FUNCTION REFERENCE *vim-latex-functions* + +The following is a reference of the available functions, sorted +alphabetically. First a short overview is given, then more detailed +descriptions follow. + +Overview:~ + + |latex#complete#omnifunc| + |latex#complete#labels| + |latex#complete#bibtex| + |latex#fold#level| + |latex#fold#text| + |latex#fold#refresh| + |latex#help| + |latex#info| + |latex#latexmk#clean| + |latex#latexmk#compile| + |latex#latexmk#errors| + |latex#latexmk#status| + |latex#latexmk#stop| + |latex#latexmk#stop_all| + |latex#motion#find_matching_pair| + |latex#motion#next_sec| + |latex#motion#select_current_env| + |latex#motion#select_inline_math| + |latex#reinit| + |latex#toc#open| + |latex#toc#toggle| + |latex#view| + +------------------------------------------------------------------------------ +Detailed descriptions:~ + + *latex#complete#omnifunc* +An 'omnifunc' for label and citation completion, see |vim-latex-completion|. + + *latex#complete#labels* +Parses aux files to gather label candidates for completion. + + *latex#complete#bibtex* +Parses included bibliography files and `thebibliography` environments to +gather candidates for completion. + + *latex#fold#level* +Sets fold level for each line. 'foldexpr' |fold-expr| + + *latex#fold#text* +Sets fold title text. 'foldtext' + + *latex#fold#refresh* +Refreshes fold levels based on which parts and sections used in the current +file buffer. + + *latex#help* +Lists all mappings that are defined specifically for the current buffer. If +the default mappings are used, then |latex#help| will display them. + + *latex#info* +Echoes the contents of |g:latex#data| and |b:latex|. This is useful mainly +for debugging. + + *latex#latexmk#clean* +Clean up temporary files with `latexmk -c`. An optional argument may be +supplied to indicate that the `-C` flag should be used, which also cleans +output files. This will only be run if `latexmk` is not already running. + + *latex#latexmk#compile* +Starts `latexmk -pvc ...` for the given buffer, if it is not already running. + + *latex#latexmk#errors* +Displays the log file in the quickfix window. The output may be controlled +slightly with the options |g:latex_errorformat_show_warnings| and +|g:latex_errorformat_ignore_warnings|. + + *latex#latexmk#status* +Show if the `latexmk` has been started for the current buffer. An optional +argument may be supplied, in which case the status for all buffers is shown. + + *latex#latexmk#stop* +Stop the `latexmk` process running for the current buffer. An optional +argument may be given, in which case the function becomes verbose. + + *latex#latexmk#stop_all* +Stops all running `latexmk` processes. + + *latex#motion#find_matching_pair* +Finds a matching pair of parenthesis or begin-end-tags. The functions is used + + *latex#motion#next_sec* +A motion command function that moves to the next section. Used to redefine +the mappings for ']]', '[[', and similar. + + *latex#motion#select_current_env* +A function that is used to create text objects for environments. + + *latex#motion#select_inline_math* +A function that is used to create text objects for inline math structures. + + *latex#reinit* +Clears the current global and local data in |g:latex#data| and |b:latex|, +stops running latexmk processes |latex#latexmk#stop_all|, and then performs +a new initialization. + + *latex#toc#open* +Open the TOC window. If it is already open, then simply move the cursor to +the TOC window. + + *latex#toc#toggle* +Toggle TOC window: If TOC window is open, then close it. If it is closed, +then open it (but do not move the cursor to the window). + + *latex#view* +Open the output file (specified with |g:latex_latexmk_output|) with the +default viewer |g:latex_viewer|. + +============================================================================== +LIMITATIONS *vim-latex-limitations* + +This plugin is written for Linux/Unix environments, and so it does NOT support +Windows. I do not plan to add support. + +============================================================================== +CREDITS *vim-latex-credits* + +|vim-latex| is developed by Karl Yngve Lervåg , and is +distributed under the MIT license. The project is available as a Git +repository: https://github.com/lervag/vim-latex. + +|vim-latex| was developed from scratch, but much of the code has been based on +LaTeX-Box: https://github.com/LaTeX-Box-Team/LaTeX-Box. LaTeX-suite was also +an inspiration: http://vim-latex.sourceforge.net/. + +The documentation of |vim-latex| is structured with inspiration from CtrlP +(https://github.com/kien/ctrlp.vim), simply because CtrlP has a very good +documentation. + +============================================================================== +CHANGELOG *vim-latex-changelog* + +First public release: 2013/10/05~ + +============================================================================== + vim:tw=78:ts=8:ft=help:norl: diff --git a/ftplugin/latextoc.vim b/ftplugin/latextoc.vim new file mode 100644 index 0000000..1642503 --- /dev/null +++ b/ftplugin/latextoc.vim @@ -0,0 +1,134 @@ +" LaTeX plugin for Vim +" +" Maintainer: Karl Yngve Lervåg +" Email: karl.yngve@gmail.com +" + +if exists('b:did_ftplugin') + finish +endif +let b:did_ftplugin = 1 + +" Set local buffer settings +setlocal buftype=nofile +setlocal bufhidden=wipe +setlocal nobuflisted +setlocal noswapfile +setlocal nowrap +setlocal nonumber +setlocal nolist +setlocal nospell +setlocal cursorline +setlocal tabstop=8 +setlocal cole=0 +setlocal cocu=nvic +if g:latex_toc_fold + setlocal foldmethod=expr + setlocal foldexpr=toc#fold(v:lnum) + setlocal foldtext=toc#fold_tex() +endif + +" Define mappings +nnoremap G G4k +nnoremap OA k +nnoremap OB j +nnoremap OC l +nnoremap OD h +nnoremap s :call toc_toggle_numbers() +nnoremap q :call toc_close() +nnoremap :call toc_close() +nnoremap :call toc_activate(0) +nnoremap :call toc_activate(0) +nnoremap :call toc_activate(1) +nnoremap <2-leftmouse> :call toc_activate(1) + +" {{{1 s:toc_activate +function! s:toc_activate(close) + let n = getpos('.')[1] - 1 + + if n >= len(b:toc) + return + endif + + let entry = b:toc[n] + + let titlestr = s:toc_escape_title(entry['text']) + + " Search for duplicates + " + let i=0 + let entry_hash = entry['level'].titlestr + let duplicates = 0 + while i 0 + if search('\\' . entry['level'] . '\_\s*{' . titlestr . '}', 'ws') + let duplicates -= 1 + endif + endwhile + + if search('\\' . entry['level'] . '\_\s*{' . titlestr . '}', 'ws') + normal zv + endif + + if a:close + if g:latex_toc_resize + silent exe "set columns-=" . g:latex_toc_width + endif + execute 'bwipeout ' . toc_bnr + else + execute toc_wnr . 'wincmd w' + endif +endfunction + +" {{{1 s:toc_close +function! s:toc_close() + if g:latex_toc_resize + silent exe "set columns-=" . g:latex_toc_width + endif + bwipeout +endfunction + +" {{{1 s:toc_escape_title +function! s:toc_escape_title(titlestr) + " Credit goes to Marcin Szamotulski for the following fix. It allows to + " match through commands added by TeX. + + let titlestr = substitute(a:titlestr, '\\\w*\>\s*\%({[^}]*}\)\?', '.*', 'g') + let titlestr = escape(titlestr, '\') + return substitute(titlestr, ' ', '\\_\\s\\+', 'g') +endfunction + +" {{{1 s:toc_toggle_numbers +function! s:toc_toggle_numbers() + if b:toc_numbers + setlocal conceallevel=3 + let b:toc_numbers = 0 + else + setlocal conceallevel=0 + let b:toc_numbers = 1 + endif +endfunction +" }}}1 + +" vim:fdm=marker:ff=unix diff --git a/ftplugin/tex.vim b/ftplugin/tex.vim new file mode 100644 index 0000000..0420cf0 --- /dev/null +++ b/ftplugin/tex.vim @@ -0,0 +1,105 @@ +" LaTeX plugin for Vim +" +" Maintainer: Karl Yngve Lervåg +" Email: karl.yngve@gmail.com +" + +if exists('b:did_ftplugin') + finish +endif +let b:did_ftplugin = 1 + +" Set default options +" {{{1 Completion +call latex#util#set_default('g:latex_complete_enabled', 1) +call latex#util#set_default('g:latex_complete_close_braces', 0) +call latex#util#set_default('g:latex_complete_patterns', { + \ 'ref' : '\C\\v\?\(eq\|page\|[cC]\)\?ref\*\?\_\s*{[^{}]*', + \ 'bib' : '\C\\\a*cite\a*\*\?\(\[[^\]]*\]\)*\_\s*{[^{}]*', + \ }) + +" {{{1 Folding +call latex#util#set_default('g:latex_fold_enabled', 1) +call latex#util#set_default('g:latex_fold_preamble', 1) +call latex#util#set_default('g:latex_fold_envs', 1) +call latex#util#set_default('g:latex_fold_parts', + \ [ + \ "appendix", + \ "frontmatter", + \ "mainmatter", + \ "backmatter", + \ ]) +call latex#util#set_default('g:latex_fold_sections', + \ [ + \ "part", + \ "chapter", + \ "section", + \ "subsection", + \ "subsubsection", + \ ]) + +" {{{1 Latexmk +call latex#util#set_default('g:latex_latexmk_enabled', 1) +call latex#util#set_default('g:latex_latexmk_options', '') +call latex#util#set_default('g:latex_latexmk_output', 'pdf') +call latex#util#set_default('g:latex_latexmk_autojump', '0') + +" {{{1 Miscelleneous +call latex#util#set_default('g:latex_default_mappings', 1) +call latex#util#set_default('g:latex_viewer', 'xdg-open') +call latex#util#set_default('g:latex_build_dir', '.') +call latex#util#set_default('g:latex_main_tex_candidates', + \ [ + \ 'main', + \ 'memo', + \ 'note', + \ 'report', + \ 'thesis', + \]) +call latex#util#set_default('g:latex_errorformat_show_warnings', 1) +call latex#util#set_default('g:latex_errorformat_ignore_warnings', + \ [ + \ 'Underfull', + \ 'Overfull', + \ 'specifier changed to', + \ ]) + +" {{{1 Motion +call latex#util#set_default('g:latex_motion_enabled', 1) +call latex#util#set_default('g:latex_motion_matchparen', 1) +call latex#util#set_default('g:latex_motion_open_pats', + \ [ + \ '{', + \ '(', + \ '\[', + \ '\\{', + \ '\\(', + \ '\\\[', + \ '\\begin\s*{.\{-}}', + \ '\\left\s*\%([^\\]\|\\.\|\\\a*\)', + \ ]) +call latex#util#set_default('g:latex_motion_close_pats', + \ [ + \ '}', + \ ')', + \ '\]', + \ '\\}', + \ '\\)', + \ '\\\]', + \ '\\end\s*{.\{-}}', + \ '\\right\s*\%([^\\]\|\\.\|\\\a*\)', + \ ]) + +" {{{1 Toc +call latex#util#set_default('g:latex_toc_enabled', 1) +call latex#util#set_default('g:latex_toc_width', 30) +call latex#util#set_default('g:latex_toc_split_side', 'leftabove') +call latex#util#set_default('g:latex_toc_resize', 1) +call latex#util#set_default('g:latex_toc_hide_help', 0) +call latex#util#set_default('g:latex_toc_fold', 0) +call latex#util#set_default('g:latex_toc_fold_levels', 0) +" }}}1 + +call latex#init() + +" vim:fdm=marker:ff=unix diff --git a/syntax/latextoc.vim b/syntax/latextoc.vim new file mode 100644 index 0000000..d9f1327 --- /dev/null +++ b/syntax/latextoc.vim @@ -0,0 +1,10 @@ +syntax match helpText /^.*: .*/ +syntax match secNum /^\S\+\(\.\S\+\)\?\s*/ contained conceal +syntax match secLine /^\S\+\t.\+/ contains=secNum +syntax match mainSecLine /^[^\.]\+\t.*/ contains=secNum +syntax match ssubSecLine /^[^\.]\+\.[^\.]\+\.[^\.]\+\t.*/ contains=secNum + +highlight link helpText PreProc +highlight link secNum Number +highlight link mainSecLine Title +highlight link ssubSecLine Comment