Why Lua?

    Neovim has an embedded lua 5.1 runtime which is used to create faster and more powerful extensions of your favorite editor. In the Neovim charter, it lists one of its goals as developing a first-class lua scripting alternative to VimL. One of the reasons for doing this is that VimL is a slow interpreted language with almost no optimizations. Much of the time spent in vim startup and in actions from plugins that can block the main loop in the editor is in parsing and executing vimscript. A great explanation of this can be found in Neovim lead maintainer, Justin M. Keyes’ talk, We can have nice things.

    With the recent introduction of the built-in LSP client in the master branch written in lua, I became more interested in the possibilities lua has to offer and began trying to use lua in Neovim. I have never written lua before and have not seen very many guides on how to utilize the lua runtime in Neovim, so I want to illustrate the process of learning how to take advantage of the powerful scripting capabilities that are available in the Neovim runtime. Given that my experience is still very basic, these examples will also be quite small, but I hope that it can be a good jumping off point for those interested in using lua more in extending Neovim.

    Getting Started

    One of the first things I was confused about was how to use lua code inside of vim and vimscript. Luckily, the documentation in :h lua gives a few examples of how lua can be used in the editor. I recommend reading it for an in-depth explanation of how Neovim treats lua and the sourcing of lua files. Here’s a high-level overview of different approaches to executing lua code in your editor:

    • From the vim command line, you can run :lua <yourCodeHere>. This is useful for keybindings, commands, and other one-off execution cases.
    • Inside of a VimL file, you can demarcate lua code with the following code fencing:
    lua << EOF
    -- your lua code here
    EOF
    
    • Inside of a VimL file you can use the lua keyword to execute commands similar to the first example. (i.e. lua <yourCodeHere>).

    One important note here is that Neovim will look for lua code in the runtimepath you’ve set in your settings. Additionally, it will append your runtimepath with /lua/?.lua and /lua/?/init.lua so it is common practice to see a /lua sub-directory inside .nvim. For more detailed information about where Neovim looks for lua code, check out :h lua-require.

    Your First Function

    Porting your init.vim to lua can be a big undertaking, so it’s best to start small. For the first example, we’ll create a function which creates a scratch buffer.

    This function will live in a file we’ll call tools, so create it in the lua directory in your nvim config: ~/.config/nvim/lua/tools.lua. Once we’ve created the file, we’ll fill it out with some boilerplate:

    -- in tools.lua
    
    local M = {}
    
    function M.makeScratch()
    end
    
    return M
    

    Using the table M here allows us to keep things out of the global scope and to use only what we need when calling the function from nvim. We’ll be using the neovim API to make a scratch buffer, so let’s create a shorthand for it in our tools.lua file:

    -- in tools.lua
    local api = vim.api
    
    local M = {}
    
    function M.makeScratch()
    end
    
    return M
    

    We can create a new buffer with the enew command, and the neovim API gives us a way to call nvim commands from lua:

    -- in tools.lua
    local api = vim.api
    
    local M = {}
    
    function M.makeScratch()
      api.nvim_command('enew') -- equivalent to :enew
    end
    
    return M
    

    Next, we want to set some buffer options so that our scratch buffer isn’t listed in the buffer list and doesn’t have a swapfile created for it:

    -- in tools.lua
    local api = vim.api
    
    local M = {}
    
    function M.makeScratch()
      api.nvim_command('enew') -- equivalent to :enew
      vim.bo[0].buftype=nofile -- set the current buffer's (buffer 0) buftype to nofile
      vim.bo[0].bufhidden=hide
      vim.bo[0].swapfile=false
    end
    
    return M
    

    That is all we need to create the scratch buffer! Now let’s use it in our init.vim:

    " in init.vim
    
    command! Scratch lua require'tools'.makeScratch()
    

    Now a scratch buffer is created by running the command :Scratch.

    You can port your init.vim to lua one function at a time, and if you get stuck, you can always use vim.api.nvim_command! When looking for help, make sure to check out :h api, and :h lua.

    Using v:lua

    The variable v:lua can be used to call lua functions from within vimscript. A great use case for this is accessing the LSP client’s omnifunc. If you wanted to use the LSP completion for Rust, you may have something like this in your configuration:

    " in init.vim
    lua << EOF
      local nvim_lsp = require 'nvim_lsp'
      nvim_lsp.rust_analyzer.setup({})
    EOF
    
    " in ftplugin/rust.vim
    
    set omnifunc=v:lua.vim.lsp.omnifunc
    

    Interop With vim.fn

    It is useful to have access to vimscript functions from inside of lua, especially when interacting with autoloaded functions or functions provided by plugins. In this example, we will have an autocmd that will execute the vimscript function, tools#loadCscope when the VimEnter event happens.

    " in autoload/tools.vim
    
    function! tools#loadCscope() abort
      try
        silent cscope add cscope.out
      catch /^Vim\%((\a\+)\)\=:E/
      endtry
    endfunction
    
    -- in file that you source, such as init.lua
    
    function sourceCScope()
      vim.fn['tools#loadCscope']() -- no arguments needed
    end
    
    function nvim_create_augroups(definitions)
      for group_name, definition in pairs(definitions) do
        vim.api.nvim_command('augroup '..group_name)
        vim.api.nvim_command('autocmd!')
        for _, def in ipairs(definition) do
          local command = table.concat(vim.tbl_flatten{'autocmd', def}, ' ')
          vim.api.nvim_command(command)
        end
        vim.api.nvim_command('augroup END')
      end
    end
    
    local autocmds = {
      startup = {
        {"VimEnter",        "*",      [[lua sourceCScope()]]};
      }
    }
    
    nvim_create_augroups(autocmds)
    

    See Also

    Mentioned around the web

    commented on Apr 6, 2020 see original

    You've to have lua require'colorizer'.setup() in your init.vim.

    commented on Apr 6, 2020 see original

    WOW MAN!!! I haven't used any lua plug-in but this one seems really interesting! Also I couldn't make any other highlighter work so this will fit perfectly! Thanks to the other guy for helping us out!

    commented on Apr 6, 2020 see original
    I want to install [https://github.com/norcalli/nvim-colorizer.lua](https://github.com/norcalli/nvim-colorizer.lua) So I add `Plug 'norcalli/nvim-colorizer.lua'` in my `init.vim` and run `:PlugInstall` And I don't know what to do next. I tried read [https://neovim.io/doc/user/lua.html](https://neovim.io/doc/user/lua.html) and [https://teukka.tech/luanvim.html](https://teukka.tech/luanvim.html) But I'm so confused what I have to do. And these are details what I think it's related. `$ nvim --version` `NVIM v0.3.4` `Build type: Release` `LuaJIT 2.1.0-beta3` Here in `neovim` `:vs $VIMRUNTIME # Show /usr/share/nvim/runtime` `$ ls -la # Inside runtime folder` `man.lua` `vim` `$ cd vim/ # Inside runtime/vim folder` `$ ls -la` `compat.lua` And here my `init.vim` location `~/.config/nvim$ ls -la` `init.vim` `.netrwhist` https://github.com/norcalli/nvim-colorizer.lua https://neovim.io/doc/user/lua.html https://teukka.tech/luanvim.html

    Mentioned on https://reddit.com/r/neovim/comments/fvwin5/how_to_install_luabased_plugin/