@rahmantech/knl-view 0.0.0-stage → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +483 -2
  3. package/bin/knl-view.js +1320 -0
  4. package/dist/assets/abnfDiagram-WEYOIA3C-hoSbsMOz.js +1 -0
  5. package/dist/assets/arc-g87Np6E5.js +1 -0
  6. package/dist/assets/architecture-WOLXFQ4H-DOIq-0IA.js +1 -0
  7. package/dist/assets/architectureDiagram-ZHVYKT53-Dxwe8n7m.js +36 -0
  8. package/dist/assets/array-BifhSqXX.js +1 -0
  9. package/dist/assets/blockDiagram-RJAE7QA3-BnGng59Z.js +140 -0
  10. package/dist/assets/c4Diagram-XDPPPE54-C5LjqIIo.js +38 -0
  11. package/dist/assets/channel-BVWkRFBw.js +1 -0
  12. package/dist/assets/chunk-2E7WHVMD-BJ1dyGLq.js +88 -0
  13. package/dist/assets/chunk-2Q5K7J3B-C1jixKkw.js +1 -0
  14. package/dist/assets/chunk-4EMNKR2T-DXF02Sgw.js +1 -0
  15. package/dist/assets/chunk-55XCU76K-CM1akAbu.js +1 -0
  16. package/dist/assets/chunk-5VM5RSS4-ZNzvKenW.js +15 -0
  17. package/dist/assets/chunk-7S6XSTFO-BOUjqldL.js +2 -0
  18. package/dist/assets/chunk-AQDTSKZB-C4isdNLA.js +72 -0
  19. package/dist/assets/chunk-DSNLSHJZ-CbOTkSVM.js +129 -0
  20. package/dist/assets/chunk-DUW6YSOI-Dgx8z5s3.js +1 -0
  21. package/dist/assets/chunk-HAGFOTED-CRfnketR.js +62 -0
  22. package/dist/assets/chunk-HNE4Q36P-rxlpGUU8.js +1 -0
  23. package/dist/assets/chunk-J5ZVWO5B-oDCd-gWO.js +1 -0
  24. package/dist/assets/chunk-JWPE2WC7-DVXcaiue.js +1 -0
  25. package/dist/assets/chunk-NGNAAXSQ-C2Wy9Xmz.js +136 -0
  26. package/dist/assets/chunk-NQOKRVIF-BOYCgTmV.js +213 -0
  27. package/dist/assets/chunk-UHW7PFBP-Cnogp1Fb.js +1 -0
  28. package/dist/assets/chunk-VQ5W62QI-Cu1WY5Uo.js +1 -0
  29. package/dist/assets/chunk-WQQL4CDK-BJc3FPE2.js +10 -0
  30. package/dist/assets/chunk-XXDRQBXY-BlPj4H7s.js +1 -0
  31. package/dist/assets/chunk-Y2CYZVJY-DsF7k-Jl.js +1 -0
  32. package/dist/assets/chunk-YZO67HFG-V85H31x4.js +1 -0
  33. package/dist/assets/chunk-ZAUX2LTX-C3XKXW09.js +1 -0
  34. package/dist/assets/chunk-ZCHMQSOR-B0mmpEgW.js +2 -0
  35. package/dist/assets/classDiagram-v2-4MYWR3UA-oMd7YaHP.js +217 -0
  36. package/dist/assets/cose-bilkent-JH36ORCC-J0kSVsDf.js +1 -0
  37. package/dist/assets/cynefin-EF2NZ3EQ-DK2TNhel.js +1 -0
  38. package/dist/assets/cynefinDiagram-WF24UDRP-DDvKgieA.js +62 -0
  39. package/dist/assets/cytoscape.esm-BegBNzOV.js +321 -0
  40. package/dist/assets/dagre-NK4GIZVX-Ca2Fe-i-.js +4 -0
  41. package/dist/assets/defaultLocale-BFoDCU3G.js +1 -0
  42. package/dist/assets/diagram-CBK3MWTC-L6dmeQ2V.js +24 -0
  43. package/dist/assets/diagram-I5XIIV2D-DhG8R-v5.js +24 -0
  44. package/dist/assets/diagram-ODIL2UCW-BpYYbYBT.js +30 -0
  45. package/dist/assets/diagram-PFBEJUXE-DKQpHSsC.js +3 -0
  46. package/dist/assets/diagram-RJA3I4VV-DP7Dsnya.js +41 -0
  47. package/dist/assets/diagram-TYUNFSNR-CfSFewUS.js +200 -0
  48. package/dist/assets/dist-CQNjnrld.js +1 -0
  49. package/dist/assets/ebnfDiagram-V4P3TTPT-DE3MZoJO.js +1 -0
  50. package/dist/assets/elk-QONTBCTQ-p_aAQPHU.js +27 -0
  51. package/dist/assets/erDiagram-KYQIGQAA-DZO3vQrk.js +99 -0
  52. package/dist/assets/eventmodeling-K75KTNOO-BII9EPl5.js +1 -0
  53. package/dist/assets/flowDiagram-IUGLLWBU-D9uPno39.js +1 -0
  54. package/dist/assets/ganttDiagram-R7GPNTJY-BKiH3v2r.js +292 -0
  55. package/dist/assets/gitGraph-VSP46ZUC-oHL6cr73.js +1 -0
  56. package/dist/assets/gitGraphDiagram-PDYL7XHY-CqPVIEeo.js +106 -0
  57. package/dist/assets/graphlib-DS17s2tU.js +1 -0
  58. package/dist/assets/index-CSpirV16.js +76 -0
  59. package/dist/assets/index-DLgMsUs2.css +1 -0
  60. package/dist/assets/info-OHQRW6UA-Drrbnhpd.js +1 -0
  61. package/dist/assets/infoDiagram-MVDRKAX5-DnQQFMAd.js +2 -0
  62. package/dist/assets/init-C-OQMol4.js +1 -0
  63. package/dist/assets/ishikawaDiagram-HPEHV2GE-DIpQlxRh.js +70 -0
  64. package/dist/assets/journeyDiagram-BOSHW7O7-EBR9I5h1.js +139 -0
  65. package/dist/assets/kanban-definition-FNSA4HC5-CdvNsjik.js +89 -0
  66. package/dist/assets/katex-B_JcIwqj.js +257 -0
  67. package/dist/assets/line-CDLzgxDc.js +1 -0
  68. package/dist/assets/linear-DfXKql9u.js +1 -0
  69. package/dist/assets/mermaid-parser.core-DnXGphT2.js +7 -0
  70. package/dist/assets/mermaid.core-h6fiCaSZ.js +44 -0
  71. package/dist/assets/mindmap-definition-6OY57UG3-BiNfio-j.js +98 -0
  72. package/dist/assets/ordinal-BDEzSJ7C.js +1 -0
  73. package/dist/assets/packet-JDAUHWVQ-D76byv1S.js +1 -0
  74. package/dist/assets/path-fybaL0A-.js +1 -0
  75. package/dist/assets/pegDiagram-ECIUJNBX-Drs8Pxdt.js +1 -0
  76. package/dist/assets/pie-XZMESJXO-D_83z1lM.js +1 -0
  77. package/dist/assets/pieDiagram-GNMPY74T-wvzIphSf.js +39 -0
  78. package/dist/assets/quadrantDiagram-D57K4MC3-B7EfMRWb.js +7 -0
  79. package/dist/assets/radar-ABXABTNO-Bs6dVdAR.js +1 -0
  80. package/dist/assets/railroad-I3PHUGI6-lKaN4miw.js +1 -0
  81. package/dist/assets/railroad-abnf-LNEFI6M7-Bhh16b7Z.js +1 -0
  82. package/dist/assets/railroad-ebnf-DWYD2UWJ-m6ybW-Y_.js +1 -0
  83. package/dist/assets/railroad-peg-JR7BVNR7-DNn72e7r.js +1 -0
  84. package/dist/assets/railroadDiagram-Y72NRBL7-DNeop1iZ.js +1 -0
  85. package/dist/assets/requirementDiagram-EQYSW5RA-BVYZ2XYJ.js +84 -0
  86. package/dist/assets/rough.esm-Dy-Kn_BL.js +1 -0
  87. package/dist/assets/sankeyDiagram-SY24JHO5-B2MP51on.js +40 -0
  88. package/dist/assets/sequenceDiagram-BLEHNHQU-BUvxstXG.js +169 -0
  89. package/dist/assets/sizeCapture-INFHLROL-3uHXrFqA.js +1 -0
  90. package/dist/assets/src-BrR0lqlG.js +1 -0
  91. package/dist/assets/stateDiagram-v2-A6IESKWZ-Ba84OGKE.js +291 -0
  92. package/dist/assets/swimlanes-B3ENRKFQ-BMSl2PNA.js +1 -0
  93. package/dist/assets/swimlanesDiagram-AYQRV4AK-M1dmvq7m.js +8 -0
  94. package/dist/assets/timeline-definition-JXYKGLGB-DOdW-dxq.js +120 -0
  95. package/dist/assets/treeView-D4JQ5SDB-DZ9KQyn5.js +1 -0
  96. package/dist/assets/treemap-SAJKECNS-WEC5wtDV.js +1 -0
  97. package/dist/assets/usecaseDiagram-WPOLBNNX-cldTztFL.js +328 -0
  98. package/dist/assets/vennDiagram-Q3Y3IDCW-c41KySoY.js +34 -0
  99. package/dist/assets/wardley-7MLQ67FV-Bf9_aSSZ.js +1 -0
  100. package/dist/assets/wardleyDiagram-IVAIDKWA-BeKEvEBG.js +78 -0
  101. package/dist/assets/xychartDiagram-4URNHDU4-tNkAxTxG.js +7 -0
  102. package/dist/index.html +22 -0
  103. package/package.json +50 -4
  104. package/server/config.js +389 -0
  105. package/server/consensus.js +180 -0
  106. package/server/graph/anchordb_client.js +253 -0
  107. package/server/graph/model.js +157 -0
  108. package/server/graph/parser.js +327 -0
  109. package/server/graph/sources.js +801 -0
  110. package/server/graph/syntax.js +437 -0
  111. package/server/index.js +742 -0
  112. package/server/install.js +437 -0
  113. package/server/map.js +126 -0
  114. package/server/models_registry.js +109 -0
  115. package/server/scaffold.js +466 -0
@@ -0,0 +1,437 @@
1
+ import { createRequire } from 'node:module'
2
+ import { Parser, Language } from 'web-tree-sitter'
3
+
4
+ // Syntax-aware import extraction with tree-sitter (web-tree-sitter, WASM).
5
+ // One uniform parser API for every supported language: adding a language
6
+ // means adding one grammar package and one extraction case. Grammars are
7
+ // lazy-loaded only when the scanned project actually contains files of
8
+ // that language.
9
+ //
10
+ // Extraction returns the raw import specifiers of a file; deciding which
11
+ // of them point inside the project is the resolver's job (sources.js), so
12
+ // this module stays language-shaped, not project-shaped.
13
+
14
+ const require = createRequire(import.meta.url)
15
+
16
+ const GRAMMAR_WASM = {
17
+ javascript: 'tree-sitter-javascript/tree-sitter-javascript.wasm',
18
+ typescript: 'tree-sitter-typescript/tree-sitter-typescript.wasm',
19
+ tsx: 'tree-sitter-typescript/tree-sitter-tsx.wasm',
20
+ go: 'tree-sitter-go/tree-sitter-go.wasm',
21
+ python: 'tree-sitter-python/tree-sitter-python.wasm',
22
+ rust: 'tree-sitter-rust/tree-sitter-rust.wasm'
23
+ }
24
+
25
+ /** The JS/TS, Go, Python, and Rust families; each extension maps to its grammar. */
26
+ export const SYNTAX_LANGUAGES = {
27
+ '.js': 'javascript',
28
+ '.mjs': 'javascript',
29
+ '.cjs': 'javascript',
30
+ '.jsx': 'javascript',
31
+ '.ts': 'typescript',
32
+ '.mts': 'typescript',
33
+ '.cts': 'typescript',
34
+ '.tsx': 'tsx',
35
+ '.go': 'go',
36
+ '.py': 'python',
37
+ '.rs': 'rust'
38
+ }
39
+
40
+ let initPromise = null
41
+ const languages = new Map() // grammar name -> Language
42
+ const parsers = new Map() // grammar name -> Parser
43
+
44
+ async function languageFor(ext) {
45
+ const name = SYNTAX_LANGUAGES[ext]
46
+ if (languages.has(name)) return languages.get(name)
47
+ initPromise ||= Parser.init()
48
+ await initPromise
49
+ const lang = await Language.load(require.resolve(GRAMMAR_WASM[name]))
50
+ languages.set(name, lang)
51
+ return lang
52
+ }
53
+
54
+ function parserFor(name, lang) {
55
+ if (!parsers.has(name)) {
56
+ const parser = new Parser()
57
+ parser.setLanguage(lang)
58
+ parsers.set(name, parser)
59
+ }
60
+ return parsers.get(name)
61
+ }
62
+
63
+ function firstDescendant(node, type) {
64
+ if (node.type === type) return node
65
+ for (let i = 0; i < node.childCount; i++) {
66
+ const found = firstDescendant(node.child(i), type)
67
+ if (found) return found
68
+ }
69
+ return null
70
+ }
71
+
72
+ /**
73
+ * Raw specifiers from one parsed tree. JS: ESM import/export sources,
74
+ * dynamic import(), and require(). Go: every import_spec path, alias or
75
+ * not. Bare names, URLs, and package paths are returned as-is; filtering
76
+ * to project-internal targets happens in the resolver.
77
+ */
78
+ function specsFromTree(grammar, tree) {
79
+ const specs = []
80
+ const names = []
81
+ const visit = (node) => {
82
+ if (grammar === 'javascript' || grammar === 'typescript' || grammar === 'tsx') {
83
+ if (node.type === 'import_statement' || node.type === 'export_statement') {
84
+ const source = node.childForFieldName('source')
85
+ const fragment = source && firstDescendant(source, 'string_fragment')
86
+ if (fragment) specs.push(fragment.text)
87
+ // Local binding names a qualified call may be addressed through:
88
+ // the default import, the namespace alias, and each named import.
89
+ for (let i = 0; i < node.childCount; i++) {
90
+ const clause = node.child(i)
91
+ if (clause.type !== 'import_clause') continue
92
+ for (let j = 0; j < clause.childCount; j++) {
93
+ const part = clause.child(j)
94
+ if (part.type === 'identifier') names.push(part.text)
95
+ else if (part.type === 'namespace_import') {
96
+ for (let k = 0; k < part.childCount; k++) {
97
+ if (part.child(k).type === 'identifier') names.push(part.child(k).text)
98
+ }
99
+ } else if (part.type === 'named_imports') {
100
+ for (let k = 0; k < part.childCount; k++) {
101
+ const specifier = part.child(k)
102
+ if (specifier.type !== 'import_specifier') continue
103
+ const alias = specifier.childForFieldName('alias') || specifier.childForFieldName('name')
104
+ if (alias) names.push(alias.text)
105
+ }
106
+ }
107
+ }
108
+ }
109
+ } else if (node.type === 'call_expression') {
110
+ const fn = node.childForFieldName('function')
111
+ if (fn && (fn.text === 'require' || fn.text === 'import')) {
112
+ const args = node.childForFieldName('arguments')
113
+ const fragment = args && firstDescendant(args, 'string_fragment')
114
+ if (fragment) specs.push(fragment.text)
115
+ // `const x = require('y')` / `const x = await import('y')`: the
116
+ // declarator name is the binding: the visit starts above it, so
117
+ // walk down from any enclosing declarator via field lookup here.
118
+ let parent = node.parent
119
+ while (parent && parent.type === 'await_expression') parent = parent.parent
120
+ if (parent && parent.type === 'variable_declarator') {
121
+ const name = parent.childForFieldName('name')
122
+ if (name) names.push(name.text)
123
+ }
124
+ }
125
+ }
126
+ } else if (grammar === 'go') {
127
+ if (node.type === 'import_spec') {
128
+ const declared = node.childForFieldName('path')
129
+ const fragment = declared && firstDescendant(declared, 'interpreted_string_literal_content')
130
+ if (fragment) {
131
+ const path = fragment.text
132
+ specs.push(path)
133
+ // The name a qualified call uses: the declared alias, or the last
134
+ // path segment (Go's convention when no alias is given).
135
+ const alias = node.childForFieldName('name')
136
+ if (alias) names.push(alias.text)
137
+ else names.push(path.slice(path.lastIndexOf('/') + 1))
138
+ }
139
+ }
140
+ } else if (grammar === 'python') {
141
+ if (node.type === 'import_statement') {
142
+ for (let i = 0; i < node.childCount; i++) {
143
+ const c = node.child(i)
144
+ if (c.type === 'dotted_name') {
145
+ specs.push(c.text)
146
+ const firstId = c.child(0)
147
+ if (firstId) names.push(firstId.text)
148
+ } else if (c.type === 'aliased_import') {
149
+ const name = c.childForFieldName('name')
150
+ const alias = c.childForFieldName('alias')
151
+ if (name) specs.push(name.text)
152
+ if (alias) names.push(alias.text)
153
+ }
154
+ }
155
+ } else if (node.type === 'import_from_statement') {
156
+ const mod = node.childForFieldName('module_name')
157
+ const modText = mod ? mod.text : ''
158
+ if (modText) specs.push(modText)
159
+ let pastImport = false
160
+ for (let i = 0; i < node.childCount; i++) {
161
+ const c = node.child(i)
162
+ if (c.type === 'import') {
163
+ pastImport = true
164
+ continue
165
+ }
166
+ if (!pastImport) continue
167
+ if (c.type === 'dotted_name') {
168
+ names.push(c.text)
169
+ if (modText) specs.push(modText + '.' + c.text)
170
+ } else if (c.type === 'aliased_import') {
171
+ const name = c.childForFieldName('name')
172
+ const alias = c.childForFieldName('alias')
173
+ if (alias) names.push(alias.text)
174
+ if (name && modText) specs.push(modText + '.' + name.text)
175
+ }
176
+ }
177
+ }
178
+ } else if (grammar === 'rust') {
179
+ if (node.type === 'mod_item') {
180
+ if (!node.childForFieldName('body')) {
181
+ const name = node.childForFieldName('name')
182
+ if (name) {
183
+ specs.push('./' + name.text)
184
+ names.push(name.text)
185
+ }
186
+ }
187
+ } else if (node.type === 'use_declaration') {
188
+ const extractRustUse = (n, prefix = '') => {
189
+ if (!n) return
190
+ if (n.type === 'scoped_use_list') {
191
+ const pathNode = n.childForFieldName('path')
192
+ const listNode = n.childForFieldName('list')
193
+ const p = (prefix ? prefix + '::' : '') + (pathNode ? pathNode.text : '')
194
+ extractRustUse(listNode, p)
195
+ } else if (n.type === 'use_list') {
196
+ for (let i = 0; i < n.childCount; i++) {
197
+ const c = n.child(i)
198
+ if (c.type === 'identifier' || c.type === 'scoped_identifier' || c.type === 'use_as_clause' || c.type === 'scoped_use_list') {
199
+ extractRustUse(c, prefix)
200
+ }
201
+ }
202
+ } else if (n.type === 'use_as_clause') {
203
+ const pathNode = n.childForFieldName('path')
204
+ const aliasNode = n.childForFieldName('alias')
205
+ const full = (prefix ? prefix + '::' : '') + (pathNode ? pathNode.text : '')
206
+ specs.push(full)
207
+ if (aliasNode) names.push(aliasNode.text)
208
+ } else if (n.type === 'scoped_identifier') {
209
+ const full = (prefix ? prefix + '::' : '') + n.text
210
+ specs.push(full)
211
+ const name = n.childForFieldName('name')
212
+ if (name) names.push(name.text)
213
+ } else if (n.type === 'identifier') {
214
+ const full = (prefix ? prefix + '::' : '') + n.text
215
+ specs.push(full)
216
+ names.push(n.text)
217
+ }
218
+ }
219
+ extractRustUse(node.childForFieldName('argument'))
220
+ }
221
+ }
222
+ for (let i = 0; i < node.childCount; i++) visit(node.child(i))
223
+ }
224
+ visit(tree.rootNode)
225
+ return { specs, names }
226
+ }
227
+
228
+ /**
229
+ * Parse one file's text with the grammar of its extension and return its
230
+ * raw import specifiers. A syntax error never throws: tree-sitter recovers
231
+ * and whatever imports are well-formed are still extracted.
232
+ * @param {string} ext lowercase extension with the dot, e.g. '.go'
233
+ * @param {string} content file text
234
+ * @returns {Promise<string[]>}
235
+ */
236
+ export async function extractImportSpecifiers(ext, content) {
237
+ return (await extractCode(ext, content)).imports
238
+ }
239
+
240
+ /**
241
+ * Parse one file once and extract everything the sources graph needs:
242
+ *
243
+ * imports raw import specifiers (see extractImportSpecifiers)
244
+ * importNames local names those imports are addressed by: JS bindings
245
+ * (default/namespace/named, plus `const x = require/import`),
246
+ * Go aliases or last path segments. Evidence that a receiver
247
+ * prefix in a qualified call refers to an import.
248
+ * symbols named definitions: { name, kind, line, lines }, line 1-based,
249
+ * lines = height of the definition
250
+ * calls call sites: { name, recv, row }, row 0-based; recv is the
251
+ * receiver/prefix text for qualified calls (recv.method(),
252
+ * pkg.Fn()) and null for plain identifiers
253
+ *
254
+ * Calls are attributed to nothing here; mapping a call to its enclosing
255
+ * symbol is the caller's job (it needs the full symbol list anyway).
256
+ * A syntax error never throws: tree-sitter recovers and whatever is
257
+ * well-formed is still extracted.
258
+ */
259
+ export async function extractCode(ext, content) {
260
+ const lang = await languageFor(ext)
261
+ const grammar = SYNTAX_LANGUAGES[ext]
262
+ const parser = parserFor(grammar, lang)
263
+ const tree = parser.parse(content)
264
+ try {
265
+ const { specs: imports, names: importNames } = specsFromTree(grammar, tree)
266
+ const { symbols, calls } = structureFromTree(grammar, tree)
267
+ return { imports, importNames, symbols, calls }
268
+ } finally {
269
+ tree.delete()
270
+ }
271
+ }
272
+
273
+ /**
274
+ * Named definitions and call sites from one parsed tree. Only definitions
275
+ * that carry an explicit name enter the graph; anonymous helpers are left
276
+ * out, deterministically.
277
+ */
278
+ function structureFromTree(grammar, tree) {
279
+ const symbols = []
280
+ const calls = []
281
+ const pushSymbol = (node, name, kind) => {
282
+ symbols.push({
283
+ name,
284
+ kind,
285
+ line: node.startPosition.row + 1,
286
+ lines: node.endPosition.row - node.startPosition.row + 1
287
+ })
288
+ }
289
+ const visit = (node, cls) => {
290
+ if (grammar === 'javascript' || grammar === 'typescript' || grammar === 'tsx') {
291
+ if (node.type === 'function_declaration' || node.type === 'generator_function_declaration') {
292
+ const name = node.childForFieldName('name')
293
+ if (name) pushSymbol(node, name.text, 'function')
294
+ } else if (node.type === 'class_declaration') {
295
+ const name = node.childForFieldName('name')
296
+ if (name) {
297
+ pushSymbol(node, name.text, 'class')
298
+ cls = name.text // class body below walks with this name in scope
299
+ }
300
+ } else if (node.type === 'method_definition') {
301
+ const name = node.childForFieldName('name')
302
+ if (name && name.type === 'property_identifier' && cls) {
303
+ pushSymbol(node, `${cls}.${name.text}`, 'method')
304
+ }
305
+ } else if (node.type === 'lexical_declaration' || node.type === 'variable_declaration') {
306
+ for (let i = 0; i < node.childCount; i++) {
307
+ const declarator = node.child(i)
308
+ if (declarator.type !== 'variable_declarator') continue
309
+ const value = declarator.childForFieldName('value')
310
+ const name = declarator.childForFieldName('name')
311
+ if (value && name && (value.type === 'arrow_function' || value.type === 'function_expression')) {
312
+ pushSymbol(declarator, name.text, 'function')
313
+ }
314
+ }
315
+ } else if (node.type === 'interface_declaration') {
316
+ const name = node.childForFieldName('name')
317
+ if (name) pushSymbol(node, name.text, 'interface')
318
+ } else if (node.type === 'type_alias_declaration') {
319
+ const name = node.childForFieldName('name')
320
+ if (name) pushSymbol(node, name.text, 'type')
321
+ } else if (node.type === 'enum_declaration') {
322
+ const name = node.childForFieldName('name')
323
+ if (name) pushSymbol(node, name.text, 'enum')
324
+ }
325
+ } else if (grammar === 'go') {
326
+ if (node.type === 'function_declaration') {
327
+ const name = node.childForFieldName('name')
328
+ if (name) pushSymbol(node, name.text, 'function')
329
+ } else if (node.type === 'method_declaration') {
330
+ const name = node.childForFieldName('name')
331
+ const receiver = node.childForFieldName('receiver')
332
+ const recvType = receiver && firstDescendant(receiver, 'type_identifier')
333
+ if (name && recvType) pushSymbol(node, `${recvType.text}.${name.text}`, 'method')
334
+ } else if (node.type === 'type_spec') {
335
+ const name = node.childForFieldName('name')
336
+ if (name) pushSymbol(node, name.text, 'type')
337
+ }
338
+ } else if (grammar === 'python') {
339
+ if (node.type === 'class_definition') {
340
+ const name = node.childForFieldName('name')
341
+ if (name) {
342
+ pushSymbol(node, name.text, 'class')
343
+ cls = name.text
344
+ }
345
+ } else if (node.type === 'function_definition') {
346
+ const name = node.childForFieldName('name')
347
+ if (name) {
348
+ if (cls) pushSymbol(node, `${cls}.${name.text}`, 'method')
349
+ else pushSymbol(node, name.text, 'function')
350
+ }
351
+ }
352
+ } else if (grammar === 'rust') {
353
+ if (node.type === 'struct_item') {
354
+ const name = node.childForFieldName('name')
355
+ if (name) pushSymbol(node, name.text, 'struct')
356
+ } else if (node.type === 'enum_item') {
357
+ const name = node.childForFieldName('name')
358
+ if (name) pushSymbol(node, name.text, 'enum')
359
+ } else if (node.type === 'trait_item') {
360
+ const name = node.childForFieldName('name')
361
+ if (name) pushSymbol(node, name.text, 'trait')
362
+ } else if (node.type === 'type_item') {
363
+ const name = node.childForFieldName('name')
364
+ if (name) pushSymbol(node, name.text, 'type')
365
+ } else if (node.type === 'impl_item') {
366
+ const typeNode = node.childForFieldName('type')
367
+ if (typeNode) cls = typeNode.text
368
+ } else if (node.type === 'function_item') {
369
+ const name = node.childForFieldName('name')
370
+ if (name) {
371
+ if (cls) pushSymbol(node, `${cls}.${name.text}`, 'method')
372
+ else pushSymbol(node, name.text, 'function')
373
+ }
374
+ }
375
+ }
376
+
377
+ if (grammar === 'python') {
378
+ if (node.type === 'call') {
379
+ const fn = node.childForFieldName('function')
380
+ if (fn) {
381
+ if (fn.type === 'identifier') {
382
+ calls.push({ name: fn.text, recv: null, row: node.startPosition.row })
383
+ } else if (fn.type === 'attribute') {
384
+ const obj = fn.childForFieldName('object')
385
+ const attr = fn.childForFieldName('attribute')
386
+ if (attr) {
387
+ calls.push({ name: attr.text, recv: obj ? obj.text : null, row: node.startPosition.row })
388
+ }
389
+ }
390
+ }
391
+ }
392
+ } else if (grammar === 'rust') {
393
+ if (node.type === 'call_expression') {
394
+ const fn = node.childForFieldName('function')
395
+ if (fn) {
396
+ if (fn.type === 'identifier') {
397
+ calls.push({ name: fn.text, recv: null, row: node.startPosition.row })
398
+ } else if (fn.type === 'field_expression') {
399
+ const val = fn.childForFieldName('value')
400
+ const field = fn.childForFieldName('field')
401
+ if (field) {
402
+ calls.push({ name: field.text, recv: val ? val.text : null, row: node.startPosition.row })
403
+ }
404
+ } else if (fn.type === 'scoped_identifier') {
405
+ const pathNode = fn.childForFieldName('path')
406
+ const nameNode = fn.childForFieldName('name')
407
+ if (nameNode) {
408
+ const recv = pathNode
409
+ ? (pathNode.type === 'scoped_identifier' ? (pathNode.childForFieldName('name') || pathNode).text : pathNode.text)
410
+ : null
411
+ calls.push({ name: nameNode.text, recv, row: node.startPosition.row })
412
+ }
413
+ }
414
+ }
415
+ }
416
+ } else {
417
+ if (node.type === 'call_expression' || node.type === 'new_expression') {
418
+ const fn = node.type === 'call_expression'
419
+ ? node.childForFieldName('function')
420
+ : node.childForFieldName('constructor')
421
+ if (fn) {
422
+ if (fn.type === 'identifier') {
423
+ calls.push({ name: fn.text, recv: null, row: node.startPosition.row })
424
+ } else if (fn.type === 'member_expression' || fn.type === 'selector_expression') {
425
+ const named = fn.namedChildren
426
+ if (named.length >= 2) {
427
+ calls.push({ name: named[named.length - 1].text, recv: named[0].text, row: node.startPosition.row })
428
+ }
429
+ }
430
+ }
431
+ }
432
+ }
433
+ for (let i = 0; i < node.childCount; i++) visit(node.child(i), cls)
434
+ }
435
+ visit(tree.rootNode, null)
436
+ return { symbols, calls }
437
+ }