11local api = vim .api
22local M = {}
33
4- --- @alias vector any[]
5-
6- local setlocal_opr_templates = {
7- set = [[ setl ${option}=${value}]] ,
8- remove = [[ exe 'setl ${option}-=${value}']] ,
9- append = [[ exe 'setl ${option}=' . (&${option} == "" ? "" : &${option} . ",") . '${value}']] ,
10- prepend = [[ exe 'setl ${option}=${value}' . (&${option} == "" ? "" : "," . &${option})]] ,
11- }
4+ --- @alias vector <T> T[]
125
136function M ._echo_multiline (msg , hl , schedule )
147 if schedule then
5043--- @return any result Return value
5144function M .no_win_event_call (f )
5245 local last = vim .o .eventignore
46+ --- @diagnostic disable-next-line : undefined-field
5347 vim .opt .eventignore :prepend (
5448 " WinEnter,WinLeave,WinNew,WinClosed,BufWinEnter,BufWinLeave,BufEnter,BufLeave"
5549 )
@@ -237,6 +231,8 @@ function M.input_char(prompt, opt)
237231
238232 local s = type (c ) == " number" and vim .fn .nr2char (c ) or nil
239233 local raw = type (c ) == " number" and s or c
234+
235+ --- @diagnostic disable-next-line : return-type-mismatch
240236 return s , raw
241237end
242238
@@ -263,45 +259,82 @@ function M.pause(msg)
263259 )
264260end
265261
266- --- @class SetLocalSpec
262+ --- Map of options that accept comma separated, list-like values, but don't work
263+ --- correctly with Option:set(), Option:append(), Option:prepend(), and
264+ --- Option:remove() (seemingly for legacy reasons).
265+ --- WARN: This map is incomplete!
266+ local list_like_options = {
267+ winhighlight = true ,
268+ listchars = true ,
269+ fillchars = true ,
270+ }
271+
272+ --- @class utils.set_local.Opt
267273--- @field method ' "set"' | ' "remove"' | ' "append"' | ' "prepend"' Assignment method. (default : " set" )
268274
269- --- @class SetLocalListSpec : string []
270- --- @field opt SetLocalSpec
275+ --- @class utils.set_local.ListSpec : string []
276+ --- @field opt utils.set_local.Opt
277+
278+ --- @alias WindowOptions table<string , boolean | integer | string | utils.set_local.ListSpec>
271279
272- --- HACK: workaround for inconsistent behavior from `vim.opt_local`.
273- --- @see [Neovim issue ](https ://github.com /neovim /neovim /issues /14670)
274280--- @param winids number[] | number Either a list of winids , or a single winid (0 for current window ).
275- --- @param option_map table<string , SetLocalListSpec | string | boolean>
276- --- @param opt ? SetLocalSpec
281+ --- @param option_map WindowOptions
282+ --- @param opt ? utils.set_local.Opt
277283function M .set_local (winids , option_map , opt )
278284 if type (winids ) ~= " table" then
279285 winids = { winids }
280286 end
281287
282- opt = vim .tbl_extend (" keep" , opt or {}, { method = " set" })
288+ opt = vim .tbl_extend (" keep" , opt or {}, { method = " set" }) --[[ @as table ]]
283289
284- local cmd
285290 for _ , id in ipairs (winids ) do
286291 api .nvim_win_call (id , function ()
287292 for option , value in pairs (option_map ) do
288- if type ( value ) == " boolean " then
289- cmd = string.format ( " setl %s%s " , value and " " or " no " , option )
290- else
291- --- @type SetLocalSpec
292- local o = opt
293- if type (value ) == " table" then
294- o = vim . tbl_extend ( " force " , opt , value .opt or {})
295- value = table.concat ( value , " , " )
293+ local o = opt
294+ local fullname = api . nvim_get_option_info ( option ). name
295+ local is_list_like = list_like_options [ fullname ]
296+ local cur_value = vim . o [ fullname ]
297+
298+ if type (value ) == " table" then
299+ if value .opt then
300+ o = vim . tbl_extend ( " force " , opt , value . opt ) --[[ @as table ]]
296301 end
297302
298- cmd = M .str_template (
299- setlocal_opr_templates [o .method ],
300- { option = option , value = tostring (value ):gsub (" '" , " ''" ) }
301- )
303+ if is_list_like then
304+ value = table.concat (value , " ," )
305+ end
302306 end
303307
304- vim .cmd (cmd )
308+ if o .method == " set" then
309+ vim .opt_local [option ] = value
310+
311+ else
312+ if o .method == " remove" then
313+ if is_list_like then
314+ vim .opt_local [fullname ] = cur_value :gsub (" ,?" .. vim .pesc (value ), " " )
315+ else
316+ vim .opt_local [fullname ]:remove (value )
317+ end
318+
319+ elseif o .method == " append" then
320+ if is_list_like then
321+ vim .opt_local [fullname ] = (" %s%s" ):format (cur_value ~= " " and cur_value .. " ," , value )
322+ else
323+ vim .opt_local [fullname ]:append (value )
324+ end
325+
326+ elseif o .method == " prepend" then
327+ if is_list_like then
328+ vim .opt_local [fullname ] = (" %s%s%s" ):format (
329+ value ,
330+ cur_value ~= " " and " ," or " " ,
331+ cur_value
332+ )
333+ else
334+ vim .opt_local [fullname ]:prepend (value )
335+ end
336+ end
337+ end
305338 end
306339 end )
307340 end
@@ -316,7 +349,7 @@ function M.unset_local(winids, option)
316349
317350 for _ , id in ipairs (winids ) do
318351 api .nvim_win_call (id , function ()
319- vim .cmd ( string.format ( " set %s< " , option ))
352+ vim .opt_local [ option ] = nil
320353 end )
321354 end
322355end
0 commit comments