vibe-coder-mcp
Version:
Production-ready MCP server with complete agent integration, multi-transport support, and comprehensive development automation tools for AI-assisted workflows.
367 lines (366 loc) • 14.8 kB
JavaScript
import { BaseLanguageHandler } from './base.js';
import { getNodeText } from '../astAnalyzer.js';
import logger from '../../../logger.js';
export class CSharpHandler extends BaseLanguageHandler {
getFunctionQueryPatterns() {
return [
'method_declaration',
'constructor_declaration',
'local_function_statement',
'anonymous_method_expression',
'lambda_expression'
];
}
getClassQueryPatterns() {
return [
'class_declaration',
'interface_declaration',
'struct_declaration',
'enum_declaration'
];
}
getImportQueryPatterns() {
return [
'using_directive'
];
}
extractFunctionName(node, sourceCode, _options) {
try {
if (node.type === 'method_declaration') {
const nameNode = node.childForFieldName('name');
if (nameNode) {
const name = getNodeText(nameNode, sourceCode);
const attributes = this.getMethodAttributes(node, sourceCode);
if (attributes.includes('[HttpGet]') ||
attributes.includes('[HttpPost]') ||
attributes.includes('[Route]') ||
attributes.includes('[ApiController]')) {
return `api_${name}`;
}
if (attributes.includes('[Fact]') ||
attributes.includes('[Theory]') ||
attributes.includes('[Test]')) {
return `test_${name}`;
}
return name;
}
}
if (node.type === 'constructor_declaration') {
const className = this.findClassName(node, sourceCode);
return `${className}_Constructor`;
}
if (node.type === 'local_function_statement') {
const nameNode = node.childForFieldName('name');
if (nameNode) {
return `local_${getNodeText(nameNode, sourceCode)}`;
}
}
if (node.type === 'anonymous_method_expression') {
if (node.parent?.type === 'assignment_expression') {
const leftNode = node.parent.childForFieldName('left');
if (leftNode) {
return getNodeText(leftNode, sourceCode);
}
}
if (node.parent?.type === 'assignment_expression' &&
node.parent.parent?.type === 'expression_statement' &&
node.parent.parent.previousNamedSibling?.type === 'event_field_declaration') {
const eventNode = node.parent.parent.previousNamedSibling.childForFieldName('declarator');
if (eventNode) {
const eventName = getNodeText(eventNode, sourceCode);
return `${eventName}Handler`;
}
}
return 'anonymousMethod';
}
if (node.type === 'lambda_expression') {
if (node.parent?.type === 'variable_declarator') {
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 === 'invocation_expression') {
const methodNode = node.parent.parent.parent.childForFieldName('name');
if (methodNode) {
const methodName = getNodeText(methodNode, sourceCode);
if (['Select', 'Where', 'OrderBy', 'GroupBy', 'Join', 'ForEach'].includes(methodName)) {
return `linq_${methodName}`;
}
}
}
if (node.parent?.type === 'assignment_expression' &&
node.parent.childForFieldName('left')?.text.includes('+=')) {
const leftNode = node.parent.childForFieldName('left');
if (leftNode) {
const eventName = getNodeText(leftNode, sourceCode).split('+=')[0].trim();
return `${eventName}Handler`;
}
}
return 'lambda';
}
return 'anonymous';
}
catch (error) {
logger.warn({ err: error, nodeType: node.type }, 'Error extracting C# function name');
return 'anonymous';
}
}
findClassName(node, sourceCode) {
try {
let current = node.parent;
while (current && current.type !== 'class_declaration') {
current = current.parent;
}
if (current) {
const nameNode = current.childForFieldName('name');
if (nameNode) {
return getNodeText(nameNode, sourceCode);
}
}
return 'Unknown';
}
catch (error) {
logger.warn({ err: error, nodeType: node.type }, 'Error finding C# class name');
return 'Unknown';
}
}
getMethodAttributes(node, sourceCode) {
try {
const attributes = [];
const current = node;
let prev = current.previousNamedSibling;
while (prev && prev.type === 'attribute_list') {
attributes.push(getNodeText(prev, sourceCode));
prev = prev.previousNamedSibling;
}
return attributes;
}
catch (error) {
logger.warn({ err: error, nodeType: node.type }, 'Error getting C# method attributes');
return [];
}
}
extractClassName(node, sourceCode) {
try {
if (node.type === 'class_declaration' ||
node.type === 'interface_declaration' ||
node.type === 'struct_declaration' ||
node.type === 'enum_declaration') {
const nameNode = node.childForFieldName('name');
if (nameNode) {
return getNodeText(nameNode, sourceCode);
}
}
return 'AnonymousClass';
}
catch (error) {
logger.warn({ err: error, nodeType: node.type }, 'Error extracting C# class name');
return 'AnonymousClass';
}
}
extractParentClass(node, sourceCode) {
try {
if (node.type === 'class_declaration') {
const baseListNode = node.childForFieldName('base_list');
if (baseListNode) {
const baseTypes = baseListNode.descendantsOfType('base_type');
for (const baseType of baseTypes) {
const typeNode = baseType.childForFieldName('type');
if (typeNode) {
const typeName = getNodeText(typeNode, sourceCode);
if (!typeName.startsWith('I') || typeName.length <= 1) {
return typeName;
}
}
}
}
}
return undefined;
}
catch (error) {
logger.warn({ err: error, nodeType: node.type }, 'Error extracting C# parent class');
return undefined;
}
}
extractImplementedInterfaces(node, sourceCode) {
try {
if (node.type === 'class_declaration' || node.type === 'struct_declaration') {
const interfaces = [];
const baseListNode = node.childForFieldName('base_list');
if (baseListNode) {
const baseTypes = baseListNode.descendantsOfType('base_type');
for (const baseType of baseTypes) {
const typeNode = baseType.childForFieldName('type');
if (typeNode) {
const typeName = getNodeText(typeNode, sourceCode);
if (typeName.startsWith('I') && typeName.length > 1) {
interfaces.push(typeName);
}
}
}
}
return interfaces.length > 0 ? interfaces : undefined;
}
return undefined;
}
catch (error) {
logger.warn({ err: error, nodeType: node.type }, 'Error extracting C# implemented interfaces');
return undefined;
}
}
extractImportPath(node, sourceCode) {
try {
if (node.type === 'using_directive') {
const nameNode = node.childForFieldName('name');
if (nameNode) {
return getNodeText(nameNode, sourceCode);
}
}
return 'unknown';
}
catch (error) {
logger.warn({ err: error, nodeType: node.type }, 'Error extracting C# import path');
return 'unknown';
}
}
extractImportedItems(node, sourceCode) {
try {
if (node.type === 'using_directive') {
const nameNode = node.childForFieldName('name');
if (nameNode) {
const fullPath = getNodeText(nameNode, sourceCode);
const parts = fullPath.split('.');
const name = parts[parts.length - 1];
const isStatic = this.isStaticUsing(node, sourceCode);
const aliasNode = node.childForFieldName('alias');
const alias = aliasNode ? getNodeText(aliasNode, sourceCode) : undefined;
const isGlobal = this.isGlobalUsing(node, sourceCode);
return [{
name: alias || name,
path: fullPath,
alias: alias,
isDefault: false,
isNamespace: true,
nodeText: node.text,
isStatic,
isGlobal
}];
}
}
return undefined;
}
catch (error) {
logger.warn({ err: error, nodeType: node.type }, 'Error extracting C# imported items');
return undefined;
}
}
isStaticUsing(node, _sourceCode) {
try {
const staticNode = node.childForFieldName('static');
return staticNode !== null;
}
catch (error) {
logger.warn({ err: error, nodeType: node.type }, 'Error checking if C# using is static');
return false;
}
}
isGlobalUsing(node, _sourceCode) {
try {
const globalNode = node.childForFieldName('global');
return globalNode !== null;
}
catch (error) {
logger.warn({ err: error, nodeType: node.type }, 'Error checking if C# using is global');
return false;
}
}
extractFunctionComment(node, _sourceCode) {
try {
let prev = node.previousNamedSibling;
while (prev && prev.type !== 'comment') {
prev = prev.previousNamedSibling;
}
if (prev && prev.type === 'comment' && prev.text.startsWith('///')) {
return this.parseXmlDocComment(prev.text);
}
return undefined;
}
catch (error) {
logger.warn({ err: error, nodeType: node.type }, 'Error extracting C# function comment');
return undefined;
}
}
extractClassComment(node, _sourceCode) {
try {
let prev = node.previousNamedSibling;
while (prev && prev.type !== 'comment') {
prev = prev.previousNamedSibling;
}
if (prev && prev.type === 'comment' && prev.text.startsWith('///')) {
return this.parseXmlDocComment(prev.text);
}
return undefined;
}
catch (error) {
logger.warn({ err: error, nodeType: node.type }, 'Error extracting C# class comment');
return undefined;
}
}
parseXmlDocComment(comment) {
try {
const lines = comment.split('\n')
.map(line => line.trim().replace(/^\/\/\/\s*/, ''));
const summaryStart = lines.findIndex(line => line.includes('<summary>'));
const summaryEnd = lines.findIndex(line => line.includes('</summary>'));
if (summaryStart !== -1 && summaryEnd !== -1 && summaryEnd > summaryStart) {
const summaryLines = lines.slice(summaryStart + 1, summaryEnd);
return summaryLines
.map(line => line.trim())
.join(' ')
.replace(/<[^>]+>/g, '')
.trim();
}
return lines
.map(line => line.trim())
.join(' ')
.replace(/<[^>]+>/g, '')
.trim();
}
catch (error) {
logger.warn({ err: error }, 'Error parsing C# XML documentation comment');
return comment;
}
}
detectFramework(sourceCode) {
try {
if (sourceCode.includes('Microsoft.AspNetCore') ||
sourceCode.includes('[ApiController]') ||
sourceCode.includes('IActionResult')) {
return 'aspnetcore';
}
if (sourceCode.includes('System.Windows') ||
sourceCode.includes('Window') ||
sourceCode.includes('UserControl')) {
return 'wpf';
}
if (sourceCode.includes('Microsoft.EntityFrameworkCore') ||
sourceCode.includes('DbContext') ||
sourceCode.includes('DbSet<')) {
return 'entityframework';
}
if (sourceCode.includes('Xunit') ||
sourceCode.includes('[Fact]') ||
sourceCode.includes('[Theory]')) {
return 'xunit';
}
return null;
}
catch (error) {
logger.warn({ err: error }, 'Error detecting C# framework');
return null;
}
}
}