Skip to content

Commit b8b7609

Browse files
perf: query caching, settings validation, cache auto-invalidation
- Cache the treesitter parametrize query at module level in call_spec.lua instead of re-parsing it on every call to get_param_call_nodes. - Add type validation to settings.update(): reject values whose type doesn't match the default, with a warning message. - Add BufWritePost/BufDelete autocommand in ts.lua to automatically invalidate fixture scan cache entries when Python files are saved or deleted. clear_scan_cache() now accepts an optional filepath argument. - Add LuaCATS annotations to settings.lua (@Class PytrizeSettings).
1 parent 0bf7327 commit b8b7609

4 files changed

Lines changed: 61 additions & 20 deletions

File tree

lua/pytrize/call_spec.lua

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -21,25 +21,25 @@ local get_root = function(bufnr)
2121
return trees[1]:root()
2222
end
2323

24+
local PARAM_QUERY = parse_query(
25+
"python",
26+
[[
27+
(decorated_definition (
28+
decorator (
29+
call
30+
function: ((attribute) @param)
31+
)
32+
))
33+
]]
34+
)
35+
2436
local get_param_call_nodes = function(bufnr)
2537
local tsroot = get_root(bufnr)
2638
if tsroot == nil then
2739
return {}
2840
end
29-
local query = parse_query(
30-
"python",
31-
-- TODO not sure why eg (#eq? @param "pytest.mark.parametrize") does not work
32-
[[
33-
(decorated_definition (
34-
decorator (
35-
call
36-
function: ((attribute) @param)
37-
)
38-
))
39-
]]
40-
)
4141
local nodes = {}
42-
for _, node, _ in query:iter_captures(tsroot) do
42+
for _, node, _ in PARAM_QUERY:iter_captures(tsroot, bufnr) do
4343
if ts.get_node_text(node, bufnr) == "pytest.mark.parametrize" then
4444
table.insert(nodes, node:parent())
4545
end

lua/pytrize/settings.lua

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,31 @@ local M = {}
22

33
local warn = require("pytrize.warn").warn
44

5-
-- defaults
5+
---@class PytrizeSettings
6+
---@field no_commands boolean Whether to skip creating user commands (default: false)
7+
---@field highlight string Highlight group for virtual text (default: "LineNr")
8+
---@field metrics boolean Show performance metrics for operations (default: false)
9+
10+
---@type PytrizeSettings
611
M.settings = {
712
no_commands = false,
813
highlight = "LineNr",
914
metrics = false,
10-
-- preferred_input = 'telescope',
1115
}
1216

17+
---@param opts table
1318
M.update = function(opts)
1419
for k, v in pairs(opts) do
1520
if M.settings[k] == nil then
16-
warn("unexpected setting " .. k)
21+
warn(string.format("unknown setting '%s'", k))
1722
else
18-
M.settings[k] = v
23+
local expected = type(M.settings[k])
24+
local actual = type(v)
25+
if expected ~= actual then
26+
warn(string.format("invalid type for setting '%s': expected %s, got %s", k, expected, actual))
27+
else
28+
M.settings[k] = v
29+
end
1930
end
2031
end
2132
end

lua/pytrize/ts.lua

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,24 @@ end
8080

8181
local scan_cache = {}
8282

83-
M.clear_scan_cache = function()
84-
scan_cache = {}
83+
-- Auto-invalidate cache when Python files are saved or deleted
84+
local cache_group = vim.api.nvim_create_augroup("PytrizeCache", { clear = true })
85+
vim.api.nvim_create_autocmd({ "BufWritePost", "BufDelete" }, {
86+
group = cache_group,
87+
pattern = "*.py",
88+
callback = function(args)
89+
local filepath = vim.api.nvim_buf_get_name(args.buf)
90+
scan_cache[filepath] = nil
91+
end,
92+
})
93+
94+
---@param filepath? string Clear a single entry, or all entries if nil.
95+
M.clear_scan_cache = function(filepath)
96+
if filepath then
97+
scan_cache[filepath] = nil
98+
else
99+
scan_cache = {}
100+
end
85101
end
86102

87103
M.scan_fixtures = function(filepath)

tests/pytrize/settings_spec.lua

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,26 @@ describe("settings", function()
2525
end)
2626

2727
it("does not crash on unknown key", function()
28-
-- Stub vim.notify to avoid errors in headless mode
2928
local original_notify = vim.notify
3029
local notified = false
3130
vim.notify = function() notified = true end
3231
settings_mod.update({ nonexistent_key = "value" })
3332
vim.notify = original_notify
3433
assert.is_true(notified)
3534
end)
35+
36+
it("rejects wrong type for a setting", function()
37+
local original_notify = vim.notify
38+
local warned = false
39+
vim.notify = function(msg)
40+
if type(msg) == "string" and msg:find("invalid type") then
41+
warned = true
42+
end
43+
end
44+
settings_mod.update({ highlight = 123 }) -- should be string, not number
45+
vim.notify = original_notify
46+
assert.is_true(warned)
47+
-- value should not have changed
48+
assert.are.equal("LineNr", settings_mod.settings.highlight)
49+
end)
3650
end)

0 commit comments

Comments
 (0)