vimを高機能なPDFリーダーにする設定

vimをPDFリーダーに仕上げるために、以下の仕様を満たすvim設定を作ります。

* pdfファイルを開いたときにテキスト形式に変換して表示してほしい
* 文字を変更しようとしたときに警告してほしい
* j, kのカーソル移動を画面移動に割り当てたい
  * デフォルトではC-E, C-Yになっている
  * [vimpager](https://github.com/rkitover/vimpager)っぽい挙動にしたい

# 最終的な設定
いきなり結論。次のautocmdを.vimrcに書く。

```vim:PDFを開いたときにテキストに変換して開くautocmd
augroup MyAutoCmd
    if executable('pdftotext')
        " PDFファイルを開いた時、text形式に変換してから開く
        autocmd BufRead *.pdf :enew | 0read !pdftotext -layout -nopgbrk "#" -
    endif
    " 圧縮ファイルとPDFファイルを開いた時、readonlyモードで開き、j/kキーマップを変更
    autocmd BufRead *.zip,*.gz,*.bz2,*.xz,*.pdf setlocal readonly nolist
        \| nn <buffer> j <C-E> | nn <buffer> k <C-Y>
augroup END
```

以下解説


# pdfファイルを開いたときにテキスト形式に変換して表示してほしい
[pdftotext](https://github.com/jalan/pdftotext)というコマンドを使えるようにして[^1]

```vim
autocmd BufRead *.pdf :enew | 0read !pdftotext -layout -nopgbrk "#" -
```

を実行します。
PDFをvimで開く肝の設定です。
1行ずつ日本語にしてみます。

* `BufRead *.pdf`: pdfファイルをバッファに開こうとしたときに次のコマンドを実行します。
* `:ene |`: 新しいバッファを開いて次のコマンドを実行します。
* `0r !pdftotext -layout -nopgbrk "#" -`: pdftotextコマンドで変換した代替バッファ(#)を新しいバッファの最初の行に読み込みます。
  * `-layout`: インデントを保持するpdftotextのオプションです。あったほうが綺麗です。
  * `-nopgbrk`: 改ページによる改行を入れないpdftotextのオプションです。あったほうが綺麗です。
  * pdftotext の最後の引数"-"ダッシュは変換結果を標準出力にアウトプットします。これがないとtxtファイルが生成されるだけでvimで開き直さないといけません。必須です。
  * 0rの数字"0"がないと最初の行が空行になってかっこ悪いので、あったほうが良いです。
  * #をダブルクォートで囲うのは空白を含んだPDFファイルが引数に与えられたときの空白エスケープ用です。必須です。


# 文字を変更しようとしたときに警告してほしい
書いているうちに両方をワンコマンドで実行できることに気がついたので、目次が変になりましたが悪しからず。

# j, kのカーソル移動を画面移動に割り当てたい
これは好みの設定ですので、なくても構いません。

```vim:圧縮ファイルかpdfファイルを開いたときの設定
autocmd BufRead *.zip,*.gz,*.bz2,*.xz,*.pdf setlocal readonly nolist
    \| nn <buffer> j <C-E> | nn <buffer> k <C-Y>
```

1行ずつ日本語にしてみます。

* `autocmd BufRead *.zip,*.gz,*.bz2,*.xz,*.pdf`: 圧縮形式(zip, gz, bz2, xz)やPDF(pdf)拡張子のファイルをバッファに読み込むときに次のコマンドを実行します。
  * 圧縮ファイルに関しては、PDFをtext変換→圧縮したのち、vimで開く際に使います。こちらも合わせてお読みください。[Qiita - ターミナル上でPDFのテキストを読む](https://qiita.com/u1and0/items/f8b53348544717d7635d)
* `setlocal readonly nolist |`: 現在のバッファのみ(setlocal)、読込専用(readonly)にして不可視文字を表示しない(nolist)ようにして、次のコマンドを実行します。
* `nn <buffer> j <C-E> | nn <buffer> k <C-Y>`: カーソル上下移動(<kbd>j</kbd>/<kbd>k</kbd>)を画面移動(<kbd>C-E</kbd>/<kbd>C-Y</kbd>)に割り当てます。
  * nnはnnoremapの略です。
  * バッファローカルなキーマップの指定はmapの引数に`<buffer>`を指定します。

`<buffer>`について、vimヘルプより

```vim:map-local
カレントバッファだけで使用できるマップを作成するには、マップコマンドの引数に"<buffer>" を指定します。例:
        :map <buffer>  ,w  /[.,;]<CR>
この場合、他のバッファで、",w" に対して別の操作を割り当てることができます:
        :map <buffer>  ,w  /[#&!]<CR>
```


> **不可視文字の設定**
> 私のvimは改行や意味のない空白を表示するために次のように設定しています。
>
```vim
set list  " 不可視文字の表示
set listchars=tab:»-,trail:-,eol:↲,extends:»,precedes:«,nbsp:%
```

> PDFを読むときは編集するわけでもないし、ない方が綺麗だと思ったので不可視文字を非表示にしました。


# 弱点
* 代替バッファにバイナリで開いたPDFが残ります。autcmdで `| buload "#"`とつなげて消したかったのですがうまくいかず…。commandを設定する方法[^2]では代替バッファが残りません。
* PDFのファイルサイズが大きいとテキスト変換に時間がかかります。これはpdftotextの問題です。


[^1]: 手前味噌ですが、pdftotextについて記事を書いています。圧縮形式で保存する方法についても触れています。[Qiita - ターミナル上でPDFのテキストを読む](https://qiita.com/u1and0/items/f8b53348544717d7635d)
[^2]: 参考: [Vim で PDF ファイルを読む](https://rhysd.hatenablog.com/entry/20120828/1346150566)  こちらはautocmdではなく`:Pdf`という自作コマンドを設定する方法です。これだとターミナルから`$ vim something.pdf`としたときに変換してくれないので参考にとどめておきました。無理やりやるなら`$ vim -c "Pdf something.pdf"`ですがタイプが長くなりますし、従来のvimコマンド`:e`や`:tabedit`や`:vsplit`と併用できません。その点、autocmdならバッファに読み込む際に処理を行うので、従来のvimコマンドと相性が良いです。