Skip to content

Commit a5e7ccc

Browse files
committed
docs: Lua plugin development guide
1 parent 28ab656 commit a5e7ccc

File tree

2 files changed

+116
-86
lines changed

2 files changed

+116
-86
lines changed

runtime/doc/lua-guide.txt

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,18 @@
1010
==============================================================================
1111
Introduction *lua-guide*
1212

13-
This guide will go through the basics of using Lua in Nvim. It is not meant
14-
to be a comprehensive encyclopedia of all available features, nor will it
15-
detail all intricacies. Think of it as a survival kit -- the bare minimum
16-
needed to know to comfortably get started on using Lua in Nvim.
17-
18-
An important thing to note is that this isn't a guide to the Lua language
19-
itself. Rather, this is a guide on how to configure and modify Nvim through
20-
the Lua language and the functions we provide to help with this. Take a look
21-
at |luaref| and |lua-concepts| if you'd like to learn more about Lua itself.
22-
Similarly, this guide assumes some familiarity with the basics of Nvim
13+
This guide introduces the basics of everyday usage of Lua to configure and
14+
operate Nvim. It assumes some familiarity with the (non-Lua) basics of Nvim
2315
(commands, options, mappings, autocommands), which are covered in the
2416
|user-manual|.
2517

18+
This is not a comprehensive encyclopedia of all available features. Think of
19+
it as a survival kit: the bare minimum needed to comfortably get started on
20+
using Lua in Nvim.
21+
22+
See |lua-plugin| for guidance on developing Lua plugins.
23+
See |luaref| and |lua-concepts| for details on the Lua programming language.
24+
2625
------------------------------------------------------------------------------
2726
Some words on the API *lua-guide-api*
2827

runtime/doc/lua-plugin.txt

Lines changed: 107 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,40 @@
22

33
NVIM REFERENCE MANUAL
44

5-
Guide to developing Lua plugins for Nvim
5+
Guide to developing Lua plugins for Nvim
66

77

88
Type |gO| to see the table of contents.
99

1010
==============================================================================
1111
Introduction *lua-plugin*
1212

13-
This is a guide for getting started with Nvim plugin development. It is not
14-
intended as a set of rules, but as a collection of recommendations for good
15-
practices.
13+
This document provides guidance for developing Nvim (Lua) plugins:
1614

17-
For a guide to using Lua in Nvim, please refer to |lua-guide|.
15+
See |lua-guide| for guidance on using Lua to configure and operate Nvim.
16+
See |luaref| and |lua-concepts| for details on the Lua programming language.
17+
18+
==============================================================================
19+
Creating your first plugin *lua-plugin-new*
20+
21+
Any Vimscript or Lua code file that lives in the right directory,
22+
automatically is a "plugin". There's no maniest or "registration" required.
23+
24+
You can try it right now:
25+
26+
1. Visit your config directory: >
27+
:exe 'edit' stdpath('config')
28+
<
29+
2. Create a `plugin/foo.lua` file in there.
30+
3. Add something to it, like: >lua
31+
vim.print('Hello World')
32+
<
33+
4. Start `nvim` and notice that it prints "Hello World" in the messages area.
34+
Check `:messages` if you don't see it.
35+
36+
Besides `plugin/foo.lua`, which is always run at startup, you can define Lua
37+
modules in the `lua/` directory. Those modules aren't loaded until your
38+
`plugin/foo.lua`, the user, calls `require(…)`.
1839

1940
==============================================================================
2041
Type safety *lua-plugin-type-safety*
@@ -24,24 +45,24 @@ virtually immediate feedback.
2445
But for larger projects, this can be a double-edged sword, leaving your plugin
2546
susceptible to unexpected bugs at the wrong time.
2647

27-
You can leverage LuaCATS https://luals.github.io/wiki/annotations/
28-
annotations, along with lua-language-server https://luals.github.io/ to catch
29-
potential bugs in your CI before your plugin's users do.
48+
You can leverage LuaCATS or "emmylua" annotations https://luals.github.io/wiki/annotations/
49+
along with lua-language-server ("LuaLS") https://luals.github.io/ to catch
50+
potential bugs in your CI before your plugin's users do. The Nvim codebase
51+
uses these annotations extensively.
3052

31-
------------------------------------------------------------------------------
32-
Tools *lua-plugin-type-safety-tools*
53+
TOOLS
3354

3455
- lua-typecheck-action https://github.com/marketplace/actions/lua-typecheck-action
3556
- lua-language-server https://luals.github.io
3657

3758
==============================================================================
38-
Keymaps *lua-plugin-keymaps*
59+
Keymaps *lua-plugin-keymaps*
3960

40-
Avoid creating keymaps automatically, unless they are not controversial. Doing
41-
so can easily lead to conflicts with user |mapping|s.
61+
Avoid creating excessive keymaps automatically. Doing so can conflict with
62+
user |mapping|s.
4263

4364
NOTE: An example for uncontroversial keymaps are buffer-local |mapping|s for
44-
specific file types or floating windows.
65+
specific file types or floating windows, or <Plug> mappings.
4566

4667
A common approach to allow keymap configuration is to define a declarative DSL
4768
https://en.wikipedia.org/wiki/Domain-specific_language via a `setup` function.
@@ -81,25 +102,24 @@ Some benefits of exposing a Lua function are:
81102
NOTE: If your function takes an options table, users may still benefit
82103
from |<Plug>| mappings for the most common combinations.
83104

84-
------------------------------------------------------------------------------
85-
Example *lua-plugin-plug-mapping-example*
105+
KEYMAP EXAMPLE
86106

87107
In your plugin:
88108
>lua
89-
vim.keymap.set("n", "<Plug>(SayHello)", function()
90-
print("Hello from normal mode")
109+
vim.keymap.set('n', '<Plug>(SayHello)', function()
110+
print('Hello from normal mode')
91111
end, { noremap = true })
92112

93-
vim.keymap.set("v", "<Plug>(SayHello)", function()
94-
print("Hello from visual mode")
113+
vim.keymap.set('v', '<Plug>(SayHello)', function()
114+
print('Hello from visual mode')
95115
end, { noremap = true })
96116
<
97117
In the user's config:
98118
>lua
99-
vim.keymap.set({"n", "v"}, "<leader>h", "<Plug>(SayHello)")
119+
vim.keymap.set({'n', 'v'}, '<leader>h', '<Plug>(SayHello)')
100120
<
101121
==============================================================================
102-
Initialization *lua-plugin-initialization*
122+
Initialization *lua-plugin-init*
103123

104124
Newcomers to Lua plugin development will often put all initialization logic in
105125
a single `setup` function, which takes a table of options.
@@ -109,8 +129,8 @@ your plugin, even if they are happy with the default configuration.
109129
Strictly separated configuration and smart initialization allow your plugin to
110130
work out of the box.
111131

112-
NOTE: A well designed plugin has minimal impact on startup time.
113-
See also |lua-plugin-lazy-loading|.
132+
NOTE: A well designed plugin has minimal impact on startup time. See also
133+
|lua-plugin-lazy|.
114134

115135
Common approaches to a strictly separated configuration are:
116136

@@ -124,37 +144,45 @@ Typically, automatic initialization logic is done in a |plugin| or |ftplugin|
124144
script. See also |'runtimepath'|.
125145

126146
==============================================================================
127-
Lazy loading *lua-plugin-lazy-loading*
147+
Lazy loading *lua-plugin-lazy*
148+
149+
Some users like to micro-manage "lazy loading" of plugins by explicitly
150+
configuring which commands and key mappings load the plugin.
151+
152+
Your plugin should not depend on every user micro-managing their configuration
153+
in such a way. Nvim has a mechanism for every plugin to do its own implicit
154+
lazy-loading (in Vimscript it's called |autoload|), via `autoload/`
155+
(Vimscript) and `lua/` (Lua). Plugin authors can provide "lazy loading" by
156+
providing a `plugin/<name>.lua` file which defines their commands and
157+
keymappings. This file should be small, and should not eagerly `require()` the
158+
rest of your plugin. Commands and mappings should do the `require()`.
159+
160+
Guidance:
128161

129-
When it comes to initializing your plugin, assume your users may not be using
130-
a plugin manager that takes care of lazy loading for you.
131-
Making sure your plugin does not unnecessarily impact startup time is your
132-
responsibility. A plugin's functionality may evolve over time, potentially
133-
leading to breakage if users have to hack into the loading mechanisms.
134-
Furthermore, a plugin that implements its own lazy initialization properly will
135-
likely have less overhead than the mechanisms used by a plugin manager or user
136-
to load that plugin lazily.
162+
- Plugins should arrange their "lazy" behavior once, instead of expecting every user to micromanage it.
163+
- Keep `plugin/<name>.lua` small, avoid eagerly calling `require()` on modules
164+
until a command or mapping is actually used.
137165

138166
------------------------------------------------------------------------------
139-
Defer `require` calls *lua-plugin-lazy-loading-defer-require*
167+
Defer require() calls *lua-plugin-defer-require*
140168

141-
|plugin| scripts should not eagerly `require` Lua modules.
169+
`plugin/<name>.lua` scripts (|plugin|) are eagerly run at startup; this is
170+
intentional, so that plugins can setup the (minimal) commands and keymappings
171+
that users will use to invoke the plugin. This also means these "plugin/"
172+
files should NOT eagerly `require` Lua modules.
142173

143174
For example, instead of:
144175
>lua
145-
local foo = require("foo")
146-
vim.api.nvim_create_user_command("MyCommand", function()
176+
local foo = require('foo')
177+
vim.api.nvim_create_user_command('MyCommand', function()
147178
foo.do_something()
148-
end, {
149-
-- ...
150-
})
179+
end, { -- ... })
151180
<
152-
which will eagerly load the `foo` module and any other modules it imports
153-
eagerly, you can lazy load it by moving the `require` into the command's
154-
implementation.
181+
which calls `require('foo')` as soon as the module is loaded, you can
182+
lazy-load it by moving the `require` into the command's implementation:
155183
>lua
156-
vim.api.nvim_create_user_command("MyCommand", function()
157-
local foo = require("foo")
184+
vim.api.nvim_create_user_command('MyCommand', function()
185+
local foo = require('foo')
158186
foo.do_something()
159187
end, {
160188
-- ...
@@ -165,23 +193,23 @@ defer `require` calls too.
165193

166194
NOTE: For a Vimscript alternative to `require`, see |autoload|.
167195

168-
NOTE: In case you are worried about eagerly creating user commands, autocommands
169-
or keymaps at startup:
170-
Plugin managers that provide abstractions for lazy-loading plugins on
171-
such events will need to create these themselves.
196+
NOTE: If you are worried about eagerly creating user commands, autocommands or
197+
keymaps at startup: Plugin managers that provide abstractions for lazy-loading
198+
plugins on such events do the same amount of work. There is no performance
199+
benefit for users to define lazy-loading entrypoints in their configuration
200+
instead of plugins defining it in `plugin/<name>.lua`.
172201

173202
NOTE: You can use |--startuptime| to |profile| the impact a plugin has on
174-
startup time.
203+
startup time.
175204

176205
------------------------------------------------------------------------------
177-
Filetype-specific functionality *lua-plugin-lazy-loading-filetype*
206+
Filetype-specific functionality *lua-plugin-filetype*
178207

179-
Consider making use of |filetype| for any functionality that is specific to a
180-
filetype, by putting the initialization logic in a `ftplugin/{filetype}.lua`
208+
Consider making use of 'filetype' for any functionality that is specific to
209+
a filetype, by putting the initialization logic in a `ftplugin/{filetype}.lua`
181210
script.
182211

183-
------------------------------------------------------------------------------
184-
Example *lua-plugin-lazy-loading-filetype-example*
212+
FILETYPE EXAMPLE
185213

186214
A plugin tailored to Rust development might have initialization in
187215
`ftplugin/rust.lua`:
@@ -197,12 +225,12 @@ A plugin tailored to Rust development might have initialization in
197225
local bufnr = vim.api.nvim_get_current_buf()
198226
-- do something specific to this buffer,
199227
-- e.g. add a |<Plug>| mapping or create a command
200-
vim.keymap.set("n", "<Plug>(MyPluginBufferAction)", function()
201-
print("Hello")
228+
vim.keymap.set('n', '<Plug>(MyPluginBufferAction)', function()
229+
print('Hello')
202230
end, { noremap = true, buffer = bufnr, })
203231
<
204232
==============================================================================
205-
Configuration *lua-plugin-configuration*
233+
Configuration *lua-plugin-config*
206234

207235
Once you have merged the default configuration with the user's config, you
208236
should validate configs.
@@ -215,12 +243,18 @@ Validations could include:
215243
check, to reduce overhead.
216244

217245
==============================================================================
218-
Troubleshooting *lua-plugin-troubleshooting*
246+
Troubleshooting *lua-plugin-troubleshooting*
219247

220-
------------------------------------------------------------------------------
221-
Health *lua-plugin-troubleshooting-health*
248+
While developing a plugin, you can use the |:restart| command to see the
249+
result of code changes in your plugin.
250+
251+
HEALTH
252+
253+
Nvim's "health" framework gives plugins a simple way to report status checks
254+
to users. See |health-dev| for an example.
222255

223-
Provide health checks in `lua/{plugin}/health.lua`.
256+
Basically, this just means your plugin will have a `lua/{plugin}/health.lua`
257+
file. |:checkhealth| will automatically find this file when it runs.
224258

225259
Some things to validate:
226260

@@ -229,33 +263,30 @@ Some things to validate:
229263
- Presence of Lua dependencies (e.g. other plugins)
230264
- Presence of external dependencies
231265

232-
See also |vim.health| and |health-dev|.
233-
234-
------------------------------------------------------------------------------
235-
Minimal config template *lua-plugin-troubleshooting-minimal-config*
266+
MINIMAL CONFIG TEMPLATE
236267

237268
It can be useful to provide a template for a minimal configuration, along with
238269
a guide on how to use it to reproduce issues.
239270

240271
==============================================================================
241-
Versioning and releases *lua-plugin-versioning-releases*
272+
Versioning and releases *lua-plugin-versioning*
242273

243-
Consider
274+
Consider:
244275

276+
- Use |vim.deprecate()| or a `---@deprecate` annotation when you need to
277+
communicate a (future) breaking change or discourged practice.
245278
- Using SemVer https://semver.org/ tags and releases to properly communicate
246279
bug fixes, new features, and breaking changes.
247280
- Automating versioning and releases in CI.
248281
- Publishing to luarocks https://luarocks.org, especially if your plugin
249282
has dependencies or components that need to be built; or if it could be a
250283
dependency for another plugin.
251284

252-
------------------------------------------------------------------------------
253-
Further reading *lua-plugin-versioning-releases-further-reading*
285+
FURTHER READING
254286

255-
- Luarocks <3 Nvim https://github.com/nvim-neorocks/sample-luarocks-plugin
287+
- Luarocks ❤️ Nvim https://github.com/nvim-neorocks/sample-luarocks-plugin
256288

257-
------------------------------------------------------------------------------
258-
Tools *lua-plugin-versioning-releases-tools*
289+
VERSIONING TOOLS
259290

260291
- luarocks-tag-release
261292
https://github.com/marketplace/actions/luarocks-tag-release
@@ -265,14 +296,14 @@ Tools *lua-plugin-versioning-releases-tools*
265296
https://github.com/semantic-release/semantic-release
266297

267298
==============================================================================
268-
Documentation *lua-plugin-documentation*
299+
Documentation *lua-plugin-doc*
269300

270301
Provide vimdoc (see |help-writing|), so that users can read your plugin's
271302
documentation in Nvim, by entering `:h {plugin}` in |command-mode|.
272303

273-
------------------------------------------------------------------------------
274-
Tools *lua-plugin-documentation-tools*
304+
DOCUMENTATION TOOLS
275305

276306
- panvimdoc https://github.com/kdheepak/panvimdoc
277307

308+
278309
vim:tw=78:ts=8:sw=4:sts=4:et:ft=help:norl:

0 commit comments

Comments
 (0)