" LaTeX plugin for Vim " " Maintainer: Karl Yngve LervÄg " Email: karl.yngve@gmail.com " function! latex#latexmk#init(initialized) " {{{1 call latex#util#set_default('g:latex_latexmk_enabled', 1) if !g:latex_latexmk_enabled | return | endif " Set default options call latex#util#set_default('g:latex_latexmk_background', 0) call latex#util#set_default('g:latex_latexmk_build_dir', '.') call latex#util#set_default('g:latex_latexmk_callback', 1) call latex#util#set_default('g:latex_latexmk_continuous', 1) call latex#util#set_default('g:latex_latexmk_options', '-pdf') call latex#util#set_default('g:latex_quickfix_autojump', '0') call latex#util#set_default('g:latex_quickfix_mode', '2') call latex#util#set_default('g:latex_quickfix_open_on_warning', '1') call latex#util#error_deprecated('g:latex_build_dir') call latex#util#error_deprecated('g:latex_latexmk_autojump') call latex#util#error_deprecated('g:latex_latexmk_output') call latex#util#error_deprecated('g:latex_latexmk_quickfix') " Check system compatibility if s:system_incompatible() | return | endif " Set compiler (this defines the errorformat) compiler latexmk " Initialize pid for current tex file if !has_key(g:latex#data[b:latex.id], 'pid') let g:latex#data[b:latex.id].pid = 0 endif " Define commands command! -buffer VimLatexCompile call latex#latexmk#compile() command! -buffer -bang VimLatexCompileSS call latex#latexmk#compile_ss( == "!") command! -buffer VimLatexCompileToggle call latex#latexmk#toggle() command! -buffer VimLatexCompileOutput call latex#latexmk#output() command! -buffer VimLatexStop call latex#latexmk#stop() command! -buffer VimLatexStopAll call latex#latexmk#stop_all() command! -buffer -bang VimLatexErrors call latex#latexmk#errors( == "!") command! -buffer -bang VimLatexClean call latex#latexmk#clean( == "!") command! -buffer -bang VimLatexStatus call latex#latexmk#status( == "!") command! -buffer VimLatexLacheck call latex#latexmk#lacheck() " Define mappings nnoremap (vl-compile) :call latex#latexmk#compile() nnoremap (vl-compile-ss) :call latex#latexmk#compile_ss(0) nnoremap (vl-compile-toggle) :call latex#latexmk#toggle() nnoremap (vl-compile-output) :call latex#latexmk#output() nnoremap (vl-stop) :call latex#latexmk#stop() nnoremap (vl-stop-all) :call latex#latexmk#stop_all() nnoremap (vl-errors) :call latex#latexmk#errors(1) nnoremap (vl-clean) :call latex#latexmk#clean(0) nnoremap (vl-clean-full) :call latex#latexmk#clean(1) nnoremap (vl-status) :call latex#latexmk#status(0) nnoremap (vl-status-all) :call latex#latexmk#status(1) nnoremap (vl-lacheck) :call latex#latexmk#lacheck() " The remaining part is only relevant for continuous mode if !g:latex_latexmk_continuous | return | endif " Ensure that all latexmk processes are stopped when vim exits " Note: Only need to define this once, globally. if !a:initialized augroup latex_latexmk autocmd! autocmd VimLeave * call latex#latexmk#stop_all() augroup END endif " If all buffers for a given latex project are closed, kill latexmk " Note: This must come after the above so that the autocmd group is properly " refreshed if necessary augroup latex_latexmk autocmd BufUnload call s:stop_buffer() augroup END endfunction " }}}1 function! latex#latexmk#callback(status) " {{{1 call latex#latexmk#errors(0) redraw! if a:status echohl Statement echo "latexmk compile: success" else echohl Error echo "latexmk compile: fail" endif echohl None " Get window ID after first callback if s:first_callback let s:first_callback = 0 if g:latex_view_method == 'mupdf' call latex#view#mupdf_poststart() endif endif endfunction " }}}1 function! latex#latexmk#clean(full) " {{{1 let data = g:latex#data[b:latex.id] if data.pid echomsg "latexmk is already running" return endif " " Run latexmk clean process " if has('win32') let cmd = 'cd /D "' . data.root . '" & ' else let cmd = 'cd ' . shellescape(data.root) . '; ' endif let cmd .= 'latexmk -outdir=' . g:latex_latexmk_build_dir if a:full let cmd .= ' -C ' else let cmd .= ' -c ' endif let cmd .= latex#util#fnameescape(data.base) let g:latex#data[b:latex.id].cmds.clean = cmd let exe = { \ 'cmd' : cmd, \ 'bg' : 0, \ } call latex#util#execute(exe) if a:full echomsg "latexmk full clean finished" else echomsg "latexmk clean finished" endif endfunction " }}}1 function! latex#latexmk#lacheck() " {{{1 compiler lacheck silent lmake % lwindow silent redraw! wincmd p compiler latexmk endfunction " }}}1 function! latex#latexmk#toggle() " {{{1 let data = g:latex#data[b:latex.id] if data.pid call latex#latexmk#stop() else call latex#latexmk#compile() endif endfunction " }}}1 function! latex#latexmk#compile() " {{{1 let data = g:latex#data[b:latex.id] if data.pid echomsg "latexmk is already running for `" . data.base . "'" return endif " Build command line and start latexmk let exe = s:latexmk_build_cmd(data) if !g:latex_latexmk_continuous && !g:latex_latexmk_background let exe.bg = 0 let exe.silent = 0 endif call latex#util#execute(exe) if g:latex_latexmk_continuous call s:latexmk_set_pid(data) echomsg 'latexmk started in continuous mode ...' else echomsg 'latexmk compiling ...' endif endfunction " }}}1 function! latex#latexmk#compile_ss(verbose) " {{{1 let data = g:latex#data[b:latex.id] if data.pid echomsg "latexmk is already running for `" . data.base . "'" return endif let l:latex_latexmk_continuous = g:latex_latexmk_continuous let g:latex_latexmk_continuous = 0 let exe = s:latexmk_build_cmd(data) if a:verbose let exe.bg = 0 let exe.silent = 0 endif call latex#util#execute(exe) let g:latex_latexmk_continuous = l:latex_latexmk_continuous endfunction " }}}1 function! latex#latexmk#errors(force) " {{{1 cclose let log = g:latex#data[b:latex.id].log() if empty(log) if a:force echo "No log file found!" endif return endif if g:latex_quickfix_autojump execute 'cfile ' . fnameescape(log) else execute 'cgetfile ' . fnameescape(log) endif " " There are two options that determine when to open the quickfix window. If " forced, the quickfix window is always opened when there are errors or " warnings (forced typically imply that the functions is called from the " normal mode mapping). Else the behaviour is based on the settings. " let open_quickfix_window = a:force \ || (g:latex_quickfix_mode > 0 \ && (g:latex_quickfix_open_on_warning \ || s:log_contains_error(log))) if open_quickfix_window botright cwindow if g:latex_quickfix_mode == 2 wincmd p endif redraw! endif endfunction " }}}1 function! latex#latexmk#output() " {{{1 if has_key(g:latex#data[b:latex.id], 'tmp') let tmp = g:latex#data[b:latex.id].tmp else echo "vim-latex: No output exists" return endif " Create latexmk output window if bufnr(tmp) >= 0 silent exe 'bwipeout' . bufnr(tmp) endif silent exe 'split ' . tmp " Better automatic update augroup tmp_update autocmd! autocmd BufEnter * silent! checktime autocmd CursorHold * silent! checktime autocmd CursorHoldI * silent! checktime autocmd CursorMoved * silent! checktime autocmd CursorMovedI * silent! checktime augroup END silent exe 'autocmd! BufDelete ' . tmp . ' augroup! tmp_update' " Set some mappings nnoremap q :bwipeout " Set some buffer options setlocal autoread setlocal nomodifiable endfunction " }}}1 function! latex#latexmk#status(detailed) " {{{1 if a:detailed let running = 0 for data in g:latex#data if data.pid if !running echo "latexmk is running" let running = 1 endif let name = data.tex if len(name) >= winwidth('.') - 20 let name = "..." . name[-winwidth('.')+23:] endif echom printf('pid: %6s, file: %-s', data.pid, name) endif endfor if !running echo "latexmk is not running" endif else if g:latex#data[b:latex.id].pid echo "latexmk is running" else echo "latexmk is not running" endif endif endfunction " }}}1 function! latex#latexmk#stop() " {{{1 let pid = g:latex#data[b:latex.id].pid let base = g:latex#data[b:latex.id].base if pid call s:latexmk_kill_pid(pid) let g:latex#data[b:latex.id].pid = 0 echo "latexmk stopped for `" . base . "'" else echo "latexmk is not running for `" . base . "'" endif endfunction " }}}1 function! latex#latexmk#stop_all() " {{{1 for data in g:latex#data if data.pid call s:latexmk_kill_pid(data.pid) let data.pid = 0 endif endfor endfunction " }}}1 " Helper functions for latexmk command function! s:latexmk_build_cmd(data) " {{{1 let exe = {} let exe.null = 0 " Note: We don't send output to /dev/null, but rather to a temporary file, " which allows inspection of latexmk output let tmp = tempname() if has('win32') let cmd = 'cd /D "' . a:data.root . '"' let cmd .= ' && set max_print_line=2000 & latexmk' else let cmd = 'cd ' . shellescape(a:data.root) let cmd .= ' && max_print_line=2000 latexmk' endif let cmd .= ' ' . g:latex_latexmk_options let cmd .= ' -e ' . shellescape('$pdflatex =~ s/ / -file-line-error /') let cmd .= ' -outdir=' . g:latex_latexmk_build_dir if g:latex_latexmk_continuous let cmd .= ' -pvc' endif if g:latex_latexmk_callback && has('clientserver') let success = v:progname let success .= ' --servername ' . v:servername let success .= ' --remote-expr \"latex\#latexmk\#callback(1)\"' let failed = v:progname let failed .= ' --servername ' . v:servername let failed .= ' --remote-expr \"latex\#latexmk\#callback(0)\"' if has('win32') let cmd .= ' -e "$success_cmd .= ''' . success . '''"' let cmd .= ' -e "$failure_cmd .= ''' . failed . '''"' else let cmd .= ' -e ''$success_cmd .= "' . success . '"''' let cmd .= ' -e ''$failure_cmd .= "' . failed . '"''' endif let s:first_callback = 1 endif let cmd .= ' ' . latex#util#fnameescape(a:data.base) if g:latex_latexmk_continuous || g:latex_latexmk_background if has('win32') let cmd .= ' >' . tmp let cmd = 'cmd /s /c "' . cmd . '"' else let cmd .= ' &>' . tmp endif endif let exe.cmd = cmd let a:data.cmds.compile = cmd let a:data.tmp = tmp return exe endfunction " }}}1 function! s:latexmk_set_pid(data) " {{{1 if has('win32') let pidcmd = 'qprocess latexmk.exe' let pidinfo = systemlist(pidcmd)[-1] let a:data.pid = split(pidinfo,'\s\+')[-2] else let a:data.pid = system('pgrep -nf "^perl.*latexmk"')[:-2] endif endfunction function! s:latexmk_kill_pid(pid) " {{{1 let exe = {} let exe.bg = 0 let exe.null = 0 if has('win32') let exe.cmd = 'taskkill /PID ' . a:pid . ' /T /F' else let exe.cmd = 'kill ' . a:pid endif call latex#util#execute(exe) endfunction " }}}1 function! s:log_contains_error(logfile) " {{{1 let lines = readfile(a:logfile) let lines = filter(lines, 'v:val =~ ''^.*:\d\+: ''') let lines = uniq(map(lines, 'matchstr(v:val, ''^.*\ze:\d\+:'')')) let lines = map(lines, 'fnameescape(fnamemodify(v:val, '':p''))') let lines = filter(lines, 'filereadable(v:val)') return len(lines) > 0 endfunction function! s:stop_buffer() " {{{1 " " Only run if latex variables are set " if !exists('b:latex') | return | endif let id = b:latex.id let pid = g:latex#data[id].pid " " Only stop if latexmk is running " if pid " " Count the number of buffers that point to current latex blob " let n = 0 for b in filter(range(1, bufnr("$")), 'buflisted(v:val)') if id == getbufvar(b, 'latex', {'id' : -1}).id let n += 1 endif endfor " " Only stop if current buffer is the last for current latex blob " if n == 1 silent call latex#latexmk#stop() endif endif endfunction function! s:system_incompatible() " {{{1 if has('win32') let required = ['latexmk'] else let required = ['latexmk', 'pgrep'] endif " " Check for required executables " for cmd in required if !executable(cmd) echom "Warning: Could not initialize latex#latexmk" echom " Missing executable: " . cmd return 1 endif endfor endfunction " }}}1 " vim: fdm=marker sw=2