This is a copy of my progress migrating my init.vim to init.lua.
The main utility here is nvim_utils.lua
, and everything else is just an example of how
I use it.
This utility can be installed with any plugin manager, presumably, such as:
Plug 'norcalli/nvim_utils'
" Then after plug#end()
lua require 'nvim_utils'
local todo_mappings = require 'todo'
function text_object_replace(is_visual_mode)
local register = nvim.v.register
local function replace()
return nvim.fn.getreg(register, 1, 1)
end
if is_visual_mode then
local visual_mode = nvim_visual_mode()
nvim_buf_transform_region_lines(nil, '<', '>', visual_mode, replace)
else
nvim_text_operator_transform_selection(replace)
end
end
local text_object_mappings = {
["n xr"] = { [[<Cmd>lua text_object_replace(false)<CR>]], noremap = true; };
["x xr"] = { [[:lua text_object_replace(true)<CR>]], noremap = true; };
["oil"] = { [[<Cmd>normal! $v^<CR>]], noremap = true; };
["xil"] = { [[<Cmd>normal! $v^<CR>]], noremap = true; };
}
local other_mappings = {
["nY"] = { [["+y]], noremap = true; };
["xY"] = { [["+y]], noremap = true; };
-- Highlight current cword
["n[,"] = { function()
-- \C forces matching exact case
-- \M forces nomagic interpretation
-- \< and \> denote whole word match
nvim.fn.setreg("/", ([[\C\M\<%s\>]]):format(nvim.fn.expand("<cword>")), "c")
nvim.o.hlsearch = true
end };
["i<c-a>"] = { function()
local pos = nvim.win_get_cursor(0)
local line = nvim.buf_get_lines(0, pos[1] - 1, pos[1], false)[1]
local _, start = line:find("^%s+")
nvim.win_set_cursor(0, {pos[1], start})
end };
}
local mappings = {
text_object_mappings,
other_mappings,
}
nvim_apply_mappings(vim.tbl_extend("error", unpack(mappings)), default_options)
FILETYPE_HOOKS = {
todo = function()
nvim.command('setl foldlevel=2')
nvim_apply_mappings(todo_mappings, { buffer = true })
end;
}
local autocmds = {
todo = {
{"BufEnter", "*.todo", "setl ft=todo"};
{"FileType", "todo", "lua FILETYPE_HOOKS.todo()"};
};
}
nvim_create_augroups(autocmds)
There are two types of things provided:
nvim
is an object which contains shortcut/magic methods that are very useful for mappingsnvim_*
functions which constitute building blocks for APIs like text operators or text manipulation or mappings
VISUAL_MODE.{line,char,block}
All of these methods cache the inital lookup in the metatable, but there is a small overhead regardless.
nvim.$method(...)
redirects tovim.api.nvim_$method(...)
- e.g.
nvim.command(...) == vim.api.nvim_command(...)
. - This is just for laziness.
- e.g.
nvim.fn.$method(...)
redirects tovim.api.nvim_call_function($method, {...})
- e.g.
nvim.fn.expand("%:h")
ornvim.fn.has("terminal")
- e.g.
nvim.ex.$command(...)
is approximately:$command flatten({...}).join(" ")
- e.g.
nvim.ex.edit("term://$SHELL")
ornvim.ex.startinsert()
- Since
!
isn't a valid identifier character, you can use_
at the end to indicate a!
- e.g.
nvim.ex.nnoremap_("x", "<Cmd>echo hi<CR>")
- e.g.
- e.g.
nvim.g
can be used to get/setg:
global variables.- e.g.
nvim.g.variable == g:variable
nvim.g.variable = 123
ornvim.g.variable = nil
to delete the variable:h nvim_get_var
:h nvim_set_var
:h nvim_del_var
for more
- e.g.
nvim.v
can be used to get/setv:
variables.- e.g.
nvim.v.count1 == v:count1
- Useful
v:
variables,v:register
,v:count1
, etc.. nvim.v.variable = 123
to set the value (when not read-only).:h nvim_get_vvar
:h nvim_set_vvar
for more
- e.g.
nvim.b
can be used to get/setb:
buffer variables for the current buffer.- e.g.
nvim.b.variable == b:variable
nvim.b.variable = 123
ornvim.b.variable = nil
to delete the variable:h nvim_buf_get_var
:h nvim_buf_set_var
:h nvim_buf_del_var
for more
- e.g.
nvim.env
can be used to get/set environment variables.- e.g.
nvim.env.PWD == $PWD
nvim.env.TEST = 123
to set the value. Equivalent tolet $TEST = 123
.:h setreg
:h setreg
for more. These aren't API functions.
- e.g.
nvim.o
can be used to get/set global options, as in:h options
which are set throughset
.- e.g.
nvim.o.shiftwidth == &shiftwidth
nvim.o.shiftwidth = 8
is equivalent toset shiftwidth=8
orlet &shiftwidth = 8
:h nvim_get_option
:h nvim_set_option
for more.
- e.g.
nvim.bo
can be used to get/set buffer options, as in:h options
which are set throughsetlocal
.- Only for the current buffer.
- e.g.
nvim.bo.shiftwidth == &shiftwidth
nvim.bo.shiftwidth = 8
is equivalent tosetlocal shiftwidth=8
:h nvim_buf_get_option
:h nvim_buf_set_option
for more.
nvim_mark_or_index(buf, input)
: An enhanced version of nvim_buf_get_mark which also accepts:- A number as input: which is taken as a line number.
- A pair, which is validated and passed through otherwise.
nvim_buf_get_region_lines(buf, mark_a, mark_b, mode)
: Return the lines of the selection, respecting selection modes.buf
defaults to current buffer.mark_a
defaults to'<'
.mark_b
defaults to'>'
.mode
defaults toVISUAL_MODE.char
block
isn't implemented because I haven't gotten around to it yet.- Accepts all forms of input that
nvim_mark_or_index
accepts formark_a
/mark_b
. - Returns a
List
.
nvim_buf_set_region_lines(buf, mark_a, mark_b, mode, lines)
: Set the lines between the marks.buf
defaults to current buffer.mark_a
defaults to'<'
.mark_b
defaults to'>'
.lines
is aList
. Can be greater or less than the number of lines in the region. It will add or delete lines.- Only
line
is currently implemented. This is because to supportchar
, you must have knowledge of the existing lines, and I wasn't going to do potentially expensive operations with a hidden cost. - If you want to use
char
mode, you want to usenvim_buf_transform_region_lines
instead. - Accepts all forms of input that
nvim_mark_or_index
accepts formark_a
/mark_b
.
nvim_buf_transform_region_lines(buf, mark_a, mark_b, mode, fn)
: Transform the lines by callingfn(lines, visualmode) -> lines
.buf
defaults to current buffer.mark_a
defaults to'<'
.mark_b
defaults to'>'
.block
isn't implemented because I haven't gotten around to it yet.fn(lines, visualmode)
should return a list of lines to set in the region.- A result of
nil
will not modify the region. - A result of
{}
will be changed to{""}
which empties the region. - Accepts all forms of input that
nvim_mark_or_index
accepts formark_a
/mark_b
.
nvim_set_selection_lines(lines)
: Literally just a shortcut tonvim_buf_set_region_lines(0, '<', '>', VISUAL_MODE.line, lines)
nvim_selection(mode)
return table.concat(nvim_buf_get_region_lines(nil, '<', '>', mode or VISUAL_MODE.char), "\n")
nvim_text_operator(fn)
: Pass in a callback which will be called likeopfunc
is forg@
text operators.fn(visualmode)
is the format. This doesn't receive any lines, so you can do anything here.- If you didn't know about text operators, I suggest
:h g@
. It sets the region described by motion followingg@
to'[,']
- For example
nvim_text_operator(function(visualmode)
nvim_print(visualmode, nvim_mark_or_index('['), nvim_mark_or_index(']'))
end)
nvim_text_operator_transform_selection(fn, force_visual_mode)
: Just likenvim_text_operator
, but different.fn(lines, visualmode) -> lines
is the expected format for lines.force_visual_mode
can be used to override the visualmode fromnvim_text_operator
- Here's the definition
function nvim_text_operator_transform_selection(fn, forced_visual_mode)
return nvim_text_operator(function(visualmode)
nvim_buf_transform_region_lines(nil, "[", "]", forced_visual_mode or visualmode, function(lines)
return fn(lines, visualmode)
end)
end)
end
nvim_visual_mode()
: callsvisualmode()
but returns one ofVISUAL_MODE
entries instead ofv, V, etc..
nvim_transform_cword(fn)
: self explanatorynvim_transform_cWORD(fn)
: self explanatorynvim_apply_mappings(mappings, default_options)
mappings
should be a dictionary.- The keys of mapping should start with the type of mapping, e.g.
n
fornormal
,x
forxmap
,v
forvmap
,!
formap!
,o
foromap
etc. The rest of the key is the mapping for that mode.- e.g.
n xr
isnmap <space>xr
.o<CR>
isomap <CR>
etc.
- e.g.
- The values should start with the value of the mapping, which is a string or a Lua function.
- The rest of it are options like
silent
,expr
,nowait
,unique
, orbuffer
- I implemented
buffer
support myself. I also implemented the Lua callback support.- You can peek at how this is done by
nvim_print(LUA_MAPPING, LUA_BUFFER_MAPPING)
- You can peek at how this is done by
- Other keys supported:
dot_repeat: bool
. If you want to add support fortpope/vim-repeat
, this will callrepeat#set
for lua function keybindings.
- For a lot of examples, look at
example_dotfiles/init.lua:82
. - Example:
local mappings = {
["n af"] = { "<Cmd>RustFmt<CR>", noremap = true; };
["x af"] = { ":RustFmtRange<CR>", noremap = true; };
["n AZ"] = { function() nvim_print("hi") end };
}
nvim_apply_mappings(mappings, { buffer = true; silent = true; })
nvim_create_augroups(definitions)
definitions
is a map of lists
local autocmds = {
todo = {
{"BufEnter", "*.todo", "setl ft=todo"};
{"BufEnter", "*meus/todo/todo.txt", "setl ft=todo"};
{"BufReadCmd", "*meus/todo/todo.txt", [[silent call rclone#load("db:todo/todo.txt")]]};
{"BufWriteCmd", "*meus/todo/todo.txt", [[silent call rclone#save("db:todo/todo.txt")]]};
{"FileReadCmd", "*meus/todo/todo.txt", [[silent call rclone#load("db:todo/todo.txt")]]};
{"FileWriteCmd", "*meus/todo/todo.txt", [[silent call rclone#save("db:todo/todo.txt")]]};
};
vimrc = {
{"BufWritePost init.vim nested source $MYVIMRC"};
{"FileType man setlocal nonumber norelativenumber"};
{"BufEnter term://* setlocal nonumber norelativenumber"};
};
}
nvim_create_augroups(autocmds)
nvim_print(...)
is approximatelyecho vim.inspect({...})
- it's also defined at
nvim.print
- This is useful for debugging. It can accept multiple arguments.
- it's also defined at
nvim_echo(...)
is approximatelyecho table.concat({...}, '\n')
- it's also defined at
nvim.echo
- It can accept multiple arguments and concatenates them with a space.
- it's also defined at
string.startswith
string.endswith