-
-
Notifications
You must be signed in to change notification settings - Fork 355
Expand file tree
/
Copy pathinit.lua
More file actions
422 lines (374 loc) · 16.8 KB
/
Copy pathinit.lua
File metadata and controls
422 lines (374 loc) · 16.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
--- ### General utils.
-- DESCRIPTION:
-- General utility functions to use within Nvim.
-- Functions:
-- -> run_cmd → Run a shell command and return true/false.
-- -> add_autocmds_to_buffer → Add autocmds to a bufnr.
-- -> apply_lsp_diagnostic_defaults → Apply Default LSP settings.
-- -> apply_user_lsp_mappings → Apply user lsp mappings to a lsp client.
-- -> del_autocmds_from_buffer → Delete autocmds from a bufnr.
-- -> get_icon → Return an icon from the icons directory.
-- -> get_mappings_template → Return a empty mappings table.
-- -> is_available → Return true if the plugin exist.
-- -> is_big_file → Return true if the file is too big.
-- -> notify → Send a notification with a default title.
-- -> os_path → Converts a path to the current OS.
-- -> get_plugin_opts → Return a plugin opts table.
-- -> set_mappings → Set a list of mappings in a clean way.
-- -> set_url_hl → Add underline highlight to urls.
-- -> open_with_program → Open the file or URL under the cursor.
-- -> trigger_event → Manually trigger an event.
-- -> which_key_register → When setting a mapping, add it to whichkey.
local M = {}
--- Run a shell command and capture the output and whether the command
--- succeeded or failed.
--- @param cmd string|string[] The terminal command to execute.
--- @param show_error? boolean If true, print errors if the command fails.
--- @return string|nil # The result of a successfully executed command, or nil if it failed.
function M.run_cmd(cmd, show_error)
-- Split cmd string into a list, if needed.
if type(cmd) == "string" then
cmd = vim.split(cmd, " ")
end
-- If windows, and prepend cmd.exe
if vim.fn.has("win32") == 1 then
cmd = vim.list_extend({ "cmd.exe", "/C" }, cmd)
end
-- Execute cmd and store result (output or error message)
local result = vim.fn.system(cmd)
local success = vim.api.nvim_get_vvar("shell_error") == 0
-- If the command failed and show_error is true or not provided, print error.
if not success and (show_error == nil or show_error) then
vim.api.nvim_echo({{
("Error running command %s\nError message:\n%s"):format(
table.concat(cmd, " "), -- Convert the cmd back to string.
result -- Show the error result
)}}, true, { err = true }
)
end
-- strip out terminal escape sequences and control characters.
local cleaned_result = result:gsub("[\27\155][][()#;?%d]*[A-PRZcf-ntqry=><~]", "")
-- Return the cleaned result if the command succeeded, or nil if it failed
return (success and cleaned_result) or nil
end
--- Adds autocmds to a specific buffer if they don't already exist.
---
--- @param augroup string The name of the autocmd group to which the autocmds belong.
--- @param bufnr number The buffer number to which the autocmds should be applied.
--- @param autocmds table|any A table or a single autocmd definition containing the autocmds to add.
function M.add_autocmds_to_buffer(augroup, bufnr, autocmds)
-- Check if autocmds is a list, if not convert it to a list
if not vim.islist(autocmds) then autocmds = { autocmds } end
-- Attempt to retrieve existing autocmds associated with the specified augroup and bufnr
local cmds_found, cmds = pcall(vim.api.nvim_get_autocmds, { group = augroup, buffer = bufnr })
-- If no existing autocmds are found or the cmds_found call fails
if not cmds_found or vim.tbl_isempty(cmds) then
-- Create a new augroup if it doesn't already exist
vim.api.nvim_create_augroup(augroup, { clear = false })
-- Iterate over each autocmd provided
for _, autocmd in ipairs(autocmds) do
-- Extract the events from the autocmd and remove the events key
local events = autocmd.events
autocmd.events = nil
-- Set the group and buffer keys for the autocmd
autocmd.group = augroup
autocmd.buffer = bufnr
-- Create the autocmd
vim.api.nvim_create_autocmd(events, autocmd)
end
end
end
--- This function define how LSP diagnostics will look.
M.apply_lsp_diagnostic_defaults = function()
-- Define LSP diagnostics icons defined in ../icons/icons.lua
local signs = {
{ name = "DiagnosticSignError", text = M.get_icon("DiagnosticError"), texthl = "DiagnosticSignError" },
{ name = "DiagnosticSignWarn", text = M.get_icon("DiagnosticWarn"), texthl = "DiagnosticSignWarn" },
{ name = "DiagnosticSignHint", text = M.get_icon("DiagnosticHint"), texthl = "DiagnosticSignHint" },
{ name = "DiagnosticSignInfo", text = M.get_icon("DiagnosticInfo"), texthl = "DiagnosticSignInfo" },
{ name = "DapStopped", text = M.get_icon("DapStopped"), texthl = "DiagnosticWarn" },
{ name = "DapBreakpoint", text = M.get_icon("DapBreakpoint"), texthl = "DiagnosticInfo" },
{ name = "DapBreakpointRejected", text = M.get_icon("DapBreakpointRejected"), texthl = "DiagnosticError" },
{ name = "DapBreakpointCondition", text = M.get_icon("DapBreakpointCondition"), texthl = "DiagnosticInfo" },
{ name = "DapLogPoint", text = M.get_icon("DapLogPoint"), texthl = "DiagnosticInfo" }
}
for _, sign in ipairs(signs) do
vim.fn.sign_define(sign.name, sign)
end
-- Define diagnostic opts
local diagnostics_opts = {
virtual_text = true,
signs = {
text = {
[vim.diagnostic.severity.ERROR] = M.get_icon("DiagnosticError"),
[vim.diagnostic.severity.HINT] = M.get_icon("DiagnosticHint"),
[vim.diagnostic.severity.WARN] = M.get_icon("DiagnosticWarn"),
[vim.diagnostic.severity.INFO] = M.get_icon("DiagnosticInfo"),
},
active = signs,
},
update_in_insert = true,
underline = true,
severity_sort = true,
float = {
focused = false,
style = "minimal",
border = "rounded",
source = "always",
header = "",
prefix = "",
},
}
-- Define the table of options used by vim.g.diagnostics_mode in ../1-options.lua
M.diagnostics_enum = {
-- diagnostics off
[0] = vim.tbl_deep_extend(
"force",
diagnostics_opts,
{ underline = false, virtual_text = false, signs = false, update_in_insert = false }
),
-- status only
vim.tbl_deep_extend("force", diagnostics_opts, { virtual_text = false, signs = false }),
-- virtual text off, signs on
vim.tbl_deep_extend("force", diagnostics_opts, { virtual_text = false }),
-- all diagnostics on
diagnostics_opts,
}
vim.diagnostic.config(M.diagnostics_enum[vim.g.diagnostics_mode])
end
--- Applies the user lsp mappings to a lsp client.
--- This function must be called every time a lsp client is attached.
--- (currently on the config of the plugin `lspconfig`)
--- @param client string The lsp client to attach the lsp mappings to.
--- @param bufnr number The bufnr to attach the lsp mappings to.
function M.apply_user_lsp_mappings(client, bufnr)
local lsp_mappings = require("base.4-mappings").lsp_mappings(client, bufnr)
if not vim.tbl_isempty(lsp_mappings.v) then
lsp_mappings.v["<leader>l"] = { desc = M.get_icon("ActiveLSP", true) .. "LSP" }
end
M.set_mappings(lsp_mappings, { buffer = bufnr })
end
--- Deletes autocmds associated with a specific buffer and autocmd group.
--- @param augroup string The name of the autocmd group from which the autocmds should be removed.
--- @param bufnr number The buffer number from which the autocmds should be removed.
function M.del_autocmds_from_buffer(augroup, bufnr)
-- Attempt to retrieve existing autocmds associated with the specified augroup and bufnr
local cmds_found, cmds = pcall(vim.api.nvim_get_autocmds, { group = augroup, buffer = bufnr })
-- If retrieval was successful
if cmds_found then
-- Map over each retrieved autocmd and delete it
vim.tbl_map(function(cmd) vim.api.nvim_del_autocmd(cmd.id) end, cmds)
end
end
--- Get an icon from given its icon name.
--- if vim.g.fallback_icons_enabled = true, it will return a fallback icon
--- unless specified otherwise.
--- @param icon_name string Name of the icon to retrieve.
--- @param fallback_to_empty_string boolean|nil If this parameter is true, when `vim.g.fallback_icons_enabled = true` then `get_icon()` will return empty string.
--- @return string icon.
function M.get_icon(icon_name, fallback_to_empty_string)
-- guard clause
if fallback_to_empty_string and vim.g.fallback_icons_enabled then return "" end
-- get icon_pack
local icon_pack = (vim.g.fallback_icons_enabled and "fallback_icons") or "icons"
-- cache icon_pack into M
if not M[icon_pack] then -- only if not cached already.
if icon_pack == "icons" then
M.icons = require("base.icons.icons")
elseif icon_pack =="fallback_icons" then
M.fallback_icons = require("base.icons.fallback_icons")
end
end
-- return specified icon
local icon = M[icon_pack] and M[icon_pack][icon_name]
return icon
end
--- Get an empty table of mappings with a key for each map mode.
--- @return table<string,table> # a table with entries for each map mode.
function M.get_mappings_template()
local maps = {}
for _, mode in ipairs {
"", "n", "v", "x", "s", "o", "!", "i", "l", "c", "t", "ia", "ca", "!a"
} do maps[mode] = {} end
return maps
end
--- Check if a plugin is defined in lazy. Useful with lazy loading
--- when a plugin is not necessarily loaded yet.
--- @param plugin string The plugin to search for.
--- @return boolean available # Whether the plugin is available.
function M.is_available(plugin)
local lazy_config_avail, lazy_config = pcall(require, "lazy.core.config")
return lazy_config_avail and lazy_config.spec.plugins[plugin] ~= nil
end
--- Returns true if the file is considered a big file,
--- according to the criteria defined in `vim.g.big_file`.
--- @param bufnr number|nil buffer number. 0 by default, which means current buf.
--- @return boolean is_big_file true or false.
function M.is_big_file(bufnr)
if bufnr == nil then bufnr = 0 end
local filesize = vim.fn.getfsize(vim.api.nvim_buf_get_name(bufnr))
local nlines = vim.api.nvim_buf_line_count(bufnr)
local is_big_file = (filesize > vim.g.big_file.size)
or (nlines > vim.g.big_file.lines)
return is_big_file
end
--- Sends a notification with 'Neovim' as default title.
--- Same as using vim.notify, but it saves us typing the title every time.
--- @param msg string The notification body.
--- @param type number|nil The type of the notification (:help vim.log.levels).
--- @param opts? table The nvim-notify options to use (:help notify-options).
function M.notify(msg, type, opts)
vim.schedule(function()
vim.notify(
msg, type, vim.tbl_deep_extend("force", { title = "Neovim" }, opts or {}))
end)
end
--- Convert a path to the path format of the current operative system.
--- It converts 'slash' to 'inverted slash' if on windows, and vice versa on UNIX.
--- @param path string A path string.
--- @return string|nil,nil path A path string formatted for the current OS.
function M.os_path(path)
if path == nil then return nil end
-- Get the platform-specific path separator
local separator = string.sub(package.config, 1, 1)
return string.gsub(path, '[/\\]', separator)
end
--- Get the options of a plugin managed by lazy.
--- @param plugin string The plugin to get options from
--- @return table opts # The plugin options, or empty table if no plugin.
function M.get_plugin_opts(plugin)
local lazy_config_avail, lazy_config = pcall(require, "lazy.core.config")
local lazy_plugin_avail, lazy_plugin = pcall(require, "lazy.core.plugin")
local opts = {}
if lazy_config_avail and lazy_plugin_avail then
local spec = lazy_config.spec.plugins[plugin]
if spec then opts = lazy_plugin.values(spec, "opts") end
end
return opts
end
--- Set a table of mappings.
--- This wrapper prevents boilerplate code, and takes care of `whichkey.nvim`.
--- @param map_table table A nested table where the first key is the vim mode,
--- the second key is the key to map, and the value is
--- the function to set the mapping to.
--- @param base? table A base set of options to set on every keybinding.
function M.set_mappings(map_table, base)
-- iterate over the first keys for each mode
for mode, maps in pairs(map_table) do
-- iterate over each keybinding set in the current mode
for keymap, options in pairs(maps) do
-- build the options for the command accordingly
if options then
local cmd
local keymap_opts = base or {}
if type(options) == "string" or type(options) == "function" then
cmd = options
else
cmd = options[1]
keymap_opts = vim.tbl_deep_extend("force", keymap_opts, options)
keymap_opts[1] = nil
end
if not cmd then -- if which-key mapping, queue it
keymap_opts[1], keymap_opts.mode = keymap, mode
if not keymap_opts.group then keymap_opts.group = keymap_opts.desc end
if not M.which_key_queue then M.which_key_queue = {} end
table.insert(M.which_key_queue, keymap_opts)
else -- if not which-key mapping, set it
vim.keymap.set(mode, keymap, cmd, keymap_opts)
end
end
end
end
-- if which-key is loaded already, register
if package.loaded["which-key"] then M.which_key_register() end
end
--- Set a highlight to apply to URLs.
function M.set_url_hl()
--- regex used for matching a valid URL/URI string
local url_matcher =
"\\v\\c%(%(h?ttps?|ftp|file|ssh|git)://|[a-z]+[@][a-z]+[.][a-z]+:)" ..
"%([&:#*@~%_\\-=?!+;/0-9a-z]+%(%([.;/?]|[.][.]+)" ..
"[&:#*@~%_\\-=?!+/0-9a-z]+|:\\d+|,%(%(%(h?ttps?|ftp|file|ssh|git)://|" ..
"[a-z]+[@][a-z]+[.][a-z]+:)@![0-9a-z]+))*|\\([&:#*@~%_\\-=?!+;/.0-9a-z]*\\)" ..
"|\\[[&:#*@~%_\\-=?!+;/.0-9a-z]*\\]|\\{%([&:#*@~%_\\-=?!+;/.0-9a-z]*" ..
"|\\{[&:#*@~%_\\-=?!+;/.0-9a-z]*})\\})+"
M.delete_url_hl()
if vim.g.url_hl_enabled then -- set url hl
vim.api.nvim_set_hl(0, "HighlightURL", { underline = true, bg = "NONE" })
vim.fn.matchadd("HighlightURL", url_matcher, 15)
end
end
--- Delete the syntax matching rules for URLs/URIs if set.
function M.delete_url_hl()
for _, match in ipairs(vim.fn.getmatches()) do
if match.group == "HighlightURL" then vim.fn.matchdelete(match.id) end
end
end
--- Open the file or url under the cursor.
--- @param path string The path of the file to open with the system opener.
function M.open_with_program(path)
-- guard clause: If a opener already exists, use it.
if vim.ui.open then return vim.ui.open(path) end
-- command to run
local cmd
-- cmd is different depending the OS
if vim.fn.has("mac") == 1 then
cmd = { "open" }
elseif vim.fn.has("win32") == 1 then
if vim.fn.executable "rundll32" then
cmd = { "rundll32", "url.dll,FileProtocolHandler" }
else
cmd = { "cmd.exe", "/K", "explorer" }
end
elseif vim.fn.has("unix") == 1 then
if vim.fn.executable("explorer.exe") == 1 then -- available in WSL
cmd = { "explorer.exe" }
elseif vim.fn.executable("xdg-open") == 1 then
cmd = { "xdg-open" }
end
end
if not cmd then M.notify("Available system opening tool not found!", vim.log.levels.ERROR) end
-- No path provided? use the file under the cursor; else, expand the path.
if not path then
path = vim.fn.expand("<cfile>")
elseif not path:match "%w+:" then
path = vim.fn.expand(path)
end
-- start job (detached)
vim.fn.jobstart(vim.list_extend(cmd, { path }), { detach = true })
end
--- Convenient wapper to save code when we Trigger events.
--- To listen for an event triggered by this function you can use `autocmd`.
--- @param event string Name of the event.
--- @param is_urgent boolean|nil If true, trigger directly instead of scheduling. Useful for startup events.
-- @usage To run a User event: `trigger_event("User MyUserEvent")`
-- @usage To run a Neovim event: `trigger_event("BufEnter")
function M.trigger_event(event, is_urgent)
-- define behavior
local function trigger()
local is_user_event = string.match(event, "^User ") ~= nil
if is_user_event then
event = event:gsub("^User ", "")
vim.api.nvim_exec_autocmds("User", { pattern = event, modeline = false })
else
vim.api.nvim_exec_autocmds(event, { modeline = false })
end
end
-- execute
if is_urgent then
trigger()
else
vim.schedule(trigger)
end
end
--- Register queued which-key mappings.
function M.which_key_register()
if M.which_key_queue then
local wk_avail, wk = pcall(require, "which-key")
if wk_avail then
wk.add(M.which_key_queue)
M.which_key_queue = nil
end
end
end
return M