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
|
local M = {}
M.escape = function()
local key = "<ESC>"
vim.api.nvim_replace_termcodes(key, true, false, true)
vim.api.nvim_feedkeys(key, "x", true)
-- vim.api.nvim_input("<ESC>")
-- vim.wait(100, function()
-- return "n" == vim.fn.mode()
-- end)
end
M.get_visual_range = function()
local start_pos = vim.fn.getpos("v")
local end_pos = vim.fn.getcurpos()
return { start_pos[2], end_pos[2] }
-- return { { line = start_pos[2], col = start_pos[3] }, { line = end_pos[2], col = end_pos[3] } }
end
--- Wrapper for bdelete/bwipeout to add a write/discard modified selection and fire autocmd event
---@param opts ?BdeleteOpts
---@return nil
M.bdelete = function(opts)
---@class BdeleteOpts
---@field bufnr number Number of the buffer to target
---@field force boolean Discard modified buffer
---@field wipeout boolean Wipeout buffer
opts = opts or {}
if opts.bufnr == nil or opts.bufnr == 0 then
opts.bufnr = vim.api.nvim_get_current_buf()
end
if not opts.force and vim.bo[opts.bufnr].modified then
return vim.ui.select({ "write", "discard", "abort" }, {
prompt = string.format("No write since last change for buffer %d:", opts.bufnr),
kind = "select_normal",
}, function(_, idx)
if idx == 1 then
vim.cmd("write")
elseif idx == 2 then
opts.force = true
else
return
end
M.bdelete(opts)
end)
end
local cmd = "bdelete"
if opts.wipeout then
cmd = "bwipeout"
end
if opts.force then
cmd = cmd .. "!"
end
vim.api.nvim_exec_autocmds("User", { pattern = "BDeletePre", data = opts })
if vim.api.nvim_buf_is_valid(opts.bufnr) then
vim.cmd(string.format("%s %d", cmd, opts.bufnr))
vim.api.nvim_exec_autocmds("User", { pattern = "BDeletePost", data = opts })
end
end
M.spinner_frames = { "⣷", "⣯", "⣟", "⡿", "⢿", "⣻", "⣽", "⣾" }
M.diagnostic_signs = {
error = { text = " ", texthl = "DiagnosticSignError" },
warn = { text = " ", texthl = "DiagnosticSignWarn" },
info = { text = " ", texthl = "DiagnosticSignInfo" },
hint = { text = "", texthl = "DiagnosticSignHint" },
}
setmetatable(M.diagnostic_signs, {
__index = function()
return M.diagnostic_signs.info
end,
})
--- Helper function to create a group of keymaps that share a common prefix and/or options. If which-key is installed
--- and group_opts.name is set, it will also create a corresponding named group.
---@param mode string|table Same mode short names as vim.keymap.set(). A list will create the group on all modes.
---@param prefix string Prefix to prepend to the lhs of all keymaps in the group.
---@param group_opts ?table Options to apply to all keymaps in this group.
---In addition to the options listed in vim.keymap.set, this table also accepts the following:
--- - name: Name of the group to create in which-key. If which-key is not installed, this does nothing.
---@return function Function to create mapping using the groups defaults.
-- TODO: Possibly add memoization to groups/subgroups using the __call metatable attribute
M.create_map_group = function(mode, prefix, group_opts)
group_opts = group_opts or {}
local name = group_opts.name
group_opts.name = nil
local status_ok, which_key = pcall(require, "which-key")
if status_ok and name ~= nil then
for _, m in pairs(vim.tbl_flatten({ mode })) do
which_key.register({ [prefix] = { name = name } }, vim.tbl_extend("force", { mode = m }, group_opts))
end
end
return function(lhs, rhs, opts)
vim.keymap.set(mode, prefix .. lhs, rhs, vim.tbl_deep_extend("keep", opts or {}, group_opts))
end
end
-- TODO: add autocommand/keymap to reload current open file/module
M.reload = function(name)
local notify_opts = { title = string.format("[utils] reload module: '%s'", name) }
local status_ok, result = pcall(require, "plenary.reload")
if status_ok then
status_ok, result = pcall(result.reload_module, name)
end
if status_ok then
status_ok, result = pcall(require, name)
end
if status_ok then
vim.notify("Successfully reloaded module", vim.log.levels.INFO, { title = "[utils]" })
else
vim.notify(string.format("Failed to reload module: %s", result), vim.log.levels.ERROR, notify_opts)
end
end
M.popup = function(file_path)
local buf = vim.api.nvim_create_buf(false, true)
vim.api.nvim_buf_set_option(buf, "bufhidden", "wipe")
local width = vim.api.nvim_get_option("columns")
local height = vim.api.nvim_get_option("lines")
local win_height = math.ceil(height * 0.8 - 4)
local win_width = math.ceil(width * 0.8)
local row = math.ceil((height - win_height) / 2 - 1)
local col = math.ceil((width - win_width) / 2)
local opts = {
style = "minimal",
relative = "editor",
width = win_width,
height = win_height,
row = row,
col = col,
border = "rounded",
}
local win = vim.api.nvim_open_win(buf, true, opts)
vim.api.nvim_win_set_option(win, "cursorline", true)
vim.api.nvim_buf_set_option(buf, "modifiable", true)
vim.api.nvim_command("$read" .. file_path)
vim.api.nvim_buf_set_option(0, "modifiable", false)
end
--- count existing buffers
---@param filter function predicate used to filter counted buffers
---@return table
M.count_bufs_by_type = function(filter)
local count = { normal = 0, acwrite = 0, help = 0, nofile = 0, nowrite = 0, quickfix = 0, terminal = 0, prompt = 0 }
local buftypes = vim.api.nvim_list_bufs()
for _, bufname in pairs(buftypes) do
if filter == nil or filter(bufname) then
local buftype = vim.api.nvim_buf_get_option(bufname, "buftype")
buftype = buftype ~= "" and buftype or "normal"
count[buftype] = count[buftype] + 1
end
end
return count
end
M.file_exists = function(file)
local ok, err, code = os.rename(file, file)
if not ok and code == 13 then
return true
end
return ok, err
end
M.isdir = function(path)
return M.file_exists(path .. "/")
end
return M
|