forked from colbymchenry/codegraph
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathscala.ts
More file actions
143 lines (125 loc) · 5.29 KB
/
Copy pathscala.ts
File metadata and controls
143 lines (125 loc) · 5.29 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
import type { Node as SyntaxNode } from 'web-tree-sitter';
import { getNodeText } from '../tree-sitter-helpers';
import type { LanguageExtractor } from '../tree-sitter-types';
function getValVarName(node: SyntaxNode, source: string): string | null {
const patternNode = node.childForFieldName('pattern');
if (!patternNode) return null;
if (patternNode.type === 'identifier') return getNodeText(patternNode, source);
const identChild = patternNode.namedChildren.find((c: SyntaxNode) => c.type === 'identifier');
return identChild ? getNodeText(identChild, source) : null;
}
function extractVisibility(node: SyntaxNode): 'public' | 'private' | 'protected' {
for (let i = 0; i < node.namedChildCount; i++) {
const child = node.namedChild(i);
if (!child) continue;
if (child.type === 'modifiers' || child.type === 'access_modifier') {
const text = child.text;
if (text.includes('private')) return 'private';
if (text.includes('protected')) return 'protected';
}
}
return 'public';
}
export const scalaExtractor: LanguageExtractor = {
// top-level function_definition is handled via methodTypes (same pattern as Kotlin)
functionTypes: [],
classTypes: ['class_definition', 'object_definition', 'trait_definition'],
methodTypes: ['function_definition', 'function_declaration'],
interfaceTypes: [],
structTypes: [],
enumTypes: ['enum_definition'],
enumMemberTypes: [], // handled in visitNode — enum_case_definitions wraps the cases
typeAliasTypes: ['type_definition'],
importTypes: ['import_declaration'],
callTypes: ['call_expression'],
variableTypes: [], // val/var handled in visitNode (use `pattern` field, not `name`)
fieldTypes: [],
extraClassNodeTypes: [],
nameField: 'name',
bodyField: 'body',
paramsField: 'parameters',
returnField: 'return_type',
interfaceKind: 'trait',
classifyClassNode: (node: SyntaxNode) => {
if (node.type === 'trait_definition') return 'trait';
return 'class';
},
getSignature: (node: SyntaxNode, source: string) => {
const params = node.childForFieldName('parameters');
const returnType = node.childForFieldName('return_type');
if (!params && !returnType) return undefined;
let sig = params ? getNodeText(params, source) : '';
if (returnType) sig += ': ' + getNodeText(returnType, source);
return sig || undefined;
},
getVisibility: (node: SyntaxNode) => extractVisibility(node),
isAsync: () => false,
isStatic: (node: SyntaxNode) => {
for (let i = 0; i < node.namedChildCount; i++) {
const child = node.namedChild(i);
if (child?.type === 'modifiers' && child.text.includes('static')) return true;
}
return false;
},
visitNode: (node: SyntaxNode, ctx) => {
const t = node.type;
// val/var: name is in `pattern` field (identifier), not `name`
if (t === 'val_definition' || t === 'var_definition') {
const name = getValVarName(node, ctx.source);
if (!name) return false;
const isInClass = ctx.nodeStack.length > 0 &&
(() => {
const parentId = ctx.nodeStack[ctx.nodeStack.length - 1];
const parentNode = ctx.nodes.find((n) => n.id === parentId);
return parentNode != null && (
parentNode.kind === 'class' || parentNode.kind === 'trait' ||
parentNode.kind === 'interface' || parentNode.kind === 'struct' ||
parentNode.kind === 'enum' || parentNode.kind === 'module'
);
})();
const kind = isInClass ? 'field' : (t === 'val_definition' ? 'constant' : 'variable');
const typeNode = node.childForFieldName('type');
const sig = typeNode
? `${t === 'val_definition' ? 'val' : 'var'} ${name}: ${getNodeText(typeNode, ctx.source)}`
: undefined;
ctx.createNode(kind, name, node, { signature: sig, visibility: extractVisibility(node) });
return true;
}
// enum_case_definitions wraps simple_enum_case / full_enum_case children
if (t === 'enum_case_definitions') {
for (let i = 0; i < node.namedChildCount; i++) {
const child = node.namedChild(i);
if (!child) continue;
if (child.type === 'simple_enum_case' || child.type === 'full_enum_case') {
const nameNode = child.childForFieldName('name');
if (nameNode) ctx.createNode('enum_member', getNodeText(nameNode, ctx.source), child);
}
}
return true;
}
// extension_definition: visit body children directly, no container node
if (t === 'extension_definition') {
const body = node.childForFieldName('body');
if (body) {
for (let i = 0; i < body.namedChildCount; i++) {
const child = body.namedChild(i);
if (child) ctx.visitNode(child);
}
}
return true;
}
return false;
},
extractImport: (node: SyntaxNode, source: string) => {
const importText = getNodeText(node, source).trim();
const pathNode = node.childForFieldName('path');
if (pathNode) return { moduleName: getNodeText(pathNode, source), signature: importText };
for (let i = 0; i < node.namedChildCount; i++) {
const child = node.namedChild(i);
if (child?.type === 'identifier' || child?.type === 'stable_identifier') {
return { moduleName: getNodeText(child, source), signature: importText };
}
}
return null;
},
};