vibe-coder-mcp
Version:
Production-ready MCP server with complete agent integration, multi-transport support, and comprehensive development automation tools for AI-assisted workflows.
322 lines (321 loc) • 12.7 kB
JavaScript
import { BaseLanguageHandler } from './base.js';
import { getNodeText } from '../astAnalyzer.js';
import logger from '../../../logger.js';
export class SwiftHandler extends BaseLanguageHandler {
getFunctionQueryPatterns() {
return [
'function_declaration',
'method_declaration',
'closure_expression'
];
}
getClassQueryPatterns() {
return [
'class_declaration',
'struct_declaration',
'enum_declaration',
'protocol_declaration',
'extension_declaration'
];
}
getImportQueryPatterns() {
return [
'import_declaration'
];
}
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')) {
return `test_${name.substring(4)}`;
}
return name;
}
}
if (node.type === 'method_declaration') {
const nameNode = node.childForFieldName('name');
if (nameNode) {
const name = getNodeText(nameNode, sourceCode);
if (name === 'init') {
return 'initializer';
}
if (name === 'deinit') {
return 'deinitializer';
}
if (this.isUIKitLifecycleMethod(name)) {
return `lifecycle_${name}`;
}
if (this.isSwiftUIViewMethod(name)) {
return `view_${name}`;
}
return name;
}
}
if (node.type === 'closure_expression') {
if (node.parent?.type === 'variable_declaration') {
const patternNode = node.parent.childForFieldName('pattern');
if (patternNode) {
return getNodeText(patternNode, sourceCode);
}
}
if (node.parent?.type === 'argument' &&
node.parent.parent?.type === 'argument_list' &&
node.parent.parent.parent?.type === 'call_expression') {
const funcNode = node.parent.parent.parent.childForFieldName('function');
if (funcNode) {
const funcName = getNodeText(funcNode, sourceCode);
if (['map', 'filter', 'reduce', 'forEach', 'compactMap', 'flatMap'].includes(funcName)) {
return `${funcName}_closure`;
}
}
}
return 'closure';
}
return 'anonymous';
}
catch (error) {
logger.warn({ err: error, nodeType: node.type }, 'Error extracting Swift function name');
return 'anonymous';
}
}
isUIKitLifecycleMethod(name) {
const lifecycleMethods = [
'viewDidLoad',
'viewWillAppear',
'viewDidAppear',
'viewWillDisappear',
'viewDidDisappear',
'viewWillLayoutSubviews',
'viewDidLayoutSubviews',
'didReceiveMemoryWarning'
];
return lifecycleMethods.includes(name);
}
isSwiftUIViewMethod(name) {
const viewMethods = [
'body',
'makeBody',
'makeUIView',
'updateUIView',
'makeCoordinator'
];
return viewMethods.includes(name);
}
extractClassName(node, sourceCode) {
try {
if (node.type === 'class_declaration' ||
node.type === 'struct_declaration' ||
node.type === 'enum_declaration' ||
node.type === 'protocol_declaration') {
const nameNode = node.childForFieldName('name');
if (nameNode) {
return getNodeText(nameNode, sourceCode);
}
}
else if (node.type === 'extension_declaration') {
const typeNode = node.childForFieldName('type');
if (typeNode) {
return `Extension_${getNodeText(typeNode, sourceCode)}`;
}
}
return 'AnonymousType';
}
catch (error) {
logger.warn({ err: error, nodeType: node.type }, 'Error extracting Swift class name');
return 'AnonymousType';
}
}
extractParentClass(node, sourceCode) {
try {
if (node.type === 'class_declaration') {
const inheritanceNode = node.childForFieldName('inheritance_clause');
if (inheritanceNode) {
const inheritanceText = getNodeText(inheritanceNode, sourceCode);
const types = inheritanceText.split(',').map(t => t.trim());
if (types.length > 0) {
return types[0];
}
}
}
return undefined;
}
catch (error) {
logger.warn({ err: error, nodeType: node.type }, 'Error extracting Swift parent class');
return undefined;
}
}
extractImplementedInterfaces(node, sourceCode) {
try {
if (node.type === 'class_declaration' ||
node.type === 'struct_declaration' ||
node.type === 'enum_declaration') {
const inheritanceNode = node.childForFieldName('inheritance_clause');
if (inheritanceNode) {
const inheritanceText = getNodeText(inheritanceNode, sourceCode);
const types = inheritanceText.split(',').map(t => t.trim());
const protocols = node.type === 'class_declaration' && types.length > 0 ?
types.slice(1) : types;
return protocols.length > 0 ? protocols : undefined;
}
}
else if (node.type === 'extension_declaration') {
const conformanceNode = node.childForFieldName('protocol_conformance');
if (conformanceNode) {
const conformanceText = getNodeText(conformanceNode, sourceCode);
const protocols = conformanceText.split(',').map(t => t.trim());
return protocols.length > 0 ? protocols : undefined;
}
}
return undefined;
}
catch (error) {
logger.warn({ err: error, nodeType: node.type }, 'Error extracting Swift implemented interfaces');
return undefined;
}
}
extractImportPath(node, sourceCode) {
try {
if (node.type === 'import_declaration') {
const pathNode = node.childForFieldName('path');
if (pathNode) {
return getNodeText(pathNode, sourceCode);
}
}
return 'unknown';
}
catch (error) {
logger.warn({ err: error, nodeType: node.type }, 'Error extracting Swift import path');
return 'unknown';
}
}
extractImportedItems(node, sourceCode) {
try {
if (node.type === 'import_declaration') {
const pathNode = node.childForFieldName('path');
if (pathNode) {
const fullPath = getNodeText(pathNode, sourceCode);
const kindNode = node.childForFieldName('kind');
const importKind = kindNode ? getNodeText(kindNode, sourceCode) : undefined;
const parts = fullPath.split('.');
const moduleName = parts[0];
if (parts.length === 1) {
return [{
name: moduleName,
path: moduleName,
isDefault: false,
isNamespace: true,
nodeText: node.text,
importKind: importKind || 'module'
}];
}
else {
const submoduleName = parts[parts.length - 1];
return [{
name: submoduleName,
path: fullPath,
isDefault: false,
isNamespace: false,
nodeText: node.text,
importKind: importKind || 'type',
moduleName: moduleName
}];
}
}
}
return undefined;
}
catch (error) {
logger.warn({ err: error, nodeType: node.type }, 'Error extracting Swift 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' &&
(prev.text.startsWith('///') || prev.text.startsWith('/**'))) {
return this.parseSwiftDocComment(prev.text);
}
return undefined;
}
catch (error) {
logger.warn({ err: error, nodeType: node.type }, 'Error extracting Swift 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' &&
(prev.text.startsWith('///') || prev.text.startsWith('/**'))) {
return this.parseSwiftDocComment(prev.text);
}
return undefined;
}
catch (error) {
logger.warn({ err: error, nodeType: node.type }, 'Error extracting Swift class comment');
return undefined;
}
}
parseSwiftDocComment(comment) {
try {
if (comment.startsWith('///')) {
return comment.replace(/^\/\/\/\s*/mg, '').trim();
}
else if (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('- Parameter') &&
!line.startsWith('- Returns') &&
!line.startsWith('- Throws'));
return lines.join(' ').trim();
}
return comment;
}
catch (error) {
logger.warn({ err: error }, 'Error parsing Swift doc comment');
return comment;
}
}
detectFramework(sourceCode) {
try {
if (sourceCode.includes('import UIKit') ||
sourceCode.includes('UIViewController') ||
sourceCode.includes('UIView')) {
return 'uikit';
}
if (sourceCode.includes('import SwiftUI') ||
sourceCode.includes('struct') && sourceCode.includes(': View') ||
sourceCode.includes('var body: some View')) {
return 'swiftui';
}
if (sourceCode.includes('import Combine') ||
sourceCode.includes('Publisher') ||
sourceCode.includes('Subscriber')) {
return 'combine';
}
if (sourceCode.includes('import CoreData') ||
sourceCode.includes('NSManagedObject') ||
sourceCode.includes('NSPersistentContainer')) {
return 'coredata';
}
return null;
}
catch (error) {
logger.warn({ err: error }, 'Error detecting Swift framework');
return null;
}
}
}