@@ -176,7 +176,7 @@ M.delete_surround = function(args)
176176 -- Call the operatorfunc if it has not been called yet
177177 if not args then
178178 -- Clear the delete cache (since it was user-called)
179- cache .delete = {}
179+ cache .delete = { count = vim . v . count1 }
180180
181181 vim .go .operatorfunc = " v:lua.require'nvim-surround'.delete_callback"
182182 return " g@l"
@@ -213,7 +213,7 @@ M.change_surround = function(args)
213213 -- Call the operatorfunc if it has not been called yet
214214 if not args .del_char or not args .add_delimiters then
215215 -- Clear the change cache (since it was user-called)
216- cache .change = { line_mode = args .line_mode }
216+ cache .change = { line_mode = args .line_mode , count = vim . v . count1 }
217217
218218 vim .go .operatorfunc = " v:lua.require'nvim-surround'.change_callback"
219219 return " g@l"
@@ -222,45 +222,48 @@ M.change_surround = function(args)
222222 buffer .set_curpos (args .curpos )
223223 -- Get the selections to change, as well as the delimiters to replace those selections
224224 local selections = utils .get_nearest_selections (args .del_char , " change" )
225- local delimiters = args .add_delimiters ()
226- if selections and delimiters then
227- -- Avoid adding any, and remove any existing whitespace after the
228- -- opening delimiter if only whitespace exists between it and the end
229- -- of the line. Avoid adding or removing leading whitespace before the
230- -- closing delimiter if only whitespace exists between it and the
231- -- beginning of the line.
232-
233- local space_begin , space_end = buffer .get_line (selections .left .last_pos [1 ]):find (" %s*$" )
234- if space_begin - 1 <= selections .left .last_pos [2 ] then -- Whitespace is adjacent to opening delimiter
235- -- Trim trailing whitespace from opening delimiter
236- delimiters [1 ][# delimiters [1 ]] = delimiters [1 ][# delimiters [1 ]]:gsub (" %s+$" , " " )
237- -- Grow selection end to include trailing whitespace, so it gets removed
238- selections .left .last_pos [2 ] = space_end
239- end
225+ local raw_delimiters = args .add_delimiters ()
226+ if not (selections and raw_delimiters ) then
227+ cache .set_callback (" v:lua.require'nvim-surround'.change_callback" )
228+ return
229+ end
230+ local delimiters = utils .normalize_delimiters (raw_delimiters )
231+ -- Avoid adding any, and remove any existing whitespace after the
232+ -- opening delimiter if only whitespace exists between it and the end
233+ -- of the line. Avoid adding or removing leading whitespace before the
234+ -- closing delimiter if only whitespace exists between it and the
235+ -- beginning of the line.
236+
237+ local space_begin , space_end = buffer .get_line (selections .left .last_pos [1 ]):find (" %s*$" )
238+ if space_begin - 1 <= selections .left .last_pos [2 ] then -- Whitespace is adjacent to opening delimiter
239+ -- Trim trailing whitespace from opening delimiter
240+ delimiters [1 ][# delimiters [1 ]] = delimiters [1 ][# delimiters [1 ]]:gsub (" %s+$" , " " )
241+ -- Grow selection end to include trailing whitespace, so it gets removed
242+ selections .left .last_pos [2 ] = space_end
243+ end
240244
241- space_begin , space_end = buffer .get_line (selections .right .first_pos [1 ]):find (" ^%s*" )
242- if space_end + 1 >= selections .right .first_pos [2 ] then -- Whitespace is adjacent to closing delimiter
243- -- Trim leading whitespace from closing delimiter
244- delimiters [2 ][1 ] = delimiters [2 ][1 ]:gsub (" ^%s+" , " " )
245- -- Shrink selection beginning to exclude leading whitespace, so it remains unchanged
246- selections .right .first_pos [2 ] = space_end + 1
247- end
245+ space_begin , space_end = buffer .get_line (selections .right .first_pos [1 ]):find (" ^%s*" )
246+ if space_end + 1 >= selections .right .first_pos [2 ] then -- Whitespace is adjacent to closing delimiter
247+ -- Trim leading whitespace from closing delimiter
248+ delimiters [2 ][1 ] = delimiters [2 ][1 ]:gsub (" ^%s+" , " " )
249+ -- Shrink selection beginning to exclude leading whitespace, so it remains unchanged
250+ selections .right .first_pos [2 ] = space_end + 1
251+ end
248252
249- local sticky_pos = buffer .with_extmark (args .curpos , function ()
250- buffer .change_selection (selections .right , delimiters [2 ])
251- buffer .change_selection (selections .left , delimiters [1 ])
252- end )
253- buffer .restore_curpos ({
254- first_pos = selections .left .first_pos ,
255- sticky_pos = sticky_pos ,
256- old_pos = args .curpos ,
257- })
253+ local sticky_pos = buffer .with_extmark (args .curpos , function ()
254+ buffer .change_selection (selections .right , delimiters [2 ])
255+ buffer .change_selection (selections .left , delimiters [1 ])
256+ end )
257+ buffer .restore_curpos ({
258+ first_pos = selections .left .first_pos ,
259+ sticky_pos = sticky_pos ,
260+ old_pos = args .curpos ,
261+ })
258262
259- if args .line_mode then
260- local first_line = selections .left .first_pos [1 ]
261- local last_line = selections .right .last_pos [1 ]
262- config .get_opts ().indent_lines (first_line , last_line + # delimiters [1 ] + # delimiters [2 ] - 2 )
263- end
263+ if args .line_mode then
264+ local first_line = selections .left .first_pos [1 ]
265+ local last_line = selections .right .last_pos [1 ]
266+ config .get_opts ().indent_lines (first_line , last_line + # delimiters [1 ] + # delimiters [2 ] - 2 )
264267 end
265268
266269 cache .set_callback (" v:lua.require'nvim-surround'.change_callback" )
@@ -338,18 +341,18 @@ M.delete_callback = function()
338341 local buffer = require (" nvim-surround.buffer" )
339342 local cache = require (" nvim-surround.cache" )
340343 local input = require (" nvim-surround.input" )
341- -- Save the current position of the cursor
342- local curpos = buffer .get_curpos ()
343344 -- Get a character input if not cached
344345 cache .delete .char = cache .delete .char or input .get_char ()
345346 if not cache .delete .char then
346347 return
347348 end
348349
349- M .delete_surround ({
350- del_char = cache .delete .char ,
351- curpos = curpos ,
352- })
350+ for _ = 1 , cache .delete .count do
351+ M .delete_surround ({
352+ del_char = cache .delete .char ,
353+ curpos = buffer .get_curpos (),
354+ })
355+ end
353356end
354357
355358M .change_callback = function ()
@@ -358,13 +361,18 @@ M.change_callback = function()
358361 local cache = require (" nvim-surround.cache" )
359362 local input = require (" nvim-surround.input" )
360363 local utils = require (" nvim-surround.utils" )
361- -- Save the current position of the cursor
362- local curpos = buffer .get_curpos ()
363- if not cache .change .del_char or not cache .change .add_delimiters then
364- local del_char = config .get_alias (input .get_char ())
365- local change = config .get_change (del_char )
364+
365+ local del_char = cache .change .del_char or config .get_alias (input .get_char ())
366+ local change = config .get_change (del_char )
367+ if not (del_char and change ) then
368+ return
369+ end
370+
371+ -- To handle number prefixing properly, we just run the replacement algorithm multiple times
372+ for _ = 1 , cache .change .count do
373+ -- If at any point we are unable to find a surrounding pair to change, early exit
366374 local selections = utils .get_nearest_selections (del_char , " change" )
367- if not ( del_char and change and selections ) then
375+ if not selections then
368376 return
369377 end
370378
@@ -378,13 +386,17 @@ M.change_callback = function()
378386 end
379387 end
380388
381- -- Get the new surrounding pair, querying the user for more input if no replacement is provided
382- local ins_char , delimiters
383- if change and change .replacement then
384- delimiters = change .replacement ()
385- else
386- ins_char = input .get_char ()
387- delimiters = config .get_delimiters (ins_char , cache .change .line_mode )
389+ -- Get the new surrounding delimiter pair, prioritizing any delimiters in the cache
390+ -- NB: This must occur between drawing the highlights and clearing them, so the selections are properly
391+ -- highlighted if the user is providing (blocking) input
392+ local delimiters = cache .change .add_delimiters and cache .change .add_delimiters ()
393+ if not delimiters then
394+ if change and change .replacement then
395+ delimiters = delimiters or change .replacement ()
396+ else
397+ local ins_char = input .get_char ()
398+ delimiters = delimiters or config .get_delimiters (ins_char , cache .change .line_mode )
399+ end
388400 end
389401
390402 -- Clear the highlights after getting the replacement surround
@@ -393,18 +405,24 @@ M.change_callback = function()
393405 return
394406 end
395407
408+ local add_delimiters = function ()
409+ return delimiters
410+ end
396411 -- Set the cache
397412 cache .change = {
398413 del_char = del_char ,
399- add_delimiters = function ()
400- return delimiters
401- end ,
414+ add_delimiters = add_delimiters ,
402415 line_mode = cache .change .line_mode ,
416+ count = cache .change .count ,
403417 }
418+ M .change_surround ({
419+ del_char = del_char ,
420+ add_delimiters = add_delimiters ,
421+ line_mode = cache .change .line_mode ,
422+ count = cache .change .count ,
423+ curpos = buffer .get_curpos (),
424+ })
404425 end
405- local args = vim .deepcopy (cache .change )
406- args .curpos = curpos
407- M .change_surround (args ) --- @diagnostic disable-line : param-type-mismatch
408426end
409427
410428return M
0 commit comments