mirror of
https://github.com/neovim/neovim.git
synced 2025-09-03 18:08:16 +00:00
Compare commits
3 Commits
1063aff643
...
c5262c4ca8
Author | SHA1 | Date | |
---|---|---|---|
![]() |
c5262c4ca8 | ||
![]() |
dd8e3d7aa5 | ||
![]() |
a7491e1457 |
@@ -10,19 +10,18 @@
|
||||
==============================================================================
|
||||
Introduction *lua-guide*
|
||||
|
||||
This guide will go through the basics of using Lua in Nvim. It is not meant
|
||||
to be a comprehensive encyclopedia of all available features, nor will it
|
||||
detail all intricacies. Think of it as a survival kit -- the bare minimum
|
||||
needed to know to comfortably get started on using Lua in Nvim.
|
||||
|
||||
An important thing to note is that this isn't a guide to the Lua language
|
||||
itself. Rather, this is a guide on how to configure and modify Nvim through
|
||||
the Lua language and the functions we provide to help with this. Take a look
|
||||
at |luaref| and |lua-concepts| if you'd like to learn more about Lua itself.
|
||||
Similarly, this guide assumes some familiarity with the basics of Nvim
|
||||
This guide introduces the basics of everyday usage of Lua to configure and
|
||||
operate Nvim. It assumes some familiarity with the (non-Lua) basics of Nvim
|
||||
(commands, options, mappings, autocommands), which are covered in the
|
||||
|user-manual|.
|
||||
|
||||
This is not a comprehensive encyclopedia of all available features. Think of
|
||||
it as a survival kit: the bare minimum needed to comfortably get started on
|
||||
using Lua in Nvim.
|
||||
|
||||
See |lua-plugin| for guidance on developing Lua plugins.
|
||||
See |luaref| and |lua-concepts| for details on the Lua programming language.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
Some words on the API *lua-guide-api*
|
||||
|
||||
|
306
runtime/doc/lua-plugin.txt
Normal file
306
runtime/doc/lua-plugin.txt
Normal file
@@ -0,0 +1,306 @@
|
||||
*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 maniest or "registration" required.
|
||||
|
||||
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`, 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, { noremap = true })
|
||||
|
||||
vim.keymap.set('v', '<Plug>(SayHello)', function()
|
||||
print('Hello from visual mode')
|
||||
end, { noremap = true })
|
||||
<
|
||||
In the user's config:
|
||||
>lua
|
||||
vim.keymap.set({'n', 'v'}, '<leader>h', '<Plug>(SayHello)')
|
||||
<
|
||||
==============================================================================
|
||||
Initialization *lua-plugin-init*
|
||||
|
||||
Newcomers to Lua plugin development will often put all initialization logic in
|
||||
a single `setup` function, which takes a table of options.
|
||||
If you do this, users will be forced to call this function in order to use
|
||||
your plugin, even if they are happy with the default configuration.
|
||||
|
||||
Strictly separated configuration and smart initialization allow your plugin to
|
||||
work out of the box.
|
||||
|
||||
NOTE: A well designed plugin has minimal impact on startup time. See also
|
||||
|lua-plugin-lazy|.
|
||||
|
||||
Common approaches to a strictly separated configuration 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'|.
|
||||
|
||||
==============================================================================
|
||||
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, { noremap = true, 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 discourged 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|.
|
||||
|
||||
DOCUMENTATION TOOLS
|
||||
|
||||
- panvimdoc https://github.com/kdheepak/panvimdoc
|
||||
|
||||
|
||||
vim:tw=78:ts=8:sw=4:sts=4:et:ft=help:norl:
|
Reference in New Issue
Block a user