(2024/01/27現在) この記事にはまだ正確性がありません
記事執筆後から始めた作業に於いて、 format がおかしいと怒られ始めました
noice.nvim 、いいよね (はじまり)
(neo)vimを使用していると、よく、ターミナルの一番下の行を眺めることになるかと思います
多くの場合、 : から始まるvimコマンドの呼び出しや / などでの検索、
また、 echo や echoe などで表示されるログメッセージの類がここに表示されるためです
しかしながら、何かしらのメッセージが表示されるたびに視線を下に下げるのは非常にめんどうです
特に vimrc を何か誤っている場合のエラーメッセージの場合、 カーソル移動をするたびにエラーが表示され 、そのたびに Enter を連打しないとならないことがあったりするため、とてもしんどいです
しかし、 noice.nvim はこの問題を解決してくれます
あらゆるメッセージを任意の位置の通知として表示してくれる ようになります (デフォルトだと macOS と同じ右上)
これによって、 エラーメッセージに対するキーアクションが必要とされなくなる ため、
たとえ LSP でエラーがたくさん出てくるような場合でも、比較的スムーズにコーディング作業をすることができます
そして私は死んだ (何があったのか?)
これによって、 エラーメッセージに対するキーアクションが必要とされなくなる ため、
たとえLSPでエラーがたくさん出てくるような場合でも、比較的スムーズにコーディング作業をすることができます
これはちょっと罠の部分があり、エラーが大量に出るような場合、 その通知で画面が埋め尽くされる ことがよくよく発生します
そしてこの場合、 float window を大量に生成する処理がneovimに発生します
一度に大量にインスタンスを作成すると何が起こるかは、皆さんならよくよくご存知でしょう
……何も操作ができなくなって死にます (死んだ)
vim-jpのslackで相談してしまったレベルです、つらい

問題
特定の大量発生しがちなエラーを出してしまった場合、何も作業ができなくなって死にます
e.g. LSPの
textDocument/hoverなど、LSPが対応していないファイルを操作している際にhoverを呼ぶような場合
あくまで私の場合の話です
私の場合、カーソルが特定のワードに置かれた際に、1秒程度の間隔を空けて textDocument/hover を呼ぶような設定にしているため、このようなことが発生します
前提
環境情報
- Ubuntu 22/04 LTS (on Windows11(WSL2))
- neovim v0.9.5
$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 22.04.3 LTS
Release: 22.04
Codename: jammy
$ nvim -v
NVIM v0.9.5
Build type: Release
LuaJIT 2.1.1692716794
system vimrc file: "$VIM/sysinit.vim"
fall-back for $VIM: "/__w/neovim/neovim/build/nvim.AppDir/usr/share/nvim"
Run :checkhealth for more info
noice.nvim の filter について
製作者様にはかなり申し訳ないのですが、私にとってはちょっとわかりづらいです…
noice.nvim の filter のヘルプ
noice.nvim の route のヘルプ
以下抜粋 (2024/01/27時点)
FILTERS *noice.nvim-noice-(nice,-noise,-notice)-filters*
**Noice** uses filters to route messages to specific views.
------------------------------------------------------------------------------
Name Type Description
------------ ---------- ------------------------------------------------------
any filter[] checks that at least one of the filters matches
blocking boolean are we in blocking mode?
cleared boolean checks if the message is cleared, meaning it’s in the
history
cmdline boolean or checks if the message was generated by executing a
string cmdline. When string, then it is used as a pattern
error boolean all error-like kinds from ext_messages
event string or any of the events from ext_messages or cmdline. See :h
string[] ui-messages
find string uses lua string.find to match the pattern
has boolean checks if the message is exists, meaning it’s in the
history
kind string or any of the kinds from ext_messages. See :h ui-messages
string[]
max_height number maximum height of the message
max_length number maximum length of the message (total width of all the
lines)
max_width number maximum width of the message
min_height number minimum height of the message
min_length number minimum length of the message (total width of all the
lines)
min_width number minimum width of the message
mode string checks if vim.api.nvim_get_mode() contains the given
mode
not filter checks whether the filter matches or not
warning boolean all warning-like kinds from ext_messages
------------------------------------------------------------------------------
わからなかったポイント
FILTERS は FILTER ですか?
この部分
FILTERS noice.nvim-noice-(nice,-noise,-notice)-filters
と、この部分
any filter[] checks that at least one of the filters matches
FILTERS が FILTER の説明なのかがちょっとわかりづらかったです
特に、 any の型が filter[] となっているところもあり、この入れ子構造のイメージがしづらかった…
また、ドキュメント側でのこのあたりの説明がかなりさらっとしており、 any を利用するパターンのイメージがうまくできませんでした
構造の理解
filter はこのような構造になっていることで理解しました
-
filterを持つテーブル構造には、ほかのキーを持たせてはならない -
anyはfilterの配列構造を前提 としている- これがかなりわかりづらいのですが、つまり、
{ filter = {...} }を期待しています
- これがかなりわかりづらいのですが、つまり、
-
filterは少なくともeventとfindを持たないと、フィルタリングできない -
filterにviewを指定することで、任意のビューの方法で表示してくれる- 例えば
view = "mini"とすると、右下に小さく、通知を表示してくれます
- 例えば
コードで表すと、このような形になります
{
{
filter = {
event = "msg_show",
any = {
{ filter = { kind = "", find = "", } }
{ filter = { kind = "", find = "", } }
{ filter = { kind = "", find = "", } }
{ : }
{ : }
},
opts = { skip = true },
},
},
{
filter = {
event = "lsp",
any = {
{ filter = { kind = "", find = "", } }
{ filter = { kind = "", find = "", } }
{ filter = { kind = "", find = "", } }
{ : }
{ : }
},
view = "mini",
},
},
}
問題の解決
このような設定を書くことで、解決することができました
local suppressMessages = {
-- ※ `suppressMessage()` は後述の `appendix` の部分で説明しています
noiceWrapper("^%d+ lines .ed %d+ times?$"),
noiceWrapper("^%d+ lines yanked$"),
noiceWrapper(".*E490.*", "emsg"),
noiceWrapper("search_count"),
-- :
-- (中略)
-- :
}
local M = {
routes = {
{
filter = {
event = "msg_show",
any = suppressMessages,
opts = { skip = true },
},
},
}
}
return M
-- lazy.nvim を使用している想定です
local noice_config = require("noice-nvim-config")
return {
{
lazy = true,
"folke/noice.nvim",
event = { "VeryLazy" },
dependencies = {
"MunifTanjim/nui.nvim",
"rcarriga/nvim-notify",
"nvim-telescope/telescope.nvim",
},
opts = function(_, opts)
-- add any options here
opts.routes = noice_config.routes
opts.notify = {
enabled = true,
view = "notify",
}
opts.messages = {
-- NOTE: If you enable messages, then the cmdline is enabled automatically.
-- This is a current Neovim limitation.
enabled = true, -- enables the Noice messages UI
view = "notify", -- default view for messages
view_error = "notify", -- view for errors
view_warn = "notify", -- view for warnings
view_history = "messages", -- view for :messages
view_search = "virtualtext", -- view for search count messages. Set to `false` to disable
}
opts.redirect = {
view = "popup",
filter = { event = "msg_show" },
}
opts.presets = {
bottom_search = true, -- use a classic bottom cmdline for search
command_palette = true, -- position the cmdline and popupmenu together
long_message_to_split = true, -- long messages will be sent to a split
inc_rename = false, -- enables an input dialog for inc-rename.nvim
lsp_doc_border = false, -- add a border to hover docs and signature help
}
end,
config = function(_, opts)
require("noice").setup(opts)
-- ※ ここは `telescope` 使用される方はお好みで
--require("telescope").load_extension("notify")
end,
}
}
appendix: ユーティリティ関数の追加
こんな感じの noice-filter のラッパーを作成しておくことで、わざわざテーブル構造を書くコストを減らしています
local function noiceWrapper(pattern, kind)
kind = kind or ""
return {
filter = {
kind = kind,
find = pattern,
},
}
end
さいごに
入れ子構造はちょっと難しいです
キーが指定されていたり、されていなかったりすると、さらに難しさに拍車がかかるような気がしています
この問題の解決のために、私は結構精神をすり減らしましたが、うまく解決できてよかったです
