Skip to content

Commit 1186ad4

Browse files
authored
Fix/fstrings skip regex format (#30)
* fix(f-strings): do not inject string on regex or `.format()` string Try to find if either the string is "regex" or is a "format" type string and ignore. * test(f-string): add tests for inserts and not insterting fstrings * test: remove duplicate and move text actions from ts tests to own file
1 parent a67cf11 commit 1186ad4

2 files changed

Lines changed: 42 additions & 6 deletions

File tree

lua/python/treesitter/commands.lua

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,26 @@ function PythonTreeSitterCommands.ts_wrap_at_cursor(subtitute_option)
209209
end)
210210
end
211211

212+
---
213+
---@param node TSNode the current ts node we are checking for parents
214+
---@return string callText check if this node has a "call" type node 3 parents up
215+
--- this is used for checking on "".format() calls for strings.
216+
local function checkForFStringCallParent(node)
217+
local callStatus, callText = pcall(function()
218+
local callNode = node:parent():parent():parent()
219+
if callNode then
220+
local text = getNodeText(callNode)
221+
return text
222+
end
223+
return ""
224+
end) -- Get potential function call on string for .format()
225+
226+
if not callStatus then
227+
callText = ""
228+
end
229+
return callText
230+
end
231+
212232
function PythonTreeSitterCommands.pythonFStr()
213233
local maxCharacters = 200 -- safeguard to prevent converting invalid code
214234
local node = getNodeAtCursor()
@@ -217,6 +237,8 @@ function PythonTreeSitterCommands.pythonFStr()
217237
end
218238

219239
local strNode
240+
local callText = checkForFStringCallParent(node)
241+
220242
if node:type() == "string" then
221243
strNode = node
222244
elseif node:type():find("^string_") then
@@ -239,10 +261,12 @@ function PythonTreeSitterCommands.pythonFStr()
239261
return
240262
end -- safeguard on converting invalid code
241263

264+
local isFormatString = callText:find([[^.*["']%.format%(]])
265+
local isRString = text:find("^r")
242266
local isFString = text:find("^r?f") -- rf -> raw-formatted-string
243267
local hasBraces = text:find("{.-[^%d,%s].-}") -- nonRegex-braces, see #12 and #15
244268

245-
if not isFString and hasBraces then
269+
if (not isFString and not isFormatString and not isRString) and hasBraces then
246270
text = "f" .. text
247271
replaceNodeText(strNode, text)
248272
elseif isFString and not hasBraces then

tests/test_text_actions.lua

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,19 +25,31 @@ local get_lines = function()
2525
return child.api.nvim_buf_get_lines(0, 0, -1, true)
2626
end
2727

28-
T["text_actions"] = MiniTest.new_set({
29-
n_retry = 3,
28+
T["f-string"] = MiniTest.new_set({
3029
hooks = {
3130
pre_case = function()
3231
child.cmd("e _not_existing_new_buffer.py")
32+
child.type_keys("cc", [["TEST"]], "<Esc>", "0")
3333
end,
3434
},
3535
})
3636

37-
T["text_actions"]["insert_f_string"] = function()
38-
child.type_keys("i", [[print("{foo}")]], "<left><esc>")
37+
T["f-string"]["insert f string"] = function()
38+
child.cmd("e! _not_existing_new_buffer.py")
39+
child.type_keys("cc", [["{foo}"]], "<Esc>", "hh", "i", "<Esc>")
40+
eq(get_lines(), { [[f"{foo}"]] })
41+
end
42+
43+
T["f-string"]["skip on r"] = function()
44+
child.cmd("e! _not_existing_new_buffer.py")
45+
child.type_keys("cc", [[r"{foo}"]], "<Esc>", "hh", "i", "<Esc>")
46+
eq(get_lines(), { [[r"{foo}"]] })
47+
end
3948

40-
eq(get_lines(), { [[print(f"{foo}")]] })
49+
T["f-string"]["skip on format"] = function()
50+
child.cmd("e! _not_existing_new_buffer.py")
51+
child.type_keys("cc", [["{foo}".format()]], "<Esc>", "0lll", "i", "<Esc>")
52+
eq(get_lines(), { [["{foo}".format()]] })
4153
end
4254

4355
-- Return test set which will be collected and execute inside `MiniTest.run()`

0 commit comments

Comments
 (0)