6. Setting Up a PDF Reader for Writing LaTeX with Vim

Last modified: 18 October 2022

This is part six in a seven part series explaining how to use the Vim or Neovim text editors to efficiently write LaTeX documents. This article explains, for both Linux and macOS, how to set up a PDF reader for displaying the PDF file associated with the LaTeX source file being edited in Vim.

Contents of this article

Background knowledge:

Choosing a PDF Reader

You want a PDF reader that:

A PDF reader on Linux

I recommend and will cover Zathura, under the assumption that anyone reading a multi-article Vim series will appreciate Zathura’s Vim-like key bindings and text-based configurability. The VimTeX plugin also makes configuration between Zathura and Vim very easy. Note, however, that many more Linux-compatible PDF readers exist—see the VimTeX plugin’s documentation at :help g:vimtex_view_method if curious.

A PDF reader on macOS

The canonical option on macOS is Skim, which you can download as a macOS dmg file from its homepage or from SourceForge. (The default macOS PDF reader, Preview, does not listen for document changes, nor, to the best of my knowledge, does it integrate nicely with SyncTeX.)

On Intel Macs, it is also possible to set up Zathura to work with VimTeX using the Homebrew formula provided by homebrew-zathura GitHub page, so I have included a section for setting up Zathura on macOS at the end of this article. Unfortunately this method does not seem to work on Apple Silicon Macs, i.e. Macs using the ARM64 processor architecture.

A PDF reader on Windows

I have not tested PDF readers on Windows (reminder of the series prerequisites for operating system), but you can find an overview of PDF reader possibilities on Windows in the VimTeX documentation section :help g:vimtex_view_method.

Summary: What works on what platform

I tested 9 combinations of editor, OS, and PDF reader when preparing this article, and the results are summarized in the table below—the more green check marks the better.

Recommendations based on my testing:

Zathura on Linux (tested with i3 on Arch using Zathura 0.4.8)

Editor Forward search works Inverse search works Editor keeps focus after forward search Focus returns to editor after inverse search
Neovim 1
Vim 1
gVim 2 2

Skim on macOS (tested on macOS 12.1 using Skim 1.6.9)

Editor Forward search works Inverse search works Editor keeps focus after forward search Focus returns to editor after inverse search
Neovim 3
Vim
MacVim

Zathura on macOS (Intel-based MacBook Pro 11,5; macOS 12.1; Zathura 0.4.9 built from homebrew-zathura)

Editor Forward search works Inverse search works Editor keeps focus after forward search Focus returns to editor after inverse search
Neovim 3
Vim
MacVim 4

Cross-platform concepts

Many of the same ideas apply on both macOS and Linux. To avoid repetition I will list these here, and leave OS-specific implementation details for later in the article.

You will hear two bits of jargon throughout this article:

Forward search is the process of jumping from the current cursor position in the LaTeX source file to the corresponding position in the PDF reader displaying the compiled document. In everyday language, forward search is a text editor telling a PDF reader: “hey, PDF reader, display the position in the PDF file corresponding to my current position in the LaTeX file”.

The following GIF demonstrates forward search:

Demonstration of forward search

Inverse search (also called backward search) is the process of switching focus from a line in the PDF document to the corresponding line in the LaTeX source file. Informally, inverse search is like the user asking, “hey, PDF viewer, please take me to the position in the LaTeX source file corresponding to my current position in the PDF file”.

The following GIF demonstrates inverse search:

Demonstration of inverse search

Compiling with SyncTeX

Positions in the PDF file are linked to positions in the LaTeX source file thanks to a utility called SyncTeX, which is implemented in a binary program called synctex that should ship by default with a standard TeX installation.

For forward and backward search to work properly, your LaTeX documents must be compiled with synctex enabled. This is as simple as passing the -synctex=1 option to the pdflatex or latexmk programs when compiling your LaTeX files. VimTeX’s compiler backends do this by default, and doing so manually was covered in the previous article in this series. If you are curious, you can find more synctex documentation at man synctex or by searching man pdflatex or man latexmk for 'synctex'.

Inter-process communication requires a server

Here is the big picture: inverse search requires one program—the PDF reader—to be able to access and open a second program—Vim—and ideally open Vim at a specific line. This type of inter-program communication is possible because of Vim’s built-in remote procedure call (RPC) protocol. The details of implementation vary between Vim and Neovim (see :help remote.txt for Vim and :help RPC for Neovim), but in both cases Vim or Neovim must run a server that listens for and processes requests from other programs (such as a PDF reader).

In this article and in the Vim and VimTeX documentation you will hear talk about a server—what we are referring to is the server Vim/Neovim must run to communicate with a PDF reader. Remembering that an RPC protocol and client-server model are required under the hood may help clarify your mental image of inverse search.

Ensuring you have a clientserver-enabled Vim

Neovim, gVim, and MacVim come with client-server functionality by default; if you use any of these programs, lucky you. You can skip to the next section.

If you use terminal Vim, run vim --version. If the output includes +clientserver, your Vim version is compiled with client-server functionality and can might be able to perform inverse search—see the macOS caveat below. If the output includes -clientserver, your Vim version does not have client-server functionality. You will need to install a new version of Vim to use inverse search. Getting a +clientserver version of Vim is easy on Linux and beyond the scope of this article on macOS:

The rest of this article assumes you have a version of Vim with +clientserver.

Here is the tricky part: if you install MacVim (e.g. brew install --cask macvim or by downloading MacVim from the MacVim website), the output of vim --version may well show +clientserver. But this is false advertising, and confused me for quite some time while writing this guide!

Here is my understanding of the issue: because of how MacVim’s version of terminal Vim handles servers, even if regular terminal Vim is started with a server (using e.g. vim --servername VIM myfile.tex) and :echo v:servername returns VIM, the server is useless because programs other than MacVim won’t be aware of it. Quoting from :help macvim-clientserver (only available if you use MacVim’s version of vim; emphasis mine):

Server listings are made possible by the frontend (MacVim) keeping a list of all currently running servers. Thus servers are not aware of each other directly; only MacVim know which servers are running.

You can easily test this for yourself on macOS:

  1. Install MacVim with brew install --cask macvim.
  2. Run vim --version and note that the output includes +clientserver
  3. Start Vim with vim --servername VIM myfile.tex and note that :echo v:servername returns VIM, suggesting client-server functionality will work.
  4. Now here is the catch: open another terminal, run vim --listservers, and notice that the result is blank! In other words, even though the Vim instance editing myfile.tex is running a server, this server is not visible to the outside world, and effectively useful for the purposes of inverse search.

See the GitHub issue Client Server mode does not work in non-GUI macvim #657 for a longer discussion of this problem.

Note that Homebrew used to offer brew install vim --with-client-server, but this option is no longer available. It may well be possible to compile a version of terminal Vim from source that includes +clientserver, and, in combination with XQuartz, get inverse search to work on macOS using terminal Vim. But that is beyond the scope of this tutorial, and would probably be more work than just switching to GUI MacVim or terminal Neovim, both of which support inverse search on macOS.

Ensure Vim starts a server (only for terminal Vim on Linux)

Neovim, gVim, and MacVim start a server on startup automatically; if you use any of these programs, lucky you—feel free to skip to the next section. If you use a +clientserver-enabled terminal Vim on Linux, place the following code snippet in your vimrc, ftplugin/tex.vim, or similar:

" This will only work if `vim --version` includes `+clientserver`!
if empty(v:servername) && exists('*remote_startserver')
  call remote_startserver('VIM')
endif

This code checks the built-in v:servername variable to see if Vim has started a server, and if it hasn’t, starts a server named VIM if Vim’s remote_startserver function is available (which it should be on a reasonably up-to-date version of Vim). Starting the server makes inverse search possible. The above code snippet was taken from the VimTeX documentation at :help vimtex-clientserver, which will give you more background on starting a server for inverse search.

After adding the above code snippet to your Vim config, restart Vim and check the output of echo v:servername—it should output VIM. Then open a LaTeX file and check the output of :VimtexInfo; the output should look something like this:

# If a server is successfully running:
Has clientserver: true
Servername: VIM

# If a server is not running---inverse search won't work
Has clientserver: true
Servername: undefined (vim started without --servername)

Reminder: on macOS, MacVim’s version of terminal Vim can misleadingly display both Has clientserver: true and Servername: VIM, but inverse search still won’t work—see the caveat above.

That’s all for the cross-platform concepts. Let’s set up a PDF reader!

Zathura (read this on Linux)

If you use macOS, scroll down to the section on configuring Skim or setting up Zathura on macOS.

Caveat: at the time of writing, you’ll need the X Window System (and not Wayland) to get Zathura working properly with VimTeX. This is because VimTeX relies on xdotool for integration with Zathura, and xdotool only works with X. For details see VimTeX issue #2046. Note that Ubuntu and other major distros are switching to Wayland by default, but you can usually manually choose to use Xorg when logging in.

Good news: Assuming you’re using the X Window System, VimTeX makes connecting Zathura and Vim/Neovim/gVim very easy. Here’s what to do:

Ensure your Zathura is SyncTeX-enabled

Zathura must be compiled with libsynctex for forward and inverse search to work properly. Most Linux platforms should ship a version with libsynctex support and this shouldn’t be a problem for you, but it isn’t 100% guaranteed—see the note towards the bottom of :help vimtex-view-zathura for more information. You can check that your version of Zathura has SyncTeX support using the ldd program, which checks for shared dependencies; just issue the following command on the command line:

# List all of Zathura's shared dependencies and search the output for libsynctex
ldd $(which zathura) | grep libsynctex

If the output returns something like libsynctex.so.2 => /usr/lib/libsynctex.so.2 (0x00007fda66e50000), your Zathura has SyncTeX support. If the output is blank, your Zathura does not have SyncTeX support, and forward and inverse search will not work—you will need a new version of Zathura or a different PDF reader.

Note that VimTeX performs this check automatically and will warn you if your Zathura version lacks SyncTeX support; for the curious, this check is implemented in the VimTeX source code in the file vimtex/autoload/vimtex/view/zathura.vim, on line 27 at the time of writing. See :help g:vimtex_view_zathura_check_libsynctex for reference.

Relevant editors: Vim and Neovim used with Zathura on Linux (for resolving gVim focus problems scroll down). You’ll also need the X Window System (and not Wayland) for xdotool to work.

Depending on your window manager and/or desktop environment, Vim may lose focus after performing forward search (this happens for me on i3 with both Vim and Neovim; YMMV). If you prefer to keep focus in Vim, you can use xdotool and some VimTeX autocommands to solve the problem. Here’s what to do:

  1. Place the following line in your ftplugin/tex.vim:

    " Get Vim's window ID for switching focus from Zathura to Vim using xdotool.
    " Only set this variable once for the current Vim instance.
    if !exists("g:vim_window_id")
      let g:vim_window_id = system("xdotool getactivewindow")
    endif
    

    Whenever you open a LaTeX file, this code will use xdotool to query for an 8-digit window ID identifying the window running Vim (which is presumably the active window) and store this ID in the global Vimscript variable g:vim_window_id. The if !exists() block only sets the g:vim_window_id variable if it has not yet been set for the current Vim instance.

  2. Then define the following Vimscript function, also in ftplugin/tex.vim:

    function! s:TexFocusVim() abort
      " Give window manager time to recognize focus moved to Zathura;
      " tweak the 200m as needed for your hardware and window manager.
      sleep 200m  
    
      " Refocus Vim and redraw the screen
      silent execute "!xdotool windowfocus " . expand(g:vim_window_id)
      redraw!
    endfunction
    

    This function calls VimtexView to execute forward search, waits a few hundred milliseconds to let the window manager recognize focus has moved to Zathura, then uses xdotool’s windowfocus command to immediately refocus the window holding Vim. Using silent execute instead of just execute suppresses Press ENTER or type command to continue messages, although you may want to start with just execute for debugging purposes.

    Although it is hacky, I have empirically found the sleep 200m wait ensures the subsequent window focus executes properly (you may want to tweak the exact sleep time for your hardware and window manager). The redraw! command refreshes Vim’s screen. If interested, you can read more about writing Vimscript functions in this series’s Vimscript article, which is the next and final article in the series.

  3. Finally, define the following Vimscript autocommand group in your ftplugin/tex.vim:

    augroup vimtex_event_focus
      au!
      au User VimtexEventView call s:TexFocusVim()
    augroup END
    

    The above autocommand runs the above-defined refocus function s:TexFocusVim() in response to the VimTeX event VimtexEventView, which triggers whenever VimtexView completes (see :help VimtexEventView for documentation.). In practice, this refocuses Vim after every forward search.

Relevant editor: gVim used with Zathura on Linux You’ll also need the X Window System (and not Wayland) for xdotool to work.

From my testing (using the i3 window manager; YMMV) gVim lost focus after forward search and failed to regain focus after inverse search. Here is how to fix both problems (some steps are the same as for terminal Vim/Neovim above, in which case I will refer to the above descriptions to avoid repetition):

  1. Place the following line in your ftplugin/tex.vim:

    " Get Vim's window ID for switching focus from Zathura to Vim using xdotool.
    " Only set this variable once for the current Vim instance.
    if !exists("g:vim_window_id")
      let g:vim_window_id = system("xdotool getactivewindow")
    endif
    

    For an explanation, see the analogous step for Vim/Neovim above.

  2. Then define the following Vimscript function, also in ftplugin/tex.vim:

    function! s:TexFocusVim(delay_ms) abort
      " Give window manager time to recognize focus 
      " moved to PDF viewer before focusing Vim.
      let delay = a:delay_ms . "m"
      execute 'sleep ' . delay
      execute "!xdotool windowfocus " . expand(g:vim_window_id)
      redraw!
    endfunction
    

    This function plays as similar role to the one in the analogous step for Vim/Neovim (see above for an explanation), but allows for a variable sleep time using the delay_ms argument, which is the number of milliseconds passed to Vim’s sleep command. The function uses a variable sleep time because (at least in my testing) post-inverse-search refocus does not require any delay to work properly, while post-forward-search refocus does.

  3. Finally, define the following Vimscript autocommand group in your ftplugin/tex.vim:

    augroup vimtex_event_focus
      au!
      " Post-forward-search refocus with 200ms delay---tweak as needed
      au User VimtexEventView call s:TexFocusVim(200)
    
      " Only perform post-inverse-search refocus on gVim; delay unnecessary
      if has("gui_running")
        au User VimtexEventViewReverse call s:TexFocusVim(0)
      endif
    augroup END
    

    The events VimtexEventView and VimtexEventViewReverse, conveniently provided by VimTeX, trigger whenever VimtexView and VimtexInverseSearch complete, respectively. The above autocommands run the above-defined refocus function s:TexFocusVim() after every execution of forward search using VimtexView or inverse search using VimtexInverseSearch. See :help VimtexEventView and :help VimtexEventViewReverse for documentation.

    Again, you may want to tweak the forward search delay time (somewhere from from 50ms to 300ms should suit most users) until refocus works properly on your window manager and hardware.

Skim (read this on macOS)

It is also possible to use Zathura on macOS for Macs with Intel processors; if you would prefer this, scroll down to the section Zathura on Intel Macs.

Here is how to set up Skim to work with Vim/Neovim running VimTeX. Some of the steps are the same as for Zathura on Linux, so excuse the repetition:

Zathura on Intel Macs

It is possible to use Zathura with VimTeX on macOS thanks to the Homebrew formulae provided by github.com/zegervdv/homebrew-zathura.

Unfortunately, this process does not seem to work on Macs with Apple Silicon processors;

see e.g. VimTeX issue #2424 for details. More specifically, Apple Silicon Macs seem to have difficulty activating the dbus service, which is required for forward search and inverse search. You can still build Zathura on an Apple Silicon Mac, but there is a chance it won’t support forward and inverse search, won’t work with VimTeX, and thus won’t be useful as a LaTeX PDF reader. If you have an Apple Silicon Mac, you should probably use Skim with VimTeX. That said, if anyone reading this successfully set up Zathura and VimTeX on an ARM64 Mac, I would be very interested in hearing more.

For Intel Macs, building Zathura is described in the VimTeX documentation at :help vimtex-faq-zathura-macos, and I can confirm the process works (at least from my own testing on an Intel CPU MacBook Pro 11,5 running macOS 12.1).

Building Zathura and dependencies on macOS (Intel Macs)

Quoting more or less directly from :help vimtex-faq-zathura-macos, here is how to build Zathura on macOS (reminder: this seems to work only on Intel Macs):

  1. Check if you already have Zathura installed using e.g. which zathura. If you have Zathura installed, I recommend uninstalling it and repeating from scratch to ensure all dependencies are correctly sorted out.

  2. If needed, uninstall your existing Zathura and related libraries with the following code:

    # Remove symlinks
    brew unlink zathura-pdf-poppler
    # or use `brew unlink zathura-pdf-mupdf` if you have mupdf installed
    brew unlink zathura
    brew unlink girara
    
    # Uninstall
    brew uninstall zathura-pdf-poppler
    # or use `brew uninstall zathura-pdf-mupdf` if you have mupdf installed
    brew uninstall zathura
    brew uninstall girara
    
  3. Zathura needs dbus to work properly; install it with brew install dbus. If you already have dbus installed, rumor has it that you should reinstall it with brew reinstall dbus, although I have not checked if this is necessary myself.

  4. Set a bus address for dbus sessions with the following environment variable:

    DBUS_SESSION_BUS_ADDRESS="unix:path=$DBUS_LAUNCHD_SESSION_BUS_SOCKET" 
    

    You should then make this change permanent by placing the following code in one of your shell’s start-up files:

    export DBUS_SESSION_BUS_ADDRESS="unix:path=$DBUS_LAUNCHD_SESSION_BUS_SOCKET" 
    

    You could place this in your ~/.bash_profile (Bash), ~/.zprofile (ZSH), or perhaps an rc file, depending on your shell and personal shell start-up configuration.

    If you are new to setting environment variables and the shell environment, you might want to read through the discussion VimTeX issue #2391, which solves a Zathura issue by properly setting DBUS_SESSION_BUS_ADDRESS—thanks to @liamd101 on this one! (This issue involves a Bash shell, but would work for ZSH by replacing ~/.bash_profile with ~/.zprofile.)

  5. Change the value of <auth><\auth> in /usr/local/opt/dbus/share/dbus-1/session.conf from EXTERNAL to DBUS_COOKIE_SHA1:

    # Before
    <auth>EXTERNAL<\auth>
    
    # After
    <auth>DBUS_COOKIE_SHA1<\auth>
    
  6. Run brew services start dbus to start dbus. You can double-check dbus is running with brew services info dbus, which should output something like this (potentially after a reboot):

    $ brew services info dbus
    dbus (org.freedesktop.dbus-session)
    Running: ✔
    Loaded: ✔
    
  7. Install the most recent version of Zathura (i.e. HEAD):

    brew tap zegervdv/zathura
    brew install girara --HEAD
    brew install zathura --HEAD --with-synctex
    brew install zathura-pdf-poppler
    mkdir -p $(brew --prefix zathura)/lib/zathura
    ln -s $(brew --prefix zathura-pdf-poppler)/libpdf-poppler.dylib $(brew --prefix zathura)/lib/zathura/libpdf-poppler.dylib
    

    Notes:

    • You might be prompted by Homebrew to install the Apple Command Line Tools before you can complete brew install girara --HEAD. If so, just follow Homebrew’s suggestion (which will probably be something along the lines of xcode-select --install), then retry brew install girara --HEAD.
    • Ensure you use brew install zathura --HEAD --with-synctex to get a Zathura with SyncTeX support; the homebrew-zathura GitHub page only suggests brew install zathura --HEAD.
  8. Reboot and enjoy Zathura.

For the original GitHub discussion that produced the instructions in :help vimtex-faq-zathura-macos, see the VimTeX issue Viewer cannot find Zathura window ID on macOS #1737.

Setting up Zathura on macOS (Intel Macs)

Here is how to set up Zathura on macOS (many steps are similar to those for setting up Zathura on Linux; please excuse any repetition):

Fixing focus loss problems on macOS

This section gives two fixes for returning focus to your text editor following inverse search on macOS.

Returning focus to Neovim after inverse search on macOS

Relevant editor: Neovim used with Skim or Zathura on macOS

From my testing (on macOS 12.1) Neovim failed to regain focus after inverse search from both Skim and Zathura. Here is how to fix this problem (some steps are similar to refocusing solutions on Linux, so please excuse the repetition):

Returning focus to MacVim after inverse search on macOS

Relevant editor: MacVim used with Zathura on macOS

From my testing (on macOS 12.1) Neovim failed to regain focus after inverse search from Zathura (but did regain inverse search from Skim). Here is how to fix the problem (the steps are similar to those for Neovim just above, so please excuse any repetition):

Further reading

I suggest you read through the VimTeX documentation beginning at :help g:vimtex_view_enabled and ending at :help g:vimtex_view_zathura_check_libsynctex. Although not all of the material will be relevant to your operating system or PDF reader, you will still find plenty of interesting information and configuration options.

Here is an example: VimTeX automatically opens your PDF reader when you first compile a document, even if you have not called :VimtexView. If you prefer to disable this behavior, place the following code in your ftplugin/tex.vim:

" Don't automatically open PDF viewer after first compilation
let g:vimtex_view_automatic = 0

The original writing, images, and animations in this series are licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
CC BY-NC 4.0

Footnotes

  1. If you use the xdotool windowfocus solution described in Optional tip: Return focus to Vim/Neovim after forward search 2

  2. If you use the xdotool windowfocus solution described in Optional tip: Return focus to gVim after forward and inverse search 2

  3. If you use the open -a TERMINAL solution described in Optional tip: Return focus to Neovim after inverse search 2

  4. If you use the open -a MacVim solution described in Optional tip: Return focus to MacVim after inverse search