"============================================================================= " File: compiler.vim " Author: Srinath Avadhanula " Created: Tue Apr 23 05:00 PM 2002 PST " " Description: functions for compiling/viewing/searching latex documents " CVS: $Id: compiler.vim,v 1.47 2003/09/07 17:22:09 srinathava Exp $ "============================================================================= " SetTeXCompilerTarget: sets the 'target' for the next call to RunLaTeX() {{{ function! SetTeXCompilerTarget(type, target) if a:target == '' if g:Tex_DefaultTargetFormat == 'dvi' let target = input('Enter the target ([dvi]/ps/pdf/...) for '.a:type.'r: ') elseif g:Tex_DefaultTargetFormat == 'ps' let target = input('Enter the target (dvi/[ps]/pdf/...) for '.a:type.'r: ') elseif g:Tex_DefaultTargetFormat =~ 'pdf' let target = input('Enter the target (dvi/ps/[pdf]/...) for '.a:type.'r: ') else let target = input('Enter the target (dvi/ps/pdf/['.g:Tex_DefaultTargetFormat.']) for '.a:type.'r: ') endif else let target = a:target endif if target == '' let target = 'dvi' endif if exists('g:Tex_'.a:type.'Rule_'.target) if a:type == 'Compile' let &l:makeprg = escape(g:Tex_CompileRule_{target}, g:Tex_EscapeChars) elseif a:type == 'View' exec 'let s:viewer = g:Tex_'.a:type.'Rule_'.target endif let s:target = target else let curd = getcwd() exe 'cd '.expand('%:p:h') if glob('makefile*') == '' && glob('Makefile*') == '' if has('gui_running') call confirm( \'No '.a:type.' rule defined for target '.target."\n". \'Please specify a rule in texrc.vim'."\n". \' :help Tex_CompileRule_format'."\n". \'for more information', \"&ok", 1, 'Warning') else call input( \'No '.a:type.' rule defined for target '.target."\n". \'Please specify a rule in texrc.vim'."\n". \' :help Tex_ViewRule_format'."\n". \'for more information' \) endif else echomsg 'assuming target is for makefile' let s:target = target endif exe 'cd '.curd endif endfunction function! SetTeXTarget(...) if a:0 < 1 if g:Tex_DefaultTargetFormat == 'dvi' let target = input('Enter the target for compiler and viewer ([dvi]/ps/pdf/...): ') elseif g:Tex_DefaultTargetFormat == 'ps' let target = input('Enter the target for compiler and viewer (dvi/[ps]/pdf/...): ') elseif g:Tex_DefaultTargetFormat =~ 'pdf' let target = input('Enter the target for compiler and viewer (dvi/ps/[pdf]/...): ') else let target = input('Enter the target for compiler and viewer (dvi/ps/pdf/['.g:Tex_DefaultTargetFormat.']): ') endif else let target = a:1 endif if target == '' let target = 'dvi' endif call SetTeXCompilerTarget('Compile', target) call SetTeXCompilerTarget('View', target) endfunction com! -nargs=1 TCTarget :call SetTeXCompilerTarget('Compile', ) com! -nargs=1 TVTarget :call SetTeXCompilerTarget('View', ) com! -nargs=? TTarget :call SetTeXTarget() " }}} " Tex_CompileLatex: compiles the present file. {{{ " Description: function! Tex_CompileLatex() if &ft != 'tex' echo "calling RunLaTeX from a non-tex file" return end " close any preview windows left open. pclose! " Logic to choose how to compile: " if b:fragmentFile exists, then this is a fragment " therefore, just compile this file " else " if makefile or Makefile exists, then use that " elseif *.latexmain exists " use that " else use current file " " if mainfname exists, then it means it was supplied to RunLaTeX(). " Extract the complete file name including the extension. let mainfname = Tex_GetMainFileName(':r') call Tex_Debug('Tex_CompileLatex: getting mainfname = ['.mainfname.'] from Tex_GetMainFileName', 'comp') if exists('b:fragmentFile') || mainfname == '' let mainfname = escape(expand('%:t'), ' ') endif " if a makefile exists and the user wants to use it, then use that " irrespective of whether *.latexmain exists or not. mainfname is still " extracted from *.latexmain (if possible) log file name depends on the " main file which will be compiled. if g:Tex_UseMakefile && (glob('makefile') != '' || glob('Makefile') != '') let _makeprg = &l:makeprg let &l:makeprg = 'make $*' if exists('s:target') call Tex_Debug('Tex_CompileLatex: execing [make! '.s:target.']', 'comp') exec 'make! '.s:target else call Tex_Debug('Tex_CompileLatex: execing [make!]', 'comp') exec 'make!' endif let &l:makeprg = _makeprg else " If &makeprg has something like "$*.ps", it means that it wants the " file-name without the extension... Therefore remove it. if &makeprg =~ '\$\*\.\w\+' let mainfname = fnamemodify(mainfname, ':r') endif call Tex_Debug('Tex_CompileLatex: execing [make! '.mainfname.']', 'comp') exec 'make! '.mainfname endif redraw! endfunction " }}} " Tex_SetupErrorWindow: sets up the cwindow and preview of the .log file {{{ " Description: function! Tex_SetupErrorWindow() let mainfname = Tex_GetMainFileName(':r') if exists('b:fragmentFile') || mainfname == '' let mainfname = expand('%:t') endif let winnum = winnr() " close the quickfix window before trying to open it again, otherwise " whether or not we end up in the quickfix window after the :cwindow " command is not fixed. cclose cwindow " create log file name from mainfname let mfnlog = fnamemodify(mainfname, ":t:r").'.log' call Tex_Debug('mfnlog = '.mfnlog, 'comp') " if we moved to a different window, then it means we had some errors. if winnum != winnr() call UpdatePreviewWindow(mfnlog) exe 'nnoremap j j:call UpdatePreviewWindow("'.mfnlog.'")' exe 'nnoremap k k:call UpdatePreviewWindow("'.mfnlog.'")' exe 'nnoremap :call UpdatePreviewWindow("'.mfnlog.'")' exe 'nnoremap :call UpdatePreviewWindow("'.mfnlog.'")' exe 'nnoremap :call GotoErrorLocation("'.mfnlog.'")' setlocal nowrap " resize the window to just fit in with the number of lines. exec ( line('$') < 4 ? line('$') : 4 ).' wincmd _' call GotoErrorLocation(mfnlog) endif endfunction " }}} " RunLaTeX: compilation function {{{ " this function runs the latex command on the currently open file. often times " the file being currently edited is only a fragment being \input'ed into some " master tex file. in this case, make a file called mainfile.latexmain in the " directory containig the file. in other words, if the current file is " ~/thesis/chapter.tex " so that doing "latex chapter.tex" doesnt make sense, then make a file called " main.tex.latexmain " in the ~/thesis directory. this will then run "latex main.tex" when " RunLaTeX() is called. function! RunLaTeX() call Tex_Debug('getting to RunLaTeX, b:fragmentFile = '.exists('b:fragmentFile'), 'comp') let dir = expand("%:p:h").'/' let curd = getcwd() exec 'cd '.expand("%:p:h") " first get the dependency chain of this format. let dependency = s:target if exists('g:Tex_FormatDependency_'.s:target) if g:Tex_FormatDependency_{s:target} !~ ','.s:target.'$' let dependency = g:Tex_FormatDependency_{s:target}.','.s:target else let dependency = g:Tex_FormatDependency_{s:target} endif endif call Tex_Debug('getting dependency chain = ['.dependency.']', 'comp') " now compile to the final target format via each dependency. let i = 1 while Tex_Strntok(dependency, ',', i) != '' let s:target = Tex_Strntok(dependency, ',', i) call SetTeXCompilerTarget('Compile', s:target) call Tex_Debug('setting target to '.s:target, 'comp') if g:Tex_MultipleCompileFormats =~ '\<'.s:target.'\>' call Tex_CompileMultipleTimes() else call Tex_CompileLatex() endif let i = i + 1 endwhile call Tex_SetupErrorWindow() exec 'cd '.curd endfunction " }}} " ViewLaTeX: opens viewer {{{ " Description: opens the DVI viewer for the file being currently edited. " Again, if the current file is a \input in a master file, see text above " RunLaTeX() to see how to set this information. " If ViewLaTeX was called with argument "part" show file which name is stored " in g:tfile variable. If g:tfile doesnt exist, no problem. Function is called " as silent. function! ViewLaTeX() if &ft != 'tex' echo "calling ViewLaTeX from a non-tex file" return end let dir = expand("%:p:h").'/' let curd = getcwd() exec 'cd '.expand("%:p:h") " If b:fragmentFile is set, it means this file was compiled as a fragment " using Tex_PartCompile, which means that we want to ignore any " *.latexmain or makefile's. if Tex_GetMainFileName() != '' && !exists('b:fragmentFile') let mainfname = Tex_GetMainFileName() else let mainfname = expand("%:p:t:r") endif if has('win32') " unfortunately, yap does not allow the specification of an external " editor from the command line. that would have really helped ensure " that this particular vim and yap are connected. exec '!start' s:viewer mainfname . '.' . s:target elseif has('macunix') if strlen(s:viewer) let s:viewer = '-a ' . s:viewer endif execute '!open' s:viewer mainfname . '.' . s:target else " taken from Dimitri Antoniou's tip on vim.sf.net (tip #225). " slight change to actually use the current servername instead of " hardcoding it as xdvi. " Using an option for specifying the editor in the command line " because that seems to not work on older bash'es. if s:target == 'dvi' if exists('g:Tex_UseEditorSettingInDVIViewer') && \ g:Tex_UseEditorSettingInDVIViewer == 1 && \ exists('v:servername') && \ (s:viewer == "xdvi" || s:viewer == "xdvik") exec '!'.s:viewer.' -editor "gvim --servername '.v:servername.' --remote-silent +\%l \%f" '.mainfname.'.dvi &' elseif exists('g:Tex_UseEditorSettingInDVIViewer') && \ g:Tex_UseEditorSettingInDVIViewer == 1 && \ s:viewer == "kdvi" exec '!kdvi --unique '.mainfname.'.dvi &' else exec '!'.s:viewer.' '.mainfname.'.dvi &' endif redraw! else exec '!'.s:viewer.' '.mainfname.'.'.s:target.' &' redraw! endif end exec 'cd '.curd endfunction " }}} " Tex_ForwardSearchLaTeX: searches for current location in dvi file. {{{ " Description: if the DVI viewr is compatible, then take the viewer to that " position in the dvi file. see docs for RunLaTeX() to set a " master file if this is an \input'ed file. " Tip: With YAP on Windows, it is possible to do forward and inverse searches " on DVI files. to do forward search, you'll have to compile the file " with the --src-specials option. then set the following as the command " line in the 'view/options/inverse search' dialog box: " gvim --servername LATEX --remote-silent +%l "%f" " For inverse search, if you are reading this, then just pressing \ls " will work. function! Tex_ForwardSearchLaTeX() if &ft != 'tex' echo "calling ViewLaTeX from a non-tex file" return end " only know how to do forward search for yap on windows and xdvik (and " some newer versions of xdvi) on unices. if !exists('g:Tex_ViewRule_dvi') return endif let viewer = g:Tex_ViewRule_dvi let dir = expand("%:p:h").'/' let curd = getcwd() exec 'cd '.expand("%:p:h") if Tex_GetMainFileName() != '' let mainfname = Tex_GetMainFileName() else let mainfname = expand("%:p:t:r") endif " inverse search tips taken from Dimitri Antoniou's tip and Benji Fisher's " tips on vim.sf.net (vim.sf.net tip #225) if has('win32') exec '!start '.viewer.' -s '.line('.').expand('%:p:t').' '.mainfname else if exists('g:Tex_UseEditorSettingInDVIViewer') && \ g:Tex_UseEditorSettingInDVIViewer == 1 && \ exists('v:servername') && \ (viewer == "xdvi" || viewer == "xdvik") exec '!'.viewer.' -name xdvi -sourceposition '.line('.').expand('%').' -editor "gvim --servername '.v:servername.' --remote-silent +\%l \%f" '.mainfname.'.dvi &' elseif exists('g:Tex_UseEditorSettingInDVIViewer') && \ g:Tex_UseEditorSettingInDVIViewer == 1 && \ viewer == "kdvi" exec '!kdvi --unique file:'.mainfname.'.dvi\#src:'.line('.').Tex_GetMainFileName(":p:t:r").' &' else exec '!'.viewer.' -name xdvi -sourceposition '.line('.').expand('%').' '.mainfname.'.dvi &' endif redraw! end exec 'cd '.curd endfunction " }}} " Tex_PartCompile: compiles selected fragment {{{ " Description: creates a temporary file from the selected fragment of text " prepending the preamble and \end{document} and then asks RunLaTeX() to " compile it. function! Tex_PartCompile() range call Tex_Debug('getting to Tex_PartCompile', 'comp') " Save position let pos = line('.').' | normal! '.virtcol('.').'|' " Create temporary file and save its name into global variable to use in " compiler.vim let tmpfile = tempname().'.tex' " If mainfile exists open it in tiny window and extract preamble there, " otherwise do it from current file let mainfile = Tex_GetMainFileName(":p:r") if mainfile != '' exe 'bot 1 split '.mainfile exe '1,/\s*\\begin{document}/w '.tmpfile wincmd q else exe '1,/\s*\\begin{document}/w '.tmpfile endif exe a:firstline.','.a:lastline."w! >> ".tmpfile " edit the temporary file exec 'drop '.tmpfile " append the \end{document} line. $ put ='\end{document}' w " set this as a fragment file. let b:fragmentFile = 1 silent! call RunLaTeX() endfunction " }}} " ============================================================================== " Helper functions for " . viewing the log file in preview mode. " . syncing the display between the quickfix window and preview window " . going to the correct line _and column_ number from from the quick fix " window. " ============================================================================== " PositionPreviewWindow: positions the preview window correctly. {{{ " Description: " The purpose of this function is to count the number of times an error " occurs on the same line. or in other words, if the current line is " something like |10 error|, then we want to count the number of " lines in the quickfix window before this line which also contain lines " like |10 error|. " function! PositionPreviewWindow(filename) if getline('.') !~ '|\d\+ \(error\|warning\)|' if !search('|\d\+ \(error\|warning\)|') echomsg "not finding error pattern anywhere in quickfix window :".bufname(bufnr('%')) pclose! return endif endif " extract the error pattern (something like 'file.tex|10 error|') on the " current line. let errpat = matchstr(getline('.'), '^\f*|\d\+ \(error\|warning\)|\ze') let errfile = matchstr(getline('.'), '^\f*\ze|\d\+ \(error\|warning\)|') " extract the line number from the error pattern. let linenum = matchstr(getline('.'), '|\zs\d\+\ze \(error\|warning\)|') " if we are on an error, then count the number of lines before this in the " quickfix window with an error on the same line. if errpat =~ 'error|$' " our location in the quick fix window. let errline = line('.') " goto the beginning of the quickfix window and begin counting the lines " which show an error on the same line. 0 let numrep = 0 while 1 " if we are on the same kind of error line, then means we have another " line containing the same error pattern. if getline('.') =~ errpat let numrep = numrep + 1 normal! 0 endif " if we have reached the original location in the quick fix window, " then break. if line('.') == errline break else " otherwise, search for the next line which contains the same " error pattern again. goto the end of the current line so we " dont count this line again. normal! $ call search(errpat, 'W') endif endwhile else let numrep = 1 endif if getline('.') =~ '|\d\+ warning|' let searchpat = escape(matchstr(getline('.'), '|\d\+ warning|\s*\zs.*'), '\ ') else let searchpat = 'l.'.linenum endif " We first need to be in the scope of the correct file in the .log file. " This is important for example, when a.tex and b.tex both have errors on " line 9 of the file and we want to go to the error of b.tex. Merely " searching forward from the beginning of the log file for l.9 will always " land us on the error in a.tex. if errfile != '' exec 'silent! bot pedit +/(\\(\\f\\|\\[\\|\]\\|\\s\\)*'.errfile.'/ '.a:filename else exec 'bot pedit +0 '.a:filename endif " Goto the preview window " TODO: This is not robust enough. Check that a wincmd j actually takes " us to the preview window. wincmd j " now search forward from this position in the preview window for the " numrep^th error of the current line in the quickfix window. while numrep > 0 call search(searchpat, 'W') let numrep = numrep - 1 endwhile normal! z. endfunction " }}} " UpdatePreviewWindow: updates the view of the log file {{{ " Description: " This function should be called when focus is in a quickfix window. " It opens the log file in a preview window and makes it display that " part of the log file which corresponds to the error which the user is " currently on in the quickfix window. Control returns to the quickfix " window when the function returns. " function! UpdatePreviewWindow(filename) call PositionPreviewWindow(a:filename) if &previewwindow 6 wincmd _ wincmd p endif endfunction " }}} " GotoErrorLocation: goes to the correct location of error in the tex file {{{ " Description: " This function should be called when focus is in a quickfix window. This " function will first open the preview window of the log file (if it is not " already open), position the display of the preview to coincide with the " current error under the cursor and then take the user to the file in " which this error has occured. " " The position is both the correct line number and the column number. function! GotoErrorLocation(filename) " first use vim's functionality to take us to the location of the error " accurate to the line (not column). This lets us go to the correct file " without applying any logic. exec "normal! \" " If the log file is not found, then going to the correct line number is " all we can do. if glob(a:filename) == '' return endif let winnum = winnr() " then come back to the quickfix window wincmd w " find out where in the file we had the error. let linenum = matchstr(getline('.'), '|\zs\d\+\ze \(warning\|error\)|') call PositionPreviewWindow(a:filename) if getline('.') =~ 'l.\d\+' let brokenline = matchstr(getline('.'), 'l.'.linenum.' \zs.*\ze') " If the line is of the form " l.10 ...and then there was some error " it means (most probably) that only part of the erroneous line is " shown. In this case, finding the length of the broken line is not " correct. Instead goto the beginning of the line and search forward " for the part which is displayed and then go to its end. if brokenline =~ '^\M...' let partline = matchstr(brokenline, '^\M...\m\zs.*') let normcmd = "0/\\V".escape(partline, "\\")."/e+1\" else let column = strlen(brokenline) + 1 let normcmd = column.'|' endif elseif getline('.') =~ 'LaTeX Warning: \(Citation\|Reference\) `.*' let ref = matchstr(getline('.'), "LaTeX Warning: \\(Citation\\|Reference\\) `\\zs[^']\\+\\ze'") let normcmd = '0/'.ref."\" else let normcmd = '0' endif " go back to the window where we came from. exec winnum.' wincmd w' exec 'silent! '.linenum.' | normal! '.normcmd endfunction " }}} " SetCompilerMaps: sets maps for compiling/viewing/searching {{{ " Description: function! SetCompilerMaps() if exists('b:Tex_doneCompilerMaps') return endif nnoremap ll :call RunLaTeX() vnoremap ll :call Tex_PartCompile() nnoremap lv :call ViewLaTeX() nnoremap ls :call Tex_ForwardSearchLaTeX() endif endfunction " }}} augroup LatexSuite au LatexSuite User LatexSuiteFileType \ call Tex_Debug('compiler.vim: Catching LatexSuiteFileType event') | \ call SetCompilerMaps() augroup END command! -nargs=0 -range=% TPartCompile :, silent! call Tex_PartCompile() " vim:fdm=marker:ff=unix:noet:ts=4:sw=4