lervag / vimtex

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

synctex preview of labels for navigation/completion #2939

Open bellecp opened 2 months ago

bellecp commented 2 months ago

After some testing, it appears possible, with the following few lines in .vimrc, to navigate to labels (or any element from vimtex's TOC) with fzf while previewing the corresponding label/TOC element inside the PDF previewer. As a different entry is focused inside vim's fzf window, the corresponding equation or section title is instantly highlighted in the PDF. The key is in the --bind ''focus:execute-silent(...) part of fzf's options.

The video describes what it looks like:

https://github.com/lervag/vimtex/assets/1019692/36f6da70-2233-41f7-bd46-8c51bd33fb9f

For label/TOC navigation:

nnoremap <c-x><c-l> :call vimtex#fzf#run('ctli', {
            \ 'up': '90%',
            \ 'sink':  function('vimtex#fzf#open_selection'),
            \ 'options': '-d "#####" --with-nth=3..  --ansi --bind ''focus:execute-silent(zathura --synctex-forward {1}:1:{2} "$(sed "s/tex$/pdf/" <<< {2})" )''  '
            \ })<CR>

For completion: (e.g., type \Cref{thm<C-L> in insert mode)

inoremap <expr> <c-l> fzf#vim#complete({
            \ 'source': map(filter(vimtex#parser#toc(), {_, d -> d['type'] == 'label'}), {_,d -> join([d['line'], d['file'], d['title']], '#####')}),
            \ 'up': '90%',
            \ 'reducer': {lines -> trim(split(split(lines[0], ' (')[0],'#####')[-1])},
            \ 'options': '--with-nth 3.. --delimiter "#####" --ansi --bind ''focus:execute-silent(zathura --synctex-forward {1}:1:{2} "$(sed "s/tex$/pdf/" <<< {2})" )''  '
            \})

Is your feature request related to a problem? Please describe it. The above is sufficient for my use case, so I am not asking to implement it more deeply into vimtex itself. I wanted to point out this nice possibility on top of some API already provided by vimtex (vimtex#parser#toc(), 'vimtex#fzf#open_selection' and fzf. With my limited testing, it provides a very pleasant workflow for navigation and label completion.

Describe the solution you'd like If you are interested in pushing this further, some remarks:

Some caveat:

  1. Vimtex's continuous compilation should be started beforehand, and zathura's window already opened. Otherwise the behavior is undetermined.

  2. With the above code, forward search is performed on the first character of the line where the label is located. Sometimes this forward search fails and the PDF previewer shows some unrelated portion of the PDF close to the label. It happens for instance with

    \begin{equation}
    \label{mylab}
    x^2+y^2=z^2
    \end{equation}

    for which synctex forward search on the line of the label focuses on the right page but fails to highlight the equation. The problem is resolved by writing instead

    \begin{equation}
    x^2+y^2=z^2 
    \label{mylab}
    \end{equation}

    I haven't found a robust way to fix this. Forward search with the line of the label plus one could give better results on average, but this does not sound ideal.

lervag commented 2 months ago

After some testing, it appears possible, with the following few lines in .vimrc, to navigate to labels (or any element from vimtex's TOC) with fzf while previewing the corresponding label/TOC element inside the PDF previewer. As a different entry is focused inside vim's fzf window, the corresponding equation or section title is instantly highlighted in the PDF. The key is in the --bind ''focus:execute-silent(...) part of fzf's options.

The video describes what it looks like: …

That's an interesting idea, for sure!

The above is sufficient for my use case, so I am not asking to implement it more deeply into vimtex itself. I wanted to point out this nice possibility on top of some API already provided by vimtex (vimtex#parser#toc(), 'vimtex#fzf#open_selection' and fzf. With my limited testing, it provides a very pleasant workflow for navigation and label completion.

First, I'm glad you are not proposing that this should be implemented directly into VimTeX. It may be a good idea, but I see a lot of complicated issues with it. So, at least for now, let's discuss the concept from the user config side.

one issue is to implement the above for any PDF previewer Zathura is hardcoded above, and I am not sure it would be even possible to.

I believe it should be possible for viewers that have a similar forward search on the command line, which I think are really most viewers.

  • the above code assumes that the PDF filename can always be obtained by replacing .tex$ by .pdf from the .tex file given by vimtex#parser#toc(). The above code will break if the latex manuscript is scattered in many .tex file using \input.

  • One difficulty is that the inside of focus:execute-silent(..) is executed by FZF in a shell: none of vimtex's vimscript functions are available at that point.

Yes, but at the same time, you are free to do some preprocessing here. I'm thinking something like this, where b:vimtex.tex and b:vimtex.compiler.get_file('pdf') are interpolated when you call the mapping.

function! OpenSync() abort
  let l:bind = printf(
        \ "--bind 'focus:execute-silent(zathura --synctex-forward {1}:1:\"%s\" \"%s\")'",
        \ b:vimtex.tex,
        \ b:vimtex.compiler.get_file('pdf')
        \)
  call vimtex#fzf#run('ctli', {
        \ 'up': '90%',
        \ 'sink':  function('vimtex#fzf#open_selection'),
        \ 'options': '-d "#####" --with-nth=3.. --ansi ' . l:bind
        \})
endfunction

nnoremap <c-x><c-l> :call OpenSync()<cr>