很长一段时间,AI 一直是一个非常热门的话题。衍生出来的工具也非常的多,从我接触的顺序来看, 从最早的 tabline 补全到后来的 GitHub Copilot 补全。再到后来的 ChatGPT 以及之后来的各自类似的 Chat 工具。
前面我使用最多的还是网页版的 ChatGPT,使用过程中最大的一个问题就是请求结果渲染展示一直是有问题的。 尤其是让他展示 markdown 源码时。比如:

上述图片 Usage 实际上也在代码块里面是,但是由于 markdown 内还有代码块,导致解析展示出问题。
正是由于这个原因,我制作了 Neovim AI 聊天插件 chat.nvim,
我需要以纯文本展示请求结果的完整内容。
使用任意插件管理器,比如 nvim-plug:
local deepseek_api_key = 'xxxxxxxxxxx'
local free_chatgpt_api_key = 'xxxxxxxxxxxxxxx'
require('plug').add({
{
'wsdjeg/chat.nvim',
opt = {
api_key = {
deepseek = deepseek_api_key,
free_chatgpt = free_chatgpt_api_key,
},
provider = 'free_chatgpt',
model = 'gpt-4o-mini',
border = 'double',
},
},
})
chat.nvim 默认是上下分屏两个浮窗,分别为输入窗口和结果展示窗口。如图:

chat.nvim 自带了一些 picker.nvim 插件的拓展,目前支持的拓展有:
:Picker chat - 搜索历史对话

:Picker chat_provider - 搜索并切换 provider

:Picker chat_model - 搜索并切换当前 provider 提供的模型

Bdelete/Bwipeout在使用 Neovim 的过程中,「删除 buffer 但不破坏窗口布局」一直是一个高频需求。
社区里已经有不少相关插件,比如 bufdelete.nvim、nvim-bufdel、mini.bufremove,以及 snacks.bufdelete。
但是在我自己长期使用过程中,总觉得缺少了我需要的功能。
于是,我写了一个新的插件:bufdel.nvim。
这篇文章简单聊聊它解决了什么问题、有哪些设计取舍,以及它和现有方案的区别。
先说结论:bufdel.nvim 设计初衷是为了删除 buffer 这个操作的每一步更加可控。
我写这个插件,主要有几个原因:
bufdelete.nvim 已经 archived
bufdelete.nvim 是一个非常优秀的插件,我也之前也使用过一段时间。但它目前已经被标记为 archived,不再维护。
我希望有一个持续维护、可扩展的替代方案,同时保留它最核心、最优雅的设计。
我需要更灵活的 buffer 选择方式
很多插件只支持按照 buffer number 删除,但是再实际使用中,我经常需要:
删除之后,切换到哪个 buffer,应该是可控的
前面提到的几个插件大多数在删除 buffer 后,选择切换到 bufnr('#')。但我更希望能明确指定下一个 buffer 是哪个或者通过函数,完全自定义切换逻辑。
一个核心 API:delete(buffers, opt)
bufdel.nvim 只暴露一个核心函数,但是它支持的参数非常灵活。不少其他的插件会设计两个 API 函数 delete 和 wipeout,其实完全可以合并,通过 opt 内一个选项区分。
require('bufdel').delete(buffers, opt)
buffers 参数:你想怎么选 buffer 都行
buffers 支持多种形式:
比如,删除所有已列出、已保存的非当前 buffer:
require('bufdel').delete(function(buf)
return not vim.bo[buf].modified
and vim.bo[buf].buflisted
and buf ~= vim.api.nvim_get_current_buf()
end, { wipe = true })
这类逻辑,在很多其他插件里是做不到的。
正则匹配 buffer 名称
如果你想清理一类文件,比如所有 .txt buffer:
require('bufdel').delete('.txt$', { wipe = true })
这在日常清理临时文件、日志文件时非常方便。
这是 bufdel.nvim 的一个重点特性。
使用函数自定义切换逻辑(推荐)
require('bufdel').delete(filter, {
wipe = true,
switch = function(deleted_buf)
return vim.fn.bufnr('#') -- 切换到 alternate buffer
end,
})
你可以在这里实现任何策略,只要返回一个有效的 buffer number。
内置几种常用策略
如果不想写函数,也可以直接用字符串:
switch = 'alt'
当前支持:
alt:alternate buffer(#)current:保持当前 bufferlastused:最近使用的 buffernext / prev:下一个 / 上一个 buffer直接指定 buffer number
switch = 3
Bdelete/Bwipeoutbufdel.nvim 提供了两个命令:
:Bdelete
:Bwipeout
行为和 :bdelete / :bwipeout 一致,但不会改变窗口布局。
示例:
:Bdelete
:Bdelete 3
:Bdelete 2 5 7
:3,6Bdelete
和原生 :bdelete 一样:
纯数字的 buffer 名称不能作为用户命令参数使用。
比如:
:e 123
:Bdelete 123
这时必须使用 bufnr,而不是 bufname。
bufdel.nvim 会在删除 buffer 前后触发两个事件:
User BufDelPre
User BufDelPost
示例:
vim.api.nvim_create_autocmd('User', {
pattern = 'BufDelPost',
callback = function(ev)
-- 被删除的 bufnr 在 ev.data.buf 中
end,
})
如果删除失败,BufDelPost 不会触发。
下面是基于我个人使用需求的一个对比表:
| Feature / Plugin | bufdel.nvim | bufdelete.nvim | nvim-bufdel | snacks.bufdelete | mini.bufremove |
|---|---|---|---|---|---|
| Preserve window layout | ✓ | ✓ | ✓ | ✓ | ✓ |
| Delete by bufnr | ✓ | ✓ | ✓ | ✓ | ✓ |
| Delete by bufname | ✓ | ✓ | ✓ | ✓ | ✗ |
| User Command | ✓ | ✓ | ✓ | ✗ | ✗ |
| Lua filter function | ✓ | ✗ | ✗ | ✓ | ✗ |
| Regex buffer matching | ✓ | ✗ | ✗ | ✗ | ✗ |
| Post-delete buffer switch | ✓ | ✓ | ✓ | ✗ | ✗ |
| User autocmd hooks | ✓ | ✓ | ✗ | ✗ | ✗ |
如果你发现表格里有不准确的地方,欢迎直接提 issue。
bufdel.nvim 并不是一个“什么都做”的插件,相反,我刻意让它保持:
如果你:
那它可能正好适合你。
👉 GitHub:wsdjeg/budel.nvim
如果你觉得有用,欢迎 star ⭐
早在写 zettelkasten.nvim 插件的时候,我就想做一个日历试图,用来查看笔记的日期。可能是因为需求不是那么的迫切, 所以一直拖着没有写这样功能。
趁着这次假日,抽空写了这样一个日历插件 calendar.nvim,功能目前还是非常简单的,只是一个简单的日历月视图。 这算是 2026 年我的第一个 Neovim 插件,这篇文字主要介绍 calendar.nvim 插件的安装使用以及制作这一插件遇到的一些问题。
calendar.nvim 是使用 Lua 实现的 Neovim 插件,零依赖,可以使用任意插件管理器直接安装,比如:nvim-plug
require('plug').add({
{
'wsdjeg/calendar.nvim',
},
})
插件的默认配置如下:
require('calendar').setup({
mark_icon = '•',
keymap = {
next_month = 'L', -- 下个月
previous_month = 'H', -- 上个月
next_day = 'l', -- 后一天
previous_day = 'h', -- 前一天
next_week = 'j', -- 下一周
previous_week = 'k', -- 前一周
today = 't', -- 跳到今天
},
highlights = {
current = 'Visual',
today = 'Todo',
mark = 'Todo',
},
})
nvim_buf_set_extmark 函数中 col 等参数指的并不是屏幕 column 列表,而是字符串的字节,
overlay virt_text 的高亮会清除掉当前位置的 extmark hl_group 高亮
最终解决逻辑是给每一个需要标记的位置按照如下逻辑添加 virt_text,其高亮参数传输一个高亮列表.
local hls = { highlights.mark }
if is_totay() then
table.insert(hls, highlights.today)
end
if is_current() then
table.insert(hls, highlights.current)
end
vim.api.nvim_buf_set_extmark(buf, ns, col, {
virt_text = { { mark_icon, hls } },
})
这里展示了一个添加了 zettelkasten 拓展的日历:
local zk_ext = {}
function zk_ext.get(year, month)
local notes = require('zettelkasten.browser').get_notes()
local marks = {}
for _, note in ipairs(notes) do
local t = vim.split(note.id, '-')
if tonumber(t[1]) == year and tonumber(t[2]) == month then
table.insert(
marks,
{
year = tonumber(t[1]),
month = tonumber(t[2]),
day = tonumber(t[3]),
}
)
end
end
return marks
end
require('calendar.extensions').register(zk_ext)
最终的效果图如下:

时隔十年,再次被 Windows 系统的路劲大小写问题坑了一把。记得上一次被坑是因为写 Vim Script 的 autoload 脚本时出现的问题。 最近使用 Lua 重新写了 ChineseLinter.vim 这个插件,最开始的文件结构:
文件:plugins/chineselinter.lua
return {
'wsdjeg/ChineseLinter.nvim',
dev = true,
opts = {
ignored_errors = { 'E015', 'E013', 'E020', 'E021' },
},
cmds = { 'CheckChinese' },
desc = 'Chinese Document Language Standards Checking Tool',
}
按照以上配置,无论如何 ignored_errors 配置都无法起效。
上述插件在载入时没有报错,说明被成功载入并且执行了 setup 函数。我试着用单独的脚本来测试,并且打入一些日志:
vim.opt.runtimepath:append("D:/wsdjeg/job.nvim")
vim.opt.runtimepath:append("D:/wsdjeg/logger.nvim")
vim.opt.runtimepath:append("D:/wsdjeg/nvim-plug")
require('plug').setup({
bundle_dir = 'D:/bundle_dir',
raw_plugin_dir = 'D:/bundle_dir/raw_plugin',
-- ui = 'notify',
http_proxy = 'http://127.0.0.1:7890',
https_proxy = 'http://127.0.0.1:7890',
enable_priority = false,
enable_luarocks = true,
max_processes = 16,
dev_path = 'D:/wsdjeg',
})
require("plug").add({
{
"wsdjeg/ChineseLinter.nvim",
dev = true,
opts = {
ignored_errors = { "E015", "E013", "E020", "E021" },
},
cmds = { "CheckChinese" },
desc = "Chinese Document Language Standards Checking Tool",
},
})
日志结果如下:
[ 23:35:32:449 ] [ Info ] [ cnlint ] module is loaded
[ 23:35:32:450 ] [ Info ] [ cnlint ] setup function is called
[ 23:35:32:450 ] [ Info ] [ plug ] load plug: ChineseLinter.nvim in 4.3624ms
[ 23:35:32:451 ] [ Info ] [ cnlint ] module is loaded
[ 23:35:32:451 ] [ Info ] [ cnlint ] check function is called
不难看出 ChineseLinter 模块被载入了两次,第一次载入及setup函数是 nvim-plug 在执行,执行后计算的载入时间,第二次是执行 CheckChinese 命令时,
而这一命令是在 plugin/ChineseLinter.lua 内定义的:
vim.api.nvim_create_user_command("CheckChinese", function(opt)
require("ChineseLinter").check()
end, { nargs = "*" })
问题就在这里,这个命令内 require('ChineseLinter') 不应该再次载入模块文件,因为前面 nvim-plug 已经执行过一次了,正常情况下 package.loaded 内会缓存模块。
看一下 nvim-plug 载入 Lua 插件的逻辑,它会给 plugSpec 自动设置一个模块名称,
以便于自动执行 require(plugSpec.module).setup(plugSpec.opts)。
问题就在于这个 module 名称生成函数原先是:
local function get_default_module(name)
return name
:lower()
:gsub('[%.%-]lua$', '')
:gsub('^n?vim-', '')
:gsub('[%.%-]n?vim', '')
end
也就是说,按照上述载入插件方式,nvim-plug 执行的是 require('chineselinter'),这在 Windows 系统下,
因为文件 lua/ChineseLinter/init.lua 已存在,那么上述 require 函数就会读取这个模块。
而 :CheckChinese 命令实际上调用的模块是 require('ChineseLinter')。
因为 Lua 的模块名称实际上是大小写敏感的,就会再次去寻找模块文件以载入。
我查阅了几个插件管理器,他们的获取模块名称的函数基本上逻辑类似,都使用了 lower() 函数:
---@param name string
---@return string
function M.normname(name)
local ret = name:lower():gsub("^n?vim%-", ""):gsub("%.n?vim$", ""):gsub("[%.%-]lua", ""):gsub("[^a-z]+", "")
return ret
end
实际上,最好是不要自动去将模块的名字全部小写,按照仓库的名称来最合适,去除掉前后缀,修改 nvim-plug 如下:
diff --git a/lua/plug/loader.lua b/lua/plug/loader.lua
index d0fc7b6..957fcb7 100644
--- a/lua/plug/loader.lua
+++ b/lua/plug/loader.lua
@@ -68,8 +68,7 @@ end
--- @param name string
--- @return string
local function get_default_module(name)
- return name:lower()
- :gsub('[%.%-]lua$', '')
+ return name:gsub('[%.%-]lua$', '')
:gsub('^n?vim-', '')
:gsub('[%.%-]n?vim', '')
end
@@ -94,6 +93,13 @@ function M.parser(plugSpec)
plugSpec.name = check_name(plugSpec)
if not plugSpec.module then
plugSpec.module = get_default_module(plugSpec.name)
+ log.info(
+ string.format(
+ 'set %s default module name to %s',
+ plugSpec.name,
+ plugSpec.module
+ )
+ )
end
if #plugSpec.name == 0 then
plugSpec.enabled = false
考虑到 Windows 系统的大小写敏感,以及 Shift 键这么难按,我将插件的名称以及其内模块的名称都改成了小写,修改后插件的安装方式:
return {
'wsdjeg/chineselinter.nvim',
dev = true,
opts = {
ignored_errors = { 'E015', 'E013', 'E020', 'E021' },
},
cmds = { 'CheckChinese' },
desc = 'Chinese Document Language Standards Checking Tool',
}
上述核心问题在于 Lua 的 require() 函数读取模块缓存时判断的是 package.load[key],这里的 key 是大小写敏感的。
而发现缓存不存在时,依照 key 去载入文件时,在 Windows 系统下路劲又是不敏感的,
会导致同一个模块被不同的大小写模块名称多次载入。
好几年前,我使用 Vim Script 实现过一个悬浮侧栏插件 scrollbar.vim, 前段时间该插件使用 Lua 进行了重写并改名称为 scrollbar.nvim, 重写后的插件只支持 Neovim。
scrollbar.nvim 会在当前窗口的右侧使用浮窗绘制一个滚动条,其位置依据当前窗口显示的内容在整个文件中所在的行数, 并且随着鼠标移动、滚屏等操作上下移动。
使用 nvim-plug 进行安装:
require('plug').add({
{
'wsdjeg/scrollbar.nvim'
}
})
或者使用 luarocks 进行安装:
luarocks install scrollbar.nvim
require('scrollbar').setup({
max_size = 10,
min_size = 5,
width = 1,
right_offset = 1,
excluded_filetypes = {
'startify',
'git-commit',
'leaderf',
'NvimTree',
'tagbar',
'defx',
'neo-tree',
'qf',
},
shape = {
head = '▲',
body = '█',
tail = '▼',
},
highlight = {
head = 'Normal',
body = 'Normal',
tail = 'Normal',
},
debug = false,
})
前面再阅读一些插件源码时,发现一个问题,很多插件的使用了 ftplugin 这个目录,其内的脚本文件中直接使用了 setlocal xx=xx 这样的语法。
在早期的 Neovim 或者 Vim 版本中这样确实没有问题,但是随着 Neovim 功能特性增加。这样写就会容易出错。
实际上,直到目前为止 Neovim 和 Vim 的官方文档 :h ftplugin 内的示例还是:
" Only do this when not done yet for this buffer
if exists("b:did_ftplugin")
finish
endif
let b:did_ftplugin = 1
setlocal textwidth=70
Neovim 插件的 ftplugin 目录是一个特殊的文件夹,其内的文件会在 FileType 事件触发是被载入。
看一下 Neovim 的源码,ftplugin 目录下的文件是如何被载入的。
augroup filetypeplugin
au FileType * call s:LoadFTPlugin()
func! s:LoadFTPlugin()
if exists("b:undo_ftplugin")
exe b:undo_ftplugin
unlet! b:undo_ftplugin b:did_ftplugin
endif
let s = expand("<amatch>")
if s != ""
if &cpo =~# "S" && exists("b:did_ftplugin")
" In compatible mode options are reset to the global values, need to
" set the local values also when a plugin was already used.
unlet b:did_ftplugin
endif
" When there is a dot it is used to separate filetype names. Thus for
" "aaa.bbb" load "aaa" and then "bbb".
for name in split(s, '\.')
" Load Lua ftplugins after Vim ftplugins _per directory_
" TODO(clason): use nvim__get_runtime when supports globs and modeline
" XXX: "[.]" in the first pattern makes it a wildcard on Windows
exe $'runtime! ftplugin/{name}[.] ftplugin/{name}_*. ftplugin/{name}/*.'
endfor
endif
endfunc
augroup END
以上内容不难看出,Neovim 实际上是监听了 FileType 这个事件,然后根据 expand('<amatch>') 的值来执行 :runtime 命令。
但是,随着 Neovim 和 Vim 增加了设置非当前 buffer 的 option 这一功能后。就会出现这样问题,当 FileType 事件触发时,触发的 buffer 并非是当前 buffer。
那么在 ftplugin 内如果使用了 setlocal 这样的命令,有可能会设置错了缓冲区。
test_ft.lua
local log = require("logger").derive("ft")
log.info("nvim_get_current_buf() is " .. vim.api.nvim_get_current_buf())
log.info("nvim_get_current_win() is " .. vim.api.nvim_get_current_win())
log.info("-----------------------------------------------------")
local real_current_win = vim.api.nvim_get_current_win()
local newbuf = vim.api.nvim_create_buf(true, false)
local events = {}
for _, v in ipairs(vim.fn.getcompletion("", "event")) do
if not vim.endswith(v, "Cmd") then
table.insert(events, v)
end
end
local id = vim.api.nvim_create_autocmd(events, {
group = vim.api.nvim_create_augroup("test_ft", { clear = true }),
pattern = { "*" },
callback = function(ev)
log.info("-----------------------------------------------------")
log.info("event is " .. ev.event)
log.info("ev.buf is " .. ev.buf)
log.info("nvim_get_current_buf() is " .. vim.api.nvim_get_current_buf())
log.info("nvim_get_current_win() is " .. vim.api.nvim_get_current_win())
log.info("real_current_win's buf is" .. vim.api.nvim_win_get_buf(real_current_win))
end,
})
vim.api.nvim_open_win(newbuf, false, { split = "above" })
vim.api.nvim_set_option_value("filetype", "test123", { buf = newbuf })
vim.api.nvim_del_autocmd(id)
log.info("-----------------------------------------------------")
log.info("nvim_get_current_buf() is " .. vim.api.nvim_get_current_buf())
log.info("nvim_get_current_win() is " .. vim.api.nvim_get_current_win())
[ 23:50:19:932 ] [ Info ] [ ft ] nvim_get_current_buf() is 7
[ 23:50:19:932 ] [ Info ] [ ft ] nvim_get_current_win() is 1000
[ 23:50:19:932 ] [ Info ] [ ft ] -----------------------------------------------------
[ 23:50:19:933 ] [ Info ] [ ft ] -----------------------------------------------------
[ 23:50:19:933 ] [ Info ] [ ft ] event is WinNew
[ 23:50:19:933 ] [ Info ] [ ft ] ev.buf is 7
[ 23:50:19:933 ] [ Info ] [ ft ] nvim_get_current_buf() is 7
[ 23:50:19:933 ] [ Info ] [ ft ] nvim_get_current_win() is 1008
[ 23:50:19:933 ] [ Info ] [ ft ] real_current_win's buf is7
[ 23:50:19:934 ] [ Info ] [ ft ] -----------------------------------------------------
[ 23:50:19:934 ] [ Info ] [ ft ] event is BufWinEnter
[ 23:50:19:934 ] [ Info ] [ ft ] ev.buf is 9
[ 23:50:19:934 ] [ Info ] [ ft ] nvim_get_current_buf() is 9
[ 23:50:19:934 ] [ Info ] [ ft ] nvim_get_current_win() is 1008
[ 23:50:19:934 ] [ Info ] [ ft ] real_current_win's buf is7
[ 23:50:19:953 ] [ Info ] [ ft ] -----------------------------------------------------
[ 23:50:19:953 ] [ Info ] [ ft ] event is Syntax
[ 23:50:19:953 ] [ Info ] [ ft ] ev.buf is 9
[ 23:50:19:953 ] [ Info ] [ ft ] nvim_get_current_buf() is 9
[ 23:50:19:953 ] [ Info ] [ ft ] nvim_get_current_win() is 1008
[ 23:50:19:953 ] [ Info ] [ ft ] real_current_win's buf is7
[ 23:50:19:954 ] [ Info ] [ ft ] -----------------------------------------------------
[ 23:50:19:954 ] [ Info ] [ ft ] event is FileType
[ 23:50:19:954 ] [ Info ] [ ft ] ev.buf is 9
[ 23:50:19:954 ] [ Info ] [ ft ] nvim_get_current_buf() is 9
[ 23:50:19:954 ] [ Info ] [ ft ] nvim_get_current_win() is 1008
[ 23:50:19:954 ] [ Info ] [ ft ] real_current_win's buf is7
[ 23:50:19:954 ] [ Info ] [ ft ] -----------------------------------------------------
[ 23:50:19:954 ] [ Info ] [ ft ] event is OptionSet
[ 23:50:19:954 ] [ Info ] [ ft ] ev.buf is 0
[ 23:50:19:954 ] [ Info ] [ ft ] nvim_get_current_buf() is 9
[ 23:50:19:954 ] [ Info ] [ ft ] nvim_get_current_win() is 1008
[ 23:50:19:954 ] [ Info ] [ ft ] real_current_win's buf is7
[ 23:50:19:954 ] [ Info ] [ ft ] -----------------------------------------------------
[ 23:50:19:954 ] [ Info ] [ ft ] nvim_get_current_buf() is 7
[ 23:50:19:954 ] [ Info ] [ ft ] nvim_get_current_win() is 1000
可以看到,在 event 触发 callback 函数内 nvim_get_current_win 和 nvim_get_current_buf 都临时被修改了。
测试一下,不开窗口效果呢?
local log = require("logger").derive("ft")
log.info("nvim_get_current_buf() is " .. vim.api.nvim_get_current_buf())
log.info("nvim_get_current_win() is " .. vim.api.nvim_get_current_win())
log.info("-----------------------------------------------------")
local real_current_win = vim.api.nvim_get_current_win()
local newbuf = vim.api.nvim_create_buf(true, false)
local events = {}
for _, v in ipairs(vim.fn.getcompletion("", "event")) do
if not vim.endswith(v, "Cmd") then
table.insert(events, v)
end
end
local id = vim.api.nvim_create_autocmd(events, {
group = vim.api.nvim_create_augroup("test_ft", { clear = true }),
pattern = { "*" },
callback = function(ev)
log.info("-----------------------------------------------------")
log.info("event is " .. ev.event)
log.info("ev.buf is " .. ev.buf)
log.info("nvim_get_current_buf() is " .. vim.api.nvim_get_current_buf())
log.info("nvim_get_current_win() is " .. vim.api.nvim_get_current_win())
log.info("real_current_win's buf is" .. vim.api.nvim_win_get_buf(real_current_win))
end,
})
-- vim.api.nvim_open_win(newbuf, false, { split = "above" })
vim.api.nvim_set_option_value("filetype", "test123", { buf = newbuf })
vim.api.nvim_del_autocmd(id)
log.info("-----------------------------------------------------")
log.info("nvim_get_current_buf() is " .. vim.api.nvim_get_current_buf())
log.info("nvim_get_current_win() is " .. vim.api.nvim_get_current_win())
[ 23:53:49:058 ] [ Info ] [ ft ] nvim_get_current_buf() is 10
[ 23:53:49:058 ] [ Info ] [ ft ] nvim_get_current_win() is 1000
[ 23:53:49:058 ] [ Info ] [ ft ] -----------------------------------------------------
[ 23:53:49:078 ] [ Info ] [ ft ] -----------------------------------------------------
[ 23:53:49:078 ] [ Info ] [ ft ] event is Syntax
[ 23:53:49:078 ] [ Info ] [ ft ] ev.buf is 12
[ 23:53:49:078 ] [ Info ] [ ft ] nvim_get_current_buf() is 12
[ 23:53:49:078 ] [ Info ] [ ft ] nvim_get_current_win() is 1001
[ 23:53:49:078 ] [ Info ] [ ft ] real_current_win's buf is10
[ 23:53:49:079 ] [ Info ] [ ft ] -----------------------------------------------------
[ 23:53:49:079 ] [ Info ] [ ft ] event is FileType
[ 23:53:49:079 ] [ Info ] [ ft ] ev.buf is 12
[ 23:53:49:079 ] [ Info ] [ ft ] nvim_get_current_buf() is 12
[ 23:53:49:079 ] [ Info ] [ ft ] nvim_get_current_win() is 1001
[ 23:53:49:079 ] [ Info ] [ ft ] real_current_win's buf is10
[ 23:53:49:079 ] [ Info ] [ ft ] -----------------------------------------------------
[ 23:53:49:079 ] [ Info ] [ ft ] event is OptionSet
[ 23:53:49:079 ] [ Info ] [ ft ] ev.buf is 0
[ 23:53:49:079 ] [ Info ] [ ft ] nvim_get_current_buf() is 12
[ 23:53:49:079 ] [ Info ] [ ft ] nvim_get_current_win() is 1001
[ 23:53:49:079 ] [ Info ] [ ft ] real_current_win's buf is10
[ 23:53:49:079 ] [ Info ] [ ft ] -----------------------------------------------------
[ 23:53:49:079 ] [ Info ] [ ft ] nvim_get_current_buf() is 10
[ 23:53:49:079 ] [ Info ] [ ft ] nvim_get_current_win() is 1000
这窗口 1001 是什么鬼?临时隐藏窗口?
local log = require("logger").derive("ft")
log.info("nvim_get_current_buf() is " .. vim.api.nvim_get_current_buf())
log.info("nvim_get_current_win() is " .. vim.api.nvim_get_current_win())
log.info("-----------------------------------------------------")
local real_current_win = vim.api.nvim_get_current_win()
local newbuf = vim.api.nvim_create_buf(true, false)
local events = {}
for _, v in ipairs(vim.fn.getcompletion("", "event")) do
if not vim.endswith(v, "Cmd") then
table.insert(events, v)
end
end
local id = vim.api.nvim_create_autocmd(events, {
group = vim.api.nvim_create_augroup("test_ft", { clear = true }),
pattern = { "*" },
callback = function(ev)
log.info("-----------------------------------------------------")
log.info("event is " .. ev.event)
log.info("ev.buf is " .. ev.buf)
log.info("nvim_get_current_buf() is " .. vim.api.nvim_get_current_buf())
log.info("nvim_get_current_win() is " .. vim.api.nvim_get_current_win())
log.info('win count is ' .. vim.fn.winnr('$'))
log.info('winconfig is ' .. vim.inspect(vim.api.nvim_win_get_config(vim.api.nvim_get_current_win())))
log.info("real_current_win's buf is" .. vim.api.nvim_win_get_buf(real_current_win))
end,
})
-- vim.api.nvim_open_win(newbuf, false, { split = "above" })
vim.api.nvim_set_option_value("filetype", "test123", { buf = newbuf })
vim.api.nvim_del_autocmd(id)
log.info("-----------------------------------------------------")
log.info("nvim_get_current_buf() is " .. vim.api.nvim_get_current_buf())
log.info("nvim_get_current_win() is " .. vim.api.nvim_get_current_win())
[ 23:57:49:249 ] [ Info ] [ ft ] nvim_get_current_buf() is 9
[ 23:57:49:249 ] [ Info ] [ ft ] nvim_get_current_win() is 1000
[ 23:57:49:249 ] [ Info ] [ ft ] -----------------------------------------------------
[ 23:57:49:268 ] [ Info ] [ ft ] -----------------------------------------------------
[ 23:57:49:268 ] [ Info ] [ ft ] event is Syntax
[ 23:57:49:268 ] [ Info ] [ ft ] ev.buf is 13
[ 23:57:49:268 ] [ Info ] [ ft ] nvim_get_current_buf() is 13
[ 23:57:49:268 ] [ Info ] [ ft ] nvim_get_current_win() is 1001
[ 23:57:49:268 ] [ Info ] [ ft ] win count is 2
[ 23:57:49:268 ] [ Info ] [ ft ] winconfig is {
anchor = "NW",
col = 0,
external = false,
focusable = false,
height = 5,
hide = false,
mouse = false,
relative = "editor",
row = 0,
width = 168,
zindex = 50
}
[ 23:57:49:268 ] [ Info ] [ ft ] real_current_win's buf is9
[ 23:57:49:269 ] [ Info ] [ ft ] -----------------------------------------------------
[ 23:57:49:269 ] [ Info ] [ ft ] event is FileType
[ 23:57:49:269 ] [ Info ] [ ft ] ev.buf is 13
[ 23:57:49:269 ] [ Info ] [ ft ] nvim_get_current_buf() is 13
[ 23:57:49:269 ] [ Info ] [ ft ] nvim_get_current_win() is 1001
[ 23:57:49:269 ] [ Info ] [ ft ] win count is 2
[ 23:57:49:270 ] [ Info ] [ ft ] winconfig is {
anchor = "NW",
col = 0,
external = false,
focusable = false,
height = 5,
hide = false,
mouse = false,
relative = "editor",
row = 0,
width = 168,
zindex = 50
}
[ 23:57:49:270 ] [ Info ] [ ft ] real_current_win's buf is9
[ 23:57:49:270 ] [ Info ] [ ft ] -----------------------------------------------------
[ 23:57:49:270 ] [ Info ] [ ft ] event is OptionSet
[ 23:57:49:270 ] [ Info ] [ ft ] ev.buf is 0
[ 23:57:49:270 ] [ Info ] [ ft ] nvim_get_current_buf() is 13
[ 23:57:49:270 ] [ Info ] [ ft ] nvim_get_current_win() is 1001
[ 23:57:49:270 ] [ Info ] [ ft ] win count is 2
[ 23:57:49:270 ] [ Info ] [ ft ] winconfig is {
anchor = "NW",
col = 0,
external = false,
focusable = false,
height = 5,
hide = false,
mouse = false,
relative = "editor",
row = 0,
width = 168,
zindex = 50
}
[ 23:57:49:270 ] [ Info ] [ ft ] real_current_win's buf is9
[ 23:57:49:270 ] [ Info ] [ ft ] -----------------------------------------------------
[ 23:57:49:270 ] [ Info ] [ ft ] nvim_get_current_buf() is 9
[ 23:57:49:270 ] [ Info ] [ ft ] nvim_get_current_win() is 1000
这里说明一下,即便是 nvim_open_win 没有执行,Neovim 也会新建一个 autocmd windows,使用 win_gettype() 函数可以获取值为 “autocmd”。