🐩

Neovimでhit-enter-promptを消すヘルパー関数

に公開

:checkhealthコマンドがhit-enter-promptを出すのにイラっと来て作りました。

前提

Neovim(およびVim)がエコーした結果に対してcmdheightが足りないときなどにPress ENTER or type command to continueが出ます。これがhit-enter-promptです。

https://vim-jp.org/vimdoc-ja/message.html#hit-enter

以前はこれを回避するのは難しかったのですが、現代では'messagesopt'が存在します。

https://vim-jp.org/vimdoc-ja/options.html#'messagesopt'

開発時のやりとりはこちら。

https://github.com/vim/vim/pull/16068

'messagesopt'は3つの値をまとめたオプションで、ちょっと複雑な構成です。

  • history
    • メッセージ履歴の件数を定める数値
  • hit-enter
    • hit-enter-promptを表示するかの真偽値(存在すれば真)
  • wait
    • hit-enter-promptを表示せず、出力の自動消去時間(ミリ秒数)を定める数値
    • 0を指定すると何も表示しない

さらに、これらの値は完全に消去することはできません。以下のような条件があります。

  • historyは常に必須
  • hit-enterかwaitの少なくとも片方は必須
    • 両方ある場合はhit-enterが有効になりwaitは無視される

この必須条件のため、いきなりset messagesopt-=hit-enterしようとすると失敗します。さきにwaitを追加しなければなりません。
ただ、常にhit-enter-promptを消してしまうと:echoなどもすぐ消えてしまって不便です。
ということで、対象のlua関数を実行している間だけhit-enter-promptを無効にする仕組みを作りました。

ヘルパー関数

以下のskip_hit_enterに関数を渡すと、'messagesopt'を一時的に書き換えて実行する関数を返します。

--- skip hit-enter prompt
-- hit-enterプロンプトをスキップするラップされた関数を返す
-- @param fn function ラップする関数
-- @param opts table|nil オプションのテーブル
--   - wait (number|nil): 待機時間(ミリ秒) デフォルトは0
-- @return function ラップされた関数
local function skip_hit_enter(fn, opts)
  opts = opts or {}
  local wait = opts.wait or 0
  return function(...)
    local save_mopt = vim.opt.messagesopt:get()
    vim.opt.messagesopt:append('wait:' .. wait)
    vim.opt.messagesopt:remove('hit-enter')
    fn(...)
    vim.schedule(function()
      vim.opt.messagesopt = save_mopt
    end)
  end
end

これを使って、例えば設定ファイルに以下のように記述します。設定をリロードしたときに多重ラップされるのを防ぐにはvim_startingで判定すると良いです。

if vim.fn.has('vim_starting') == 1 then
  vim.cmd.checkhealth = skip_hit_enter(vim.cmd.checkhealth)
end

これでvim.cmd.checkhealth('vim.lsp')などでhit-enter-promptが抑制されます。
なおExコマンドの:checkhealthの方には影響しませんが、そちらは:silentで黙らせられるのでヨシとしました。

Discussion