我碰到的neovim不好实现的几个功能

嗨大家好,我在使用neovim的过程中发现一些它目前不能、不容易做到的功能(即使是通过插件),发出来想着印证下我是不是错了,也借此了解下这些功能在emacs中的实现思路如果可以的话。

在显示一条消息(msg)到cmdline出现n秒后清除:我没有找到在msg出现时触发的事件,也没法全面接管vimscript的echo、echoerr及lua的api.nvim_echo。这样我觉得没办法实现定时清理这类消息的功能。当然,有通过vim.ui_attach({ext_cmdline, ext_messages})接管了cmdline和消息显示的插件,但对于我这个小需求来说也太overkill了。

librime的前端:我曾想找到方法接管neovim的用户输入,然后借助librime+luajit ffi配合floating window实现个rime的前端,但最终没有找到这样的接口。比较接近的是 vim.on_key,但它的工作方式类似于echo hello | tee rime-frontend > nvim,这样我也就不能拦截用户输入。 当然也有基于lsp的实现,不过我觉得延迟会高不少,我宁愿直接用fcitx5-rime。

方便地自定义mode:(n)vim虽然以其模式编辑为特色,但它却没有提供简单的方法来实现自定义mode。 虽然有hydra.nvim可用但看它的实现觉得超级复杂;作为对比,tmux提供了bind-key{ set-option key-table}、i3wm提供了bindsym mode。自己实现的话,我觉得可以通过getchar来读取用户输入加以处理,或者也可以通过临时修改keymaps在自定义模式结束后再复原keymaps,但都没有nnoremap my-mode lhs rhs这样省心

3 秒后清除

 (message "Some message")
 (run-with-timer 3 nil (lambda () (message nil)))

Emacs RIME

Minor Mode

(define-minor-mode hungry-mode
  "Toggle Hungry mode.
Interactively with no argument, this command toggles the mode.
A positive prefix argument enables the mode, any other prefix
argument disables it.  From Lisp, argument omitted or nil enables
the mode, `toggle' toggles the state.

When Hungry mode is enabled, the control delete key
gobbles all preceding whitespace except the last.
See the command \\[hungry-electric-delete]."
 ;; The initial value.
 nil
 ;; The indicator for the mode line.
 " Hungry"
 ;; The minor mode bindings.
 '(([C-backspace] . hungry-electric-delete)))

第一个,容易想到的是定时器 echo 个空字符串清除。但问题是,貌似容易把其它插件的消息给错误清掉?如果重度依赖消息回显,其实可以放到 statusbar 或 winbar。尤其是 cmdline 还要处理用户输入。

第二个想不到使用场景,Neovim 应该尽量保持简单。

自定义 mode 是要什么意思?你可以在某个 mode 下直接 set_keymap,没有比 nnoremap my-mode 复杂多少。

可以通过设置 set-message-functionclear-message-function 来控制 echo area 的行为

不过我猜楼主可能更想要的是没有任何操作 n 秒后清除 echo area,这个可以用 run-with-idle-timer 实现。

定时清空

function MyEcho(msg)
  vim.cmd.echo("\"" .. msg .. "\"")
  local timer = vim.loop.new_timer()
  timer:start(1000,0,vim.schedule_wrap(function()
    vim.cmd.echo([[""]])
  end))
end

不太清楚 emacs,emacs echo area 好像不需要处理用户输入?Neovim 下 cmdline 是用户重度输入区。

Emacs 一样,M-x 会进入 minibuffer,和 echo area 在同一个地方。 如果你想在 Emacs 里面临时在 echo area 显示某个信息,然后恢复之前显示的信息,可以用 with-temp-message,这样就不会覆盖原来的消息了。

挺好。可以把上一条消息存起来,然后稍后 restore。不过回显的地方很多,不必过分纠结。

vim没有emacs那种mode的概念,可以用localleader配合autocmd或者别的东西实现吧

谢谢各位回复!

简单地使用timer每n秒执行一次cmdline清空有点粗暴,它忽视了一些情况:

  • 在用户使用cmdline输入命令时不能执行,且timer应该在退出编辑cmdline时适当延时执行
  • 输出到cmdline的msg我们没办法知道具体时间,如果只是机械地执行n秒清空,很可能一条消息刚出现就被清空了,没机会被用户看到。而且消息出现时,nvim很可能是处于idle状态,即没有用户输入,更基本的是nvim中没有提供类似的事件。

第二个想不到使用场景,Neovim 应该尽量保持简单。

我不认同“nvim要尽量保持简单”这个论断。在neovimconf 2022上说“text editor as a platform”。我也刚巧嵌了个libmpv到nvim中放歌,以替换cmus。

自定义 mode 是要什么意思?

我特意举例了tmux的bind-key key-table跟i3wm的bindsym mode,我觉得是像它们这样的功能。

这个minor mode就是我期望的功能,妙啊。

cmdline 作为输入输出区,我想不到有什么办法可以避免把其它消息错误清除。Emacs 的 echo area 情况类似。

librime 的问题:是否提供了比原生方案更好的体验?tradeoff 是否值得?而后果几乎是立即的。与原生输入法的冲突,自动补全和 snippets 插件的适配…等等。lsp 的方案看起来也显得不那么糟糕了。不过,把 Vim/Emacs 作为 GUI 框架来用,这个现象仍然很有意思。

mode 的话,简单就用 localleader 或 buffer 绑定,过分纠结 mode 没意义。复杂的可能干扰其它插件的,最好自定义事件向全区广播,方便协作。

谢谢回复!我感觉跟你观念差挺多的,昨天看完就琢磨着回复,但最终不知道该怎么回复,也就不说啥了…