neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

lua-plugin.txt (14069B)


      1 *lua-plugin.txt*                     Nvim
      2 
      3                            NVIM REFERENCE MANUAL
      4 
      5                   Guide to developing Lua plugins for Nvim
      6 
      7 
      8                                       Type |gO| to see the table of contents.
      9 
     10 ==============================================================================
     11 Introduction                                                       *lua-plugin*
     12 
     13 This document provides guidance for developing Nvim Lua plugins.
     14 
     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 manifest or "registration" step.
     23 
     24 You can try it right now:
     25 
     26 1. Visit your config directory: >
     27    :exe 'edit' stdpath('config')
     28 2. Create a `plugin/foo.lua` file in there.
     29 3. Add something to it, like: >lua
     30    vim.print('Hello World')
     31 4. Start `nvim` and notice that it prints "Hello World" in the messages area.
     32   Check `:messages` if you don't see it.
     33 
     34 Besides `plugin/foo.lua`, which is always run at startup, you can define Lua
     35 modules in the `lua/` directory. Those modules aren't loaded until your
     36 `plugin/foo.lua`, or the user, calls `require(…)`.
     37 
     38 ==============================================================================
     39 Type safety                                            *lua-plugin-type-safety*
     40 
     41 Lua, as a dynamically typed language, is great for configuration. It provides
     42 virtually immediate feedback.
     43 But for larger projects, this can be a double-edged sword, leaving your plugin
     44 susceptible to unexpected bugs at the wrong time.
     45 
     46 You can leverage LuaCATS or "emmylua" annotations https://luals.github.io/wiki/annotations/
     47 along with lua-language-server ("LuaLS") https://luals.github.io/ to catch
     48 potential bugs in your CI before your plugin's users do. The Nvim codebase
     49 uses these annotations extensively.
     50 
     51 TOOLS
     52 
     53 - lua-typecheck-action https://github.com/marketplace/actions/lua-typecheck-action
     54 - lua-language-server https://luals.github.io
     55 
     56 ==============================================================================
     57 Keymaps                                                   *lua-plugin-keymaps*
     58 
     59 Avoid creating excessive keymaps automatically. Doing so can conflict with
     60 user |mapping|s.
     61 
     62 NOTE: An example for uncontroversial keymaps are buffer-local |mapping|s for
     63      specific file types or floating windows, or <Plug> mappings.
     64 
     65 A common approach to allow keymap configuration is to define a declarative DSL
     66 https://en.wikipedia.org/wiki/Domain-specific_language via a `setup` function.
     67 
     68 However, doing so means that
     69 
     70 - You will have to implement and document it yourself.
     71 - Users will likely face inconsistencies if another plugin has a slightly
     72  different DSL.
     73 - |init.lua| scripts that call such a `setup` function may throw an error if
     74  the plugin is not installed or disabled.
     75 
     76 As an alternative, you can provide |<Plug>| mappings to allow users to define
     77 their own keymaps with |vim.keymap.set()|.
     78 
     79 - This requires one line of code in user configs.
     80 - Even if your plugin is not installed or disabled, creating the keymap won't
     81  throw an error.
     82 
     83 Another option is to simply expose a Lua function or |user-commands|.
     84 
     85 Some benefits of |<Plug>| mappings are that you can
     86 
     87 - Enforce options like `expr = true`.
     88 - Use |vim.keymap|'s built-in mode handling to expose functionality only for
     89  specific |map-modes|.
     90 - Handle different |map-modes| differently with a single mapping, without
     91  adding mode checks to the underlying implementation.
     92 - Detect user-defined mappings through |hasmapto()| before creating defaults.
     93 
     94 Some benefits of exposing a Lua function are:
     95 
     96 - Extensibility, if the function takes an options table as an argument.
     97 - A cleaner UX, if there are many options and enumerating all combinations
     98  of options would result in a lot of |<Plug>| mappings.
     99 
    100 NOTE: If your function takes an options table, users may still benefit
    101      from |<Plug>| mappings for the most common combinations.
    102 
    103 KEYMAP EXAMPLE
    104 
    105 In your plugin:
    106 >lua
    107    vim.keymap.set('n', '<Plug>(SayHello)', function()
    108        print('Hello from normal mode')
    109    end)
    110 
    111    vim.keymap.set('v', '<Plug>(SayHello)', function()
    112        print('Hello from visual mode')
    113    end)
    114 <
    115 In the user's config:
    116 >lua
    117    vim.keymap.set({'n', 'v'}, '<leader>h', '<Plug>(SayHello)')
    118 <
    119 ==============================================================================
    120 Initialization                                               *lua-plugin-init*
    121 
    122 Strictly separated configuration and smart initialization allow your plugin to
    123 work out of the box. Common approaches are:
    124 
    125 - A Lua function, e.g. `setup(opts)` or `configure(opts)`, which only overrides the
    126  default configuration and does not contain any initialization logic.
    127 - A Vimscript compatible table (e.g. in the |vim.g| or |vim.b| namespace) that your
    128  plugin reads from and validates at initialization time.
    129  See also |lua-vim-variables|.
    130 
    131 Typically, automatic initialization logic is done in a |plugin| or |ftplugin|
    132 script. See also 'runtimepath'.
    133 
    134 On the other hand, a single `setup(opts)` that combines configuration and
    135 initialization may be useful in specific cases:
    136 
    137 - Customizing complex initialization, where there is a significant risk of
    138  misconfiguration.
    139 - Requiring users to opt in for plugin functionality that should not be
    140  initialized automatically.
    141 
    142 Keep in mind that this approach requires users to call `setup` in order to
    143 use your plugin, even if the default configuration is enough for them.
    144 Consider carefully whether your plugin benefits from combined `setup()` pattern
    145 before adopting it. This article chronicles the history and tradeoffs of
    146 `setup()`: https://mrcjkb.dev/posts/2023-08-22-setup.html
    147 
    148 NOTE: A well designed plugin has minimal impact on startup time. See also
    149 |lua-plugin-lazy|.
    150 
    151 ==============================================================================
    152 Lazy loading                                                 *lua-plugin-lazy*
    153 
    154 Some users like to micro-manage "lazy loading" of plugins by explicitly
    155 configuring which commands and key mappings load the plugin.
    156 
    157 Your plugin should not depend on every user micro-managing their configuration
    158 in such a way. Nvim has a mechanism for every plugin to do its own implicit
    159 lazy-loading (in Vimscript it's called |autoload|), via `autoload/`
    160 (Vimscript) and `lua/` (Lua). Plugin authors can provide "lazy loading" by
    161 providing a `plugin/<name>.lua` file which defines their commands and
    162 keymappings. This file should be small, and should not eagerly `require()` the
    163 rest of your plugin. Commands and mappings should do the `require()`.
    164 
    165 Guidance:
    166 
    167 - Plugins should arrange their "lazy" behavior once, instead of expecting every user to micromanage it.
    168 - Keep `plugin/<name>.lua` small, avoid eagerly calling `require()` on modules
    169  until a command or mapping is actually used.
    170 
    171 ------------------------------------------------------------------------------
    172 Defer require() calls                               *lua-plugin-defer-require*
    173 
    174 `plugin/<name>.lua` scripts (|plugin|) are eagerly run at startup; this is
    175 intentional, so that plugins can setup the (minimal) commands and keymappings
    176 that users will use to invoke the plugin. This also means these "plugin/"
    177 files should NOT eagerly `require` Lua modules.
    178 
    179 For example, instead of:
    180 >lua
    181    local foo = require('foo')
    182    vim.api.nvim_create_user_command('MyCommand', function()
    183        foo.do_something()
    184    end, {
    185      -- ...
    186    })
    187 <
    188 which calls `require('foo')` as soon as the module is loaded, you can
    189 lazy-load it by moving the `require` into the command's implementation:
    190 >lua
    191    vim.api.nvim_create_user_command('MyCommand', function()
    192        local foo = require('foo')
    193        foo.do_something()
    194    end, {
    195      -- ...
    196    })
    197 <
    198 Likewise, if a plugin uses a Lua module as an entrypoint, it should
    199 defer `require` calls too.
    200 
    201 NOTE: For a Vimscript alternative to `require`, see |autoload|.
    202 
    203 NOTE: If you are worried about eagerly creating user commands, autocommands or
    204 keymaps at startup: Plugin managers that provide abstractions for lazy-loading
    205 plugins on such events do the same amount of work. There is no performance
    206 benefit for users to define lazy-loading entrypoints in their configuration
    207 instead of plugins defining it in `plugin/<name>.lua`.
    208 
    209 NOTE: You can use |--startuptime| to |profile| the impact a plugin has on
    210 startup time.
    211 
    212 ------------------------------------------------------------------------------
    213 Filetype-specific functionality                          *lua-plugin-filetype*
    214 
    215 Consider making use of 'filetype' for any functionality that is specific to
    216 a filetype, by putting the initialization logic in a `ftplugin/{filetype}.lua`
    217 script.
    218 
    219 For buffers owned by your plugin (often used to show a custom UI or view),
    220 typically your plugin will set a custom 'filetype'. In that case, it's useful
    221 to set the 'filetype' "as late as possible", so that users can override
    222 buffer-local settings after your plugin has (re)initialized the buffer.
    223 
    224 FILETYPE EXAMPLE
    225 
    226 A plugin tailored to Rust development might have initialization in
    227 `ftplugin/rust.lua`:
    228 >lua
    229    if not vim.g.loaded_my_rust_plugin then
    230        -- Initialize
    231    end
    232    -- NOTE: Using `vim.g.loaded_` prevents the plugin from initializing twice
    233    -- and allows users to prevent plugins from loading
    234    -- (in both Lua and Vimscript).
    235    vim.g.loaded_my_rust_plugin = true
    236 
    237    local bufnr = vim.api.nvim_get_current_buf()
    238    -- do something specific to this buffer,
    239    -- e.g. add a |<Plug>| mapping or create a command
    240    vim.keymap.set('n', '<Plug>(MyPluginBufferAction)', function()
    241        print('Hello')
    242    end, { buffer = bufnr, })
    243 <
    244 ==============================================================================
    245 Configuration                                              *lua-plugin-config*
    246 
    247 To allow users to override buffer-local configuration for filetypes owned by
    248 your plugin, publish a |FileType| event, "as late as possible".
    249 |lua-plugin-filetype|
    250 
    251 Once you have merged the default configuration with the user's config, you
    252 should validate configs.
    253 
    254 Validations could include:
    255 
    256 - Correct types, see |vim.validate()|
    257 - Unknown fields in the user config (e.g. due to typos).
    258  This can be tricky to implement, and may be better suited for a |health|
    259  check, to reduce overhead.
    260 
    261 ==============================================================================
    262 UI                                                           *lua-plugin-ui*
    263 
    264 Some plugins have their own "UI" which they present in a buffer that the
    265 plugin "owns". In that buffer typically you will want to provide custom
    266 actions.
    267 
    268 Besides creating |<Plug>| mappings, you may want to consider providing actions
    269 by defining an in-process LSP server. Offering actions as code-actions
    270 |vim.lsp.buf.code_action()| means the user can see all available actions using
    271 the default |gra| mapping to view the code-actions menu. They can even define
    272 mappings to a specific action by invoking `vim.lsp.buf.code_action()` with the
    273 `filter` + `apply` parameters: >lua
    274 
    275    vim.lsp.buf.code_action({
    276        apply = true,
    277        filter = function(a)
    278            return a.title == 'Do something'
    279        end,
    280    })
    281 <
    282 
    283 Example: See `runtime/lua/vim/pack/_lsp.lua` for how vim.pack defines an
    284 in-process LSP server to provide interactive features in its
    285 `nvim-pack://confirm` buffer.
    286 
    287 ==============================================================================
    288 Troubleshooting                                   *lua-plugin-troubleshooting*
    289 
    290 While developing a plugin, you can use the |:restart| command to see the
    291 result of code changes in your plugin.
    292 
    293 HEALTH
    294 
    295 Nvim's "health" framework gives plugins a simple way to report status checks
    296 to users. See |health-dev| for an example.
    297 
    298 Basically, this just means your plugin will have a `lua/{plugin}/health.lua`
    299 file. |:checkhealth| will automatically find this file when it runs.
    300 
    301 Some things to validate:
    302 
    303 - User configuration
    304 - Proper initialization
    305 - Presence of Lua dependencies (e.g. other plugins)
    306 - Presence of external dependencies
    307 
    308 MINIMAL CONFIG TEMPLATE
    309 
    310 It can be useful to provide a template for a minimal configuration, along with
    311 a guide on how to use it to reproduce issues.
    312 
    313 ==============================================================================
    314 Versioning and releases                                *lua-plugin-versioning*
    315 
    316 Consider:
    317 
    318 - Use |vim.deprecate()| or a `---@deprecate` annotation when you need to
    319  communicate a (future) breaking change or discouraged practice.
    320 - Using SemVer https://semver.org/ tags and releases to properly communicate
    321  bug fixes, new features, and breaking changes.
    322 - Automating versioning and releases in CI.
    323 - Publishing to luarocks https://luarocks.org, especially if your plugin
    324  has dependencies or components that need to be built; or if it could be a
    325  dependency for another plugin.
    326 
    327 FURTHER READING
    328 
    329 - Luarocks ❤️ Nvim https://github.com/nvim-neorocks/sample-luarocks-plugin
    330 
    331 VERSIONING TOOLS
    332 
    333 - luarocks-tag-release
    334  https://github.com/marketplace/actions/luarocks-tag-release
    335 - release-please-action
    336  https://github.com/marketplace/actions/release-please-action
    337 - semantic-release
    338  https://github.com/semantic-release/semantic-release
    339 
    340 ==============================================================================
    341 Documentation                                                 *lua-plugin-doc*
    342 
    343 Provide vimdoc (see |help-writing|), so that users can read your plugin's
    344 documentation in Nvim, by entering `:h {plugin}` in |command-mode|. The
    345 help-tags (the right-aligned "search keywords" in the help documents) are
    346 regenerated using the |:helptags| command.
    347 
    348 DOCUMENTATION TOOLS
    349 
    350 - panvimdoc https://github.com/kdheepak/panvimdoc
    351 
    352 
    353 vim:tw=78:ts=8:sw=4:sts=4:et:ft=help:norl: