Skip to content

Commit 294eddc

Browse files
authored
fix(utils): escape percent signs in placeholder replacements (#2736)
1 parent bb571be commit 294eddc

File tree

2 files changed

+28
-2
lines changed

2 files changed

+28
-2
lines changed

lua/codecompanion/utils/init.lua

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,14 +108,20 @@ function M.extract_all_placeholders(prompts)
108108
return all_placeholders
109109
end
110110

111+
---@param str string The string to escape percent signs in for gsub replacement
112+
---@return string The escaped string, safe for use as a gsub replacement
113+
local function escape_gsub_replacement(str)
114+
return (str:gsub("%%", "%%%%"))
115+
end
116+
111117
---Replace any placeholders (e.g. ${placeholder}) in a string or table
112118
---@param t table|string The content to process
113119
---@param replacements table<string, string> Map of placeholder names to replacement values
114120
---@return string|nil The replaced string if input was string, or nil if input was table (modified in place)
115121
function M.replace_placeholders(t, replacements)
116122
if type(t) == "string" then
117123
for placeholder, replacement in pairs(replacements) do
118-
t = t:gsub("%${" .. vim.pesc(placeholder) .. "}", replacement)
124+
t = t:gsub("%${" .. vim.pesc(placeholder) .. "}", escape_gsub_replacement(replacement))
119125
end
120126
return t
121127
else
@@ -124,7 +130,7 @@ function M.replace_placeholders(t, replacements)
124130
M.replace_placeholders(value, replacements)
125131
elseif type(value) == "string" then
126132
for placeholder, replacement in pairs(replacements) do
127-
value = value:gsub("%${" .. vim.pesc(placeholder) .. "}", replacement)
133+
value = value:gsub("%${" .. vim.pesc(placeholder) .. "}", escape_gsub_replacement(replacement))
128134
end
129135
t[key] = value
130136
end

tests/utils/test_utils.lua

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,26 @@ T["Utils"]["replace_placeholders()"]["handles special characters in placeholder
200200
h.eq(result, "Value: test")
201201
end
202202

203+
T["Utils"]["replace_placeholders()"]["handles percent signs in replacement values"] = function()
204+
local result = child.lua([[
205+
return utils.replace_placeholders(
206+
'(<a href="https://my.test.page/tester.php?login=${email}" title="Test Page">%20</a>)',
207+
{ email = "testuser%40testing.org" }
208+
)
209+
]])
210+
h.eq(result, '(<a href="https://my.test.page/tester.php?login=testuser%40testing.org" title="Test Page">%20</a>)')
211+
end
212+
213+
T["Utils"]["replace_placeholders()"]["handles simple percent signs in replacement"] = function()
214+
local result = child.lua([[
215+
return utils.replace_placeholders(
216+
"Encoded: ${value}",
217+
{ value = "%20" }
218+
)
219+
]])
220+
h.eq(result, "Encoded: %20")
221+
end
222+
203223
T["Utils"]["resolve_nested_value()"] = MiniTest.new_set()
204224

205225
T["Utils"]["resolve_nested_value()"]["resolves top-level value"] = function()

0 commit comments

Comments
 (0)