⌨️

[16AnsiColors] wezterm の color selector と nvim 運用

に公開

これ見て試行錯誤していたのだがやっとできた。
wezterm は 1000 以上 builtin の color scheme があるのでメニューで適当に選びたいのである。

https://github.com/wezterm/wezterm/discussions/5144

sed とか fzf に行くと難しくなる。
InputSelector により wezterm で完結する手法が簡単。
fzf なら preview ができてよさそうに思えたので osc1337 経由で挑戦したのだが、
激遅になり断念。

シンプル版

local choices = {}
for k, v in pairs(wezterm.color.get_builtin_schemes()) do
  table.insert(choices, {
    id = k,
    label = k,
  })
end

config.keys = {
  {
    mods = "ALT",
    key = 'F12',
    action = wezterm.action_callback(function(win, pane)
      win:perform_action(wezterm.action.InputSelector({
        title   = 'color_scheme',
        choices = choices,
        action  = wezterm.action_callback(function(inner_window, _, id)
          if id then
            inner_window:set_config_overrides {
              color_scheme = id,
            }
          end
        end),
        fuzzy   = true,
      }), pane)
    end),
  }
}

ちょっとおされな choices

local choices = {}
local function make_pallete(ansi)
  local SGR = '\x1b['
  local str = ''
  for _, v in ipairs(ansi) do
    local r = tonumber(v:sub(2, 3), 16)
    local g = tonumber(v:sub(4, 5), 16)
    local b = tonumber(v:sub(6, 7), 16)
    local add = string.format("%s48;2;%d;%d;%dm ", SGR, r, g, b)
    str = str .. add
  end
  return str
end
for k, v in pairs(wezterm.color.get_builtin_schemes()) do
  table.insert(choices, {
    id = k,
    label = wezterm.format({
      { Text = make_pallete(v.ansi) },
      { Text = make_pallete(v.brights) },
      { Text = '\x1b[0m ' },
      { Foreground = { Color = v.foreground } },
      { Background = { Color = v.background } },
      { Text = k }, }),
  })
end

nvim の color scheme が ansi 16 色を使えばよいのでは?

せっかく wezterm の色を変えても、nvim は独自の色を使っているのが気になった。
以下の記事に ansi 16 色を使う colorscheme を自作する例がある。

https://hamvocke.com/blog/ansi-vim-color-scheme/

以下のようにして、ansi16色を使う colortheme を用意すれば、
wezterm の色を活かせる。

" Force vim to use 16 colors only
set notermguicolors

TreeSitter の色付けをカスタムする

link を使って同系のものを同じ色にして、
色数減らすとわりと見やすいような気がする。
:Inspect で調べながら手作業で調整する感じに。

例👇

  -- comment
  vim.api.nvim_set_hl(0, "Comment", { ctermfg = 8, italic = true, })
  vim.api.nvim_set_hl(0, "SpecialComment", { link = "Comment" })
  vim.api.nvim_set_hl(0, "@comment", { link = "Comment" })
  vim.api.nvim_set_hl(0, "@markup.quote", { link = "Comment" })

  -- keyword
  vim.api.nvim_set_hl(0, "Keyword", { ctermfg = 5 })
  vim.api.nvim_set_hl(0, "@keyword", { link = "Keyword" })
  vim.api.nvim_set_hl(0, "Operator", { link = "Keyword" })
  vim.api.nvim_set_hl(0, "Delimiter", { link = "Keyword" })
  vim.api.nvim_set_hl(0, "@punctuation", { link = "Keyword" })
  vim.api.nvim_set_hl(0, "@operator", { link = "Keyword" })
  vim.api.nvim_set_hl(0, "@keyword.type", { link = "Keyword" })

  -- const / compile time
  vim.api.nvim_set_hl(0, "Constant", { ctermfg = 3 })
  vim.api.nvim_set_hl(0, "Type", { link = "Constant" })
  vim.api.nvim_set_hl(0, "Define", { link = "Constant" })
  vim.api.nvim_set_hl(0, "Structure", { link = "Constant" })
  vim.api.nvim_set_hl(0, "@type", { link = "Constant" })
  vim.api.nvim_set_hl(0, "@keyword.modifier.cpp", { link = "Constant" })

  -- literal
  vim.api.nvim_set_hl(0, "String", { ctermfg = 4 })
  vim.api.nvim_set_hl(0, "@string", { link = "String" })
  vim.api.nvim_set_hl(0, "@boolean", { link = "String" })
  vim.api.nvim_set_hl(0, "@markup.link", { link = "String" })
  vim.api.nvim_set_hl(0, "@number", { link = "String" })

  -- id
  vim.api.nvim_set_hl(0, "Identifier", { ctermfg = 2 })
  vim.api.nvim_set_hl(0, "Function", { link = "Identifier" })
  vim.api.nvim_set_hl(0, "@markup.heading", { link = "Identifier" })
  vim.api.nvim_set_hl(0, "@variable", { link = "Identifier" })
  -- vim.api.nvim_set_hl(0, "@function.call", { link = "Constant" })

lsp の semantic token を止める

強い。止めないと tree-sitter の色が負ける。

local function lsp_attach(args)
  local client = assert(vim.lsp.get_client_by_id(args.data.client_id))

  -- disable semantic highlights
  client.server_capabilities.semanticTokensProvider = nil
end

Hlargs

色を減らしてから Hlargs で赤とかにすると見やすい。

https://github.com/m-demare/hlargs.nvim

  vim.api.nvim_set_hl(0, "Hlargs", { ctermfg = 1, bold = true })

tsnode-marker

markdown のコードブロックの背景色指定とか。

https://github.com/atusy/tsnode-marker.nvim

色決めの方針

color_scheme を選択して具体的な色を決めるのを wezterm の責務にして、
シンタックスによる 16 色選択を tree-sitter の責務にする。

  • comment 灰色とか目立たないが読める色
  • keyword(if, for, return, operator, かっこなど ... etc)
  • const(型名, 定数, enum...etc)
  • id(変数名、関数名)
  • literal(string, num, bool...etc)

👇 wezterm でカラーテーマを選択して、treesitter で文法要素の ansi16 割り当てを調整している

適当に色変えて試せるようになった。

wezterm.lua の lsp 支援

https://github.com/DrKJeff16/wezterm-types

16色ではなかった

color palette には以下のスロットが有る。
base8(black, red, green, yellow, blue, magenta, cyan, white), brights8, foreground, background

都合18色指定できる。

普通は、foreground を white と同じ、background を black と同じにするようである。
そうすれば16色になる。
wezterm では color scheme の末尾に (base16) がついているものが
そういう規約を持っているようだ。

foreground と background

escape sequence は \e[39m および \e[49m となっていて、
foreground色を背景色に、逆にbackground色を前景色に指定することはできない。
neredfont の区切り線で表示できない組み合せがありえる。

neovim では ctermfg, ctermbg に nil を指定する

vim.api.nvim_set_hl(0, "String", { ctermfg = nil })

0 が背景色なのか黒なのか 7 が前景色なのか白なのか

という意味的な問題が発生しているのである。
dark テーマではあまり問題にならないのだが、
light テーマでは、意味を選択する必要が発生する。
白が 0, 黒が 7 になっているのが主流のようだが、
そうじゃないものもある。

Discussion