lervag / vimtex

VimTeX: A modern Vim and neovim filetype plugin for LaTeX files.
MIT License
5.56k stars 391 forks source link

Support passing column in synctex inverse search? #3005

Closed user202729 closed 1 month ago

user202729 commented 1 month ago

Is your feature request related to a problem? Please describe it.

Currently, VimtexInverseSearch only supports passing line and file. It would be nice if it support column as well.

That said, currently TeX itself does not output column information. But this is in theory possible, I have a preliminary patch to luatex that implements this. https://github.com/user202729/luatex/tree/synctex-column

Since it's a lot of hassle to recompile LuaTeX just for this, this is a TeX+PDF+synctex file which hopefully is testable. a.zip

Describe the solution you'd like Maybe something like this? (I try to make the change backwards compatible. Not sure about other editors but the suggested command works for Zathura for me.)

diff --git a/autoload/vimtex/view.vim b/autoload/vimtex/view.vim
index 90265f14..5879d46b 100644
--- a/autoload/vimtex/view.vim
+++ b/autoload/vimtex/view.vim
@@ -67,7 +67,8 @@ endfunction

 " }}}1

-function! vimtex#view#inverse_search(line, filename) abort " {{{1
+function! vimtex#view#inverse_search(line, filename, column) abort " {{{1
+  echom "line=".a:line.", column=".a:column
   " Only activate in VimTeX buffers
   if !exists('b:vimtex') | return -1 | endif

@@ -114,7 +115,7 @@ function! vimtex#view#inverse_search(line, filename) abort " {{{1
     execute g:vimtex_view_reverse_search_edit_cmd l:file
   endtry

-  execute 'normal!' a:line . 'G'
+  execute 'normal!' a:line . 'G' . (a:column ? a:column . '|' : '')
   if b:vimtex.viewer.xdo_check()
     call b:vimtex.viewer.xdo_focus_vim()
   endif
@@ -126,17 +127,18 @@ function! vimtex#view#inverse_search(line, filename) abort " {{{1
 endfunction

 " }}}1
-function! vimtex#view#inverse_search_cmd(line, filename) abort " {{{1
+function! vimtex#view#inverse_search_cmd(line, filename, column = 0) abort " {{{1
   " One may call this function manually, but the main usage is to through the
   " command "VimtexInverseSearch". See ":help vimtex-synctex-inverse-search"
   " for more info.
+  echom "first: column=".a:column

   if a:line > 0 && !empty(a:filename)
     try
       if has('nvim')
-        call s:inverse_search_cmd_nvim(a:line, a:filename)
+        call s:inverse_search_cmd_nvim(a:line, a:filename, a:column)
       else
-        call s:inverse_search_cmd_vim(a:line, a:filename)
+        call s:inverse_search_cmd_vim(a:line, a:filename, a:column)
       endif
     catch
     endtry
@@ -147,7 +149,7 @@ endfunction

 " }}}1

-function! s:inverse_search_cmd_nvim(line, filename) abort " {{{1
+function! s:inverse_search_cmd_nvim(line, filename, column) abort " {{{1
   if !filereadable(s:nvim_servernames) | return | endif

   for l:server in readfile(s:nvim_servernames)
@@ -159,15 +161,15 @@ function! s:inverse_search_cmd_nvim(line, filename) abort " {{{1
     call rpcnotify(l:socket,
           \ 'nvim_call_function',
           \ 'vimtex#view#inverse_search',
-          \ [a:line, a:filename])
+          \ [a:line, a:filename, a:column])
     call chanclose(l:socket)
   endfor
 endfunction

-function! s:inverse_search_cmd_vim(line, filename) abort " {{{1
+function! s:inverse_search_cmd_vim(line, filename, column) abort " {{{1
   for l:server in split(serverlist(), "\n")
     call remote_expr(l:server,
-          \ printf("vimtex#view#inverse_search(%d, '%s')", a:line, a:filename))
+          \ printf("vimtex#view#inverse_search(%d, '%s', %d)", a:line, a:filename, a:column))
   endfor
 endfunction

diff --git a/doc/vimtex.txt b/doc/vimtex.txt
index e2f7c60d..459da544 100644
--- a/doc/vimtex.txt
+++ b/doc/vimtex.txt
@@ -6037,6 +6037,10 @@ interested in learning how inverse-search configuration works, in which case
 one should read |vimtex-synctex-inverse-search|. The interpolation variables
 for Zathura configuration are `%{line}` and `%{input}`, not `%l` and `%f`.

+Zathura also supports `%{column}`, so if your synctex somehow supports column,
+you can put the following in your `zathurarc`: >conf
+  set synctex-editor-command "vim -v --not-a-term -T dumb -c \"VimtexInverseSearch %{line}:%{column} '%{input}'\""
+
 The main variant uses `xdotool` to help avoid duplicate Zathura instances.
 However, in some environments, `xdotool` is not available. Here the simple
 variant should work well.
diff --git a/plugin/vimtex.vim b/plugin/vimtex.vim
index bcf40dce..340a7f63 100644
--- a/plugin/vimtex.vim
+++ b/plugin/vimtex.vim
@@ -14,12 +14,21 @@ command! -nargs=* VimtexInverseSearch

 function! s:parse_args(args) abort
-  let l:line = matchstr(a:args, '^\s*\zs\d\+')
+  " Example:
+  " parse_args("5 a.tex") = [5, "a.tex"]
+  " parse_args("5 \"a.tex\"") = [5, "a.tex"]
+  " parse_args("5:3 a.tex") = [5, "a.tex", 3]
+  let l:line = matchstr(a:args, '^\s*\zs\d\+\%(:\d\+\)\?')
   if empty(l:line) | return [-1, ''] | endif

-  let l:file = matchstr(a:args, '^\s*\d\+\s*\zs.*')
+  let l:file = matchstr(a:args, '^\s*\d\+\%(:\d\+\)\?\s*\zs.*')
   let l:file = substitute(l:file, '\v^([''"])(.*)\1\s*', '\2', '')
   if empty(l:file) | return [-1, ''] | endif

-  return [str2nr(l:line), l:file]
+  let l:parts = split(l:line, ':')
+  if len(l:parts) == 2
+    return [str2nr(l:parts[0]), l:file, str2nr(l:parts[1])]
+  else
+    return [str2nr(l:line), l:file]
+  endif
 endfunction
lervag commented 1 month ago

Thanks, I don't see any reason not to add this. It seems backward compatible and will allow to pass the column position if you are able to get it. It's probably not useful to most people right now, but perhaps it may be useful to more people in the future.

user202729 commented 1 month ago

Maybe it's a good idea to configure the -x flag passed to zathura in autoload/vimtex/view/zathura.vim too.

function! vimtex#view#zathura#cmdline(outfile, synctex, start) abort " {{{1
  let l:cmd  = 'zathura'

  if a:start
    let l:cmd .= ' ' . g:vimtex_view_zathura_options
    if a:synctex
      let l:cmd .= printf(
            \ ' -x "%s -c \"VimtexInverseSearch %%{line} ''%%{input}''\""',
            \ s:inverse_search_cmd)
    endif
  endif

Edit %%{line} to %%{line}:%%{column}, I think. (zathura has been supporting %{column} since forever despite no actual support from from synctex so it should be safe right?)

Also at the moment looks like zathura will return -1 if column number is not supported. So it would be a good idea to

diff --git a/plugin/vimtex.vim b/plugin/vimtex.vim
index 72ea520c..624aec38 100644
--- a/plugin/vimtex.vim
+++ b/plugin/vimtex.vim
@@ -19,14 +19,14 @@ function! s:parse_args(args) abort
   "   parse_args("5 a.tex")   = [5, 'a.tex', 0]
   "   parse_args("5 'a.tex'") = [5, 'a.tex', 0]
   "   parse_args("5:3 a.tex") = [5, 'a.tex', 3]
-  let l:matchlist = matchlist(a:args, '^\s*\(\d\+\)\%(:\(\d\+\)\)\?\s\+\(.*\)')
-  if empty(l:matchlist) | return [-1, ''] | endif
+  let l:matchlist = matchlist(a:args, '^\s*\(\d\+\)\%(:\(-\?\d\+\)\)\?\s\+\(.*\)')
+  if empty(l:matchlist) | return [-1, '', 0] | endif
   let l:lnum = str2nr(l:matchlist[1])
   let l:cnum = str2nr(l:matchlist[2])
   let l:file = l:matchlist[3]

   let l:file = substitute(l:file, '\v^([''"])(.*)\1\s*', '\2', '')
-  if empty(l:file) | return [-1, ''] | endif
+  if empty(l:file) | return [-1, '', 0] | endif

   return [l:lnum, l:file, l:cnum]
 endfunction
lervag commented 1 month ago

Thanks!