文章时效性提示
这是一篇发布于 173 天前的文章,部分信息可能已发生改变,请注意甄别。
最近在使用neovim和LazyVim配置写代码,感觉很不错. 得益于neovim简单的功能和强大的第三方生态,可以很简单的写出一个插件. 这里根据官方文档搭配lazyvim等资料写一个简单插件.
Lua
lua语法本身并不复杂,参考教程
- Learn Lua in Y Minutes
- lua-users.org
- Lua 教程 | 菜鸟教程
- Welcome to Lua: Your First Step into Programming - Lua Tutorial - W3schools
- Programming in Lua (first edition)
相关项目
- uhub/awesome-lua: A curated list of awesome Lua frameworks, libraries and software.
- All In One Simple List | awesome lua
- LÖVE - Free 2D Game Engine 2D游戏引擎
表与模块
1 | l1 = {a=2,b=4} |
1 | -- Suppose the file mod.lua looks like this: |
重要函数
重要的库: io,string.os,table,math
库 | 函数 | 作用 |
---|---|---|
string | string.upper(s) | Converts s to uppercase |
string.lower(s) | Converts s to lowercase | |
string.len(s) | Returns the length of s | |
math | math.max(x, …) | Returns the maximum value among its arguments |
math.min(x, …) | Returns the minimum value among its arguments | |
math.random([m [, n]]) | Generates a random number | |
table | table.insert(t, [pos,] value) | Inserts value into t at position pos |
table.remove(t [, pos]) | Removes from t the element at position pos | |
table.sort(t [, comp]) | Sorts table elements in a given order |
require
在 Lua 中,require
函数用于加载和初始化模块。它会按照一定的顺序搜索指定的模块,并根据配置从不同的目录中加载模块文件。具体来说,require
的查找路径由 package.path
和 package.cpath
两个变量定义。
package.path
用途:用于指定 Lua 模块(
.lua
文件)的搜索路径。默认值:在不同平台和 Lua 版本上,默认值可能有所不同,但通常类似于以下格式:
1
2
3
4
5
6
7
8package.path = './?.lua;' .. -- 当前目录下的 .lua 文件
'./?/init.lua;' .. -- 当前目录下的子目录中的 init.lua 文件
'/usr/local/share/lua/5.4/?.lua;' ..
'/usr/local/share/lua/5.4/?/init.lua;' ..
'/usr/local/lib/lua/5.4/?.lua;' ..
'/usr/local/lib/lua/5.4/?/init.lua;' ..
'./lua/?.lua;' ..
'./lua/?/init.lua'模式说明:
?
是一个占位符,代表模块名。?/init.lua
表示如果模块名是目录,则尝试加载该目录下的init.lua
文件。
package.cpath
用途:用于指定 C 模块(即使用 C 或 C++ 编写的模块,通常是
.so
或.dll
文件)的搜索路径。默认值:同样地,这取决于平台和 Lua 版本,但一般包括如下路径:
1
2
3
4
5package.cpath = './?.so;' .. -- 当前目录下的共享库文件
'./?.dll;' .. -- Windows 上的动态链接库文件
'./loadall.so;' .. -- 加载所有符号的共享库
'/usr/local/lib/lua/5.4/?.so;' ..
'/usr/local/lib/lua/5.4/loadall.so;'模式说明:
?
同样作为模块名的占位符。- 其他部分指定了操作系统特定的库文件扩展名。
package.preload
除了上述路径外,require
还会在 package.preload
表中查找是否已经预加载了相应的模块。如果找到了匹配项,则直接返回对应的函数而不进行文件系统搜索
Neovim的基本功能
主要组件
基础配置
Nvim 支持使用 init.vim 或 init.lua 作为配置文件,但不能同时使用这两个文件。 该文件应放在config目录中、
对于 Linux、BSD 或 macOS,该目录通常为 ~/.config/nvim,而对于
/AppData/Local/nvim/(Windows)。 请注意,可以在 init.vim 中使用 Lua
中使用 Lua,在 init.lua 中使用 Vimscript。 如果想在启动时自动运行任何其他 Lua 脚本,那么
只需将其放入运行时路径中的 plugin/。
nvim中的lua
vim.g
vim.g
是一个 Lua 表,它提供了对全局变量的访问。通过 vim.g
,你可以设置或读取任何定义在全局命名空间中的变量。这对于配置插件或共享状态信息非常有用,因为全局变量可以在整个会话期间保持不变,并且可以在不同的脚本之间传递数据。
vim.cmd
执行vimscript
1 | vim.cmd('echo 42') |
vim.opt
vim.opt
是另一个 Lua 表,但它专注于编辑器的选项配置。它允许用户以一种结构化的方式设置和查询 Vim/Neovim 的各种行为参数。与传统的 :set
命令相比,vim.opt
提供了一个更加直观和易于维护的方式来管理和调整编辑器的行为.可以指定选项的作用范围,比如 vim.o
对于全局选项,vim.bo
对于缓冲区特定选项,vim.wo
对于窗口特定选项
一个特殊的接口 vim.opt,可以方便地与 Lua 中的列表和映射样式选项进行交互:它允许以 Lua 表格的形式访问它们,并提供面向对象的方法来添加和删除条目
In Lua using vim.o
:
1 | vim.o.wildignore = '*.o,*.a,__pycache__' |
In Lua using vim.opt
:
1 | vim.opt.wildignore = { '*.o', '*.a', '__pycache__' } |
vim.api
vim.api.{func}({...}
)
调用带有参数 {…} 的 Nvim API 函数 {func}。 示例:调用 “nvim_get_current_line()” API 函数:
1 | print(tostring(vim.api.nvim_get_current_line())) |
vim.fn
vim.fn
是一个特别设计用来桥接 Vimscript 和 Lua 的接口。它使得开发者能够在 Lua 脚本中调用所有可用的 Vim 内置函数。
- 执行系统命令并捕获输出:
local output = vim.fn.system('uname -a')
- 获取当前文件名:
local filename = vim.fn.expand('%')
- 访问环境变量:
local path = vim.fn.getenv('PATH')
Neovim会默认从某个目录中加载init.lua
,路径如下
OS | PATH |
---|---|
Linux, MacOS | $XDG_CONFIG_HOME/nvim , ~/.config/nvim |
Windows (cmd) | %localappdata%\nvim\ |
Windows (powershell) | $env:LOCALAPPDATA\nvim\ |
vim.system
vim.system({cmd}, {opts}, {on_exit})
运行系统命令,如果 {cmd} 无法运行,则抛出错误信息。
1 | local on_exit = function(obj) |
vim.uv vim.loop
vim.uv暴露 Nvim 用于网络、文件系统和进程管理的 libUV 库的 “luv” Lua 绑定.
vim.uv
是 Neovim 提供的一个接口,它封装了 libuv 库的功能。libuv 是一个用于异步 I/O 的多平台支持库,最初为 Node.js 开发,但因其高效性和跨平台特性而被广泛采用。通过 vim.uv
,Neovim 用户和插件开发者可以直接访问底层的文件系统、网络和其他系统操作功能,从而实现更复杂的应用逻辑或优化性能关键部分
vim.env
编辑器会话中定义的环境变量
vim.schedule
通过事件循环执行函数,避免阻塞
vim.lsp
Nvim 支持语言服务器协议 (LSP),这意味着它充当 LSP 服务器的客户端,并包含一个 Lua 框架 vim.lsp
用于构建增强的 LSP 工具
vim.tbl_deep_extend
Neovim 提供的一个用于深度合并 Lua 表的函数。它允许你将一个或多个表的内容递归地合并到目标表中,而不会简单地覆盖原有的键值对。这对于配置管理、插件开发以及其他需要合并多层级数据结构的场景非常有用
1 | vim.tbl_deep_extend(strategy, target, source1, source2, ...) |
strategy
:指定合并策略,可以是以下之一:
"force"
:强制覆盖目标表中的现有键值。"keep"
:保留目标表中的现有键值,不被源表覆盖。"error"
:如果遇到冲突(即同一个键存在于目标和源表中),则抛出错误。
target:目标表,即将要接收合并结果的表。
source1, source2, …:一个或多个源表,它们的内容将被合并到目标表中。
kickstart
kickstart是配置nvim很好的一个参考. 要点:勤用:help
,:help lua-guide
,可以查阅neovim提供的lua函数
配置全局变量与选项(vim.g&&vim.opt)
1 | -- Set <space> as the leader key |
快捷键(keymap)
1 | -- [[ Basic Keymaps ]] |
指令执行(autocmd)
1 | -- [[ Basic Autocommands ]] |
自动命令是 Vim 命令或 Lua 函数,每当触发一个或多个事件触发时自动执行的 Vim 命令或 Lua 函数。
读取或写入文件,或创建窗口时自动执行。 可以通过Lua使用Nvim API 访问。
安装包管理器
1 | -- [[ Install `lazy.nvim` plugin manager ]] |
vim.fn.stdpath
用于获取标准路径的绝对路径。它可以帮助你轻松找到配置文件、数据文件或缓存文件所在的目录,这对于编写可移植的脚本和插件非常有用。
kind:指定要查询的标准路径类型,可以是以下字符串之一:
"config"
:配置文件的位置。通常对应于$XDG_CONFIG_HOME/nvim
或$HOME/.config/nvim
。"data"
:用户特定的数据文件位置。通常对应于$XDG_DATA_HOME/nvim
或$HOME/.local/share/nvim
。"cache"
:缓存文件的位置。通常对应于$XDG_CACHE_HOME/nvim
或$HOME/.cache/nvim
。"state"
:状态文件的位置(如 swap 文件、undo 文件等)。通常与data
目录相同,但在某些情况下可能会有所不同
v:shell_error最后一条 shell 命令的结果。 非零时,表示最后一条shell 命令出错。 当为零时,表示没有问题。只有当 shell 向 Vim 返回错误代码时才会起作用。当命令无法执行时,通常使用 -1。 执行。 只读。
Vim/Neovim 使用 rtp
来定位各种类型的文件,如:
- 插件 (
plugin/*.vim
) - 脚本 (
autoload/*.vim
,ftplugin/*.vim
) - 语法定义 (
syntax/*.vim
) - 颜色方案 (
colors/*.vim
) - 文档 (
doc/*.txt
) 其他配置文件
加载顺序:当有多个相同名称的文件存在于不同的目录中时,会按照
rtp
中列出的顺序依次查找并加载第一个找到的文件。这意味着较早出现在rtp
中的目录具有更高的优先级。
配置插件管理
1 | -- [[ Configure and install plugins ]] |
NvChad
比kickstart更复杂一点,但也很容易上手. 其配置也是类似,首先包括全局变量、设置选项以及环境变量
1 | local opt = vim.opt |
快捷键
1 | local map = vim.keymap.set |
autocmd
1 | local autocmd = vim.api.nvim_create_autocmd |
LazyVim
neovim的插件编写How to write a neovim plugin in lua与使用lazyvim不同但有相似点
- How I Developed My First Neovim Plugin: A Step-by-Step Guide - DEV Community
- Lazy.nvim: plugin configuration - DEV Community
- r/neovim —- Are there any good starter templates for writing Neovim plugins? : r/neovim
- LazyVim local development · LazyVim/LazyVim · Discussion #1129
- Loading local plugins · LazyVim/LazyVim · Discussion #2202
- How to write a neovim plugin in lua
1 | ├── LICENSE |
plugin和 lua 文件夹是特例,其含义如下:
- plugin 文件夹 该文件夹中的所有文件将在 Neovim 启动时立即执行,如果想设置keymap或autocmd而不管用户是否需要该插件
- lua 文件夹 在大多数情况下,lua 文件夹是您的插件代码所在的文件夹,只有在用户明确需要您的插件时才会执行这些代码,例如 require(‘scratch-buffer’)
插件代码该放哪
对于普通neovim插件,可以放在lua文件夹下,通过neovim加载配置路径中的init.lua
通过require
加载
而在lazyvim中,添加spec.
1 | require("lazy").setup({ |
重新启动 Neovim 或运行 :Lazy sync 来加载插件.有了这种设置,您就可以轻松迭代您的插件: 1. 更改插件代码。 2. 保存文件。 3. 在 Neovim 中运行 :Lazy reload plugin-name 重新加载插件。 4. 运行 :HelloWorld 或使用关键映射测试更改。 此工作流程可实现快速开发和测试,而无需不断重启 Neovim 或手动获取文件。
可以直接使用的nvim配置,lazyvim会自动加载预定义的autocmd,keymaps和options.🔌 Plugin Spec | lazy.nvim
1 | ~/.config/nvim |
写本地插件时直接在plugins目录中即可. 添加插件时可用的选项
Defaults merging rules:
- cmd:命令列表将使用您的自定义命令进行扩展
- event:事件列表将使用您的自定义事件进行扩展
- FT:文件类型列表将扩展为自定义文件类型
- keys:键盘映射列表将使用您的自定义键盘映射进行扩展
- opts:自定义 opts 将与默认 opts 合并
- dependencies:依赖项列表将使用您的自定义依赖项进行扩展
- 任何其他属性都将覆盖默认值
对于 ft
, event
, keys
, cmd
和 opts
您还可以指定一个 values
函数,该函数可以更改默认值,或返回要使用的新值.除此之外,还有init,config函数. Lua 插件遵循一个常见的约定,它们有一个名为 setup
的函数暴露的 Lua 模块。所以当使用 opts
时,是在告诉 lazy.nvim 该插件遵循该约定。因此,lazy.nvim 会将那个 opts
属性传递给插件的 setup
函数。
使用lazyvim写插件的debug方式🚀 Usage | lazy.nvim
小案例 插件重载器
1 | -- init.lua |
1 | -- utils.lua |