ucn 3.0.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.

Potentially problematic release.


This version of ucn might be problematic. Click here for more details.

Files changed (45) hide show
  1. package/.claude/skills/ucn/SKILL.md +77 -0
  2. package/LICENSE +21 -0
  3. package/README.md +135 -0
  4. package/cli/index.js +2437 -0
  5. package/core/discovery.js +513 -0
  6. package/core/imports.js +558 -0
  7. package/core/output.js +1274 -0
  8. package/core/parser.js +279 -0
  9. package/core/project.js +3261 -0
  10. package/index.js +52 -0
  11. package/languages/go.js +653 -0
  12. package/languages/index.js +267 -0
  13. package/languages/java.js +826 -0
  14. package/languages/javascript.js +1346 -0
  15. package/languages/python.js +667 -0
  16. package/languages/rust.js +950 -0
  17. package/languages/utils.js +457 -0
  18. package/package.json +42 -0
  19. package/test/fixtures/go/go.mod +3 -0
  20. package/test/fixtures/go/main.go +257 -0
  21. package/test/fixtures/go/service.go +187 -0
  22. package/test/fixtures/java/DataService.java +279 -0
  23. package/test/fixtures/java/Main.java +287 -0
  24. package/test/fixtures/java/Utils.java +199 -0
  25. package/test/fixtures/java/pom.xml +6 -0
  26. package/test/fixtures/javascript/main.js +109 -0
  27. package/test/fixtures/javascript/package.json +1 -0
  28. package/test/fixtures/javascript/service.js +88 -0
  29. package/test/fixtures/javascript/utils.js +67 -0
  30. package/test/fixtures/python/main.py +198 -0
  31. package/test/fixtures/python/pyproject.toml +3 -0
  32. package/test/fixtures/python/service.py +166 -0
  33. package/test/fixtures/python/utils.py +118 -0
  34. package/test/fixtures/rust/Cargo.toml +3 -0
  35. package/test/fixtures/rust/main.rs +253 -0
  36. package/test/fixtures/rust/service.rs +210 -0
  37. package/test/fixtures/rust/utils.rs +154 -0
  38. package/test/fixtures/typescript/main.ts +154 -0
  39. package/test/fixtures/typescript/package.json +1 -0
  40. package/test/fixtures/typescript/repository.ts +149 -0
  41. package/test/fixtures/typescript/types.ts +114 -0
  42. package/test/parser.test.js +3661 -0
  43. package/test/public-repos-test.js +477 -0
  44. package/test/systematic-test.js +619 -0
  45. package/ucn.js +8 -0
package/index.js ADDED
@@ -0,0 +1,52 @@
1
+ /**
2
+ * UCN - Universal Code Navigator
3
+ *
4
+ * Code navigation built by AI, for AI.
5
+ * Reduces context usage by 90%+ when working with large codebases.
6
+ */
7
+
8
+ const parser = require('./core/parser');
9
+ const { ProjectIndex } = require('./core/project');
10
+ const discovery = require('./core/discovery');
11
+ const imports = require('./core/imports');
12
+ const output = require('./core/output');
13
+ const languages = require('./languages');
14
+
15
+ /**
16
+ * Main API
17
+ */
18
+ module.exports = {
19
+ // Core parser functions
20
+ parse: parser.parse,
21
+ parseFile: parser.parseFile,
22
+ extractFunction: parser.extractFunction,
23
+ extractClass: parser.extractClass,
24
+ getToc: parser.getToc,
25
+ findSymbol: parser.findSymbol,
26
+ getExportedSymbols: parser.getExportedSymbols,
27
+
28
+ // Language detection
29
+ detectLanguage: parser.detectLanguage,
30
+ isSupported: parser.isSupported,
31
+
32
+ // Project-level operations
33
+ ProjectIndex,
34
+
35
+ // File discovery
36
+ expandGlob: discovery.expandGlob,
37
+ findProjectRoot: discovery.findProjectRoot,
38
+ detectProjectPattern: discovery.detectProjectPattern,
39
+ isTestFile: discovery.isTestFile,
40
+ findTestFileFor: discovery.findTestFileFor,
41
+
42
+ // Import/export analysis
43
+ extractImports: imports.extractImports,
44
+ extractExports: imports.extractExports,
45
+ resolveImport: imports.resolveImport,
46
+
47
+ // Output formatting
48
+ output,
49
+
50
+ // Language modules (for advanced use)
51
+ languages
52
+ };
@@ -0,0 +1,653 @@
1
+ /**
2
+ * languages/go.js - Tree-sitter based Go parsing
3
+ *
4
+ * Handles: function declarations, method declarations (with receivers),
5
+ * struct/interface types, and const/var declarations.
6
+ */
7
+
8
+ const {
9
+ traverseTree,
10
+ nodeToLocation,
11
+ parseStructuredParams,
12
+ extractGoDocstring
13
+ } = require('./utils');
14
+ const { PARSE_OPTIONS } = require('./index');
15
+
16
+ /**
17
+ * Extract return type from Go function/method
18
+ */
19
+ function extractReturnType(node) {
20
+ const resultNode = node.childForFieldName('result');
21
+ if (resultNode) {
22
+ return resultNode.text.trim() || null;
23
+ }
24
+ return null;
25
+ }
26
+
27
+ /**
28
+ * Extract Go parameters
29
+ */
30
+ function extractGoParams(paramsNode) {
31
+ if (!paramsNode) return '...';
32
+ const text = paramsNode.text;
33
+ let params = text.replace(/^\(|\)$/g, '').trim();
34
+ if (!params) return '...';
35
+ return params;
36
+ }
37
+
38
+ /**
39
+ * Extract receiver from method declaration
40
+ */
41
+ function extractReceiver(receiverNode) {
42
+ if (!receiverNode) return null;
43
+ const text = receiverNode.text;
44
+ const match = text.match(/\(\s*\w*\s*(\*?\w+)\s*\)/);
45
+ return match ? match[1] : text.replace(/^\(|\)$/g, '').trim();
46
+ }
47
+
48
+ /**
49
+ * Find all functions in Go code using tree-sitter
50
+ */
51
+ function findFunctions(code, parser) {
52
+ const tree = parser.parse(code, undefined, PARSE_OPTIONS);
53
+ const functions = [];
54
+ const processedRanges = new Set();
55
+
56
+ traverseTree(tree.rootNode, (node) => {
57
+ const rangeKey = `${node.startIndex}-${node.endIndex}`;
58
+
59
+ // Function declarations
60
+ if (node.type === 'function_declaration') {
61
+ if (processedRanges.has(rangeKey)) return true;
62
+ processedRanges.add(rangeKey);
63
+
64
+ const nameNode = node.childForFieldName('name');
65
+ const paramsNode = node.childForFieldName('parameters');
66
+
67
+ if (nameNode) {
68
+ const { startLine, endLine, indent } = nodeToLocation(node, code);
69
+ const returnType = extractReturnType(node);
70
+ const docstring = extractGoDocstring(code, startLine);
71
+ const typeParams = extractTypeParams(node);
72
+
73
+ // Check if exported (capitalized)
74
+ const isExported = /^[A-Z]/.test(nameNode.text);
75
+
76
+ functions.push({
77
+ name: nameNode.text,
78
+ params: extractGoParams(paramsNode),
79
+ paramsStructured: parseStructuredParams(paramsNode, 'go'),
80
+ startLine,
81
+ endLine,
82
+ indent,
83
+ modifiers: isExported ? ['export'] : [],
84
+ ...(returnType && { returnType }),
85
+ ...(docstring && { docstring }),
86
+ ...(typeParams && { generics: typeParams })
87
+ });
88
+ }
89
+ return true;
90
+ }
91
+
92
+ // Method declarations (with receivers)
93
+ if (node.type === 'method_declaration') {
94
+ if (processedRanges.has(rangeKey)) return true;
95
+ processedRanges.add(rangeKey);
96
+
97
+ const nameNode = node.childForFieldName('name');
98
+ const paramsNode = node.childForFieldName('parameters');
99
+ const receiverNode = node.childForFieldName('receiver');
100
+
101
+ if (nameNode) {
102
+ const { startLine, endLine, indent } = nodeToLocation(node, code);
103
+ const receiver = extractReceiver(receiverNode);
104
+ const returnType = extractReturnType(node);
105
+ const docstring = extractGoDocstring(code, startLine);
106
+
107
+ // Check if exported
108
+ const isExported = /^[A-Z]/.test(nameNode.text);
109
+
110
+ functions.push({
111
+ name: nameNode.text,
112
+ params: extractGoParams(paramsNode),
113
+ paramsStructured: parseStructuredParams(paramsNode, 'go'),
114
+ startLine,
115
+ endLine,
116
+ indent,
117
+ isMethod: true,
118
+ receiver,
119
+ modifiers: isExported ? ['export'] : [],
120
+ ...(returnType && { returnType }),
121
+ ...(docstring && { docstring })
122
+ });
123
+ }
124
+ return true;
125
+ }
126
+
127
+ return true;
128
+ });
129
+
130
+ functions.sort((a, b) => a.startLine - b.startLine);
131
+ return functions;
132
+ }
133
+
134
+ /**
135
+ * Extract type parameters (generics) from function/type
136
+ */
137
+ function extractTypeParams(node) {
138
+ const typeParamsNode = node.childForFieldName('type_parameters');
139
+ if (typeParamsNode) {
140
+ return typeParamsNode.text;
141
+ }
142
+ return null;
143
+ }
144
+
145
+ /**
146
+ * Find all types (structs, interfaces) in Go code using tree-sitter
147
+ */
148
+ function findClasses(code, parser) {
149
+ const tree = parser.parse(code, undefined, PARSE_OPTIONS);
150
+ const types = [];
151
+ const processedRanges = new Set();
152
+
153
+ traverseTree(tree.rootNode, (node) => {
154
+ const rangeKey = `${node.startIndex}-${node.endIndex}`;
155
+
156
+ if (node.type === 'type_declaration') {
157
+ if (processedRanges.has(rangeKey)) return true;
158
+ processedRanges.add(rangeKey);
159
+
160
+ for (let i = 0; i < node.namedChildCount; i++) {
161
+ const spec = node.namedChild(i);
162
+ if (spec.type === 'type_spec') {
163
+ const nameNode = spec.childForFieldName('name');
164
+ const typeNode = spec.childForFieldName('type');
165
+
166
+ if (nameNode && typeNode) {
167
+ const { startLine, endLine } = nodeToLocation(node, code);
168
+ const name = nameNode.text;
169
+ const docstring = extractGoDocstring(code, startLine);
170
+ const typeParams = extractTypeParams(spec);
171
+
172
+ let typeKind = 'type';
173
+ if (typeNode.type === 'struct_type') {
174
+ typeKind = 'struct';
175
+ } else if (typeNode.type === 'interface_type') {
176
+ typeKind = 'interface';
177
+ }
178
+
179
+ // Check if exported
180
+ const isExported = /^[A-Z]/.test(name);
181
+
182
+ types.push({
183
+ name,
184
+ startLine,
185
+ endLine,
186
+ type: typeKind,
187
+ members: typeKind === 'struct' ? extractStructFields(typeNode, code) : [],
188
+ modifiers: isExported ? ['export'] : [],
189
+ ...(docstring && { docstring }),
190
+ ...(typeParams && { generics: typeParams })
191
+ });
192
+ }
193
+ }
194
+ }
195
+ return true;
196
+ }
197
+
198
+ return true;
199
+ });
200
+
201
+ types.sort((a, b) => a.startLine - b.startLine);
202
+ return types;
203
+ }
204
+
205
+ /**
206
+ * Extract struct fields
207
+ */
208
+ function extractStructFields(structNode, code) {
209
+ const fields = [];
210
+ const fieldListNode = structNode.childForFieldName('body') || structNode;
211
+
212
+ for (let i = 0; i < fieldListNode.namedChildCount; i++) {
213
+ const field = fieldListNode.namedChild(i);
214
+ if (field.type === 'field_declaration') {
215
+ const { startLine, endLine } = nodeToLocation(field, code);
216
+ const nameNode = field.childForFieldName('name');
217
+ const typeNode = field.childForFieldName('type');
218
+
219
+ if (nameNode) {
220
+ fields.push({
221
+ name: nameNode.text,
222
+ startLine,
223
+ endLine,
224
+ memberType: 'field',
225
+ ...(typeNode && { fieldType: typeNode.text })
226
+ });
227
+ }
228
+ }
229
+ }
230
+
231
+ return fields;
232
+ }
233
+
234
+ /**
235
+ * Find state objects (constants) in Go code
236
+ */
237
+ function findStateObjects(code, parser) {
238
+ const tree = parser.parse(code, undefined, PARSE_OPTIONS);
239
+ const objects = [];
240
+
241
+ const statePattern = /^(CONFIG|SETTINGS|[A-Z][A-Z0-9_]+|Default[A-Z][a-zA-Z]*|[A-Z][a-zA-Z]*(?:Config|Settings|Options))$/;
242
+
243
+ // Check if a value node is a composite literal
244
+ function isCompositeLiteral(valueNode) {
245
+ if (!valueNode) return false;
246
+ if (valueNode.type === 'composite_literal') return true;
247
+ for (let i = 0; i < valueNode.namedChildCount; i++) {
248
+ if (valueNode.namedChild(i).type === 'composite_literal') return true;
249
+ }
250
+ return false;
251
+ }
252
+
253
+ traverseTree(tree.rootNode, (node) => {
254
+ // Handle const declarations
255
+ if (node.type === 'const_declaration') {
256
+ for (let i = 0; i < node.namedChildCount; i++) {
257
+ const spec = node.namedChild(i);
258
+ if (spec.type === 'const_spec') {
259
+ const nameNode = spec.childForFieldName('name');
260
+ const valueNode = spec.childForFieldName('value');
261
+
262
+ if (nameNode && valueNode && isCompositeLiteral(valueNode)) {
263
+ const name = nameNode.text;
264
+ if (statePattern.test(name)) {
265
+ const { startLine, endLine } = nodeToLocation(spec, code);
266
+ objects.push({ name, startLine, endLine });
267
+ }
268
+ }
269
+ }
270
+ }
271
+ return true;
272
+ }
273
+
274
+ // Handle var declarations
275
+ if (node.type === 'var_declaration') {
276
+ for (let i = 0; i < node.namedChildCount; i++) {
277
+ const spec = node.namedChild(i);
278
+ if (spec.type === 'var_spec') {
279
+ const nameNode = spec.childForFieldName('name');
280
+ const valueNode = spec.childForFieldName('value');
281
+
282
+ if (nameNode && valueNode && isCompositeLiteral(valueNode)) {
283
+ const name = nameNode.text;
284
+ if (statePattern.test(name)) {
285
+ const { startLine, endLine } = nodeToLocation(spec, code);
286
+ objects.push({ name, startLine, endLine });
287
+ }
288
+ }
289
+ }
290
+ }
291
+ return true;
292
+ }
293
+
294
+ return true;
295
+ });
296
+
297
+ objects.sort((a, b) => a.startLine - b.startLine);
298
+ return objects;
299
+ }
300
+
301
+ /**
302
+ * Parse a Go file completely
303
+ */
304
+ function parse(code, parser) {
305
+ return {
306
+ language: 'go',
307
+ totalLines: code.split('\n').length,
308
+ functions: findFunctions(code, parser),
309
+ classes: findClasses(code, parser),
310
+ stateObjects: findStateObjects(code, parser),
311
+ imports: [],
312
+ exports: []
313
+ };
314
+ }
315
+
316
+ /**
317
+ * Find all function calls in Go code using tree-sitter AST
318
+ * @param {string} code - Source code to analyze
319
+ * @param {object} parser - Tree-sitter parser instance
320
+ * @returns {Array<{name: string, line: number, isMethod: boolean, receiver?: string}>}
321
+ */
322
+ function findCallsInCode(code, parser) {
323
+ const tree = parser.parse(code, undefined, PARSE_OPTIONS);
324
+ const calls = [];
325
+ const functionStack = []; // Stack of { name, startLine, endLine }
326
+
327
+ // Helper to check if a node creates a function scope
328
+ const isFunctionNode = (node) => {
329
+ return ['function_declaration', 'method_declaration', 'func_literal'].includes(node.type);
330
+ };
331
+
332
+ // Helper to extract function name from a function node
333
+ const extractFunctionName = (node) => {
334
+ if (node.type === 'function_declaration') {
335
+ const nameNode = node.childForFieldName('name');
336
+ return nameNode?.text || '<anonymous>';
337
+ }
338
+ if (node.type === 'method_declaration') {
339
+ const nameNode = node.childForFieldName('name');
340
+ return nameNode?.text || '<anonymous>';
341
+ }
342
+ if (node.type === 'func_literal') {
343
+ return '<anonymous>';
344
+ }
345
+ return '<anonymous>';
346
+ };
347
+
348
+ // Helper to get current enclosing function
349
+ const getCurrentEnclosingFunction = () => {
350
+ return functionStack.length > 0
351
+ ? { ...functionStack[functionStack.length - 1] }
352
+ : null;
353
+ };
354
+
355
+ traverseTree(tree.rootNode, (node) => {
356
+ // Track function entry
357
+ if (isFunctionNode(node)) {
358
+ functionStack.push({
359
+ name: extractFunctionName(node),
360
+ startLine: node.startPosition.row + 1,
361
+ endLine: node.endPosition.row + 1
362
+ });
363
+ }
364
+
365
+ // Handle function calls: foo(), pkg.Foo(), obj.Method()
366
+ if (node.type === 'call_expression') {
367
+ const funcNode = node.childForFieldName('function');
368
+ if (!funcNode) return true;
369
+
370
+ const enclosingFunction = getCurrentEnclosingFunction();
371
+
372
+ if (funcNode.type === 'identifier') {
373
+ // Direct call: foo()
374
+ calls.push({
375
+ name: funcNode.text,
376
+ line: node.startPosition.row + 1,
377
+ isMethod: false,
378
+ enclosingFunction
379
+ });
380
+ } else if (funcNode.type === 'selector_expression') {
381
+ // Method or package call: obj.Method() or pkg.Func()
382
+ const fieldNode = funcNode.childForFieldName('field');
383
+ const operandNode = funcNode.childForFieldName('operand');
384
+
385
+ if (fieldNode) {
386
+ calls.push({
387
+ name: fieldNode.text,
388
+ line: node.startPosition.row + 1,
389
+ isMethod: true,
390
+ receiver: operandNode?.type === 'identifier' ? operandNode.text : undefined,
391
+ enclosingFunction
392
+ });
393
+ }
394
+ }
395
+ return true;
396
+ }
397
+
398
+ return true;
399
+ }, {
400
+ onLeave: (node) => {
401
+ if (isFunctionNode(node)) {
402
+ functionStack.pop();
403
+ }
404
+ }
405
+ });
406
+
407
+ return calls;
408
+ }
409
+
410
+ /**
411
+ * Find all imports in Go code using tree-sitter AST
412
+ * @param {string} code - Source code to analyze
413
+ * @param {object} parser - Tree-sitter parser instance
414
+ * @returns {Array<{module: string, names: string[], type: string, line: number}>}
415
+ */
416
+ function findImportsInCode(code, parser) {
417
+ const tree = parser.parse(code, undefined, PARSE_OPTIONS);
418
+ const imports = [];
419
+
420
+ function processImportSpec(spec) {
421
+ const line = spec.startPosition.row + 1;
422
+ let modulePath = null;
423
+ let alias = null;
424
+ let importType = 'import';
425
+
426
+ for (let i = 0; i < spec.namedChildCount; i++) {
427
+ const child = spec.namedChild(i);
428
+ if (child.type === 'interpreted_string_literal') {
429
+ // Remove quotes
430
+ modulePath = child.text.slice(1, -1);
431
+ } else if (child.type === 'package_identifier') {
432
+ alias = child.text;
433
+ } else if (child.type === 'blank_identifier') {
434
+ alias = '_';
435
+ importType = 'side-effect';
436
+ } else if (child.type === 'dot') {
437
+ alias = '.';
438
+ importType = 'dot-import';
439
+ }
440
+ }
441
+
442
+ if (modulePath) {
443
+ // Package name is last segment of path
444
+ const pkgName = alias || modulePath.split('/').pop();
445
+ imports.push({
446
+ module: modulePath,
447
+ names: [pkgName],
448
+ type: importType,
449
+ line
450
+ });
451
+ }
452
+ }
453
+
454
+ traverseTree(tree.rootNode, (node) => {
455
+ if (node.type === 'import_declaration') {
456
+ for (let i = 0; i < node.namedChildCount; i++) {
457
+ const child = node.namedChild(i);
458
+ if (child.type === 'import_spec') {
459
+ processImportSpec(child);
460
+ } else if (child.type === 'import_spec_list') {
461
+ for (let j = 0; j < child.namedChildCount; j++) {
462
+ const spec = child.namedChild(j);
463
+ if (spec.type === 'import_spec') {
464
+ processImportSpec(spec);
465
+ }
466
+ }
467
+ }
468
+ }
469
+ return true;
470
+ }
471
+
472
+ return true;
473
+ });
474
+
475
+ return imports;
476
+ }
477
+
478
+ /**
479
+ * Find all exports in Go code using tree-sitter AST
480
+ * In Go, exports are capitalized public symbols
481
+ * @param {string} code - Source code to analyze
482
+ * @param {object} parser - Tree-sitter parser instance
483
+ * @returns {Array<{name: string, type: string, line: number}>}
484
+ */
485
+ function findExportsInCode(code, parser) {
486
+ const tree = parser.parse(code, undefined, PARSE_OPTIONS);
487
+ const exports = [];
488
+
489
+ traverseTree(tree.rootNode, (node) => {
490
+ // Exported functions
491
+ if (node.type === 'function_declaration') {
492
+ const nameNode = node.childForFieldName('name');
493
+ if (nameNode && /^[A-Z]/.test(nameNode.text)) {
494
+ exports.push({
495
+ name: nameNode.text,
496
+ type: 'function',
497
+ line: node.startPosition.row + 1
498
+ });
499
+ }
500
+ return true;
501
+ }
502
+
503
+ // Exported methods
504
+ if (node.type === 'method_declaration') {
505
+ const nameNode = node.childForFieldName('name');
506
+ if (nameNode && /^[A-Z]/.test(nameNode.text)) {
507
+ exports.push({
508
+ name: nameNode.text,
509
+ type: 'method',
510
+ line: node.startPosition.row + 1
511
+ });
512
+ }
513
+ return true;
514
+ }
515
+
516
+ // Exported types (struct, interface, type alias)
517
+ if (node.type === 'type_declaration') {
518
+ for (let i = 0; i < node.namedChildCount; i++) {
519
+ const spec = node.namedChild(i);
520
+ if (spec.type === 'type_spec') {
521
+ const nameNode = spec.childForFieldName('name');
522
+ if (nameNode && /^[A-Z]/.test(nameNode.text)) {
523
+ exports.push({
524
+ name: nameNode.text,
525
+ type: 'type',
526
+ line: node.startPosition.row + 1
527
+ });
528
+ }
529
+ }
530
+ }
531
+ return true;
532
+ }
533
+
534
+ // Exported const/var
535
+ if (node.type === 'const_declaration' || node.type === 'var_declaration') {
536
+ for (let i = 0; i < node.namedChildCount; i++) {
537
+ const spec = node.namedChild(i);
538
+ if (spec.type === 'const_spec' || spec.type === 'var_spec') {
539
+ const nameNode = spec.childForFieldName('name');
540
+ if (nameNode && /^[A-Z]/.test(nameNode.text)) {
541
+ exports.push({
542
+ name: nameNode.text,
543
+ type: node.type === 'const_declaration' ? 'const' : 'var',
544
+ line: node.startPosition.row + 1
545
+ });
546
+ }
547
+ }
548
+ }
549
+ return true;
550
+ }
551
+
552
+ return true;
553
+ });
554
+
555
+ return exports;
556
+ }
557
+
558
+ /**
559
+ * Find all usages of a name in code using AST
560
+ * @param {string} code - Source code
561
+ * @param {string} name - Symbol name to find
562
+ * @param {object} parser - Tree-sitter parser instance
563
+ * @returns {Array<{line: number, column: number, usageType: string}>}
564
+ */
565
+ function findUsagesInCode(code, name, parser) {
566
+ const tree = parser.parse(code, undefined, PARSE_OPTIONS);
567
+ const usages = [];
568
+
569
+ traverseTree(tree.rootNode, (node) => {
570
+ // Only look for identifiers with the matching name
571
+ if (node.type !== 'identifier' || node.text !== name) {
572
+ return true;
573
+ }
574
+
575
+ const line = node.startPosition.row + 1;
576
+ const column = node.startPosition.column;
577
+ const parent = node.parent;
578
+
579
+ let usageType = 'reference';
580
+
581
+ if (parent) {
582
+ // Import: import_spec
583
+ if (parent.type === 'import_spec' ||
584
+ parent.type === 'package_identifier') {
585
+ usageType = 'import';
586
+ }
587
+ // Call: identifier is function in call_expression
588
+ else if (parent.type === 'call_expression' &&
589
+ parent.childForFieldName('function') === node) {
590
+ usageType = 'call';
591
+ }
592
+ // Definition: function name
593
+ else if (parent.type === 'function_declaration' &&
594
+ parent.childForFieldName('name') === node) {
595
+ usageType = 'definition';
596
+ }
597
+ // Definition: method name
598
+ else if (parent.type === 'method_declaration' &&
599
+ parent.childForFieldName('name') === node) {
600
+ usageType = 'definition';
601
+ }
602
+ // Definition: type name
603
+ else if (parent.type === 'type_spec' &&
604
+ parent.childForFieldName('name') === node) {
605
+ usageType = 'definition';
606
+ }
607
+ // Definition: variable name in short var declaration
608
+ else if (parent.type === 'short_var_declaration') {
609
+ const left = parent.childForFieldName('left');
610
+ if (left && (left === node || left.text.includes(name))) {
611
+ usageType = 'definition';
612
+ }
613
+ }
614
+ // Definition: const/var spec
615
+ else if (parent.type === 'const_spec' || parent.type === 'var_spec') {
616
+ const nameNode = parent.childForFieldName('name');
617
+ if (nameNode === node) {
618
+ usageType = 'definition';
619
+ }
620
+ }
621
+ // Definition: parameter
622
+ else if (parent.type === 'parameter_declaration') {
623
+ usageType = 'definition';
624
+ }
625
+ // Method call: selector_expression followed by call
626
+ else if (parent.type === 'selector_expression' &&
627
+ parent.childForFieldName('field') === node) {
628
+ const grandparent = parent.parent;
629
+ if (grandparent && grandparent.type === 'call_expression') {
630
+ usageType = 'call';
631
+ } else {
632
+ usageType = 'reference';
633
+ }
634
+ }
635
+ }
636
+
637
+ usages.push({ line, column, usageType });
638
+ return true;
639
+ });
640
+
641
+ return usages;
642
+ }
643
+
644
+ module.exports = {
645
+ findFunctions,
646
+ findClasses,
647
+ findStateObjects,
648
+ findCallsInCode,
649
+ findImportsInCode,
650
+ findExportsInCode,
651
+ findUsagesInCode,
652
+ parse
653
+ };