Skip to content

Commit 7e2482e

Browse files
kylechuiphgz
authored andcommitted
feat: Repeat operations for adding delimiters. (kylechui#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 5edb013 commit 7e2482e

File tree

4 files changed

+136
-11
lines changed

4 files changed

+136
-11
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, line_mode: boolean }
88
M.delete = {}

lua/nvim-surround/init.lua

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -63,24 +63,35 @@ M.normal_surround = function(args)
6363
local config = require("nvim-surround.config")
6464
local buffer = require("nvim-surround.buffer")
6565
local cache = require("nvim-surround.cache")
66+
local utils = require("nvim-surround.utils")
6667

6768
-- Call the operatorfunc if it has not been called yet
6869
if not args.selection then
69-
-- Clear the normal cache (since it was user-called)
70-
cache.normal = { line_mode = args.line_mode }
70+
-- Clear the normal cache's delimiters (since it was user-called)
71+
cache.normal = { line_mode = args.line_mode, count = vim.v.count1 }
7172
M.old_pos = buffer.get_curpos()
7273
M.pending_surround = true
7374

7475
vim.go.operatorfunc = "v:lua.require'nvim-surround'.normal_callback"
75-
return "g@"
76+
77+
-- Very jank way of resetting v:count to 1 before getting the motion, to ensure that the count
78+
-- does not multiply against the motion
79+
local del_str = ""
80+
local n = vim.v.count1
81+
while n > 1 do
82+
del_str = del_str .. "<Del>"
83+
n = n / 10
84+
end
85+
return del_str .. "g@"
7686
end
7787

7888
local first_pos = args.selection.first_pos
7989
local last_pos = { args.selection.last_pos[1], args.selection.last_pos[2] + 1 }
8090

91+
local delimiters = utils.repeat_delimiters(args.delimiters, cache.normal.count)
8192
local sticky_pos = buffer.with_extmark(M.old_pos, function()
82-
buffer.insert_text(last_pos, args.delimiters[2])
83-
buffer.insert_text(first_pos, args.delimiters[1])
93+
buffer.insert_text(last_pos, delimiters[2])
94+
buffer.insert_text(first_pos, delimiters[1])
8495
end)
8596

8697
buffer.restore_curpos({
@@ -90,7 +101,7 @@ M.normal_surround = function(args)
90101
})
91102

92103
if args.line_mode then
93-
config.get_opts().indent_lines(first_pos[1], last_pos[1] + #args.delimiters[1] + #args.delimiters[2] - 2)
104+
config.get_opts().indent_lines(first_pos[1], last_pos[1] + #delimiters[1] + #delimiters[2] - 2)
94105
end
95106
M.pending_surround = false
96107
end
@@ -101,18 +112,19 @@ M.visual_surround = function(args)
101112
local config = require("nvim-surround.config")
102113
local buffer = require("nvim-surround.buffer")
103114
local input = require("nvim-surround.input")
104-
115+
local utils = require("nvim-surround.utils")
105116
local ins_char = input.get_char()
106117

107118
if vim.fn.visualmode() == "V" then
108119
args.line_mode = true
109120
end
110-
local delimiters = config.get_delimiters(ins_char, args.line_mode)
111121
local first_pos, last_pos = buffer.get_mark("<"), buffer.get_mark(">")
112-
if not delimiters or not first_pos or not last_pos then
122+
local raw_delimiters = config.get_delimiters(ins_char, args.line_mode)
123+
if not raw_delimiters or not first_pos or not last_pos then
113124
return
114125
end
115126

127+
local delimiters = utils.repeat_delimiters(raw_delimiters, vim.v.count1)
116128
if vim.o.selection == "exclusive" then
117129
last_pos[2] = last_pos[2] - 1
118130
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)