vibe-coder-mcp
Version:
Production-ready MCP server with complete agent integration, multi-transport support, and comprehensive development automation tools for AI-assisted workflows.
346 lines (345 loc) • 13.6 kB
JavaScript
import { BaseLanguageHandler } from './base.js';
import { getNodeText } from '../astAnalyzer.js';
import logger from '../../../logger.js';
export class GoHandler extends BaseLanguageHandler {
getFunctionQueryPatterns() {
return [
'function_declaration',
'method_declaration',
'func_literal'
];
}
getClassQueryPatterns() {
return [
'type_declaration',
'type_spec',
'struct_type',
'interface_type'
];
}
getImportQueryPatterns() {
return [
'import_declaration',
'import_spec'
];
}
extractFunctionName(node, sourceCode, _options) {
try {
if (node.type === 'function_declaration') {
const nameNode = node.childForFieldName('name');
if (nameNode) {
const name = getNodeText(nameNode, sourceCode);
if (name.startsWith('Test') && this.hasTestSignature(node, sourceCode)) {
return `test_${name.substring(4)}`;
}
if (name.startsWith('Benchmark') && this.hasBenchmarkSignature(node, sourceCode)) {
return `benchmark_${name.substring(9)}`;
}
if (name.startsWith('Example')) {
return `example_${name.substring(7)}`;
}
if (name === 'main') {
return 'main_entrypoint';
}
if (name === 'init') {
return 'init_function';
}
return name;
}
}
if (node.type === 'method_declaration') {
const nameNode = node.childForFieldName('name');
const receiverNode = node.childForFieldName('receiver');
if (nameNode) {
const name = getNodeText(nameNode, sourceCode);
if (receiverNode) {
const receiverType = this.extractReceiverType(receiverNode, sourceCode);
if (this.isHttpHandler(node, sourceCode)) {
return `http_handler_${receiverType}_${name}`;
}
return `${receiverType}.${name}`;
}
return name;
}
}
if (node.type === 'func_literal') {
if (node.parent?.type === 'short_var_declaration' || node.parent?.type === 'assignment_statement') {
const leftNode = node.parent.childForFieldName('left');
if (leftNode?.firstChild) {
return getNodeText(leftNode.firstChild, sourceCode);
}
}
if (node.parent?.type === 'call_expression' &&
node.parent.previousSibling?.type === 'go') {
return 'goroutine';
}
if (node.parent?.type === 'argument_list' &&
node.parent.parent?.type === 'call_expression') {
const funcNode = node.parent.parent.childForFieldName('function');
if (funcNode) {
if (funcNode.type === 'selector_expression') {
const methodNode = funcNode.childForFieldName('field');
if (methodNode) {
const methodName = getNodeText(methodNode, sourceCode);
if (['Map', 'Filter', 'ForEach', 'Handle', 'HandleFunc'].includes(methodName)) {
return `${methodName.toLowerCase()}_callback`;
}
}
}
}
}
return 'closure';
}
return 'anonymous';
}
catch (error) {
logger.warn({ err: error, nodeType: node.type }, 'Error extracting Go function name');
return 'anonymous';
}
}
hasTestSignature(node, _sourceCode) {
try {
const paramsNode = node.childForFieldName('parameters');
if (!paramsNode)
return false;
return paramsNode.text.includes('*testing.T');
}
catch (error) {
logger.warn({ err: error, nodeType: node.type }, 'Error checking Go test signature');
return false;
}
}
hasBenchmarkSignature(node, _sourceCode) {
try {
const paramsNode = node.childForFieldName('parameters');
if (!paramsNode)
return false;
return paramsNode.text.includes('*testing.B');
}
catch (error) {
logger.warn({ err: error, nodeType: node.type }, 'Error checking Go benchmark signature');
return false;
}
}
extractReceiverType(receiverNode, sourceCode) {
try {
const parameterNode = receiverNode.childForFieldName('parameter');
if (!parameterNode)
return 'Unknown';
const typeNode = parameterNode.childForFieldName('type');
if (!typeNode)
return 'Unknown';
if (typeNode.type === 'pointer_type') {
const baseTypeNode = typeNode.childForFieldName('type');
if (baseTypeNode) {
return getNodeText(baseTypeNode, sourceCode);
}
}
return getNodeText(typeNode, sourceCode);
}
catch (error) {
logger.warn({ err: error, nodeType: 'unknown' }, 'Error extracting Go receiver type');
return 'Unknown';
}
}
isHttpHandler(node, sourceCode) {
try {
const paramsNode = node.childForFieldName('parameters');
if (!paramsNode)
return false;
const paramsText = getNodeText(paramsNode, sourceCode);
return paramsText.includes('http.ResponseWriter') && paramsText.includes('*http.Request');
}
catch (error) {
logger.warn({ err: error, nodeType: node.type }, 'Error checking if Go method is HTTP handler');
return false;
}
}
extractClassName(node, sourceCode) {
try {
if (node.type === 'type_declaration') {
const specNode = node.childForFieldName('spec');
if (specNode && specNode.type === 'type_spec') {
const nameNode = specNode.childForFieldName('name');
if (nameNode) {
return getNodeText(nameNode, sourceCode);
}
}
}
else if (node.type === 'type_spec') {
const nameNode = node.childForFieldName('name');
if (nameNode) {
return getNodeText(nameNode, sourceCode);
}
}
return 'AnonymousType';
}
catch (error) {
logger.warn({ err: error, nodeType: node.type }, 'Error extracting Go class name');
return 'AnonymousType';
}
}
extractImportPath(node, sourceCode) {
try {
if (node.type === 'import_spec') {
const pathNode = node.childForFieldName('path');
if (pathNode) {
const path = getNodeText(pathNode, sourceCode);
return path.replace(/^["']|["']$/g, '');
}
}
else if (node.type === 'import_declaration') {
const specNode = node.childForFieldName('spec');
if (specNode && specNode.type === 'import_spec') {
const pathNode = specNode.childForFieldName('path');
if (pathNode) {
const path = getNodeText(pathNode, sourceCode);
return path.replace(/^["']|["']$/g, '');
}
}
}
return 'unknown';
}
catch (error) {
logger.warn({ err: error, nodeType: node.type }, 'Error extracting Go import path');
return 'unknown';
}
}
extractImportedItems(node, sourceCode) {
try {
if (node.type === 'import_spec') {
const nameNode = node.childForFieldName('name');
const pathNode = node.childForFieldName('path');
if (pathNode) {
const path = getNodeText(pathNode, sourceCode).replace(/^["']|["']$/g, '');
const parts = path.split('/');
const name = parts[parts.length - 1];
if (nameNode) {
return [{
name: getNodeText(nameNode, sourceCode),
path: path,
isDefault: false,
isNamespace: false,
nodeText: node.text
}];
}
else {
return [{
name: name,
path: path,
isDefault: false,
isNamespace: false,
nodeText: node.text
}];
}
}
}
return undefined;
}
catch (error) {
logger.warn({ err: error, nodeType: node.type }, 'Error extracting Go imported items');
return undefined;
}
}
isDefaultImport(node, sourceCode) {
try {
if (node.type === 'import_spec') {
const nameNode = node.childForFieldName('name');
return nameNode ? getNodeText(nameNode, sourceCode) === '.' : false;
}
return undefined;
}
catch (error) {
logger.warn({ err: error, nodeType: node.type }, 'Error checking if Go import is default');
return undefined;
}
}
extractImportAlias(node, sourceCode) {
try {
if (node.type === 'import_spec') {
const nameNode = node.childForFieldName('name');
if (nameNode) {
return getNodeText(nameNode, sourceCode);
}
}
return undefined;
}
catch (error) {
logger.warn({ err: error, nodeType: node.type }, 'Error extracting Go import alias');
return undefined;
}
}
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.parseGoDocComment(prev.text);
}
return undefined;
}
catch (error) {
logger.warn({ err: error, nodeType: node.type }, 'Error extracting Go 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.parseGoDocComment(prev.text);
}
return undefined;
}
catch (error) {
logger.warn({ err: error, nodeType: node.type }, 'Error extracting Go class comment');
return undefined;
}
}
parseGoDocComment(comment) {
try {
const lines = comment.split('\n')
.map(line => line.trim().replace(/^\/\/\s*/, ''));
return lines.join(' ').trim();
}
catch (error) {
logger.warn({ err: error }, 'Error parsing Go doc comment');
return comment;
}
}
detectFramework(sourceCode) {
try {
if (sourceCode.includes('github.com/gin-gonic/gin') ||
sourceCode.includes('gin.Context') ||
sourceCode.includes('gin.Engine')) {
return 'gin';
}
if (sourceCode.includes('github.com/labstack/echo') ||
sourceCode.includes('echo.Context') ||
sourceCode.includes('echo.New()')) {
return 'echo';
}
if (sourceCode.includes('github.com/gorilla/mux') ||
sourceCode.includes('mux.Router') ||
sourceCode.includes('gorilla/websocket')) {
return 'gorilla';
}
if (sourceCode.includes('net/http') &&
(sourceCode.includes('http.HandleFunc') ||
sourceCode.includes('http.Handler') ||
sourceCode.includes('http.ServeMux'))) {
return 'net/http';
}
return null;
}
catch (error) {
logger.warn({ err: error }, 'Error detecting Go framework');
return null;
}
}
}