-
Notifications
You must be signed in to change notification settings - Fork 109
/
hacks.lua
289 lines (248 loc) · 7.02 KB
/
hacks.lua
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
local require = require("noice.util.lazy")
local Util = require("noice.util")
local Router = require("noice.message.router")
local Api = require("noice.api")
local Cmdline = require("noice.ui.cmdline")
-- HACK: a bunch of hacks to make Noice behave
local M = {}
---@type fun()[]
M._disable = {}
function M.reset_augroup()
M.group = vim.api.nvim_create_augroup("noice.hacks", { clear = true })
end
function M.enable()
M.reset_augroup()
M.fix_incsearch()
M.fix_input()
M.fix_redraw()
M.fix_cmp()
M.fix_vim_sleuth()
end
function M.fix_vim_sleuth()
vim.g.sleuth_noice_heuristics = 0
end
function M.disable()
M.reset_augroup()
for _, fn in pairs(M._disable) do
fn()
end
M._disable = {}
end
-- start a timer that checks for vim.v.hlsearch.
-- Clears search count and stops timer when hlsearch==0
function M.fix_nohlsearch()
M.fix_nohlsearch = Util.interval(30, function()
if vim.o.hlsearch and vim.v.hlsearch == 0 then
local m = require("noice.ui.msg").get("msg_show", "search_count")
require("noice.message.manager").remove(m)
end
end, {
enabled = function()
return vim.o.hlsearch and vim.v.hlsearch == 1
end,
})
M.fix_nohlsearch()
end
---@see https://github.com/neovim/neovim/issues/17810
function M.fix_incsearch()
---@type integer|nil
local conceallevel
vim.api.nvim_create_autocmd("CmdlineEnter", {
group = M.group,
callback = function(event)
if event.match == "/" or event.match == "?" then
conceallevel = vim.wo.conceallevel
vim.wo.conceallevel = 0
end
end,
})
vim.api.nvim_create_autocmd("CmdlineLeave", {
group = M.group,
callback = function(event)
if conceallevel and (event.match == "/" or event.match == "?") then
vim.wo.conceallevel = conceallevel
conceallevel = nil
end
end,
})
end
-- we need to intercept redraw so we can safely ignore message triggered by redraw
-- This wraps vim.cmd, nvim_cmd, nvim_command and nvim_exec
---@see https://github.com/neovim/neovim/issues/20416
M.inside_redraw = false
function M.fix_redraw()
local nvim_cmd = vim.api.nvim_cmd
local function wrap(fn, ...)
local inside_redraw = M.inside_redraw
M.inside_redraw = true
---@type boolean, any
local ok, ret = pcall(fn, ...)
-- check if the ui needs updating
Util.try(Router.update)
if not inside_redraw then
M.inside_redraw = false
end
if ok then
return ret
end
error(ret)
end
vim.api.nvim_cmd = function(cmd, ...)
if type(cmd) == "table" and cmd.cmd and cmd.cmd == "redraw" then
return wrap(nvim_cmd, cmd, ...)
else
return nvim_cmd(cmd, ...)
end
end
local nvim_command = vim.api.nvim_command
vim.api.nvim_command = function(cmd, ...)
if cmd == "redraw" then
return wrap(nvim_command, cmd, ...)
else
return nvim_command(cmd, ...)
end
end
local nvim_exec = vim.api.nvim_exec
vim.api.nvim_exec = function(cmd, ...)
if type(cmd) == "string" and cmd:find("redraw") then
-- WARN: this will potentially lose messages before or after the redraw ex command
-- example: echo "foo" | redraw | echo "bar"
-- the 'foo' message will be lost
return wrap(nvim_exec, cmd, ...)
else
return nvim_exec(cmd, ...)
end
end
table.insert(M._disable, function()
vim.api.nvim_cmd = nvim_cmd
vim.api.nvim_command = nvim_command
vim.api.nvim_exec = nvim_exec
end)
end
---@see https://github.com/neovim/neovim/issues/20311
M.before_input = false
function M.fix_input()
local function wrap(fn, skip)
return function(...)
if skip and skip(...) then
return fn(...)
end
local Manager = require("noice.message.manager")
-- do any updates now before blocking
M.before_input = true
Router.update()
---@type boolean, any
local ok, ret = pcall(fn, ...)
-- clear any message right after input
Manager.clear({ event = "msg_show", kind = { "echo", "echomsg", "" } })
M.before_input = false
if ok then
return ret
end
error(ret)
end
end
local function skip(expr)
return expr ~= nil
end
local getchar = vim.fn.getchar
local getcharstr = vim.fn.getcharstr
local inputlist = vim.fn.inputlist
-- local confirm = vim.fn.confirm
vim.fn.getchar = wrap(vim.fn.getchar, skip)
vim.fn.getcharstr = wrap(vim.fn.getcharstr, skip)
vim.fn.inputlist = wrap(vim.fn.inputlist, nil)
-- vim.fn.confirm = wrap(vim.fn.confirm, nil)
table.insert(M._disable, function()
vim.fn.getchar = getchar
vim.fn.getcharstr = getcharstr
vim.fn.inputlist = inputlist
-- vim.fn.confirm = confirm
end)
end
-- Fixes cmp cmdline position
function M.fix_cmp()
M.on_module("cmp.utils.api", function(api)
local get_cursor = api.get_cursor
api.get_cursor = function()
if api.is_cmdline_mode() then
local pos = Api.get_cmdline_position()
if pos then
return { pos.bufpos.row, vim.fn.getcmdpos() - 1 }
end
end
return get_cursor()
end
local get_screen_cursor = api.get_screen_cursor
api.get_screen_cursor = function()
if api.is_cmdline_mode() then
local pos = Api.get_cmdline_position()
if pos then
local col = vim.fn.getcmdpos() - Cmdline.last().offset
return { pos.screenpos.row, pos.screenpos.col + col }
end
end
return get_screen_cursor()
end
table.insert(M._disable, function()
api.get_cursor = get_cursor
api.get_screen_cursor = get_screen_cursor
end)
end)
end
function M.cmdline_force_redraw()
if not require("noice.util.ffi").cmdpreview then
return
end
-- HACK: this will trigger redraw during substitute and cmdpreview,
-- but when moving the cursor, the screen will be cleared until
-- a new character is entered
vim.api.nvim_input(" <bs>")
end
---@type string?
M._guicursor = nil
function M.hide_cursor()
if M._guicursor == nil then
M._guicursor = vim.go.guicursor
end
-- schedule this, since otherwise Neovide crashes
vim.schedule(function()
if M._guicursor then
vim.go.guicursor = "a:NoiceHiddenCursor"
end
end)
M._disable.guicursor = M.show_cursor
end
function M.show_cursor()
if M._guicursor then
if not Util.is_exiting() then
vim.schedule(function()
if M._guicursor and not Util.is_exiting() then
-- we need to reset all first and then wait for some time before resetting the guicursor. See #114
vim.go.guicursor = "a:"
vim.cmd.redrawstatus()
vim.go.guicursor = M._guicursor
M._guicursor = nil
end
end)
end
end
end
---@param fn fun(mod)
function M.on_module(module, fn)
if package.loaded[module] then
return fn(package.loaded[module])
end
package.preload[module] = function()
package.preload[module] = nil
for _, loader in pairs(package.loaders) do
local ret = loader(module)
if type(ret) == "function" then
local mod = ret()
fn(mod)
return mod
end
end
end
end
return M