├── LICENSE
├── README.md
├── example_dotfiles
├── base16-snazzy.lua
├── init.lua
└── todo.lua
└── lua
└── nvim_utils.lua
/LICENSE:
--------------------------------------------------------------------------------
1 | nvim_utils, a utility for neovim.
2 | Copyright © 2019 Ashkan Kiani
3 |
4 | This program is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | This program is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with this program. If not, see .
16 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # nvim_utils.lua
2 |
3 | This is a copy of my progress migrating my init.vim to init.lua.
4 |
5 | The main utility here is `nvim_utils.lua`, and everything else is just an example of how
6 | I use it.
7 |
8 | This utility can be installed with any plugin manager, presumably, such as:
9 |
10 | ```vim
11 | Plug 'norcalli/nvim_utils'
12 |
13 | " Then after plug#end()
14 |
15 | lua require 'nvim_utils'
16 | ```
17 |
18 | # Example
19 |
20 | ```lua
21 | local todo_mappings = require 'todo'
22 |
23 | function text_object_replace(is_visual_mode)
24 | local register = nvim.v.register
25 | local function replace()
26 | return nvim.fn.getreg(register, 1, 1)
27 | end
28 | if is_visual_mode then
29 | local visual_mode = nvim_visual_mode()
30 | nvim_buf_transform_region_lines(nil, '<', '>', visual_mode, replace)
31 | else
32 | nvim_text_operator_transform_selection(replace)
33 | end
34 | end
35 |
36 | local text_object_mappings = {
37 | ["n xr"] = { [[lua text_object_replace(false)]], noremap = true; };
38 | ["x xr"] = { [[:lua text_object_replace(true)]], noremap = true; };
39 | ["oil"] = { [[normal! $v^]], noremap = true; };
40 | ["xil"] = { [[normal! $v^]], noremap = true; };
41 | }
42 |
43 | local other_mappings = {
44 | ["nY"] = { [["+y]], noremap = true; };
45 | ["xY"] = { [["+y]], noremap = true; };
46 | -- Highlight current cword
47 | ["n[,"] = { function()
48 | -- \C forces matching exact case
49 | -- \M forces nomagic interpretation
50 | -- \< and \> denote whole word match
51 | nvim.fn.setreg("/", ([[\C\M\<%s\>]]):format(nvim.fn.expand("")), "c")
52 | nvim.o.hlsearch = true
53 | end };
54 | ["i"] = { function()
55 | local pos = nvim.win_get_cursor(0)
56 | local line = nvim.buf_get_lines(0, pos[1] - 1, pos[1], false)[1]
57 | local _, start = line:find("^%s+")
58 | nvim.win_set_cursor(0, {pos[1], start})
59 | end };
60 | }
61 |
62 | local mappings = {
63 | text_object_mappings,
64 | other_mappings,
65 | }
66 |
67 | nvim_apply_mappings(vim.tbl_extend("error", unpack(mappings)), default_options)
68 |
69 | FILETYPE_HOOKS = {
70 | todo = function()
71 | nvim.command('setl foldlevel=2')
72 | nvim_apply_mappings(todo_mappings, { buffer = true })
73 | end;
74 | }
75 |
76 |
77 | local autocmds = {
78 | todo = {
79 | {"BufEnter", "*.todo", "setl ft=todo"};
80 | {"FileType", "todo", "lua FILETYPE_HOOKS.todo()"};
81 | };
82 | }
83 |
84 | nvim_create_augroups(autocmds)
85 | ```
86 |
87 |
88 | # Things `nvim_utils` provides
89 |
90 | There are two types of things provided:
91 |
92 | - `nvim` is an object which contains shortcut/magic methods that are very useful for mappings
93 | - `nvim_*` functions which constitute building blocks for APIs like text operators or text manipulation or mappings
94 |
95 | ## Constants
96 |
97 | - `VISUAL_MODE.{line,char,block}`
98 |
99 | ## API Function and Command Shortcuts
100 |
101 | All of these methods cache the inital lookup in the metatable, but there is a small overhead regardless.
102 |
103 | - `nvim.$method(...)` redirects to `vim.api.nvim_$method(...)`
104 | - e.g. `nvim.command(...) == vim.api.nvim_command(...)`.
105 | - This is just for laziness.
106 | - `nvim.fn.$method(...)` redirects to `vim.api.nvim_call_function($method, {...})`
107 | - e.g. `nvim.fn.expand("%:h")` or `nvim.fn.has("terminal")`
108 | - `nvim.ex.$command(...)` is approximately `:$command flatten({...}).join(" ")`
109 | - e.g. `nvim.ex.edit("term://$SHELL")` or `nvim.ex.startinsert()`
110 | - Since `!` isn't a valid identifier character, you can use `_` at the end to indicate a `!`
111 | - e.g. `nvim.ex.nnoremap_("x", "echo hi")`
112 |
113 | ## Variable shortcuts
114 |
115 | - `nvim.g` can be used to get/set `g:` global variables.
116 | - e.g. `nvim.g.variable == g:variable`
117 | - `nvim.g.variable = 123` or `nvim.g.variable = nil` to delete the variable
118 | - `:h nvim_get_var` `:h nvim_set_var` `:h nvim_del_var` for more
119 | - `nvim.v` can be used to get/set `v:` variables.
120 | - e.g. `nvim.v.count1 == v:count1`
121 | - Useful `v:` variables, `v:register`, `v:count1`, etc..
122 | - `nvim.v.variable = 123` to set the value (when not read-only).
123 | - `:h nvim_get_vvar` `:h nvim_set_vvar` for more
124 | - `nvim.b` can be used to get/set `b:` buffer variables for the current buffer.
125 | - e.g. `nvim.b.variable == b:variable`
126 | - `nvim.b.variable = 123` or `nvim.b.variable = nil` to delete the variable
127 | - `:h nvim_buf_get_var` `:h nvim_buf_set_var` `:h nvim_buf_del_var` for more
128 | - `nvim.env` can be used to get/set environment variables.
129 | - e.g. `nvim.env.PWD == $PWD`
130 | - `nvim.env.TEST = 123` to set the value. Equivalent to `let $TEST = 123`.
131 | - `:h setreg` `:h setreg` for more. These aren't API functions.
132 | - `nvim.o` can be used to get/set global options, as in `:h options` which are set through `set`.
133 | - e.g. `nvim.o.shiftwidth == &shiftwidth`
134 | - `nvim.o.shiftwidth = 8` is equivalent to `set shiftwidth=8` or `let &shiftwidth = 8`
135 | - `:h nvim_get_option` `:h nvim_set_option` for more.
136 | - `nvim.bo` can be used to get/set **buffer** options, as in `:h options` which are set through `setlocal`.
137 | - Only for the current buffer.
138 | - e.g. `nvim.bo.shiftwidth == &shiftwidth`
139 | - `nvim.bo.shiftwidth = 8` is equivalent to `setlocal shiftwidth=8`
140 | - `:h nvim_buf_get_option` `:h nvim_buf_set_option` for more.
141 |
142 | ## Extra API functions
143 |
144 | - `nvim_mark_or_index(buf, input)`: An enhanced version of nvim_buf_get_mark which also accepts:
145 | - A number as input: which is taken as a line number.
146 | - A pair, which is validated and passed through otherwise.
147 | - `nvim_buf_get_region_lines(buf, mark_a, mark_b, mode)`: Return the lines of the selection, respecting selection modes.
148 | - `buf` defaults to current buffer. `mark_a` defaults to `'<'`. `mark_b` defaults to `'>'`. `mode` defaults to `VISUAL_MODE.char`
149 | - `block` isn't implemented because I haven't gotten around to it yet.
150 | - Accepts all forms of input that `nvim_mark_or_index` accepts for `mark_a`/`mark_b`.
151 | - Returns a `List`.
152 | - `nvim_buf_set_region_lines(buf, mark_a, mark_b, mode, lines)`: Set the lines between the marks.
153 | - `buf` defaults to current buffer. `mark_a` defaults to `'<'`. `mark_b` defaults to `'>'`.
154 | - `lines` is a `List`. Can be greater or less than the number of lines in the region. It will add or delete lines.
155 | - Only `line` is currently implemented. This is because to support `char`, you must have knowledge of the existing lines, and
156 | I wasn't going to do potentially expensive operations with a hidden cost.
157 | - If you want to use `char` mode, you want to use `nvim_buf_transform_region_lines` instead.
158 | - Accepts all forms of input that `nvim_mark_or_index` accepts for `mark_a`/`mark_b`.
159 | - `nvim_buf_transform_region_lines(buf, mark_a, mark_b, mode, fn)`: Transform the lines by calling `fn(lines, visualmode) -> lines`.
160 | - `buf` defaults to current buffer. `mark_a` defaults to `'<'`. `mark_b` defaults to `'>'`.
161 | - `block` isn't implemented because I haven't gotten around to it yet.
162 | - `fn(lines, visualmode)` should return a list of lines to set in the region.
163 | - A result of `nil` will not modify the region.
164 | - A result of `{}` will be changed to `{""}` which empties the region.
165 | - Accepts all forms of input that `nvim_mark_or_index` accepts for `mark_a`/`mark_b`.
166 | - `nvim_set_selection_lines(lines)`: Literally just a shortcut to `nvim_buf_set_region_lines(0, '<', '>', VISUAL_MODE.line, lines)`
167 | - `nvim_selection(mode)`
168 | - `return table.concat(nvim_buf_get_region_lines(nil, '<', '>', mode or VISUAL_MODE.char), "\n")`
169 | - `nvim_text_operator(fn)`: Pass in a callback which will be called like `opfunc` is for `g@` text operators.
170 | - `fn(visualmode)` is the format. This doesn't receive any lines, so you can do anything here.
171 | - If you didn't know about text operators, I suggest `:h g@`. It sets the region described by motion following `g@` to `'[,']`
172 | - For example
173 | ```lua
174 | nvim_text_operator(function(visualmode)
175 | nvim_print(visualmode, nvim_mark_or_index('['), nvim_mark_or_index(']'))
176 | end)
177 | ```
178 | - `nvim_text_operator_transform_selection(fn, force_visual_mode)`: Just like `nvim_text_operator`, but different.
179 | - `fn(lines, visualmode) -> lines` is the expected format for lines.
180 | - `force_visual_mode` can be used to override the visualmode from `nvim_text_operator`
181 | - Here's the definition
182 | ```lua
183 | function nvim_text_operator_transform_selection(fn, forced_visual_mode)
184 | return nvim_text_operator(function(visualmode)
185 | nvim_buf_transform_region_lines(nil, "[", "]", forced_visual_mode or visualmode, function(lines)
186 | return fn(lines, visualmode)
187 | end)
188 | end)
189 | end
190 | ```
191 | - `nvim_visual_mode()`: calls `visualmode()` but returns one of `VISUAL_MODE` entries instead of `v, V, etc..`
192 | - `nvim_transform_cword(fn)`: self explanatory
193 | - `nvim_transform_cWORD(fn)`: self explanatory
194 | - `nvim_apply_mappings(mappings, default_options)`
195 | - `mappings` should be a dictionary.
196 | - The keys of mapping should start with the type of mapping, e.g. `n` for `normal`, `x` for `xmap`, `v` for `vmap`, `!` for `map!`, `o` for `omap` etc. The rest of the key is the mapping for that mode.
197 | - e.g. `n xr` is `nmap xr`. `o` is `omap ` etc.
198 | - The values should start with the value of the mapping, which is a string or a Lua function.
199 | - The rest of it are options like `silent`, `expr`, `nowait`, `unique`, or `buffer`
200 | - I implemented `buffer` support myself. I also implemented the Lua callback support.
201 | - You can peek at how this is done by `nvim_print(LUA_MAPPING, LUA_BUFFER_MAPPING)`
202 | - Other keys supported:
203 | - `dot_repeat: bool`. If you want to add support for `tpope/vim-repeat`, this will call `repeat#set` for lua function keybindings.
204 | - For a lot of examples, look at `example_dotfiles/init.lua:82`.
205 | - Example:
206 | ```lua
207 | local mappings = {
208 | ["n af"] = { "RustFmt", noremap = true; };
209 | ["x af"] = { ":RustFmtRange", noremap = true; };
210 | ["n AZ"] = { function() nvim_print("hi") end };
211 | }
212 | nvim_apply_mappings(mappings, { buffer = true; silent = true; })
213 | ```
214 | - `nvim_create_augroups(definitions)`
215 | - `definitions` is a map of lists
216 | ```lua
217 | local autocmds = {
218 | todo = {
219 | {"BufEnter", "*.todo", "setl ft=todo"};
220 | {"BufEnter", "*meus/todo/todo.txt", "setl ft=todo"};
221 | {"BufReadCmd", "*meus/todo/todo.txt", [[silent call rclone#load("db:todo/todo.txt")]]};
222 | {"BufWriteCmd", "*meus/todo/todo.txt", [[silent call rclone#save("db:todo/todo.txt")]]};
223 | {"FileReadCmd", "*meus/todo/todo.txt", [[silent call rclone#load("db:todo/todo.txt")]]};
224 | {"FileWriteCmd", "*meus/todo/todo.txt", [[silent call rclone#save("db:todo/todo.txt")]]};
225 | };
226 | vimrc = {
227 | {"BufWritePost init.vim nested source $MYVIMRC"};
228 | {"FileType man setlocal nonumber norelativenumber"};
229 | {"BufEnter term://* setlocal nonumber norelativenumber"};
230 | };
231 | }
232 | nvim_create_augroups(autocmds)
233 | ```
234 |
235 | ## Additional functionality
236 |
237 | ### Utilities
238 |
239 | - `nvim_print(...)` is approximately `echo vim.inspect({...})`
240 | - it's also defined at `nvim.print`
241 | - This is useful for debugging. It can accept multiple arguments.
242 | - `nvim_echo(...)` is approximately `echo table.concat({...}, '\n')`
243 | - it's also defined at `nvim.echo`
244 | - It can accept multiple arguments and concatenates them with a space.
245 |
246 | ### Things Lua is missing
247 |
248 | - `string.startswith`
249 | - `string.endswith`
250 |
--------------------------------------------------------------------------------
/example_dotfiles/base16-snazzy.lua:
--------------------------------------------------------------------------------
1 | require 'nvim_utils'
2 |
3 | local gui00 = "282a36"
4 | local gui01 = "34353e"
5 | local gui02 = "43454f"
6 | local gui03 = "78787e"
7 | local gui04 = "a5a5a9"
8 | local gui05 = "e2e4e5"
9 | local gui06 = "eff0eb"
10 | local gui07 = "f1f1f0"
11 | local gui08 = "ff5c57"
12 | local gui09 = "ff9f43"
13 | local gui0A = "f3f99d"
14 | local gui0B = "5af78e"
15 | local gui0C = "9aedfe"
16 | local gui0D = "57c7ff"
17 | local gui0E = "ff6ac1"
18 | local gui0F = "b2643c"
19 |
20 | -- " Terminal color definitions
21 | local cterm00 = "00"
22 | local cterm03 = "08"
23 | local cterm05 = "07"
24 | local cterm07 = "15"
25 | local cterm08 = "01"
26 | local cterm0A = "03"
27 | local cterm0B = "02"
28 | local cterm0C = "06"
29 | local cterm0D = "04"
30 | local cterm0E = "05"
31 |
32 | local cterm01, cterm02, cterm04, cterm06, cterm09, cterm0F
33 |
34 | if use_256_colorspace then
35 | cterm01 = "18"
36 | cterm02 = "19"
37 | cterm04 = "20"
38 | cterm06 = "21"
39 | cterm09 = "16"
40 | cterm0F = "17"
41 | else
42 | cterm01 = "10"
43 | cterm02 = "11"
44 | cterm04 = "12"
45 | cterm06 = "13"
46 | cterm09 = "09"
47 | cterm0F = "14"
48 | end
49 |
50 | -- " Neovim terminal colours
51 | if nvim.fn.has("nvim") then
52 | local terminal_color_0 = "#282a36"
53 | local terminal_color_1 = "#ff5c57"
54 | local terminal_color_2 = "#5af78e"
55 | local terminal_color_3 = "#f3f99d"
56 | local terminal_color_4 = "#57c7ff"
57 | local terminal_color_5 = "#ff6ac1"
58 | local terminal_color_6 = "#9aedfe"
59 | local terminal_color_7 = "#e2e4e5"
60 | local terminal_color_8 = "#78787e"
61 | local terminal_color_9 = "#ff5c57"
62 | local terminal_color_10 = "#5af78e"
63 | local terminal_color_11 = "#f3f99d"
64 | local terminal_color_12 = "#57c7ff"
65 | local terminal_color_13 = "#ff6ac1"
66 | local terminal_color_14 = "#9aedfe"
67 | local terminal_color_15 = "#f1f1f0"
68 | local terminal_color_background = terminal_color_0
69 | local terminal_color_foreground = terminal_color_5
70 | if nvim.o.background == "light" then
71 | local terminal_color_background = terminal_color_7
72 | local terminal_color_foreground = terminal_color_2
73 | end
74 | elseif nvim.fn.has("terminal") then
75 | local terminal_ansi_colors = { "#282a36",
76 | "#ff5c57",
77 | "#5af78e",
78 | "#f3f99d",
79 | "#57c7ff",
80 | "#ff6ac1",
81 | "#9aedfe",
82 | "#e2e4e5",
83 | "#78787e",
84 | "#ff5c57",
85 | "#5af78e",
86 | "#f3f99d",
87 | "#57c7ff",
88 | "#ff6ac1",
89 | "#9aedfe",
90 | "#f1f1f0",
91 | }
92 | end
93 |
94 | -- nvim.command "hi clear"
95 | -- nvim.command "syntax reset"
96 |
97 | local function highlight(group, guifg, guibg, ctermfg, ctermbg, attr, guisp)
98 | local parts = {group}
99 | if guifg then table.insert(parts, "guifg=#"..guifg) end
100 | if guibg then table.insert(parts, "guibg=#"..guibg) end
101 | if ctermfg then table.insert(parts, "ctermfg="..ctermfg) end
102 | if ctermbg then table.insert(parts, "ctermbg="..ctermbg) end
103 | if attr then
104 | table.insert(parts, "gui="..attr)
105 | table.insert(parts, "cterm="..attr)
106 | end
107 | if guisp then table.insert(parts, "guisp=#"..guisp) end
108 | -- nvim_print(parts)
109 | -- nvim.ex.highlight(parts)
110 | vim.api.nvim_command('highlight '..table.concat(parts, ' '))
111 | end
112 |
113 | -- Vim editor colors
114 | highlight("Normal", gui05, gui00, cterm05, cterm00, nil, nil)
115 | highlight("Bold", nil, nil, nil, nil, "bold", nil)
116 | highlight("Debug", gui08, nil, cterm08, nil, nil, nil)
117 | highlight("Directory", gui0D, nil, cterm0D, nil, nil, nil)
118 | highlight("Error", gui00, gui08, cterm00, cterm08, nil, nil)
119 | highlight("ErrorMsg", gui08, gui00, cterm08, cterm00, nil, nil)
120 | highlight("Exception", gui08, nil, cterm08, nil, nil, nil)
121 | highlight("FoldColumn", gui0C, gui01, cterm0C, cterm01, nil, nil)
122 | highlight("Folded", gui03, gui01, cterm03, cterm01, nil, nil)
123 | highlight("IncSearch", gui01, gui09, cterm01, cterm09, "none", nil)
124 | highlight("Italic", nil, nil, nil, nil, "none", nil)
125 | highlight("Macro", gui08, nil, cterm08, nil, nil, nil)
126 | highlight("MatchParen", nil, gui03, nil, cterm03, nil, nil)
127 | highlight("ModeMsg", gui0B, nil, cterm0B, nil, nil, nil)
128 | highlight("MoreMsg", gui0B, nil, cterm0B, nil, nil, nil)
129 | highlight("Question", gui0D, nil, cterm0D, nil, nil, nil)
130 | highlight("Search", gui01, gui0A, cterm01, cterm0A, nil, nil)
131 | highlight("Substitute", gui01, gui0A, cterm01, cterm0A, "none", nil)
132 | highlight("SpecialKey", gui03, nil, cterm03, nil, nil, nil)
133 | highlight("TooLong", gui08, nil, cterm08, nil, nil, nil)
134 | highlight("Underlined", gui08, nil, cterm08, nil, nil, nil)
135 | highlight("Visual", nil, gui02, nil, cterm02, nil, nil)
136 | highlight("VisualNOS", gui08, nil, cterm08, nil, nil, nil)
137 | highlight("WarningMsg", gui08, nil, cterm08, nil, nil, nil)
138 | highlight("WildMenu", gui08, gui0A, cterm08, nil, nil, nil)
139 | highlight("Title", gui0D, nil, cterm0D, nil, "none", nil)
140 | highlight("Conceal", gui0D, gui00, cterm0D, cterm00, nil, nil)
141 | highlight("Cursor", gui00, gui05, cterm00, cterm05, nil, nil)
142 | highlight("NonText", gui03, nil, cterm03, nil, nil, nil)
143 | highlight("LineNr", gui03, gui01, cterm03, cterm01, nil, nil)
144 | highlight("SignColumn", gui03, gui01, cterm03, cterm01, nil, nil)
145 | highlight("StatusLine", gui04, gui02, cterm04, cterm02, "none", nil)
146 | highlight("StatusLineNC", gui03, gui01, cterm03, cterm01, "none", nil)
147 | highlight("VertSplit", gui02, gui02, cterm02, cterm02, "none", nil)
148 | highlight("ColorColumn", nil, gui01, nil, cterm01, "none", nil)
149 | highlight("CursorColumn", nil, gui01, nil, cterm01, "none", nil)
150 | highlight("CursorLine", nil, gui01, nil, cterm01, "none", nil)
151 | highlight("CursorLineNr", gui04, gui01, cterm04, cterm01, nil, nil)
152 | highlight("QuickFixLine", nil, gui01, nil, cterm01, "none", nil)
153 | highlight("PMenu", gui05, gui01, cterm05, cterm01, "none", nil)
154 | highlight("PMenuSel", gui01, gui05, cterm01, cterm05, nil, nil)
155 | highlight("TabLine", gui03, gui01, cterm03, cterm01, "none", nil)
156 | highlight("TabLineFill", gui03, gui01, cterm03, cterm01, "none", nil)
157 | highlight("TabLineSel", gui0B, gui01, cterm0B, cterm01, "none", nil)
158 |
159 | -- Standard syntax highlighting
160 | highlight("Boolean", gui09, nil, cterm09, nil, nil, nil)
161 | highlight("Character", gui08, nil, cterm08, nil, nil, nil)
162 | highlight("Comment", gui03, nil, cterm03, nil, nil, nil)
163 | highlight("Conditional", gui0E, nil, cterm0E, nil, nil, nil)
164 | highlight("Constant", gui09, nil, cterm09, nil, nil, nil)
165 | highlight("Define", gui0E, nil, cterm0E, nil, "none", nil)
166 | highlight("Delimiter", gui0F, nil, cterm0F, nil, nil, nil)
167 | highlight("Float", gui09, nil, cterm09, nil, nil, nil)
168 | highlight("Function", gui0D, nil, cterm0D, nil, nil, nil)
169 | highlight("Identifier", gui08, nil, cterm08, nil, "none", nil)
170 | highlight("Include", gui0D, nil, cterm0D, nil, nil, nil)
171 | highlight("Keyword", gui0E, nil, cterm0E, nil, nil, nil)
172 | highlight("Label", gui0A, nil, cterm0A, nil, nil, nil)
173 | highlight("Number", gui09, nil, cterm09, nil, nil, nil)
174 | highlight("Operator", gui05, nil, cterm05, nil, "none", nil)
175 | highlight("PreProc", gui0A, nil, cterm0A, nil, nil, nil)
176 | highlight("Repeat", gui0A, nil, cterm0A, nil, nil, nil)
177 | highlight("Special", gui0C, nil, cterm0C, nil, nil, nil)
178 | highlight("SpecialChar", gui0F, nil, cterm0F, nil, nil, nil)
179 | highlight("Statement", gui08, nil, cterm08, nil, nil, nil)
180 | highlight("StorageClass", gui0A, nil, cterm0A, nil, nil, nil)
181 | highlight("String", gui0B, nil, cterm0B, nil, nil, nil)
182 | highlight("Structure", gui0E, nil, cterm0E, nil, nil, nil)
183 | highlight("Tag", gui0A, nil, cterm0A, nil, nil, nil)
184 | highlight("Todo", gui0A, gui01, cterm0A, cterm01, nil, nil)
185 | highlight("Type", gui0A, nil, cterm0A, nil, "none", nil)
186 | highlight("Typedef", gui0A, nil, cterm0A, nil, nil, nil)
187 |
188 | -- nvim.command 'syntax on'
189 |
190 |
--------------------------------------------------------------------------------
/example_dotfiles/init.lua:
--------------------------------------------------------------------------------
1 | require 'nvim_utils'
2 | -- require 'base16-snazzy'
3 | local todo_mappings = require 'todo'
4 |
5 | function text_object_replace(is_visual_mode)
6 | local register = nvim.v.register
7 | local function replace()
8 | return nvim.fn.getreg(register, 1, 1)
9 | end
10 | if is_visual_mode then
11 | local visual_mode = nvim_visual_mode()
12 | nvim_buf_transform_region_lines(nil, '<', '>', visual_mode, replace)
13 | else
14 | -- It's unfortunate that lines must be fetched here considering it's not used,
15 | -- but for a "char" visual mode, it's required regardless. For lines, it wouldn't be,
16 | -- but that optimization is not made yet.
17 | -- TODO investigate if nvim_text_operator_transform_selection can be optimized
18 | nvim_text_operator_transform_selection(replace)
19 | end
20 | end
21 |
22 | local function duplicate(lines, mode)
23 | if mode == VISUAL_MODE.line then
24 | return vim.tbl_flatten {lines, lines}
25 | elseif mode == VISUAL_MODE.char then
26 | if #lines == 0 then
27 | return lines
28 | end
29 | if #lines == 1 then
30 | return {lines[1]..lines[1]}
31 | end
32 | local first_line = table.remove(lines, 1)
33 | local last_line = table.remove(lines)
34 | return vim.tbl_flatten {first_line, lines, last_line..first_line, lines, last_line}
35 | end
36 | end
37 |
38 | function text_object_duplicate(is_visual_mode)
39 | if is_visual_mode then
40 | local visual_mode = nvim_visual_mode()
41 | nvim_buf_transform_region_lines(nil, '<', '>', visual_mode, duplicate)
42 | else
43 | nvim_text_operator_transform_selection(duplicate)
44 | end
45 | end
46 |
47 | function text_object_comment_and_duplicate(is_visual_mode)
48 | local visual_mode = VISUAL_MODE.line
49 | local commentstring = nvim.bo.commentstring
50 | local function comment_dupe(lines)
51 | local commented = {}
52 | for _, line in ipairs(lines) do
53 | table.insert(commented, commentstring:format(line))
54 | end
55 | return vim.tbl_flatten { lines, commented }
56 | end
57 | if is_visual_mode then
58 | nvim_buf_transform_region_lines(nil, '<', '>', visual_mode, comment_dupe)
59 | else
60 | nvim_text_operator_transform_selection(comment_dupe, visual_mode)
61 | end
62 | end
63 |
64 | local function text_object_define(mapping, function_name)
65 | local options = { silent = true, noremap = true }
66 | nvim.set_keymap('n', mapping, ("lua %s(%s)"):format(function_name, false), options)
67 | nvim.set_keymap('x', mapping, (":lua %s(%s)"):format(function_name, true), options)
68 | -- TODO figure out why mappings for this seem to not be working.
69 | -- nvim.set_keymap('x', mapping, ("lua %s(%s)"):format(function_name, true), options)
70 | -- nvim.ex.nnoremap {"", mapping, (":lua %s(%s)"):format(function_name, false)}
71 | -- nvim.ex.xnoremap {"", mapping, (":lua %s(%s)"):format(function_name, true)}
72 | end
73 |
74 | text_object_define(" xr", "text_object_replace")
75 | text_object_define(" xd", "text_object_duplicate")
76 | text_object_define(" xy", "text_object_comment_and_duplicate")
77 | text_object_define("gy", "text_object_comment_and_duplicate")
78 |
79 | -- local default_options = { silent = true; unique = true; }
80 | local default_options = { silent = true; }
81 |
82 | local text_object_mappings = {
83 | -- ["n xd"] = { [[lua text_object_duplicate(false)]], noremap = true; };
84 | -- ["n xr"] = { [[lua text_object_replace(false)]], noremap = true; };
85 | -- ["n xy"] = { [[lua text_object_comment_and_duplicate(false)]], noremap = true; };
86 | -- ["x xd"] = { [[:lua text_object_duplicate(true)]], noremap = true; };
87 | -- ["x xr"] = { [[:lua text_object_replace(true)]], noremap = true; };
88 | -- ["x xy"] = { [[:lua text_object_comment_and_duplicate(true)]], noremap = true; };
89 | ["n xdd"] = { [[ xd_]], };
90 | ["n xrr"] = { [[ xr_]], };
91 | ["n xyy"] = { [[ xyl]], };
92 | ["oil"] = { [[normal! $v^]], noremap = true; };
93 | ["xil"] = { [[normal! $v^]], noremap = true; };
94 | ["oal"] = { [[normal! V]], noremap = true; };
95 | ["xal"] = { [[normal! V]], noremap = true; };
96 | ["oae"] = { [[normal! ggVG]], noremap = true; };
97 | ["xae"] = { [[normal! ggVG]], noremap = true; };
98 | ["o\\"] = { [[$]], noremap = true; };
99 | ["x\\"] = { [[$]], noremap = true; };
100 | }
101 |
102 | local function map_cmd(...)
103 | return { ("%s"):format(table.concat(vim.tbl_flatten {...}, " ")), noremap = true; }
104 | end
105 |
106 | local function map_set(...)
107 | return { ("silent set %s"):format(table.concat(vim.tbl_flatten {...}, " ")), noremap = true; }
108 | end
109 |
110 | local function toggle_settings(...)
111 | local parts = {}
112 | for _, setting in ipairs(vim.tbl_flatten{...}) do
113 | table.insert(parts, ("%s! %s?"):format(setting, setting))
114 | end
115 | return parts
116 | end
117 |
118 | local function map_toggle_settings(...)
119 | local parts = {}
120 | for _, setting in ipairs(vim.tbl_flatten{...}) do
121 | table.insert(parts, ("%s! %s?"):format(setting, setting))
122 | end
123 | return map_set(parts)
124 | end
125 |
126 | -- The mapping helps a lot with deduplicating, but you could still bypass it with key combos
127 | -- which are valid both in lowercase and uppercase like and
128 | local other_mappings = {
129 | -- Highlight current cword
130 | ["n[,"] = { function()
131 | -- \C forces matching exact case
132 | -- \M forces nomagic interpretation
133 | -- \< and \> denote whole word match
134 | nvim.fn.setreg("/", ([[\C\M\<%s\>]]):format(nvim.fn.expand("")), "c")
135 | nvim.o.hlsearch = true
136 | end };
137 | -- Highlight current selection
138 | ["x[,"] = { function()
139 | local selection = table.concat(nvim_buf_get_region_lines(0, '<', '>', VISUAL_MODE.char), '\n')
140 | nvim.fn.setreg("/", ([[\C\M%s]]):format(selection), "c")
141 | nvim.o.hlsearch = true
142 | end };
143 | ["n\\ "] = { function()
144 | nvim.put({" "}, "c", false, false)
145 | -- local pos = vim.api.nvim_win_get_cursor(0)
146 | -- nvim_buf_transform_region_lines(nil, pos, pos, VISUAL_MODE.char, function(lines) return {" "..lines[1]} end)
147 | end };
148 | ["n\\"] = { function()
149 | -- nvim.put({""}, "c", false, false)
150 | local pos = vim.api.nvim_win_get_cursor(0)
151 | nvim_buf_transform_region_lines(nil, pos, pos, VISUAL_MODE.char, function(lines)
152 | return {"", lines[1]}
153 | end)
154 | -- TODO to indent or not indent? That is the question
155 | -- nvim.ex.normal_("=j")
156 | end };
157 | ["n jj"] = { "\\" };
158 | ["i"] = { function()
159 | local pos = nvim.win_get_cursor(0)
160 | local line = nvim.buf_get_lines(0, pos[1] - 1, pos[1], false)[1]
161 | nvim.win_set_cursor(0, {pos[1], #line})
162 | end };
163 | ["i"] = { function()
164 | local pos = nvim.win_get_cursor(0)
165 | local line = nvim.buf_get_lines(0, pos[1] - 1, pos[1], false)[1]
166 | local _, start = line:find("^%s+")
167 | nvim.win_set_cursor(0, {pos[1], start})
168 | end };
169 | ["i"] = { function()
170 | local pos = nvim.win_get_cursor(0)
171 | nvim_buf_transform_region_lines(0, pos, pos, VISUAL_MODE.line, function(lines)
172 | return {lines[1]:sub(1, pos[2])}
173 | end)
174 | end };
175 | -- This should work, but mappings seem to have difficulty with this for some reason.
176 | -- ["v>"] = { function()
177 | -- nvim_buf_transform_region_lines(0, '<', '>', 'line', function(lines)
178 | -- local prefix
179 | -- if not nvim.bo.expandtab then
180 | -- prefix = "\t"
181 | -- else
182 | -- prefix = string.rep(" ", nvim.fn.shiftwidth())
183 | -- end
184 | -- for i, line in ipairs(lines) do
185 | -- lines[i] = prefix..line
186 | -- end
187 | -- return lines
188 | -- end)
189 | -- end };
190 | -- I like neosnippet expansion on therefore remap to
191 | ["i"] = { "", noremap = true; };
192 | ["i"] = { "(neosnippet_expand_or_jump)", noremap = false; };
193 | ["s"] = { "(neosnippet_expand_or_jump)", noremap = false; };
194 | ["x"] = { "(neosnippet_expand_target)", noremap = false; };
195 |
196 | -- Indent shit
197 | ["x>"] = { "normal! >gv", noremap = true; };
198 | ["x<"] = { "normal! ", noremap = true; };
199 |
200 | -- Misc bindings
201 | ["nQ"] = { "bd", noremap = true; };
202 | -- This goes back a space, I wonder if there's a programmatic way to exit insert mode
203 | ["i"] = { "", noremap = true; };
204 | -- Pop into editing a command quickly
205 | ["n :"] = { ":cc", noremap = true; };
206 |
207 | -- Diff bindings
208 | ["n do"] = { "diffoff!", noremap = true; };
209 | ["n dt"] = { "diffthis", noremap = true; };
210 | ["n du"] = { "diffupdate", noremap = true; };
211 | ["n dg"] = { "diffget", noremap = true; };
212 | ["n dp"] = { "diffput", noremap = true; };
213 | ["x dp"] = { "diffput", noremap = true; };
214 |
215 | -- TODO insert these into mappings only if the appropriate plugins exist.
216 | -- git/vim-fugitive aliases/mappings
217 | ["n gS"] = { [[FZFGFiles?]], noremap = true; };
218 | ["n gT"] = { [[FZFBCommits]], noremap = true; };
219 | ["n gb"] = { [[Gblame]], noremap = true; };
220 | ["n gc"] = { [[Gcommit]], noremap = true; };
221 | ["n gd"] = { [[Gdiff]], noremap = true; };
222 | ["n ge"] = { [[Gedit]], noremap = true; };
223 | ["n gl"] = { [[Gpull]], noremap = true; };
224 | ["n gp"] = { [[Gpush]], noremap = true; };
225 | ["n gq"] = { [[Gcommit -m "%"]], noremap = true; };
226 | ["n gr"] = { [[Gread]], noremap = true; };
227 | ["n gs"] = { [[Gstatus]], noremap = true; };
228 | ["n gt"] = { [[0Glog]], noremap = true; };
229 | ["n gw"] = { [[Gwrite]], noremap = true; };
230 | ["x gt"] = { [[:Glog]], noremap = true; };
231 |
232 | ["n ldo"] = { [[LinediffReset]], noremap = true; };
233 | ["n ldt"] = { [[Linediff]], noremap = true; };
234 | ["x ldo"] = { [[LinediffReset]], noremap = true; };
235 | ["x ldt"] = { [[Linediff]], noremap = true; };
236 |
237 | ["n sw"] = map_toggle_settings("wrap");
238 | ["n sn"] = map_toggle_settings("number", "relativenumber");
239 | ["n sb"] = map_toggle_settings("scb");
240 | ["n sp"] = map_toggle_settings("paste");
241 | ["n sh"] = map_toggle_settings("list");
242 | ["n sc"] = map_toggle_settings("hlsearch");
243 | ["n sP"] = map_cmd("silent setlocal", toggle_settings("spell"));
244 |
245 | -- Open terminal at $PWD
246 | ["n at"] = { function()
247 | -- TODO use terminal api directly?
248 | nvim.ex.edit("term://$SHELL")
249 | nvim.ex.startinsert()
250 | end };
251 | -- Open terminal at current buffer's directory
252 | ["n aT"] = { function()
253 | nvim.ex.edit(("term://%s//$SHELL"):format(nvim.fn.expand("%:h")))
254 | nvim.ex.startinsert()
255 | end };
256 | ["n ae"] = { "Explore", noremap = true };
257 |
258 | -- Insert blank lines after the current line.
259 | ["n] "] = { function()
260 | local repetition = nvim.v.count1
261 | local pos = nvim.win_get_cursor(0)
262 | nvim_buf_transform_region_lines(0, pos, pos, VISUAL_MODE.line, function(lines)
263 | for _ = 1, repetition do
264 | table.insert(lines, '')
265 | end
266 | return lines
267 | end)
268 | end };
269 | -- Insert blank lines before the current line.
270 | ["n[ "] = { function()
271 | local repetition = nvim.v.count1
272 | local pos = nvim.win_get_cursor(0)
273 | nvim_buf_transform_region_lines(0, pos, pos, VISUAL_MODE.line, function(lines)
274 | local result = {}
275 | for _ = 1, repetition do
276 | table.insert(result, '')
277 | end
278 | return vim.tbl_flatten {result, lines}
279 | end)
280 | nvim.win_set_cursor(0, {pos[1]+repetition, pos[2]})
281 | end };
282 |
283 | -- Transpose line downwards
284 | ["n]p"] = { function()
285 | nvim.put(nvim.fn.getreg(nvim.v.register, 1, true), "l", true, false)
286 | end };
287 | ["n[p"] = { function()
288 | nvim.put(nvim.fn.getreg(nvim.v.register, 1, true), "l", false, false)
289 | end };
290 | -- Transpose line downwards
291 | ["n]e"] = { function()
292 | local repetition = nvim.v.count1
293 | local pos = nvim.win_get_cursor(0)
294 | nvim_buf_transform_region_lines(0, pos, pos[1] + repetition, VISUAL_MODE.line, function(lines)
295 | table.insert(lines, table.remove(lines, 1))
296 | return lines
297 | end)
298 | -- TODO Follow the line or not?
299 | nvim.win_set_cursor(0, {pos[1] + repetition, pos[2]})
300 | end };
301 | -- Transpose line upwards
302 | ["n[e"] = { function()
303 | local repetition = nvim.v.count1
304 | local pos = nvim.win_get_cursor(0)
305 | nvim_buf_transform_region_lines(0, pos[1] - repetition, pos, VISUAL_MODE.line, function(lines)
306 | table.insert(lines, 1, table.remove(lines))
307 | return lines
308 | end)
309 | nvim.win_set_cursor(0, {pos[1] - repetition, pos[2]})
310 | end };
311 |
312 | ["nZZ"] = { function()
313 | if #nvim.list_bufs() > 1 then
314 | if not nvim.bo.modifiable then
315 | nvim.command("bd")
316 | else
317 | nvim.command("w | bd")
318 | end
319 | else
320 | nvim.command("xit")
321 | end
322 | -- if #vim.api.nvim_list_bufs() > 1 then
323 | -- if not vim.api.nvim_buf_get_option(vim.api.nvim_get_current_buf(), "modifiable") then
324 | -- vim.api.nvim_command("bd")
325 | -- else
326 | -- vim.api.nvim_command("w | bd")
327 | -- end
328 | -- else
329 | -- vim.api.nvim_command("xit")
330 | -- end
331 | end };
332 |
333 | ["n"] = { [[gcl]] };
334 | ["x"] = { [[normal gcl]], noremap = true; };
335 |
336 | ["n"] = map_cmd("silent bprev");
337 | ["n"] = map_cmd("silent bnext");
338 | ["n"] = map_cmd("silent bnext");
339 |
340 | ["i"] = map_cmd("silent bprev");
341 | ["i"] = map_cmd("silent bnext");
342 | ["i"] = map_cmd("silent bnext");
343 |
344 | -- TODO replicate BD in lua, which is to delete the current buffer without
345 | -- closing the window.
346 | ["n"] = map_cmd("silent BD");
347 | ["n"] = map_cmd("silent BD!");
348 | ["i"] = map_cmd("silent BD");
349 | ["i"] = map_cmd("silent BD!");
350 |
351 | -- TODO redo in lua
352 | ["n"] = { [[n:set buftype=nofile]], noremap = true; };
353 |
354 | -- TODO for some reason this doesn't work like I expect.
355 | -- ["ch"] = { [[expand("%:h")."/"]], nowait = true; noremap = true; expr = true; };
356 | -- ["cp"] = { [[expand("%:p")]], nowait = true; noremap = true; expr = true; };
357 | -- ["ct"] = { [[expand("%:t")]], nowait = true; noremap = true; expr = true; };
358 | -- ["c"] = { [[expand("%")]], nowait = true; noremap = true; expr = true; };
359 |
360 | -- ["i"] = { "l", noremap = true; };
361 | -- TODO Try to do it programmatically
362 | -- ["i"] = { function()
363 | -- local pos = nvim.win_get_cursor(0)
364 | -- nvim.feedkeys("", 'n', false)
365 | -- -- nvim.ex.stopinsert()
366 | -- nvim.win_set_cursor(0, pos)
367 | -- end };
368 |
369 | ["nY"] = { [["+y]], noremap = true; };
370 | ["xY"] = { [["+y]], noremap = true; };
371 |
372 | -- replace 'f' with 1-char Sneak
373 | ["n"] = { [[Sneak_f]] };
374 | ["o"] = { [[Sneak_f]] };
375 | ["x"] = { [[Sneak_f]] };
376 | ["n"] = { [[Sneak_F]] };
377 | ["o"] = { [[Sneak_F]] };
378 | ["x"] = { [[Sneak_F]] };
379 |
380 | -- replace 't' with 1-char Sneak
381 | ["n"] = { [[Sneak_t]] };
382 | ["o"] = { [[Sneak_t]] };
383 | ["x"] = { [[Sneak_t]] };
384 | ["n"] = { [[Sneak_T]] };
385 | ["o"] = { [[Sneak_T]] };
386 | ["x"] = { [[Sneak_T]] };
387 |
388 | ["ngx"] = map_cmd [[call jobstart(["ldo", expand("")])]];
389 | -- I wish I could use for this.
390 | ["xgx"] = { [[:lua spawn("ldo", { args = { nvim_selection() } })]], noremap = true; };
391 | }
392 |
393 | local motion_mappings = {
394 | ["n"] = { [[j]], noremap = true; };
395 | ["n"] = { [[k]], noremap = true; };
396 | ["n"] = { [[h]], noremap = true; };
397 | ["n"] = { [[l]], noremap = true; };
398 | ["n"] = { [[s]], noremap = true; };
399 | ["n"] = { [[v]], noremap = true; };
400 | ["n"] = { [[o]], noremap = true; };
401 |
402 | ["n"] = { [[=]], noremap = true; };
403 | ["n"] = { [[+]], noremap = true; };
404 | ["n"] = { [[-]], noremap = true; };
405 | ["n"] = { [[<]], noremap = true; };
406 | ["n>"] = { [[>]], noremap = true; };
407 | ["n"] = { [[q]], noremap = true; };
408 | ["n"] = { [[n]], noremap = true; };
409 |
410 | ["n"] = { [[z]], noremap = true; };
411 |
412 | ["n"] = { [[H]], noremap = true; };
413 | ["n"] = { [[J]], noremap = true; };
414 | ["n"] = { [[K]], noremap = true; };
415 | ["n"] = { [[L]], noremap = true; };
416 |
417 | ["n"] = { [[]], noremap = true; };
418 | ["n"] = { [[]], noremap = true; };
419 |
420 | -- -- TODO autocreate these from the existing n
421 | -- ["i"] = { [[z]], noremap = true; };
422 | -- ["i"] = { [[H]], noremap = true; };
423 | -- ["i"] = { [[J]], noremap = true; };
424 | -- ["i"] = { [[K]], noremap = true; };
425 | -- ["i"] = { [[L]], noremap = true; };
426 | }
427 |
428 | local insert_motion_mappings = {}
429 | for k, v in pairs(motion_mappings) do
430 | insert_motion_mappings["i"..k:sub(2)] = map_cmd("normal! "..v[1])
431 | end
432 |
433 | local terminal_motion_mappings = {}
434 | for k, v in pairs(motion_mappings) do
435 | terminal_motion_mappings["t"..k:sub(2)] = { [[]]..v[1], noremap = true; }
436 | -- terminal_motion_mappings["t"..k:sub(2)] = map_cmd("stopinsert | normal! "..v[1])
437 | end
438 |
439 | local mappings = {
440 | text_object_mappings,
441 | other_mappings,
442 | motion_mappings,
443 | insert_motion_mappings,
444 | terminal_motion_mappings,
445 | }
446 |
447 | nvim_apply_mappings(vim.tbl_extend("error", unpack(mappings)), default_options)
448 |
449 | FILETYPE_HOOKS = {
450 | todo = function()
451 | todo_mappings["n AZ"] = { function()
452 | nvim_print("hi")
453 | end }
454 | nvim.command('setl foldlevel=2')
455 | -- TODO why doesn't this work?
456 | -- nvim.bo.foldlevel = 2
457 | nvim_apply_mappings(todo_mappings, { buffer = true })
458 | end;
459 | sql = function()
460 | nvim.bo.commentstring = "-- %s"
461 | nvim.bo.formatprg = "pg_format -"
462 | end;
463 | rust = function()
464 | local mappings = {
465 | ["n af"] = { "RustFmt", noremap = true; };
466 | ["x af"] = { ":RustFmtRange", noremap = true; };
467 | }
468 | nvim_apply_mappings(mappings, { buffer = true; silent = true; })
469 | end;
470 | i3config = function()
471 | local mappings = {
472 | ["n ab"] = { ".w !xargs swaymsg --", noremap = true; };
473 | ["x ab"] = { ":w !xargs swaymsg --", noremap = true; };
474 | }
475 | nvim_apply_mappings(mappings, { buffer = true; silent = true; })
476 | end;
477 | jq = function()
478 | nvim.bo.commentstring = "# %s"
479 | end;
480 | terraform = function()
481 | nvim.bo.commentstring = "# %s"
482 | end;
483 | ["play2-conf"] = function()
484 | nvim.bo.commentstring = "# %s"
485 | end;
486 | lua = function()
487 | local mappings = {
488 | -- ["n all"] = { [[.!lualambda]], noremap = true; };
489 | -- ["x all"] = { [[:!lualambda]], noremap = true; };
490 | ["n aL"] = { [[.!lualambda]], noremap = true; };
491 | ["x aL"] = { [[:!lualambda]], noremap = true; };
492 | ["n ab"] = { [[.luado loadstring(line)()