以前偶尔也会录制一些动图,使用的是 LICEcap,这也是一个开源项目,但是不能录制成视频。 在网上搜到不少的录视频的软件,比较火热的是班迪录屏,但是我还是倾向于免费开源方案。
Neovim 增加了一个 vim.on_key
,这个函数在按键按下后会触发。借助这个函数及 Neovim 的悬浮窗口,
实现了一个按键弹窗提示的效果插件 record-key.nvim。
默认的着色是 Normal
高亮组,如果需要突出显示,可以设置为:
require('record-key').setup({
timeout = 3000,
max_count = 7,
winhighlight = 'NormalFloat:Todo,FloatBorder:WinSeparator',
})
record-screen.nvim 是一个 Neovim 屏幕录制的插件, 借助 ffmpeg 这个命令和 Neovim 的异步机制。
前面的设置是只录制桌面图像,但是在日常视频录制的过程中,还是会涉及到其他的一些设备,比如麦克风、摄像头、音响等等。
首先使用 ffmpeg -list_devices true -f dshow -i dummy
命令获取设备列表:
ffmpeg version 7.1.1-full_build-www.gyan.dev Copyright (c) 2000-2025 the FFmpeg developers
built with gcc 14.2.0 (Rev1, Built by MSYS2 project)
configuration: --enable-gpl --enable-version3 --enable-static --disable-w32threads --disable-autodetect --enable-fontconfig --enable-iconv --enable-gnutls --enable-lcms2 --enable-libxml2 --enable-gmp --enable-bzlib --enable-lzma --enable-libsnappy --enable-zlib --enable-librist --enable-libsrt --enable-libssh --enable-libzmq --enable-avisynth --enable-libbluray --enable-libcaca --enable-libdvdnav --enable-libdvdread --enable-sdl2 --enable-libaribb24 --enable-libaribcaption --enable-libdav1d --enable-libdavs2 --enable-libopenjpeg --enable-libquirc --enable-libuavs3d --enable-libxevd --enable-libzvbi --enable-libqrencode --enable-librav1e --enable-libsvtav1 --enable-libvvenc --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxavs2 --enable-libxeve --enable-libxvid --enable-libaom --enable-libjxl --enable-libvpx --enable-mediafoundation --enable-libass --enable-frei0r --enable-libfreetype --enable-libfribidi --enable-libharfbuzz --enable-liblensfun --enable-libvidstab --enable-libvmaf --enable-libzimg --enable-amf --enable-cuda-llvm --enable-cuvid --enable-dxva2 --enable-d3d11va --enable-d3d12va --enable-ffnvcodec --enable-libvpl --enable-nvdec --enable-nvenc --enable-vaapi --enable-libshaderc --enable-vulkan --enable-libplacebo --enable-opencl --enable-libcdio --enable-libgme --enable-libmodplug --enable-libopenmpt --enable-libopencore-amrwb --enable-libmp3lame --enable-libshine --enable-libtheora --enable-libtwolame --enable-libvo-amrwbenc --enable-libcodec2 --enable-libilbc --enable-libgsm --enable-liblc3 --enable-libopencore-amrnb --enable-libopus --enable-libspeex --enable-libvorbis --enable-ladspa --enable-libbs2b --enable-libflite --enable-libmysofa --enable-librubberband --enable-libsoxr --enable-chromaprint
libavutil 59. 39.100 / 59. 39.100
libavcodec 61. 19.101 / 61. 19.101
libavformat 61. 7.100 / 61. 7.100
libavdevice 61. 3.100 / 61. 3.100
libavfilter 10. 4.100 / 10. 4.100
libswscale 8. 3.100 / 8. 3.100
libswresample 5. 3.100 / 5. 3.100
libpostproc 58. 3.100 / 58. 3.100
[dshow @ 000001f024fc6d40] "Integrated Camera" (video)
[dshow @ 000001f024fc6d40] Alternative name "@device_pnp_\\?\usb#vid_13d3&pid_5419&mi_00#7&17d116b8&1&0000#{65e8773d-8f56-11d0-a3b9-00a0c9223196}\global"
[dshow @ 000001f024fc6d40] "麦克风阵列 (Realtek(R) Audio)" (audio)
[dshow @ 000001f024fc6d40] Alternative name "@device_cm_{33D9A762-90C8-11D0-BD43-00A0C911CE86}\wave_{253B9838-6E9C-47DB-A420-848E63B3931C}"
[in#0 @ 000001f024fad380] Error opening input: Immediate exit requested
Error opening input file dummy.
从上述的输出内容,不难看出,目前支持的设备仅仅有摄像头Integrated Camera
和麦克风麦克风阵列 (Realtek(R) Audio)
,并没有扬声器,解决方法如下:
一、鼠标右键点击桌面右下角音量图标
二、打开更多音量设置
三、点击录制、右击立体声混声启用
这时候再执行 ffmpeg -list_devices true -f dshow -i dummy
输出为:
ffmpeg version 7.1.1-full_build-www.gyan.dev Copyright (c) 2000-2025 the FFmpeg developers
built with gcc 14.2.0 (Rev1, Built by MSYS2 project)
configuration: --enable-gpl --enable-version3 --enable-static --disable-w32threads --disable-autodetect --enable-fontconfig --enable-iconv --enable-gnutls --enable-lcms2 --enable-libxml2 --enable-gmp --enable-bzlib --enable-lzma --enable-libsnappy --enable-zlib --enable-librist --enable-libsrt --enable-libssh --enable-libzmq --enable-avisynth --enable-libbluray --enable-libcaca --enable-libdvdnav --enable-libdvdread --enable-sdl2 --enable-libaribb24 --enable-libaribcaption --enable-libdav1d --enable-libdavs2 --enable-libopenjpeg --enable-libquirc --enable-libuavs3d --enable-libxevd --enable-libzvbi --enable-libqrencode --enable-librav1e --enable-libsvtav1 --enable-libvvenc --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxavs2 --enable-libxeve --enable-libxvid --enable-libaom --enable-libjxl --enable-libvpx --enable-mediafoundation --enable-libass --enable-frei0r --enable-libfreetype --enable-libfribidi --enable-libharfbuzz --enable-liblensfun --enable-libvidstab --enable-libvmaf --enable-libzimg --enable-amf --enable-cuda-llvm --enable-cuvid --enable-dxva2 --enable-d3d11va --enable-d3d12va --enable-ffnvcodec --enable-libvpl --enable-nvdec --enable-nvenc --enable-vaapi --enable-libshaderc --enable-vulkan --enable-libplacebo --enable-opencl --enable-libcdio --enable-libgme --enable-libmodplug --enable-libopenmpt --enable-libopencore-amrwb --enable-libmp3lame --enable-libshine --enable-libtheora --enable-libtwolame --enable-libvo-amrwbenc --enable-libcodec2 --enable-libilbc --enable-libgsm --enable-liblc3 --enable-libopencore-amrnb --enable-libopus --enable-libspeex --enable-libvorbis --enable-ladspa --enable-libbs2b --enable-libflite --enable-libmysofa --enable-librubberband --enable-libsoxr --enable-chromaprint
libavutil 59. 39.100 / 59. 39.100
libavcodec 61. 19.101 / 61. 19.101
libavformat 61. 7.100 / 61. 7.100
libavdevice 61. 3.100 / 61. 3.100
libavfilter 10. 4.100 / 10. 4.100
libswscale 8. 3.100 / 8. 3.100
libswresample 5. 3.100 / 5. 3.100
libpostproc 58. 3.100 / 58. 3.100
[dshow @ 0000020942ff6cc0] "Integrated Camera" (video)
[dshow @ 0000020942ff6cc0] Alternative name "@device_pnp_\\?\usb#vid_13d3&pid_5419&mi_00#7&17d116b8&1&0000#{65e8773d-8f56-11d0-a3b9-00a0c9223196}\global"
[dshow @ 0000020942ff6cc0] "麦克风阵列 (Realtek(R) Audio)" (audio)
[dshow @ 0000020942ff6cc0] Alternative name "@device_cm_{33D9A762-90C8-11D0-BD43-00A0C911CE86}\wave_{253B9838-6E9C-47DB-A420-848E63B3931C}"
[dshow @ 0000020942ff6cc0] "立体声混音 (Realtek(R) Audio)" (audio)
[dshow @ 0000020942ff6cc0] Alternative name "@device_cm_{33D9A762-90C8-11D0-BD43-00A0C911CE86}\wave_{A04F0465-843E-4E64-891D-31A28192D215}"
[in#0 @ 0000020942fdd340] Error opening input: Immediate exit requested
Error opening input file dummy.
可以看到有三个设备:
Integrated Camera
麦克风阵列 (Realtek(R) Audio)
立体声混音 (Realtek(R) Audio)
当然,也可以安装开源项目 rdp/screen-capture-recorder-to-video-windows-free, 安装完成后,`` 命令输出如下:
ffmpeg version 7.1.1-full_build-www.gyan.dev Copyright (c) 2000-2025 the FFmpeg developers
built with gcc 14.2.0 (Rev1, Built by MSYS2 project)
configuration: --enable-gpl --enable-version3 --enable-static --disable-w32threads --disable-autodetect --enable-fontconfig --enable-iconv --enable-gnutls --enable-lcms2 --enable-libxml2 --enable-gmp --enable-bzlib --enable-lzma --enable-libsnappy --enable-zlib --enable-librist --enable-libsrt --enable-libssh --enable-libzmq --enable-avisynth --enable-libbluray --enable-libcaca --enable-libdvdnav --enable-libdvdread --enable-sdl2 --enable-libaribb24 --enable-libaribcaption --enable-libdav1d --enable-libdavs2 --enable-libopenjpeg --enable-libquirc --enable-libuavs3d --enable-libxevd --enable-libzvbi --enable-libqrencode --enable-librav1e --enable-libsvtav1 --enable-libvvenc --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxavs2 --enable-libxeve --enable-libxvid --enable-libaom --enable-libjxl --enable-libvpx --enable-mediafoundation --enable-libass --enable-frei0r --enable-libfreetype --enable-libfribidi --enable-libharfbuzz --enable-liblensfun --enable-libvidstab --enable-libvmaf --enable-libzimg --enable-amf --enable-cuda-llvm --enable-cuvid --enable-dxva2 --enable-d3d11va --enable-d3d12va --enable-ffnvcodec --enable-libvpl --enable-nvdec --enable-nvenc --enable-vaapi --enable-libshaderc --enable-vulkan --enable-libplacebo --enable-opencl --enable-libcdio --enable-libgme --enable-libmodplug --enable-libopenmpt --enable-libopencore-amrwb --enable-libmp3lame --enable-libshine --enable-libtheora --enable-libtwolame --enable-libvo-amrwbenc --enable-libcodec2 --enable-libilbc --enable-libgsm --enable-liblc3 --enable-libopencore-amrnb --enable-libopus --enable-libspeex --enable-libvorbis --enable-ladspa --enable-libbs2b --enable-libflite --enable-libmysofa --enable-librubberband --enable-libsoxr --enable-chromaprint
libavutil 59. 39.100 / 59. 39.100
libavcodec 61. 19.101 / 61. 19.101
libavformat 61. 7.100 / 61. 7.100
libavdevice 61. 3.100 / 61. 3.100
libavfilter 10. 4.100 / 10. 4.100
libswscale 8. 3.100 / 8. 3.100
libswresample 5. 3.100 / 5. 3.100
libpostproc 58. 3.100 / 58. 3.100
[dshow @ 0000020d0d8f6d40] "Integrated Camera" (video)
[dshow @ 0000020d0d8f6d40] Alternative name "@device_pnp_\\?\usb#vid_13d3&pid_5419&mi_00#7&17d116b8&1&0000#{65e8773d-8f56-11d0-a3b9-00a0c9223196}\global"
[dshow @ 0000020d0d8f6d40] "screen-capture-recorder" (video)
[dshow @ 0000020d0d8f6d40] Alternative name "@device_sw_{860BB310-5D01-11D0-BD3B-00A0C911CE86}\{4EA69364-2C8A-4AE6-A561-56E4B5044439}"
[dshow @ 0000020d0d8f6d40] "立体声混音 (Realtek(R) Audio)" (audio)
[dshow @ 0000020d0d8f6d40] Alternative name "@device_cm_{33D9A762-90C8-11D0-BD43-00A0C911CE86}\wave_{A04F0465-843E-4E64-891D-31A28192D215}"
[dshow @ 0000020d0d8f6d40] "virtual-audio-capturer" (audio)
[dshow @ 0000020d0d8f6d40] Alternative name "@device_sw_{33D9A762-90C8-11D0-BD43-00A0C911CE86}\{8E146464-DB61-4309-AFA1-3578E927E935}"
[dshow @ 0000020d0d8f6d40] "麦克风阵列 (Realtek(R) Audio)" (audio)
[dshow @ 0000020d0d8f6d40] Alternative name "@device_cm_{33D9A762-90C8-11D0-BD43-00A0C911CE86}\wave_{253B9838-6E9C-47DB-A420-848E63B3931C}"
[in#0 @ 0000020d0d8dd380] Error opening input: Immediate exit requested
Error opening input file dummy.
此时多出了两个设备:
screen-capture-recorder
virtual-audio-capturer
如果仅仅需要录制桌面,而不需要录制摄像头和声音,可以使用如下配置:
require('record-screen').setup({
command = 'ffmpeg',
argvs = {
'-f',
'gdigrab',
'-i',
'desktop',
'-pix_fmt',
'yuv420p',
'-f',
'mp4',
},
})
require('record-screen').setup({
cmd = 'ffmpeg',
argvs = {
'-f',
'dshow',
'-i',
'audio=麦克风阵列 (Realtek(R) Audio)',
'-f',
'dshow',
'-i',
'audio=立体声混音 (Realtek(R) Audio)',
'-filter_complex',
'amix=inputs=2:duration=first:dropout_transition=2',
'-f',
'gdigrab',
'-r',
'60',
'-draw_mouse',
'1',
'-i',
'desktop',
'-pix_fmt',
'yuv420p',
'-f',
'mp4',
},
})
录制视频时,偶尔会遇到类似于 real-time buffer frame dropped
这样的错误提示,
参考issue 136
提到的解决方案,增加参数 -rtbufsize 1500M
使用 nvim-plug 插件管理器:
require('plug').add({
{
'wsdjeg/record-key.nvim',
cmds = { 'RecordKeyToggle' },
config_before = function()
vim.keymap.set(
'n',
'<leader>rk',
'<cmd>RecordKeyToggle<cr>',
{ silent = true }
)
end,
},
{
'wsdjeg/record-screen.nvim',
depends = {
{ 'wsdjeg/job.nvim' },
{ 'wsdjeg/notify.nvim' },
},
config = function()
require('plugins.record-screen')
end,
},
})
插件详细配置参考文件 nvim-config/lua/plugins/record-screen.lua
通过这个配置文件,我新建了一个用户自定义命令 :RecordScreen
,支持如下参数:
-camera
: 录制摄像头-speaker
: 录制扬声器--microphone
: 录制麦克风使用 :RecordScreen stop
停止录屏。
在使用 Neovim 打开某个文件时,我希望 Neovim 自动把当前目录切换到该文件所在的项目根目录。 其实,能实现这一功能的有不少的插件,我最早使用的是 vim-rooter,但是后来因为切换到了 Neovim, 因此使用 Lua 重写了该功能,这个功能早期是 SpaceVim 的内置的, 在 SpaceVim 项目停止维护后独立成单独的插件:rooter.nvim。
可以使用任意插件管理器进行安装,比如 nvim-plug
require('plug').add({
{
'wsdjeg/rooter.nvim',
config = function()
require('rooter').setup({})
end,
},
})
以下是默认的配置:
require('rooter').setup({
root_patterns = { '.git/' },
outermost = true,
enable_cache = true,
project_non_root = '', -- this can be '', 'home' or 'current'
enable_logger = true, -- enable runtime log via logger.nvim
})
project_non_root
: 配置打开非项目文件时的行为outermost
: 若设为 true
,那么通过 root_patterns
检索到的多个目录时,取最外层目录。rooter.nvim 自带 telescope.nvim 拓展,可以使用 :Telescope project
列出过往打开过的项目。
在插件运行过程中产生的日志信息,可以使用 logger.nvim 进行查看。 如果有这一需求,那么在安装 rooter.nvim 时,需要添加相应的依赖插件。
require('plug').add({
{
'wsdjeg/rooter.nvim',
config = function()
require('rooter').setup({})
end,
depends = {
{
'wsdjeg/logger.nvim',
},
},
},
})
通过 reg_callback
可以设置 callback 函数,该函数会在项目切换时被调用。
local function update_ctags_option()
local project_root = vim.fn.getcwd()
local dir = require('util').unify_path(require('tags').cache_dir)
.. require('util').path_to_fname(project_root)
table.insert(tags, dir .. '/tags')
vim.o.tags = table.concat(tags, ',')
end
require('rooter').reg_callback(update_gtags_option)
logger.nvim
提供了一个基础的日志框架,不同的插件可以共用一个日志系统。
和安装其他插件一样,可以使用nvim-plug安装:
require('plug').add({
'wsdjeg/logger.nvim',
config = function()
require('logger').setup({
-- the level only can be:
-- 0 : log debug, info, warn, error messages
-- 1 : log info, warn, error messages
-- 2 : log warn, error messages
-- 3 : log error messages
level = 0,
})
end,
})
比如新建了一个插件 fyz.nvim
,此时可以添加一个文件 lua/fyz/log.lua
:
local M = {}
local logger
function M.info(msg)
if not logger then
pcall(function()
logger = require('logger').derive('fyz')
logger.info('hello world')
end)
else
logger.info('hello world')
end
end
return M
在自己的插件中就可以使用:
local log = require('fyz.log')
log.info('this is log from fyz.nvim')
可以使用 logger.viewRuntimeLog()
查看所有的日志输出,其中就会有如下一行:
[ fyz ] [23:22:50:576] [ Info ] this is log from fyz.nvim
关于任务(Tasks)管理,实际上早在 2020 年的时候就已经给 SpaceVim 增加了 Tasks 支持,参考的是 Vscode Tasks Manager 的实现。 最早的版本使用 Vim Script 实现的,大约在 2023 年的时候增加了 Lua 实现版本, 不过这些都是在 SpaceVim 内置的插件。
现在,SpaceVim 已经不再维护,而这些常用的功能,我也会陆续剥离出来单独形成插件,这篇文章主要介绍 tasks.nvim
可以使用任意插件管理器进行安装,这里以 nvim-plug 为例:
require('plug').add({
{
'wsdjeg/tasks.nvim',
depends = {
{
'wsdjeg/code-runner.nvim',
},
},
config = function()
require('tasks').setup({
global_tasks = '~/.tasks.toml',
local_tasks = '.tasks.toml',
provider = { 'npm' },
})
end,
},
})
tasks.nvim
提供了三个常用命令:
:TasksEdit
:用于打开 tasks 配置文件,默认打开的是项目配置文件,加上感叹号(:TasksEdit!
)则打开全局配置文件。:TasksList
:使用分屏列出所有 tasks:TasksSelect
:选择某个 task 并执行当然,如果你也安装了 telescope.nvim
那么,可以使用 :Telescope tasks
模糊搜索可用的 tasks.
Vscode 有一个非常出名的插件,叫做 Code Runner,我曾经也给 SpaceVim 添加了这么一个功能。
现在将这一功能剥离出来形成一个单独独立的 neovim 插件:code-runner.nvim
可以使用任意插件管理器进行安装,这里以 nvim-plug 为例:
require('plug').add({
{
'wsdjeg/code-runner.nvim',
config = function()
require('code-runner').setup({
runners = {
lua = { exe = 'lua', opt = { '-' }, usestdin = true },
},
enter_win = false,
})
end,
},
})
借助 rooter.nvim 插件,可以在切换项目时,读取 .clang
文件内容,并且更新 c 语言的 runner。具体代码实现:
local c_runner = {
exe = 'gcc',
targetopt = '-o',
usestdin = true,
opt = { '-std=c11', '-xc', '-' },
}
require('code-runner').setup({
runners = {
c = { c_runner, '#TEMP#' },
},
})
vim.keymap.set(
'n',
'<leader>lr',
'<cmd>lua require("code-runner").open()<cr>',
{ silent = true }
)
-- make sure rooter.nvim plugin is loaded before code-runner
local function update_clang_flag()
if vim.fn.filereadable('.clang') == 1 then
local flags = vim.fn.readfile('.clang')
local opt = { '-std=c11' }
for _, v in ipairs(flags) do
table.insert(opt, v)
end
table.insert(opt, '-xc')
table.insert(opt, '-')
c_runner.opt = opt
end
end
require('rooter').reg_callback(update_clang_flag)
这篇文章给大家介绍一下我开发的新的 Neovim 插件管理器 nvim-plug。
不管是 Neovim 还是 Vim,已经有太多太多的插件管理器了,
最早期时候刚接触 Vim 的时候,从刚开始编辑 vimrc 时候开始,
只是简单的 set rtp+=path/to/plugdir
,到后来接触到了 Bundle,后来这个插件改名为 Vundle,
仓库的 README 中文版还是我翻译的。
我的插件管理器使用经历: 直接设置rtp -> Bundle(Vundle) -> vim-plug -> neobundle.vim -> dein.vim
越往后,插件管理器增加的功能越多,但实际上本质并未改变, 我前面写过一篇插件管理器的运行机制《Neovim 和 Vim 插件管理器的实现逻辑》 。
我有很长很长一段时间在使用的 dein.vim,包括现在 Vim 下还是使用 dein,。 实际上 dein.vim 已经实现了我所需要的所有的插件管理器的核心逻辑功能,但是,目前我大部分情况使用的是 Neovim, 因为 dein 还是 vimscript 写的,因此速度上在插件很多的时候,还是有些慢的, 因此使用 Lua 来实现一个 Neovim 插件管理器:nvim-plug
目前已实现的功能包括:
config
、config_before
、config_after
三种函数,分别在不同时机执行。其实关于插件管理器的界面,因为历史原因,一直习惯了 Vundle 的界面模式,包括 Vim 下知名插件 vim-plug 也是使用这样类似的插件管理器界面。 而我之前使用的 dein.vim 没有提供默认的界面,为此我还写过一个插件dein-ui.vim。那么在设计 nvim-plug 这个插件管理器的时候, 自然而然就实现了一个类似于 Vundle 的可视化界面,但是考虑到有可能会有其他操作界面模式的需求,因此 nvim-plug 这个插件管理器的操作界面是设计成界面和逻辑分离的模式。
如果兴趣,也可以实现一个新的插件列表界面,可以通过如下模式进行修改:
--- your custom UI
local function on_ui_update(name, data)
-- logic
end
require('plug').setup({
bundle_dir = 'D:/bundle_dir',
max_processes = 5, -- max number of processes used for nvim-plug job
base_url = 'https://github.com',
ui = on_ui_update, -- default ui is notify, use `default` for split window UI
})
上述的 on_ui_update
函数会在插件下载、更新、build 等命令执行过程种被调用,函数调用时被传入两个参数:插件名称、界面更新数据 plugUiData。
The plugUiData is table with following keys:
plugUiData
是一个 Lua table,其键值如下:
键值 | 描述 |
---|---|
clone_done |
boolead, is true when clone successfully |
command |
string, clone, pull or build |
clone_process |
string, git clone progress, such as 16% (160/1000) |
clone_done |
boolean, git clone exit status |
building |
boolean |
build_done |
boolean |
pull_done |
boolean |
pull_process |
string |