@@ -164,6 +164,7 @@ function M.get_key_at_cursor(bufnr, position)
164
164
end
165
165
166
166
--- @class GetTDetail
167
+ --- @field t_func_name string
167
168
--- @field namespace string
168
169
--- @field key_prefix string
169
170
--- @field scope_node TSNode | nil
@@ -174,13 +175,16 @@ end
174
175
--- @param query vim.treesitter.Query クエリ
175
176
--- @return GetTDetail | nil
176
177
local function parse_get_t (target_node , source , query )
178
+ local t_func_name = nil
177
179
local namespace = " "
178
180
local key_prefix = " "
179
181
180
182
for id , node , _ in query :iter_captures (target_node , source , 0 , - 1 ) do
181
183
local name = query .captures [id ]
182
184
183
- if name == " i18n.namespace" then
185
+ if name == " i18n.t_func_name" then
186
+ t_func_name = t_func_name or vim .treesitter .get_node_text (node , source )
187
+ elseif name == " i18n.namespace" then
184
188
namespace = vim .treesitter .get_node_text (node , source )
185
189
elseif name == " i18n.key_prefix" then
186
190
key_prefix = vim .treesitter .get_node_text (node , source )
@@ -190,13 +194,15 @@ local function parse_get_t(target_node, source, query)
190
194
local scope_node = M .find_closest_node (target_node , { " statement_block" , " jsx_element" })
191
195
192
196
return {
197
+ t_func_name = t_func_name ,
193
198
namespace = namespace ,
194
199
key_prefix = key_prefix ,
195
200
scope_node = scope_node ,
196
201
}
197
202
end
198
203
199
204
--- @class CallTDetail
205
+ --- @field t_func_name string
200
206
--- @field key string
201
207
--- @field key_node TSNode
202
208
--- @field key_arg_node TSNode
209
215
--- @param query vim.treesitter.Query クエリ
210
216
--- @return CallTDetail | nil
211
217
local function parse_call_t (target_node , source , query )
218
+ local t_func_name = nil
212
219
local key = nil
213
220
local key_node = nil
214
221
local key_arg_node = nil
@@ -220,7 +227,9 @@ local function parse_call_t(target_node, source, query)
220
227
221
228
-- t関数の呼び出しがネストしている場合があるため、最初に見つかったものを採用する
222
229
-- そのため key = ke or ... のような形にしている
223
- if name == " i18n.key" then
230
+ if name == " i18n.t_func_name" then
231
+ t_func_name = t_func_name or vim .treesitter .get_node_text (node , source )
232
+ elseif name == " i18n.key" then
224
233
key = key or vim .treesitter .get_node_text (node , source )
225
234
key_node = key_node or node
226
235
elseif name == " i18n.key_arg" then
@@ -237,6 +246,7 @@ local function parse_call_t(target_node, source, query)
237
246
end
238
247
239
248
return {
249
+ t_func_name = t_func_name ,
240
250
key = key ,
241
251
key_node = key_node ,
242
252
key_arg_node = key_arg_node ,
@@ -308,56 +318,103 @@ function M.find_call_t_expressions(source, lib, lang)
308
318
309
319
local query = vim .treesitter .query .parse (language , query_str )
310
320
311
- --- @type GetTDetail[]
321
+ --- @type table<string , GetTDetail[]>
312
322
local scope_stack = {}
313
323
324
+ local function preprocess_t_func_name_for_scope (t_func_name )
325
+ if lib == utils .Library .I18Next then
326
+ return t_func_name
327
+ elseif lib == utils .Library .NextIntl then
328
+ return vim .split (t_func_name , " ." , { plain = true })[1 ]
329
+ end
330
+
331
+ return t_func_name
332
+ end
333
+
314
334
--- @param value GetTDetail
315
335
local function enter_scope (value )
316
- table.insert (scope_stack , value )
336
+ local t_func_name = value .t_func_name or " t"
337
+ scope_stack [t_func_name ] = scope_stack [t_func_name ] or {}
338
+ table.insert (scope_stack [t_func_name ], value )
317
339
end
318
340
319
- local function leave_scope ()
320
- table.remove (scope_stack )
341
+ --- @param t_func_name string
342
+ local function leave_scope (t_func_name )
343
+ table.remove (scope_stack [t_func_name or " t" ])
321
344
end
322
345
323
- local function current_scope ()
324
- return scope_stack [# scope_stack ]
346
+ --- @param t_func_name string
347
+ local function current_scope (t_func_name )
348
+ local t_func_name = preprocess_t_func_name_for_scope (t_func_name )
349
+ local stack = scope_stack [t_func_name or " t" ] or {}
350
+ return stack [# stack ]
325
351
or {
326
352
namespace = " " ,
327
353
key_prefix = " " ,
328
354
scope_node = root_node ,
329
355
}
330
356
end
331
357
358
+ local function is_t_func (t_func_name )
359
+ if t_func_name == " t" or scope_stack [t_func_name ] ~= nil then
360
+ return true
361
+ end
362
+
363
+ if lib == utils .Library .I18Next then
364
+ if t_func_name == " i18next.t" then
365
+ return true
366
+ end
367
+ elseif lib == utils .Library .NextIntl then
368
+ -- {t_func_name}.rich や {t_func_name}.markup などの形式も考慮する
369
+ local split = vim .split (t_func_name , " ." , { plain = true })
370
+ local name = split [1 ]
371
+ local member = split [2 ]
372
+
373
+ local allow_members = {
374
+ [" rich" ] = true ,
375
+ [" markup" ] = true ,
376
+ [" raw" ] = true ,
377
+ }
378
+ if (name == " t" or scope_stack [name ] ~= nil ) and allow_members [member ] then
379
+ return true
380
+ end
381
+ end
382
+
383
+ return false
384
+ end
385
+
332
386
--- @type FindTExpressionResultItem[]
333
387
local result = {}
334
388
335
389
for id , node , _ in query :iter_captures (root_node , source ) do
336
390
local name = query .captures [id ]
337
391
338
392
-- 現在のスコープから抜けたかどうかを判定する
339
- local current_scope_node = current_scope ().scope_node or root_node
340
- if node :start () > current_scope_node :end_ () or node :end_ () < current_scope_node :start () then
341
- leave_scope ()
393
+ for t_func_name in pairs (scope_stack ) do
394
+ local current_scope_node = current_scope (t_func_name ).scope_node or root_node
395
+ if node :start () > current_scope_node :end_ () or node :end_ () < current_scope_node :start () then
396
+ leave_scope (t_func_name )
397
+ end
342
398
end
343
399
344
400
if name == " i18n.get_t" then
345
401
local get_t_detail = parse_get_t (node , source , query )
346
402
if get_t_detail then
347
403
-- 同一のスコープ内で get_t が呼ばれた場合はスコープを上書きする形になるように、一度 leave_scope してから enter_scope する
348
- if get_t_detail .scope_node == current_scope ().scope_node then
349
- leave_scope ()
404
+ if get_t_detail .scope_node == current_scope (get_t_detail . t_func_name ).scope_node then
405
+ leave_scope (get_t_detail . t_func_name )
350
406
end
351
407
enter_scope (get_t_detail )
352
408
end
353
409
elseif name == " i18n.call_t" then
354
- local scope = current_scope ()
355
410
local call_t_detail = parse_call_t (node , source , query )
356
411
357
- if call_t_detail == nil then
412
+ if call_t_detail == nil or not is_t_func ( call_t_detail . t_func_name ) then
358
413
goto continue
359
414
end
360
415
416
+ local scope = current_scope (call_t_detail .t_func_name )
417
+
361
418
local key_prefix = call_t_detail .key_prefix or scope .key_prefix
362
419
local key = call_t_detail .key
363
420
if key_prefix ~= " " then
0 commit comments