Vim+TeXでページ追従

# TL;DR
こんなことができるようになりました。
![20181223_222954.gif](https://qiita-image-store.s3.amazonaws.com/0/45682/9a844bff-af30-f0d8-093f-dd7902ebdd86.gif)

# 今回紹介するプラグイン
[xuhdev/vim-latex-live-preview](https://github.com/xuhdev/vim-latex-live-preview)
VimでTeXのリアルタイムプレビューを可能にするプラグインです。
今回はこれにページ追従を加えます。

## プラグイン導入
プラグインマネージャとしてdeinを使っている場合はこれで動くと思います。
pdfビューアはevinceを指定しています。試した限りではevinceが最もよく動きました。
pLaTeXでなくLuaLaTeXを指定しているのは中間ファイルを作らず、直接pdfを出力するからです。
`jsarticle/jsreport/jsbook`と高い互換性のある文書クラス`ltjsarticle/ltjsreport/ltjsbook`があるので、表紙テンプレートがpLaTeX版で渡されるなどしても簡単に対応できると思います。

```txt:~/.vim/rc/dein_lazy.toml
[[plugins]]
repo = 'xuhdev/vim-latex-live-preview'
on_ft = 'tex'
hook_add = '''
  let g:livepreview_previewer = 'evince'
"  let g:livepreview_engine = 'luajittex --fmt=luajitlatex.fmt' "コンパイル速度を上げたい場合はこちら
  let g:livepreview_engine = 'lualatex'
  set swapfile
  set updatetime=1000
'''
```

# 追加する関数
自分でプラグインを変更したくない方は[こちら](https://github.com/nat-chan/vim-latex-live-preview)

## ソースコードの行番号と生成されたpdfのページ番号の対応をダンプする関数

```vim:~/dein/repos/github.com/xuhdev/vim-latex-live-preview/plugin/latexlivepreview.vim
function! s:Preprocess(file)
execute s:py_exe "<< EEOOFF"
with open(vim.eval("a:file"), "r") as f:
    lines = f.readlines()
for i in range(len(lines) - 1):
    lines[i] = '\write\pagedump{\    \\thepage ,}' + lines[i]
lines[-1] = '\write\pagedump{\    \\thepage ]}' + lines[-1]
lines.insert(0, '\\newwrite\pagedump\n')
lines.insert(1, '\openout\pagedump=pagedump.txt\n')
lines.insert(2, '\write\pagedump{let pagedump = [}')
lines.append('\closeout\pagedump')
with open(vim.eval("a:file"), "w") as f:
    f.writelines(lines)
EEOOFF
endfunction
```
この関数の動きを説明します。以下のようなTeXソースコードがあったとき、

```tex:before_preprocess.tex
\documentclass{ltjsarticle}
\begin{document}
page1
\newpage
page2
\end{document}
```

次のようにコンパイル前に前処理をします。

```tex:after_preprocess.tex
\newwrite\pagedump
\openout\pagedump=pagedump.txt
\write\pagedump{let pagedump = [}
\write\pagedump{\ \thepage ,}\documentclass{ltjsarticle}
\write\pagedump{\ \thepage ,}\begin{document}
\write\pagedump{\ \thepage ,}page1
\write\pagedump{\ \thepage ,}\newpage
\write\pagedump{\ \thepage ,}page2
\write\pagedump{\ \thepage ]}\end{document}
\closeout\pagedump
```
コンパイルすると次のような配列の定義ファイルが`$TEXMFOUTPUT/pagedump.txt`に書き出されます。
これをvimから`source pagedump.txt`することでカーソル行とpdfのページ番号の対応を扱うことができます。

```vim:pagedump.txt
let pagedump = [
\ 1,
\ 1,
\ 1,
\ 1,
\ 2,
\ 2]
```
`s:Compile()`の末尾でコンパイル直前に`s:Preprocess`を呼ぶように書き換えます

```vim:~/dein/repos/github.com/xuhdev/vim-latex-live-preview/plugin/latexlivepreview.vim
    " Write the current buffer in a temporary file
    silent exec 'write! ' . b:livepreview_buf_data['tmp_src_file']
    call s:Preprocess(b:livepreview_buf_data['tmp_src_file'])
    call s:RunInBackground(b:livepreview_buf_data['run_cmd'])
    lcd -
endfunction
```

## evinceにページ番号を指定するキーストロークを投げる関数
私の使っているWSLとXmingの組み合わせでは、WSL内からxdotoolでキーを投げるとXmingが扱っているウィンドウのうち、直近に選択されたウィンドウに投げられる。という挙動になっています。
そのため、Vimの中からフォーカスを外さずに裏のevinceにキーストロークを投げることができます。
当然ながら、他の環境ではこの関数を書き換える必要があります。

```vim:~/dein/repos/github.com/xuhdev/vim-latex-live-preview/plugin/latexlivepreview.vim
function! g:EvinceGoTo()
    execute 'source '                   .
    \ b:livepreview_buf_data['tmp_dir'] .
    \      expand('%:p:h')              .
    \      '/pagedump.txt'
    call system("xdotool key 'ctrl+l'")
    call system("xdotool key 'ctrl+a'")
    call system("xdotool key " . g:pagedump[getcurpos()[1]-1])
    call system("xdotool key KP_Enter")
endfunction
```

# 操作方法
`:LLPStartPreview`でevinceを表示したのち、`:call EvinceGoTo()`でカーソル行のページが表示されます。

```vim:~/dein/repos/github.com/xuhdev/vim-latex-live-preview/plugin/latexlivepreview.vim
nnoremap <Leader>e :<C-u>call EvinceGoTo()<CR>
```

としておけば`Space e`ですみます。(私は`<Leader>`にスペースを指定している)
ただし、現状`:call EvinceGoTo()`ごとに`pagedump.txt`を読み込みに行っているので、`autocmd cursormoved`でフックするにはもうすこし`pagedump.txt`の読み出しタイミングを工夫する必要がありそうです。

# 〆
明日はクリスマス・イブですね、VimConf2018ではDark Poweredな発表で話題だったΛlisueさんの記事になります。
全く関係ないですが下は大学で最近見かけたポスターです。いったいモデルは何すえさんなのか…？。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/45682/dcb33c46-fddf-7c2a-25ac-c8842850156c.png)
そういえばVimConf2018ではVimの父Bramさんからサインを頂いちゃいました。

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">Vimの父Bram氏からSurfaceにサイン貰った！<a href="https://twitter.com/hashtag/vimconf?src=hash&amp;ref_src=twsrc%5Etfw">#vimconf</a> <a href="https://t.co/5L52g5v8ed">pic.twitter.com/5L52g5v8ed</a></p>&mdash; ℕ (@mathbbN) <a href="https://twitter.com/mathbbN/status/1066262489535438850?ref_src=twsrc%5Etfw">2018年11月24日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

それでは、Happy Vimming!