Vimscript to lua: everything you need to know
A complete guide to translating/writing your configuration in Lua for neovim.
Have you been wanting to write your own neovim configuration in Lua, or translate your old vimrc? If the answer is yes, you're in the right place. In this article, I'm going to tell you everything you need to know to accomplish this goal.
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.
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:
.config/nvim/
| init.lua
| lua/
| | options.lua
| | plugins/
| | | init.lua
| | | <plugin-name>.lua
In this case, your main init.lua would look like this:
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.
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 insidevim.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:
-- 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. Refer to the official GitHub page for installation and configuration. Here is a simple example showing how to install a plugin.
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:
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 (to replace vim-airline)
nvim-tree (to replace nerdtree)
gitsigns (to replace vim-gitgutter)
telescope (to replace fzf, or any kind of fuzzy finding)
To replace coc and 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, which sets up automatically language servers and autocompletion, without you having to write everything on your own.
Alternatively, you can manually set up mason, nvim-cmp, null-ls 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, which allows you to use the terminal inside neovim in different layouts and styles.