😸

zsh + Starship + WezTermで最強のターミナル環境を構築する

2023/05/05に公開1

VSCodeやIntelliJ DEAなどのリッチで高機能な開発環境が最近どんどん進化していっていますが、低機能でシンプルなツールを組み合わせて自分好みにカスタマイズしていく潮流も、決して下火になっているわけではありません。

細かい調整が可能、プログラムの動きを把握しやすい、言語に関わらず共通のインターフェースを使用できる、玄人っぽくて格好いいなど、ターミナル環境での開発の利点は数多くあります。

また、個人的には初学者の方こそリッチなIDEよりもシンプルなターミナル環境からはじめた方がいいと思っています。全てテキストファイルで設定していくので、1つ1つの作業について理解できるという点で遥かに有利です。

version
zsh : 5.9-3
Starship : 1.14.2-1
wezterm : 20230408-112425-69ae8472
OS : Arch Linux

というわけで、開発で使用しているツールのうち特にお世話になっているzsh, Starship, WezTermについて紹介します。

ショートカットキー一覧はこんな感じになります。全て後述する設定ファイルで指定していますので、お好みに合わせて変更してください。

  • zsh
    • Ctrl p/n : コマンドの履歴を検索
  • WezTerm
    • Ctrl q + c : タブを新規作成
    • Ctrl q + q : タブを閉じる
    • Ctrl q + n/p : 次/前のタブを移動
    • Ctrl q + w : タブを一覧表示
    • Ctrl q + v/s : ペインを垂直/水平に分割
    • Ctrl q + x : ペインを閉じる
    • Ctrl q + z : ペインを最大化
    • Ctrl q + h/l/k/j : ペインを左/右/上/下に移動
    • Ctrl q + H/L/K/J : ペインを左/右/上/下に拡大
    • Ctrl Shift x : コピーモード起動
    • Ctrl Shift v : クリップボードから貼り付け

zsh

zshはUNIXやLinuxで使用されるシェルの一種です。Bashみたいなものです。

https://www.zsh.org/

今回の目玉は入力途中のコマンドから履歴を検索する機能なので、そちらに絞って記載しました。設定を.zshrcにコピペして、PATHなどは適宜追加してください。

.zshrc
# コマンド履歴を1万行保存する
HISTFILE=~/.zsh_history
HISTSIZE=10000
SAVEHIST=10000
setopt hist_ignore_dups  # 同じコマンドを履歴に残さない
setopt share_history     # 同時に起動したzshで履歴を共有する

# Ctrl + N/Pでコマンド履歴を検索する
autoload history-search-end
zle -N history-beginning-search-backward-end history-search-end
zle -N history-beginning-search-forward-end history-search-end
bindkey "^N" history-beginning-search-forward-end
bindkey "^P" history-beginning-search-backward-end

コマンド履歴の検索機能について具体的に説明していきましょう。

例えば.zsh_historyの中に下記のコマンド履歴があったとします。

cd Git/Flutter
ls -l
ls -la
flutter doctor
flutter create App1 -t app --org com.example --project-name app1 -a kotlin --platforms web,android --description "Flutter Application 1"
cd App5
flutter run --debug -d web
flutter help run
flutter devices

この時、fluなどと途中まで入力した状態でCtrl n/pを入力すれば、flutter devices, flutter help runなどの履歴を探索して補完してくれます。fだけでもOKです。cと入力した状態であれば、cd Git/Flutterなどのディレクトリ移動の履歴が出てくるでしょう。

flutter create App5 ...といった長いコマンドをもう一度全て入力する必要はありません。オプションの書式を忘れてしまった時でも大丈夫です。

覚えるには面倒だけど、時々しか使わないからaliasに保存しておくほどでもないし、いちいちhistoryコマンドで調べるのも面倒……みたいな時に(要するにいつも)お世話になってます。

開発をしているとDockerやcurlで長々しいコマンドを何度も実行することがあるかと思いますが、過去の実行履歴に手軽にアクセスできるようになるだけでも効率は爆上がりするでしょう。超オススメです。


設定はこちらから拝借しました。多謝

https://unix.stackexchange.com/questions/97843/how-can-i-search-history-with-text-already-entered-at-the-prompt-in-zsh

Starship

プロンプトをカスタマイズするツール(という呼び方でいいのでしょうか?)であるStarshipを使います。Rust製です。

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

.starship.toml
add_newline = false

[character]
success_symbol = "[>](bold green)"
error_symbol   = "[X](bold red)"
vicmd_symbol   = "[V](bold green)"
.bashrc
eval "$(starship init bash)"
.zshrc
eval "$(starship init zsh)"

要件:Nerd Fontの一つがインストールされていて、ターミナルで有効になっていること

とりあえず使うだけならシンプルな設定でOKです。プロンプト文字をASCIIにしているのと、後ろの改行を無効化しているのは個人的な好みです。デフォルトでも違和感なく使用できます。お使いのシェルに合わせてevalコマンドを書いておけば起動します。


プロンプトがおしゃれになるだけでも気分がアガりますが、Starshipの最も素敵な機能はディレクトリ内のプロジェクトの状態を検知してプロンプトに表示してくれる機能です。現在のリポジトリ、ブランチ名、インストールされているプログラムのversionなどを表示してくれます。

毎回git statusなどのコマンドを打たなくても現在の情報がわかるのはとても便利です。

WezTerm

端末はWezTermを使用します。これもRust製です。シンプルな外観と高いカスタマイズ性が特徴です。

https://wezfurlong.org/wezterm/

下記の設定を.config/wezterm/wetzterm.luaにコピペすれば起動します。前半は外観、後半はショートカットキーの設定です。

フォントはPlemolJPを使用していますので、別途インストールしていただくかお好みのものに変更して下さい。

.config/wezterm/wezterm.lua
local wezterm = require 'wezterm';
local act = wezterm.action

return {
  initial_cols = 100,
  initial_rows = 40,
  font_size = 12.0,
  cell_width = 1.0,
  line_height = 1.0,
  color_scheme = "iceberg-dark",
  font = wezterm.font_with_fallback({
    {family="PlemolJP Console NF", weight="Medium"},
  }),
  use_fancy_tab_bar = false,
  hide_tab_bar_if_only_one_tab = true,
  window_background_opacity = 0.95,
  text_background_opacity = 0.95,
  -- window_background_image = "Pictures/IMG_2162.JPG",
  -- window_background_image_hsb = {
  --   brightness = 0.03,
  --   hue = 1.0,
  --   saturation = 0.1,
  -- },
  skip_close_confirmation_for_processes_named = {""},
  window_padding = {
    left = 0,
    right = 0,
    top = 0,
    bottom = 0,
  },
  wezterm.on("format-tab-title", function(tab, tabs, panes, config, hover, max_width)
    return {
      {Text=" " .. tab.active_pane.title .. " "},
    }
  end),
  colors = {
    tab_bar = {
      background = "#1b1f2f",

      active_tab = {
        bg_color = "#444b71",
        fg_color = "#c6c8d1",
        intensity = "Normal",
        underline = "None",
        italic = false,
        strikethrough = false,
      },

      inactive_tab = {
        bg_color = "#282d3e",
        fg_color = "#c6c8d1",
        intensity = "Normal",
        underline = "None",
        italic = false,
        strikethrough = false,
      },

      inactive_tab_hover = {
        bg_color = "#1b1f2f",
        fg_color = "#c6c8d1",
        intensity = "Normal",
        underline = "None",
        italic = true,
        strikethrough = false,
      },

      new_tab = {
        bg_color = "#1b1f2f",
        fg_color = "#c6c8d1",
        italic = false
      },

      new_tab_hover = {
        bg_color = "#444b71",
        fg_color = "#c6c8d1",
        italic = false
      },
    }
  },
  leader = { key="q", mods="CTRL", timeout_milliseconds=1000 },
  keys = {
      { key = "c", mods = "LEADER", action = act.SpawnTab("CurrentPaneDomain") },
      { key = "q", mods = "LEADER", action = act.CloseCurrentTab{ confirm = true } },
      { key = "n", mods = "LEADER", action = act.ActivateTabRelative(1) },
      { key = "p", mods = "LEADER", action = act.ActivateTabRelative(-1) },
      { key = "w", mods = "LEADER", action = act.ShowTabNavigator },

      { key = "v", mods = "LEADER", action = act.SplitHorizontal{ domain = "CurrentPaneDomain" } },
      { key = "s", mods = "LEADER", action = act.SplitVertical{ domain = "CurrentPaneDomain" } },
      { key = "x", mods = "LEADER", action = act.CloseCurrentPane{ confirm = true } },
      { key = "z", mods = "LEADER", action = act.TogglePaneZoomState },

      { key = "h", mods = "LEADER", action = act.ActivatePaneDirection("Left") },
      { key = "l", mods = "LEADER", action = act.ActivatePaneDirection("Right") },
      { key = "k", mods = "LEADER", action = act.ActivatePaneDirection("Up") },
      { key = "j", mods = "LEADER", action = act.ActivatePaneDirection("Down") },
      { key = "H", mods = "LEADER", action = act.AdjustPaneSize{"Left", 10} },
      { key = "L", mods = "LEADER", action = act.AdjustPaneSize{"Right", 10} },
      { key = "K", mods = "LEADER", action = act.AdjustPaneSize{"Up", 5} },
      { key = "J", mods = "LEADER", action = act.AdjustPaneSize{"Down", 5} },

      -- { key = "x", mods = "CTRL|SHIFT", action = act.ActivateCopyMode },
      { key = "v", mods = "CTRL|SHIFT", action = act.PasteFrom("Clipboard") },

      { key = "Enter", mods = "ALT", action = 'DisableDefaultAssignment' },
  },
}


個人的に気に入っているWezTermの利点は、タブ・ペイン機能が標準でついている点です。

冒頭で示したように画面を分割して、エディタやプログラムを同時に開くことができるため、tmuxやscreenのような使い方をすることができます。Ctrl q + s/vでペイン分割して、Ctrl q + cでタブを作成しています。

また、公式ドキュメントがめちゃめちゃ充実していて読みやすい点も好感度高いです。カスタマイズするならググってよさげなものをコピペだけじゃなくてドキュメント読みたくなりますよね。新しいプロジェクトだとドキュメントが存在しない、みたいな場合も時々あるので、こういうところはすごくありがたいです。

https://wezfurlong.org/wezterm/config/files.html

最後に

だいたい1年くらいGoとかRubys on RailsとかTypeScriptの開発をやってきましたが、この環境で困ったことはありません。周りがリッチなIDEを使う人ばかりなので話題が合わない、くらいでしょうか。筆者はLinux環境で使っていますが、Macでもほとんど変わりなく使用できるでしょう。皆様の参考になれば幸いです。