Starship でイマドキのシェルプロンプトを！

# Starship

最近 Mac を新調したのだが、シェル環境も少し変えてみようと思っていたところ **Starship** という新し目？のプロンプトを見つけたので使ってみることにした。
高速に動作し、カスタマイズも柔軟かつ簡単にできるということで、ワクワクしている:rocket::star:

https://starship.rs/ja-JP/

## 前提情報

### この Mac について

- MacBook Pro(16インチ、2021)
- Apple M1 Max

### ターミナルアプリ

[iTerm2](https://iterm2.com/)

### シェル

zsh(Mac デフォルト)

```sh
$ zsh --version
#zsh 5.9 (x86_64-apple-darwin23.0)
```

## インストール

インストール方法は複数あるが、私は homebrew 経由で行った。

```sh
$ brew install starship

$ starship --version
#starship 1.16.0
#branch:master
#commit_hash:
#build_time:2023-07-30 13:35:43 +00:00
#build_env:rustc 1.71.0 (8ede3aae2 2023-07-12),stable-x86_64-apple-darwin
```

また、初期化のためのスクリプトをシェルの設定ファイルに追加する必要がある（最後に追記）。

```sh:~/.zshrc
eval "$(starship init zsh)"
```

## 基本設定

詳しいガイドは以下に詳しい。

https://starship.rs/ja-JP/config/#%E8%A8%AD%E5%AE%9A

こちらを参考に、私が行った設定を紹介したいと思う。

### 用語の整理

まずは Starship を使用する上でこれだけ知っていれば十分という言葉たちを紹介する。
[こちら](https://starship.rs/ja-jp/config/#%E7%94%A8%E8%AA%9E) に正式な説明があるが、より噛み砕いた解釈をすると以下のようになる。

#### モジュール

ある **コンテキスト**[^1] に対して、プロンプトの設定内容をまとめたもの。
一つのモジュールには、通常複数の[オプション](#オプション)と[変数](#変数)が含まれ、これらを駆使して好みの設定を構築する。

[^1]: ここでの **コンテキスト** とは、例えばカレントディレクトリが「Node.js プロジェクトである」や「Git 管理されている」などのことである。


Starship は、OSのコンテキスト情報に基づいてカレントディレクトリにどのモジュールを適用すべきかを判断する。
例えば、Node.js のモジュールが適用されるには、カレントディレクトに **「package.json があること」** や **「node_modules があること」** などいくつかの条件がある。
また、この判断基準は `detect_files` と `detect_folders` および `detect_extensions` というオプションによってカスタマイズできる。詳しくは [ここ](https://starship.rs/ja-jp/config/#node-js) を参照。


#### オプション

モジュールにおいて、**プロンプトに関する設定値を実際に記述する部分。**
文字のスタイルに関するものや、モジュールの有効無効の切り替えその他色々行える。
また、オプションには大抵の場合デフォルト値が設定されているため、明記しないとそれが適用される。
モジュールにより使用可能なオプションは異なる。

#### 変数

モジュールにおいて、プロンプトの表示内容に使用できる定数のようなもので、**変更は不可**(多分)。
ツールのバージョン番号や Git のブランチ名など色々ある。
一部の変数は、オプションで設定したものを変数として使用できるものがある。[^2]
モジュールにより使用可能な変数は異なる。

[^2]: symbol や style など

### 設定ファイル

> Starshipの設定を開始するには、~/.config/starship.toml ファイルを作成します。

とのことなので、まずは作成する。

```sh
$ mkdir -p ~/.config && touch ~/.config/starship.toml
```

:::note info
`toml` とは:
設定ファイルのフォーマットの一種であり、構文は `key="value"`、`[テーブル]`、`#コメント` の3種類の記述から構成される。
大体の使用法としては、コメントはいわゆるコメントで、`[テーブル]` の下に複数の `key=value` を記載していき、そのようなテーブルのセットが複数存在するような形となる。
:::


### ひたすら設定を書いていく

私の設定ファイルはこのような形になっている:

```starship.toml
continuation_prompt = '[❯❯ ](fg:blue)'

[directory]
style = "fg:blue"
format = "[$path]($style)"
truncation_length = 4
truncation_symbol = "..."
truncate_to_repo = false

[character]
success_symbol = '[❯❯❯](fg:blue)'
error_symbol = '[❯❯❯](fg:red)'


[git_status]
style = "fg:blue"
format = '([\[$all_status$ahead_behind\]]($style))'


[git_branch]
format = " [$symbol $branch]($style)"
style = "fg:#888888"
symbol = ""


[git_metrics]
disabled = false
added_style = 'bold blue'
format = '\([+$added]($added_style)/[-$deleted]($deleted_style)\)'
only_nonzero_diffs = true


[package]
disabled = true


[nodejs]
style = "#ffc0cb"
format = " [$symbol $version]($style)"
symbol = ''


[python]
detect_extensions = ['py', 'ipynb']
pyenv_version_name = true
style = "yellow"
format = ' [${symbol}${pyenv_prefix}(${version} )(\($virtualenv\) )]($style)'


[java]
format = ' [${symbol}(${version} )]($style)'


[cmd_duration]
show_milliseconds = true
format = ' [󱫌 $duration]($style)'
#min_time = 10
```

### 全体感
まず、`[テーブル]` 部分は、Starship では、モジュール単位で大方割り当てられている。
例えば、`[nodejs]` に対しては、Node.js に関するプロンプトの設定を、`[git_branch]` には git のブランチに関するプロンプトの設定を行う。
ただし、`continuation_prompt = '[❯❯](fg:blue)'` という記述は唯一、テーブルに含まれていない。
これは**特定のモジュールに紐づく設定ではなく、プロンプト全体に効いてくる設定項目となる。**

:::note warn
`󱫌` の表示について:
特別なフォントにしか存在しない文字を表示するために、`nerd` というフォントをインストールする必要がある。（ブラウザおよび Qiita Web ページが）nerd 対応していないため、`󱫌` のような表示となっていると思われる。
後述する symbol に設定する記号には、なるべくアイコニックなものをあてがった方がプロンプトとして識別しやすくなるが、この記号（文字）が特殊であるので対応したフォントを入れる必要がある、というお話である。
ちなみに、私は[こちら](https://www.nerdfonts.com/font-downloads)を吟味した結果、 `CodeNewRoman Nerd Font` というものをチョイスした。
<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/671630/5ee92eae-2737-a24d-072c-90a68ac5855c.png">
:::

### 各モジュールの共通設定

#### 代表的なオプション

設定可能なオプションはそのモジュールによって異なるが、共有なものも多い。
以下に挙げたものは大体のモジュールに存在している。（気がする）
そして、これらでほぼプロンプトの見た目が決定されると言っても過言ではない。（気がする）

- `style`: [表示スタイルの設定](https://starship.rs/ja-JP/config/#%E3%82%B9%E3%82%BF%E3%82%A4%E3%83%AB%E3%81%AE%E8%A8%AD%E5%AE%9A)。テキストの文字色/背景色、テキストのスタイルなどを設定する。
    - `fg`/`bg`: foreground/background で、それぞれ文字色/背景色を指定する
        - `fg:<color>`/`bg:<color>` という形式で、<color> の部分には以下のフォーマットがある:
            - 色名による指定(e.g. `style = "fg:blue"`: ※iTerm2 の **Settings > Profiles > Colors > ANSI Colors** に対応)
                <img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/671630/664ee7ef-ddaf-bce7-e41b-cdd146b085ed.png">

            - カラーコードによる指定(e.g. `style = "bg:#123456"`)
            - [ANSI Color](https://i.stack.imgur.com/KTSQa.png) での指定
    - `bold`: 文字を **太字** にする(e.g. `style = "yellow bold"`: 黄色の太字)
    - `italic`: 文字を _イタリック_ にする(`style = "yellow bold italic"`: 黄色の太字のイタリック)
    - `underline`: 文字を  <ins>下線あり</ins> にする(`style = "yellow bold italic underline"`: 黄色の太字のイタリック下線付き)
    - これらは組み合わせることができる:
        - e.g. `style = "italic fg:blue bg:#555555"`  
            <img width="200" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/671630/abc4f747-2c30-59dd-e943-c9369c43d1b7.png">

- `symbol`: プロンプトの内容を一眼で理解しやすくするべく、（主に）記号などを割り振る。
    - e.g. `symbol = "😇"`, `symbol = "🎄🎄🎄"`
- `format`: style と symbol およびその他オプション、[変数](https://starship.rs/ja-jp/config/#%E7%94%A8%E8%AA%9E)を用い、最終的にプロンプトとして表示されるものを表現する。用法は、`symbol` や `branch` などのオプションおよび変数を `[]` で囲み、続けて、当てる `style` を `()` で囲む。
    - e.g. `format = " [$symbol $branch]($style)"`
- `disabled`: モジュールの有効/無効の切り替え


### 設定例

プロンプトの紹介と、それを実現するためのモジュールの設定を紹介する。
基本的には、[ひたすら設定を書いていく](#ひたすら設定を書いていく)で記載した内容の部分的な再掲載となる。

#### 1. Git で管理されている Node.js プロジェクト

<img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/671630/c1cb8e7c-aed9-288f-dbf8-18edac09fc33.png">

##### 使用モジュール

- **[directory](https://starship.rs/ja-jp/config/#%E3%82%AA%E3%83%95%E3%82%9A%E3%82%B7%E3%83%A7%E3%83%B3-19)**
    - `style = "fg:blue"`
    - `format = "[$path]($style)"`: `$path` はカレントディレクトリへのパスを表す変数
    - `truncation_length = 4`: カレントディレクトリに対して、表示上の親フォルダの最大表示数。これを超えると、超えた分 `truncation_symbol` に置き換わる。
    - `truncation_symbol = "..."`: 同上
    - `truncate_to_repo = false`: カレントディレクトリが Git リポジトリの場合、ルートのみではなく全パス表示する。(`truncation_length` による制約内において)

- **[nodejs](https://starship.rs/ja-jp/config/#node-js)**
    - `style = "#ffc0cb"`
    - `format = " [$symbol $version]($style)"`: `$version` は Node のバージョン番号
    - `symbol = `'<img width="20" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/671630/f805828c-b027-090b-78d9-ad9ff94b442d.png">' **※nerd フォント**

- **[git_branch](https://starship.rs/ja-jp/config/#git-branch)**
    - `format = " [$symbol $branch]($style)"`: `$branch` は、ブランチ名
    - `style = "fg:#888888"`
    - `symbol = `'<img width="20" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/671630/c96a5fab-be4c-acf5-ebbe-654f683750ae.png">': **※nerd フォント**


- **[git_metrics](https://starship.rs/ja-jp/config/#git-metrics)**
    - `disabled = false`: デフォルトでは true になっている
    - `added_style = 'bold blue'`
    - `format = '\([+$added]($added_style)/[-$deleted]($deleted_style)\)'`: `$added` と `$deleted` はそれぞれ、現在の状態の追加された行数および削除された行数で、`$added_style` と `$deleted_style` はこれらに当てるスタイル。例では、追加行数は青で、削除行数は赤で見せている。
    - `only_nonzero_diffs = true`: 追加/削除行数が 0　の場合、数字を出さない


#### 2. 実行時間が一定以上かかったコマンド

<img width="240" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/671630/64a123dc-01d4-fdac-9130-f43831a8488b.png">

##### 使用モジュール

- **directory**
    - [Git で管理されている Node.js プロジェクト](#Gitで管理されているNode.jsプロジェクト) と同じ

- **[cmd_duration](https://starship.rs/ja-JP/config/#command-duration)**
    - `show_milliseconds = true`
    - `format = ' [󱫌 $duration]($style)'` ※`󱫌` == <img width="20" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/671630/8b90fd1a-100b-551a-f924-9c443d59f4b5.png">(nerd フォント): `$duration` はコマンドの実行時間を表す変数。
    - `min_time = 1000`: 実行時間を表示する最短期間（ミリ秒単位）

おまけ: **CodeNewRoman Nerd Font** ではこのような楽しい時計文字が用意されている:clock1::clock2::clock3:
<img width="200" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/671630/5f834157-b8fc-e65d-d128-ab68a0055866.png">


## 高度な設定

### ウィンドウのタイトルの変更

ターミナルのウィンドウタイトルと、タブタイトル(複数開いている場合)を変更する。
`.zshrc` に以下を追加する。

```diff_shell
# 「eval "$(starship init zsh)"」 の直前に追加
# カレントディレクトリをタイトルに表示する
+ function set_win_title(){
+    echo -ne "\033]0; $(basename "$PWD") \007"
+ }
+ precmd_functions+=(set_win_title1)

eval "$(starship init zsh)"
```
このような見た目となる。


<img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/671630/da94a2d3-7d6e-f846-c618-dd074b677e99.png">


### Continuation Prompt

`continuation propmt` とは、ユーザーが不完全な入力を行った際に表示されるプロンプトのことである。
例えば、`(`　や `"` などの閉じる方がない状態でエンターを押した場合に、このプロンプトが適用される。
これは特定のモジュールに属さず、(おそらく)すべてのモジュールに対して有効となるオプションである。

```diff_toml
+ continuation_prompt = '[❯❯ ](fg:blue)'

...

[character]
success_symbol = '[❯❯❯](fg:blue)'
```

「まだ入力をしています」ということをわかりやすくできる。

<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/671630/c2644849-a10f-0ba1-a6b8-47ebf90b8518.png">


### フォーマットの中に複数のスタイルやシンボルを入れてみる


```diff_toml
[character]
- success_symbol = '[❯❯❯](fg:blue)'
+ success_symbol = '[❯](#cbc547)[❯](#b9c42f)[❯](#c3d941)[ ここにコマンド入れてね！](#666666)'
```

最後のメッセージはお遊びだ。

<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/671630/e13d83a3-3c68-9533-b618-d8084bcab6f7.png">


## Starship の優れているところ

- 設定がシンプル
    - 上述の通り、 Starship は設定内容を全て単独のファイル(starship.toml)に集約できる。他のプロンプト(e.g. pure prompt)では、設定内容を .zshrc に直書きしたりするので、汚くなる。
- 軽快な動作
    - Starship は Rust 製ということで、非常に軽快に動作する。手持ちのマシンで確認したところ、ターミナルの新規タブを起動するのに 0.5 秒ほどであるが、pure prompt の場合 1 秒以上かかっているようであった。
- .zshrc リロードが不要
    - 設定ファイルを更新・保存後、直ちにプロンプトに反映され、`source ~/.zshrc` を実行する必要がないのが地味に嬉しい。


## まとめ

多くのプロンプトに触れた経験があるわけではないが、とてもライトに使いやすいものだと感じた。
この記事を読んでいただいた方から、新たな Starshipper が誕生することを期待している。