ステータスラインをカスタマイズしたい (neovim,lua)
neovim のパッケージ管理ツール packer の作者の dotfiles を眺めていると nvim
以下に statusline.lua がある。これを参考に(丸パクリ)しつつ自分好みのステータスラインを書きたい。
目次
事前知識
lualine を使えば何も問題ない。
API
API のドキュメントはここ https://neovim.io/doc/user/api.html
Diagnostic はここ https://neovim.io/doc/user/diagnostic.htmlステータスラインに関する知識
コマンド:h statusline
でヘルプを読む。%f
でファイル名を表示。%#HIGHLIGHT_ID#
でハイライトの ID を指定して色を変更する%*
がその終端。%=
でセクションを区切る。%<
で省略箇所の指定中身の確認方法
適当な lua ファイルにprint(vim.o.fenc)
とかfor k, v in pairs(vim.diagnostic) do print(k, v) end
を書いて:source %
すれば中身が見れる
やりたいこと
LSP の error と warning の数を表示したい。
分割したウィンドウそれぞれのステータスラインを表示したい。 statusline.lua では
local statuslines = {}
で、各ウィンドウのステータスライン(文字列)を保持しておいて、ウィンドウがアクティブになったらvim.o.statusline
にセットしている。これはこれで合理的で自作する理由の一つなんだろうけど、ウィンドウを分割した場合にアクティブなウィンドウのステータスラインのみが表示されるので、どのウィンドウがアクティブなのか分かりづらい(カラースキームで非アクティブなウィンドウが暗くなるとかしてれば別だろうけど)
コード
~/.config/nvim/lua/statusline.lua
(function() -- このファイルが最初に require されたときのみ実行される local set_hl = vim.api.nvim_set_hl local bg_color = '#262626' local fg_color = '#999999' -- ハイライトの設定。Statusline はアクティブ StatuslineNC は非アクティブ set_hl(0, 'Statusline', { fg = fg_color, bg = bg_color }) set_hl(0, 'StatuslineNC', { fg = '#444444', bg = '#181818' }) set_hl(0, 'StatuslineNormalAccent', { fg = fg_color, bold = true, bg = '#333333' }) set_hl(0, 'StatuslineInsertAccent', { fg = fg_color, bold = true, bg = '#555555' }) set_hl(0, 'StatuslineReplaceAccent', { fg = fg_color, bold = true, bg = '#666b10' }) set_hl(0, 'StatuslineConfirmAccent', { fg = fg_color, bold = true, bg = '#83adad' }) set_hl(0, 'StatuslineTerminalAccent', { fg = fg_color, bold = true, bg = '#555555' }) set_hl(0, 'StatuslineMiscAccent', { fg = fg_color, bold = true, bg = '#666b10' }) set_hl(0, 'StatuslineDiagnosticERROR', { fg = '#FF0000', bold = true, bg = bg_color }) set_hl(0, 'StatuslineDiagnosticWARN', { fg = '#FFAF00', bold = true, bg = bg_color }) end)() local mode_name_table = { n = 'Normal', no = 'N·Operator Pending', v = 'Visual', V = 'V·Line', ['^V'] = 'V·Block', s = 'Select', S = 'S·Line', ['^S'] = 'S·Block', i = 'Insert', ic = 'Insert', R = 'Replace', Rv = 'V·Replace', c = 'Command', cv = 'Vim Ex', ce = 'Ex', r = 'Prompt', rm = 'More', ['r?'] = 'Confirm', ['!'] = 'Shell', t = 'Terminal', } local mode_color_table = { ['n'] = 'StatuslineNormalAccent', ['i'] = 'StatuslineInsertAccent', ['ic'] = 'StatuslineInsertAccent', ['R'] = 'StatuslineReplaceAccent', ['t'] = 'StatuslineTerminalAccent', } -- モードに応じたハイライトIDとモード名を返す。ノーマルモードなら '%#StatuslineNormalAccent# NORMAL %*' local function get_mode() local mode = vim.api.nvim_get_mode().mode local color = mode_color_table[mode] or 'StatuslineMiscAccent' local name = string.upper(mode_name_table[mode] or 'V-Block') return '%#' .. color .. '# ' .. name .. ' %*' end local function get_paste() return vim.o.paste and ' PASTE ' or '' end local function get_readonly_space() return ((vim.o.paste and vim.bo.readonly) and ' ' or '') and '%r' .. (vim.bo.readonly and ' ' or '') end local function get_encoding() return vim.o.fenc or vim.o.enc end local function get_fileformat() return vim.o.ff end local ft_table = { ['javascript'] = 'js', ['typescript'] = 'ts' } local function get_filetype() local ft = vim.o.ft return (not ft or ft == '') and '' or (ft_table[ft] or ft) .. '|' end local function get_filename() return '%f %m' .. get_paste() .. get_readonly_space() end -- エラーかワーニングがあったら '%#StatuslineDiagnosticERROR#1%*' の様に返す。 local function get_diagnostic() local d = {} for _, v in pairs({ 'ERROR', 'WARN' }) do local t = vim.diagnostic.get(0, { severity = vim.diagnostic.severity[v] }) if t ~= nil and #t > 0 then table.insert(d, '%#StatuslineDiagnostic' .. v .. '#' .. tostring(#t) .. '%*') end end return table.concat(d, ' ') end -- アクティブウィンドウのステータスライン local function get_active() return string.format([[%s %s %s %%= %%< %s|%s|%s%%l/%%L(%%P) ]], get_mode(), get_diagnostic(), get_filename(), get_encoding(), get_fileformat(), get_filetype() ) end -- 非アクティブウィンドウのステータスライン local function get_inactive() return ' %f%=%l/%L' end return { get_active = get_active, get_inactive = get_inactive }
~/.config/nvim/init.lua
local function draw() statusline = require('statusline') for _, win_id in pairs(vim.api.nvim_list_wins()) do if vim.api.nvim_get_current_win() == win_id then vim.wo[win_id].statusline = '%!v:lua.statusline.get_active()' elseif vim.api.nvim_buf_get_name(0) ~= '' then vim.wo[win_id].statusline = '%!v:lua.statusline.get_inactive()' end end end -- イベントは過不足あるかもしれない vim.api.nvim_create_autocmd({ 'BufEnter', 'WinEnter', 'TabEnter' }, { group = vim.api.nvim_create_augroup('draw_statusline', {}), pattern = '*', callback = draw, })
できたやつ
lualine を使ってるときと起動速度(nvim --startuptime bench.txt
で確認)は大差ないし軽くもなってないと思われる