summaryrefslogblamecommitdiffstats
path: root/ftplugin/latex-box/latexmk.vim
blob: 2ce16d26fccaf1d7d73965f52c7595e6c11cdf42 (plain) (tree)
1
2
3
4
5
6
7
8
9
10






                                           


                                       



















































































                                                                                               
             
                                                       









                                                                      
                                                               





































                                                                                                    






                                                                                      















                                                                           


                                                     


















                                                                               



                                                                                  


































                                                                                               





                                                                                              

                                                          
























                                                                                                                                   











                                                                                          






                                                                                                        




                                                                                           







                                                                                                       




















































                                                                                                           






                                                                                      
                                                 
 















































































































































                                                                                          
" 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

" }}}

" 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)
	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_GetTexBasename(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
		let env = 'max_print_line=' . max_print_line
	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
		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 /')
	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
		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 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 . '"'),'\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_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