diff options
Diffstat (limited to 'ftplugin')
-rw-r--r-- | ftplugin/ansible.vim | 2 | ||||
-rw-r--r-- | ftplugin/latex-box/common.vim | 417 | ||||
-rw-r--r-- | ftplugin/latex-box/complete.vim | 936 | ||||
-rw-r--r-- | ftplugin/latex-box/findmain.vim | 66 | ||||
-rw-r--r-- | ftplugin/latex-box/folding.vim | 382 | ||||
-rw-r--r-- | ftplugin/latex-box/latexmk.vim | 558 | ||||
-rw-r--r-- | ftplugin/latex-box/mappings.vim | 110 | ||||
-rw-r--r-- | ftplugin/latex-box/motion.vim | 548 | ||||
-rw-r--r-- | ftplugin/latextoc.vim | 206 | ||||
-rw-r--r-- | ftplugin/tex_LatexBox.vim | 37 | ||||
-rw-r--r-- | ftplugin/vue.vim | 7 |
11 files changed, 3268 insertions, 1 deletions
diff --git a/ftplugin/ansible.vim b/ftplugin/ansible.vim index bbfd05df..e068dd3a 100644 --- a/ftplugin/ansible.vim +++ b/ftplugin/ansible.vim @@ -4,6 +4,6 @@ if !exists('g:polyglot_disabled') || index(g:polyglot_disabled, 'ansible') == -1 if exists('+regexpengine') && ('®expengine' == 0) setlocal regexpengine=1 endif -set path+=./../templates,./../files +set path+=./../templates,./../files,templates,files endif diff --git a/ftplugin/latex-box/common.vim b/ftplugin/latex-box/common.vim new file mode 100644 index 00000000..20488846 --- /dev/null +++ b/ftplugin/latex-box/common.vim @@ -0,0 +1,417 @@ +if !exists('g:polyglot_disabled') || index(g:polyglot_disabled, 'latex') == -1 + +" LaTeX Box common functions + +" Error Format {{{ +" Note: The error formats assume we're using the -file-line-error with +" [pdf]latex. +" Note: See |errorformat-LaTeX| for more info. + +" Check for options +if !exists("g:LatexBox_show_warnings") + let g:LatexBox_show_warnings=1 +endif +if !exists("g:LatexBox_ignore_warnings") + let g:LatexBox_ignore_warnings = + \['Underfull', + \ 'Overfull', + \ 'specifier changed to'] +endif + +" Standard error message formats +" Note: We consider statements that starts with "!" as 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<argument>\ %m + +" More info for some errors +setlocal efm+=%Cl.%l\ %m + +" Show or ignore warnings +if g:LatexBox_show_warnings + " Parse biblatex warnings + setlocal efm+=%-C(biblatex)%.%#in\ t%.%# + setlocal efm+=%-C(biblatex)%.%#Please\ v%.%# + setlocal efm+=%-C(biblatex)%.%#LaTeX\ a%.%# + setlocal efm+=%-Z(biblatex)%m + + " Parse hyperref warnings + setlocal efm+=%-C(hyperref)%.%#on\ input\ line\ %l. + + for w in g:LatexBox_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 +else + setlocal efm+=%-WLaTeX\ %.%#Warning:\ %.%#line\ %l%.%# + setlocal efm+=%-W%.%#\ at\ lines\ %l--%*\\d + setlocal efm+=%-WLaTeX\ %.%#Warning:\ %m + setlocal efm+=%-W%.%#Warning:\ %m +endif + +" Push file to file stack +setlocal efm+=%+P**%f +setlocal efm+=%+P**\"%f\" + +" Ignore unmatched lines +setlocal efm+=%-G%.%# +" }}} + +" Vim Windows {{{ + +" Type of split, "new" for horiz. "vnew" for vert. +if !exists('g:LatexBox_split_type') + let g:LatexBox_split_type = "vnew" +endif + +" Length of vertical splits +if !exists('g:LatexBox_split_length') + let g:LatexBox_split_length = 15 +endif + +" Width of horizontal splits +if !exists('g:LatexBox_split_width') + let g:LatexBox_split_width = 30 +endif + +" Where splits appear +if !exists('g:LatexBox_split_side') + let g:LatexBox_split_side = "aboveleft" +endif + +" Resize when split? +if !exists('g:LatexBox_split_resize') + let g:LatexBox_split_resize = 0 +endif + +" Toggle help info +if !exists('g:LatexBox_toc_hidehelp') + let g:LatexBox_toc_hidehelp = 0 +endif +" }}} + +" Filename utilities {{{ +function! LatexBox_GetMainTexFile() + + " 1. check for the b:main_tex_file variable + if exists('b:main_tex_file') && filereadable(b:main_tex_file) + return b:main_tex_file + endif + + + " 2. scan the first few lines of the file for root = filename + for linenum in range(1,5) + let linecontents = getline(linenum) + if linecontents =~ 'root\s*=' + " Remove everything but the filename + let b:main_tex_file = substitute(linecontents, + \ '.*root\s*=\s*', "", "") + let b:main_tex_file = substitute(b:main_tex_file, '\s*$', "", "") + " Prepend current directory if this isn't an absolute path + if b:main_tex_file !~ '^/' + let b:main_tex_file = expand('%:p:h') . '/' . b:main_tex_file + endif + let b:main_tex_file = fnamemodify(b:main_tex_file, ":p") + if b:main_tex_file !~ '\.tex$' + let b:main_tex_file .= '.tex' + endif + return b:main_tex_file + endif + endfor + + " 3. scan current file for "\begin{document}" + if &filetype == 'tex' && search('\m\C\\begin\_\s*{document}', 'nw') != 0 + return expand('%:p') + endif + + " 4. use 'main.tex' if it exists in the same directory (and is readable) + let s:main_dot_tex_file=expand('%:p:h') . '/main.tex' + if filereadable(s:main_dot_tex_file) + let b:main_tex_file=s:main_dot_tex_file + return b:main_tex_file + endif + + " 5. borrow the Vim-Latex-Suite method of finding it + if LatexBox_GetMainFileName() != expand('%:p') + let b:main_tex_file = LatexBox_GetMainFileName() + return b:main_tex_file + endif + + " 6. prompt for file with completion + let b:main_tex_file = s:PromptForMainFile() + return b:main_tex_file +endfunction + +function! s:PromptForMainFile() + let saved_dir = getcwd() + execute 'cd ' . fnameescape(expand('%:p:h')) + + " Prompt for file + let l:file = '' + while !filereadable(l:file) + let l:file = input('main LaTeX file: ', '', 'file') + if l:file !~ '\.tex$' + let l:file .= '.tex' + endif + endwhile + let l:file = fnamemodify(l:file, ':p') + + " Make persistent + let l:persistent = '' + while l:persistent !~ '\v^(y|n)' + let l:persistent = input('make choice persistent? (y, n) ') + if l:persistent == 'y' + call writefile([], l:file . '.latexmain') + endif + endwhile + + execute 'cd ' . fnameescape(saved_dir) + return l:file +endfunction + +" Return the directory of the main tex file +function! LatexBox_GetTexRoot() + return fnamemodify(LatexBox_GetMainTexFile(), ':h') +endfunction + +function! LatexBox_GetBuildBasename(with_dir) + " 1. Check for g:LatexBox_jobname + if exists('g:LatexBox_jobname') + return g:LatexBox_jobname + endif + + " 2. Get the basename from the main tex file + if a:with_dir + return fnamemodify(LatexBox_GetMainTexFile(), ':r') + else + return fnamemodify(LatexBox_GetMainTexFile(), ':t:r') + endif +endfunction + +function! LatexBox_GetAuxFile() + " 1. check for b:build_dir variable + if exists('b:build_dir') && isdirectory(b:build_dir) + return b:build_dir . '/' . LatexBox_GetBuildBasename(0) . '.aux' + endif + + " 2. check for g:LatexBox_build_dir variable + if exists('g:LatexBox_build_dir') && isdirectory(g:LatexBox_build_dir) + return g:LatexBox_build_dir . '/' . LatexBox_GetBuildBasename(0) . '.aux' + endif + + " 3. use the base name of main tex file + return LatexBox_GetBuildBasename(1) . '.aux' +endfunction + +function! LatexBox_GetLogFile() + " 1. check for b:build_dir variable + if exists('b:build_dir') && isdirectory(b:build_dir) + return b:build_dir . '/' . LatexBox_GetBuildBasename(0) . '.log' + endif + + " 2. check for g:LatexBox_build_dir variable + if exists('g:LatexBox_build_dir') && isdirectory(g:LatexBox_build_dir) + return g:LatexBox_build_dir . '/' . LatexBox_GetBuildBasename(0) . '.log' + endif + + " 3. use the base name of main tex file + return LatexBox_GetBuildBasename(1) . '.log' +endfunction + +function! LatexBox_GetOutputFile() + " 1. check for b:build_dir variable + if exists('b:build_dir') && isdirectory(b:build_dir) + return b:build_dir . '/' . LatexBox_GetBuildBasename(0) + \ . '.' . g:LatexBox_output_type + endif + + " 2. check for g:LatexBox_build_dir variable + if exists('g:LatexBox_build_dir') && isdirectory(g:LatexBox_build_dir) + return g:LatexBox_build_dir . '/' . LatexBox_GetBuildBasename(0) + \ . '.' . g:LatexBox_output_type + endif + + " 3. use the base name of main tex file + return LatexBox_GetBuildBasename(1) . '.' . g:LatexBox_output_type +endfunction +" }}} + +" View Output {{{ + +" Default pdf viewer +if !exists('g:LatexBox_viewer') + " On windows, 'running' a file will open it with the default program + let s:viewer = '' + if has('unix') + " echo -n necessary as uname -s will append \n otherwise + let s:uname = system('echo -n $(uname -s)') + if s:uname == "Darwin" + let s:viewer = 'open' + else + let s:viewer = 'xdg-open' + endif + endif + let g:LatexBox_viewer = s:viewer +endif + +function! LatexBox_View(...) + let lvargs = join(a:000, ' ') + let outfile = LatexBox_GetOutputFile() + if !filereadable(outfile) + echomsg fnamemodify(outfile, ':.') . ' is not readable' + return + endif + let cmd = g:LatexBox_viewer . ' ' . lvargs . ' ' . shellescape(outfile) + if has('win32') + let cmd = '!start /b ' . cmd . ' >nul' + else + let cmd = '!' . cmd . ' ' + if fnamemodify(&shell, ':t') ==# 'fish' + let cmd .= ' >/dev/null ^/dev/null &' + else + let cmd .= ' &>/dev/null &' + endif + endif + silent execute cmd + if !has("gui_running") + redraw! + endif +endfunction + +command! -nargs=* LatexView call LatexBox_View('<args>') +" }}} + +" In Comment {{{ + +" LatexBox_InComment([line], [col]) +" return true if inside comment +function! LatexBox_InComment(...) + 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 +" }}} + +" Get Current Environment {{{ + +" LatexBox_GetCurrentEnvironment([with_pos]) +" Returns: +" - environment +" if with_pos is not given +" - [envirnoment, lnum_begin, cnum_begin, lnum_end, cnum_end] +" if with_pos is nonzero +function! LatexBox_GetCurrentEnvironment(...) + if a:0 > 0 + let with_pos = a:1 + else + let with_pos = 0 + endif + + let begin_pat = '\C\\begin\_\s*{[^}]*}\|\\\@<!\\\[\|\\\@<!\\(' + let end_pat = '\C\\end\_\s*{[^}]*}\|\\\@<!\\\]\|\\\@<!\\)' + let saved_pos = getpos('.') + + " move to the left until on a backslash + let [bufnum, lnum, cnum, off] = getpos('.') + let line = getline(lnum) + while cnum > 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, + \ 'LatexBox_InComment()') + + 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, + \ 'LatexBox_InComment()') + + call setpos('.', saved_pos) + return [env, lnum1, cnum1, lnum2, cnum2] + else + call setpos('.', saved_pos) + return env + endif +endfunction +" }}} + +" Tex To Tree {{{ +" stores nested braces in a tree structure +function! LatexBox_TexToTree(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, LatexBox_TexToTree(strpart(a:str, i1, i2 - i1))) + let i1 = i2 + 1 + endif + endif + endwhile + return tree +endfunction +" }}} + +" Tree To Tex {{{ +function! LatexBox_TreeToTex(tree) + if type(a:tree) == type('') + return a:tree + else + return '{' . join(map(a:tree, 'LatexBox_TreeToTex(v:val)'), '') . '}' + endif +endfunction +" }}} + +" vim:fdm=marker:ff=unix:noet:ts=4:sw=4 + +endif diff --git a/ftplugin/latex-box/complete.vim b/ftplugin/latex-box/complete.vim new file mode 100644 index 00000000..aecb0d8d --- /dev/null +++ b/ftplugin/latex-box/complete.vim @@ -0,0 +1,936 @@ +if !exists('g:polyglot_disabled') || index(g:polyglot_disabled, 'latex') == -1 + +" LaTeX Box completion + +setlocal omnifunc=LatexBox_Complete + +" <SID> Wrap {{{ +function! s:GetSID() + return matchstr(expand('<sfile>'), '\zs<SNR>\d\+_\ze.*$') +endfunction +let s:SID = s:GetSID() +function! s:SIDWrap(func) + return s:SID . a:func +endfunction +" }}} + +" Completion {{{ +if !exists('g:LatexBox_completion_close_braces') + let g:LatexBox_completion_close_braces = 1 +endif +if !exists('g:LatexBox_bibtex_wild_spaces') + let g:LatexBox_bibtex_wild_spaces = 1 +endif + +if !exists('g:LatexBox_cite_pattern') + let g:LatexBox_cite_pattern = '\C\\\a*cite\a*\*\?\(\[[^\]]*\]\)*\_\s*{' +endif +if !exists('g:LatexBox_ref_pattern') + let g:LatexBox_ref_pattern = '\C\\v\?\(eq\|page\|[cC]\|labelc\|name\|auto\)\?ref\*\?\_\s*{' +endif + +if !exists('g:LatexBox_completion_environments') + let g:LatexBox_completion_environments = [ + \ {'word': 'itemize', 'menu': 'bullet list' }, + \ {'word': 'enumerate', 'menu': 'numbered list' }, + \ {'word': 'description', 'menu': 'description' }, + \ {'word': 'center', 'menu': 'centered text' }, + \ {'word': 'figure', 'menu': 'floating figure' }, + \ {'word': 'table', 'menu': 'floating table' }, + \ {'word': 'equation', 'menu': 'equation (numbered)' }, + \ {'word': 'align', 'menu': 'aligned equations (numbered)' }, + \ {'word': 'align*', 'menu': 'aligned equations' }, + \ {'word': 'document' }, + \ {'word': 'abstract' }, + \ ] +endif + +if !exists('g:LatexBox_completion_commands') + let g:LatexBox_completion_commands = [ + \ {'word': '\begin{' }, + \ {'word': '\end{' }, + \ {'word': '\item' }, + \ {'word': '\label{' }, + \ {'word': '\ref{' }, + \ {'word': '\eqref{eq:' }, + \ {'word': '\cite{' }, + \ {'word': '\chapter{' }, + \ {'word': '\section{' }, + \ {'word': '\subsection{' }, + \ {'word': '\subsubsection{' }, + \ {'word': '\paragraph{' }, + \ {'word': '\nonumber' }, + \ {'word': '\bibliography' }, + \ {'word': '\bibliographystyle' }, + \ ] +endif + +if !exists('g:LatexBox_complete_inlineMath') + let g:LatexBox_complete_inlineMath = 0 +endif + +if !exists('g:LatexBox_eq_env_patterns') + let g:LatexBox_eq_env_patterns = 'equation\|gather\|multiline\|align\|flalign\|alignat\|eqnarray' +endif + +" }}} + +"LatexBox_kpsewhich {{{ +function! LatexBox_kpsewhich(file) + let old_dir = getcwd() + execute 'lcd ' . fnameescape(LatexBox_GetTexRoot()) + let out = system('kpsewhich "' . a:file . '"') + + " 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 + + execute 'lcd ' . fnameescape(old_dir) + + return out +endfunction +"}}} + +" Omni Completion {{{ + +let s:completion_type = '' + +function! LatexBox_Complete(findstart, base) + if a:findstart + " return the starting position of the word + let line = getline('.') + let pos = col('.') - 1 + while pos > 0 && line[pos - 1] !~ '\\\|{' + let pos -= 1 + endwhile + + let line_start = line[:pos-1] + if line_start =~ '\m\C\\begin\_\s*{$' + let s:completion_type = 'begin' + elseif line_start =~ '\m\C\\end\_\s*{$' + let s:completion_type = 'end' + elseif line_start =~ '\m' . g:LatexBox_ref_pattern . '$' + let s:completion_type = 'ref' + elseif line_start =~ '\m' . g:LatexBox_cite_pattern . '$' + let s:completion_type = 'bib' + " check for multiple citations + let pos = col('.') - 1 + while pos > 0 && line[pos - 1] !~ '{\|,' + let pos -= 1 + endwhile + elseif s:LatexBox_complete_inlineMath_or_not() + let s:completion_type = 'inlineMath' + let pos = s:eq_pos + else + let s:completion_type = 'command' + if line[pos - 1] == '\' + let pos -= 1 + endif + endif + return pos + else + " return suggestions in an array + let suggestions = [] + + if s:completion_type == 'begin' + " suggest known environments + for entry in g:LatexBox_completion_environments + if entry.word =~ '^' . escape(a:base, '\') + if g:LatexBox_completion_close_braces && !s:NextCharsMatch('^}') + " add trailing '}' + let entry = copy(entry) + let entry.abbr = entry.word + let entry.word = entry.word . '}' + endif + call add(suggestions, entry) + endif + endfor + elseif s:completion_type == 'end' + " suggest known environments + let env = LatexBox_GetCurrentEnvironment() + if env != '' + if g:LatexBox_completion_close_braces && !s:NextCharsMatch('^\s*[,}]') + call add(suggestions, {'word': env . '}', 'abbr': env}) + else + call add(suggestions, env) + endif + endif + elseif s:completion_type == 'command' + " suggest known commands + for entry in g:LatexBox_completion_commands + if entry.word =~ '^' . escape(a:base, '\') + " do not display trailing '{' + if entry.word =~ '{' + let entry.abbr = entry.word[0:-2] + endif + call add(suggestions, entry) + endif + endfor + elseif s:completion_type == 'ref' + let suggestions = s:CompleteLabels(a:base) + elseif s:completion_type == 'bib' + " suggest BibTeX entries + let suggestions = LatexBox_BibComplete(a:base) + elseif s:completion_type == 'inlineMath' + let suggestions = s:LatexBox_inlineMath_completion(a:base) + endif + if !has('gui_running') + redraw! + endif + return suggestions + endif +endfunction +" }}} + +" BibTeX search {{{ + +" find the \bibliography{...} commands +" the optional argument is the file name to be searched + +function! s:FindBibData(...) + if a:0 == 0 + let file = LatexBox_GetMainTexFile() + else + let file = a:1 + 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 filtered = filter(copy(lines), + \ 'v:val =~ ''\C' . cmd . '\s*{[^}]\+}''') + let files = map(filtered, + \ 'matchstr(v:val, ''\C' . cmd . '\s*{\zs[^}]\+\ze}'')') + for file in files + let bibdata_list += map(split(file, ','), + \ 'fnamemodify(v:val, '':r'')') + endfor + endfor + + " + " Also search included files + " + for input in filter(lines, + \ 'v:val =~ ''\C\\\%(input\|include\)\s*{[^}]\+}''') + let bibdata_list += s:FindBibData(LatexBox_kpsewhich( + \ matchstr(input, + \ '\C\\\%(input\|include\)\s*{\zs[^}]\+\ze}'))) + endfor + + return bibdata_list +endfunction + +let s:bstfile = expand('<sfile>:p:h') . '/vimcomplete' + +function! LatexBox_BibSearch(regexp) + let res = [] + + " Find data from bib files + let bibdata = join(s:FindBibData(), ',') + if bibdata != '' + + " write temporary aux file + let tmpbase = LatexBox_GetTexRoot() . '/_LatexBox_BibComplete' + let auxfile = tmpbase . '.aux' + let bblfile = tmpbase . '.bbl' + let blgfile = tmpbase . '.blg' + + call writefile(['\citation{*}', '\bibstyle{' . s:bstfile . '}', + \ '\bibdata{' . bibdata . '}'], auxfile) + + if has('win32') + let l:old_shellslash = &l:shellslash + setlocal noshellslash + call system('cd ' . shellescape(LatexBox_GetTexRoot()) . + \ ' & bibtex -terse ' + \ . fnamemodify(auxfile, ':t') . ' >nul') + let &l:shellslash = l:old_shellslash + else + call system('cd ' . shellescape(LatexBox_GetTexRoot()) . + \ ' ; bibtex -terse ' + \ . fnamemodify(auxfile, ':t') . ' >/dev/null') + endif + + let lines = split(substitute(join(readfile(bblfile), "\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]) + let s:type_length = max([s:type_length, + \ len(matches[2]) + 3]) + call add(res, { + \ 'key': matches[1], + \ 'type': matches[2], + \ 'author': matches[3], + \ 'year': matches[4], + \ 'title': matches[5], + \ }) + endif + endfor + + call delete(auxfile) + call delete(bblfile) + call delete(blgfile) + endif + + " Find data from 'thebibliography' environments + let lines = readfile(LatexBox_GetMainTexFile()) + if match(lines, '\C\\begin{thebibliography}') >= 0 + 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 +" }}} + +" BibTeX completion {{{ +let s:type_length=0 +function! LatexBox_BibComplete(regexp) + + " treat spaces as '.*' if needed + if g:LatexBox_bibtex_wild_spaces + "let regexp = substitute(a:regexp, '\s\+', '.*', 'g') + let regexp = '.*' . substitute(a:regexp, '\s\+', '\\\&.*', 'g') + else + let regexp = a:regexp + endif + + let res = [] + let s:type_length = 4 + for m in LatexBox_BibSearch(regexp) + let type = m['type'] == '' ? '[-]' : '[' . m['type'] . '] ' + let type = printf('%-' . s:type_length . 's', type) + let auth = m['author'] == '' ? '' : m['author'][:20] . ' ' + let auth = substitute(auth, '\~', ' ', 'g') + let auth = substitute(auth, ',.*\ze', ' et al. ', '') + let year = m['year'] == '' ? '' : '(' . m['year'] . ')' + let w = { 'word': m['key'], + \ 'abbr': type . auth . year, + \ 'menu': m['title'] } + + " close braces if needed + if g:LatexBox_completion_close_braces && !s:NextCharsMatch('^\s*[,}]') + let w.word = w.word . '}' + endif + + call add(res, w) + endfor + return res +endfunction +" }}} + +" ExtractLabels {{{ +" Generate list of \newlabel commands in current buffer. +" +" Searches the current buffer for commands of the form +" \newlabel{name}{{number}{page}.* +" and returns list of [ name, number, page ] tuples. +function! s:ExtractLabels() + call cursor(1,1) + + let matches = [] + let [lblline, lblbegin] = searchpos( '\\newlabel{', 'ecW' ) + + while [lblline, lblbegin] != [0,0] + let [nln, nameend] = searchpairpos( '{', '', '}', 'W' ) + if nln != lblline + let [lblline, lblbegin] = searchpos( '\\newlabel{', 'ecW' ) + continue + endif + let curname = strpart( getline( lblline ), lblbegin, nameend - lblbegin - 1 ) + + " Ignore cref entries (because they are duplicates) + if curname =~# "@cref$" + let [lblline, lblbegin] = searchpos( '\\newlabel{', 'ecW' ) + continue + endif + + if 0 == search( '\m{\w*{', 'ce', lblline ) + let [lblline, lblbegin] = searchpos( '\\newlabel{', 'ecW' ) + continue + endif + + let numberbegin = getpos('.')[2] + let [nln, numberend] = searchpairpos( '{', '', '}', 'W' ) + if nln != lblline + let [lblline, lblbegin] = searchpos( '\\newlabel{', 'ecW' ) + continue + endif + let curnumber = strpart( getline( lblline ), numberbegin, numberend - numberbegin - 1 ) + + if 0 == search( '\m\w*{', 'ce', lblline ) + let [lblline, lblbegin] = searchpos( '\\newlabel{', 'ecW' ) + continue + endif + + let pagebegin = getpos('.')[2] + let [nln, pageend] = searchpairpos( '{', '', '}', 'W' ) + if nln != lblline + let [lblline, lblbegin] = searchpos( '\\newlabel{', 'ecW' ) + continue + endif + let curpage = strpart( getline( lblline ), pagebegin, pageend - pagebegin - 1 ) + + let matches += [ [ curname, curnumber, curpage ] ] + + let [lblline, lblbegin] = searchpos( '\\newlabel{', 'ecW' ) + endwhile + + return matches +endfunction +"}}} + +" ExtractInputs {{{ +" Generate list of \@input commands in current buffer. +" +" Searches the current buffer for \@input{file} entries and +" returns list of all files. +function! s:ExtractInputs() + call cursor(1,1) + + let matches = [] + let [inline, inbegin] = searchpos( '\\@input{', 'ecW' ) + + while [inline, inbegin] != [0,0] + let [nln, inend] = searchpairpos( '{', '', '}', 'W' ) + if nln != inline + let [inline, inbegin] = searchpos( '\\@input{', 'ecW' ) + continue + endif + let matches += [ LatexBox_kpsewhich(strpart( getline( inline ), inbegin, inend - inbegin - 1 )) ] + + let [inline, inbegin] = searchpos( '\\@input{', 'ecW' ) + endwhile + + " Remove empty strings for nonexistant .aux files + return filter(matches, 'v:val != ""') +endfunction +"}}} + +" LabelCache {{{ +" Cache of all labels. +" +" LabelCache is a dictionary mapping filenames to tuples +" [ time, labels, inputs ] +" where +" * time is modification time of the cache entry +" * labels is a list like returned by ExtractLabels +" * inputs is a list like returned by ExtractInputs +let s:LabelCache = {} +"}}} + +" GetLabelCache {{{ +" Extract labels from LabelCache and update it. +" +" Compares modification time of each entry in the label +" cache and updates it, if necessary. During traversal of +" the LabelCache, all current labels are collected and +" returned. +function! s:GetLabelCache(file) + if !filereadable(a:file) + return [] + endif + + if !has_key(s:LabelCache , a:file) || s:LabelCache[a:file][0] != getftime(a:file) + " Open file in temporary split window for label extraction. + let main_tex_file = LatexBox_GetMainTexFile() + silent execute '1sp +let\ b:main_tex_file=main_tex_file|let\ labels=s:ExtractLabels()|let\ inputs=s:ExtractInputs()|quit! ' . fnameescape(a:file) + let s:LabelCache[a:file] = [ getftime(a:file), labels, inputs ] + endif + + " We need to create a copy of s:LabelCache[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:LabelCache[a:file][1]) + + for input in s:LabelCache[a:file][2] + let labels += s:GetLabelCache(input) + endfor + + return labels +endfunction +"}}} + +" Complete Labels {{{ +function! s:CompleteLabels(regex) + let labels = s:GetLabelCache(LatexBox_GetAuxFile()) + + let matches = filter( copy(labels), 'match(v:val[0], "' . a:regex . '") != -1' ) + if empty(matches) + " also try to match label and number + 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), 'match(v:val[0], "' . base . '") != -1 && match(v:val[1], "' . number . '") != -1' ) + endif + endif + if empty(matches) + " also try to match number + let matches = filter( copy(labels), 'match(v:val[1], "' . a:regex . '") != -1' ) + endif + + let suggestions = [] + for m in matches + let entry = {'word': m[0], 'menu': printf("%7s [p. %s]", '('.m[1].')', m[2])} + if g:LatexBox_completion_close_braces && !s:NextCharsMatch('^\s*[,}]') + " add trailing '}' + let entry = copy(entry) + let entry.abbr = entry.word + let entry.word = entry.word . '}' + endif + call add(suggestions, entry) + endfor + + return suggestions +endfunction +" }}} + +" Complete Inline Math Or Not {{{ +" Return 1, when cursor is in a math env: +" 1, there is a single $ in the current line on the left of cursor +" 2, there is an open-eq-env on/above the current line +" (open-eq-env : \(, \[, and \begin{eq-env} ) +" Return 0, when cursor is not in a math env +function! s:LatexBox_complete_inlineMath_or_not() + + " switch of inline math completion feature + if g:LatexBox_complete_inlineMath == 0 + return 0 + endif + + " env names that can't appear in an eq env + if !exists('s:LatexBox_doc_structure_patterns') + let s:LatexBox_doc_structure_patterns = '\%(' . '\\begin\s*{document}\|' . + \ '\\\%(chapter\|section\|subsection\|subsubsection\)\*\?\s*{' . '\)' + endif + + if !exists('s:LatexBox_eq_env_open_patterns') + let s:LatexBox_eq_env_open_patterns = ['\\(','\\\['] + endif + if !exists('s:LatexBox_eq_env_close_patterns') + let s:LatexBox_eq_env_close_patterns = ['\\)','\\\]'] + endif + + let notcomment = '\%(\%(\\\@<!\%(\\\\\)*\)\@<=%.*\)\@<!' + + let lnum_saved = line('.') + let cnum_saved = col('.') -1 + + let line = getline('.') + let line_start_2_cnum_saved = line[:cnum_saved] + + " determine whether there is a single $ before cursor + let cursor_dollar_pair = 0 + while matchend(line_start_2_cnum_saved, '\$[^$]\+\$', cursor_dollar_pair) >= 0 + " find the end of dollar pair + let cursor_dollar_pair = matchend(line_start_2_cnum_saved, '\$[^$]\+\$', cursor_dollar_pair) + endwhile + " find single $ after cursor_dollar_pair + let cursor_single_dollar = matchend(line_start_2_cnum_saved, '\$', cursor_dollar_pair) + + " if single $ is found + if cursor_single_dollar >= 0 + " check whether $ is in \(...\), \[...\], or \begin{eq}...\end{eq} + + " check current line, + " search for LatexBox_eq_env_close_patterns: \[ and \( + let lnum = line('.') + for i in range(0, (len(s:LatexBox_eq_env_open_patterns)-1)) + call cursor(lnum_saved, cnum_saved) + let cnum_close = searchpos(''. s:LatexBox_eq_env_close_patterns[i].'', 'cbW', lnum_saved)[1] + let cnum_open = matchend(line_start_2_cnum_saved, s:LatexBox_eq_env_open_patterns[i], cnum_close) + if cnum_open >= 0 + let s:eq_dollar_parenthesis_bracket_empty = '' + let s:eq_pos = cursor_single_dollar - 1 + return 1 + end + endfor + + " check the lines above + " search for s:LatexBox_doc_structure_patterns, and end-of-math-env + let lnum -= 1 + while lnum > 0 + let line = getline(lnum) + if line =~ notcomment . '\(' . s:LatexBox_doc_structure_patterns . + \ '\|' . '\\end\s*{\(' . g:LatexBox_eq_env_patterns . '\)\*\?}\)' + " when s:LatexBox_doc_structure_patterns or g:LatexBox_eq_env_patterns + " are found first, complete math, leave with $ at both sides + let s:eq_dollar_parenthesis_bracket_empty = '$' + let s:eq_pos = cursor_single_dollar + break + elseif line =~ notcomment . '\\begin\s*{\(' . g:LatexBox_eq_env_patterns . '\)\*\?}' + " g:LatexBox_eq_env_patterns is found, complete math, remove $ + let s:eq_dollar_parenthesis_bracket_empty = '' + let s:eq_pos = cursor_single_dollar - 1 + break + endif + let lnum -= 1 + endwhile + + return 1 + else + " no $ is found, then search for \( or \[ in current line + " 1, whether there is \( + call cursor(lnum_saved, cnum_saved) + let cnum_parenthesis_close = searchpos('\\)', 'cbW', lnum_saved)[1] + let cnum_parenthesis_open = matchend(line_start_2_cnum_saved, '\\(', cnum_parenthesis_close) + if cnum_parenthesis_open >= 0 + let s:eq_dollar_parenthesis_bracket_empty = '\)' + let s:eq_pos = cnum_parenthesis_open + return 1 + end + + " 2, whether there is \[ + call cursor(lnum_saved, cnum_saved) + let cnum_bracket_close = searchpos('\\\]', 'cbW', lnum_saved)[1] + let cnum_bracket_open = matchend(line_start_2_cnum_saved, '\\\[', cnum_bracket_close) + if cnum_bracket_open >= 0 + let s:eq_dollar_parenthesis_bracket_empty = '\]' + let s:eq_pos = cnum_bracket_open + return 1 + end + + " not inline math completion + return 0 + endif + +endfunction +" }}} + +" Complete inline euqation{{{ +function! s:LatexBox_inlineMath_completion(regex, ...) + + if a:0 == 0 + let file = LatexBox_GetMainTexFile() + else + let file = a:1 + endif + + if empty(glob(file, 1)) + return '' + endif + + if empty(s:eq_dollar_parenthesis_bracket_empty) + let inline_pattern1 = '\$\s*\(' . escape(substitute(a:regex[1:], '^\s\+', '', ""), '\.*^') . '[^$]*\)\s*\$' + let inline_pattern2 = '\\(\s*\(' . escape(substitute(a:regex[1:], '^\s\+', '', ""), '\.*^') . '.*\)\s*\\)' + else + let inline_pattern1 = '\$\s*\(' . escape(substitute(a:regex, '^\s\+', '', ""), '\.*^') . '[^$]*\)\s*\$' + let inline_pattern2 = '\\(\s*\(' . escape(substitute(a:regex, '^\s\+', '', ""), '\.*^') . '.*\)\s*\\)' + endif + + + let suggestions = [] + let line_num = 0 + for line in readfile(file) + let line_num = line_num + 1 + + let suggestions += s:LatexBox_inlineMath_mathlist(line,inline_pattern1 , line_num) + s:LatexBox_inlineMath_mathlist( line,inline_pattern2, line_num) + + " search for included files + let included_file = matchstr(line, '^\\@input{\zs[^}]*\ze}') + if included_file != '' + let included_file = LatexBox_kpsewhich(included_file) + call extend(suggestions, s:LatexBox_inlineMath_completion(a:regex, included_file)) + endif + endfor + + return suggestions +endfunction +" }}} + +" Search for inline maths {{{ +" search for $ ... $ and \( ... \) in each line +function! s:LatexBox_inlineMath_mathlist(line,inline_pattern, line_num) + let col_start = 0 + let suggestions = [] + while 1 + let matches = matchlist(a:line, a:inline_pattern, col_start) + if !empty(matches) + + " show line number of inline math + let entry = {'word': matches[1], 'menu': '[' . a:line_num . ']'} + + if s:eq_dollar_parenthesis_bracket_empty != '' + let entry = copy(entry) + let entry.abbr = entry.word + let entry.word = entry.word . s:eq_dollar_parenthesis_bracket_empty + endif + call add(suggestions, entry) + + " update col_start + let col_start = matchend(a:line, a:inline_pattern, col_start) + else + break + endif + endwhile + + return suggestions +endfunction +" }}} + +" Close Current Environment {{{ +function! s:CloseCurEnv() + " first, try with \left/\right pairs + let [lnum, cnum] = searchpairpos('\C\\left\>', '', '\C\\right\>', 'bnW', 'LatexBox_InComment()') + if lnum + let line = strpart(getline(lnum), cnum - 1) + let bracket = matchstr(line, '^\\left\zs\((\|\[\|\\{\||\|\.\)\ze') + for [open, close] in [['(', ')'], ['\[', '\]'], ['\\{', '\\}'], ['|', '|'], ['\.', '|']] + let bracket = substitute(bracket, open, close, 'g') + endfor + return '\right' . bracket + endif + + " second, try with environments + let env = LatexBox_GetCurrentEnvironment() + if env == '\[' + return '\]' + elseif env == '\(' + return '\)' + elseif env != '' + return '\end{' . env . '}' + endif + return '' +endfunction +" }}} + +" Wrap Selection {{{ +function! s:WrapSelection(wrapper) + keepjumps normal! `>a} + execute 'keepjumps normal! `<i\' . a:wrapper . '{' +endfunction +" }}} + +" Wrap Selection in Environment with Prompt {{{ +function! s:PromptEnvWrapSelection(...) + let env = input('environment: ', '', 'customlist,' . s:SIDWrap('GetEnvironmentList')) + if empty(env) + return + endif + " LaTeXBox's custom indentation can interfere with environment + " insertion when environments are indented (common for nested + " environments). Temporarily disable it for this operation: + let ieOld = &indentexpr + setlocal indentexpr="" + if visualmode() ==# 'V' + execute 'keepjumps normal! `>o\end{' . env . '}' + execute 'keepjumps normal! `<O\begin{' . env . '}' + " indent and format, if requested. + if a:0 && a:1 + normal! gv> + normal! gvgq + endif + else + execute 'keepjumps normal! `>a\end{' . env . '}' + execute 'keepjumps normal! `<i\begin{' . env . '}' + endif + exe "setlocal indentexpr=" . ieOld +endfunction +" }}} + +" List Labels with Prompt {{{ +function! s:PromptLabelList(...) + " Check if window already exists + let winnr = bufwinnr(bufnr('LaTeX Labels')) + if winnr >= 0 + if a:0 == 0 + silent execute winnr . 'wincmd w' + else + " Supplying an argument to this function causes toggling instead + " of jumping to the labels window + if g:LatexBox_split_resize + silent exe "set columns-=" . g:LatexBox_split_width + endif + silent execute 'bwipeout' . bufnr('LaTeX Labels') + endif + return + endif + + " Get label suggestions + let regexp = input('filter labels with regexp: ', '') + let labels = s:CompleteLabels(regexp) + + let calling_buf = bufnr('%') + + " Create labels window and set local settings + if g:LatexBox_split_resize + silent exe "set columns+=" . g:LatexBox_split_width + endif + silent exe g:LatexBox_split_side g:LatexBox_split_width . 'vnew LaTeX\ Labels' + let b:toc = [] + let b:toc_numbers = 1 + let b:calling_win = bufwinnr(calling_buf) + setlocal filetype=latextoc + + " Add label entries and jump to the closest section + for entry in labels + let number = matchstr(entry['menu'], '^\s*(\zs[^)]\+\ze)') + let page = matchstr(entry['menu'], '^[^)]*)\s*\[\zs[^]]\+\ze\]') + let e = {'file': bufname(calling_buf), + \ 'level': 'label', + \ 'number': number, + \ 'text': entry['abbr'], + \ 'page': page} + call add(b:toc, e) + if b:toc_numbers + call append('$', e['number'] . "\t" . e['text']) + else + call append('$', e['text']) + endif + endfor + if !g:LatexBox_toc_hidehelp + call append('$', "") + call append('$', "<Esc>/q: close") + call append('$', "<Space>: jump") + call append('$', "<Enter>: jump and close") + call append('$', "s: hide numbering") + endif + 0delete _ + + " Lock buffer + setlocal nomodifiable +endfunction +" }}} + +" Change Environment {{{ +function! s:ChangeEnvPrompt() + + let [env, lnum, cnum, lnum2, cnum2] = LatexBox_GetCurrentEnvironment(1) + + let new_env = input('change ' . env . ' for: ', '', 'customlist,' . s:SIDWrap('GetEnvironmentList')) + if empty(new_env) + return + endif + + if new_env == '\[' || new_env == '[' + let begin = '\[' + let end = '\]' + elseif new_env == '\(' || new_env == '(' + let begin = '\(' + let end = '\)' + else + let l:begin = '\begin{' . new_env . '}' + let l:end = '\end{' . new_env . '}' + endif + + if env == '\[' || env == '\(' + let line = getline(lnum2) + let line = strpart(line, 0, cnum2 - 1) . l:end . strpart(line, cnum2 + 1) + call setline(lnum2, line) + + let line = getline(lnum) + let line = strpart(line, 0, cnum - 1) . l:begin . strpart(line, cnum + 1) + call setline(lnum, line) + else + let line = getline(lnum2) + let line = strpart(line, 0, cnum2 - 1) . l:end . strpart(line, cnum2 + len(env) + 5) + call setline(lnum2, line) + + let line = getline(lnum) + let line = strpart(line, 0, cnum - 1) . l:begin . strpart(line, cnum + len(env) + 7) + call setline(lnum, line) + endif +endfunction + +function! s:GetEnvironmentList(lead, cmdline, pos) + let suggestions = [] + for entry in g:LatexBox_completion_environments + let env = entry.word + if env =~ '^' . a:lead + call add(suggestions, env) + endif + endfor + return suggestions +endfunction + +function! s:LatexToggleStarEnv() + let [env, lnum, cnum, lnum2, cnum2] = LatexBox_GetCurrentEnvironment(1) + + if env == '\(' + return + elseif env == '\[' + let begin = '\begin{equation}' + let end = '\end{equation}' + elseif env[-1:] == '*' + let begin = '\begin{' . env[:-2] . '}' + let end = '\end{' . env[:-2] . '}' + else + let begin = '\begin{' . env . '*}' + let end = '\end{' . env . '*}' + endif + + if env == '\[' + let line = getline(lnum2) + let line = strpart(line, 0, cnum2 - 1) . l:end . strpart(line, cnum2 + 1) + call setline(lnum2, line) + + let line = getline(lnum) + let line = strpart(line, 0, cnum - 1) . l:begin . strpart(line, cnum + 1) + call setline(lnum, line) + else + let line = getline(lnum2) + let line = strpart(line, 0, cnum2 - 1) . l:end . strpart(line, cnum2 + len(env) + 5) + call setline(lnum2, line) + + let line = getline(lnum) + let line = strpart(line, 0, cnum - 1) . l:begin . strpart(line, cnum + len(env) + 7) + call setline(lnum, line) + endif +endfunction +" }}} + +" Next Charaters Match {{{ +function! s:NextCharsMatch(regex) + let rest_of_line = strpart(getline('.'), col('.') - 1) + return rest_of_line =~ a:regex +endfunction +" }}} + +" Mappings {{{ +inoremap <silent> <Plug>LatexCloseCurEnv <C-R>=<SID>CloseCurEnv()<CR> +vnoremap <silent> <Plug>LatexWrapSelection :<c-u>call <SID>WrapSelection('')<CR>i +vnoremap <silent> <Plug>LatexEnvWrapSelection :<c-u>call <SID>PromptEnvWrapSelection()<CR> +vnoremap <silent> <Plug>LatexEnvWrapFmtSelection :<c-u>call <SID>PromptEnvWrapSelection(1)<CR> +nnoremap <silent> <Plug>LatexChangeEnv :call <SID>ChangeEnvPrompt()<CR> +nnoremap <silent> <Plug>LatexToggleStarEnv :call <SID>LatexToggleStarEnv()<CR> +" }}} + +" Commands {{{ +command! LatexLabels call <SID>PromptLabelList() +" }}} + +" vim:fdm=marker:ff=unix:noet:ts=4:sw=4 + +endif diff --git a/ftplugin/latex-box/findmain.vim b/ftplugin/latex-box/findmain.vim new file mode 100644 index 00000000..3b81b0d7 --- /dev/null +++ b/ftplugin/latex-box/findmain.vim @@ -0,0 +1,66 @@ +if !exists('g:polyglot_disabled') || index(g:polyglot_disabled, 'latex') == -1 + +" LatexBox_GetMainFileName: gets the name of the main file being compiled. {{{ +" Description: returns the full path name of the main file. +" This function checks for the existence of a .latexmain file +" which might point to the location of a "main" latex file. +" If .latexmain exists, then return the full path name of the +" file being pointed to by it. +" +" Otherwise, return the full path name of the current buffer. +" +" You can supply an optional "modifier" argument to the +" function, which will optionally modify the file name before +" returning. +" NOTE: From version 1.6 onwards, this function always trims +" away the .latexmain part of the file name before applying the +" modifier argument. +" NOTE: This function is copied from the Latex-Suite project! +function! LatexBox_GetMainFileName(...) + if a:0 > 0 + let modifier = a:1 + else + let modifier = ':p' + endif + + let s:origdir = fnameescape(getcwd()) + + let dirmodifier = '%:p:h' + let dirLast = fnameescape(expand(dirmodifier)) + exe 'cd '.dirLast + + " move up the directory tree until we find a .latexmain file. + " TODO: Should we be doing this recursion by default, or should there be a + " setting? + while glob('*.latexmain',1) == '' + let dirmodifier = dirmodifier.':h' + let dirNew = fnameescape(expand(dirmodifier)) + " break from the loop if we cannot go up any further. + if dirNew == dirLast + break + endif + let dirLast = dirNew + exe 'cd '.dirLast + endwhile + + let lheadfile = glob('*.latexmain',1) + if lheadfile != '' + " Remove the trailing .latexmain part of the filename... We never want + " that. + let lheadfile = fnamemodify(substitute(lheadfile, '\.latexmain$', '', ''), modifier) + else + " If we cannot find any main file, just modify the filename of the + " current buffer. + let lheadfile = expand('%'.modifier) + endif + + exe 'cd '.s:origdir + + " NOTE: The caller of this function needs to escape the file name with + " fnameescape() . The reason its not done here is that escaping is not + " safe if this file is to be used as part of an external command on + " certain platforms. + return lheadfile +endfunction + +endif diff --git a/ftplugin/latex-box/folding.vim b/ftplugin/latex-box/folding.vim new file mode 100644 index 00000000..d6f36272 --- /dev/null +++ b/ftplugin/latex-box/folding.vim @@ -0,0 +1,382 @@ +if !exists('g:polyglot_disabled') || index(g:polyglot_disabled, 'latex') == -1 + +" Folding support for LaTeX + +" +" Options +" g:LatexBox_Folding - Turn on/off folding +" g:LatexBox_fold_text - Turn on/off LatexBox fold text function +" g:LatexBox_fold_preamble - Turn on/off folding of preamble +" g:LatexBox_fold_parts - Define parts (eq. appendix, frontmatter) to fold +" g:LatexBox_fold_sections - Define section levels to fold +" g:LatexBox_fold_envs - Turn on/off folding of environments +" g:LatexBox_fold_toc - Turn on/off folding of TOC +" g:LatexBox_fold_toc_levels - Set max TOC fold level +" +" {{{1 Initialize options to default values. +if !exists('g:LatexBox_Folding') + let g:LatexBox_Folding=0 +endif +if !exists('g:LatexBox_fold_text') + let g:LatexBox_fold_text=1 +endif +if !exists('g:LatexBox_fold_preamble') + let g:LatexBox_fold_preamble=1 +endif +if !exists('g:LatexBox_fold_envs') + let g:LatexBox_fold_envs=1 +endif +if !exists('g:LatexBox_fold_envs_force') + let g:LatexBox_fold_envs_force = [] +endif +if !exists('g:LatexBox_fold_parts') + let g:LatexBox_fold_parts=[ + \ "appendix", + \ "frontmatter", + \ "mainmatter", + \ "backmatter" + \ ] +endif +if !exists('g:LatexBox_fold_sections') + let g:LatexBox_fold_sections=[ + \ "part", + \ "chapter", + \ "section", + \ "subsection", + \ "subsubsection" + \ ] +endif +if !exists('g:LatexBox_fold_toc') + let g:LatexBox_fold_toc=0 +endif +if !exists('g:LatexBox_fold_toc_levels') + let g:LatexBox_fold_toc_levels=1 +endif +if !exists('g:LatexBox_fold_automatic') + let g:LatexBox_fold_automatic=1 +endif +" }}}1 + +if g:LatexBox_Folding == 0 + finish +endif + +" {{{1 Set folding options for vim +setl foldexpr=LatexBox_FoldLevel(v:lnum) +if g:LatexBox_fold_text == 1 + setl foldtext=LatexBox_FoldText() +endif +if g:LatexBox_fold_automatic == 1 + setl foldmethod=expr + + " + " 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 + " + augroup FastFold + autocmd! + autocmd InsertEnter *.tex if !&diff | setlocal foldmethod=manual | endif + autocmd InsertLeave *.tex if !&diff | setlocal foldmethod=expr | endif + augroup end +else + setl foldmethod=manual +endif + +function! LatexBox_FoldOnDemand() + setl foldmethod=expr + normal! zx + setl foldmethod=manual +endfunction + +command! LatexFold call LatexBox_FoldOnDemand() + +" {{{1 LatexBox_FoldLevel help functions + +" 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. +function! s:FoldSectionLevels() + " 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:LatexBox_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:LatexBox_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 LatexBox_FoldLevel + +" Parse file to dynamically set the sectioning fold levels +let b:LatexBox_FoldSections = s:FoldSectionLevels() + +" Optimize by predefine common patterns +let s:notbslash = '\%(\\\@<!\%(\\\\\)*\)\@<=' +let s:notcomment = '\%(\%(\\\@<!\%(\\\\\)*\)\@<=%.*\)\@<!' +let s:envbeginpattern = s:notcomment . s:notbslash . '\\begin\s*{.\{-}}' +let s:envendpattern = s:notcomment . s:notbslash . '\\end\s*{.\{-}}' +let s:foldparts = '^\s*\\\%(' . join(g:LatexBox_fold_parts, '\|') . '\)' +let s:folded = '\(% Fake\|\\\(document\|begin\|end\|paragraph\|' + \ . 'front\|main\|back\|app\|sub\|section\|chapter\|part\)\)' + +function! LatexBox_FoldLevel(lnum) + " Check for normal lines first (optimization) + let line = getline(a:lnum) + if line !~ s:folded + return "=" + endif + + " Fold preamble + if g:LatexBox_fold_preamble == 1 + if line =~# s:notcomment . s:notbslash . '\s*\\documentclass' + return ">1" + elseif line =~# s:notcomment . s:notbslash . '\s*\\begin\s*{\s*document\s*}' + return "0" + endif + endif + + " Fold parts (\frontmatter, \mainmatter, \backmatter, and \appendix) + if line =~# s:foldparts + return ">1" + endif + + " Fold chapters and sections + for [part, level] in b:LatexBox_FoldSections + if line =~# part + return ">" . level + endif + endfor + + " Never fold \end{document} + if line =~# '^\s*\\end{document}' + return 0 + endif + + " Fold environments + if line =~# s:envbeginpattern && line =~# s:envendpattern + " If the begin and end pattern are on the same line , do not fold + return "=" + else + if line =~# s:envbeginpattern + if g:LatexBox_fold_envs == 1 + return "a1" + else + let env = matchstr(line,'\\begin\*\?{\zs\w*\*\?\ze}') + if index(g:LatexBox_fold_envs_force, env) >= 0 + return "a1" + else + return "=" + endif + endif + elseif line =~# s:envendpattern + if g:LatexBox_fold_envs == 1 + return "s1" + else + let env = matchstr(line,'\\end\*\?{\zs\w*\*\?\ze}') + if index(g:LatexBox_fold_envs_force, env) >= 0 + return "s1" + else + return "=" + endif + endif + endif + endif + + " Return foldlevel of previous line + return "=" +endfunction + +" {{{1 LatexBox_FoldText help functions +function! s:LabelEnv() + 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 + +function! s:CaptionEnv() + let i = v:foldend + while i >= v:foldstart + if getline(i) =~ '^\s*\\caption' + return matchstr(getline(i), '^\s*\\caption\(\[.*\]\)\?{\zs.\+') + end + let i -= 1 + endwhile + return "" +endfunction + +function! s:CaptionTable() + let i = v:foldstart + while i <= v:foldend + if getline(i) =~ '^\s*\\caption' + return matchstr(getline(i), '^\s*\\caption\(\[.*\]\)\?{\zs.\+') + end + let i += 1 + endwhile + return "" +endfunction + +function! s:CaptionFrame(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.\+') + end + let i += 1 + endwhile + + return "" + endif +endfunction + +function! LatexBox_FoldText_title() + let line = getline(v:foldstart) + let title = 'Not defined' + + " Preamble + if line =~ '\s*\\documentclass' + return "Preamble" + endif + + " Parts, sections and fakesections + let sections = '\(\(sub\)*\(section\|paragraph\)\|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 based on type of environment + if env == 'frame' + let label = '' + let caption = s:CaptionFrame(line) + elseif env == 'table' + let label = s:LabelEnv() + let caption = s:CaptionTable() + else + let label = s:LabelEnv() + let caption = s:CaptionEnv() + endif + + " If no caption found, check for a caption comment + if caption == '' + let caption = matchstr(line,'\\begin\*\?{.*}\s*%\s*\zs.*') + endif + + " Create title based on caption and label + if caption . label == '' + let title = env + elseif label == '' + let title = printf('%-12s%s', env . ':', + \ substitute(caption, '}\s*$', '','')) + elseif caption == '' + let title = printf('%-12s%56s', env, '(' . label . ')') + else + let title = printf('%-12s%-30s %21s', env . ':', + \ strpart(substitute(caption, '}\s*$', '',''),0,34), + \ '(' . label . ')') + endif + endif + + return title +endfunction + +" {{{1 LatexBox_FoldText +function! LatexBox_FoldText() + let nlines = v:foldend - v:foldstart + 1 + let title = strpart(LatexBox_FoldText_title(), 0, 68) + let level = '' + + " 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) + + return printf('%-3s %-68s #%5d', level, title, nlines) +endfunction + +" {{{1 Footer +" vim:fdm=marker:ff=unix:ts=4:sw=4 + +endif diff --git a/ftplugin/latex-box/latexmk.vim b/ftplugin/latex-box/latexmk.vim new file mode 100644 index 00000000..15db3686 --- /dev/null +++ b/ftplugin/latex-box/latexmk.vim @@ -0,0 +1,558 @@ +if !exists('g:polyglot_disabled') || index(g:polyglot_disabled, 'latex') == -1 + +" LaTeX Box latexmk functions + +" Options and variables {{{ + +if !exists('g:LatexBox_latexmk_options') + let g:LatexBox_latexmk_options = '' +endif +if !exists('g:LatexBox_latexmk_env') + let g:LatexBox_latexmk_env = '' +endif +if !exists('g:LatexBox_latexmk_async') + let g:LatexBox_latexmk_async = 0 +endif +if !exists('g:LatexBox_latexmk_preview_continuously') + let g:LatexBox_latexmk_preview_continuously = 0 +endif +if !exists('g:LatexBox_output_type') + let g:LatexBox_output_type = 'pdf' +endif +if !exists('g:LatexBox_autojump') + let g:LatexBox_autojump = 0 +endif +if ! exists('g:LatexBox_quickfix') + let g:LatexBox_quickfix = 1 +endif +if ! exists('g:LatexBox_personal_latexmkrc') + let g:LatexBox_personal_latexmkrc = 0 +endif + +" }}} + +" Process ID management (used for asynchronous and continuous mode) {{{ + +" A dictionary of latexmk PID's (basename: pid) +if !exists('g:latexmk_running_pids') + let g:latexmk_running_pids = {} +endif + +" Set PID {{{ +function! s:LatexmkSetPID(basename, pid) + let g:latexmk_running_pids[a:basename] = a:pid +endfunction +" }}} + +" kill_latexmk_process {{{ +function! s:kill_latexmk_process(pid) + if has('win32') + silent execute '!taskkill /PID ' . a:pid . ' /T /F' + else + if g:LatexBox_latexmk_async + " vim-server mode + let pids = [] + let tmpfile = tempname() + silent execute '!ps x -o pgid,pid > ' . tmpfile + for line in readfile(tmpfile) + let new_pid = matchstr(line, '^\s*' . a:pid . '\s\+\zs\d\+\ze') + if !empty(new_pid) + call add(pids, new_pid) + endif + endfor + call delete(tmpfile) + if !empty(pids) + silent execute '!kill ' . join(pids) + endif + else + " single background process + silent execute '!kill ' . a:pid + endif + endif + if !has('gui_running') + redraw! + endif +endfunction +" }}} + +" kill_all_latexmk_processes {{{ +function! s:kill_all_latexmk_processes() + for pid in values(g:latexmk_running_pids) + call s:kill_latexmk_process(pid) + endfor +endfunction +" }}} + +" }}} + +" Setup for vim-server {{{ +function! s:SIDWrap(func) + if !exists('s:SID') + let s:SID = matchstr(expand('<sfile>'), '\zs<SNR>\d\+_\ze.*$') + endif + return s:SID . a:func +endfunction + +function! s:LatexmkCallback(basename, status) + " Only remove the pid if not in continuous mode + if !g:LatexBox_latexmk_preview_continuously + call remove(g:latexmk_running_pids, a:basename) + endif + call LatexBox_LatexErrors(a:status, a:basename) +endfunction + +function! s:setup_vim_server() + if !exists('g:vim_program') + + " attempt autodetection of vim executable + let g:vim_program = '' + if has('win32') + " Just drop through to the default for windows + else + if match(&shell, '\(bash\|zsh\)$') >= 0 + let ppid = '$PPID' + else + let ppid = '$$' + endif + + let tmpfile = tempname() + silent execute '!ps -o command= -p ' . ppid . ' > ' . tmpfile + for line in readfile(tmpfile) + let line = matchstr(line, '^\S\+\>') + if !empty(line) && executable(line) + let g:vim_program = line . ' -g' + break + endif + endfor + call delete(tmpfile) + endif + + if empty(g:vim_program) + if has('gui_macvim') + let g:vim_program + \ = '/Applications/MacVim.app/Contents/MacOS/Vim -g' + else + let g:vim_program = v:progname + endif + endif + endif +endfunction +" }}} + +" Latexmk {{{ + +function! LatexBox_Latexmk(force) + " Define often used names + let basepath = LatexBox_GetBuildBasename(1) + let basename = fnamemodify(basepath, ':t') + let texroot = shellescape(LatexBox_GetTexRoot()) + let mainfile = fnameescape(fnamemodify(LatexBox_GetMainTexFile(), ':t')) + + " Check if latexmk is installed + if !executable('latexmk') + echomsg "Error: LaTeX-Box relies on latexmk for compilation, but it" . + \ " is not installed!" + return + endif + + " Check if already running + if has_key(g:latexmk_running_pids, basepath) + echomsg "latexmk is already running for `" . basename . "'" + return + endif + + " Set wrap width in log file + let max_print_line = 2000 + if has('win32') + let env = 'set max_print_line=' . max_print_line . ' & ' + elseif match(&shell, '/tcsh$') >= 0 + let env = 'setenv max_print_line ' . max_print_line . '; ' + else + if fnamemodify(&shell, ':t') ==# 'fish' + let env = 'set max_print_line ' . max_print_line . '; and ' + else + let env = 'max_print_line=' . max_print_line + endif + endif + + " Set environment options + let env .= ' ' . g:LatexBox_latexmk_env . ' ' + + " Set latexmk command with options + if has('win32') + " Make sure to switch drive as well as directory + let cmd = 'cd /D ' . texroot . ' && ' + else + if fnamemodify(&shell, ':t') ==# 'fish' + let cmd = 'cd ' . texroot . '; and ' + else + let cmd = 'cd ' . texroot . ' && ' + endif + endif + let cmd .= env . ' latexmk' + if ! g:LatexBox_personal_latexmkrc + let cmd .= ' -' . g:LatexBox_output_type + endif + let cmd .= ' -quiet ' + let cmd .= g:LatexBox_latexmk_options + if a:force + let cmd .= ' -g' + endif + if g:LatexBox_latexmk_preview_continuously + let cmd .= ' -pvc' + endif + let cmd .= ' -e ' . shellescape('$pdflatex =~ s/ / -file-line-error /') + let cmd .= ' -e ' . shellescape('$latex =~ s/ / -file-line-error /') + if g:LatexBox_latexmk_preview_continuously + let cmd .= ' -e ' . shellescape('$success_cmd = $ENV{SUCCESSCMD}') + let cmd .= ' -e ' . shellescape('$failure_cmd = $ENV{FAILURECMD}') + endif + let cmd .= ' ' . mainfile + + " Redirect output to null + if has('win32') + let cmd .= ' >nul' + else + if fnamemodify(&shell, ':t') ==# 'fish' + let cmd .= ' >/dev/null ^/dev/null' + else + let cmd .= ' &>/dev/null' + endif + endif + + if g:LatexBox_latexmk_async + " Check if VIM server exists + if empty(v:servername) + echoerr "cannot run latexmk in background without a VIM server" + echoerr "set g:LatexBox_latexmk_async to 0 to change compiling mode" + return + endif + + " Start vim server if necessary + call s:setup_vim_server() + + let setpidfunc = s:SIDWrap('LatexmkSetPID') + let callbackfunc = s:SIDWrap('LatexmkCallback') + if has('win32') + let vim_program = substitute(g:vim_program, + \ 'gvim\.exe$', 'vim.exe', '') + + " Define callback to set the pid + let callsetpid = setpidfunc . '(''' . basepath . ''', %CMDPID%)' + let vimsetpid = vim_program . ' --servername ' . v:servername + \ . ' --remote-expr ' . shellescape(callsetpid) + + " Define callback after latexmk is finished + let callback = callbackfunc . '(''' . basepath . ''', %LATEXERR%)' + let vimcmd = vim_program . ' --servername ' . v:servername + \ . ' --remote-expr ' . shellescape(callback) + let scallback = callbackfunc . '(''' . basepath . ''', 0)' + let svimcmd = vim_program . ' --servername ' . v:servername + \ . ' --remote-expr ' . shellescape(scallback) + let fcallback = callbackfunc . '(''' . basepath . ''', 1)' + let fvimcmd = vim_program . ' --servername ' . v:servername + \ . ' --remote-expr ' . shellescape(fcallback) + + let asyncbat = tempname() . '.bat' + if g:LatexBox_latexmk_preview_continuously + call writefile(['setlocal', + \ 'set T=%TEMP%\sthUnique.tmp', + \ 'wmic process where (Name="WMIC.exe" AND CommandLine LIKE "%%%TIME%%%") ' + \ . 'get ParentProcessId /value | find "ParentProcessId" >%T%', + \ 'set /P A=<%T%', + \ 'set CMDPID=%A:~16% & del %T%', + \ vimsetpid, + \ 'set SUCCESSCMD='.svimcmd, + \ 'set FAILURECMD='.fvimcmd, + \ cmd, + \ 'endlocal'], asyncbat) + else + call writefile(['setlocal', + \ 'set T=%TEMP%\sthUnique.tmp', + \ 'wmic process where (Name="WMIC.exe" AND CommandLine LIKE "%%%TIME%%%") ' + \ . 'get ParentProcessId /value | find "ParentProcessId" >%T%', + \ 'set /P A=<%T%', + \ 'set CMDPID=%A:~16% & del %T%', + \ vimsetpid, + \ cmd, + \ 'set LATEXERR=%ERRORLEVEL%', + \ vimcmd, + \ 'endlocal'], asyncbat) + endif + + " Define command + let cmd = '!start /b ' . asyncbat . ' & del ' . asyncbat + else + " Define callback to set the pid + let callsetpid = shellescape(setpidfunc).'"(\"'.basepath.'\",$$)"' + let vimsetpid = g:vim_program . ' --servername ' . v:servername + \ . ' --remote-expr ' . callsetpid + + " Define callback after latexmk is finished + let callback = shellescape(callbackfunc).'"(\"'.basepath.'\",$?)"' + let vimcmd = g:vim_program . ' --servername ' . v:servername + \ . ' --remote-expr ' . callback + let scallback = shellescape(callbackfunc).'"(\"'.basepath.'\",0)"' + let svimcmd = g:vim_program . ' --servername ' . v:servername + \ . ' --remote-expr ' . scallback + let fcallback = shellescape(callbackfunc).'"(\"'.basepath.'\",1)"' + let fvimcmd = g:vim_program . ' --servername ' . v:servername + \ . ' --remote-expr ' . fcallback + + " Define command + " Note: Here we escape '%' because it may be given as a user option + " through g:LatexBox_latexmk_options, for instance with + " g:Latex..._options = "-pdflatex='pdflatex -synctex=1 \%O \%S'" + if g:LatexBox_latexmk_preview_continuously + let cmd = vimsetpid . ' ; ' + \ . 'export SUCCESSCMD=' . shellescape(svimcmd) . ' ' + \ . ' FAILURECMD=' . shellescape(fvimcmd) . ' ; ' + \ . escape(cmd, '%') + else + let cmd = vimsetpid . ' ; ' . escape(cmd, '%') . ' ; ' . vimcmd + endif + let cmd = '! (' . cmd . ') >/dev/null &' + endif + + if g:LatexBox_latexmk_preview_continuously + echo 'Compiling to ' . g:LatexBox_output_type + \ . ' with continuous preview.' + else + echo 'Compiling to ' . g:LatexBox_output_type . ' ...' + endif + silent execute cmd + else + if g:LatexBox_latexmk_preview_continuously + if has('win32') + let cmd = '!start /b cmd /s /c "' . cmd . '"' + else + let cmd = '!' . cmd . ' &' + endif + echo 'Compiling to ' . g:LatexBox_output_type . ' ...' + silent execute cmd + + " Save PID in order to be able to kill the process when wanted. + if has('win32') + let tmpfile = tempname() + let pidcmd = 'cmd /c "wmic process where ' + \ . '(CommandLine LIKE "latexmk\%'.mainfile.'\%") ' + \ . 'get ProcessId /value | find "ProcessId" ' + \ . '>'.tmpfile.' "' + silent execute '! ' . pidcmd + let pids = readfile(tmpfile) + let pid = strpart(pids[0], 10) + let g:latexmk_running_pids[basepath] = pid + else + let pid = substitute(system('pgrep -f "perl.*' + \ . mainfile . '" | head -n 1'),'\D','','') + let g:latexmk_running_pids[basepath] = pid + endif + else + " Execute command and check for errors + echo 'Compiling to ' . g:LatexBox_output_type . ' ... (async off!)' + call system(cmd) + call LatexBox_LatexErrors(v:shell_error) + endif + endif + + " Redraw screen if necessary + if !has("gui_running") + redraw! + endif +endfunction +" }}} + +" LatexmkClean {{{ +function! LatexBox_LatexmkClean(cleanall) + " Check if latexmk is installed + if !executable('latexmk') + echomsg "Error: LaTeX-Box relies on latexmk for compilation, but it" . + \ " is not installed!" + return + endif + + let basename = LatexBox_GetBuildBasename(1) + + if has_key(g:latexmk_running_pids, basename) + echomsg "don't clean when latexmk is running" + return + endif + + if has('win32') + let cmd = 'cd /D ' . shellescape(LatexBox_GetTexRoot()) . ' & ' + else + let cmd = 'cd ' . shellescape(LatexBox_GetTexRoot()) . ';' + endif + if a:cleanall + let cmd .= 'latexmk -C ' + else + let cmd .= 'latexmk -c ' + endif + let cmd .= shellescape(LatexBox_GetMainTexFile()) + if has('win32') + let cmd .= ' >nul' + else + let cmd .= ' >&/dev/null' + endif + + call system(cmd) + if !has('gui_running') + redraw! + endif + + echomsg "latexmk clean finished" +endfunction +" }}} + +" LatexErrors {{{ +function! LatexBox_LatexErrors(status, ...) + if a:0 >= 1 + let log = a:1 . '.log' + else + let log = LatexBox_GetLogFile() + endif + + cclose + + " set cwd to expand error file correctly + let l:cwd = fnamemodify(getcwd(), ':p') + execute 'lcd ' . fnameescape(LatexBox_GetTexRoot()) + try + if g:LatexBox_autojump + execute 'cfile ' . fnameescape(log) + else + execute 'cgetfile ' . fnameescape(log) + endif + finally + " restore cwd + execute 'lcd ' . fnameescape(l:cwd) + endtry + + " Always open window if started by LatexErrors command + if a:status < 0 + botright copen + else + " Only open window when an error/warning is detected + if g:LatexBox_quickfix >= 3 + \ ? s:log_contains_error(log) + \ : g:LatexBox_quickfix > 0 + belowright cw + if g:LatexBox_quickfix == 2 || g:LatexBox_quickfix == 4 + wincmd p + endif + endif + redraw + + " Write status message to screen + if a:status > 0 || len(getqflist())>1 + if s:log_contains_error(log) + let l:status_msg = ' ... failed!' + else + let l:status_msg = ' ... there were warnings!' + endif + else + let l:status_msg = ' ... success!' + endif + echomsg 'Compiling to ' . g:LatexBox_output_type . l:status_msg + endif +endfunction + +" Redefine uniq() for compatibility with older Vim versions (< 7.4.218) +function! s:uniq(list) + if exists('*uniq') + return uniq(a:list) + elseif len(a:list) <= 1 + return a:list + endif + + let last_element = get(a:list,0) + let uniq_list = [last_element] + + for i in range(1, len(a:list)-1) + let next_element = get(a:list, i) + if last_element == next_element + continue + endif + let last_element = next_element + call add(uniq_list, next_element) + endfor + return uniq_list +endfunction + +function! s:log_contains_error(file) + let lines = readfile(a:file) + let lines = filter(lines, 'v:val =~ ''^.*:\d\+: ''') + let lines = s:uniq(map(lines, 'matchstr(v:val, ''^.*\ze:\d\+:'')')) + let lines = filter(lines, 'filereadable(fnameescape(v:val))') + return len(lines) > 0 +endfunction +" }}} + +" LatexmkStatus {{{ +function! LatexBox_LatexmkStatus(detailed) + if a:detailed + if empty(g:latexmk_running_pids) + echo "latexmk is not running" + else + let plist = "" + for [basename, pid] in items(g:latexmk_running_pids) + if !empty(plist) + let plist .= '; ' + endif + let plist .= fnamemodify(basename, ':t') . ':' . pid + endfor + echo "latexmk is running (" . plist . ")" + endif + else + let basename = LatexBox_GetBuildBasename(1) + if has_key(g:latexmk_running_pids, basename) + echo "latexmk is running" + else + echo "latexmk is not running" + endif + endif +endfunction +" }}} + +" LatexmkStop {{{ +function! LatexBox_LatexmkStop(silent) + if empty(g:latexmk_running_pids) + if !a:silent + let basepath = LatexBox_GetBuildBasename(1) + let basename = fnamemodify(basepath, ':t') + echoerr "latexmk is not running for `" . basename . "'" + endif + else + let basepath = LatexBox_GetBuildBasename(1) + let basename = fnamemodify(basepath, ':t') + if has_key(g:latexmk_running_pids, basepath) + call s:kill_latexmk_process(g:latexmk_running_pids[basepath]) + call remove(g:latexmk_running_pids, basepath) + if !a:silent + echomsg "latexmk stopped for `" . basename . "'" + endif + elseif !a:silent + echoerr "latexmk is not running for `" . basename . "'" + endif + endif +endfunction +" }}} + +" Commands {{{ + +command! -bang Latexmk call LatexBox_Latexmk(<q-bang> == "!") +command! -bang LatexmkClean call LatexBox_LatexmkClean(<q-bang> == "!") +command! -bang LatexmkStatus call LatexBox_LatexmkStatus(<q-bang> == "!") +command! LatexmkStop call LatexBox_LatexmkStop(0) +command! LatexErrors call LatexBox_LatexErrors(-1) + +if g:LatexBox_latexmk_async || g:LatexBox_latexmk_preview_continuously + autocmd BufUnload <buffer> call LatexBox_LatexmkStop(1) + autocmd VimLeave * call <SID>kill_all_latexmk_processes() +endif + +" }}} + +" vim:fdm=marker:ff=unix:noet:ts=4:sw=4 + +endif diff --git a/ftplugin/latex-box/mappings.vim b/ftplugin/latex-box/mappings.vim new file mode 100644 index 00000000..7141635e --- /dev/null +++ b/ftplugin/latex-box/mappings.vim @@ -0,0 +1,110 @@ +if !exists('g:polyglot_disabled') || index(g:polyglot_disabled, 'latex') == -1 + +" LaTeX Box mappings + +if exists("g:LatexBox_no_mappings") + finish +endif + +" latexmk {{{ +noremap <buffer> <LocalLeader>ll :Latexmk<CR> +noremap <buffer> <LocalLeader>lL :Latexmk!<CR> +noremap <buffer> <LocalLeader>lc :LatexmkClean<CR> +noremap <buffer> <LocalLeader>lC :LatexmkClean!<CR> +noremap <buffer> <LocalLeader>lg :LatexmkStatus<CR> +noremap <buffer> <LocalLeader>lG :LatexmkStatus!<CR> +noremap <buffer> <LocalLeader>lk :LatexmkStop<CR> +noremap <buffer> <LocalLeader>le :LatexErrors<CR> +" }}} + +" View {{{ +noremap <buffer> <LocalLeader>lv :LatexView<CR> +" }}} + +" TOC {{{ +noremap <silent> <buffer> <LocalLeader>lt :LatexTOC<CR> +" }}} + +" List of labels {{{ +noremap <silent> <buffer> <LocalLeader>lj :LatexLabels<CR> +" }}} + +" Folding {{{ +if g:LatexBox_Folding == 1 + noremap <buffer> <LocalLeader>lf :LatexFold<CR> +endif +" }}} + +" Jump to match {{{ +if !exists('g:LatexBox_loaded_matchparen') + nmap <buffer> % <Plug>LatexBox_JumpToMatch + vmap <buffer> % <Plug>LatexBox_JumpToMatch + omap <buffer> % <Plug>LatexBox_JumpToMatch +endif +" }}} + +" Define text objects {{{ +vmap <buffer> ie <Plug>LatexBox_SelectCurrentEnvInner +vmap <buffer> ae <Plug>LatexBox_SelectCurrentEnvOuter +onoremap <buffer> ie :normal vie<CR> +onoremap <buffer> ae :normal vae<CR> +vmap <buffer> i$ <Plug>LatexBox_SelectInlineMathInner +vmap <buffer> a$ <Plug>LatexBox_SelectInlineMathOuter +onoremap <buffer> i$ :normal vi$<CR> +onoremap <buffer> a$ :normal va$<CR> +" }}} + +" Jump between sections {{{ +function! s:LatexBoxNextSection(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 + let notcomment = '\%(\%(\\\@<!\%(\\\\\)*\)\@<=%.*\)\@<!' + let pattern = notcomment . '\v\s*\\(' . join([ + \ '(sub)*section', + \ 'chapter', + \ 'part', + \ 'appendix', + \ '(front|back|main)matter'], '|') . ')>' + call search(pattern, 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 +noremap <buffer> <silent> ]] :call <SID>LatexBoxNextSection(0,0,0)<CR> +noremap <buffer> <silent> ][ :call <SID>LatexBoxNextSection(1,0,0)<CR> +noremap <buffer> <silent> [] :call <SID>LatexBoxNextSection(1,1,0)<CR> +noremap <buffer> <silent> [[ :call <SID>LatexBoxNextSection(0,1,0)<CR> +vnoremap <buffer> <silent> ]] :<c-u>call <SID>LatexBoxNextSection(0,0,1)<CR> +vnoremap <buffer> <silent> ][ :<c-u>call <SID>LatexBoxNextSection(1,0,1)<CR> +vnoremap <buffer> <silent> [] :<c-u>call <SID>LatexBoxNextSection(1,1,1)<CR> +vnoremap <buffer> <silent> [[ :<c-u>call <SID>LatexBoxNextSection(0,1,1)<CR> +" }}} + +" vim:fdm=marker:ff=unix:noet:ts=4:sw=4 + +endif diff --git a/ftplugin/latex-box/motion.vim b/ftplugin/latex-box/motion.vim new file mode 100644 index 00000000..2053149c --- /dev/null +++ b/ftplugin/latex-box/motion.vim @@ -0,0 +1,548 @@ +if !exists('g:polyglot_disabled') || index(g:polyglot_disabled, 'latex') == -1 + +" LaTeX Box motion functions + +" Motion options {{{ +" Opening and closing patterns +if !exists('g:LatexBox_open_pats') + let g:LatexBox_open_pats = [ '\\{','{','\\(','(','\\\[','\[', + \ '\\begin\s*{.\{-}}', '\\left\s*\%([^\\]\|\\.\|\\\a*\)'] + let g:LatexBox_close_pats = [ '\\}','}','\\)',')','\\\]','\]', + \ '\\end\s*{.\{-}}', '\\right\s*\%([^\\]\|\\.\|\\\a*\)'] +endif +" }}} + +" HasSyntax {{{ +" s:HasSyntax(syntaxName, [line], [col]) +function! s:HasSyntax(syntaxName, ...) + let line = a:0 >= 1 ? a:1 : line('.') + let col = a:0 >= 2 ? a:2 : col('.') + return index(map(synstack(line, col), + \ 'synIDattr(v:val, "name") == "' . a:syntaxName . '"'), + \ 1) >= 0 +endfunction +" }}} + +" Search and Skip Comments {{{ +" s:SearchAndSkipComments(pattern, [flags], [stopline]) +function! s:SearchAndSkipComments(pat, ...) + 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 LatexBox_InComment() + 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 +" }}} + +" Finding Matching Pair {{{ +function! s:FindMatchingPair(mode) + + if a:mode =~ 'h\|i' + 2match none + elseif a:mode == 'v' + normal! gv + endif + + if LatexBox_InComment() | return | endif + + " open/close pairs (dollars signs are treated apart) + let dollar_pat = '\$' + let notbslash = '\%(\\\@<!\%(\\\\\)*\)\@<=' + let notcomment = '\%(\%(\\\@<!\%(\\\\\)*\)\@<=%.*\)\@<!' + let anymatch = '\(' + \ . join(g:LatexBox_open_pats + g:LatexBox_close_pats, '\|') + \ . '\|' . dollar_pat . '\)' + + let lnum = line('.') + let cnum = searchpos('\A', 'cbnW', 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^'. anymatch , 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'. anymatch , col('.') - 1) + 1 + if cnum == 0 | return | endif + call cursor(lnum, cnum) + let delim = matchstr(getline(lnum), '\C^'. anymatch , 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^'. anymatch , 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 && s:HasSyntax('texMathZoneX', lnum0, cnum0) + let [lnum2, cnum2] = searchpos(notcomment . notbslash. dollar_pat, 'nW', line('w$')*(a:mode =~ 'h\|i') , 200) + else + let [lnum2, cnum2] = searchpos('\%(\%'. lnum . 'l\%' . cnum . 'c\)\@!'. notcomment . notbslash . dollar_pat, '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:LatexBox_open_pats)) + let open_pat = notbslash . g:LatexBox_open_pats[i] + let close_pat = notbslash . g:LatexBox_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', 'LatexBox_InComment()', + \ line('w$')*(a:mode =~ 'h\|i') , 200) + if a:mode =~ 'h\|i' + execute '2match MatchParen /\%(\%' . lnum . 'l\%' . cnum + \ . 'c' . g:LatexBox_open_pats[i] . '\|\%' + \ . lnum2 . 'l\%' . cnum2 . 'c' + \ . g:LatexBox_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', 'LatexBox_InComment()', + \ line('w0')*(a:mode =~ 'h\|i') , 200) + if a:mode =~ 'h\|i' + execute '2match MatchParen /\%(\%' . lnum2 . 'l\%' . cnum2 + \ . 'c' . g:LatexBox_open_pats[i] . '\|\%' + \ . lnum . 'l\%' . cnum . 'c' + \ . g:LatexBox_close_pats[i] . '\)/' + elseif a:mode =~ 'n\|v\|o' + call cursor(lnum2,cnum2) + endif + break + endif + endfor + + endif +endfunction + +" Allow to disable functionality if desired +if !exists('g:LatexBox_loaded_matchparen') + " Disable matchparen autocommands + augroup LatexBox_HighlightPairs + autocmd BufEnter * if !exists("g:loaded_matchparen") || !g:loaded_matchparen | runtime plugin/matchparen.vim | endif + autocmd BufEnter *.tex 3match none | unlet! g:loaded_matchparen | au! matchparen + autocmd! CursorMoved *.tex call s:FindMatchingPair('h') + autocmd! CursorMovedI *.tex call s:FindMatchingPair('i') + augroup END +endif + +" Use LatexBox'es FindMatchingPair as '%' (enable jump between e.g. $'s) +nnoremap <silent> <Plug>LatexBox_JumpToMatch :call <SID>FindMatchingPair('n')<CR> +vnoremap <silent> <Plug>LatexBox_JumpToMatch :call <SID>FindMatchingPair('v')<CR> +onoremap <silent> <Plug>LatexBox_JumpToMatch v:call <SID>FindMatchingPair('o')<CR> + +" }}} + +" select inline math {{{ +" s:SelectInlineMath(seltype) +" where seltype is either 'inner' or 'outer' +function! s:SelectInlineMath(seltype) + + let dollar_pat = '\\\@<!\$' + + if s:HasSyntax('texMathZoneX') + call s:SearchAndSkipComments(dollar_pat, 'cbW') + elseif getline('.')[col('.') - 1] == '$' + call s:SearchAndSkipComments(dollar_pat, 'bW') + else + return + endif + + if a:seltype == 'inner' + normal! l + endif + + if visualmode() ==# 'V' + normal! V + else + normal! v + endif + + call s:SearchAndSkipComments(dollar_pat, 'W') + + if a:seltype == 'inner' + normal! h + endif +endfunction + +vnoremap <silent> <Plug>LatexBox_SelectInlineMathInner + \ :<C-U>call <SID>SelectInlineMath('inner')<CR> +vnoremap <silent> <Plug>LatexBox_SelectInlineMathOuter + \ :<C-U>call <SID>SelectInlineMath('outer')<CR> +" }}} + +" select current environment {{{ +function! s:SelectCurrentEnv(seltype) + let [env, lnum, cnum, lnum2, cnum2] = LatexBox_GetCurrentEnvironment(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 +vnoremap <silent> <Plug>LatexBox_SelectCurrentEnvInner :<C-U>call <SID>SelectCurrentEnv('inner')<CR> +vnoremap <silent> <Plug>LatexBox_SelectCurrentEnvOuter :<C-U>call <SID>SelectCurrentEnv('outer')<CR> +" }}} + +" Jump to the next braces {{{ +" +function! LatexBox_JumpToNextBraces(backward) + let flags = '' + if a:backward + normal h + let flags .= 'b' + else + let flags .= 'c' + endif + if search('[][}{]', flags) > 0 + normal l + endif + let prev = strpart(getline('.'), col('.') - 2, 1) + let next = strpart(getline('.'), col('.') - 1, 1) + if next =~ '[]}]' && prev !~ '[][{}]' + return "\<Right>" + else + return '' + endif +endfunction +" }}} + +" Table of Contents {{{ + +" Special UTF-8 conversion +function! s:ConvertBack(line) + let line = a:line + if exists('g:LatexBox_plaintext_toc') + " + " Substitute stuff like '\IeC{\"u}' to plain 'u' + " + let line = substitute(line, '\\IeC\s*{\\.\(.\)}', '\1', 'g') + else + " + " Substitute stuff like '\IeC{\"u}' to corresponding unicode symbols + " + for [pat, symbol] in s:ConvBackPats + let line = substitute(line, pat, symbol, 'g') + endfor + endif + return line +endfunction + +function! s:ReadTOC(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) + let included = matchstr(line, '^\\@input{\zs[^}]*\ze}') + if included != '' + " append the input TOX to `toc` and `fileindices` + let newaux = prefix . '/' . included + let newtex = fnamemodify(newaux, ':r') . '.tex' + call s:ReadTOC(newaux, newtex, toc, fileindices) + continue + endif + + " Parse statements like: + " \@writefile{toc}{\contentsline {section}{\numberline {secnum}Section Title}{pagenumber}} + " \@writefile{toc}{\contentsline {section}{\tocsection {}{1}{Section Title}}{pagenumber}} + " \@writefile{toc}{\contentsline {section}{\numberline {secnum}Section Title}{pagenumber}{otherstuff}} + + let line = matchstr(line, + \ '\\@writefile{toc}{\\contentsline\s*\zs.*\ze}\s*$') + if empty(line) + continue + endif + + let tree = LatexBox_TexToTree(s:ConvertBack(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 section number + let secnum = '' + let tree = tree[1] + if len(tree) > 3 && empty(tree[1]) + call remove(tree, 1) + endif + if len(tree) > 1 && type(tree[0]) == type("") && tree[0] =~ '^\\\(\(chapter\)\?numberline\|tocsection\)' + let secnum = LatexBox_TreeToTex(tree[1]) + let secnum = substitute(secnum, '\\\S\+\s', '', 'g') + let secnum = substitute(secnum, '\\\S\+{\(.\{-}\)}', '\1', 'g') + let secnum = substitute(secnum, '^{\+\|}\+$', '', 'g') + call remove(tree, 1) + endif + " parse section title + let text = LatexBox_TreeToTex(tree) + let text = substitute(text, '^{\+\|}\+$', '', 'g') + let text = substitute(text, '\m^\\\(no\)\?\(chapter\)\?numberline\s*', '', '') + 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 [toc, fileindices] + +endfunction + +function! LatexBox_TOC(...) + + " Check if window already exists + let winnr = bufwinnr(bufnr('LaTeX TOC')) + " Two types of splits, horizontal and vertical + let l:hori = "new" + let l:vert = "vnew" + + " Set General Vars and initialize size + let l:type = g:LatexBox_split_type + let l:size = 10 + + " Size detection + if l:type == l:hori + let l:size = g:LatexBox_split_length + elseif l:type == l:vert + let l:size = g:LatexBox_split_width + endif + + if winnr >= 0 + if a:0 == 0 + silent execute winnr . 'wincmd w' + else + " Supplying an argument to this function causes toggling instead + " of jumping to the TOC window + if g:LatexBox_split_resize + silent exe "set columns-=" . l:size + endif + silent execute 'bwipeout' . bufnr('LaTeX TOC') + endif + return + endif + " Read TOC + let [toc, fileindices] = s:ReadTOC(LatexBox_GetAuxFile(), + \ LatexBox_GetMainTexFile()) + let calling_buf = bufnr('%') + + " Find closest section in current buffer + let closest_index = s:FindClosestSection(toc,fileindices) + + " Create TOC window and set local settings + if g:LatexBox_split_resize + silent exe "set columns+=" . l:size + endif + silent exe g:LatexBox_split_side l:size . l:type . ' LaTeX\ TOC' + + let b:toc = toc + let b:toc_numbers = 1 + let b:calling_win = bufwinnr(calling_buf) + setlocal filetype=latextoc + + " Add TOC entries and jump to the closest section + for entry in toc + call append('$', entry['number'] . "\t" . entry['text']) + endfor + if !g:LatexBox_toc_hidehelp + call append('$', "") + call append('$', "<Esc>/q: close") + call append('$', "<Space>: jump") + call append('$', "<Enter>: jump and close") + call append('$', "s: hide numbering") + endif + 0delete _ + + execute 'normal! ' . (closest_index + 1) . 'G' + + " Lock buffer + setlocal nomodifiable +endfunction + +" Binary search for the closest section +" return the index of the TOC entry +function! s:FindClosestSection(toc, fileindices) + let file = expand('%:p') + if !has_key(a:fileindices, file) + return 0 + endif + + let imax = len(a:fileindices[file]) + if imax > 0 + let imin = 0 + while imin < imax - 1 + let i = (imax + imin) / 2 + let tocindex = a:fileindices[file][i] + let entry = a:toc[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:fileindices[file][imin] + else + return 0 + endif +endfunction + +let s:ConvBackPats = 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}' , 'ö'], + \ ['\\''U}' , 'Ú'], + \ ['\\`U}' , 'Ù'], + \ ['\\^U}' , 'Û'], + \ ['\\¨U}' , 'Ü'], + \ ['\\"U}' , 'Ü'], + \ ['\\''u}' , 'ú'], + \ ['\\`u}' , 'ù'], + \ ['\\^u}' , 'û'], + \ ['\\¨u}' , 'ü'], + \ ['\\"u}' , 'ü'], + \ ['\\`N}' , 'Ǹ'], + \ ['\\\~N}' , 'Ñ'], + \ ['\\''n}' , 'ń'], + \ ['\\`n}' , 'ǹ'], + \ ['\\\~n}' , 'ñ'], + \], '[''\C\(\\IeC\s*{\)\?'' . v:val[0], v:val[1]]') +" }}} + +" TOC Command {{{ +command! LatexTOC call LatexBox_TOC() +command! LatexTOCToggle call LatexBox_TOC(1) +" }}} + +" vim:fdm=marker:ff=unix:noet:ts=4:sw=4 + +endif diff --git a/ftplugin/latextoc.vim b/ftplugin/latextoc.vim new file mode 100644 index 00000000..bfb8658e --- /dev/null +++ b/ftplugin/latextoc.vim @@ -0,0 +1,206 @@ +if !exists('g:polyglot_disabled') || index(g:polyglot_disabled, 'latex') == -1 + +" {{{1 Settings +setlocal buftype=nofile +setlocal bufhidden=wipe +setlocal nobuflisted +setlocal noswapfile +setlocal nowrap +setlocal nospell +setlocal cursorline +setlocal nonumber +setlocal nolist +setlocal tabstop=8 +setlocal cole=0 +setlocal cocu=nvic +if g:LatexBox_fold_toc + setlocal foldmethod=expr + setlocal foldexpr=TOCFoldLevel(v:lnum) + setlocal foldtext=TOCFoldText() +endif +" }}}1 + +" {{{1 Functions +" {{{2 TOCClose +function! s:TOCClose() + if g:LatexBox_split_resize + silent exe "set columns-=" . g:LatexBox_split_width + endif + bwipeout +endfunction + +" {{{2 TOCToggleNumbers +function! s:TOCToggleNumbers() + if b:toc_numbers + setlocal conceallevel=3 + let b:toc_numbers = 0 + else + setlocal conceallevel=0 + let b:toc_numbers = 1 + endif +endfunction + +" {{{2 EscapeTitle +function! s:EscapeTitle(titlestr) + let titlestr = substitute(a:titlestr, '\\[a-zA-Z@]*\>\s*{\?', '.*', 'g') + let titlestr = substitute(titlestr, '}', '', 'g') + let titlestr = substitute(titlestr, '\%(\.\*\s*\)\{2,}', '.*', 'g') + return titlestr +endfunction + +" {{{2 TOCActivate +function! s:TOCActivate(close) + let n = getpos('.')[1] - 1 + + if n >= len(b:toc) + return + endif + + let entry = b:toc[n] + + let titlestr = s:EscapeTitle(entry['text']) + + " Search for duplicates + " + let i=0 + let entry_hash = entry['level'].titlestr + let duplicates = 0 + while i<n + let i_entry = b:toc[n] + let i_hash = b:toc[i]['level'].s:EscapeTitle(b:toc[i]['text']) + if i_hash == entry_hash + let duplicates += 1 + endif + let i += 1 + endwhile + let toc_bnr = bufnr('%') + let toc_wnr = winnr() + + execute b:calling_win . 'wincmd w' + + let root = fnamemodify(entry['file'], ':h') . '/' + let files = [entry['file']] + for line in filter(readfile(entry['file']), 'v:val =~ ''\\input{''') + let file = matchstr(line, '{\zs.\{-}\ze\(\.tex\)\?}') . '.tex' + if file[0] != '/' + let file = root . file + endif + call add(files, file) + endfor + + " Find section in buffer (or inputted files) + if entry['level'] == 'label' + let re = '\(\\label\_\s*{\|label\s*=\s*\)' . titlestr . '\>' + else + let re = '\\' . entry['level'] . '\_\s*{' . titlestr . '}' + endif + call s:TOCFindMatch(re, duplicates, files) + + if a:close + if g:LatexBox_split_resize + silent exe "set columns-=" . g:LatexBox_split_width + endif + execute 'bwipeout ' . toc_bnr + else + execute toc_wnr . 'wincmd w' + endif +endfunction + +" {{{2 TOCFindMatch +function! s:TOCFindMatch(strsearch,duplicates,files) + if len(a:files) == 0 + echoerr "Could not find: " . a:strsearch + return + endif + + call s:TOCOpenBuf(a:files[0]) + let dups = a:duplicates + + " Skip duplicates + while dups > 0 + if search(a:strsearch, 'w') + let dups -= 1 + else + break + endif + endwhile + + if search(a:strsearch, 'w') + normal! zv + return + endif + + call s:TOCFindMatch(a:strsearch,dups,a:files[1:]) +endfunction + +" {{{2 TOCFoldLevel +function! TOCFoldLevel(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:LatexBox_fold_toc_levels >= 3 + if match_s3 + return ">3" + endif + endif + + if g:LatexBox_fold_toc_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 + +" {{{2 TOCFoldText +function! TOCFoldText() + let parts = matchlist(getline(v:foldstart), '^\(.*\)\t\(.*\)$') + return printf('%-8s%-72s', parts[1], parts[2]) +endfunction + +" {{{2 TOCOpenBuf +function! s:TOCOpenBuf(file) + + let bnr = bufnr(a:file) + if bnr == -1 + execute 'badd ' . a:file + let bnr = bufnr(a:file) + endif + execute 'buffer! ' . bnr + normal! gg + +endfunction + +" }}}1 + +" {{{1 Mappings +nnoremap <buffer> <silent> s :call <SID>TOCToggleNumbers()<CR> +nnoremap <buffer> <silent> q :call <SID>TOCClose()<CR> +nnoremap <buffer> <silent> <Esc> :call <SID>TOCClose()<CR> +nnoremap <buffer> <silent> <Space> :call <SID>TOCActivate(0)<CR> +nnoremap <buffer> <silent> <CR> :call <SID>TOCActivate(1)<CR> +nnoremap <buffer> <silent> <leftrelease> :call <SID>TOCActivate(0)<cr> +nnoremap <buffer> <silent> <2-leftmouse> :call <SID>TOCActivate(1)<cr> +nnoremap <buffer> <silent> G G4k +nnoremap <buffer> <silent> <Esc>OA k +nnoremap <buffer> <silent> <Esc>OB j +nnoremap <buffer> <silent> <Esc>OC l +nnoremap <buffer> <silent> <Esc>OD h +" }}}1 + +" vim:fdm=marker:ff=unix:et:ts=4:sw=4 + +endif diff --git a/ftplugin/tex_LatexBox.vim b/ftplugin/tex_LatexBox.vim new file mode 100644 index 00000000..6c8899a6 --- /dev/null +++ b/ftplugin/tex_LatexBox.vim @@ -0,0 +1,37 @@ +if !exists('g:polyglot_disabled') || index(g:polyglot_disabled, 'latex') == -1 + +" LaTeX Box plugin for Vim +" Maintainer: David Munger +" Email: mungerd@gmail.com +" Version: 0.9.6 + +if exists('*fnameescape') + function! s:FNameEscape(s) + return fnameescape(a:s) + endfunction +else + function! s:FNameEscape(s) + return a:s + endfunction +endif + +if !exists('b:LatexBox_loaded') + + let prefix = expand('<sfile>:p:h') . '/latex-box/' + + execute 'source ' . s:FNameEscape(prefix . 'common.vim') + execute 'source ' . s:FNameEscape(prefix . 'complete.vim') + execute 'source ' . s:FNameEscape(prefix . 'motion.vim') + execute 'source ' . s:FNameEscape(prefix . 'latexmk.vim') + execute 'source ' . s:FNameEscape(prefix . 'folding.vim') + " added by AH to add main.tex file finder + execute 'source ' . s:FNameEscape(prefix . 'findmain.vim') + execute 'source ' . s:FNameEscape(prefix . 'mappings.vim') + + let b:LatexBox_loaded = 1 + +endif + +" vim:fdm=marker:ff=unix:noet:ts=4:sw=4 + +endif diff --git a/ftplugin/vue.vim b/ftplugin/vue.vim index 14ad26a0..cc10c561 100644 --- a/ftplugin/vue.vim +++ b/ftplugin/vue.vim @@ -13,6 +13,13 @@ runtime! ftplugin/html.vim setlocal suffixesadd+=.vue +if !exists('g:no_plugin_maps') && !exists('g:no_vue_maps') + nnoremap <silent> <buffer> [[ :call search('^<\(template\<Bar>script\<Bar>style\)', 'bW')<CR> + nnoremap <silent> <buffer> ]] :call search('^<\(template\<Bar>script\<Bar>style\)', 'W')<CR> + nnoremap <silent> <buffer> [] :call search('^</\(template\<Bar>script\<Bar>style\)', 'bW')<CR> + nnoremap <silent> <buffer> ][ :call search('^</\(template\<Bar>script\<Bar>style\)', 'W')<CR> +endif + if exists('g:loaded_ale') let g:ale_linters = get(g:, 'ale_linters', {}) let g:ale_linters.vue = get(g:ale_linters, 'vue', ['eslint']) |