File: lua-plugin.txt

package info (click to toggle)
neovim 0.11.5-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 64,140 kB
  • sloc: ansic: 263,427; python: 1,472; lisp: 1,237; sh: 1,138; makefile: 382; xml: 84; ruby: 6
file content (316 lines) | stat: -rw-r--r-- 12,340 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
*lua-plugin.txt*                     Nvim

                            NVIM REFERENCE MANUAL

                   Guide to developing Lua plugins for Nvim


                                       Type |gO| to see the table of contents.

==============================================================================
Introduction                                                       *lua-plugin*

This document provides guidance for developing Nvim Lua plugins.

See |lua-guide| for guidance on using Lua to configure and operate Nvim.
See |luaref| and |lua-concepts| for details on the Lua programming language.

==============================================================================
Creating your first plugin                                    *lua-plugin-new*

Any Vimscript or Lua code file that lives in the right directory,
automatically is a "plugin". There's no manifest or "registration" step.

You can try it right now:

1. Visit your config directory: >
    :exe 'edit' stdpath('config')
<
2. Create a `plugin/foo.lua` file in there.
3. Add something to it, like: >lua
    vim.print('Hello World')
<
4. Start `nvim` and notice that it prints "Hello World" in the messages area.
   Check `:messages` if you don't see it.

Besides `plugin/foo.lua`, which is always run at startup, you can define Lua
modules in the `lua/` directory. Those modules aren't loaded until your
`plugin/foo.lua`, or the user, calls `require(…)`.

==============================================================================
Type safety                                            *lua-plugin-type-safety*

Lua, as a dynamically typed language, is great for configuration. It provides
virtually immediate feedback.
But for larger projects, this can be a double-edged sword, leaving your plugin
susceptible to unexpected bugs at the wrong time.

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

TOOLS

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

==============================================================================
Keymaps                                                   *lua-plugin-keymaps*

Avoid creating excessive keymaps automatically. Doing so can conflict with
user |mapping|s.

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

A common approach to allow keymap configuration is to define a declarative DSL
https://en.wikipedia.org/wiki/Domain-specific_language via a `setup` function.

However, doing so means that

- You will have to implement and document it yourself.
- Users will likely face inconsistencies if another plugin has a slightly
  different DSL.
- |init.lua| scripts that call such a `setup` function may throw an error if
  the plugin is not installed or disabled.

As an alternative, you can provide |<Plug>| mappings to allow users to define
their own keymaps with |vim.keymap.set()|.

- This requires one line of code in user configs.
- Even if your plugin is not installed or disabled, creating the keymap won't
  throw an error.

Another option is to simply expose a Lua function or |user-commands|.

Some benefits of |<Plug>| mappings are that you can

- Enforce options like `expr = true`.
- Use |vim.keymap|'s built-in mode handling to expose functionality only for
  specific |map-modes|.
- Handle different |map-modes| differently with a single mapping, without
  adding mode checks to the underlying implementation.
- Detect user-defined mappings through |hasmapto()| before creating defaults.

Some benefits of exposing a Lua function are:

- Extensibility, if the function takes an options table as an argument.
- A cleaner UX, if there are many options and enumerating all combinations
  of options would result in a lot of |<Plug>| mappings.

NOTE: If your function takes an options table, users may still benefit
      from |<Plug>| mappings for the most common combinations.

KEYMAP EXAMPLE

In your plugin:
>lua
    vim.keymap.set('n', '<Plug>(SayHello)', function()
        print('Hello from normal mode')
    end)

    vim.keymap.set('v', '<Plug>(SayHello)', function()
        print('Hello from visual mode')
    end)
<
In the user's config:
>lua
    vim.keymap.set({'n', 'v'}, '<leader>h', '<Plug>(SayHello)')
<
==============================================================================
Initialization                                               *lua-plugin-init*

Strictly separated configuration and smart initialization allow your plugin to
work out of the box. Common approaches are:

- A Lua function, e.g. `setup(opts)` or `configure(opts)`, which only overrides the
  default configuration and does not contain any initialization logic.
- A Vimscript compatible table (e.g. in the |vim.g| or |vim.b| namespace) that your
  plugin reads from and validates at initialization time.
  See also |lua-vim-variables|.

Typically, automatic initialization logic is done in a |plugin| or |ftplugin|
script. See also |'runtimepath'|.

On the other hand, a single `setup(opts)` that combines configuration and
initialization may be useful in specific cases:

- Customizing complex initialization, where there is a significant risk of
  misconfiguration.
- Requiring users to opt in for plugin functionality that should not be
  initialized automatically.

Keep in mind that this approach requires users to call `setup` in order to
use your plugin, even if the default configuration is enough for them.
Consider carefully whether your plugin benefits from combined `setup()` pattern
before adopting it.

NOTE: A well designed plugin has minimal impact on startup time. See also
|lua-plugin-lazy|.

==============================================================================
Lazy loading                                                 *lua-plugin-lazy*

Some users like to micro-manage "lazy loading" of plugins by explicitly
configuring which commands and key mappings load the plugin.

Your plugin should not depend on every user micro-managing their configuration
in such a way. Nvim has a mechanism for every plugin to do its own implicit
lazy-loading (in Vimscript it's called |autoload|), via `autoload/`
(Vimscript) and `lua/` (Lua). Plugin authors can provide "lazy loading" by
providing a `plugin/<name>.lua` file which defines their commands and
keymappings. This file should be small, and should not eagerly `require()` the
rest of your plugin. Commands and mappings should do the `require()`.

Guidance:

- Plugins should arrange their "lazy" behavior once, instead of expecting every user to micromanage it.
- Keep `plugin/<name>.lua` small, avoid eagerly calling `require()` on modules
  until a command or mapping is actually used.

------------------------------------------------------------------------------
Defer require() calls                               *lua-plugin-defer-require*

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

For example, instead of:
>lua
    local foo = require('foo')
    vim.api.nvim_create_user_command('MyCommand', function()
        foo.do_something()
    end, {
      -- ...
    })
<
which calls `require('foo')` as soon as the module is loaded, you can
lazy-load it by moving the `require` into the command's implementation:
>lua
    vim.api.nvim_create_user_command('MyCommand', function()
        local foo = require('foo')
        foo.do_something()
    end, {
      -- ...
    })
<
Likewise, if a plugin uses a Lua module as an entrypoint, it should
defer `require` calls too.

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

NOTE: If you are worried about eagerly creating user commands, autocommands or
keymaps at startup: Plugin managers that provide abstractions for lazy-loading
plugins on such events do the same amount of work. There is no performance
benefit for users to define lazy-loading entrypoints in their configuration
instead of plugins defining it in `plugin/<name>.lua`.

NOTE: You can use |--startuptime| to |profile| the impact a plugin has on
startup time.

------------------------------------------------------------------------------
Filetype-specific functionality                          *lua-plugin-filetype*

Consider making use of 'filetype' for any functionality that is specific to
a filetype, by putting the initialization logic in a `ftplugin/{filetype}.lua`
script.

FILETYPE EXAMPLE

A plugin tailored to Rust development might have initialization in
`ftplugin/rust.lua`:
>lua
    if not vim.g.loaded_my_rust_plugin then
        -- Initialize
    end
    -- NOTE: Using `vim.g.loaded_` prevents the plugin from initializing twice
    -- and allows users to prevent plugins from loading
    -- (in both Lua and Vimscript).
    vim.g.loaded_my_rust_plugin = true

    local bufnr = vim.api.nvim_get_current_buf()
    -- do something specific to this buffer,
    -- e.g. add a |<Plug>| mapping or create a command
    vim.keymap.set('n', '<Plug>(MyPluginBufferAction)', function()
        print('Hello')
    end, { buffer = bufnr, })
<
==============================================================================
Configuration                                              *lua-plugin-config*

Once you have merged the default configuration with the user's config, you
should validate configs.

Validations could include:

- Correct types, see |vim.validate()|
- Unknown fields in the user config (e.g. due to typos).
  This can be tricky to implement, and may be better suited for a |health|
  check, to reduce overhead.

==============================================================================
Troubleshooting                                   *lua-plugin-troubleshooting*

HEALTH

Nvim's "health" framework gives plugins a simple way to report status checks
to users. See |health-dev| for an example.

Basically, this just means your plugin will have a `lua/{plugin}/health.lua`
file. |:checkhealth| will automatically find this file when it runs.

Some things to validate:

- User configuration
- Proper initialization
- Presence of Lua dependencies (e.g. other plugins)
- Presence of external dependencies

MINIMAL CONFIG TEMPLATE

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

==============================================================================
Versioning and releases                                *lua-plugin-versioning*

Consider:

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

FURTHER READING

- Luarocks ❤️ Nvim https://github.com/nvim-neorocks/sample-luarocks-plugin

VERSIONING TOOLS

- luarocks-tag-release
  https://github.com/marketplace/actions/luarocks-tag-release
- release-please-action
  https://github.com/marketplace/actions/release-please-action
- semantic-release
  https://github.com/semantic-release/semantic-release

==============================================================================
Documentation                                                 *lua-plugin-doc*

Provide vimdoc (see |help-writing|), so that users can read your plugin's
documentation in Nvim, by entering `:h {plugin}` in |command-mode|. The
help-tags (the right-aligned "search keywords" in the help documents) are
regenerated using the |:helptags| command.

DOCUMENTATION TOOLS

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


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