Skip to content

Commit ded28a6

Browse files
authored
feat: Repeat operations for adding delimiters. (#360)
* feat: Very jank way of supporting numeric prefixes for normal_surround. * fix: Slightly smarter way of handling numeric prefixes for normal_surround. * feat: Repeat visual surrounds. * tests: Repeating delimiters.
1 parent ae29810 commit ded28a6

File tree

4 files changed

+136
-10
lines changed

4 files changed

+136
-10
lines changed

lua/nvim-surround/cache.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ local M = {}
22

33
-- These variables hold cache values for dot-repeating the three actions
44

5-
---@type { delimiters: string[][]|nil, line_mode: boolean }
5+
---@type { delimiters: string[][]|nil, line_mode: boolean, count: integer }
66
M.normal = {}
77
---@type { char: string }
88
M.delete = {}

lua/nvim-surround/init.lua

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -50,23 +50,34 @@ M.normal_surround = function(args)
5050
local config = require("nvim-surround.config")
5151
local buffer = require("nvim-surround.buffer")
5252
local cache = require("nvim-surround.cache")
53+
local utils = require("nvim-surround.utils")
5354
-- Call the operatorfunc if it has not been called yet
5455
if not args.selection then
55-
-- Clear the normal cache (since it was user-called)
56-
cache.normal = { line_mode = args.line_mode }
56+
-- Clear the normal cache's delimiters (since it was user-called)
57+
cache.normal = { line_mode = args.line_mode, count = vim.v.count1 }
5758
M.normal_curpos = buffer.get_curpos()
5859
M.pending_surround = true
5960

6061
vim.go.operatorfunc = "v:lua.require'nvim-surround'.normal_callback"
61-
return "g@"
62+
63+
-- Very jank way of resetting v:count to 1 before getting the motion, to ensure that the count
64+
-- does not multiply against the motion
65+
local del_str = ""
66+
local n = vim.v.count1
67+
while n > 1 do
68+
del_str = del_str .. "<Del>"
69+
n = n / 10
70+
end
71+
return del_str .. "g@"
6272
end
6373

6474
local first_pos = args.selection.first_pos
6575
local last_pos = { args.selection.last_pos[1], args.selection.last_pos[2] + 1 }
6676

77+
local delimiters = utils.repeat_delimiters(args.delimiters, cache.normal.count)
6778
local sticky_pos = buffer.with_extmark(M.normal_curpos, function()
68-
buffer.insert_text(last_pos, args.delimiters[2])
69-
buffer.insert_text(first_pos, args.delimiters[1])
79+
buffer.insert_text(last_pos, delimiters[2])
80+
buffer.insert_text(first_pos, delimiters[1])
7081
end)
7182
buffer.restore_curpos({
7283
first_pos = first_pos,
@@ -75,7 +86,7 @@ M.normal_surround = function(args)
7586
})
7687

7788
if args.line_mode then
78-
config.get_opts().indent_lines(first_pos[1], last_pos[1] + #args.delimiters[1] + #args.delimiters[2] - 2)
89+
config.get_opts().indent_lines(first_pos[1], last_pos[1] + #delimiters[1] + #delimiters[2] - 2)
7990
end
8091
M.pending_surround = false
8192
end
@@ -86,17 +97,19 @@ M.visual_surround = function(args)
8697
local config = require("nvim-surround.config")
8798
local buffer = require("nvim-surround.buffer")
8899
local input = require("nvim-surround.input")
100+
local utils = require("nvim-surround.utils")
89101
local ins_char = input.get_char()
90102

91103
if vim.fn.visualmode() == "V" then
92104
args.line_mode = true
93105
end
94-
local delimiters = config.get_delimiters(ins_char, args.line_mode)
95106
local first_pos, last_pos = buffer.get_mark("<"), buffer.get_mark(">")
96-
if not delimiters or not first_pos or not last_pos then
107+
local raw_delimiters = config.get_delimiters(ins_char, args.line_mode)
108+
if not raw_delimiters or not first_pos or not last_pos then
97109
return
98110
end
99111

112+
local delimiters = utils.repeat_delimiters(raw_delimiters, vim.v.count1)
100113
if vim.o.selection == "exclusive" then
101114
last_pos[2] = last_pos[2] - 1
102115
end

lua/nvim-surround/utils.lua

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,22 @@ local M = {}
77
-- Do nothing.
88
M.NOOP = function() end
99

10+
-- Repeats a delimiter pair n times.
11+
---@param delimiters delimiter_pair The delimiters to be repeated.
12+
---@param n integer The number of times to repeat the delimiters.
13+
---@return delimiter_pair @The repeated delimiters.
14+
---@nodiscard
15+
M.repeat_delimiters = function(delimiters, n)
16+
local acc = { { "" }, { "" } }
17+
for _ = 1, n do
18+
acc[1][#acc[1]] = acc[1][#acc[1]] .. delimiters[1][1]
19+
vim.list_extend(acc[1], delimiters[1], 2)
20+
acc[2][#acc[2]] = acc[2][#acc[2]] .. delimiters[2][1]
21+
vim.list_extend(acc[2], delimiters[2], 2)
22+
end
23+
return acc
24+
end
25+
1026
-- Gets the nearest two selections for the left and right surrounding pair.
1127
---@param char string|nil A character representing what kind of surrounding pair is to be selected.
1228
---@param action "delete"|"change" A string representing what action is being performed.

tests/basics_spec.lua

Lines changed: 98 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ describe("nvim-surround", function()
8585
set_lines({ "here", "we", "have", "several", "lines" })
8686
set_curpos({ 2, 2 })
8787
vim.cmd("normal 3ySS`")
88-
check_lines({ "here", "`", "we", "have", "several", "`", "lines" })
88+
check_lines({ "here", "`", "`", "`", "we", "`", "`", "`", "have", "several", "lines" })
8989
end)
9090

9191
it("can surround empty lines", function()
@@ -769,4 +769,101 @@ describe("nvim-surround", function()
769769
"{hello world}",
770770
})
771771
end)
772+
773+
it("can handle number prefixing for normal surround", function()
774+
set_lines({
775+
"some more placeholder text",
776+
"some more lines",
777+
"hello world",
778+
})
779+
set_curpos({ 2, 7 })
780+
vim.cmd("normal 2ysiw*")
781+
check_lines({
782+
"some more placeholder text",
783+
"some **more** lines",
784+
"hello world",
785+
})
786+
787+
vim.cmd("normal 3yssa")
788+
check_lines({
789+
"some more placeholder text",
790+
"<<<some **more** lines>>>",
791+
"hello world",
792+
})
793+
794+
set_curpos({ 1, 6 })
795+
vim.cmd("normal 2ys2wb")
796+
check_lines({
797+
"some ((more placeholder)) text",
798+
"<<<some **more** lines>>>",
799+
"hello world",
800+
})
801+
802+
set_curpos({ 3, 1 })
803+
vim.cmd("normal 20ysw'")
804+
check_lines({
805+
"some ((more placeholder)) text",
806+
"<<<some **more** lines>>>",
807+
"''''''''''''''''''''hello'''''''''''''''''''' world",
808+
})
809+
end)
810+
811+
it("can handle number prefixing for visual surround", function()
812+
set_lines({
813+
"some more placeholder text",
814+
"some more lines",
815+
"hello world",
816+
})
817+
set_curpos({ 1, 6 })
818+
vim.cmd("normal! v")
819+
set_curpos({ 2, 11 })
820+
vim.cmd("normal 3Sr")
821+
check_lines({
822+
"some [[[more placeholder text",
823+
"some more l]]]ines",
824+
"hello world",
825+
})
826+
827+
set_curpos({ 3, 8 })
828+
vim.cmd("normal! v")
829+
set_curpos({ 2, 3 })
830+
vim.cmd("normal 2Sa")
831+
check_lines({
832+
"some [[[more placeholder text",
833+
"so<<me more l]]]ines",
834+
"hello wo>>rld",
835+
})
836+
end)
837+
838+
it("can handle number prefixing for visual surround (line mode)", function()
839+
set_lines({
840+
"some more placeholder text",
841+
})
842+
set_curpos({ 1, 6 })
843+
vim.cmd("normal V3Sa")
844+
check_lines({
845+
"<",
846+
"<",
847+
"<",
848+
"some more placeholder text",
849+
">",
850+
">",
851+
">",
852+
})
853+
end)
854+
855+
it("can handle number prefixing for visual surround (block mode)", function()
856+
set_lines({
857+
"some more placeholder text",
858+
"a short line",
859+
"a slightly longer line",
860+
})
861+
set_curpos({ 1, 6 })
862+
vim.cmd("normal " .. ctrl_v .. "jj2w3Sa")
863+
check_lines({
864+
"some <<<more placehold>>>er text",
865+
"a sho<<<rt line>>>",
866+
"a sli<<<ghtly longer l>>>ine",
867+
})
868+
end)
772869
end)

0 commit comments

Comments
 (0)