Saying Goodbye to Vimscript
After using Neovim exclusively for the past several years, I’ve decided it’s finally time to migrate all my
configuration files from Vimscript to Lua. I no longer wish to remember the syntax peculiarities of an archaic language
not used anywhere else. Similarly, I can’t think of a reason to ever return to vanilla Vim. In this post, I summarize
what needs to be done to switch to lua for others thinking of taking the dive.
File Layout
To get started with Lua, replace your init.vim
file with init.lua
. Note that if you have both files present at once,
Neovim will complain and your configuration will not load. The scripts contained in the ftplugin
directory can also be
converted to Lua in the same way. Finally, it is also possible to split the configuration into multiple files by
creating a lua
subdirectory and using require
to import scripts contained therein.
require('common')
require('lsp')
require('plugins')
require('util')
require('autocmd')
With these changes, your directory structure may look similar to this:
├── ftplugin
│ ├── c.lua
│ ├── cpp.lua
│ ├── go.lua
│ ├── html.lua
│ ├── markdown.lua
│ └── yaml.lua
├── init.lua
├── lua
│ ├── autocmd.lua
│ ├── common.lua
│ ├── ginit.lua
│ ├── lsp.lua
│ ├── plugins.lua
│ └── util.lua
└── pack
└── plugins
If you use Neovim Qt, you may be wondering about the presence of ginit.lua
from the above file listing. Unfortunately, Neovim Qt does not support a Lua-only configuration path and insists on
using ginit.vim
for GUI-specific configuration. Furthermore, attempting to include Neovim Qt specific commands in
init.lua
will cause errors on load (tests for GUI presence via has
do not appear to work). Fortunately, we can to
use auto command to conditionally require
the ginit.lua
file only when running under a GUI.
vim.api.nvim_create_autocmd(
'UIEnter', {
callback = function()
if vim.v.event.chan == 1 then
require('ginit')
end
end,
once = true,
}
)
Color Scheme and Leader
The color scheme can be set by executing the same colorscheme
command as in Vim. There are two different ways to do
this, with the latter looking a bit cleaner than the former. This pattern of treating the command name as a callable
method in cmd
extends to other commands (including ones exposed by plugins) as well.
vim.cmd('colorscheme solarized8')
-- or with some syntax sugar...
vim.cmd.colorscheme('solarized8')
The leader key is configured with a global variable, just like in Vim.
vim.g.mapleader = ' '
Core Options
Consider comparing your configuration against the defaults listed in the
documentation. Neovim ships with mostly sane defaults and you can reduce the size of your configuration file by skipping
redundant settings. Note that boolean settings in Vim that start with no when false
(such as nonumber
) are
represented by assignments instead.
vim.opt.autowrite = true
vim.opt.completeopt = 'menuone,noselect'
vim.opt.expandtab = true
vim.opt.fileformats = 'unix,dos,mac'
vim.opt.foldenable = false
vim.opt.guicursor = 'n:blinkon0'
vim.opt.ignorecase = true
vim.opt.linebreak = true
vim.opt.modeline = false
vim.opt.modelines = 0
vim.opt.number = true
vim.opt.shiftround = true
vim.opt.shiftwidth = 4
vim.opt.showmode = false
vim.opt.smartcase = true
vim.opt.swapfile = false
vim.opt.tabstop = 4
vim.opt.termguicolors = true
vim.opt.updatetime = 300
vim.opt.wrap = false
vim.opt.writebackup = false
Plugin Configuration
Plugins written for Vim are generally configured with global variables; these are exposed in the vim.g
table.
Vimscript supports namespaces via the #
character, which can cause syntax issues for Lua. To workaround this problem,
use the square bracket index operator on the table instead.
-- vim-airline
vim.g['airline#extensions#tabline#enabled'] = 1
vim.g['airline_symbols_ascii'] = 1
-- vim-dirvish
vim.g.dirvish_mode = ':sort ,^.*[\\/],'
-- vim-go
vim.g.go_diagnostics_enabled = 0
vim.g.go_imports_autosave = 0
vim.g.go_metalinter_enabled = {}
vim.g.go_null_module_warning = 0
vim.g.go_version_warning = 0
Key Mappings
Key mappings are also reasonably straightforward. The main difference from vanilla Vim is that commands must start with
with a <cmd>
tag instead of the :
prefix. When defining key mappings for a combination of modes (such as normal and
visual), the mode parameter has to be specified as a Lua table; a comma-delimited string will not work.
-- common keymaps
vim.keymap.set('i', '<c-c>', '<esc>')
vim.keymap.set('n', '<bs>', '<cmd>bd<cr>')
vim.keymap.set('n', '<c-c><c-c>', '<cmd>nohlsearch<cr>')
vim.keymap.set('n', '<leader><leader>', '<cmd>b#<cr>')
vim.keymap.set('n', '<leader>w', '<cmd>w<cr>')
vim.keymap.set('n', '<leader>x', '<cmd>x<cr>')
vim.keymap.set('n', 'j', 'gj')
vim.keymap.set('n', 'k', 'gk')
-- clipboard keymaps
vim.keymap.set({'n', 'v'}, '<leader>P', '"+P')
vim.keymap.set({'n', 'v'}, '<leader>Y', '"+y$')
vim.keymap.set({'n', 'v'}, '<leader>d', '"+d')
vim.keymap.set({'n', 'v'}, '<leader>d', '"+dd')
vim.keymap.set({'n', 'v'}, '<leader>p', '"+p')
vim.keymap.set({'n', 'v'}, '<leader>y', '"+y')
vim.keymap.set({'n', 'v'}, '<leader>yy', '"+yy')
-- split keymaps
vim.keymap.set('n', '<a-=>', '<c-w><c-=>')
vim.keymap.set('n', '<a-h>', '<c-w><')
vim.keymap.set('n', '<a-j>', '<c-w>+')
vim.keymap.set('n', '<a-k>', '<c-w>-')
vim.keymap.set('n', '<a-l>', '<c-w>>')
vim.keymap.set('n', '<c-h>', '<c-w>h')
vim.keymap.set('n', '<c-j>', '<c-w>j')
vim.keymap.set('n', '<c-k>', '<c-w>k')
vim.keymap.set('n', '<c-l>', '<c-w>l')
-- fzf.vim keymaps
vim.keymap.set('n', '<leader>fg', vim.cmd.GFiles)
vim.keymap.set('n', '<leader>fh', vim.cmd.History)
vim.keymap.set('n', '<leader>fb', vim.cmd.Buffers)
vim.keymap.set('n', '<leader>fl', vim.cmd.Lines)
Auto Commands
Auto commands are defined as a list of conditions followed by a table specifying what you want the action to do. Unlike
key mappings, it is possible to declare the condition list as either a table or a comma-delimited string.
vim.api.nvim_create_autocmd(
'BufRead,BufNewFile', {
pattern = '*.gohtml',
command = 'set filetype=html'
}
)
-- or using the table syntax...
vim.api.nvim_create_autocmd(
{'BufRead', 'BufNewFile'}, {
pattern = '*.gohtml',
command = 'set filetype=html'
}
)
Useful Resources