diff --git a/README.md b/README.md index dc2b86a..847ff42 100644 --- a/README.md +++ b/README.md @@ -83,7 +83,7 @@ If you need full functionality of any plugin, please use it directly with your p - [jsx](https://github.com/mxw/vim-jsx) (after) - [julia](https://github.com/JuliaEditorSupport/julia-vim) (syntax, indent, autoload, ftplugin) - [kotlin](https://github.com/udalov/kotlin-vim) (syntax, indent) -- [latex](https://github.com/LaTeX-Box-Team/LaTeX-Box) () +- [latex](https://github.com/LaTeX-Box-Team/LaTeX-Box) (syntax, indent, ftplugin) - [less](https://github.com/groenewege/vim-less) (syntax, indent, ftplugin) - [liquid](https://github.com/tpope/vim-liquid) (syntax, indent, ftplugin) - [livescript](https://github.com/gkz/vim-ls) (syntax, indent, compiler, ftplugin) diff --git a/after/syntax/tex.vim b/after/syntax/tex.vim new file mode 100644 index 0000000..f066fbe --- /dev/null +++ b/after/syntax/tex.vim @@ -0,0 +1,13 @@ +if !exists('g:polyglot_disabled') || index(g:polyglot_disabled, 'latex') == -1 + +" adds support for cleverref package +" \Cref, \cref, \cpageref, \labelcref, \labelcpageref +syn region texRefZone matchgroup=texStatement start="\\Cref{" end="}\|%stopzone\>" contains=@texRefGroup +syn region texRefZone matchgroup=texStatement start="\\\(label\|\)c\(page\|\)ref{" end="}\|%stopzone\>" contains=@texRefGroup + +" adds support for listings package +syn region texZone start="\\begin{lstlisting}" end="\\end{lstlisting}\|%stopzone\>" +syn match texInputFile "\\lstinputlisting\s*\(\[.*\]\)\={.\{-}}" contains=texStatement,texInputCurlies,texInputFileOpt +syn match texZone "\\lstinline\s*\(\[.*\]\)\={.\{-}}" + +endif diff --git a/ftdetect/polyglot.vim b/ftdetect/polyglot.vim index dc95205..d1a5f64 100644 --- a/ftdetect/polyglot.vim +++ b/ftdetect/polyglot.vim @@ -237,7 +237,7 @@ augroup END augroup filetypedetect " erlang:vim-erlang/vim-erlang-runtime -au BufNewFile,BufRead *.erl,*.hrl,rebar.config,*.app,*.app.src,*.yaws,*.xrl set ft=erlang +au BufNewFile,BufRead *.erl,*.hrl,rebar.config,*.app,*.app.src,*.yaws,*.xrl,*.escript set ft=erlang augroup END augroup filetypedetect diff --git a/ftplugin/ansible.vim b/ftplugin/ansible.vim index bbfd05d..e068dd3 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 0000000..2048884 --- /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\ %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('') +" }}} + +" 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*{[^}]*}\|\\\@ 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 0000000..aecb0d8 --- /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 + +" Wrap {{{ +function! s:GetSID() + return matchstr(expand(''), '\zs\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(':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 = '\%(\%(\\\@= 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! `o\end{' . env . '}' + execute 'keepjumps normal! ` + normal! gvgq + endif + else + execute 'keepjumps normal! `>a\end{' . env . '}' + execute 'keepjumps normal! `= 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('$', "/q: close") + call append('$', ": jump") + call append('$', ": 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 LatexCloseCurEnv =CloseCurEnv() +vnoremap LatexWrapSelection :call WrapSelection('')i +vnoremap LatexEnvWrapSelection :call PromptEnvWrapSelection() +vnoremap LatexEnvWrapFmtSelection :call PromptEnvWrapSelection(1) +nnoremap LatexChangeEnv :call ChangeEnvPrompt() +nnoremap LatexToggleStarEnv :call LatexToggleStarEnv() +" }}} + +" Commands {{{ +command! LatexLabels call 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 0000000..3b81b0d --- /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 0000000..d6f3627 --- /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 = '\%(\\\@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 0000000..15db368 --- /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(''), '\zs\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( == "!") +command! -bang LatexmkClean call LatexBox_LatexmkClean( == "!") +command! -bang LatexmkStatus call LatexBox_LatexmkStatus( == "!") +command! LatexmkStop call LatexBox_LatexmkStop(0) +command! LatexErrors call LatexBox_LatexErrors(-1) + +if g:LatexBox_latexmk_async || g:LatexBox_latexmk_preview_continuously + autocmd BufUnload call LatexBox_LatexmkStop(1) + autocmd VimLeave * call 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 0000000..7141635 --- /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 ll :Latexmk +noremap lL :Latexmk! +noremap lc :LatexmkClean +noremap lC :LatexmkClean! +noremap lg :LatexmkStatus +noremap lG :LatexmkStatus! +noremap lk :LatexmkStop +noremap le :LatexErrors +" }}} + +" View {{{ +noremap lv :LatexView +" }}} + +" TOC {{{ +noremap lt :LatexTOC +" }}} + +" List of labels {{{ +noremap lj :LatexLabels +" }}} + +" Folding {{{ +if g:LatexBox_Folding == 1 + noremap lf :LatexFold +endif +" }}} + +" Jump to match {{{ +if !exists('g:LatexBox_loaded_matchparen') + nmap % LatexBox_JumpToMatch + vmap % LatexBox_JumpToMatch + omap % LatexBox_JumpToMatch +endif +" }}} + +" Define text objects {{{ +vmap ie LatexBox_SelectCurrentEnvInner +vmap ae LatexBox_SelectCurrentEnvOuter +onoremap ie :normal vie +onoremap ae :normal vae +vmap i$ LatexBox_SelectInlineMathInner +vmap a$ LatexBox_SelectInlineMathOuter +onoremap i$ :normal vi$ +onoremap a$ :normal va$ +" }}} + +" 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 = '\%(\%(\\\@' + 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 ]] :call LatexBoxNextSection(0,0,0) +noremap ][ :call LatexBoxNextSection(1,0,0) +noremap [] :call LatexBoxNextSection(1,1,0) +noremap [[ :call LatexBoxNextSection(0,1,0) +vnoremap ]] :call LatexBoxNextSection(0,0,1) +vnoremap ][ :call LatexBoxNextSection(1,0,1) +vnoremap [] :call LatexBoxNextSection(1,1,1) +vnoremap [[ :call LatexBoxNextSection(0,1,1) +" }}} + +" 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 0000000..2053149 --- /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 = '\%(\\\@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 LatexBox_JumpToMatch :call FindMatchingPair('n') +vnoremap LatexBox_JumpToMatch :call FindMatchingPair('v') +onoremap LatexBox_JumpToMatch v:call FindMatchingPair('o') + +" }}} + +" select inline math {{{ +" s:SelectInlineMath(seltype) +" where seltype is either 'inner' or 'outer' +function! s:SelectInlineMath(seltype) + + let dollar_pat = '\\\@ LatexBox_SelectInlineMathInner + \ :call SelectInlineMath('inner') +vnoremap LatexBox_SelectInlineMathOuter + \ :call SelectInlineMath('outer') +" }}} + +" 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 LatexBox_SelectCurrentEnvInner :call SelectCurrentEnv('inner') +vnoremap LatexBox_SelectCurrentEnvOuter :call SelectCurrentEnv('outer') +" }}} + +" 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 "\" + 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('$', "/q: close") + call append('$', ": jump") + call append('$', ": 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 0000000..bfb8658 --- /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' + 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 s :call TOCToggleNumbers() +nnoremap q :call TOCClose() +nnoremap :call TOCClose() +nnoremap :call TOCActivate(0) +nnoremap :call TOCActivate(1) +nnoremap :call TOCActivate(0) +nnoremap <2-leftmouse> :call TOCActivate(1) +nnoremap G G4k +nnoremap OA k +nnoremap OB j +nnoremap OC l +nnoremap 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 0000000..6c8899a --- /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(':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 14ad26a..cc10c56 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 [[ :call search('^<\(template\script\style\)', 'bW') + nnoremap ]] :call search('^<\(template\script\style\)', 'W') + nnoremap [] :call search('^script\style\)', 'bW') + nnoremap ][ :call search('^script\style\)', 'W') +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']) diff --git a/indent/javascript.vim b/indent/javascript.vim index 010077d..93d348e 100644 --- a/indent/javascript.vim +++ b/indent/javascript.vim @@ -360,10 +360,8 @@ function GetJavascriptIndent() " start with strings,comments,etc. if s:stack[-1] =~? 'comment\|doc' - if l:line =~ '^\s*\*' - return cindent(v:lnum) - elseif l:line !~ '^\s*\/[/*]' - return -1 + if l:line !~ '^\s*\/[/*]' + return l:line =~ '^\s*\*' ? cindent(v:lnum) : -1 endif elseif s:stack[-1] =~? b:syng_str if b:js_cache[0] == v:lnum - 1 && s:Balanced(v:lnum-1,getline(v:lnum-1)) diff --git a/indent/tex.vim b/indent/tex.vim new file mode 100644 index 0000000..14af7b2 --- /dev/null +++ b/indent/tex.vim @@ -0,0 +1,140 @@ +if !exists('g:polyglot_disabled') || index(g:polyglot_disabled, 'latex') == -1 + +" LaTeX indent file (part of LaTeX Box) +" Maintainer: David Munger (mungerd@gmail.com) + +if exists("g:LatexBox_custom_indent") && ! g:LatexBox_custom_indent + finish +endif +if exists("b:did_indent") + finish +endif + +let b:did_indent = 1 + +setlocal indentexpr=LatexBox_TexIndent() +setlocal indentkeys=0=\\end,0=\\end{enumerate},0=\\end{itemize},0=\\end{description},0=\\right,0=\\item,0=\\),0=\\],0},o,O,0\\ + +let s:list_envs = ['itemize', 'enumerate', 'description'] +" indent on \left( and on \(, but not on ( +" indent on \left[ and on \[, but not on [ +" indent on \left\{ and on {, but not on \{ +let s:open_pat = '\\\@ 703 || (v:version == 703 && has('patch598')) + augroup LatexBox_Completion + autocmd! + autocmd CompleteDone call Latexbox_CallIndent() + augroup END +endif + +" vim:fdm=marker:ff=unix:noet:ts=4:sw=4 + +endif diff --git a/syntax/elixir.vim b/syntax/elixir.vim index ba1f10c..fa45260 100644 --- a/syntax/elixir.vim +++ b/syntax/elixir.vim @@ -64,9 +64,10 @@ syn match elixirVariable '&\d\+' syn keyword elixirPseudoVariable __FILE__ __DIR__ __MODULE__ __ENV__ __CALLER__ -syn match elixirNumber '\<\d\(_\?\d\)*\(\.[^[:space:][:digit:]]\@!\(_\?\d\)*\)\?\([eE][-+]\?\d\(_\?\d\)*\)\?\>' -syn match elixirNumber '\<0[xX][0-9A-Fa-f]\+\>' -syn match elixirNumber '\<0[bB][01]\+\>' +syn match elixirNumber '\<-\?\d\(_\?\d\)*\(\.[^[:space:][:digit:]]\@!\(_\?\d\)*\)\?\([eE][-+]\?\d\(_\?\d\)*\)\?\>' +syn match elixirNumber '\<-\?0[xX][0-9A-Fa-f]\+\>' +syn match elixirNumber '\<-\?0[oO][0-7]\+\>' +syn match elixirNumber '\<-\?0[bB][01]\+\>' syn match elixirRegexEscape "\\\\\|\\[aAbBcdDefGhHnrsStvVwW]\|\\\d\{3}\|\\x[0-9a-fA-F]\{2}" contained syn match elixirRegexEscapePunctuation "?\|\\.\|*\|\\\[\|\\\]\|+\|\\^\|\\\$\|\\|\|\\(\|\\)\|\\{\|\\}" contained diff --git a/syntax/go.vim b/syntax/go.vim index 69c2b01..ef0bdd6 100644 --- a/syntax/go.vim +++ b/syntax/go.vim @@ -40,6 +40,10 @@ if !exists("g:go_highlight_functions") let g:go_highlight_functions = 0 endif +if !exists("g:go_highlight_function_arguments") + let g:go_highlight_function_arguments = 0 +endif + if !exists("g:go_highlight_methods") let g:go_highlight_methods = 0 endif @@ -96,7 +100,7 @@ if exists("g:go_fold_enable") if index(g:go_fold_enable, 'package_comment') == -1 let s:fold_package_comment = 0 endif - + " Disabled by default. if index(g:go_fold_enable, 'comment') > -1 let s:fold_comment = 1 @@ -231,14 +235,14 @@ endif " var, const if s:fold_varconst syn region goVar start='var (' end='^\s*)$' transparent fold - \ contains=ALLBUT,goParen,goBlock,goFunction,goTypeName,goReceiverType,goReceiverVar + \ contains=ALLBUT,goParen,goBlock,goFunction,goTypeName,goReceiverType,goReceiverVar,goArgumentName,goArgumentType,goSimpleArguments syn region goConst start='const (' end='^\s*)$' transparent fold - \ contains=ALLBUT,goParen,goBlock,goFunction,goTypeName,goReceiverType,goReceiverVar + \ contains=ALLBUT,goParen,goBlock,goFunction,goTypeName,goReceiverType,goReceiverVar,goArgumentName,goArgumentType,goSimpleArguments else - syn region goVar start='var (' end='^\s*)$' transparent - \ contains=ALLBUT,goParen,goBlock,goFunction,goTypeName,goReceiverType,goReceiverVar + syn region goVar start='var (' end='^\s*)$' transparent + \ contains=ALLBUT,goParen,goBlock,goFunction,goTypeName,goReceiverType,goReceiverVar,goArgumentName,goArgumentType,goSimpleArguments syn region goConst start='const (' end='^\s*)$' transparent - \ contains=ALLBUT,goParen,goBlock,goFunction,goTypeName,goReceiverType,goReceiverVar + \ contains=ALLBUT,goParen,goBlock,goFunction,goTypeName,goReceiverType,goReceiverVar,goArgumentName,goArgumentType,goSimpleArguments endif " Single-line var, const, and import. @@ -345,14 +349,22 @@ endif hi def link goOperator Operator " Functions; -if g:go_highlight_functions != 0 - syn match goDeclaration /\/ nextgroup=goReceiver,goFunction skipwhite skipnl - syn match goReceiver /(\(\w\|[ *]\)\+)/ contained nextgroup=goFunction contains=goReceiverVar skipwhite skipnl - syn match goReceiverVar /\w\+/ nextgroup=goPointerOperator,goReceiverType skipwhite skipnl contained +if g:go_highlight_functions isnot 0 || g:go_highlight_function_arguments isnot 0 + syn match goFunctionCall /\w\+\ze(/ contains=goBuiltins,goDeclaration + syn match goDeclaration /\/ nextgroup=goReceiver,goFunction,goSimpleArguments skipwhite skipnl + syn match goReceiverVar /\w\+\ze\s\+\(\w\|\*\)/ nextgroup=goPointerOperator,goReceiverType skipwhite skipnl contained syn match goPointerOperator /\*/ nextgroup=goReceiverType contained skipwhite skipnl + syn match goFunction /\w\+/ nextgroup=goSimpleArguments contained skipwhite skipnl syn match goReceiverType /\w\+/ contained - syn match goFunction /\w\+/ contained - syn match goFunctionCall /\w\+\ze(/ contains=GoBuiltins,goDeclaration +if g:go_highlight_function_arguments isnot 0 + syn match goSimpleArguments /(\(\w\|\_s\|[*\.\[\],\{\}<>-]\)*)/ contained contains=goArgumentName nextgroup=goSimpleArguments skipwhite skipnl + syn match goArgumentName /\w\+\(\s*,\s*\w\+\)*\ze\s\+\(\w\|\.\|\*\|\[\)/ contained nextgroup=goArgumentType skipwhite skipnl + syn match goArgumentType /\([^,)]\|\_s\)\+,\?/ contained nextgroup=goArgumentName skipwhite skipnl + \ contains=goVarArgs,goType,goSignedInts,goUnsignedInts,goFloats,goComplexes,goDeclType,goBlock + hi def link goReceiverVar goArgumentName + hi def link goArgumentName Identifier +endif + syn match goReceiver /(\s*\w\+\(\s\+\*\?\s*\w\+\)\?\s*)\ze\s*\w/ contained nextgroup=goFunction contains=goReceiverVar skipwhite skipnl else syn keyword goDeclaration func endif diff --git a/syntax/latextoc.vim b/syntax/latextoc.vim new file mode 100644 index 0000000..0faca70 --- /dev/null +++ b/syntax/latextoc.vim @@ -0,0 +1,13 @@ +if !exists('g:polyglot_disabled') || index(g:polyglot_disabled, 'latex') == -1 + +syntax match helpText /^.*: .*/ +syntax match secNum /^\S\+\(\.\S\+\)\?\s*/ contained conceal +syntax match secLine /^\S\+\t.\+/ contains=secNum +syntax match mainSecLine /^[^\.]\+\t.*/ contains=secNum +syntax match ssubSecLine /^[^\.]\+\.[^\.]\+\.[^\.]\+\t.*/ contains=secNum +highlight link helpText PreProc +highlight link secNum Number +highlight link mainSecLine Title +highlight link ssubSecLine Comment + +endif diff --git a/syntax/markdown.vim b/syntax/markdown.vim index 2d2ee52..79af3af 100644 --- a/syntax/markdown.vim +++ b/syntax/markdown.vim @@ -110,11 +110,9 @@ syn match mkdCode /^\s*\n\(\(\s\{4,}[^ ]\|\t\+[^\t]\).*\n\)\+/ containe syn match mkdListItem /^\s*\%([-*+]\|\d\+\.\)\ze\s\+/ contained syn region mkdListItemLine start="^\s*\%([-*+]\|\d\+\.\)\s\+" end="$" oneline contains=@mkdNonListItem,mkdListItem,@Spell syn region mkdNonListItemBlock start="\(\%^\(\s*\([-*+]\|\d\+\.\)\s\+\)\@!\|\n\(\_^\_$\|\s\{4,}[^ ]\|\t+[^\t]\)\@!\)" end="^\(\s*\([-*+]\|\d\+\.\)\s\+\)\@=" contains=@mkdNonListItem,@Spell -syn match mkdRule /^\s*\*\s\{0,1}\*\s\{0,1}\*$/ -syn match mkdRule /^\s*-\s\{0,1}-\s\{0,1}-$/ -syn match mkdRule /^\s*_\s\{0,1}_\s\{0,1}_$/ -syn match mkdRule /^\s*-\{3,}$/ -syn match mkdRule /^\s*\*\{3,5}$/ +syn match mkdRule /^\s*\*\s\{0,1}\*\s\{0,1}\*\(\*\|\s\)*$/ +syn match mkdRule /^\s*-\s\{0,1}-\s\{0,1}-\(-\|\s\)*$/ +syn match mkdRule /^\s*_\s\{0,1}_\s\{0,1}_\(_\|\s\)*$/ " YAML frontmatter if get(g:, 'vim_markdown_frontmatter', 0) diff --git a/syntax/proto.vim b/syntax/proto.vim index 1f7c3c1..9c29977 100644 --- a/syntax/proto.vim +++ b/syntax/proto.vim @@ -61,6 +61,7 @@ syn keyword pbRepeat optional required repeated syn keyword pbDefault default syn keyword pbExtend extend extensions to max reserved syn keyword pbRPC service rpc returns +syn keyword pbStream stream syn keyword pbType int32 int64 uint32 uint64 sint32 sint64 syn keyword pbType fixed32 fixed64 sfixed32 sfixed64 @@ -92,6 +93,7 @@ if version >= 508 || !exists("did_proto_syn_inits") HiLink pbDefault Keyword HiLink pbExtend Keyword HiLink pbRPC Keyword + HiLink pbStream Keyword HiLink pbType Type HiLink pbTypedef Typedef HiLink pbBool Boolean diff --git a/syntax/qml.vim b/syntax/qml.vim index 2fef61e..2832dfe 100644 --- a/syntax/qml.vim +++ b/syntax/qml.vim @@ -59,7 +59,7 @@ syn keyword qmlLabel case default syn keyword qmlException try catch finally throw syn keyword qmlMessage alert confirm prompt status syn keyword qmlGlobal self -syn keyword qmlDeclaration property signal +syn keyword qmlDeclaration property signal readonly syn keyword qmlReserved abstract boolean byte char class const debugger enum export extends final float goto implements import interface long native package pragma private protected public short static super synchronized throws transient volatile if get(g:, 'qml_fold', 0) diff --git a/syntax/tmux.vim b/syntax/tmux.vim index 6915389..64dd0e4 100644 --- a/syntax/tmux.vim +++ b/syntax/tmux.vim @@ -19,9 +19,9 @@ if !exists('g:polyglot_disabled') || index(g:polyglot_disabled, 'tmux') == -1 " - Switch on syntax highlighting by adding "syntax enable" to .vimrc. " -if version < 600 +if v:version < 600 syntax clear -elseif exists("b:current_syntax") +elseif exists('b:current_syntax') finish endif @@ -52,6 +52,7 @@ syn keyword tmuxCmds \ confirm \ confirm-before \ copy-mode + \ copy-mode-vi \ delete-buffer \ deleteb \ detach @@ -327,6 +328,6 @@ hi def link tmuxTodo Todo hi def link tmuxVariable Constant hi def link tmuxVariableExpansion Constant -let b:current_syntax = "tmux" +let b:current_syntax = 'tmux' endif diff --git a/syntax/toml.vim b/syntax/toml.vim index 17ea3cf..7a5b44f 100644 --- a/syntax/toml.vim +++ b/syntax/toml.vim @@ -38,7 +38,9 @@ syn match tomlBoolean /\<\%(true\|false\)\>/ display hi def link tomlBoolean Boolean " https://tools.ietf.org/html/rfc3339 -syn match tomlDate /\d\{4\}-\d\{2\}-\d\{2\}T\d\{2\}:\d\{2\}:\d\{2\}\%(\.\d\+\)\?\%(Z\|[+-]\d\{2\}:\d\{2\}\)/ display +syn match tomlDate /\d\{4\}-\d\{2\}-\d\{2\}/ display +syn match tomlDate /\d\{2\}:\d\{2\}:\d\{2\}\%(\.\d\+\)\?/ display +syn match tomlDate /\d\{4\}-\d\{2\}-\d\{2\}[T ]\d\{2\}:\d\{2\}:\d\{2\}\%(\.\d\+\)\?\%(Z\|[+-]\d\{2\}:\d\{2\}\)\?/ display hi def link tomlDate Constant syn region tomlKeyDq oneline start=/"/ end=/"/ contains=tomlEscape contained