Latex-workshopにサポートされていないパッケージ(physics2)の補完を追加する

## Abstract

LatexをVSCodeで書いていますか？もちろん書いていますよね？
VSCodeでLatexを書く上で、Latex Workshopを使っていない人はいないでしょう。

https://marketplace.visualstudio.com/items?itemName=James-Yu.latex-workshop

またVSCodeを用いて論文を書く上で、physicsパッケージを使っている人は多いでしょう。しかしながら、近年では**脱physicsパッケージ**が叫ばれています。

https://qiita.com/Yarakashi_Kikohshi/items/131e2324f401c3effb84

physicsの代わりに**physics2パッケージ**を使おうという提案がなされていますが、実はLatex Workshopからはコマンドや環境の補完が提供されていません。

:::note info
2024/03/14追記: [issue](https://github.com/James-Yu/LaTeX-Workshop/issues/4202)を立てたところ、補完が追加されました！ 対応するcommitは[これ](https://github.com/James-Yu/LaTeX-Workshop/commit/28892b878acc7c2c839b986f1065f13cdf485ed2)です。
:::

補完を増やすためには、専用のjson形式が必要ですが、これについて解説している日本語情報は知る限り一つしかありません。
また既存パッケージに対して、補完のためのjsonファイルを生成する方法を解説した記事はありません。

そこで本記事では、**physics2をパッケージの補完をLatex Workshopに追加する**方法を紹介します。
Latex Workshopに補完を追加するために、専用の形式のjsonファイルが必要です。
そのため、補完のためのphysics2.jsonを、Latex Workshopが提供する公式のPythonスクリプトを用いて生成します。

## 環境

本記事では、下記の環境で作業を行います。

VSCode 1.86.0
Latex Workshop 9.18.0
Python 3.10.9
Windows 10

:::note warn
一番大事なのは、Latex Workshopのバージョンです。
補完ファイルの形式が直近に変更されているようです。
:::

## Latex-Workshop

Latex Workshopは、言わずと知れた最強のLatex用拡張機能です。
通常、`\usepackage{hoge}`とすることで、hogeパッケージのコマンドが補完に出ます。
ただし、もちろんですが自作のスタイルファイルやパッケージの場合は、補完ファイルを自作する必要があります。

https://qiita.com/Yarakashi_Kikohshi/items/165d1180aa515655aebd

ただ、physics2パッケージとか、既存のパッケージの補完jsonファイルを手打ちするのは嫌ですね！
というわけで公式リポジトリを眺めていると、**cwlファイルからjsonを生成する仕組み**がありました。

https://github.com/James-Yu/LaTeX-Workshop/tree/master/dev

Pythonスクリプトの`pkgcommand.py`が、補完用jsonを生成するようです。
つまりcwlファイルさえあればオッケーです！

## LAtex Workshopにパッケージの補完の追加

ファイルの生成と、Latex Workshopが補完ファイルを読み込むフォルダに配置するステップからなります。

### 補完ファイル(physics2.json)を生成

下記の流れで作成します。

1. 公式リポジトリをclone
1. cwlを探してダウンロード
1. 公式リポジトリのdevフォルダーのPythonスクリプトを実行

PowerShellでは次のコマンドに相当します。

```powershell
# 公式リポジトリのclone
git clone https://github.com/James-Yu/LaTeX-Workshop.git
cd LaTeX-Workshop\dev
# 「physics2 cwl」で検索すると、texstudio 用の補完ファイルが見つかりました。
Invoke-WebRequest https://github.com/texstudio-org/texstudio/blob/master/completion/physics2.cwl -OutFile physics2.cwl

# 下準備
python -u .\unimathsymbols.py
mkdir out

# json生成！
python -u .\pkgcommand.py -i .\physics2.cwl -o out
```

これでoutフォルダにphysics2.jsonが生成されています。

<details><summary>出てくるphysics2.jsonの中身</summary>

```physics2.json
{
  "includes": {
    "keyval": [],
    "amsmath": []
  },
  "macros": {
    "usephysicsmodule{}": {
      "snippet": "usephysicsmodule{${1:modules}}"
    },
    "usephysicsmodule[]{}": {
      "snippet": "usephysicsmodule[${2:options}]{${1:modules}}",
      "keyvalindex": 0,
      "keyvalpos": 0
    },
    "ab": {
      "snippet": "ab( $1 )$0"
    },
    "pab{}": {
      "snippet": "pab{${1:arg}}"
    },
    "pab*{}": {
      "snippet": "pab*{${1:arg}}"
    },
    "pab[]{}": {
      "snippet": "pab[${2:size}]{${1:arg}}"
    },
    "bab{}": {
      "snippet": "bab{${1:arg}}"
    },
    "bab*{}": {
      "snippet": "bab*{${1:arg}}"
    },
    "bab[]{}": {
      "snippet": "bab[${2:size}]{${1:arg}}"
    },
    "Bab{}": {
      "snippet": "Bab{${1:arg}}"
    },
    "Bab*{}": {
      "snippet": "Bab*{${1:arg}}"
    },
    "Bab[]{}": {
      "snippet": "Bab[${2:size}]{${1:arg}}"
    },
    "vab{}": {
      "snippet": "vab{${1:arg}}"
    },
    "vab*{}": {
      "snippet": "vab*{${1:arg}}"
    },
    "vab[]{}": {
      "snippet": "vab[${2:size}]{${1:arg}}"
    },
    "aab{}": {
      "snippet": "aab{${1:arg}}"
    },
    "aab*{}": {
      "snippet": "aab*{${1:arg}}"
    },
    "aab[]{}": {
      "snippet": "aab[${2:size}]{${1:arg}}"
    },
    "Vab{}": {
      "snippet": "Vab{${1:arg}}"
    },
    "Vab*{}": {
      "snippet": "Vab*{${1:arg}}"
    },
    "Vab[]{}": {
      "snippet": "Vab[${2:size}]{${1:arg}}"
    },
    "ketbra": {
      "snippet": "ketbra|subformula1>${1:optional}<subformula2|"
    },
    "ketbra*": {
      "snippet": "ketbra*|subformula1>${1:optional}<subformula2|"
    },
    "diagmat{}": {
      "snippet": "diagmat{${1:entry1,entry2,...}}"
    },
    "diagmat[]{}": {
      "snippet": "diagmat[${2:empty=entry}]{${1:entry1,entry2,...}}"
    },
    "pdiagmat{}": {
      "snippet": "pdiagmat{${1:entry1,entry2,...}}"
    },
    "pdiagmat[]{}": {
      "snippet": "pdiagmat[${2:empty=entry}]{${1:entry1,entry2,...}}"
    },
    "bdiagmat{}": {
      "snippet": "bdiagmat{${1:entry1,entry2,...}}"
    },
    "bdiagmat[]{}": {
      "snippet": "bdiagmat[${2:empty=entry}]{${1:entry1,entry2,...}}"
    },
    "Bdiagmat{}": {
      "snippet": "Bdiagmat{${1:entry1,entry2,...}}"
    },
    "Bdiagmat[]{}": {
      "snippet": "Bdiagmat[${2:empty=entry}]{${1:entry1,entry2,...}}"
    },
    "vdiagmat{}": {
      "snippet": "vdiagmat{${1:entry1,entry2,...}}"
    },
    "vdiagmat[]{}": {
      "snippet": "vdiagmat[${2:empty=entry}]{${1:entry1,entry2,...}}"
    },
    "Vdiagmat{}": {
      "snippet": "Vdiagmat{${1:entry1,entry2,...}}"
    },
    "Vdiagmat[]{}": {
      "snippet": "Vdiagmat[${2:empty=entry}]{${1:entry1,entry2,...}}"
    },
    "doublecross": {},
    "doubledot": {},
    "xmat{}{}{}": {
      "snippet": "xmat{${1:entry}}{${2:rows shown}}{${3:cols shown}}"
    },
    "xmat[]{}{}{}": {
      "snippet": "xmat[${4:options}]{${1:entry}}{${2:rows shown}}{${3:cols shown}}",
      "keyvalindex": 1,
      "keyvalpos": 0
    },
    "pxmat{}{}{}": {
      "snippet": "pxmat{${1:entry}}{${2:rows shown}}{${3:cols shown}}"
    },
    "pxmat[]{}{}{}": {
      "snippet": "pxmat[${4:options}]{${1:entry}}{${2:rows shown}}{${3:cols shown}}",
      "keyvalindex": 1,
      "keyvalpos": 0
    },
    "bxmat{}{}{}": {
      "snippet": "bxmat{${1:entry}}{${2:rows shown}}{${3:cols shown}}"
    },
    "bxmat[]{}{}{}": {
      "snippet": "bxmat[${4:options}]{${1:entry}}{${2:rows shown}}{${3:cols shown}}",
      "keyvalindex": 1,
      "keyvalpos": 0
    },
    "Bxmat{}{}{}": {
      "snippet": "Bxmat{${1:entry}}{${2:rows shown}}{${3:cols shown}}"
    },
    "Bxmat[]{}{}{}": {
      "snippet": "Bxmat[${4:options}]{${1:entry}}{${2:rows shown}}{${3:cols shown}}",
      "keyvalindex": 1,
      "keyvalpos": 0
    },
    "vxmat{}{}{}": {
      "snippet": "vxmat{${1:entry}}{${2:rows shown}}{${3:cols shown}}"
    },
    "vxmat[]{}{}{}": {
      "snippet": "vxmat[${4:options}]{${1:entry}}{${2:rows shown}}{${3:cols shown}}",
      "keyvalindex": 1,
      "keyvalpos": 0
    },
    "Vxmat{}{}{}": {
      "snippet": "Vxmat{${1:entry}}{${2:rows shown}}{${3:cols shown}}"
    },
    "Vxmat[]{}{}{}": {
      "snippet": "Vxmat[${4:options}]{${1:entry}}{${2:rows shown}}{${3:cols shown}}",
      "keyvalindex": 1,
      "keyvalpos": 0
    },
    "abs{}": {
      "snippet": "abs{${1:arg}}"
    },
    "abs*{}": {
      "snippet": "abs*{${1:arg}}"
    },
    "abs[]{}": {
      "snippet": "abs[${2:size}]{${1:arg}}"
    },
    "norm{}": {
      "snippet": "norm{${1:arg}}"
    },
    "norm*{}": {
      "snippet": "norm*{${1:arg}}"
    },
    "norm[]{}": {
      "snippet": "norm[${2:size}]{${1:arg}}"
    },
    "eval{}": {
      "snippet": "eval{${1:arg}}"
    },
    "eval*{}": {
      "snippet": "eval*{${1:arg}}"
    },
    "eval[]{}": {
      "snippet": "eval[${2:size}]{${1:arg}}"
    },
    "peval{}": {
      "snippet": "peval{${1:arg}}"
    },
    "peval*{}": {
      "snippet": "peval*{${1:arg}}"
    },
    "peval[]{}": {
      "snippet": "peval[${2:size}]{${1:arg}}"
    },
    "beval{}": {
      "snippet": "beval{${1:arg}}"
    },
    "beval*{}": {
      "snippet": "beval*{${1:arg}}"
    },
    "beval[]{}": {
      "snippet": "beval[${2:size}]{${1:arg}}"
    },
    "order{}": {
      "snippet": "order{${1:arg}}"
    },
    "order*{}": {
      "snippet": "order*{${1:arg}}"
    },
    "order[]{}": {
      "snippet": "order[${2:size}]{${1:arg}}"
    },
    "bm{}": {
      "snippet": "bm{${1:letter}}"
    },
    "grad": {},
    "curl": {},
    "laplacian": {},
    "asin": {},
    "acos": {},
    "atan": {},
    "acsc": {},
    "asec": {},
    "acot": {},
    "Tr": {},
    "tr": {},
    "rank": {},
    "erf": {},
    "Res": {},
    "res": {},
    "PV": {},
    "pv": {},
    "Resymbol": {},
    "Imsymbol": {},
    "qqtext{}": {
      "snippet": "qqtext{${1:text}}"
    },
    "qqtext*{}": {
      "snippet": "qqtext*{${1:text}}"
    },
    "qq{}": {
      "snippet": "qq{${1:text}}"
    },
    "qq*{}": {
      "snippet": "qq*{${1:text}}"
    },
    "qcomma": {},
    "qc": {},
    "qcc": {},
    "qcc*": {},
    "qif": {},
    "qthen": {},
    "qelse": {},
    "qotherwise": {},
    "qunless": {},
    "qgive": {},
    "qusing": {},
    "qassume": {},
    "qsince": {},
    "qlet": {},
    "qfor": {},
    "qall": {},
    "qeven": {},
    "qodd": {},
    "qinteger": {},
    "qand": {},
    "qor": {},
    "qas": {},
    "qin": {}
  },
  "envs": {},
  "options": [],
  "keyvals": [
    [
      "ab.braket",
      "ab.legacy",
      "ab",
      "bm-um.legacy",
      "braket",
      "diagmat",
      "doubleprod",
      "nabla.legacy",
      "op.legacy",
      "qtext.legacy",
      "xmat"
    ],
    [
      "showtop=${1:rows}",
      "showleft=${1:columns}",
      "format=${1:format code}"
    ]
  ]
}
```
</details>


### jsonファイルの配置

拡張機能が補完ファイルを読み込むフォルダにphysics2.jsonを配置します。
Windowsでは、次のようなフォルダに相当します。
C:\Users\TakeMe\\.vscode\extensions\james-yu.latex-workshop-x.x.x\data\packages

これで`\usepackage{physics2}`とするとLatexを書くときに補完が有効になります！
`usephysicsmodule{}`のコマンド自体と読み込むモジュールの選択肢も補完されます！

## 結論

本記事では、Latex Workshopが対応していないパッケージの補完を追加する方法を紹介しました。
特に、pyhsicsの代替として有効なphysics2パッケージに注目して、その補完ファイルの作成を行いました。
本記事の方法は、cwlファイルさえあればどんなパッケージでもLatex Workshopに対応できるので、参考にしてください。

## 参考文献

- LaTeX Workshop を使いこなす
    - Latex Workshopを用いたVSCodeの環境構築記事

https://qiita.com/Yarakashi_Kikohshi/items/a9357dd469320ffb65a0

- LaTeX Workshop のインテリセンスをカスタマイズする
    - マジで聖書
    - 日本語でインテリセンスのカスタマイズについて紹介している記事は貴重

https://qiita.com/Yarakashi_Kikohshi/items/165d1180aa515655aebd

- 脱physics パッケージして physics2 パッケージを使おう
    - 脱physicsパッケージしようという話

https://qiita.com/Yarakashi_Kikohshi/items/131e2324f401c3effb84