summaryrefslogtreecommitdiffstats
path: root/ftplugin/latex-box/complete.vim
diff options
context:
space:
mode:
Diffstat (limited to 'ftplugin/latex-box/complete.vim')
-rw-r--r--ftplugin/latex-box/complete.vim805
1 files changed, 805 insertions, 0 deletions
diff --git a/ftplugin/latex-box/complete.vim b/ftplugin/latex-box/complete.vim
new file mode 100644
index 00000000..76f909b9
--- /dev/null
+++ b/ftplugin/latex-box/complete.vim
@@ -0,0 +1,805 @@
+" LaTeX Box completion
+
+setlocal omnifunc=LatexBox_Complete
+
+" <SID> Wrap {{{
+function! s:GetSID()
+ return matchstr(expand('<sfile>'), '\zs<SNR>\d\+_\ze.*$')
+endfunction
+let s:SID = s:GetSID()
+function! s:SIDWrap(func)
+ return s:SID . a:func
+endfunction
+" }}}
+
+" Completion {{{
+if !exists('g:LatexBox_completion_close_braces')
+ let g:LatexBox_completion_close_braces = 1
+endif
+if !exists('g:LatexBox_bibtex_wild_spaces')
+ let g:LatexBox_bibtex_wild_spaces = 1
+endif
+
+if !exists('g:LatexBox_cite_pattern')
+ let g:LatexBox_cite_pattern = '\C\\\a*cite\a*\*\?\(\[[^\]]*\]\)*\_\s*{'
+endif
+if !exists('g:LatexBox_ref_pattern')
+ let g:LatexBox_ref_pattern = '\C\\v\?\(eq\|page\|[cC]\)\?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 =~ '\C\\begin\_\s*{$'
+ let s:completion_type = 'begin'
+ elseif line_start =~ '\C\\end\_\s*{$'
+ let s:completion_type = 'end'
+ elseif line_start =~ g:LatexBox_ref_pattern . '$'
+ let s:completion_type = 'ref'
+ elseif line_start =~ 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 bibliography_cmds = [
+ \ '\\bibliography',
+ \ '\\addbibresource',
+ \ '\\addglobalbib',
+ \ '\\addsectionbib',
+ \ ]
+
+ let lines = readfile(file)
+
+ let bibdata_list = []
+
+ for cmd in bibliography_cmds
+ let bibdata_list += map(filter(copy(lines),
+ \ 'v:val =~ ''\C' . cmd . '\s*{[^}]\+}'''),
+ \ 'matchstr(v:val, ''\C' . cmd . '\s*{\zs[^}]\+\ze}'')')
+ endfor
+
+ let bibdata_list += map(filter(copy(lines),
+ \ 'v:val =~ ''\C\\\%(input\|include\)\s*{[^}]\+}'''),
+ \ 's:FindBibData(LatexBox_kpsewhich(matchstr(v:val,'
+ \ . '''\C\\\%(input\|include\)\s*{\zs[^}]\+\ze}'')))')
+
+ let bibdata_list += map(filter(copy(lines),
+ \ 'v:val =~ ''\C\\\%(input\|include\)\s\+\S\+'''),
+ \ 's:FindBibData(LatexBox_kpsewhich(matchstr(v:val,'
+ \ . '''\C\\\%(input\|include\)\s\+\zs\S\+\ze'')))')
+
+ return join(bibdata_list, ',')
+endfunction
+
+let s:bstfile = expand('<sfile>:p:h') . '/vimcomplete'
+
+function! LatexBox_BibSearch(regexp)
+ let res = []
+
+ " Find data from bib files
+ let bibdata = 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)
+
+ silent execute '! cd ' shellescape(LatexBox_GetTexRoot()) .
+ \ ' ; bibtex -terse '
+ \ . fnamemodify(auxfile, ':t') . ' >/dev/null'
+
+ 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])
+ 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}')
+ 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 {{{
+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 = []
+ for m in LatexBox_BibSearch(regexp)
+ let type = m['type'] == '' ? '[-]' : '[' . m['type'] . '] '
+ let auth = m['author'] == '' ? '' : m['author'][:20] . ' '
+ let year = m['year'] == '' ? '' : '(' . m['year'] . ')'
+ let w = { 'word': m['key'],
+ \ 'abbr': type . auth . year,
+ \ 'menu': m['title'] }
+
+ " close braces if 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"
+ continue
+ endif
+
+ if 0 == search( '{\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( '\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.
+ silent execute '1sp +let\ labels=s:ExtractLabels()|let\ inputs=s:ExtractInputs()|quit! ' . a:file
+ let s:LabelCache[a:file] = [ getftime(a:file), labels, inputs ]
+ endif
+
+ " We need to create a copy of s:LabelCache[fid][1], otherwise all inputs'
+ " labels would be added to the current file's label cache upon each
+ " completion call, leading to duplicates/triplicates/etc. and decreased
+ " performance.
+ " Also, because we don't anything with the list besides matching copies,
+ " we can get away with a shallow copy for now.
+ let labels = copy(s:LabelCache[a:file][1])
+
+ for input in s:LabelCache[a:file][2]
+ let labels += s:GetLabelCache(input)
+ endfor
+
+ return labels
+endfunction
+"}}}
+
+" Complete Labels {{{
+function! s:CompleteLabels(regex)
+ let labels = s:GetLabelCache(LatexBox_GetAuxFile())
+
+ let matches = filter( copy(labels), 'match(v:val[0], "' . a:regex . '") != -1' )
+ if empty(matches)
+ " also try to match label and number
+ let regex_split = split(a:regex)
+ if len(regex_split) > 1
+ let base = regex_split[0]
+ let number = escape(join(regex_split[1:], ' '), '.')
+ let matches = filter( copy(labels), 'match(v:val[0], "' . base . '") != -1 && match(v:val[1], "' . number . '") != -1' )
+ endif
+ endif
+ if empty(matches)
+ " also try to match number
+ let matches = filter( copy(labels), 'match(v:val[1], "' . a:regex . '") != -1' )
+ endif
+
+ let suggestions = []
+ for m in matches
+ let entry = {'word': m[0], 'menu': printf("%7s [p. %s]", '('.m[1].')', m[2])}
+ if g:LatexBox_completion_close_braces && !s:NextCharsMatch('^\s*[,}]')
+ " add trailing '}'
+ let entry = copy(entry)
+ let entry.abbr = entry.word
+ let entry.word = entry.word . '}'
+ endif
+ call add(suggestions, entry)
+ endfor
+
+ return suggestions
+endfunction
+" }}}
+
+" Complete Inline Math Or Not {{{
+" Return 1, when cursor is in a math env:
+" 1, there is a single $ in the current line on the left of cursor
+" 2, there is an open-eq-env on/above the current line
+" (open-eq-env : \(, \[, and \begin{eq-env} )
+" Return 0, when cursor is not in a math env
+function! s:LatexBox_complete_inlineMath_or_not()
+
+ " switch of inline math completion feature
+ if g:LatexBox_complete_inlineMath == 0
+ return 0
+ endif
+
+ " env names that can't appear in an eq env
+ if !exists('s:LatexBox_doc_structure_patterns')
+ let s:LatexBox_doc_structure_patterns = '\%(' . '\\begin\s*{document}\|' .
+ \ '\\\%(chapter\|section\|subsection\|subsubsection\)\*\?\s*{' . '\)'
+ endif
+
+ if !exists('s:LatexBox_eq_env_open_patterns')
+ let s:LatexBox_eq_env_open_patterns = ['\\(','\\\[']
+ endif
+ if !exists('s:LatexBox_eq_env_close_patterns')
+ let s:LatexBox_eq_env_close_patterns = ['\\)','\\\]']
+ endif
+
+ let notcomment = '\%(\%(\\\@<!\%(\\\\\)*\)\@<=%.*\)\@<!'
+
+ let lnum_saved = line('.')
+ let cnum_saved = col('.') -1
+
+ let line = getline('.')
+ let line_start_2_cnum_saved = line[:cnum_saved]
+
+ " determine whether there is a single $ before cursor
+ let cursor_dollar_pair = 0
+ while matchend(line_start_2_cnum_saved, '\$[^$]\+\$', cursor_dollar_pair) >= 0
+ " find the end of dollar pair
+ let cursor_dollar_pair = matchend(line_start_2_cnum_saved, '\$[^$]\+\$', cursor_dollar_pair)
+ endwhile
+ " find single $ after cursor_dollar_pair
+ let cursor_single_dollar = matchend(line_start_2_cnum_saved, '\$', cursor_dollar_pair)
+
+ " if single $ is found
+ if cursor_single_dollar >= 0
+ " check whether $ is in \(...\), \[...\], or \begin{eq}...\end{eq}
+
+ " check current line,
+ " search for LatexBox_eq_env_close_patterns: \[ and \(
+ let lnum = line('.')
+ for i in range(0, (len(s:LatexBox_eq_env_open_patterns)-1))
+ call cursor(lnum_saved, cnum_saved)
+ let cnum_close = searchpos(''. s:LatexBox_eq_env_close_patterns[i].'', 'cbW', lnum_saved)[1]
+ let cnum_open = matchend(line_start_2_cnum_saved, s:LatexBox_eq_env_open_patterns[i], cnum_close)
+ if cnum_open >= 0
+ let s:eq_dollar_parenthesis_bracket_empty = ''
+ let s:eq_pos = cursor_single_dollar - 1
+ return 1
+ end
+ endfor
+
+ " check the lines above
+ " search for s:LatexBox_doc_structure_patterns, and end-of-math-env
+ let lnum -= 1
+ while lnum > 0
+ let line = getline(lnum)
+ if line =~ notcomment . '\(' . s:LatexBox_doc_structure_patterns .
+ \ '\|' . '\\end\s*{\(' . g:LatexBox_eq_env_patterns . '\)\*\?}\)'
+ " when s:LatexBox_doc_structure_patterns or g:LatexBox_eq_env_patterns
+ " are found first, complete math, leave with $ at both sides
+ let s:eq_dollar_parenthesis_bracket_empty = '$'
+ let s:eq_pos = cursor_single_dollar
+ break
+ elseif line =~ notcomment . '\\begin\s*{\(' . g:LatexBox_eq_env_patterns . '\)\*\?}'
+ " g:LatexBox_eq_env_patterns is found, complete math, remove $
+ let s:eq_dollar_parenthesis_bracket_empty = ''
+ let s:eq_pos = cursor_single_dollar - 1
+ break
+ endif
+ let lnum -= 1
+ endwhile
+
+ return 1
+ else
+ " no $ is found, then search for \( or \[ in current line
+ " 1, whether there is \(
+ call cursor(lnum_saved, cnum_saved)
+ let cnum_parenthesis_close = searchpos('\\)', 'cbW', lnum_saved)[1]
+ let cnum_parenthesis_open = matchend(line_start_2_cnum_saved, '\\(', cnum_parenthesis_close)
+ if cnum_parenthesis_open >= 0
+ let s:eq_dollar_parenthesis_bracket_empty = '\)'
+ let s:eq_pos = cnum_parenthesis_open
+ return 1
+ end
+
+ " 2, whether there is \[
+ call cursor(lnum_saved, cnum_saved)
+ let cnum_bracket_close = searchpos('\\\]', 'cbW', lnum_saved)[1]
+ let cnum_bracket_open = matchend(line_start_2_cnum_saved, '\\\[', cnum_bracket_close)
+ if cnum_bracket_open >= 0
+ let s:eq_dollar_parenthesis_bracket_empty = '\]'
+ let s:eq_pos = cnum_bracket_open
+ return 1
+ end
+
+ " not inline math completion
+ return 0
+ endif
+
+endfunction
+" }}}
+
+" Complete inline euqation{{{
+function! s:LatexBox_inlineMath_completion(regex, ...)
+
+ if a:0 == 0
+ let file = LatexBox_GetMainTexFile()
+ else
+ let file = a:1
+ endif
+
+ if empty(glob(file, 1))
+ return ''
+ endif
+
+ if empty(s:eq_dollar_parenthesis_bracket_empty)
+ let inline_pattern1 = '\$\s*\(' . escape(substitute(a:regex[1:], '^\s\+', '', ""), '\.*^') . '[^$]*\)\s*\$'
+ let inline_pattern2 = '\\(\s*\(' . escape(substitute(a:regex[1:], '^\s\+', '', ""), '\.*^') . '.*\)\s*\\)'
+ else
+ let inline_pattern1 = '\$\s*\(' . escape(substitute(a:regex, '^\s\+', '', ""), '\.*^') . '[^$]*\)\s*\$'
+ let inline_pattern2 = '\\(\s*\(' . escape(substitute(a:regex, '^\s\+', '', ""), '\.*^') . '.*\)\s*\\)'
+ endif
+
+
+ let suggestions = []
+ let line_num = 0
+ for line in readfile(file)
+ let line_num = line_num + 1
+
+ let suggestions += s:LatexBox_inlineMath_mathlist(line,inline_pattern1 , line_num) + s:LatexBox_inlineMath_mathlist( line,inline_pattern2, line_num)
+
+ " search for included files
+ let included_file = matchstr(line, '^\\@input{\zs[^}]*\ze}')
+ if included_file != ''
+ let included_file = LatexBox_kpsewhich(included_file)
+ call extend(suggestions, s:LatexBox_inlineMath_completion(a:regex, included_file))
+ endif
+ endfor
+
+ return suggestions
+endfunction
+" }}}
+
+" Search for inline maths {{{
+" search for $ ... $ and \( ... \) in each line
+function! s:LatexBox_inlineMath_mathlist(line,inline_pattern, line_num)
+ let col_start = 0
+ let suggestions = []
+ while 1
+ let matches = matchlist(a:line, a:inline_pattern, col_start)
+ if !empty(matches)
+
+ " show line number of inline math
+ let entry = {'word': matches[1], 'menu': '[' . a:line_num . ']'}
+
+ if s:eq_dollar_parenthesis_bracket_empty != ''
+ let entry = copy(entry)
+ let entry.abbr = entry.word
+ let entry.word = entry.word . s:eq_dollar_parenthesis_bracket_empty
+ endif
+ call add(suggestions, entry)
+
+ " update col_start
+ let col_start = matchend(a:line, a:inline_pattern, col_start)
+ else
+ break
+ endif
+ endwhile
+
+ return suggestions
+endfunction
+" }}}
+
+" Close Current Environment {{{
+function! s:CloseCurEnv()
+ " first, try with \left/\right pairs
+ let [lnum, cnum] = searchpairpos('\C\\left\>', '', '\C\\right\>', 'bnW', 'LatexBox_InComment()')
+ if lnum
+ let line = strpart(getline(lnum), cnum - 1)
+ let bracket = matchstr(line, '^\\left\zs\((\|\[\|\\{\||\|\.\)\ze')
+ for [open, close] in [['(', ')'], ['\[', '\]'], ['\\{', '\\}'], ['|', '|'], ['\.', '|']]
+ let bracket = substitute(bracket, open, close, 'g')
+ endfor
+ return '\right' . bracket
+ endif
+
+ " second, try with environments
+ let env = LatexBox_GetCurrentEnvironment()
+ if env == '\['
+ return '\]'
+ elseif env == '\('
+ return '\)'
+ elseif env != ''
+ return '\end{' . env . '}'
+ endif
+ return ''
+endfunction
+" }}}
+
+" Wrap Selection {{{
+function! s:WrapSelection(wrapper)
+ keepjumps normal! `>a}
+ execute 'keepjumps normal! `<i\' . a:wrapper . '{'
+endfunction
+" }}}
+
+" Wrap Selection in Environment with Prompt {{{
+function! s:PromptEnvWrapSelection(...)
+ let env = input('environment: ', '', 'customlist,' . s:SIDWrap('GetEnvironmentList'))
+ if empty(env)
+ return
+ endif
+ " LaTeXBox's custom indentation can interfere with environment
+ " insertion when environments are indented (common for nested
+ " environments). Temporarily disable it for this operation:
+ let ieOld = &indentexpr
+ setlocal indentexpr=""
+ if visualmode() ==# 'V'
+ execute 'keepjumps normal! `>o\end{' . env . '}'
+ execute 'keepjumps normal! `<O\begin{' . env . '}'
+ " indent and format, if requested.
+ if a:0 && a:1
+ normal! gv>
+ normal! gvgq
+ endif
+ else
+ execute 'keepjumps normal! `>a\end{' . env . '}'
+ execute 'keepjumps normal! `<i\begin{' . env . '}'
+ endif
+ exe "setlocal indentexpr=" . ieOld
+endfunction
+" }}}
+
+" 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
+" }}}
+
+" Next Charaters Match {{{
+function! s:NextCharsMatch(regex)
+ let rest_of_line = strpart(getline('.'), col('.') - 1)
+ return rest_of_line =~ a:regex
+endfunction
+" }}}
+
+" Mappings {{{
+inoremap <silent> <Plug>LatexCloseCurEnv <C-R>=<SID>CloseCurEnv()<CR>
+vnoremap <silent> <Plug>LatexWrapSelection :<c-u>call <SID>WrapSelection('')<CR>i
+vnoremap <silent> <Plug>LatexEnvWrapSelection :<c-u>call <SID>PromptEnvWrapSelection()<CR>
+vnoremap <silent> <Plug>LatexEnvWrapFmtSelection :<c-u>call <SID>PromptEnvWrapSelection(1)<CR>
+nnoremap <silent> <Plug>LatexChangeEnv :call <SID>ChangeEnvPrompt()<CR>
+" }}}
+
+" vim:fdm=marker:ff=unix:noet:ts=4:sw=4