diff --git a/autoload/vimtex/view.vim b/autoload/vimtex/view.vim index 4496ec8..8c5ec15 100644 --- a/autoload/vimtex/view.vim +++ b/autoload/vimtex/view.vim @@ -4,15 +4,6 @@ " Email: karl.yngve@gmail.com " -let s:viewers = [ - \ 'general', - \ 'mupdf', - \ 'zathura', - \ ] -for viewer in s:viewers - execute 'let s:' . viewer . ' = {}' -endfor - function! vimtex#view#init_options() " {{{1 call vimtex#util#set_default('g:vimtex_view_enabled', 1) if !g:vimtex_view_enabled | return | endif @@ -21,37 +12,32 @@ function! vimtex#view#init_options() " {{{1 call vimtex#util#set_default('g:vimtex_view_use_temp_files', 0) endfunction -" }}}1 -function! vimtex#view#init_script() " {{{1 -endfunction - " }}}1 function! vimtex#view#init_buffer() " {{{1 if !g:vimtex_view_enabled | return | endif " - " Add viewer to the data blob + " Add viewer to the data blob (if it does not already exist) " if !has_key(b:vimtex, 'viewer') - let viewer = 's:' . g:vimtex_view_method - if !exists(viewer) + try + let b:vimtex.viewer = vimtex#view#{g:vimtex_view_method}#new() + catch /E117/ call vimtex#echo#warning('viewer ' \ . g:vimtex_view_method . ' does not exist!') call vimtex#echo#echo('- Please see :h g:vimtex_view_method') call vimtex#echo#wait() - let b:vimtex.viewer = {} return - endif + endtry - execute 'let b:vimtex.viewer = ' . viewer - call b:vimtex.viewer.init() + " Make the following code more concise + let l:v = b:vimtex.viewer " " Add latexmk callback to callback hooks (if it exists) " - if exists('*b:vimtex.viewer.latexmk_callback') - call add(g:vimtex_latexmk_callback_hooks, - \ 'b:vimtex.viewer.latexmk_callback') + if exists('*l:v.latexmk_callback') + call add(g:vimtex_latexmk_callback_hooks, 'l:v.latexmk_callback') endif " @@ -63,7 +49,7 @@ function! vimtex#view#init_buffer() " {{{1 if exists(hook) execute 'let hookfunc = ''*'' . ' . hook if exists(hookfunc) - execute 'let b:vimtex.viewer.hook_' . point . ' = function(' . hook . ')' + execute 'let l:v.hook_' . point . ' = function(' . hook . ')' endif endif endfor @@ -92,528 +78,4 @@ endfunction " }}}1 -" -" Define viewers -" -" {{{1 General -function! s:general.init() dict " {{{2 - " - " Set default options - " - - call vimtex#util#set_default_os_specific('g:vimtex_view_general_viewer', - \ { - \ 'linux' : 'xdg-open', - \ 'mac' : 'open', - \ }) - call vimtex#util#set_default('g:vimtex_view_general_options', '@pdf') - call vimtex#util#set_default('g:vimtex_view_general_options_latexmk', '') - - if exists('g:vimtex_view_general_callback') - let self.latexmk_callback = function(g:vimtex_view_general_callback) - endif - - if !executable(g:vimtex_view_general_viewer) - call vimtex#echo#warning('viewer "' - \ . g:vimtex_view_general_viewer . '" is not executable!') - call vimtex#echo#echo('- Please see :h g:vimtex_view_general_viewer') - call vimtex#echo#wait() - endif - - if g:vimtex_view_use_temp_files - let self.out = b:vimtex.root . '/' . b:vimtex.name . '_vimtex.pdf' - let self.synctex = fnamemodify(self.out, ':r') . '.synctex.gz' - let self.copy_files = function('s:copy_files') - else - let self.out = b:vimtex.out() - let self.synctex = b:vimtex.ext('synctex.gz') - endif -endfunction - -" }}}2 -function! s:general.view(file) dict " {{{2 - if empty(a:file) - let outfile = self.out - - " Only copy files if they don't exist - if g:vimtex_view_use_temp_files - \ && s:output_not_readable(outfile) - call self.copy_files() - endif - else - let outfile = a:file - endif - if s:output_not_readable(outfile) | return | endif - - " Parse options - let opts = g:vimtex_view_general_options - let opts = substitute(opts, '@line', line('.'), 'g') - let opts = substitute(opts, '@col', col('.'), 'g') - let opts = substitute(opts, '@tex', - \ vimtex#util#shellescape(expand('%:p')), 'g') - let opts = substitute(opts, '@pdf', vimtex#util#shellescape(outfile), 'g') - - " Construct the command - let exe = {} - let exe.cmd = g:vimtex_view_general_viewer . ' ' . opts - call vimtex#util#execute(exe) - let self.cmd_view = exe.cmd - - if has_key(self, 'hook_view') - call self.hook_view() - endif -endfunction - -" }}}2 -function! s:general.latexmk_append_argument() dict " {{{2 - if g:vimtex_view_use_temp_files - return ' -view=none' - else - let l:option = g:vimtex_view_general_viewer - if !empty(g:vimtex_view_general_options_latexmk) - let l:option .= ' ' - let l:option .= substitute(g:vimtex_view_general_options_latexmk, - \ '@line', line('.'), 'g') - endif - return vimtex#latexmk#add_option('pdf_previewer', l:option) - endif -endfunction - -" }}}2 - -" }}}1 -" {{{1 MuPDF -function! s:mupdf.init() dict " {{{2 - " Only initialize once - if has_key(self, 'xwin_id') | return | endif - - " - " Default MuPDF settings - " - call vimtex#util#set_default('g:vimtex_view_mupdf_options', '') - call vimtex#util#set_default('g:vimtex_view_mupdf_send_keys', '') - - if !executable('mupdf') - call vimtex#echo#warning('MuPDF is not executable!') - call vimtex#echo#echo('- vimtex viewer will not work!') - call vimtex#echo#wait() - endif - - if !executable('xdotool') - call vimtex#echo#warning('viewer MuPDF requires xdotool!') - endif - - if g:vimtex_view_use_temp_files - let self.out = b:vimtex.root . '/' . b:vimtex.name . '_vimtex.pdf' - let self.synctex = fnamemodify(self.out, ':r') . '.synctex.gz' - let self.copy_files = function('s:copy_files') - else - let self.out = b:vimtex.out() - let self.synctex = b:vimtex.ext('synctex.gz') - endif - - let self.class = 'MuPDF' - let self.xwin_id = 0 - let self.view = function('s:view') - let self.xwin_exists = function('s:xwin_exists') - let self.xwin_get_id = function('s:xwin_get_id') - let self.xwin_send_keys = function('s:xwin_send_keys') - let self.focus_vim = function('s:focus_vim') - let self.focus_viewer = function('s:focus_viewer') - call self.xwin_exists() -endfunction - -" }}}2 -function! s:mupdf.start(outfile) dict " {{{2 - let exe = {} - let exe.cmd = 'mupdf ' . g:vimtex_view_mupdf_options - let exe.cmd .= ' ' . vimtex#util#shellescape(a:outfile) - call vimtex#util#execute(exe) - let self.cmd_start = exe.cmd - - call self.xwin_get_id() - call self.xwin_send_keys(g:vimtex_view_mupdf_send_keys) - call self.forward_search(a:outfile) -endfunction - -" }}}2 -function! s:mupdf.forward_search(outfile) dict " {{{2 - if !executable('xdotool') | return | endif - if !executable('synctex') | return | endif - - let self.cmd_synctex_view = 'synctex view -i ' - \ . (line('.') + 1) . ':' - \ . (col('.') + 1) . ':' - \ . vimtex#util#shellescape(expand('%:p')) - \ . ' -o ' . vimtex#util#shellescape(a:outfile) - \ . " | grep -m1 'Page:' | sed 's/Page://' | tr -d '\n'" - let self.page = system(self.cmd_synctex_view) - - if self.page > 0 - let exe = {} - let exe.cmd = 'xdotool' - let exe.cmd .= ' type --window ' . self.xwin_id - let exe.cmd .= ' "' . self.page . 'g"' - call vimtex#util#execute(exe) - let self.cmd_forward_search = exe.cmd - endif - - call self.focus_viewer() -endfunction - -" }}}2 -function! s:mupdf.reverse_search() dict " {{{2 - if !executable('xdotool') | return | endif - if !executable('synctex') | return | endif - - let outfile = b:vimtex.out() - if s:output_not_readable(outfile) | return | endif - - if !self.xwin_exists() - call vimtex#echo#warning('reverse search failed (is MuPDF open?)') - return - endif - - " Get page number - let self.cmd_getpage = 'xdotool getwindowname ' . self.xwin_id - let self.cmd_getpage .= " | sed 's:.* - \\([0-9]*\\)/.*:\\1:'" - let self.cmd_getpage .= " | tr -d '\n'" - let self.page = system(self.cmd_getpage) - if self.page <= 0 | return | endif - - " Get file - let self.cmd_getfile = 'synctex edit ' - let self.cmd_getfile .= "-o \"" . self.page . ':288:108:' . outfile . "\"" - let self.cmd_getfile .= "| grep 'Input:' | sed 's/Input://' " - let self.cmd_getfile .= "| head -n1 | tr -d '\n' 2>/dev/null" - let self.file = system(self.cmd_getfile) - - " Get line - let self.cmd_getline = 'synctex edit ' - let self.cmd_getline .= "-o \"" . self.page . ':288:108:' . outfile . "\"" - let self.cmd_getline .= "| grep -m1 'Line:' | sed 's/Line://' " - let self.cmd_getline .= "| head -n1 | tr -d '\n'" - let self.line = system(self.cmd_getline) - - " Go to file and line - silent exec 'edit ' . fnameescape(self.file) - if self.line > 0 - silent exec ':' . self.line - " Unfold, move to top line to correspond to top pdf line, and go to end of - " line in case the corresponding pdf line begins on previous pdf page. - normal! zvztg_ - endif -endfunction - -" }}}2 -function! s:mupdf.latexmk_callback(status) dict " {{{2 - if !a:status | return | endif - - if g:vimtex_view_use_temp_files - call self.copy_files() - else - " - " Search for existing window created by latexmk - " It may be necessary to wait some time before it is opened and - " recognized. Sometimes it is very quick, other times it may take - " a second. This way, we don't block longer than necessary. - " - if !has_key(self, 'started_through_callback') - for l:dummy in range(30) - sleep 50m - if self.xwin_exists() | break | endif - endfor - endif - endif - - if !self.xwin_exists() && !has_key(self, 'started_through_callback') - call self.start(self.out) - let self.started_through_callback = 1 - endif - - if g:vimtex_view_use_temp_files - call self.xwin_send_keys('r') - endif - - call self.xwin_send_keys(g:vimtex_view_mupdf_send_keys) - if has_key(self, 'hook_callback') - call self.hook_callback() - endif -endfunction - -" }}}2 -function! s:mupdf.latexmk_append_argument() dict " {{{2 - if g:vimtex_view_use_temp_files - let cmd = ' -view=none' - else - let cmd = vimtex#latexmk#add_option('new_viewer_always', '0') - let cmd .= vimtex#latexmk#add_option('pdf_update_method', '2') - let cmd .= vimtex#latexmk#add_option('pdf_update_signal', 'SIGHUP') - let cmd .= vimtex#latexmk#add_option('pdf_previewer', - \ 'mupdf ' . g:vimtex_view_mupdf_options) - endif - return cmd -endfunction - -" }}}2 - -" }}}1 -" {{{1 Zathura -function! s:zathura.init() dict " {{{2 - " Only initialize once - if has_key(self, 'xwin_id') | return | endif - - " - " Default Zathura settings - " - call vimtex#util#set_default('g:vimtex_view_zathura_options', '') - - if !executable('zathura') - call vimtex#echo#warning('Zathura is not executable!') - call vimtex#echo#echo('- vimtex viewer will not work!') - call vimtex#echo#wait() - endif - - if !executable('xdotool') - call vimtex#echo#warning('viewer Zathura requires xdotool!') - endif - - if g:vimtex_view_use_temp_files - let self.out = b:vimtex.root . '/' . b:vimtex.name . '_vimtex.pdf' - let self.synctex = fnamemodify(self.out, ':r') . '.synctex.gz' - let self.copy_files = function('s:copy_files') - else - let self.out = b:vimtex.out() - let self.synctex = b:vimtex.ext('synctex.gz') - endif - - let self.class = 'Zathura' - let self.xwin_id = 0 - let self.view = function('s:view') - let self.xwin_get_id = function('s:xwin_get_id') - let self.xwin_exists = function('s:xwin_exists') - call self.xwin_exists() -endfunction - -" }}}2 -function! s:zathura.start(outfile) dict " {{{2 - let exe = {} - let exe.cmd = 'zathura' - let exe.cmd .= ' -x "' . g:vimtex_latexmk_progname - \ . ' --servername ' . v:servername - \ . ' --remote +\%{line} \%{input}"' - let exe.cmd .= ' ' . g:vimtex_view_zathura_options - let exe.cmd .= ' ' . vimtex#util#shellescape(a:outfile) - call vimtex#util#execute(exe) - let self.cmd_start = exe.cmd - - call self.xwin_get_id() - call self.forward_search(a:outfile) -endfunction - -" }}}2 -function! s:zathura.forward_search(outfile) dict " {{{2 - if !filereadable(self.synctex) | return | endif - - let exe = {} - let exe.cmd = 'zathura --synctex-forward ' - let exe.cmd .= line('.') - let exe.cmd .= ':' . col('.') - let exe.cmd .= ':' . vimtex#util#shellescape(expand('%:p')) - let exe.cmd .= ' ' . vimtex#util#shellescape(a:outfile) - call vimtex#util#execute(exe) - let self.cmd_forward_search = exe.cmd -endfunction - -" }}}2 -function! s:zathura.latexmk_callback(status) dict " {{{2 - if !a:status | return | endif - - if g:vimtex_view_use_temp_files - call self.copy_files() - else - " - " Search for existing window created by latexmk - " It may be necessary to wait some time before it is opened and - " recognized. Sometimes it is very quick, other times it may take - " a second. This way, we don't block longer than necessary. - " - if !has_key(self, 'started_through_callback') - for l:dummy in range(30) - sleep 50m - if self.xwin_exists() | break | endif - endfor - endif - endif - - if !self.xwin_exists() && !has_key(self, 'started_through_callback') - call self.start(self.out) - let self.started_through_callback = 1 - endif - - if has_key(self, 'hook_callback') - call self.hook_callback() - endif -endfunction - -" }}}2 -function! s:zathura.latexmk_append_argument() dict " {{{2 - if g:vimtex_view_use_temp_files - let cmd = ' -view=none' - else - let cmd = vimtex#latexmk#add_option('new_viewer_always', '0') - let cmd .= vimtex#latexmk#add_option('pdf_previewer', - \ 'zathura ' . g:vimtex_view_zathura_options - \ . ' -x \"' . g:vimtex_latexmk_progname - \ . ' --servername ' . v:servername - \ . ' --remote +\%{line} \%{input}\" \%S') - endif - - return cmd -endfunction - -" }}}2 - -" }}}1 - -" -" Common functionality -" -function! s:view(file) dict " {{{1 - if empty(a:file) - let outfile = self.out - else - let outfile = a:file - endif - if s:output_not_readable(outfile) | return | endif - - if self.xwin_exists() - call self.forward_search(outfile) - else - if g:vimtex_view_use_temp_files - call self.copy_files() - endif - call self.start(outfile) - endif - - if has_key(self, 'hook_view') - call self.hook_view() - endif -endfunction - -" }}}1 -function! s:output_not_readable(output) " {{{1 - if !filereadable(a:output) - call vimtex#echo#warning('viewer can not read PDF file!') - return 1 - else - return 0 - endif -endfunction - -" }}}1 -function! s:xwin_get_id() dict " {{{1 - if !executable('xdotool') | return 0 | endif - if self.xwin_id > 0 | return self.xwin_id | endif - - " Allow some time for the viewer to start properly - sleep 500m - - " - " Get the window ID - " - let cmd = 'xdotool search --class ' . self.class - let xwin_ids = split(system(cmd), '\n') - if len(xwin_ids) == 0 - call vimtex#echo#warning( - \ 'viewer can not find ' . self.class . ' window ID!') - let self.xwin_id = 0 - else - let self.xwin_id = xwin_ids[-1] - endif - - return self.xwin_id -endfunction - -" }}}1 -function! s:xwin_exists() dict " {{{1 - if !executable('xdotool') | return 0 | endif - - " - " If xwin_id is already set, check if it still exists - " - if self.xwin_id > 0 - let cmd = 'xdotool search --class ' . self.class - if index(split(system(cmd), '\n'), self.xwin_id) < 0 - let self.xwin_id = 0 - endif - endif - - " - " If xwin_id is unset, check if matching viewer windows exist - " - if self.xwin_id == 0 - let cmd = 'xdotool search --name ' . fnamemodify(self.out, ':t') - let result = split(system(cmd), '\n') - if len(result) > 0 - let self.xwin_id = result[-1] - endif - endif - - return (self.xwin_id > 0) -endfunction - -" }}}1 -function! s:xwin_send_keys(keys) dict " {{{1 - if !executable('xdotool') | return | endif - - if a:keys !=# '' - let cmd = 'xdotool key --window ' . self.xwin_id - let cmd .= ' ' . a:keys - silent call system(cmd) - endif -endfunction - -" }}}1 -function! s:copy_files() dict " {{{1 - " - " Copy pdf file - " - if getftime(b:vimtex.out()) > getftime(self.out) - call writefile(readfile(b:vimtex.out(), 'b'), self.out, 'b') - endif - - " - " Copy synctex file - " - let l:old = b:vimtex.ext('synctex.gz') - if getftime(l:old) > getftime(self.synctex) - call rename(l:old, self.synctex) - endif -endfunction - -" }}}1 - -" -" Hook functions (used as default hooks in some cases) -" -function! s:focus_viewer() dict " {{{1 - if !executable('xdotool') | return | endif - - if self.xwin_id > 0 - silent call system('xdotool windowfocus ' . self.xwin_id . ' --sync') - silent call system('xdotool windowraise ' . self.xwin_id) - endif -endfunction - -" }}}1 -function! s:focus_vim() dict " {{{1 - if !executable('xdotool') | return | endif - - silent call system('xdotool windowfocus ' . v:windowid . ' --sync') - silent call system('xdotool windowraise ' . v:windowid) -endfunction - -" }}}1 - " vim: fdm=marker sw=2 diff --git a/autoload/vimtex/view/common.vim b/autoload/vimtex/view/common.vim new file mode 100644 index 0000000..7cee10d --- /dev/null +++ b/autoload/vimtex/view/common.vim @@ -0,0 +1,142 @@ +" vimtex - LaTeX plugin for Vim +" +" Maintainer: Karl Yngve Lervåg +" Email: karl.yngve@gmail.com +" + +function! vimtex#view#common#apply_xwin_template(class, viewer) " {{{1 + let a:viewer.class = a:class + let a:viewer.xwin_id = 0 + call extend(a:viewer, deepcopy(s:xwin_template)) + call a:viewer.xwin_exists() + return a:viewer +endfunction + +" }}}1 +function! vimtex#view#common#use_temp_files_p(viewer) " {{{1 + if g:vimtex_view_use_temp_files + let a:viewer.out = b:vimtex.root . '/' . b:vimtex.name . '_vimtex.pdf' + let a:viewer.synctex = fnamemodify(a:viewer.out, ':r') . '.synctex.gz' + let a:viewer.copy_files = function('s:copy_files') + else + let a:viewer.out = b:vimtex.out() + let a:viewer.synctex = b:vimtex.ext('synctex.gz') + endif + + return a:viewer +endfunction + +" }}}1 + +let s:xwin_template = {} + +function! s:xwin_template.view(file) dict " {{{1 + if empty(a:file) + let outfile = self.out + else + let outfile = a:file + endif + if !filereadable(outfile) + call vimtex#echo#warning('viewer can not read PDF file!') + return + endif + + if self.xwin_exists() + call self.forward_search(outfile) + else + if g:vimtex_view_use_temp_files + call self.copy_files() + endif + call self.start(outfile) + endif + + if has_key(self, 'hook_view') + call self.hook_view() + endif +endfunction + +" }}}1 +function! s:xwin_template.xwin_get_id() dict " {{{1 + if !executable('xdotool') | return 0 | endif + if self.xwin_id > 0 | return self.xwin_id | endif + + " Allow some time for the viewer to start properly + sleep 500m + + " + " Get the window ID + " + let cmd = 'xdotool search --class ' . self.class + let xwin_ids = split(system(cmd), '\n') + if len(xwin_ids) == 0 + call vimtex#echo#warning( + \ 'viewer can not find ' . self.class . ' window ID!') + let self.xwin_id = 0 + else + let self.xwin_id = xwin_ids[-1] + endif + + return self.xwin_id +endfunction + +" }}}1 +function! s:xwin_template.xwin_exists() dict " {{{1 + if !executable('xdotool') | return 0 | endif + + " + " If xwin_id is already set, check if it still exists + " + if self.xwin_id > 0 + let cmd = 'xdotool search --class ' . self.class + if index(split(system(cmd), '\n'), self.xwin_id) < 0 + let self.xwin_id = 0 + endif + endif + + " + " If xwin_id is unset, check if matching viewer windows exist + " + if self.xwin_id == 0 + let cmd = 'xdotool search --name ' . fnamemodify(self.out, ':t') + let result = split(system(cmd), '\n') + if len(result) > 0 + let self.xwin_id = result[-1] + endif + endif + + return (self.xwin_id > 0) +endfunction + +" }}}1 +function! s:xwin_template.xwin_send_keys(keys) dict " {{{1 + if !executable('xdotool') | return | endif + + if a:keys !=# '' + let cmd = 'xdotool key --window ' . self.xwin_id + let cmd .= ' ' . a:keys + silent call system(cmd) + endif +endfunction + +" }}}1 + +function! s:copy_files() dict " {{{1 + " + " Copy pdf file + " + if getftime(b:vimtex.out()) > getftime(self.out) + call writefile(readfile(b:vimtex.out(), 'b'), self.out, 'b') + endif + + " + " Copy synctex file + " + let l:old = b:vimtex.ext('synctex.gz') + if getftime(l:old) > getftime(self.synctex) + call rename(l:old, self.synctex) + endif +endfunction + +" }}}1 + +" vim: fdm=marker sw=2 diff --git a/autoload/vimtex/view/general.vim b/autoload/vimtex/view/general.vim new file mode 100644 index 0000000..be633b0 --- /dev/null +++ b/autoload/vimtex/view/general.vim @@ -0,0 +1,110 @@ +" vimtex - LaTeX plugin for Vim +" +" Maintainer: Karl Yngve Lervåg +" Email: karl.yngve@gmail.com +" + +function! vimtex#view#general#new() " {{{1 + " + " Set default options + " + call vimtex#util#set_default_os_specific('g:vimtex_view_general_viewer', + \ { + \ 'linux' : 'xdg-open', + \ 'mac' : 'open', + \ }) + call vimtex#util#set_default('g:vimtex_view_general_options', '@pdf') + call vimtex#util#set_default('g:vimtex_view_general_options_latexmk', '') + + " + " Check if the viewer is executable + " + if !executable(g:vimtex_view_general_viewer) + call vimtex#echo#warning('viewer "' + \ . g:vimtex_view_general_viewer . '" is not executable!') + call vimtex#echo#echo('- Please see :h g:vimtex_view_general_viewer') + call vimtex#echo#wait() + return {} + endif + + " + " Start from standard template + " + let l:viewer = vimtex#view#common#use_temp_files_p(deepcopy(s:general)) + + " + " Add callback hook + " + if exists('g:vimtex_view_general_callback') + let l:viewer.latexmk_callback = function(g:vimtex_view_general_callback) + endif + + return l:viewer +endfunction + +" }}}1 + +let s:general = {} + +function! s:general.view(file) dict " {{{1 + if empty(a:file) + let outfile = self.out + + " Only copy files if they don't exist + if g:vimtex_view_use_temp_files + \ && s:output_not_readable(outfile) + call self.copy_files() + endif + else + let outfile = a:file + endif + if s:output_not_readable(outfile) | return | endif + + " Parse options + let opts = g:vimtex_view_general_options + let opts = substitute(opts, '@line', line('.'), 'g') + let opts = substitute(opts, '@col', col('.'), 'g') + let opts = substitute(opts, '@tex', + \ vimtex#util#shellescape(expand('%:p')), 'g') + let opts = substitute(opts, '@pdf', vimtex#util#shellescape(outfile), 'g') + + " Construct the command + let exe = {} + let exe.cmd = g:vimtex_view_general_viewer . ' ' . opts + call vimtex#util#execute(exe) + let self.cmd_view = exe.cmd + + if has_key(self, 'hook_view') + call self.hook_view() + endif +endfunction + +" }}}1 +function! s:general.latexmk_append_argument() dict " {{{1 + if g:vimtex_view_use_temp_files + return ' -view=none' + else + let l:option = g:vimtex_view_general_viewer + if !empty(g:vimtex_view_general_options_latexmk) + let l:option .= ' ' + let l:option .= substitute(g:vimtex_view_general_options_latexmk, + \ '@line', line('.'), 'g') + endif + return vimtex#latexmk#add_option('pdf_previewer', l:option) + endif +endfunction + +" }}}1 + +function! s:output_not_readable(output) " {{{1 + if !filereadable(a:output) + call vimtex#echo#warning('viewer can not read PDF file!') + return 1 + else + return 0 + endif +endfunction + +" }}}1 + +" vim: fdm=marker sw=2 diff --git a/autoload/vimtex/view/mupdf.vim b/autoload/vimtex/view/mupdf.vim new file mode 100644 index 0000000..3e8f4d7 --- /dev/null +++ b/autoload/vimtex/view/mupdf.vim @@ -0,0 +1,194 @@ +" vimtex - LaTeX plugin for Vim +" +" Maintainer: Karl Yngve Lervåg +" Email: karl.yngve@gmail.com +" + +function! vimtex#view#mupdf#new() " {{{1 + " + " Set default options + " + call vimtex#util#set_default('g:vimtex_view_mupdf_options', '') + call vimtex#util#set_default('g:vimtex_view_mupdf_send_keys', '') + + " + " Check if the viewer is executable + " + if !executable('mupdf') + call vimtex#echo#warning('MuPDF is not executable!') + call vimtex#echo#echo('- vimtex viewer will not work!') + call vimtex#echo#wait() + return {} + endif + + " + " Check if the xdotool is available + " + if !executable('xdotool') + call vimtex#echo#warning('MuPDF requires xdotool!') + return {} + endif + + " + " Use the xwin template + " + return vimtex#view#common#apply_xwin_template('MuPDF', + \ vimtex#view#common#use_temp_files_p(deepcopy(s:mupdf))) +endfunction + +" }}}1 + +let s:mupdf = {} + +function! s:mupdf.start(outfile) dict " {{{1 + let exe = {} + let exe.cmd = 'mupdf ' . g:vimtex_view_mupdf_options + let exe.cmd .= ' ' . vimtex#util#shellescape(a:outfile) + call vimtex#util#execute(exe) + let self.cmd_start = exe.cmd + + call self.xwin_get_id() + call self.xwin_send_keys(g:vimtex_view_mupdf_send_keys) + call self.forward_search(a:outfile) +endfunction + +" }}}1 +function! s:mupdf.forward_search(outfile) dict " {{{1 + if !executable('xdotool') | return | endif + if !executable('synctex') | return | endif + + let self.cmd_synctex_view = 'synctex view -i ' + \ . (line('.') + 1) . ':' + \ . (col('.') + 1) . ':' + \ . vimtex#util#shellescape(expand('%:p')) + \ . ' -o ' . vimtex#util#shellescape(a:outfile) + \ . " | grep -m1 'Page:' | sed 's/Page://' | tr -d '\n'" + let self.page = system(self.cmd_synctex_view) + + if self.page > 0 + let exe = {} + let exe.cmd = 'xdotool' + let exe.cmd .= ' type --window ' . self.xwin_id + let exe.cmd .= ' "' . self.page . 'g"' + call vimtex#util#execute(exe) + let self.cmd_forward_search = exe.cmd + endif + + call self.focus_viewer() +endfunction + +" }}}1 +function! s:mupdf.reverse_search() dict " {{{1 + if !executable('xdotool') | return | endif + if !executable('synctex') | return | endif + + let outfile = b:vimtex.out() + if s:output_not_readable(outfile) | return | endif + + if !self.xwin_exists() + call vimtex#echo#warning('reverse search failed (is MuPDF open?)') + return + endif + + " Get page number + let self.cmd_getpage = 'xdotool getwindowname ' . self.xwin_id + let self.cmd_getpage .= " | sed 's:.* - \\([0-9]*\\)/.*:\\1:'" + let self.cmd_getpage .= " | tr -d '\n'" + let self.page = system(self.cmd_getpage) + if self.page <= 0 | return | endif + + " Get file + let self.cmd_getfile = 'synctex edit ' + let self.cmd_getfile .= "-o \"" . self.page . ':288:108:' . outfile . "\"" + let self.cmd_getfile .= "| grep 'Input:' | sed 's/Input://' " + let self.cmd_getfile .= "| head -n1 | tr -d '\n' 2>/dev/null" + let self.file = system(self.cmd_getfile) + + " Get line + let self.cmd_getline = 'synctex edit ' + let self.cmd_getline .= "-o \"" . self.page . ':288:108:' . outfile . "\"" + let self.cmd_getline .= "| grep -m1 'Line:' | sed 's/Line://' " + let self.cmd_getline .= "| head -n1 | tr -d '\n'" + let self.line = system(self.cmd_getline) + + " Go to file and line + silent exec 'edit ' . fnameescape(self.file) + if self.line > 0 + silent exec ':' . self.line + " Unfold, move to top line to correspond to top pdf line, and go to end of + " line in case the corresponding pdf line begins on previous pdf page. + normal! zvztg_ + endif +endfunction + +" }}}1 +function! s:mupdf.latexmk_callback(status) dict " {{{1 + if !a:status | return | endif + + if g:vimtex_view_use_temp_files + call self.copy_files() + else + " + " Search for existing window created by latexmk + " It may be necessary to wait some time before it is opened and + " recognized. Sometimes it is very quick, other times it may take + " a second. This way, we don't block longer than necessary. + " + if !has_key(self, 'started_through_callback') + for l:dummy in range(30) + sleep 50m + if self.xwin_exists() | break | endif + endfor + endif + endif + + if !self.xwin_exists() && !has_key(self, 'started_through_callback') + call self.start(self.out) + let self.started_through_callback = 1 + endif + + if g:vimtex_view_use_temp_files + call self.xwin_send_keys('r') + endif + + call self.xwin_send_keys(g:vimtex_view_mupdf_send_keys) + if has_key(self, 'hook_callback') + call self.hook_callback() + endif +endfunction + +" }}}1 +function! s:mupdf.latexmk_append_argument() dict " {{{1 + if g:vimtex_view_use_temp_files + let cmd = ' -view=none' + else + let cmd = vimtex#latexmk#add_option('new_viewer_always', '0') + let cmd .= vimtex#latexmk#add_option('pdf_update_method', '2') + let cmd .= vimtex#latexmk#add_option('pdf_update_signal', 'SIGHUP') + let cmd .= vimtex#latexmk#add_option('pdf_previewer', + \ 'mupdf ' . g:vimtex_view_mupdf_options) + endif + return cmd +endfunction + +" }}}1 +function! s:mupdf.focus_viewer() dict " {{{1 + if !executable('xdotool') | return | endif + + if self.xwin_id > 0 + silent call system('xdotool windowfocus ' . self.xwin_id . ' --sync') + silent call system('xdotool windowraise ' . self.xwin_id) + endif +endfunction + +" }}}1 +function! s:mupdf.focus_vim() dict " {{{1 + if !executable('xdotool') | return | endif + + silent call system('xdotool windowfocus ' . v:windowid . ' --sync') + silent call system('xdotool windowraise ' . v:windowid) +endfunction + +" }}}1 + +" vim: fdm=marker sw=2 diff --git a/autoload/vimtex/view/zathura.vim b/autoload/vimtex/view/zathura.vim new file mode 100644 index 0000000..4b159fe --- /dev/null +++ b/autoload/vimtex/view/zathura.vim @@ -0,0 +1,120 @@ +" vimtex - LaTeX plugin for Vim +" +" Maintainer: Karl Yngve Lervåg +" Email: karl.yngve@gmail.com +" + +function! vimtex#view#zathura#new() " {{{1 + " + " Set default options + " + call vimtex#util#set_default('g:vimtex_view_zathura_options', '') + + " + " Check if the viewer is executable + " + if !executable('zathura') + call vimtex#echo#warning('Zathura is not executable!') + call vimtex#echo#echo('- vimtex viewer will not work!') + call vimtex#echo#wait() + return {} + endif + + " + " Check if the xdotool is available + " + if !executable('xdotool') + call vimtex#echo#warning('Zathura requires xdotool!') + return {} + endif + + " + " Use the xwin template + " + return vimtex#view#common#apply_xwin_template('Zathura', + \ vimtex#view#common#use_temp_files_p(deepcopy(s:zathura))) +endfunction + +" }}}1 + +let s:zathura = {} + +function! s:zathura.start(outfile) dict " {{{1 + let exe = {} + let exe.cmd = 'zathura' + let exe.cmd .= ' -x "' . g:vimtex_latexmk_progname + \ . ' --servername ' . v:servername + \ . ' --remote +\%{line} \%{input}"' + let exe.cmd .= ' ' . g:vimtex_view_zathura_options + let exe.cmd .= ' ' . vimtex#util#shellescape(a:outfile) + call vimtex#util#execute(exe) + let self.cmd_start = exe.cmd + + call self.xwin_get_id() + call self.forward_search(a:outfile) +endfunction + +" }}}1 +function! s:zathura.forward_search(outfile) dict " {{{1 + if !filereadable(self.synctex) | return | endif + + let exe = {} + let exe.cmd = 'zathura --synctex-forward ' + let exe.cmd .= line('.') + let exe.cmd .= ':' . col('.') + let exe.cmd .= ':' . vimtex#util#shellescape(expand('%:p')) + let exe.cmd .= ' ' . vimtex#util#shellescape(a:outfile) + call vimtex#util#execute(exe) + let self.cmd_forward_search = exe.cmd +endfunction + +" }}}1 +function! s:zathura.latexmk_callback(status) dict " {{{1 + if !a:status | return | endif + + if g:vimtex_view_use_temp_files + call self.copy_files() + else + " + " Search for existing window created by latexmk + " It may be necessary to wait some time before it is opened and + " recognized. Sometimes it is very quick, other times it may take + " a second. This way, we don't block longer than necessary. + " + if !has_key(self, 'started_through_callback') + for l:dummy in range(30) + sleep 50m + if self.xwin_exists() | break | endif + endfor + endif + endif + + if !self.xwin_exists() && !has_key(self, 'started_through_callback') + call self.start(self.out) + let self.started_through_callback = 1 + endif + + if has_key(self, 'hook_callback') + call self.hook_callback() + endif +endfunction + +" }}}1 +function! s:zathura.latexmk_append_argument() dict " {{{1 + if g:vimtex_view_use_temp_files + let cmd = ' -view=none' + else + let cmd = vimtex#latexmk#add_option('new_viewer_always', '0') + let cmd .= vimtex#latexmk#add_option('pdf_previewer', + \ 'zathura ' . g:vimtex_view_zathura_options + \ . ' -x \"' . g:vimtex_latexmk_progname + \ . ' --servername ' . v:servername + \ . ' --remote +\%{line} \%{input}\" \%S') + endif + + return cmd +endfunction + +" }}}1 + +" vim: fdm=marker sw=2