summaryrefslogtreecommitdiffstats
path: root/ftplugin/latex-box/latexmk.vim
diff options
context:
space:
mode:
Diffstat (limited to 'ftplugin/latex-box/latexmk.vim')
-rw-r--r--ftplugin/latex-box/latexmk.vim442
1 files changed, 442 insertions, 0 deletions
diff --git a/ftplugin/latex-box/latexmk.vim b/ftplugin/latex-box/latexmk.vim
new file mode 100644
index 00000000..4ea3ff09
--- /dev/null
+++ b/ftplugin/latex-box/latexmk.vim
@@ -0,0 +1,442 @@
+" LaTeX Box latexmk functions
+
+" Options and variables {{{
+
+if !exists('g:LatexBox_latexmk_options')
+ let g:LatexBox_latexmk_options = ''
+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
+
+" }}}
+
+" Process ID management (used for asynchronous and continuous mode) {{{
+
+" A dictionary of latexmk PID's (basename: pid)
+if !exists('g:latexmk_running_pids')
+ let g:latexmk_running_pids = {}
+endif
+
+" Set PID {{{
+function! s:LatexmkSetPID(basename, pid)
+ let g:latexmk_running_pids[a:basename] = a:pid
+endfunction
+" }}}
+
+" kill_latexmk_process {{{
+function! s:kill_latexmk_process(pid)
+ if has('win32')
+ silent execute '!taskkill /PID ' . a:pid . ' /T /F'
+ else
+ if g:LatexBox_latexmk_async
+ " vim-server mode
+ let pids = []
+ let tmpfile = tempname()
+ silent execute '!ps x -o pgid,pid > ' . tmpfile
+ for line in readfile(tmpfile)
+ let new_pid = matchstr(line, '^\s*' . a:pid . '\s\+\zs\d\+\ze')
+ if !empty(new_pid)
+ call add(pids, new_pid)
+ endif
+ endfor
+ call delete(tmpfile)
+ if !empty(pids)
+ silent execute '!kill ' . join(pids)
+ endif
+ else
+ " single background process
+ silent execute '!kill ' . a:pid
+ endif
+ endif
+ if !has('gui_running')
+ redraw!
+ endif
+endfunction
+" }}}
+
+" kill_all_latexmk_processes {{{
+function! s:kill_all_latexmk_processes()
+ for pid in values(g:latexmk_running_pids)
+ call s:kill_latexmk_process(pid)
+ endfor
+endfunction
+" }}}
+
+" }}}
+
+" Setup for vim-server {{{
+function! s:SIDWrap(func)
+ if !exists('s:SID')
+ let s:SID = matchstr(expand('<sfile>'), '\zs<SNR>\d\+_\ze.*$')
+ endif
+ return s:SID . a:func
+endfunction
+
+function! s:LatexmkCallback(basename, status)
+ " Only remove the pid if not in continuous mode
+ if !g:LatexBox_latexmk_preview_continuously
+ call remove(g:latexmk_running_pids, a:basename)
+ call LatexBox_LatexErrors(a:status, a:basename)
+ endif
+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_GetTexBasename(1)
+ let basename = fnamemodify(basepath, ':t')
+ let texroot = shellescape(LatexBox_GetTexRoot())
+ let mainfile = fnameescape(fnamemodify(LatexBox_GetMainTexFile(), ':t'))
+
+ " 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
+ let env = 'max_print_line=' . max_print_line
+ endif
+
+ " Set latexmk command with options
+ if has('win32')
+ " Make sure to switch drive as well as directory
+ let cmd = 'cd /D ' . texroot . ' && '
+ else
+ let cmd = 'cd ' . texroot . ' && '
+ endif
+ let cmd .= env . ' latexmk'
+ let cmd .= ' -' . g:LatexBox_output_type
+ 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 /')
+ let cmd .= ' ' . mainfile
+
+ " Redirect output to null
+ if has('win32')
+ let cmd .= ' >nul'
+ else
+ let cmd .= ' &>/dev/null'
+ 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 asyncbat = tempname() . '.bat'
+ 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)
+
+ " 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
+
+ " 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'"
+ let cmd = vimsetpid . ' ; ' . escape(cmd, '%') . ' ; ' . vimcmd
+ 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 . '"'),'\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)
+ let basename = LatexBox_GetTexBasename(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
+ " Write status message to screen
+ redraw
+ if a:status > 0 || len(getqflist())>1
+ echomsg 'Compiling to ' . g:LatexBox_output_type . ' ... failed!'
+ else
+ echomsg 'Compiling to ' . g:LatexBox_output_type . ' ... success!'
+ endif
+
+ " Only open window when an error/warning is detected
+ if g:LatexBox_quickfix
+ belowright cw
+ if g:LatexBox_quickfix==2
+ wincmd p
+ endif
+ endif
+ endif
+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_GetTexBasename(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_GetTexBasename(1)
+ let basename = fnamemodify(basepath, ':t')
+ echoerr "latexmk is not running for `" . basename . "'"
+ endif
+ else
+ let basepath = LatexBox_GetTexBasename(1)
+ let basename = fnamemodify(basepath, ':t')
+ if has_key(g:latexmk_running_pids, basepath)
+ call s:kill_latexmk_process(g:latexmk_running_pids[basepath])
+ call remove(g:latexmk_running_pids, basepath)
+ if !a:silent
+ echomsg "latexmk stopped for `" . basename . "'"
+ endif
+ elseif !a:silent
+ echoerr "latexmk is not running for `" . basename . "'"
+ endif
+ endif
+endfunction
+" }}}
+
+" Commands {{{
+
+command! -bang Latexmk call LatexBox_Latexmk(<q-bang> == "!")
+command! -bang LatexmkClean call LatexBox_LatexmkClean(<q-bang> == "!")
+command! -bang LatexmkStatus call LatexBox_LatexmkStatus(<q-bang> == "!")
+command! LatexmkStop call LatexBox_LatexmkStop(0)
+command! LatexErrors call LatexBox_LatexErrors(-1)
+
+if g:LatexBox_latexmk_async || g:LatexBox_latexmk_preview_continuously
+ autocmd BufUnload <buffer> call LatexBox_LatexmkStop(1)
+ autocmd VimLeave * call <SID>kill_all_latexmk_processes()
+endif
+
+" }}}
+
+" vim:fdm=marker:ff=unix:noet:ts=4:sw=4