Skip to main content
Glama
r.js13.1 kB
/** * R language handler for the Code-Map Generator tool. * This file contains the language handler for R files. */ import { BaseLanguageHandler } from './base.js'; import { getNodeText } from '../astAnalyzer.js'; import logger from '../../../logger.js'; /** * Language handler for R. * Provides enhanced function name detection for R files. */ export class RHandler extends BaseLanguageHandler { /** * Gets the query patterns for function detection. */ getFunctionQueryPatterns() { return [ 'function_definition', 'assignment', 'left_assignment', 'right_assignment' ]; } /** * Gets the query patterns for class detection. */ getClassQueryPatterns() { return [ 'call', 'assignment', 'left_assignment', 'right_assignment' ]; } /** * Gets the query patterns for import detection. */ getImportQueryPatterns() { return [ 'call' ]; } /** * Extracts the function name from an AST node. */ extractFunctionName(node, sourceCode, options) { try { // Handle function definitions if (node.type === 'function_definition') { // Check if this function is being assigned to a variable 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); // Check for test functions if (name.startsWith('test_') || name.startsWith('test.')) { return `test_${name.substring(5)}`; } // Check for S3 methods if (name.includes('.')) { const parts = name.split('.'); if (parts.length >= 2) { return `method_${parts[0]}_${parts[1]}`; } } return name; } } return 'anonymous_function'; } // Handle assignments with function on the right side 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); // Check for test functions if (name.startsWith('test_') || name.startsWith('test.')) { return `test_${name.substring(5)}`; } // Check for S3 methods 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'; } } /** * Extracts the class name from an AST node. */ extractClassName(node, sourceCode) { try { // In R, classes are often created with setClass or similar functions 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'); // Check for S4 class definition if (rightNode?.type === 'call' && getNodeText(rightNode.childForFieldName('function') || rightNode, sourceCode) === 'setClass' && leftNode) { return getNodeText(leftNode, sourceCode); } // Check for R6 class definition if (rightNode?.type === 'call' && getNodeText(rightNode.childForFieldName('function') || rightNode, sourceCode).includes('R6Class') && leftNode) { return getNodeText(leftNode, sourceCode); } // Check for S3 class definition 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'; } } /** * Extracts the parent class from an AST node. */ extractParentClass(node, sourceCode) { try { // For S4 classes if (node.type === 'call') { const functionNode = node.childForFieldName('function'); if (functionNode && getNodeText(functionNode, sourceCode) === 'setClass') { const argsNode = node.childForFieldName('arguments'); if (argsNode) { // Look for 'contains' argument 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); } } } } } } // For R6 classes 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) { // Look for 'inherit' argument 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; } } /** * Extracts the import path from an AST node. */ extractImportPath(node, sourceCode) { try { // In R, imports are often done with library() or require() 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'; } } /** * Extracts the function comment from an AST node. */ extractFunctionComment(node, sourceCode) { try { // Look for comments before the function const current = node; let prev = current.previousNamedSibling; while (prev && prev.type !== 'comment') { prev = prev.previousNamedSibling; } if (prev && prev.type === 'comment') { // Extract the comment text const commentText = getNodeText(prev, sourceCode); // Remove comment markers and whitespace return commentText .replace(/^#'\s*/mg, '') // Roxygen comments .replace(/^#\s*/mg, '') // Regular comments .trim(); } return undefined; } catch (error) { logger.warn({ err: error, nodeType: node.type }, 'Error extracting R function comment'); return undefined; } } /** * Extracts the class comment from an AST node. */ extractClassComment(node, sourceCode) { try { // Look for comments before the class const current = node; let prev = current.previousNamedSibling; while (prev && prev.type !== 'comment') { prev = prev.previousNamedSibling; } if (prev && prev.type === 'comment') { // Extract the comment text const commentText = getNodeText(prev, sourceCode); // Remove comment markers and whitespace return commentText .replace(/^#'\s*/mg, '') // Roxygen comments .replace(/^#\s*/mg, '') // Regular comments .trim(); } return undefined; } catch (error) { logger.warn({ err: error, nodeType: node.type }, 'Error extracting R class comment'); return undefined; } } /** * Detects the framework used in the source code. */ detectFramework(sourceCode) { try { // Shiny detection if (sourceCode.includes('library(shiny)') || sourceCode.includes('shinyApp(') || sourceCode.includes('renderPlot(')) { return 'shiny'; } // ggplot2 detection if (sourceCode.includes('library(ggplot2)') || sourceCode.includes('ggplot(') || sourceCode.includes('geom_')) { return 'ggplot2'; } // dplyr detection if (sourceCode.includes('library(dplyr)') || sourceCode.includes('%>%') || sourceCode.includes('mutate(')) { return 'dplyr'; } // tidyverse detection 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; } } }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/freshtechbro/vibe-coder-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server