VSCode拡張のLaTeX Workshopにコントリビュートした話


これは「[TeX ＆ LaTeX Advent Caleandar 2018](https://adventar.org/calendars/3382)」の5日目の記事です。
（4日目は [golden_lucky](http://golden-lucky.hatenablog.com/entry/2018/12/04/183000)さん、6日目は @uwabami さん です。）

VSCode拡張の[LaTeX Workshop](https://marketplace.visualstudio.com/items?itemName=James-Yu.latex-workshop)にコントリビュートしました。

* MathJax でレンダリングされた数式が  hover に表示される機能。
* PDFビューアで横の margin を消す trim mode。
* \ref や \eqref の hover に SyncTeX 用のリンクを表示する。

などを実装し pull request として投げてマージされました。まだマージされていませんが

* \ref 上の hover に数式を表示する。
* SyncTeX コマンド相当の機能の実装。

なども実装して PR を投げました。

## MathJax でレンダリングされた数式が  hover に表示される機能

こういうやつです。

![Nov-24-2018 18-12-35.gif](https://qiita-image-store.s3.amazonaws.com/0/310575/f2009071-edda-6986-f1ef-b268124d75f4.gif)

TeXstudioでも同様の機能があります。実装してから知りました。TeXstudioでは dvipng コマンドを使って実装されてますが、LaTeX Workshop では MathJax と [MathJax-node](https://github.com/mathjax/MathJax-node) を使って実装しています。VSCode の hover の内容には現在のところ Markdown しか指定できませんので、MathJax で SVG を生成して dataURL に変換して Markdown 中の画像として表示しています。キャッシュは使わず、毎回動的に画像を生成しています。

ユーザからの評判は良いようです。

デフォルトでは数式中にカーソルがある場合、カーソルもダブルダガーとして表示します。長い数式のどこを編集しているかを見失うことがよくあるので、それの対策として表示するようにしました。設定でオフにできます。

## PDFビューアで横の margin を消す trim mode

こういうやつです。

![Nov-24-2018 18-24-25.gif](https://qiita-image-store.s3.amazonaws.com/0/310575/febea5df-d8c2-52d3-ecff-b0cae5dbbd2b.gif)


TeXstudioでも同様の(もっとちゃんとした)機能があります。実装してから知りました。LaTeX Workshop の PDF ビューアは Mozilla の [pdf.js](https://github.com/mozilla/pdf.js) と付属の viewer.js と viewer.html に SyncTeX などのために少し手を加える形で実装されています。これにさらに手を加えて trim mode を実装しました。各ページを少し拡大した上で CSS left プロパティを適当なマイナスの値に設定することで実現しています。あとその過程で LaTeX Workshop が依存する pdf.js を v1.x 系から最近リリースされたv2.0系の初の安定版 v2.0.943 にアップグレードしました(これ以前の v2.0.x は実は安定版ではなかったのです)。

この機能は自分で使っていても非常に便利だと感じます。

## \ref や \eqref の hover に SyncTeX 用のリンクを表示する

こういうやつです。`\ref`が参照している数式へ SyncTeX できます。下で説明するコマンドリンクを使って実装しています。

![Nov-24-2018 19-23-45.gif](https://qiita-image-store.s3.amazonaws.com/0/310575/7021203f-e091-85fa-fe71-06d65f800abd.gif)

ちなみに`\ref`が参照している数式をLaTeX文書ファイル側で見たい場合は、VSCode の Go to Definition や Peek Definition などの機能を使います。

入力ファイルが複数ある場合に対応してなかったのでこれから修正します。

## \ref 上の hover に数式を表示する

こういうやつです。

![Dec-03-2018 11-46-33.gif](https://qiita-image-store.s3.amazonaws.com/0/310575/7a31d2c8-c4ac-39eb-bf20-18bb7b00b0f1.gif)

\ref や \eqref が数式を参照している場合、MathJaxでレンダリングして表示します。そこそこ便利です。これも入力ファイルが複数ある場合に対応していません。時間があれば対応します。

## SyncTeX コマンド相当の機能の実装

SyncTeX が同梱されていない MikTeX ユーザーから SyncTeX が動作しないという問い合わせが多いため、MikTeX ユーザーのために SyncTeX コマンドのバイナリを LaTeX Workshop に同梱しようという案が出ました。それはそれでサポートが大変そうなので、SyncTeX コマンド相当の機能を TypeScript で実装しました。検索してみると [JavaScript で書かれた SyncTeX ファイルのパーサが公開](https://github.com/tdurieux/synctex-js)されていたのでそれを使いました。探してみるもんですね。実装アルゴリズムはオリジナルのSyncTeXのものを再現せず適当に済ませてあります。PDFファイルからLaTeX文書ファイルへのジャンプの精度は要改善です。

## VSCode 拡張作成 Tips

以下は上記の機能を実装中に知った VSCode 拡張作成 Tipsです。

### ユーザが使用中のVSCodeテーマがダークかどうかを取得する

画像を動的に生成して表示する場合、ユーザが使用中の VSCode テーマがダークかどうかによって色を変える必要が出てきます。しかしながら現在ユーザが使用中のVSCodeテーマがダークであるかどうかを取得するAPIは[提供されていません](https://github.com/Microsoft/vscode/issues/32813)🤔。ユーザが使用中のテーマの名前自体はAPIから取得できるので、テーマ(と拡張機能)のインストールフォルダからテーマが記述されたの json ファイルを直接読むことで LaTeX Workshop ではこの問題を回避しています。これは LaTeX Workshop 作者の James Yu さんのアイディアです。 uiTheme プロパティの値が `"vs"`, `"vs-dark"`, `"hc-black"` のいずれであるかで[判断](https://github.com/Microsoft/vscode/blob/master/extensions/theme-defaults/package.json)するのが今のところ無難でしょうか。

### コマンドリンク

ドキュメントにはさらっと書かれているだけですが、 [isTrusted](https://code.visualstudio.com/docs/extensionAPI/vscode-api#MarkdownString.isTrusted) フラグが true である Markdown ドキュメントでは `command` をスキームとして持つコマンドリンクが利用可能です。  Markdown ドキュメント中の `[link](commmand:commandName)` をユーザがクリックするとコマンド `commandName` が実行されます。 実際に呼び出されるのは `vscode.registerCommand(commandName: string, callback)` で `commandName` に登録した関数 `callback` です。  isTrusted フラグはデフォルトでは false ですので、明示的に true に指定した場合しかこの機能は有効になりません。`commandName` に引数を渡したい方法は、さらにどこにも書かれてないのですが、[テスト](https://github.com/Microsoft/vscode/blob/master/src/vs/editor/test/browser/services/openerService.test.ts)を見る限り

```TypeScript
vscode.Uri.parse('command:commandName').with({ query: '123' })
vscode.Uri.parse('command:commandName').with({ query: JSON.stringify([123, true]) })
```

みたいに JSON 形式のシリアライズを使って書くようです。

### ctrl + mouse click をショートカットとして登録する

[できません](https://github.com/Microsoft/vscode/issues/3130)🤔。

### hover の横幅の最大値を大きくする

[できません](https://github.com/Microsoft/vscode/issues/14165)🤔。

### ソースの構文情報を取得する

[できません](https://github.com/Microsoft/vscode/issues/580)🤔。

## LaTeX に関する質問

この記事を読まれている方は TeX や LaTeX に詳しい方が多そうですので質問です。LaTeX文書ファイルのパスやファイル名に`%`を含む場合 `latex` などの処理系が extended mode に入ってしまうのですがこれってどうやったら回避できるのでしょうか。

【追記】ありがとうございます。理解できました。

- [%.texという名前のファイルをLaTeXで処理する方法](http://golden-lucky.hatenablog.com/entry/2018/12/05/110337)
- [TeX コマンドの引数を理解したい話](http://d.hatena.ne.jp/zrbabbler/20151101/1446339017)

## まとめ

Webにおける技術のコアのひとつは文字列を操作してどうブラウザ画面上に表示するかであり、その意味において JavaScript や node.js のエコシステムはエディタと非常に親和性が高いように思いました。あと TeXstudio すごい。