forked from colbymchenry/codegraph
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdart.ts
More file actions
195 lines (183 loc) · 7.9 KB
/
Copy pathdart.ts
File metadata and controls
195 lines (183 loc) · 7.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
import type { Node as SyntaxNode } from 'web-tree-sitter';
import { getNodeText } from '../tree-sitter-helpers';
import type { LanguageExtractor } from '../tree-sitter-types';
export const dartExtractor: LanguageExtractor = {
functionTypes: ['function_signature'],
classTypes: ['class_definition'],
methodTypes: ['method_signature'],
interfaceTypes: [],
structTypes: [],
enumTypes: ['enum_declaration'],
enumMemberTypes: ['enum_constant'],
typeAliasTypes: ['type_alias'],
importTypes: ['import_or_export'],
callTypes: [], // Dart calls use identifier+selector, handled via extractBareCall
variableTypes: [],
extraClassNodeTypes: ['mixin_declaration', 'extension_declaration'],
resolveBody: (node, bodyField) => {
// Dart: function_body is a next sibling of function_signature/method_signature
if (node.type === 'function_signature' || node.type === 'method_signature') {
const next = node.nextNamedSibling;
if (next?.type === 'function_body') return next;
return null;
}
// For class/mixin/extension: try standard field, then class_body/extension_body
const standard = node.childForFieldName(bodyField);
if (standard) return standard;
return node.namedChildren.find((c: SyntaxNode) =>
c.type === 'class_body' || c.type === 'extension_body'
) || null;
},
nameField: 'name',
bodyField: 'body', // class_definition uses 'body' field
paramsField: 'formal_parameter_list',
returnField: 'type',
getSignature: (node, source) => {
// For function_signature: extract params + return type
// For method_signature: delegate to inner function_signature
let sig = node;
if (node.type === 'method_signature') {
const inner = node.namedChildren.find((c: SyntaxNode) =>
c.type === 'function_signature' || c.type === 'getter_signature' || c.type === 'setter_signature'
);
if (inner) sig = inner;
}
const params = sig.namedChildren.find((c: SyntaxNode) => c.type === 'formal_parameter_list');
const retType = sig.namedChildren.find((c: SyntaxNode) =>
c.type === 'type_identifier' || c.type === 'void_type'
);
if (!params && !retType) return undefined;
let result = '';
if (retType) result += getNodeText(retType, source) + ' ';
if (params) result += getNodeText(params, source);
return result.trim() || undefined;
},
getVisibility: (node) => {
// Dart convention: _ prefix means private, otherwise public
let nameNode: SyntaxNode | null = null;
if (node.type === 'method_signature') {
const inner = node.namedChildren.find((c: SyntaxNode) =>
c.type === 'function_signature' || c.type === 'getter_signature' || c.type === 'setter_signature'
);
if (inner) nameNode = inner.namedChildren.find((c: SyntaxNode) => c.type === 'identifier') || null;
} else {
nameNode = node.childForFieldName('name');
}
if (nameNode && nameNode.text.startsWith('_')) return 'private';
return 'public';
},
isAsync: (node) => {
// In Dart, 'async' is on the function_body (next sibling), not the signature
const nextSibling = node.nextNamedSibling;
if (nextSibling?.type === 'function_body') {
for (let i = 0; i < nextSibling.childCount; i++) {
const child = nextSibling.child(i);
if (child?.type === 'async') return true;
}
}
return false;
},
isStatic: (node) => {
// For method_signature, check for 'static' child
if (node.type === 'method_signature') {
for (let i = 0; i < node.childCount; i++) {
const child = node.child(i);
if (child?.type === 'static') return true;
}
}
return false;
},
extractImport: (node, source) => {
const importText = source.substring(node.startIndex, node.endIndex).trim();
let moduleName = '';
// Dart imports: import 'dart:async'; import 'package:foo/bar.dart' as bar;
const libraryImport = node.namedChildren.find((c: SyntaxNode) => c.type === 'library_import');
if (libraryImport) {
const importSpec = libraryImport.namedChildren.find((c: SyntaxNode) => c.type === 'import_specification');
if (importSpec) {
const configurableUri = importSpec.namedChildren.find((c: SyntaxNode) => c.type === 'configurable_uri');
if (configurableUri) {
const uri = configurableUri.namedChildren.find((c: SyntaxNode) => c.type === 'uri');
if (uri) {
const stringLiteral = uri.namedChildren.find((c: SyntaxNode) => c.type === 'string_literal');
if (stringLiteral) {
moduleName = getNodeText(stringLiteral, source).replace(/['"]/g, '');
}
}
}
}
}
// Also handle exports: export 'src/foo.dart';
if (!moduleName) {
const libraryExport = node.namedChildren.find((c: SyntaxNode) => c.type === 'library_export');
if (libraryExport) {
const configurableUri = libraryExport.namedChildren.find((c: SyntaxNode) => c.type === 'configurable_uri');
if (configurableUri) {
const uri = configurableUri.namedChildren.find((c: SyntaxNode) => c.type === 'uri');
if (uri) {
const stringLiteral = uri.namedChildren.find((c: SyntaxNode) => c.type === 'string_literal');
if (stringLiteral) {
moduleName = getNodeText(stringLiteral, source).replace(/['"]/g, '');
}
}
}
}
}
if (moduleName) {
return { moduleName, signature: importText };
}
return null;
},
extractBareCall: (node, _source) => {
// Dart calls are: identifier + selector(argument_part), not a dedicated call node.
// Match on selector nodes that contain argument_part.
if (node.type === 'selector') {
const hasArgPart = node.namedChildren.some((c: SyntaxNode) => c.type === 'argument_part');
if (!hasArgPart) return undefined;
const prev = node.previousNamedSibling;
if (!prev) return undefined;
// Simple function/constructor call: prev is identifier (e.g., runApp(...), MyWidget(...))
if (prev.type === 'identifier') {
return prev.text;
}
// Method call: prev is selector with accessor (e.g., obj.method(...), Navigator.push(...))
if (prev.type === 'selector') {
const accessor = prev.namedChildren.find((c: SyntaxNode) =>
c.type === 'unconditional_assignable_selector' || c.type === 'conditional_assignable_selector'
);
if (accessor) {
const methodId = accessor.namedChildren.find((c: SyntaxNode) => c.type === 'identifier');
if (methodId) {
// Include receiver for first call in chain (receiver is a direct identifier)
const accessorPrev = prev.previousNamedSibling;
if (accessorPrev?.type === 'identifier') {
return accessorPrev.text + '.' + methodId.text;
}
return methodId.text;
}
}
}
// super.method() / this.method(): prev is bare unconditional_assignable_selector
if (prev.type === 'unconditional_assignable_selector' || prev.type === 'conditional_assignable_selector') {
const methodId = prev.namedChildren.find((c: SyntaxNode) => c.type === 'identifier');
if (methodId) return methodId.text;
}
return undefined;
}
// new MyWidget() — explicit constructor call
if (node.type === 'new_expression') {
const typeId = node.namedChildren.find((c: SyntaxNode) => c.type === 'type_identifier');
if (typeId) return typeId.text;
return undefined;
}
// const EdgeInsets.all(8.0) — const constructor call
if (node.type === 'const_object_expression') {
const typeId = node.namedChildren.find((c: SyntaxNode) => c.type === 'type_identifier');
const nameId = node.namedChildren.find((c: SyntaxNode) => c.type === 'identifier');
if (typeId && nameId) return typeId.text + '.' + nameId.text;
if (typeId) return typeId.text;
return undefined;
}
return undefined;
},
};