vibe-coder-mcp
Version:
Production-ready MCP server with complete agent integration, multi-transport support, and comprehensive development automation tools for AI-assisted workflows.
350 lines (349 loc) • 14.4 kB
JavaScript
import { BaseLanguageHandler } from './base.js';
import { getNodeText } from '../astAnalyzer.js';
import logger from '../../../logger.js';
export class ScalaHandler extends BaseLanguageHandler {
getFunctionQueryPatterns() {
return [
'function_definition',
'method_definition',
'val_definition',
'var_definition',
'anonymous_function'
];
}
getClassQueryPatterns() {
return [
'class_definition',
'object_definition',
'trait_definition',
'case_class_definition'
];
}
getImportQueryPatterns() {
return [
'import_declaration',
'package_declaration'
];
}
extractFunctionName(node, sourceCode, _options) {
try {
if (node.type === 'function_definition' || node.type === 'method_definition') {
const nameNode = node.childForFieldName('name');
if (nameNode) {
const name = getNodeText(nameNode, sourceCode);
if (name.startsWith('test') || this.hasAnnotation(node, 'Test')) {
return `test_${name}`;
}
if (name === 'apply') {
return 'factory_apply';
}
else if (name === 'unapply') {
return 'extractor_unapply';
}
return name;
}
}
if (node.type === 'val_definition' || node.type === 'var_definition') {
const nameNode = node.childForFieldName('name');
const valueNode = node.childForFieldName('value');
if (nameNode && valueNode &&
(valueNode.type === 'anonymous_function' ||
valueNode.text.includes('=>'))) {
return getNodeText(nameNode, sourceCode);
}
}
if (node.type === 'anonymous_function') {
if (node.parent?.type === 'val_definition' || node.parent?.type === 'var_definition') {
const nameNode = node.parent.childForFieldName('name');
if (nameNode) {
return getNodeText(nameNode, sourceCode);
}
}
if (node.parent?.type === 'argument' &&
node.parent.parent?.type === 'argument_list' &&
node.parent.parent.parent?.type === 'call_expression') {
const funcNode = node.parent.parent.parent.childForFieldName('function');
if (funcNode) {
const funcName = getNodeText(funcNode, sourceCode);
if (['map', 'flatMap', 'filter', 'foreach', 'fold', 'reduce'].includes(funcName)) {
return `${funcName}_function`;
}
}
}
return 'anonymous_function';
}
return 'anonymous';
}
catch (error) {
logger.warn({ err: error, nodeType: node.type }, 'Error extracting Scala function name');
return 'anonymous';
}
}
hasAnnotation(node, annotationName) {
try {
const annotationsNode = node.childForFieldName('annotations');
if (!annotationsNode)
return false;
for (let i = 0; i < annotationsNode.childCount; i++) {
const annotation = annotationsNode.child(i);
if (annotation?.text.includes(`@${annotationName}`)) {
return true;
}
}
return false;
}
catch (error) {
logger.warn({ err: error, nodeType: node.type }, 'Error checking Scala annotation');
return false;
}
}
extractClassName(node, sourceCode) {
try {
if (node.type === 'class_definition' ||
node.type === 'trait_definition' ||
node.type === 'case_class_definition') {
const nameNode = node.childForFieldName('name');
if (nameNode) {
return getNodeText(nameNode, sourceCode);
}
}
else if (node.type === 'object_definition') {
const nameNode = node.childForFieldName('name');
if (nameNode) {
return `Object_${getNodeText(nameNode, sourceCode)}`;
}
}
return 'AnonymousClass';
}
catch (error) {
logger.warn({ err: error, nodeType: node.type }, 'Error extracting Scala class name');
return 'AnonymousClass';
}
}
extractParentClass(node, sourceCode) {
try {
if (node.type === 'class_definition' ||
node.type === 'case_class_definition' ||
node.type === 'object_definition') {
const extendsNode = node.childForFieldName('extends_clause');
if (extendsNode) {
const typeNode = extendsNode.childForFieldName('type');
if (typeNode) {
return getNodeText(typeNode, sourceCode);
}
}
}
return undefined;
}
catch (error) {
logger.warn({ err: error, nodeType: node.type }, 'Error extracting Scala parent class');
return undefined;
}
}
extractImplementedInterfaces(node, sourceCode) {
try {
if (node.type === 'class_definition' ||
node.type === 'case_class_definition' ||
node.type === 'object_definition') {
const withClausesNode = node.childForFieldName('with_clauses');
if (withClausesNode) {
const traits = [];
for (let i = 0; i < withClausesNode.childCount; i++) {
const withClause = withClausesNode.child(i);
if (withClause?.type === 'with_clause') {
const typeNode = withClause.childForFieldName('type');
if (typeNode) {
traits.push(getNodeText(typeNode, sourceCode));
}
}
}
return traits.length > 0 ? traits : undefined;
}
}
return undefined;
}
catch (error) {
logger.warn({ err: error, nodeType: node.type }, 'Error extracting Scala implemented interfaces');
return undefined;
}
}
extractImportPath(node, sourceCode) {
try {
if (node.type === 'import_declaration') {
const importeeNode = node.childForFieldName('importee');
if (importeeNode) {
return getNodeText(importeeNode, sourceCode);
}
}
else if (node.type === 'package_declaration') {
const refNode = node.childForFieldName('ref');
if (refNode) {
return getNodeText(refNode, sourceCode);
}
}
return 'unknown';
}
catch (error) {
logger.warn({ err: error, nodeType: node.type }, 'Error extracting Scala import path');
return 'unknown';
}
}
extractImportedItems(node, sourceCode) {
try {
if (node.type === 'import_declaration') {
const importeeNode = node.childForFieldName('importee');
if (importeeNode) {
const fullPath = getNodeText(importeeNode, sourceCode);
if (fullPath.endsWith('._')) {
const basePath = fullPath.substring(0, fullPath.length - 2);
return [{
name: '*',
path: basePath,
isDefault: false,
isNamespace: true,
nodeText: node.text,
isWildcardImport: true
}];
}
else if (fullPath.includes('{') && fullPath.includes('}')) {
const basePath = fullPath.substring(0, fullPath.indexOf('{'));
const selectorsText = fullPath.substring(fullPath.indexOf('{') + 1, fullPath.lastIndexOf('}'));
const selectors = selectorsText.split(',').map(s => s.trim());
const items = [];
for (const selector of selectors) {
if (selector.includes('=>')) {
const [originalName, alias] = selector.split('=>').map(s => s.trim());
items.push({
name: originalName,
path: basePath + originalName,
alias: alias,
isDefault: false,
isNamespace: false,
nodeText: selector,
isSelectorImport: true
});
}
else {
items.push({
name: selector,
path: basePath + selector,
isDefault: false,
isNamespace: false,
nodeText: selector,
isSelectorImport: true
});
}
}
return items.length > 0 ? items : undefined;
}
else {
const parts = fullPath.split('.');
const name = parts[parts.length - 1];
return [{
name: name,
path: fullPath,
isDefault: false,
isNamespace: false,
nodeText: node.text
}];
}
}
}
else if (node.type === 'package_declaration') {
const refNode = node.childForFieldName('ref');
if (refNode) {
const packagePath = getNodeText(refNode, sourceCode);
return [{
name: packagePath,
path: packagePath,
isDefault: false,
isNamespace: true,
nodeText: node.text,
isPackageDeclaration: true
}];
}
}
return undefined;
}
catch (error) {
logger.warn({ err: error, nodeType: node.type }, 'Error extracting Scala imported items');
return undefined;
}
}
extractFunctionComment(node, _sourceCode) {
try {
const current = node;
let prev = current.previousNamedSibling;
while (prev && prev.type !== 'comment') {
prev = prev.previousNamedSibling;
}
if (prev && prev.type === 'comment' && prev.text.startsWith('/**')) {
return this.parseScaladocComment(prev.text);
}
return undefined;
}
catch (error) {
logger.warn({ err: error, nodeType: node.type }, 'Error extracting Scala function comment');
return undefined;
}
}
extractClassComment(node, _sourceCode) {
try {
const current = node;
let prev = current.previousNamedSibling;
while (prev && prev.type !== 'comment') {
prev = prev.previousNamedSibling;
}
if (prev && prev.type === 'comment' && prev.text.startsWith('/**')) {
return this.parseScaladocComment(prev.text);
}
return undefined;
}
catch (error) {
logger.warn({ err: error, nodeType: node.type }, 'Error extracting Scala class comment');
return undefined;
}
}
parseScaladocComment(comment) {
try {
const text = comment.substring(3, comment.length - 2);
const lines = text.split('\n')
.map(line => line.trim().replace(/^\*\s*/, ''))
.filter(line => !line.startsWith('@'));
return lines.join(' ').trim();
}
catch (error) {
logger.warn({ err: error }, 'Error parsing Scaladoc comment');
return comment;
}
}
detectFramework(sourceCode) {
try {
if (sourceCode.includes('import akka.') ||
sourceCode.includes('extends Actor') ||
sourceCode.includes('ActorSystem')) {
return 'akka';
}
if (sourceCode.includes('import play.') ||
sourceCode.includes('extends Controller') ||
sourceCode.includes('Action {')) {
return 'play';
}
if (sourceCode.includes('import org.apache.spark') ||
sourceCode.includes('SparkContext') ||
sourceCode.includes('SparkSession')) {
return 'spark';
}
if (sourceCode.includes('import cats.') ||
sourceCode.includes('import cats.effect.') ||
sourceCode.includes('extends IOApp')) {
return 'cats';
}
return null;
}
catch (error) {
logger.warn({ err: error }, 'Error detecting Scala framework');
return null;
}
}
}