Vimscript to lua: everything you need to know
Tags: vim, linux, neovim, lua
Published: 2023-01-03 18:25:16

## Introduction to Lua

If you already know the syntax of the Lua programming language, you can skip this section. Alternatively, you can watch a quick introduction to the language on YouTube, there are a lot of valid ones. Here, I'm going to provide a quick overview of most of the features used for vim configuration you need to know about.

Before I provide some usage examples, you need to know that the Lua programming language uses one single data structure, tables, which emulate arrays and maps/dictionaries. Note that the indexing starts at 1.

```lua
var1 = 1 -- global variable
local var2 = 2 -- local variable
strings = "1" .. "2" .. [[3]] -- ways to write a string and concatenation

condition = not false -- booleans and if statments
if false then
	print(var1)
elseif condition then
	print(var2)
else
	print(strings)
end

function say_hi_to(name)
	print("Hi " .. name)
end

local table = { "hello", "goodbye" } -- used as array
table["hello"] = "world" -- and dictionary

for k, v in ipairs(table) do -- for range loop
    -- ipairs only gets indexed values (with no user-defined key)
    -- pairs gets all keys and values in tables
	print(k, v)
end

require('plugin-name') -- used to import files or use plugins
```

## Getting started

To get started configuring your neovim with Lua, you need to create an `init.lua` file, in the same directory where you would put your vimrc for neovim. For instance, on Linux, it would be something like `$HOME/.config/nvim/init.lua`.

From here, you can have two different approaches:

* writing your entire config inside the init file
    
* dividing your config into different files
    

If you choose the second approach, you'll have to put all the files except your `init.lua` inside the `lua` folder, in your `nvim` config folder. Here is an example:

```plaintext
.config/nvim/
|  init.lua
|  lua/
|  |  options.lua
|  |  plugins/
|  |  |  init.lua
|  |  |  <plugin-name>.lua
```

In this case, your main init.lua would look like this:

```lua
require("options") -- no file extension
require("plugins") -- require a folder
```

If you require a folder, what Lua actually does is search for an `init.lua` inside that folder, so inside that file, you would have to manually require all the other files in the folder.

## Setting options

Now you're finally ready to start translating or writing your config in Lua. The first thing I'll go over is setting options. To do so, you'll most likely always use the `vim` module.

```lua
vim.opt.tabstop = 4
vim.opt.autoindent = true
```

`vim.opt` is the most used method and works pretty much the same way `:set` does. If you're not sure what to use, this is the recommended way to set any option, if you don't have specific needs.

You can also set general settings with `vim.o` or buffer-scoped settings with `vim.bo`.

In some cases, you need to use `vim.cmd`, for example in setting the color scheme `vim.cmd.colorscheme("gruvbox")` which is equivalent to `vim.cmd("colorscheme gruvbox")`.  
The string inside`vim.cmd()` is interpreted as Vimscript.

Lastly, global options are set with `vim.g`, which is mostly used by plugins, and to set the leader key `vim.g.leader = " "`.

## Setting key mappings

To set keymaps in Lua, you have mainly two options: `vim.api.nvim_set_keymap()` or `vim.keymap.set()`, where the latter is basically a wrapper for the former. The main difference in terms of configuration is that using `vim.keymap.set` allows you to assign a mapping to a Lua function in a more elegant way. Here are examples for both:

```lua
-- opts set in a table to not repeat them everytime
local opts = { noremap = true, silent = true }
-- (mode, lhs, rhs, options)
-- lhs = keys to be mapped, rhs = command executed
vim.api.nvim_set_keymap("v", ">", ">gv", opts) -- like vnoremap

-- (mode, lhs, rhs, options)
-- here mode can be a either string or a table
-- rhs can be either a string with commands or a lua function
function my_func()
    vim.cmd([[echo "hello world"]]) -- this is a string
end
vim.keymap.set({"v", "n"}, ">", my_func, opts)

-- map a command
vim.keymap.set("v", "<leader>a", ":wq<CR>", opts)
```

## Plugins

To finish off your configuration, you'll probably want to add some plugins to your neovim, possibly replacing Vimscript plugins with Lua ones.

### Plugin manager

Changing your plugin manager is the first step, and will make configuring all the other plugins a lot easier. You can use for example [packer.nvim](https://github.com/wbthomason/packer.nvim). Refer to the official GitHub page for installation and configuration. Here is a simple example showing how to install a plugin.

```lua
require("packer").startup(function()
    use("plugin name")
end)
```

### Plugin customization

Most neovim plugins will then have either a setup or configure function, which will allow you to customize the plugin's options. Here is an example:

```lua
require("plugin name").setup({
    option1 = "hello",
    option2 = {
        a = "aaa",
        b = "bbb",
    },
})
```

Refer to the specific plugin wiki to see how it's possible to configure it.

### Lua plugins

In this section, I will try to provide some examples of plugins to act as replacements for your old vim plugins.

You can try:

* [lualine](https://github.com/nvim-lualine/lualine.nvim) (to replace vim-airline)
    
* [nvim-tree](https://github.com/nvim-tree/nvim-tree.lua) (to replace nerdtree)
    
* [gitsigns](https://github.com/lewis6991/gitsigns.nvim) (to replace vim-gitgutter)
    
* [telescope](https://github.com/nvim-telescope/telescope.nvim) (to replace fzf, or any kind of fuzzy finding)
    

To replace [coc](https://github.com/neoclide/coc.nvim) and [ale](https://github.com/dense-analysis/ale), you'll probably need to install more than two plugins. Neovim is in fact well known for its built-in LSP, which can be configured in many ways.  
The quickest one is by using [lsp-zero](https://github.com/VonHeikemen/lsp-zero.nvim), which sets up automatically language servers and autocompletion, without you having to write everything on your own.  
Alternatively, you can manually set up [mason](https://github.com/williamboman/mason.nvim), [nvim-cmp](https://github.com/hrsh7th/nvim-cmp), [null-ls](https://github.com/jose-elias-alvarez/null-ls.nvim) and more, to achieve a similar result with your own efforts. I will write another blog post about this, so stay tuned.

One last cool plugin I'd like to suggest is [toggleterm](https://github.com/akinsho/toggleterm.nvim), which allows you to use the terminal inside neovim in different layouts and styles.