iTerm2 のステータスバーで動くコンポーネント作る

iTerm2 のステータスバーについては下記の記事ですでに書きました。

* [iTerm2 にステータスバーが付いた - Qiita](https://qiita.com/delphinus/items/1748937aefeb241bdcee)

ですが改めて読んでみると、例に挙げたバッテリー表示コンポーネントはちょっと内容が複雑すぎた感があります。実は **Python スクリプトなしでもコンポーネントは自作できるんです。**それについてもまとめてみました。

# ロードアベレージを表示するコンポーネント

<img width="506" alt="スクリーンショット 0001-08-11 10.30.58.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/8880/81b69f13-95cd-923d-b148-b61753aba1fb.png">

* [LoadAverage(ロードアベレージ)について - Qiita](https://qiita.com/toshihirock/items/c121400dd58c5d77f85a)

これです。３つの数字が並んでるやつ。これを表示してみましょう。

## シェルからコマンドで表示する

iTerm2 の様々な機能にアクセスするにはエスケープシーケンスを使うのが基本です。

* [Proprietary Escape Codes - Documentation - iTerm2 - macOS Terminal Replacement](https://www.iterm2.com/documentation-escape-codes.html)

> （抄訳）
> SetUserVar
> `^[]1337;SetUserVar=Ps1=Ps2^G`
>
> ユーザー定義変数に値をセットします。iTerm2 はキーと値の辞書を保持し、内部で文字列の置換に利用します。
> Ps1 はキーです。
> Ps2 は base64 にエンコードされた値です。

このエスケープシーケンスを使うと、iTerm2 の内部変数に値を設定することができます。内部変数を表示できるコンポーネントはすでに用意されていますので追加してみましょう。

<img width="659" alt="スクリーンショット 0001-08-11 11.04.55.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/8880/73b90b94-cad1-95cb-c998-388f88fd289b.png">

ステータスバーの設定画面（**Preferences → Profiles → Session → Configure Status Bar**）で **Interpolated String** という名前のコンポーネントを追加し、`\(user.load_average)` という値を設定しておきます。

この上で、シェル上で以下のコマンドを叩いてみましょう。[^1]

[^1]: Tmux 内から叩こうするとこれは失敗します。Tmux を透過しエスケープシーケンスを iTerm2 に届かせるには `printf "\ePtmux;\e\e]1337;SetUserVar=%s=%s\a\e\\\\"` などとすれば良いでしょう。

```sh
printf "\e]1337;SetUserVar=%s=%s\a" load_average "$(echo -n 'hoge' | base64)"
```

<img width="506" alt="スクリーンショット 0001-08-11 11.09.54.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/8880/bed12991-6bde-5c17-b184-541e84f20940.png">

ステータスバーに `hoge` という文字列が表示されていますね。これを使ってロードアベレージを表示してみます。macOS では `w` コマンドでロードアベレージを得られますから、以下のように書けます。

```sh
printf "\e]1337;SetUserVar=%s=%s\a" load_average "$(w | head -n1 | cut -d: -f4 | tr -d "\n" | base64)"
```

<img width="506" alt="スクリーンショット 0001-08-11 11.19.09.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/8880/b3103777-6911-7b59-d769-a3ffa15db0c6.png">

ただ、これでは一回表示しただけで、自動で更新はされません。例えば5秒ごとに自動で更新するとすると `while` 構文を使うことになるでしょう。

```sh
sh -c 'while true; do printf "\e]1337;SetUserVar=%s=%s\a" load_average "$(w | head -n1 | cut -d: -f4 | tr -d "\n" | base64)"; sleep 5; done' &
```

最後の `sleep 5` を書き換えれば間隔は自由に調整できます。更新を止めたいときは `kill %1` とでもすれば良いでしょう。

<img width="506" alt="スクリーンショット 0001-08-11 11.26.51.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/8880/486a4b94-6131-e9aa-91af-ec51959070f8.png">

簡単ですね！

## Python でコンポーネントを作る

前節ではエスケープシーケンスを使って単純にコマンドの実行結果を表示してみました。単純なコマンドならそれでも良いですが、ある程度凝ったことをやりたい場合はこの方法を選択することになります。

この場合はシェルのときと違って利用者が自らプロセスを生成する必要はありません。iTerm2 起動時にバックグランドで起動した、Python プロセスが文字列の生成を担当することになります。

事前に、iTerm2 の Python Runtime の準備が必要です。**Scripts → Manage → Install Python Runtime** の順にクリックするとインストールされます。

以下のスクリプト[^2]を `~/Library/ApplicationSupport/iTerm2/Scripts/Autolaunch` というディレクトリに適当な名前で保存すると、コンポーネントの一覧に表示されるようになります。

[^2]: Python Runtime 内のディレクトリを `PYTHONPATH` 環境変数に設定すると、エディタや IDE で入力補完・ドキュメントの参照も可能です。例えば、現行バージョンだと `~/Library/ApplicationSupport/iTerm2/iterm2env-3.7.2/versions/3.7.2/lib/python3.7/site-packages` に設定すると良いようです。

```python
#!/usr/bin/env python3

from os import getloadavg
from iterm2 import Connection, StatusBarComponent, StatusBarRPC, run_forever
from iterm2.statusbar import Knob
from typing import List


async def main(connection: Connection) -> None:
    component: StatusBarComponent = StatusBarComponent(
        "Load Average",
        "Show load average",
        [],
        "0.01 0.12 1.23",
        30,
        "dev.delphinus.load_average",
    )

    @StatusBarRPC
    async def load_average(knobs: List[Knob]) -> str:
        return " ".join(f"{x:.2f}" for x in getloadavg())

    await component.async_register(connection, load_average, timeout=None)


if __name__ == "__main__":
    run_forever(main)
```

文字列を生成してるのは下の方にある `load_average()` という関数です。これを変えることで好きな文字列を表示させることができるでしょう。

<img width="658" alt="スクリーンショット_0001-08-11_11_35_15_mod.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/8880/a32f4a2c-66f7-1efe-0381-c5fde36edb97.png">

うまくここに現れない場合はスクリプトの実行に失敗していますので **Script Console**（**Scripts → Manage → Console**）でエラーを確認してみましょう。

Python API について詳しくは公式のドキュメントを参考にしてください。

* [Python API — iTerm2 Python API 0.26 documentation](https://iterm2.com/python-api/)

# まとめ

iTerm2 でコンポーネントを作ってロードアベレージを表示してみました。Python API を使うには多少 Python の知識が必要ですが、シェルで設定する方法なら文字列を吐くコマンドされあれば可能です。既存のコマンドを使って色々遊べると思いますので試してみてください。