akimo.dev

WezTermでタブに色付きのアイコンを表示しよう

やりたいこと

WezTermで複数のタブを開いていると、どのタブで何をしているのかが分かりにくくなる。そこで、起動中のプロセスに応じたアイコンをタブに表示させたい。ただアイコンを表示するだけだとテキストと同じ色になってしまい味気ないので、アイコン部分だけ色を変えて目立たせるのが今回の目標だ。

WezTermでタブに色付きのアイコンを表示した画像

プロセス名とアイコンの対応関係を定義する

まず使いたいアイコンを次のページから選ぶ。

wezterm.nerdfonts - Wez's Terminal Emulator
wezfurlong.org

そうしたら次のようなテーブルを作る(色はお好み)。

local process_icons = {
  docker = {
    color = "#1d63ed",
    icon = wezterm.nerdfonts.md_docker,
  },
  go = {
    color = "#79d4fd",
    icon = wezterm.nerdfonts.md_language_go,
  },
  nvim = {
    color = "#00b952",
    icon = wezterm.nerdfonts.custom_neovim,
  },
  zsh = {
    icon = wezterm.nerdfonts.dev_terminal,
  },
}

これにより、process_icons["docker"].colorprocess_icons["docker"].iconなどでそれぞれのプロセスに対応する色とアイコンを取得できるようになる。

タブのカスタマイズ

WezTermのタブはformat-tab-titleイベントの発火時に実行される関数でカスタマイズできる1

wezterm.on("format-tab-title", function(tab, _, _, _, _, _)
  -- タブに表示したい内容を返す
end)

以下の設定は基本的にこの関数の中に書いていく。

プロセス名の取得

プロセス名はtab.active_pane.foreground_process_nameで取得できる23が、フルパスなのでファイル名だけを取り出す必要がある。僕はタブにカレントディレクトリを表示するためにも使いたいので、関数として切り出しておく。

---@param path string
local function trim_path(path)
  return string.gsub(path, "(.*[/\\])(.*)", "%2") -- 拡張子を考慮していないのでWindowsでは動かないかも
end

basenameコマンドを使うという手もあるが、Neovimが重くなるように感じたので正規表現にした。ちゃんと調べていないが、キーボードを押下するたびにformat-tab-titleイベントが走っていそうな気がする。

テキストの色の設定

format-tab-titleイベントで実行される関数でstringを返すとデフォルトの色でその文字列が表示されるが、今回はアイコンの色を変えたいのでテキストの色も自分で設定する必要がある。デフォルトの色4を参考に設定するとこんな感じ。

local text_color = tab.is_active and "#c0c0c0" or "#808080"

厳密にはデフォルトだとホバー時にも色が変わるが、上手く動作しなかったので省略した。

タブの表示内容を設定

format-tab-titleの関数からFormatItemのテーブルを返すことでタブの表示内容を設定できる1。ドキュメントを参考にすると、次のような設定で求めている結果が得られるはず。

return {
  {
    {
      Foreground = {
        Color = tab.is_active and process_icons[process_name].color or text_color,
      },
    },
    { Text = process_icons[process_name].icon or process_name .. " on" },
    {
      Foreground = {
        Color = text_color,
      },
    },
    { Text = " " .. trim_path(pane.current_working_dir.file_path) },
  }
}

しかし、これではなぜか閉じるボタンもアイコンと同じ色になってしまう。

閉じるボタンの色も変わってしまった様子

ドキュメントを見ても閉じるボタンの色がどのように決定されるのかについての記述は見つけられなかったが、色々試した結果タブの最初に現れる非空文字列と同じ色になるっぽいということが分かった。なので、最初に閉じるボタンと同じ色のゼロ幅スペースを入れることで対処できる。

return {
  {
    {
      Foreground = {
        Color = text_color,
      },
    },
    {
      Text = utf8.char(0x200B),
    },
    {
      Foreground = {
        Color = tab.is_active and process_icons[process_name].color or text_color,
      },
    },
    { Text = process_icons[process_name].icon or process_name .. " on" },
    {
      Foreground = {
        Color = text_color,
      },
    },
    { Text = " " .. trim_path(pane.current_working_dir.file_path) },
  }
}

まとめ

最終的には次のような設定になる。

---@param path string
local function trim_path(path)
  return string.gsub(path, "(.*[/\\])(.*)", "%2")
end

---@class Icon
---@field color? string
---@field icon string

---@type { [string]: Icon }
local process_icons = {
  docker = {
    color = "#1d63ed",
    icon = wezterm.nerdfonts.md_docker,
  },
  go = {
    color = "#79d4fd",
    icon = wezterm.nerdfonts.md_language_go,
  },
  nvim = {
    color = "#00b952",
    icon = wezterm.nerdfonts.custom_neovim,
  },
  zsh = {
    icon = wezterm.nerdfonts.dev_terminal,
  },
}

wezterm.on("format-tab-title", function(tab, _, _, _, _, _)
  local pane = tab.active_pane
  local process_name = trim_path(pane.foreground_process_name)

  if not pane.current_working_dir then
    return process_name
  end
  ---@type string
  local cwd = pane.current_working_dir.file_path
  local text_color = tab.is_active and "#c0c0c0" or "#808080"

  return {
    {
      Foreground = {
        Color = text_color,
      },
    },
    {
      Text = utf8.char(0x200B),
    },
    {
      Foreground = {
        Color = tab.is_active and process_icons[process_name].color or text_color,
      },
    },
    { Text = process_icons[process_name].icon or process_name .. " on" },
    {
      Foreground = {
        Color = text_color,
      },
    },
    { Text = " " .. trim_path(cwd) },
  }
end)

Footnotes

  1. format-tab-title - Wez's Terminal Emulator 2

  2. object: TabInformation - Wez's Terminal Emulator

  3. object: PaneInformation - Wez's Terminal Emulator

  4. wezterm/config/src/color.rs at 6f375e29a2c4d70b8b51956edd494693196c6692 · wez/wezterm