UNPKG

vibe-coder-mcp

Version:

Production-ready MCP server with complete agent integration, multi-transport support, and comprehensive development automation tools for AI-assisted workflows.

505 lines (504 loc) 20.5 kB
import { BaseLanguageHandler } from './base.js'; import { getNodeText } from '../astAnalyzer.js'; import logger from '../../../logger.js'; import { ImportResolverFactory } from '../importResolvers/importResolverFactory.js'; import * as path from 'path'; import * as fs from 'fs'; export class CppHandler extends BaseLanguageHandler { getFunctionQueryPatterns() { return [ 'function_definition', 'method_definition', 'lambda_expression', 'function_declarator' ]; } getClassQueryPatterns() { return [ 'class_specifier', 'struct_specifier', 'enum_specifier', 'namespace_definition' ]; } getImportQueryPatterns() { return [ 'include_directive', 'using_declaration', 'using_directive' ]; } extractFunctionName(node, sourceCode, _options) { try { if (node.type === 'function_definition') { const declaratorNode = node.childForFieldName('declarator'); if (declaratorNode) { if (declaratorNode.type === 'function_declarator') { const declaratorName = this.extractFunctionDeclaratorName(declaratorNode, sourceCode); if (declaratorName) { return declaratorName; } } } } if (node.type === 'method_definition') { const declaratorNode = node.childForFieldName('declarator'); if (declaratorNode) { if (declaratorNode.type === 'function_declarator') { const declaratorName = this.extractFunctionDeclaratorName(declaratorNode, sourceCode); if (declaratorName) { if (this.isConstructor(declaratorName, node, sourceCode)) { return 'constructor'; } else if (this.isDestructor(declaratorName, node, sourceCode)) { return 'destructor'; } return declaratorName; } } } } if (node.type === 'function_declarator') { const declaratorName = this.extractFunctionDeclaratorName(node, sourceCode); if (declaratorName) { return declaratorName; } } if (node.type === 'lambda_expression') { if (node.parent?.type === 'init_declarator') { const declaratorNode = node.parent.childForFieldName('declarator'); if (declaratorNode) { return getNodeText(declaratorNode, sourceCode); } } if (node.parent?.type === 'argument_list' && node.parent.parent?.type === 'call_expression') { const funcNode = node.parent.parent.childForFieldName('function'); if (funcNode) { const funcName = getNodeText(funcNode, sourceCode); if (['for_each', 'transform', 'find_if', 'sort'].includes(funcName)) { return `${funcName}_lambda`; } } } return 'lambda'; } return 'anonymous'; } catch (error) { logger.warn({ err: error, nodeType: node.type }, 'Error extracting C/C++ function name'); return 'anonymous'; } } extractFunctionDeclaratorName(node, sourceCode) { try { const declaratorNode = node.childForFieldName('declarator'); if (declaratorNode) { if (declaratorNode.type === 'identifier') { return getNodeText(declaratorNode, sourceCode); } if (declaratorNode.type === 'qualified_identifier') { return getNodeText(declaratorNode, sourceCode); } if (declaratorNode.type === 'field_expression') { const fieldNode = declaratorNode.childForFieldName('field'); if (fieldNode) { return getNodeText(fieldNode, sourceCode); } } } return null; } catch (error) { logger.warn({ err: error, nodeType: node.type }, 'Error extracting C/C++ function declarator name'); return null; } } isConstructor(methodName, node, sourceCode) { try { let current = node.parent; while (current && current.type !== 'class_specifier') { current = current.parent; } if (current) { const nameNode = current.childForFieldName('name'); if (nameNode) { const className = getNodeText(nameNode, sourceCode); return methodName === className || methodName.endsWith(`::${className}`); } } return false; } catch (error) { logger.warn({ err: error, nodeType: node.type }, 'Error checking if C++ method is constructor'); return false; } } isDestructor(methodName, node, sourceCode) { try { let current = node.parent; while (current && current.type !== 'class_specifier') { current = current.parent; } if (current) { const nameNode = current.childForFieldName('name'); if (nameNode) { const className = getNodeText(nameNode, sourceCode); return methodName === `~${className}` || methodName.endsWith(`::~${className}`); } } return false; } catch (error) { logger.warn({ err: error, nodeType: node.type }, 'Error checking if C++ method is destructor'); return false; } } extractClassName(node, sourceCode) { try { if (node.type === 'class_specifier' || node.type === 'struct_specifier' || node.type === 'enum_specifier') { const nameNode = node.childForFieldName('name'); if (nameNode) { return getNodeText(nameNode, sourceCode); } } else if (node.type === 'namespace_definition') { const nameNode = node.childForFieldName('name'); if (nameNode) { return `Namespace_${getNodeText(nameNode, sourceCode)}`; } } return 'AnonymousType'; } catch (error) { logger.warn({ err: error, nodeType: node.type }, 'Error extracting C/C++ class name'); return 'AnonymousType'; } } extractParentClass(node, sourceCode) { try { if (node.type === 'class_specifier') { const baseClauseNode = node.childForFieldName('base_clause'); if (baseClauseNode) { const baseSpecifiers = baseClauseNode.namedChildren; if (baseSpecifiers.length > 0) { const firstBase = baseSpecifiers[0]; if (firstBase.type === 'base_class_clause') { const typeNode = firstBase.childForFieldName('type'); if (typeNode) { return getNodeText(typeNode, sourceCode); } } } } } return undefined; } catch (error) { logger.warn({ err: error, nodeType: node.type }, 'Error extracting C/C++ parent class'); return undefined; } } extractImplementedInterfaces(node, sourceCode) { try { if (node.type === 'class_specifier') { const baseClauseNode = node.childForFieldName('base_clause'); if (baseClauseNode) { const interfaces = []; const baseSpecifiers = baseClauseNode.namedChildren; for (let i = 0; i < baseSpecifiers.length; i++) { const baseSpec = baseSpecifiers[i]; if (baseSpec.type === 'base_class_clause') { const typeNode = baseSpec.childForFieldName('type'); if (typeNode) { interfaces.push(getNodeText(typeNode, sourceCode)); } } } if (interfaces.length > 0 && this.extractParentClass(node, sourceCode)) { interfaces.shift(); } return interfaces.length > 0 ? interfaces : undefined; } } return undefined; } catch (error) { logger.warn({ err: error, nodeType: node.type }, 'Error extracting C/C++ implemented interfaces'); return undefined; } } extractImportPath(node, sourceCode) { try { if (node.type === 'include_directive') { const pathNode = node.childForFieldName('path'); if (pathNode) { return getNodeText(pathNode, sourceCode); } } else if (node.type === 'using_declaration' || 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/C++ import path'); return 'unknown'; } } extractImportedItems(node, sourceCode) { try { if (node.type === 'include_directive') { const pathNode = node.childForFieldName('path'); if (pathNode) { const path = getNodeText(pathNode, sourceCode); const isSystemInclude = path.startsWith('<') && path.endsWith('>'); const isLocalInclude = path.startsWith('"') && path.endsWith('"'); const cleanPath = isSystemInclude ? path.substring(1, path.length - 1) : isLocalInclude ? path.substring(1, path.length - 1) : path; const parts = cleanPath.split('/'); const headerName = parts[parts.length - 1]; return [{ name: headerName, path: cleanPath, isDefault: false, isNamespace: false, nodeText: node.text, isSystemInclude, isLocalInclude }]; } } else if (node.type === 'using_declaration') { const nameNode = node.childForFieldName('name'); if (nameNode) { const fullPath = getNodeText(nameNode, sourceCode); const parts = fullPath.split('::'); const name = parts[parts.length - 1]; return [{ name: name, path: fullPath, isDefault: false, isNamespace: false, nodeText: node.text, namespaceParts: parts.slice(0, parts.length - 1) }]; } } else if (node.type === 'using_directive') { const nameNode = node.childForFieldName('name'); if (nameNode) { const namespaceName = getNodeText(nameNode, sourceCode); return [{ name: namespaceName, path: namespaceName, isDefault: false, isNamespace: true, nodeText: node.text, isUsingNamespace: true }]; } } return undefined; } catch (error) { logger.warn({ err: error, nodeType: node.type }, 'Error extracting C/C++ 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') { if (prev.text.startsWith('/**') || prev.text.startsWith('/*!') || prev.text.startsWith('///') || prev.text.startsWith('//!')) { return this.parseDoxygenComment(prev.text); } return prev.text.replace(/^\/\/\s*/mg, '').trim(); } return undefined; } catch (error) { logger.warn({ err: error, nodeType: node.type }, 'Error extracting C/C++ 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') { if (prev.text.startsWith('/**') || prev.text.startsWith('/*!') || prev.text.startsWith('///') || prev.text.startsWith('//!')) { return this.parseDoxygenComment(prev.text); } return prev.text.replace(/^\/\/\s*/mg, '').trim(); } return undefined; } catch (error) { logger.warn({ err: error, nodeType: node.type }, 'Error extracting C/C++ class comment'); return undefined; } } parseDoxygenComment(comment) { try { if (comment.startsWith('/**') || comment.startsWith('/*!')) { const text = comment.substring(3, comment.length - 2); const lines = text.split('\n') .map(line => line.trim().replace(/^\*\s*/, '')) .filter(line => !line.startsWith('@') && !line.startsWith('\\')); return lines.join(' ').trim(); } else if (comment.startsWith('///') || comment.startsWith('//!')) { return comment.replace(/^\/\/[/!]\s*/mg, '').trim(); } return comment; } catch (error) { logger.warn({ err: error }, 'Error parsing Doxygen comment'); return comment; } } detectFramework(sourceCode) { try { if (sourceCode.includes('#include <QObject>') || sourceCode.includes('#include <QWidget>') || sourceCode.includes('Q_OBJECT')) { return 'qt'; } if (sourceCode.includes('#include <boost/') || sourceCode.includes('boost::') || sourceCode.includes('BOOST_')) { return 'boost'; } if (sourceCode.includes('#include <vector>') || sourceCode.includes('#include <map>') || sourceCode.includes('std::')) { return 'stl'; } if (sourceCode.includes('#include <GL/gl.h>') || sourceCode.includes('#include <GLFW/glfw3.h>') || sourceCode.includes('glBegin')) { return 'opengl'; } return null; } catch (error) { logger.warn({ err: error }, 'Error detecting C/C++ framework'); return null; } } async enhanceImportInfo(filePath, imports, options) { try { const sourceDir = path.dirname(filePath); const includePaths = [sourceDir]; const sourceCode = await this.readFileContent(filePath); const framework = this.detectFramework(sourceCode); if (framework) { switch (framework) { case 'qt': if (process.env.QTDIR) { includePaths.push(path.join(process.env.QTDIR, 'include')); } break; case 'boost': if (process.env.BOOST_ROOT) { includePaths.push(path.join(process.env.BOOST_ROOT, 'include')); } break; case 'opengl': if (process.env.OPENGL_INCLUDE) { includePaths.push(process.env.OPENGL_INCLUDE); } break; } } const systemIncludePaths = [ '/usr/include', '/usr/local/include', '/opt/homebrew/include' ]; const opts = options; const factory = new ImportResolverFactory({ allowedDir: opts.allowedDir, outputDir: opts.outputDir, maxDepth: opts.maxDepth || 3, clangdPath: opts.clangdPath, compileFlags: ['-std=c++17'], includePaths: [...includePaths, ...systemIncludePaths] }); const resolver = factory.getImportResolver(filePath); if (!resolver) { return imports; } const enhancedImports = await resolver.analyzeImports(filePath, { clangdPath: opts.clangdPath, compileFlags: ['-std=c++17'], includePaths: [...includePaths, ...systemIncludePaths], maxDepth: opts.maxDepth || 3 }); return this.mergeImportInfo(imports, enhancedImports); } catch (error) { logger.error({ err: error, filePath }, 'Error enhancing import info for C/C++'); return imports; } } async readFileContent(filePath) { try { return await fs.promises.readFile(filePath, 'utf8'); } catch (error) { logger.error({ err: error, filePath }, 'Error reading file content'); return ''; } } mergeImportInfo(original, enhanced) { if (!enhanced || enhanced.length === 0) { return original; } const originalImportMap = new Map(); for (const imp of original) { originalImportMap.set(imp.path, imp); } const result = []; for (const enhancedImport of enhanced) { const originalImport = originalImportMap.get(enhancedImport.path); if (originalImport) { result.push({ ...originalImport, metadata: { ...originalImport.metadata, ...enhancedImport.metadata }, isCore: enhancedImport.isCore, isExternalPackage: enhancedImport.isExternalPackage }); originalImportMap.delete(enhancedImport.path); } else { result.push(enhancedImport); } } for (const [, remainingImport] of originalImportMap) { result.push(remainingImport); } return result; } }