mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-10-03 02:21:44 +00:00
feat(scala): add Scala language provider and all extractors
Full tree-sitter-based Scala support using tree-sitter-scala@0.24.0 (N-API binding, ABI version 14 — no vendoring needed): - SCALA_QUERIES: class/trait/object/enum definitions, method defs + declarations, import declarations, field/free/constructor calls, val/var declarations, extends heritage, field assignments - Type extractor with val/var type annotations and constructor binding - All pipeline extractors following Java/Go provider pattern - Export checker (public by default, like Kotlin) - JVM import resolver reusing resolveJvmWildcard/resolveJvmMemberImport - CLASS_CONTAINER_TYPES and CONTAINER_TYPE_TO_LABEL extended for Scala (object_definition, trait_definition, enum_definition) - FUNCTION_NODE_TYPES extended with function_definition
This commit is contained in:
parent
48765ef8ce
commit
9c6caff879
17 changed files with 525 additions and 0 deletions
20
gitnexus/package-lock.json
generated
20
gitnexus/package-lock.json
generated
|
|
@ -40,6 +40,7 @@
|
|||
"tree-sitter-python": "0.23.4",
|
||||
"tree-sitter-ruby": "^0.23.1",
|
||||
"tree-sitter-rust": "0.23.1",
|
||||
"tree-sitter-scala": "^0.24.0",
|
||||
"tree-sitter-typescript": "^0.23.2",
|
||||
"uuid": "^14.0.0"
|
||||
},
|
||||
|
|
@ -5203,6 +5204,25 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/tree-sitter-scala": {
|
||||
"version": "0.24.0",
|
||||
"resolved": "https://registry.npmjs.org/tree-sitter-scala/-/tree-sitter-scala-0.24.0.tgz",
|
||||
"integrity": "sha512-vkMuAUrBZ1zZz2XcGDQk18Kz73JkpgaeXzbNVobPke0G35sd9jH32aUxG6OLRKM7et0TbsfqkWf4DeJoGk4K1g==",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"node-addon-api": "^8.2.2",
|
||||
"node-gyp-build": "^4.8.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"tree-sitter": "^0.21.1"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"tree-sitter": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/tree-sitter-swift": {
|
||||
"resolved": "vendor/tree-sitter-swift",
|
||||
"link": true
|
||||
|
|
|
|||
|
|
@ -82,6 +82,7 @@
|
|||
"tree-sitter-python": "0.23.4",
|
||||
"tree-sitter-ruby": "^0.23.1",
|
||||
"tree-sitter-rust": "0.23.1",
|
||||
"tree-sitter-scala": "^0.24.0",
|
||||
"tree-sitter-typescript": "^0.23.2",
|
||||
"uuid": "^14.0.0"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
import { SupportedLanguages } from 'gitnexus-shared';
|
||||
import type { CallExtractionConfig } from '../../call-types.js';
|
||||
|
||||
export const scalaCallConfig: CallExtractionConfig = {
|
||||
language: SupportedLanguages.Scala,
|
||||
};
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
import { SupportedLanguages } from 'gitnexus-shared';
|
||||
import type { ClassExtractionConfig, ClassLikeNodeLabel } from '../../class-types.js';
|
||||
|
||||
export const scalaClassConfig: ClassExtractionConfig = {
|
||||
language: SupportedLanguages.Scala,
|
||||
typeDeclarationNodes: [
|
||||
'class_definition',
|
||||
'object_definition',
|
||||
'trait_definition',
|
||||
'enum_definition',
|
||||
],
|
||||
fileScopeNodeTypes: ['compilation_unit'],
|
||||
ancestorScopeNodeTypes: ['class_definition', 'object_definition', 'trait_definition'],
|
||||
|
||||
extractName(node) {
|
||||
return node.childForFieldName('name')?.text;
|
||||
},
|
||||
|
||||
extractType(node): ClassLikeNodeLabel | undefined {
|
||||
switch (node.type) {
|
||||
case 'class_definition':
|
||||
return 'Class';
|
||||
case 'trait_definition':
|
||||
return 'Interface';
|
||||
case 'object_definition':
|
||||
return 'Class';
|
||||
case 'enum_definition':
|
||||
return 'Enum';
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
|
@ -246,3 +246,26 @@ export const rubyExportChecker: ExportChecker = (_node, _name) => true;
|
|||
|
||||
/** Dart: public if no leading underscore (convention, same as Python). */
|
||||
export const dartExportChecker: ExportChecker = (_node, name) => !name.startsWith('_');
|
||||
|
||||
/**
|
||||
* Scala: default visibility is public (like Kotlin).
|
||||
* Only private/protected are non-exported. Walk up to find modifier.
|
||||
*/
|
||||
export const scalaExportChecker: ExportChecker = (node, _name) => {
|
||||
let current: SyntaxNode | null = node;
|
||||
while (current) {
|
||||
if (current.type === 'modifiers') {
|
||||
const text = current.text;
|
||||
if (text?.includes('private') || text?.includes('protected')) return false;
|
||||
}
|
||||
for (let i = 0; i < (current.childCount ?? 0); i++) {
|
||||
const child = current.child?.(i);
|
||||
if (child?.type === 'modifiers') {
|
||||
const text = child.text;
|
||||
if (text?.includes('private') || text?.includes('protected')) return false;
|
||||
}
|
||||
}
|
||||
current = current.parent;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -0,0 +1,42 @@
|
|||
import { SupportedLanguages } from 'gitnexus-shared';
|
||||
import type { FieldExtractionConfig } from '../generic.js';
|
||||
import { extractSimpleTypeName } from '../../type-extractors/shared.js';
|
||||
|
||||
export const scalaFieldConfig: FieldExtractionConfig = {
|
||||
language: SupportedLanguages.Scala,
|
||||
typeDeclarationNodes: ['class_definition', 'object_definition', 'trait_definition'],
|
||||
fieldNodeTypes: ['val_definition', 'var_definition', 'val_declaration', 'var_declaration'],
|
||||
bodyNodeTypes: ['template_body'],
|
||||
defaultVisibility: 'public',
|
||||
|
||||
extractName(node) {
|
||||
const pattern = node.childForFieldName('pattern');
|
||||
if (pattern?.type === 'identifier') return pattern.text;
|
||||
return undefined;
|
||||
},
|
||||
|
||||
extractType(node) {
|
||||
const typeNode = node.childForFieldName('type');
|
||||
if (typeNode) return extractSimpleTypeName(typeNode) ?? typeNode.text?.trim();
|
||||
return undefined;
|
||||
},
|
||||
|
||||
extractVisibility(node) {
|
||||
for (let i = 0; i < node.childCount; i++) {
|
||||
const child = node.child(i);
|
||||
if (child?.type === 'modifiers') {
|
||||
if (child.text?.includes('private')) return 'private';
|
||||
if (child.text?.includes('protected')) return 'protected';
|
||||
}
|
||||
}
|
||||
return 'public';
|
||||
},
|
||||
|
||||
isStatic(_node) {
|
||||
return false;
|
||||
},
|
||||
|
||||
isReadonly(node) {
|
||||
return node.type === 'val_definition' || node.type === 'val_declaration';
|
||||
},
|
||||
};
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
import { SupportedLanguages } from 'gitnexus-shared';
|
||||
import type { HeritageExtractionConfig } from '../../heritage-types.js';
|
||||
|
||||
export const scalaHeritageConfig: HeritageExtractionConfig = {
|
||||
language: SupportedLanguages.Scala,
|
||||
};
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
/**
|
||||
* Scala import resolution config.
|
||||
* Reuses JVM wildcard/member import strategy, then standard fallback.
|
||||
*/
|
||||
|
||||
import { SupportedLanguages } from 'gitnexus-shared';
|
||||
import type { ImportResolutionConfig, ImportResolverStrategy } from '../types.js';
|
||||
import { createStandardStrategy } from '../standard.js';
|
||||
import { resolveJvmWildcard, resolveJvmMemberImport } from '../jvm.js';
|
||||
|
||||
export const scalaJvmStrategy: ImportResolverStrategy = (rawImportPath, _filePath, ctx) => {
|
||||
const dotPath = rawImportPath
|
||||
.replace(/\{[^}]*\}/g, '')
|
||||
.trim()
|
||||
.replace(/\._$/, '.*');
|
||||
|
||||
const exts = ['.scala', '.java'];
|
||||
|
||||
if (dotPath.endsWith('.*') || dotPath.endsWith('._')) {
|
||||
const files = resolveJvmWildcard(
|
||||
dotPath,
|
||||
ctx.normalizedFileList,
|
||||
ctx.allFileList,
|
||||
exts,
|
||||
ctx.index,
|
||||
);
|
||||
if (files.length > 0) {
|
||||
const pkgPath = dotPath.slice(0, -2).replace(/\./g, '/');
|
||||
return { kind: 'package', files, dirSuffix: pkgPath };
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
const resolved = resolveJvmMemberImport(
|
||||
dotPath,
|
||||
ctx.normalizedFileList,
|
||||
ctx.allFileList,
|
||||
exts,
|
||||
ctx.index,
|
||||
);
|
||||
return resolved ? { kind: 'files', files: [resolved] } : null;
|
||||
};
|
||||
|
||||
export const scalaImportConfig: ImportResolutionConfig = {
|
||||
language: SupportedLanguages.Scala,
|
||||
strategies: [scalaJvmStrategy, createStandardStrategy(SupportedLanguages.Scala)],
|
||||
};
|
||||
|
|
@ -47,6 +47,9 @@ export const EXTENSIONS = [
|
|||
'.swift',
|
||||
// Ruby
|
||||
'.rb',
|
||||
// Scala
|
||||
'.scala',
|
||||
'.sc',
|
||||
];
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import { rubyProvider } from './ruby.js';
|
|||
import { swiftProvider } from './swift.js';
|
||||
import { dartProvider } from './dart.js';
|
||||
import { vueProvider } from './vue.js';
|
||||
import { scalaProvider } from './scala.js';
|
||||
import { cobolProvider } from './cobol.js';
|
||||
|
||||
export const providers = {
|
||||
|
|
@ -41,6 +42,7 @@ export const providers = {
|
|||
[SupportedLanguages.Ruby]: rubyProvider,
|
||||
[SupportedLanguages.Swift]: swiftProvider,
|
||||
[SupportedLanguages.Dart]: dartProvider,
|
||||
[SupportedLanguages.Scala]: scalaProvider,
|
||||
[SupportedLanguages.Vue]: vueProvider,
|
||||
[SupportedLanguages.Cobol]: cobolProvider,
|
||||
} satisfies Record<SupportedLanguages, LanguageProvider>;
|
||||
|
|
|
|||
71
gitnexus/src/core/ingestion/languages/scala.ts
Normal file
71
gitnexus/src/core/ingestion/languages/scala.ts
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
/**
|
||||
* Scala Language Provider
|
||||
*
|
||||
* Assembles all Scala-specific ingestion capabilities into a single
|
||||
* LanguageProvider, following the Strategy pattern used by the pipeline.
|
||||
*
|
||||
* Key Scala traits:
|
||||
* - importSemantics: 'named' (explicit imports like Java/Kotlin)
|
||||
* - JVM package resolution reused from Java
|
||||
* - class/trait/object as first-class constructs
|
||||
* - Methods are function_definition inside template_body
|
||||
*/
|
||||
|
||||
import { SupportedLanguages } from 'gitnexus-shared';
|
||||
import { createClassExtractor } from '../class-extractors/generic.js';
|
||||
import { scalaClassConfig } from '../class-extractors/configs/scala.js';
|
||||
import { defineLanguage } from '../language-provider.js';
|
||||
import { typeConfig as scalaConfig } from '../type-extractors/scala.js';
|
||||
import { scalaExportChecker } from '../export-detection.js';
|
||||
import { createImportResolver } from '../import-resolvers/resolver-factory.js';
|
||||
import { scalaImportConfig } from '../import-resolvers/configs/scala.js';
|
||||
import { SCALA_QUERIES } from '../tree-sitter-queries.js';
|
||||
import { createFieldExtractor } from '../field-extractors/generic.js';
|
||||
import { scalaFieldConfig } from '../field-extractors/configs/scala.js';
|
||||
import { createMethodExtractor } from '../method-extractors/generic.js';
|
||||
import { scalaMethodConfig } from '../method-extractors/configs/scala.js';
|
||||
import { createVariableExtractor } from '../variable-extractors/generic.js';
|
||||
import { scalaVariableConfig } from '../variable-extractors/configs/scala.js';
|
||||
import { createCallExtractor } from '../call-extractors/generic.js';
|
||||
import { scalaCallConfig } from '../call-extractors/configs/scala.js';
|
||||
import { createHeritageExtractor } from '../heritage-extractors/generic.js';
|
||||
import { scalaHeritageConfig } from '../heritage-extractors/configs/scala.js';
|
||||
|
||||
const BUILT_INS: ReadonlySet<string> = new Set([
|
||||
'println',
|
||||
'print',
|
||||
'require',
|
||||
'assert',
|
||||
'assume',
|
||||
'throw',
|
||||
'sys',
|
||||
'classOf',
|
||||
'isInstanceOf',
|
||||
'asInstanceOf',
|
||||
'toString',
|
||||
'hashCode',
|
||||
'equals',
|
||||
'getClass',
|
||||
'synchronized',
|
||||
'wait',
|
||||
'notify',
|
||||
'notifyAll',
|
||||
]);
|
||||
|
||||
export const scalaProvider = defineLanguage({
|
||||
id: SupportedLanguages.Scala,
|
||||
extensions: ['.scala', '.sc'],
|
||||
treeSitterQueries: SCALA_QUERIES,
|
||||
typeConfig: scalaConfig,
|
||||
exportChecker: scalaExportChecker,
|
||||
importResolver: createImportResolver(scalaImportConfig),
|
||||
importSemantics: 'named',
|
||||
mroStrategy: 'implements-split',
|
||||
callExtractor: createCallExtractor(scalaCallConfig),
|
||||
fieldExtractor: createFieldExtractor(scalaFieldConfig),
|
||||
methodExtractor: createMethodExtractor(scalaMethodConfig),
|
||||
variableExtractor: createVariableExtractor(scalaVariableConfig),
|
||||
classExtractor: createClassExtractor(scalaClassConfig),
|
||||
heritageExtractor: createHeritageExtractor(scalaHeritageConfig),
|
||||
builtInNames: BUILT_INS,
|
||||
});
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
import { SupportedLanguages } from 'gitnexus-shared';
|
||||
import type { MethodExtractionConfig, ParameterInfo } from '../../method-types.js';
|
||||
import { extractSimpleTypeName } from '../../type-extractors/shared.js';
|
||||
import type { SyntaxNode } from '../../utils/ast-helpers.js';
|
||||
|
||||
function extractScalaName(node: SyntaxNode): string | undefined {
|
||||
return node.childForFieldName('name')?.text;
|
||||
}
|
||||
|
||||
function extractScalaReturnType(node: SyntaxNode): string | undefined {
|
||||
const retType = node.childForFieldName('return_type');
|
||||
if (retType) return extractSimpleTypeName(retType) ?? retType.text?.trim();
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function extractScalaParameters(node: SyntaxNode): ParameterInfo[] {
|
||||
const params: ParameterInfo[] = [];
|
||||
for (let i = 0; i < node.namedChildCount; i++) {
|
||||
const child = node.namedChild(i);
|
||||
if (child?.type !== 'parameters') continue;
|
||||
for (let j = 0; j < child.namedChildCount; j++) {
|
||||
const param = child.namedChild(j);
|
||||
if (param?.type !== 'parameter') continue;
|
||||
const nameNode = param.childForFieldName('name');
|
||||
const typeNode = param.childForFieldName('type');
|
||||
const typeName = typeNode
|
||||
? (extractSimpleTypeName(typeNode) ?? typeNode.text?.trim() ?? null)
|
||||
: null;
|
||||
params.push({
|
||||
name: nameNode?.text ?? `_${j}`,
|
||||
type: typeName,
|
||||
rawType: typeNode?.text?.trim() ?? null,
|
||||
isOptional: false,
|
||||
isVariadic: param.text?.includes('*') ?? false,
|
||||
});
|
||||
}
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
function extractScalaVisibility(node: SyntaxNode): 'public' | 'private' | 'protected' {
|
||||
for (let i = 0; i < node.childCount; i++) {
|
||||
const child = node.child(i);
|
||||
if (child?.type === 'modifiers') {
|
||||
if (child.text?.includes('private')) return 'private';
|
||||
if (child.text?.includes('protected')) return 'protected';
|
||||
}
|
||||
}
|
||||
return 'public';
|
||||
}
|
||||
|
||||
export const scalaMethodConfig: MethodExtractionConfig = {
|
||||
language: SupportedLanguages.Scala,
|
||||
typeDeclarationNodes: [
|
||||
'class_definition',
|
||||
'object_definition',
|
||||
'trait_definition',
|
||||
'enum_definition',
|
||||
],
|
||||
methodNodeTypes: ['function_definition', 'function_declaration'],
|
||||
bodyNodeTypes: ['template_body'],
|
||||
|
||||
extractName: extractScalaName,
|
||||
extractReturnType: extractScalaReturnType,
|
||||
extractParameters: extractScalaParameters,
|
||||
extractVisibility: extractScalaVisibility,
|
||||
|
||||
isStatic(_node) {
|
||||
return false;
|
||||
},
|
||||
|
||||
isAbstract(node) {
|
||||
return node.type === 'function_declaration';
|
||||
},
|
||||
|
||||
isFinal(node) {
|
||||
for (let i = 0; i < node.childCount; i++) {
|
||||
const child = node.child(i);
|
||||
if (child?.type === 'modifiers' && child.text?.includes('final')) return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
};
|
||||
|
|
@ -1373,6 +1373,62 @@ export const DART_QUERIES = `
|
|||
(type_identifier) @heritage.trait))) @heritage
|
||||
`;
|
||||
|
||||
export const SCALA_QUERIES = `
|
||||
; Classes
|
||||
(class_definition name: (identifier) @name) @definition.class
|
||||
|
||||
; Traits
|
||||
(trait_definition name: (identifier) @name) @definition.trait
|
||||
|
||||
; Objects (companion or standalone)
|
||||
(object_definition name: (identifier) @name) @definition.class
|
||||
|
||||
; Enums (Scala 3)
|
||||
(enum_definition name: (identifier) @name) @definition.enum
|
||||
|
||||
; Methods (def inside class/trait/object)
|
||||
(function_definition name: (identifier) @name) @definition.method
|
||||
|
||||
; Abstract method declarations
|
||||
(function_declaration name: (identifier) @name) @definition.method
|
||||
|
||||
; Imports — capture the whole declaration (path extracted from text)
|
||||
(import_declaration) @import
|
||||
|
||||
; Calls — field access: obj.method(...)
|
||||
(call_expression
|
||||
function: (field_expression
|
||||
field: (identifier) @call.name)) @call
|
||||
|
||||
; Calls — constructor: new Foo(...)
|
||||
(instance_expression (type_identifier) @call.name) @call
|
||||
|
||||
; Calls — free function (rare in Scala, but possible)
|
||||
(call_expression function: (identifier) @call.name) @call
|
||||
|
||||
; Val declarations
|
||||
(val_definition pattern: (identifier) @name) @definition.const
|
||||
|
||||
; Var declarations
|
||||
(var_definition pattern: (identifier) @name) @definition.variable
|
||||
|
||||
; Heritage — extends
|
||||
(class_definition
|
||||
name: (identifier) @heritage.class
|
||||
(extends_clause (type_identifier) @heritage.extends)) @heritage
|
||||
|
||||
; Heritage — trait extends trait
|
||||
(trait_definition
|
||||
name: (identifier) @heritage.class
|
||||
(extends_clause (type_identifier) @heritage.extends)) @heritage
|
||||
|
||||
; Write access: obj.field = value
|
||||
(assignment_expression
|
||||
left: (field_expression
|
||||
value: (_) @assignment.receiver
|
||||
field: (identifier) @assignment.property)) @assignment
|
||||
`;
|
||||
|
||||
import { SupportedLanguages } from 'gitnexus-shared';
|
||||
|
||||
export const LANGUAGE_QUERIES: Record<SupportedLanguages, string> = {
|
||||
|
|
@ -1390,6 +1446,7 @@ export const LANGUAGE_QUERIES: Record<SupportedLanguages, string> = {
|
|||
[SupportedLanguages.Ruby]: RUBY_QUERIES,
|
||||
[SupportedLanguages.Swift]: SWIFT_QUERIES,
|
||||
[SupportedLanguages.Dart]: DART_QUERIES,
|
||||
[SupportedLanguages.Scala]: SCALA_QUERIES,
|
||||
[SupportedLanguages.Vue]: TYPESCRIPT_QUERIES, // Vue <script> blocks are parsed as TypeScript
|
||||
[SupportedLanguages.Cobol]: '', // Standalone regex processor — no tree-sitter queries
|
||||
};
|
||||
|
|
|
|||
68
gitnexus/src/core/ingestion/type-extractors/scala.ts
Normal file
68
gitnexus/src/core/ingestion/type-extractors/scala.ts
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
import type { SyntaxNode } from '../utils/ast-helpers.js';
|
||||
import type {
|
||||
ConstructorBindingScanner,
|
||||
LanguageTypeConfig,
|
||||
ParameterExtractor,
|
||||
TypeBindingExtractor,
|
||||
} from './types.js';
|
||||
import { extractSimpleTypeName, extractVarName } from './shared.js';
|
||||
|
||||
const DECLARATION_NODE_TYPES: ReadonlySet<string> = new Set([
|
||||
'val_definition',
|
||||
'var_definition',
|
||||
]);
|
||||
|
||||
const extractDeclaration: TypeBindingExtractor = (
|
||||
node: SyntaxNode,
|
||||
env: Map<string, string>,
|
||||
): void => {
|
||||
const pattern = node.childForFieldName('pattern');
|
||||
const typeNode = node.childForFieldName('type');
|
||||
if (!pattern || !typeNode) return;
|
||||
const varName = extractVarName(pattern);
|
||||
const typeName = extractSimpleTypeName(typeNode);
|
||||
if (varName && typeName) env.set(varName, typeName);
|
||||
};
|
||||
|
||||
const extractParameter: ParameterExtractor = (
|
||||
node: SyntaxNode,
|
||||
env: Map<string, string>,
|
||||
): void => {
|
||||
if (node.type !== 'parameter' && node.type !== 'class_parameter') return;
|
||||
const nameNode = node.childForFieldName('name');
|
||||
const typeNode = node.childForFieldName('type');
|
||||
if (!nameNode || !typeNode) return;
|
||||
const varName = extractVarName(nameNode);
|
||||
const typeName = extractSimpleTypeName(typeNode);
|
||||
if (varName && typeName) env.set(varName, typeName);
|
||||
};
|
||||
|
||||
const scanConstructorBinding: ConstructorBindingScanner = (node) => {
|
||||
if (node.type !== 'val_definition') return undefined;
|
||||
const pattern = node.childForFieldName('pattern');
|
||||
const value = node.childForFieldName('value');
|
||||
if (!pattern || !value) return undefined;
|
||||
const varName = extractVarName(pattern);
|
||||
if (!varName) return undefined;
|
||||
|
||||
if (value.type === 'call_expression') {
|
||||
const funcNode = value.childForFieldName('function');
|
||||
if (funcNode) {
|
||||
const calleeName = funcNode.text;
|
||||
if (calleeName) return { varName, calleeName };
|
||||
}
|
||||
}
|
||||
if (value.type === 'instance_expression') {
|
||||
const typeNode = value.namedChildren.find((c: SyntaxNode) => c.type === 'type_identifier');
|
||||
if (typeNode) return { varName, calleeName: typeNode.text };
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
export const typeConfig: LanguageTypeConfig = {
|
||||
declarationNodeTypes: DECLARATION_NODE_TYPES,
|
||||
forLoopNodeTypes: new Set(['for_expression']),
|
||||
extractDeclaration,
|
||||
extractParameter,
|
||||
scanConstructorBinding,
|
||||
};
|
||||
|
|
@ -95,6 +95,8 @@ export const FUNCTION_NODE_TYPES = new Set([
|
|||
// Dart
|
||||
'function_signature',
|
||||
'method_signature',
|
||||
// Scala
|
||||
'function_definition',
|
||||
]);
|
||||
|
||||
/**
|
||||
|
|
@ -132,6 +134,10 @@ export const CLASS_CONTAINER_TYPES = new Set([
|
|||
// Kotlin
|
||||
'object_declaration',
|
||||
'companion_object',
|
||||
// Scala
|
||||
'object_definition',
|
||||
'trait_definition',
|
||||
'enum_definition',
|
||||
]);
|
||||
|
||||
export const CONTAINER_TYPE_TO_LABEL: Record<string, string> = {
|
||||
|
|
@ -164,6 +170,9 @@ export const CONTAINER_TYPE_TO_LABEL: Record<string, string> = {
|
|||
singleton_class: 'Class', // Ruby: class << self inherits enclosing class name
|
||||
object_declaration: 'Class',
|
||||
companion_object: 'Class',
|
||||
object_definition: 'Class',
|
||||
trait_definition: 'Trait',
|
||||
enum_definition: 'Enum',
|
||||
};
|
||||
|
||||
/** Return the first matching ancestor unless a boundary ancestor is reached first. */
|
||||
|
|
|
|||
|
|
@ -0,0 +1,49 @@
|
|||
import { SupportedLanguages } from 'gitnexus-shared';
|
||||
import type { VariableExtractionConfig, VariableVisibility } from '../../variable-types.js';
|
||||
import { extractSimpleTypeName } from '../../type-extractors/shared.js';
|
||||
import type { SyntaxNode } from '../../utils/ast-helpers.js';
|
||||
|
||||
function extractScalaVarName(node: SyntaxNode): string | undefined {
|
||||
const pattern = node.childForFieldName('pattern');
|
||||
if (pattern?.type === 'identifier') return pattern.text;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function extractScalaVarType(node: SyntaxNode): string | undefined {
|
||||
const typeNode = node.childForFieldName('type');
|
||||
if (typeNode) return extractSimpleTypeName(typeNode) ?? typeNode.text?.trim();
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export const scalaVariableConfig: VariableExtractionConfig = {
|
||||
language: SupportedLanguages.Scala,
|
||||
constNodeTypes: ['val_definition'],
|
||||
staticNodeTypes: [],
|
||||
variableNodeTypes: ['var_definition'],
|
||||
|
||||
extractName: extractScalaVarName,
|
||||
extractType: extractScalaVarType,
|
||||
|
||||
extractVisibility(node): VariableVisibility {
|
||||
for (let i = 0; i < node.childCount; i++) {
|
||||
const child = node.child(i);
|
||||
if (child?.type === 'modifiers') {
|
||||
if (child.text?.includes('private')) return 'private';
|
||||
if (child.text?.includes('protected')) return 'protected';
|
||||
}
|
||||
}
|
||||
return 'public';
|
||||
},
|
||||
|
||||
isConst(node) {
|
||||
return node.type === 'val_definition';
|
||||
},
|
||||
|
||||
isStatic(_node) {
|
||||
return false;
|
||||
},
|
||||
|
||||
isMutable(node) {
|
||||
return node.type === 'var_definition';
|
||||
},
|
||||
};
|
||||
|
|
@ -153,6 +153,11 @@ const SOURCES: Record<string, GrammarSource> = {
|
|||
'Likely cause: native compile failed at install (missing python3/make/g++). ' +
|
||||
`See ${ISSUES_URL}/1125.`,
|
||||
},
|
||||
[SupportedLanguages.Scala]: {
|
||||
load: () => _require('tree-sitter-scala'),
|
||||
unavailableNote:
|
||||
'Scala parsing requires `tree-sitter-scala`. Check the install and native binding.',
|
||||
},
|
||||
[SupportedLanguages.Kotlin]: {
|
||||
load: () => _require('tree-sitter-kotlin'),
|
||||
optional: true,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue