mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-10-14 04:08:05 +00:00
fix(lua): preserve callable binding positions
This commit is contained in:
parent
bff8f59153
commit
151bb7865f
4 changed files with 70 additions and 23 deletions
|
|
@ -1,13 +1,15 @@
|
|||
/**
|
||||
* Optional grammar availability check.
|
||||
*
|
||||
* tree-sitter-dart, -proto, -swift, -kotlin, -zig, and -lua are vendored under
|
||||
* tree-sitter-dart, -proto, -swift, -kotlin, and -zig are vendored under
|
||||
* vendor/ and loaded from there by absolute path (NEVER copied into
|
||||
* node_modules — see core/tree-sitter/vendored-grammars.ts / #2111). Each
|
||||
* ships committed platform prebuilds activated via node-gyp-build. All can
|
||||
* be skipped via GITNEXUS_SKIP_OPTIONAL_GRAMMARS=1 (postinstall scripts), or
|
||||
* can silently soft-fail when no prebuild matches the host platform (and a
|
||||
* source build was unavailable / not attempted).
|
||||
* ships committed platform prebuilds activated via node-gyp-build.
|
||||
* tree-sitter-lua is also vendored, but is source-built during install because
|
||||
* it does not currently ship committed prebuilds. All can be skipped via
|
||||
* GITNEXUS_SKIP_OPTIONAL_GRAMMARS=1 (postinstall scripts), or can silently
|
||||
* soft-fail when no prebuild matches the host platform (and a source build was
|
||||
* unavailable / not attempted).
|
||||
*
|
||||
* Either path produces the same observable: the .node binding is absent
|
||||
* at runtime. This helper detects that condition and surfaces a single
|
||||
|
|
|
|||
|
|
@ -166,6 +166,56 @@ function collectLuaImportCaptures(root: Parser.SyntaxNode): readonly CaptureMatc
|
|||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Capture callable local bindings by source/destination position.
|
||||
*
|
||||
* A tree-sitter query that independently matches `variable_list` and a
|
||||
* function-valued `expression_list` cross-pairs Lua multi-assignment:
|
||||
* `local value, callback = 1, function() end` must name only `callback` as a
|
||||
* closure. Keep the positional pairing in the same structural walk used by
|
||||
* import capture collection.
|
||||
*/
|
||||
function collectLuaCallableBindingCaptures(root: Parser.SyntaxNode): readonly CaptureMatch[] {
|
||||
const out: CaptureMatch[] = [];
|
||||
const visit = (node: Parser.SyntaxNode): void => {
|
||||
if (node.type === 'local_variable_declaration') {
|
||||
const variables =
|
||||
node.namedChildren.find((child) => child.type === 'variable_list')?.namedChildren ?? [];
|
||||
const sources =
|
||||
node.namedChildren.find((child) => child.type === 'expression_list')?.namedChildren ?? [];
|
||||
const count = Math.min(variables.length, sources.length);
|
||||
for (let index = 0; index < count; index++) {
|
||||
const destination = variables[index];
|
||||
const source = sources[index];
|
||||
const name = destination?.childForFieldName('name');
|
||||
if (
|
||||
destination?.type !== 'variable' ||
|
||||
destination.childForFieldName('table') !== null ||
|
||||
destination.childForFieldName('field') !== null ||
|
||||
name?.type !== 'identifier' ||
|
||||
source === undefined
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
const declarationKind =
|
||||
source.type === 'function_definition'
|
||||
? '@declaration.function'
|
||||
: source.type === 'variable'
|
||||
? '@declaration.variable'
|
||||
: undefined;
|
||||
if (declarationKind === undefined) continue;
|
||||
out.push({
|
||||
[declarationKind]: nodeToCapture(declarationKind, node),
|
||||
'@declaration.name': nodeToCapture('@declaration.name', name),
|
||||
});
|
||||
}
|
||||
}
|
||||
for (const child of node.namedChildren) visit(child);
|
||||
};
|
||||
visit(root);
|
||||
return out;
|
||||
}
|
||||
|
||||
function collectLuaReturnedNames(root: Parser.SyntaxNode): readonly string[] {
|
||||
const returnedNames: string[] = [];
|
||||
for (const node of root.namedChildren) {
|
||||
|
|
@ -367,6 +417,8 @@ export function emitLuaScopeCaptures(
|
|||
out.push(grouped);
|
||||
}
|
||||
|
||||
out.push(...collectLuaCallableBindingCaptures(tree.rootNode));
|
||||
|
||||
// Imports are collected structurally instead of through the generic query:
|
||||
// the AST lists preserve positional local/RHS pairing and support all Lua
|
||||
// string-call forms without producing a cross-product of captures.
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@
|
|||
*
|
||||
* Captures the structural skeleton the central ScopeExtractor consumes:
|
||||
* - scopes: (chunk) root + function bodies
|
||||
* - declarations: functions, local callable/value bindings, and methods
|
||||
* (function Obj:m())
|
||||
* - declarations: functions and methods (function Obj:m())
|
||||
* (local callable/value bindings are captured structurally in captures.ts)
|
||||
* - imports: Lua module-loading calls — the call + its string arg
|
||||
* - references: free calls foo() + member calls obj:m() / obj.f()
|
||||
*
|
||||
|
|
@ -38,20 +38,6 @@ const LUA_SCOPE_QUERY = `
|
|||
(local_function_definition_statement
|
||||
name: (identifier) @declaration.name) @declaration.function
|
||||
|
||||
;; Local callable bindings need a SymbolDefinition before the shared
|
||||
;; callable-flow pass can attach alias/formal/argument facts to their cells.
|
||||
;; Keep ordinary variable aliases as Variable nodes, while closure bindings
|
||||
;; below are Functions so the anonymous-callable convention can resolve them.
|
||||
(local_variable_declaration
|
||||
(variable_list
|
||||
(variable name: (identifier) @declaration.name))) @declaration.variable
|
||||
|
||||
(local_variable_declaration
|
||||
(variable_list
|
||||
(variable name: (identifier) @declaration.name))
|
||||
(expression_list
|
||||
value: (function_definition))) @declaration.function
|
||||
|
||||
;; ── Declarations — methods: function Obj:method() / function Obj.field() ────
|
||||
;; name is a (variable) with table + method (colon) or table + field (dot).
|
||||
(function_definition_statement
|
||||
|
|
|
|||
|
|
@ -202,7 +202,7 @@ describe('Lua local callable declarations', () => {
|
|||
`function target() end
|
||||
function entry()
|
||||
local alias = target
|
||||
local callback = function() end
|
||||
local value, callback = 1, function() end
|
||||
callback()
|
||||
end
|
||||
`,
|
||||
|
|
@ -218,10 +218,17 @@ end
|
|||
expect(
|
||||
captures.some(
|
||||
(match) =>
|
||||
match['@declaration.function']?.text === 'local callback = function() end' &&
|
||||
match['@declaration.function']?.text === 'local value, callback = 1, function() end' &&
|
||||
match['@declaration.name']?.text === 'callback',
|
||||
),
|
||||
).toBe(true);
|
||||
expect(
|
||||
captures.some(
|
||||
(match) =>
|
||||
match['@declaration.function']?.text === 'local value, callback = 1, function() end' &&
|
||||
match['@declaration.name']?.text === 'value',
|
||||
),
|
||||
).toBe(false);
|
||||
expect(
|
||||
captures.some(
|
||||
(match) =>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue