Skip to content

Commit 439f374

Browse files
authored
refactor: analyzer.lua (#38)
1 parent 7748127 commit 439f374

File tree

4 files changed

+133
-91
lines changed

4 files changed

+133
-91
lines changed

.editorconfig

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
root = true
2+
3+
[*]
4+
indent_style = space
5+
indent_size = 2
6+
charset = utf-8
7+
max_line_length = 120
8+
insert_final_newline = true

lua/js-i18n/analyzer.lua

Lines changed: 32 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -73,52 +73,51 @@ end
7373
--- Treesitterパーサーをセットアップしてキーにマッチするノードを取得する関数
7474
--- @param bufnr number 文言ファイルのバッファ番号
7575
--- @param keys string[] キー
76-
--- @param start? integer 開始位置
77-
--- @param stop? integer 終了位置
7876
--- @return TSNode | nil, string | nil
79-
function M.get_node_for_key(bufnr, keys, start, stop)
80-
keys = { unpack(keys) }
81-
local key = table.concat(keys, c.config.key_separator)
77+
function M.get_node_for_key(bufnr, keys)
8278
local ts = vim.treesitter
8379

8480
local parser = ts.get_parser(bufnr, "json")
8581
local tree = parser:parse()[1]
8682
local root = tree:root()
8783

88-
local query =
89-
ts.query.parse("json", '(pair key: (string) @key (#eq? @key "\\"' .. keys[1] .. '\\""))')
90-
91-
--- @type TSNode[]
92-
local match_nodes = {}
84+
local json_node = root
85+
for i, k in ipairs(keys) do
86+
local query = ts.query.parse("json", [[
87+
(pair
88+
key: (string) @key (#eq? @key "\"]] .. k .. [[\"")
89+
value: [(object)(string)] @value
90+
)
91+
]])
92+
93+
local key_node = nil
94+
local key_node_depth = 9999
95+
local value_node = nil
96+
local value_node_depth = 9999
97+
for id, node, _ in query:iter_captures(json_node, bufnr) do
98+
local name = query.captures[id]
99+
local depth = calculate_node_depth(node)
100+
if name == "key" and depth < key_node_depth then
101+
key_node = node
102+
key_node_depth = depth
103+
elseif name == "value" and depth < value_node_depth then
104+
value_node = node
105+
value_node_depth = depth
106+
end
107+
end
93108

94-
for _, match, _ in query:iter_matches(root, bufnr, start, stop) do
95-
for _, node in ipairs(match) do
96-
table.insert(match_nodes, node)
109+
if key_node == nil or value_node == nil then
110+
break
97111
end
98-
end
99112

100-
--- @type TSNode
101-
local node = vim
102-
.iter(match_nodes)
103-
-- find min depth
104-
:fold(match_nodes[1], function(acc, node)
105-
if calculate_node_depth(node) < calculate_node_depth(acc) then
106-
return node
107-
else
108-
return acc
109-
end
110-
end)
111-
112-
if #keys == 1 then
113-
return node, nil
114-
elseif node ~= nil then
115-
table.remove(keys, 1)
116-
local parent = node:parent()
117-
if parent ~= nil then
118-
return M.get_node_for_key(bufnr, keys, parent:start(), parent:end_())
113+
if i == #keys then
114+
return key_node, nil
119115
end
116+
117+
json_node = value_node
120118
end
121119

120+
local key = table.concat(keys, c.config.key_separator)
122121
return nil, "Key not found: " .. key
123122
end
124123

tests/busted.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ require("lazy.minit").busted({
88
spec = {
99
"nvim-lua/plenary.nvim",
1010
"neovim/nvim-lspconfig",
11-
{ "nvim-treesitter/nvim-treesitter", build = ":TSUpdate javascript typescript tsx" },
11+
{ "nvim-treesitter/nvim-treesitter", build = ":TSUpdate json javascript typescript tsx" },
1212
{ dir = vim.uv.cwd(), opts = {} },
1313
},
1414
})

tests/js-i18n/analyzer_spec.lua

Lines changed: 92 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -2,72 +2,107 @@ local helper = require("tests.helper")
22

33
local analyzer = require("js-i18n.analyzer")
44

5-
--- test helper
5+
describe("analyzer.get_node_for_key", function()
6+
--- @type test.Project
7+
local project = nil
8+
before_each(function()
9+
project = helper.use_project("i18next")
10+
end)
611

7-
--- @param get_project function(): test.Project
8-
--- @param file string
9-
--- @param assertion function(result: FindTExpressionResultItem[], utils: table)
10-
local function test_analyze_file(get_project, file, assertion)
11-
it("should find 't' function calls in " .. file, function()
12-
-- Arrange
13-
local project = get_project()
14-
vim.cmd("e " .. project.path .. "/test_analyzer/" .. file)
12+
after_each(function()
13+
vim.cmd("bufdo bd!")
14+
end)
1515

16-
-- Act
17-
local result = analyzer.find_call_t_expressions(0)
16+
local tests = {
17+
-- stylua: ignore start
18+
{ key = "exists-key", exp_key_row = 2 },
19+
{ key = "nested.key", exp_key_row = 4 },
20+
{ key = "nested", exp_key_row = 3 },
21+
-- stylua: ignore end
22+
}
1823

19-
local assert_item = function(idx, exp)
20-
local item = result[idx]
24+
for _, test in ipairs(tests) do
25+
it("#hoge " .. test.key, function()
26+
-- Arrange
27+
vim.cmd("e " .. project.path .. "/locales/en/translation.json")
2128

22-
local function assert_key_value(key)
23-
-- stylua: ignore start
24-
assert(
25-
exp[key] == item[key],
26-
string.format("result[%d].%s to be equal.\nPassed:\n(%s) %s\nExpected:\n(%s) %s",
27-
idx, key, type(item[key]), item[key], type(exp[key]), exp[key])
28-
)
29-
-- stylua: ignore end
29+
-- Act
30+
local result = analyzer.get_node_for_key(0, vim.split(test.key, ".", { plain = true }))
31+
32+
-- Assert
33+
if not result then
34+
error("Key not found: " .. test.key)
3035
end
31-
assert_key_value("key")
32-
assert_key_value("key_prefix")
33-
assert_key_value("key_arg")
34-
end
36+
local key_row = result:start() + 1
37+
assert.are.equal(test.exp_key_row, key_row)
38+
end)
39+
end
40+
end)
3541

36-
local assert_items = function(exp_list)
37-
assert.are.equal(#exp_list, #result)
38-
for idx, exp in ipairs(exp_list) do
39-
assert_item(idx, exp)
42+
describe("analyzer.find_call_t_expressions", function()
43+
--- @param get_project function(): test.Project
44+
--- @param file string
45+
--- @param assertion function(result: FindTExpressionResultItem[], utils: table)
46+
local function test_analyze_file(get_project, file, assertion)
47+
it("should find 't' function calls in " .. file, function()
48+
--Arrange
49+
local project = get_project()
50+
vim.cmd("e " .. project.path .. "/test_analyzer/" .. file)
51+
52+
-- Act
53+
local result = analyzer.find_call_t_expressions(0)
54+
55+
local assert_item = function(idx, exp)
56+
local item = result[idx]
57+
58+
local function assert_key_value(key)
59+
-- stylua: ignore start
60+
assert(
61+
exp[key] == item[key],
62+
string.format("result[%d].%s to be equal.\nPassed:\n(%s) %s\nExpected:\n(%s) %s",
63+
idx, key, type(item[key]), item[key], type(exp[key]), exp[key])
64+
)
65+
-- stylua: ignore end
66+
end
67+
assert_key_value("key")
68+
assert_key_value("key_prefix")
69+
assert_key_value("key_arg")
4070
end
41-
end
4271

43-
-- Assert
44-
assertion(result, {
45-
assert_item = assert_item,
46-
assert_items = assert_items,
47-
})
48-
end)
49-
end
50-
51-
--- @param get_project function(): test.Project
52-
--- @param text string
53-
--- @param expected boolean
54-
local function test_find_t_call(get_project, text, expected)
55-
local assertion = expected and "should" or "should NOT"
56-
it(assertion .. " find 't' function calls in `" .. text .. "`", function()
57-
-- Arrange
58-
local project = get_project()
59-
vim.cmd("e " .. project.path .. "/index.js")
60-
vim.api.nvim_buf_set_lines(0, 0, -1, false, vim.split(text, "\n"))
61-
62-
-- Act
63-
local result = analyzer.find_call_t_expressions(0)
64-
65-
-- Assert
66-
assert.are.equal(expected and 1 or 0, #result)
67-
end)
68-
end
72+
local assert_items = function(exp_list)
73+
assert.are.equal(#exp_list, #result)
74+
for idx, exp in ipairs(exp_list) do
75+
assert_item(idx, exp)
76+
end
77+
end
78+
79+
-- Assert
80+
assertion(result, {
81+
assert_item = assert_item,
82+
assert_items = assert_items,
83+
})
84+
end)
85+
end
86+
87+
--- @param get_project function(): test.Project
88+
--- @param text string
89+
--- @param expected boolean
90+
local function test_find_t_call(get_project, text, expected)
91+
local assertion = expected and "should" or "should NOT"
92+
it(assertion .. " find 't' function calls in `" .. text .. "`", function()
93+
-- Arrange
94+
local project = get_project()
95+
vim.cmd("e " .. project.path .. "/index.js")
96+
vim.api.nvim_buf_set_lines(0, 0, -1, false, vim.split(text, "\n"))
97+
98+
-- Act
99+
local result = analyzer.find_call_t_expressions(0)
100+
101+
-- Assert
102+
assert.are.equal(expected and 1 or 0, #result)
103+
end)
104+
end
69105

70-
describe("analyzer.find_call_t_expressions", function()
71106
describe("when using 'i18next'", function()
72107
--- @type test.Project
73108
local project = nil

0 commit comments

Comments
 (0)