SATySFi の VSCode 拡張を拡張してみた話

この記事は [SATySFi Advent Calendar 2021](https://adventar.org/calendars/6336) の 20 日目の記事です。

はじめましてこんにちは。今回、既存の SATySFi 用 VSCode 拡張機能である [vscode-satysfi](https://marketplace.visualstudio.com/items?itemName=wraikny.wraikny-satysfi-extension) を拡張し、新たな拡張機能を作成してみたのでご紹介します。

https://marketplace.visualstudio.com/items?itemName=pickoba.satysfi-workshop

リポジトリは[こちら](https://github.com/pickoba/satysfi-workshop)です。

## 追記 : Windows での利用について

Windows で SATySFi を利用する場合、WSL へのインストールがおすすめされています（[参考](https://qiita.com/na4zagin3/items/a6e025c17ef991a4c923)）。この場合、VSCode も [Remote WSL](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-wsl) 経由で使うのが良いでしょう。以下で Language Server や pdf2svg 等外部ツールを利用する機能を紹介していますが、いずれも WSL 上でのインストール・設定をおすすめします。

## 開発の経緯

VSCode で SATySFi 文書を執筆しようと考えた時、現状唯一の選択肢が wraikny さんの [vscode-satysfi](https://marketplace.visualstudio.com/items?itemName=wraikny.wraikny-satysfi-extension) です。しかしながら、こちらの拡張機能はおよそ 2 年前に開発が停止しており、リポジトリも現存しません。そこで、バグ修正や機能追加を行うとともに、PR 等を通じてコミュニティの皆さんからの要望を受け入れられるよう、新たな拡張機能として公開することにしました。SATySFi Workshop の名は LaTeX Workshop にならったものです。

## 機能の紹介

### ビルド機能

![building.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/696152/574871d0-cc55-0aeb-f3b2-82bfb410720d.gif)

わざわざターミナルを開かずとも、GUI 上のボタンやキーボードショートカット（ctrl/cmd+alt+b）からビルドを実行できるようにしました。[vscode-pdf](https://marketplace.visualstudio.com/items?itemName=tomoki1207.pdf) のような VSCode 内の PDF ビューワーと組み合わせることで、出力を随時確認しながら執筆を進めることができます。ビルドが失敗した際にはそのログを確認できるほか、ファイル保存時に自動的にビルドするような設定も可能です。

`.satyh`, `.satyg` 上でビルドを実行すると、ワークスペース内を検索し、最初に見つかった `.saty` ファイルをビルドするという挙動を示すため、複数ファイルからなる文書にも対応できます[^root-file]。

[^root-file]: ワークスペース内に複数の `.saty` ファイルが存在する場合には、そのファイルを明示的に開いてコマンドを実行するか、`build.rootFile` を設定で指定する必要があります。

### 型チェック機能

![type-checking.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/696152/129e0437-df38-e984-c805-144cbed9693a.png)


ファイルの保存時やタイプ時など好きなタイミングで型チェックを実行できます。内部的には satysfi コマンドを `--type-check-only` で走らせているだけで、ビルド機能とほぼ同一です。こちらは元々 vscode-satysfi に存在した機能ですが、コマンドを非同期に実行するように変更しました。

### パッケージ名の補完

![completion.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/696152/3f6c1a8d-7138-ea1e-84d8-488ab0c650a9.gif)

`@require`/`@import` においてパッケージ名が補完できるようにしました。こちらも実装は単純で、`@require` の場合は `~/.satysfi/dist/packages`、`@import` の場合はそのファイルが存在するディレクトリを参照しているだけです[^require]。後述の Language Server と機能が被らない範囲で便利なものということで実装しました。

[^require]: `@require` は `~/.satysfi/local/packages` とかも見ないといけないのですが現状は見てません（今後改修予定）

### Language Server 対応

![languageServer.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/696152/36682c5f-dc46-a3e6-2072-f37d553ce8d0.gif)


[12/10 の monaqa さんの記事](https://zenn.dev/monaqa/articles/2021-12-10-satysfi-language-server)で紹介されていた Language Server にも対応しています。といっても私は受け口を作っただけなので、Language Server 自体のダウンロード・ビルドは別に必要です。Language Server があると無いとでは快適さが段違いです…！

<details>

<summary>設定方法の詳細</summary>

SATySFi の Language Server は Rust で記述されているため、Rust の環境が必要です。インストール方法は[公式サイト](https://www.rust-lang.org/ja/tools/install)等を確認してください。上述の通り、Windows の場合は WSL にインストールすることをおすすめします。

Rust の環境ができたら、後は以下のコマンドを入力するだけで Language Server がインストールできます。アップデートする際も同じコマンドです。

```console
cargo install --git https://github.com/monaqa/satysfi-language-server
```

VSCode 側の設定は以下の通りです。インストール先（`~/.cargo/bin`）には通常パスが通っているため、2行目の設定は多くの場合不要です。

```json
{
  "satysfi-workshop.languageServer.enabled": true,
  "satysfi-workshop.languageServer.path": "/path/to/language server"
}
```

</details>

なお、Language Server を利用する場合は、ビルトインのスニペットはオフにすることをおすすめします。SATySFi のファイルを開いた状態でコマンドパレットから「スニペットの挿入（Insert Snippet）」を選択し、各要素の右側にある目のアイコンをクリックすることで On/Off が可能です[^snippet]。また、スニペットの入力中（Tab キーで括弧間を移動できる状態）でも補完が出るようにしたい場合には、以下を個人設定に追加してください（VSCode 1.79.0 以降ではデフォルト設定のため不要）。

```json
{
    "editor.suggest.snippetsPreventQuickSuggestions": false
}
```

[^snippet]: ここで On/Off が連動するものがあるのですが、どういうグループ分けになっているのか不明です。

### 数式プレビュー （v1.4.0 以降）

![math-preview.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/696152/2b1a6a0e-52ac-c2a3-a89e-6e8cc94bdbae.png)

プログラム中の数式にマウスカーソルをホバーさせることで、プレビュー表示が可能です。 ファイル冒頭の `@require`/`@import` を踏まえてレンダリングするため、任意のパッケージやコマンドが利用できます。

なお、この機能の利用には [pdf2svg](https://github.com/dawbarton/pdf2svg) のインストールが必要です。Ubuntu (WSL 含む) の場合は `sudo apt install pdf2svg` で、macOS の場合は `brew install pdf2svg` でインストールできます。VSCode 上で必要な設定は以下の一行だけです。

```json
{
  "satysfi-workshop.mathPreview.when": "onHover"
}
```

なお、数式の抽出を含むプログラムの構文解析には monaqa さんの [tree-sitter-satysfi](https://github.com/monaqa/tree-sitter-satysfi) を利用させていただいております。

## その他特筆すべき点

### シンタックスハイライトの改善

SATySFi の Lexer はかなりステートフルなので、シンタックスハイライトは意外と大変です。単にマークアップ言語として SATySFi を利用している場合にはあまり問題になりませんが、繰り返しモードが遷移するような記述をするとシンタックスハイライトが崩れてしまうことがありました。VSCode でシンタックスハイライトに利用される TextMate 文法は表現力が高くない（行レベルパターンマッチしかできない）ので、ある程度は仕方がないかとも思いましたが、マッチの仕方を工夫することで大抵の文書には対応できるようになったのではないかと思います。

### 開発環境の更新・改善

開発環境も 2 年前のものだったので、そちらの更新も進めました。各種依存パッケージの更新・Linter の変更（tslint→eslint）・Formatter の追加（prettier）が主な内容です。さらに（数は少ないですが）テストを追加し、GitHub Actions で main への push 時や PR 時に自動的に実行されるよう設定しました。

## 終わりに

Advent Calendar に登録されている方が強い方ばかりなので少々ビビってますが笑、「始めたばかりの方が書く記事も大歓迎」との言葉を信じて書かせていただきました。SATySFi のことを知ったのは 10 月頃で、まだ研究室内発表用のスライド作成程度にしか利用していませんが、この型で守られた快適さを知ってしまうともう戻れませんね。この拡張機能もそのスライド作成の過程で開発したもので、現状最も快適な SATySFi 執筆環境になったと自負しています。OSS を公開するのが初めてなので至らない点あるかと思いますが、この拡張機能を通じて SATySFi の良さを知っていただけたら幸いです。

## 更新履歴

### 2023-06-09

* VSCode 1.79.0 におけるデフォルト設定の変化について追記

### 2023-03-20

* 「Windows での利用について」の節を追加
* Language Server のインストール方法の詳細を追加
* 「数式プレビュー」の節を追加