Fixed #551: Ensure fully buffer local variables
This commit also involves a strong refactoring of the viewer code, where individual viewers (general, mupdf, zathura) are separated into individual modules. This makes it easier to distinguish common and specialized code, as well as making the viewer interfaces clearer.
This commit is contained in:
parent
4df9f85e45
commit
4ba99f83bd
@ -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
|
||||
|
142
autoload/vimtex/view/common.vim
Normal file
142
autoload/vimtex/view/common.vim
Normal file
@ -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
|
110
autoload/vimtex/view/general.vim
Normal file
110
autoload/vimtex/view/general.vim
Normal file
@ -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
|
194
autoload/vimtex/view/mupdf.vim
Normal file
194
autoload/vimtex/view/mupdf.vim
Normal file
@ -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
|
120
autoload/vimtex/view/zathura.vim
Normal file
120
autoload/vimtex/view/zathura.vim
Normal file
@ -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
|
Loading…
Reference in New Issue
Block a user