vibe-coder-mcp
Version:
Production-ready MCP server with complete agent integration, multi-transport support, and comprehensive development automation tools for AI-assisted workflows.
261 lines (260 loc) • 10.8 kB
JavaScript
import { BaseLanguageHandler } from './base.js';
import { getNodeText } from '../astAnalyzer.js';
import logger from '../../../logger.js';
export class RHandler extends BaseLanguageHandler {
getFunctionQueryPatterns() {
return [
'function_definition',
'assignment',
'left_assignment',
'right_assignment'
];
}
getClassQueryPatterns() {
return [
'call',
'assignment',
'left_assignment',
'right_assignment'
];
}
getImportQueryPatterns() {
return [
'call'
];
}
extractFunctionName(node, sourceCode, _options) {
try {
if (node.type === 'function_definition') {
if (node.parent?.type === 'assignment' ||
node.parent?.type === 'left_assignment' ||
node.parent?.type === 'right_assignment') {
const leftNode = node.parent.childForFieldName('left');
if (leftNode) {
const name = getNodeText(leftNode, sourceCode);
if (name.startsWith('test_') || name.startsWith('test.')) {
return `test_${name.substring(5)}`;
}
if (name.includes('.')) {
const parts = name.split('.');
if (parts.length >= 2) {
return `method_${parts[0]}_${parts[1]}`;
}
}
return name;
}
}
return 'anonymous_function';
}
if (node.type === 'assignment' ||
node.type === 'left_assignment' ||
node.type === 'right_assignment') {
const rightNode = node.childForFieldName('right');
const leftNode = node.childForFieldName('left');
if (rightNode?.type === 'function_definition' && leftNode) {
const name = getNodeText(leftNode, sourceCode);
if (name.startsWith('test_') || name.startsWith('test.')) {
return `test_${name.substring(5)}`;
}
if (name.includes('.')) {
const parts = name.split('.');
if (parts.length >= 2) {
return `method_${parts[0]}_${parts[1]}`;
}
}
return name;
}
}
return 'anonymous';
}
catch (error) {
logger.warn({ err: error, nodeType: node.type }, 'Error extracting R function name');
return 'anonymous';
}
}
extractClassName(node, sourceCode) {
try {
if (node.type === 'call') {
const functionNode = node.childForFieldName('function');
if (functionNode && getNodeText(functionNode, sourceCode) === 'setClass') {
const argsNode = node.childForFieldName('arguments');
if (argsNode?.firstChild) {
return getNodeText(argsNode.firstChild, sourceCode).replace(/^["']|["']$/g, '');
}
}
}
else if (node.type === 'assignment' ||
node.type === 'left_assignment' ||
node.type === 'right_assignment') {
const rightNode = node.childForFieldName('right');
const leftNode = node.childForFieldName('left');
if (rightNode?.type === 'call' &&
getNodeText(rightNode.childForFieldName('function') || rightNode, sourceCode) === 'setClass' &&
leftNode) {
return getNodeText(leftNode, sourceCode);
}
if (rightNode?.type === 'call' &&
getNodeText(rightNode.childForFieldName('function') || rightNode, sourceCode).includes('R6Class') &&
leftNode) {
return getNodeText(leftNode, sourceCode);
}
if (rightNode?.type === 'call' &&
getNodeText(rightNode.childForFieldName('function') || rightNode, sourceCode) === 'structure' &&
leftNode) {
const argsNode = rightNode.childForFieldName('arguments');
if (argsNode) {
for (let i = 0; i < argsNode.childCount; i++) {
const arg = argsNode.child(i);
if (arg?.text.includes('class =')) {
return getNodeText(leftNode, sourceCode);
}
}
}
}
}
return 'AnonymousClass';
}
catch (error) {
logger.warn({ err: error, nodeType: node.type }, 'Error extracting R class name');
return 'AnonymousClass';
}
}
extractParentClass(node, sourceCode) {
try {
if (node.type === 'call') {
const functionNode = node.childForFieldName('function');
if (functionNode && getNodeText(functionNode, sourceCode) === 'setClass') {
const argsNode = node.childForFieldName('arguments');
if (argsNode) {
for (let i = 0; i < argsNode.childCount; i++) {
const arg = argsNode.child(i);
if (arg?.text.includes('contains =')) {
const valueNode = arg.childForFieldName('value');
if (valueNode) {
return getNodeText(valueNode, sourceCode);
}
}
}
}
}
}
if (node.type === 'assignment' ||
node.type === 'left_assignment' ||
node.type === 'right_assignment') {
const rightNode = node.childForFieldName('right');
if (rightNode?.type === 'call' &&
getNodeText(rightNode.childForFieldName('function') || rightNode, sourceCode).includes('R6Class')) {
const argsNode = rightNode.childForFieldName('arguments');
if (argsNode) {
for (let i = 0; i < argsNode.childCount; i++) {
const arg = argsNode.child(i);
if (arg?.text.includes('inherit =')) {
const valueNode = arg.childForFieldName('value');
if (valueNode) {
return getNodeText(valueNode, sourceCode);
}
}
}
}
}
}
return undefined;
}
catch (error) {
logger.warn({ err: error, nodeType: node.type }, 'Error extracting R parent class');
return undefined;
}
}
extractImportPath(node, sourceCode) {
try {
if (node.type === 'call') {
const functionNode = node.childForFieldName('function');
if (functionNode) {
const funcName = getNodeText(functionNode, sourceCode);
if (funcName === 'library' || funcName === 'require') {
const argsNode = node.childForFieldName('arguments');
if (argsNode?.firstChild) {
return getNodeText(argsNode.firstChild, sourceCode).replace(/^["']|["']$/g, '');
}
}
}
}
return 'unknown';
}
catch (error) {
logger.warn({ err: error, nodeType: node.type }, 'Error extracting R import path');
return 'unknown';
}
}
extractFunctionComment(node, sourceCode) {
try {
const current = node;
let prev = current.previousNamedSibling;
while (prev && prev.type !== 'comment') {
prev = prev.previousNamedSibling;
}
if (prev && prev.type === 'comment') {
const commentText = getNodeText(prev, sourceCode);
return commentText
.replace(/^#'\s*/mg, '')
.replace(/^#\s*/mg, '')
.trim();
}
return undefined;
}
catch (error) {
logger.warn({ err: error, nodeType: node.type }, 'Error extracting R 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') {
const commentText = getNodeText(prev, sourceCode);
return commentText
.replace(/^#'\s*/mg, '')
.replace(/^#\s*/mg, '')
.trim();
}
return undefined;
}
catch (error) {
logger.warn({ err: error, nodeType: node.type }, 'Error extracting R class comment');
return undefined;
}
}
detectFramework(sourceCode) {
try {
if (sourceCode.includes('library(shiny)') ||
sourceCode.includes('shinyApp(') ||
sourceCode.includes('renderPlot(')) {
return 'shiny';
}
if (sourceCode.includes('library(ggplot2)') ||
sourceCode.includes('ggplot(') ||
sourceCode.includes('geom_')) {
return 'ggplot2';
}
if (sourceCode.includes('library(dplyr)') ||
sourceCode.includes('%>%') ||
sourceCode.includes('mutate(')) {
return 'dplyr';
}
if (sourceCode.includes('library(tidyverse)') ||
sourceCode.includes('tidyr::') ||
sourceCode.includes('readr::')) {
return 'tidyverse';
}
return null;
}
catch (error) {
logger.warn({ err: error }, 'Error detecting R framework');
return null;
}
}
}