Neovim 0.7 刚刚发布,带来了很多新功能(当然还有大量的错误修复)。你可以在这里找到完整的发布说明这里,但在本文中,我将只介绍一些新功能。
Neovim 0.5 引入了 Lua 作为 Neovim 生态系统中的一等公民:现在可以在用户的 init 文件、插件、配色方案、ftplugins 等中使用 Lua。基本上,任何可以使用 .vim
文件的地方,现在都可以使用 .lua
来代替。
但是,当时的 Lua API 仍然存在一些不足。特别缺少的是在 Lua 中创建自动命令的能力,以及将键映射直接绑定到 Lua 函数的能力。为了完成这两件事,用户需要诉诸涉及通过 Vimscript 转换的往返操作的解决方法,这有点笨拙。
-- Using a Lua function in a key mapping prior to 0.7
local function say_hello()
print("Hello world!")
end
_G.my_say_hello = say_hello
vim.api.nvim_set_keymap("n", "<leader>H", "<Cmd>call v:lua.my_say_hello()<CR>", {noremap = true})
自动命令和自定义用户命令的情况类似。
在 Neovim 0.7 中,现在可以直接在 Lua 中使用所有常见的配置原语(键映射、自动命令、用户命令等),无需 Vimscript 转换。这也使得将键映射和自动命令直接绑定到局部 Lua 函数成为可能。
-- Using a Lua function in a key mapping in 0.7
vim.api.nvim_set_keymap("n", "<leader>H", "", {
noremap = true,
callback = function()
print("Hello world!")
end,
})
-- Creating an autocommand in 0.7
vim.api.nvim_create_autocmd("BufEnter", {
pattern = "*",
callback = function(args)
print("Entered buffer " .. args.buf .. "!")
end,
desc = "Tell me when I enter a buffer",
})
-- Creating a custom user command in 0.7
vim.api.nvim_create_user_command("SayHello", function(args)
print("Hello " .. args.args)
end, {
nargs = "*",
desc = "Say hi to someone",
})
你可能注意到 nvim_set_keymap
必须将 Lua 回调设置为最终表参数中的键,而 nvim_create_user_command
可以直接将回调函数作为位置参数传递。这是 Neovim 严格的 API 契约的结果,该契约要求 API 函数在进入稳定版本后,其签名不得以任何方式更改。但是,因为 nvim_create_user_command
是一个新的 API 函数,我们能够通过使其第二个参数接受字符串或函数来增加一点便利性。
Neovim 0.7 还包括一个仅供 Lua 使用的便捷函数 vim.keymap.set
,用于轻松创建新的键映射。
vim.keymap.set("n", "<leader>H", function() print("Hello world!") end)
vim.keymap.set
与 nvim_set_keymap
的区别在于以下几点:
noremap
,因为这是用户 99% 的时间想要的东西。帮助文档包含更多信息:在 Neovim 中运行 :h vim.keymap.set
以了解更多信息。
最后,用户现在可以使用 API 函数 nvim_set_hl
来修改全局高亮组(等同于使用 :hi
),为纯 Lua 配色方案打开了大门。
作为一款基于终端的应用程序,Neovim 长期以来一直受到终端模拟器的限制,其中之一就是很多键的编码相同,因此对于在终端中运行的应用程序来说无法区分。例如,<Tab>
和 <C-I>
使用相同的表示方式,<CR>
和 <C-M>
也是如此。这长期以来意味着无法分别映射 <C-I>
和 <Tab>
:映射一个就必然映射了另一个。
这长期以来一直是让人感到困扰的一点,而且在野外有许多解决方案来解决这个问题。Neovim 使用了 Paul Evans 的 libtermkey,该库反过来利用了 Evans 自己提出的 fixterms 方案,以一种明确的方式对修饰键进行编码。只要控制 Neovim 的终端模拟器以这种方式发送编码的键,Neovim 就可以正确地解释它们。
Neovim 0.7 现在可以正确地区分这些修饰键组合,以便用户现在可以分别映射例如 <Tab>
和 <C-I>
。此外,Neovim 在启动时发送了一个转义序列,向控制终端模拟器发出信号,表明它支持这种键编码风格。一些终端模拟器(如 iTerm2、foot 和 tmux)使用此序列来以编程方式启用不同的编码。
需要注意的是,这种方式是双向的!你可能会发现,以前映射到 <Tab>
或 <C-I>
(或 <CR>
/<C-M>
)的映射不再起作用。然而,修复起来很简单;只需修改你的映射,使用你想要使用的实际键即可。
除了区分这些修饰键对之外,这也启用了以前无法实现的新键映射,例如 <C-;>
和 <C-1>
。
对此的支持在很大程度上取决于你使用的终端,因此这不会影响所有用户。
Neovim 0.7 引入了一个新的“全局”状态栏,可以通过设置 laststatus=3
来启用。与每个窗口都有一个状态栏不同,全局状态栏始终运行在 Neovim 容器窗口的整个可用宽度上。这使得它对于显示不随窗口更改的信息非常有用,例如 VCS 信息或当前工作目录。许多状态栏插件已经开始利用这个新功能。
在 Neovim 0.7 中,有一种新的(实验性的)方法可以进行文件类型检测。关于文件类型检测的简要说明:当您第一次启动 Neovim 时,它会在 $VIMRUNTIME
目录中源文件名为 filetype.vim
的文件。此文件创建了数百个 BufRead,BufNewFile
自动命令,其唯一目的是根据有关文件的信息推断文件的类型,最常见的是文件的名字或扩展名,但有时也使用文件的内容。
如果您使用 nvim --startuptime
对启动时间进行分析,您会注意到 filetype.vim
是加载速度最慢的文件之一。这是因为创建如此多的自动命令是昂贵的。另一种进行文件类型检测的方法是,创建一个只针对每个新缓冲区触发的单一自动命令,然后通过一系列顺序步骤尝试匹配文件类型。这就是新的 filetype.lua
所做的事情。
除了使用单个自动命令之外,filetype.lua
还使用基于表的查找结构,这意味着在很多情况下,文件类型检测是在恒定时间内完成的。如果您的 Neovim 是使用 LuaJIT 编译的(很可能就是这样),您还将获得此文件类型匹配的即时编译的好处。
此功能目前是选择加入的,因为它还没有完全匹配 filetype.vim
所覆盖的所有文件类型,尽管它非常接近(我已经独占使用它很多个月,没有任何问题)。有两种方法可以加入此功能
使用 filetype.lua
,但回退到 filetype.vim
将 let g:do_filetype_lua = 1
添加到您的 init.vim
文件中。这可以防止文件类型匹配出现任何回归,并确保文件类型始终至少与 filetype.vim
一样好地被检测到。但是,您将为 filetype.lua
和 filetype.vim
都支付启动时间成本。
只使用 filetype.lua
,根本不加载 filetype.vim
将 let g:do_filetype_lua = 1
和 let g:did_load_filetypes = 0
添加到您的 init.vim
文件中。这将独占使用 filetype.lua
进行文件类型匹配,并提供上面概述的所有性能优势,但也存在错过文件类型检测的(很小)风险。
除了性能优势外,filetype.lua
还让添加自定义文件类型变得很容易。只需创建一个新的文件 ~/.config/nvim/filetype.lua
,然后调用 vim.filetype.add
来创建新的匹配规则。例如
vim.filetype.add({
extension = {
foo = "fooscript",
},
filename = {
["Foofile"] = "fooscript",
},
pattern = {
["~/%.config/foo/.*"] = "fooscript",
}
})
vim.filetype.add
接受一个包含 3 个(可选)键的表,分别对应“扩展名”、“文件名”和“模式”匹配。每个表项的值可以是字符串(在这种情况下,它被解释为文件类型)或函数。例如,您可能希望覆盖 Neovim 的默认行为,即始终将 .h
文件分类为 C++ 头文件,方法是使用启发式方法,只有在头文件包含另一个 C++ 风格的头文件(即没有尾随 .h
的头文件)时才将文件类型设置为 C++
vim.filetype.add({
extension = {
h = function(path, bufnr)
if vim.fn.search("\\C^#include <[^>.]\\+>$", "nw") ~= 0 then
return "cpp"
end
return "c"
end,
},
})
我们每天都在让 filetype.lua
更接近与 filetype.vim
的完全一致性。目标是在 Neovim 0.8 中将其设为默认值(并能够选择退出使用传统的 filetype.vim
)。
Neovim 0.7 将 neovim-remote 的一些功能引入到核心编辑器中。您现在可以使用 nvim --remote
在已经运行的 Neovim 实例中打开文件。举个例子
# In one shell session
nvim --listen /tmp/nvim.sock
# In another shell session, opens foo.txt in the first Nvim instance
nvim --server /tmp/nvim.sock --remote foo.txt
新的远程功能的一个用例是,能够从主 Neovim 实例的嵌入式终端模拟器中打开文件,而不是在 Neovim 本身内部创建嵌入式 Neovim 实例。
Neovim 是一个由热心人士组成的松散结构的项目,他们出于兴趣进行工作;因此,任何路线图都始终有点像猜谜游戏。但是,已经有一些正在酝酿中的东西,你可能会在 Neovim 0.8 中看到
Neovim 是一款基于 Vim 的文本编辑器,专为可扩展性和可用性而设计,旨在鼓励新的应用程序和贡献。
访问#neovim:matrix.org或 irc.libera.chat 上的 #neovim,与团队聊天。