diff --git a/.gitignore b/.gitignore index b076b5e..59e547a 100644 --- a/.gitignore +++ b/.gitignore @@ -9,4 +9,6 @@ review-agent-test-pk.pem src/test.ts src/test-files.ts src/tests -src/scripts \ No newline at end of file +src/scripts +.vscode/ +.idx/ \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index f60579a..7c5fcbc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,6 +20,7 @@ "gpt-tokenizer": "^2.1.2", "groq-sdk": "^0.8.0", "octokit": "^3.1.1", + "web-tree-sitter": "^0.24.4", "xml2js": "^0.6.2" }, "devDependencies": { @@ -2189,6 +2190,12 @@ "node": ">= 14" } }, + "node_modules/web-tree-sitter": { + "version": "0.24.4", + "resolved": "https://registry.npmjs.org/web-tree-sitter/-/web-tree-sitter-0.24.4.tgz", + "integrity": "sha512-sETP1Sf9OTd4LusrKBNznNgTt3fWoWhJnAFaKPiGSeVKXJbZ72qoMpxddKMdVI5BgXv32OI7tkKQre5PmF9reA==", + "license": "MIT" + }, "node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", @@ -3740,6 +3747,11 @@ "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz", "integrity": "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==" }, + "web-tree-sitter": { + "version": "0.24.4", + "resolved": "https://registry.npmjs.org/web-tree-sitter/-/web-tree-sitter-0.24.4.tgz", + "integrity": "sha512-sETP1Sf9OTd4LusrKBNznNgTt3fWoWhJnAFaKPiGSeVKXJbZ72qoMpxddKMdVI5BgXv32OI7tkKQre5PmF9reA==" + }, "webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", diff --git a/package.json b/package.json index fb08515..582e7a5 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "gpt-tokenizer": "^2.1.2", "groq-sdk": "^0.8.0", "octokit": "^3.1.1", + "web-tree-sitter": "^0.24.4", "xml2js": "^0.6.2" }, "devDependencies": { diff --git a/src/constants.ts b/src/constants.ts index 14c7de1..4b65e20 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -107,6 +107,7 @@ const EXTENSIONS_TO_PARSERS: Map = new Map([ ["tsx", new JavascriptParser()], ["js", new JavascriptParser()], ["jsx", new JavascriptParser()], + ["py", new JavascriptParser()], ]); export const getParserForExtension = (filename: string) => { diff --git a/src/context/language/python-parser.ts b/src/context/language/python-parser.ts index 845e90b..ecb1156 100644 --- a/src/context/language/python-parser.ts +++ b/src/context/language/python-parser.ts @@ -1,15 +1,160 @@ import { AbstractParser, EnclosingContext } from "../../constants"; +import Parser from 'web-tree-sitter'; + +interface TreeSitterNode { + type: string; + startPosition: { + row: number; + column: number; + }; + endPosition: { + row: number; + column: number; + }; + children: TreeSitterNode[]; + parent: TreeSitterNode | null; +} + export class PythonParser implements AbstractParser { - findEnclosingContext( + private parser: Parser | null = null; + + private async initializeParser() { + if (this.parser) return; + + await Parser.init(); + this.parser = new Parser(); + const Lang = await Parser.Language.load('tree-sitter-python.wasm'); + this.parser.setLanguage(Lang); + } + + private findLargestEnclosingNode( + node: TreeSitterNode, + lineStart: number, + lineEnd: number, + largestSize: number, + largestNode: TreeSitterNode | null + ): { largestSize: number; largestNode: TreeSitterNode | null } { + + if ( + (node.type === 'function_definition' || + node.type === 'class_definition' || + node.type === 'async_function_definition') && + node.startPosition.row <= lineStart && + node.endPosition.row >= lineEnd + ) { + const size = node.endPosition.row - node.startPosition.row; + if (size > largestSize) { + return { + largestSize: size, + largestNode: node + }; + } + } + + // Recursively check all children + let currentLargest = { largestSize, largestNode }; + for (const child of node.children) { + const result = this.findLargestEnclosingNode( + child, + lineStart, + lineEnd, + currentLargest.largestSize, + currentLargest.largestNode + ); + currentLargest = result; + } + + return currentLargest; + } + + private extractNodeName(node: TreeSitterNode): string { + // Find the identifier node within function or class definition + const identifierNode = node.children.find(child => + child.type === 'identifier' + ); + return identifierNode ? identifierNode.toString() : 'anonymous'; + } + + async findEnclosingContext( file: string, lineStart: number, lineEnd: number - ): EnclosingContext { - // TODO: Implement this method for Python - return null; + ): Promise { + try { + await this.initializeParser(); + if (!this.parser) { + throw new Error('Parser initialization failed'); + } + + // Parse the file + const tree = this.parser.parse(file); + + // Convert to 0-based line numbers for tree-sitter + const zeroBasedStart = lineStart - 1; + const zeroBasedEnd = lineEnd - 1; + + // Find the largest enclosing node + const { largestNode } = this.findLargestEnclosingNode( + tree.rootNode, + zeroBasedStart, + zeroBasedEnd, + 0, + null + ); + + if (largestNode) { + return { + enclosingContext: { + type: largestNode.type, + name: this.extractNodeName(largestNode), + loc: { + start: { + line: largestNode.startPosition.row + 1, + column: largestNode.startPosition.column + }, + end: { + line: largestNode.endPosition.row + 1, + column: largestNode.endPosition.column + } + } + } + }; + } + + return { enclosingContext: null }; + } catch (error) { + console.error('Error parsing Python code:', error); + return { enclosingContext: null }; + } } - dryRun(file: string): { valid: boolean; error: string } { - // TODO: Implement this method for Python - return { valid: false, error: "Not implemented yet" }; + + async dryRun(file: string): Promise<{ valid: boolean; error: string }> { + try { + await this.initializeParser(); + if (!this.parser) { + throw new Error('Parser initialization failed'); + } + + const tree = this.parser.parse(file); + + const hasErrors = tree.rootNode.hasError(); + + if (hasErrors) { + return { + valid: false, + error: 'Syntax error in Python code' + }; + } + + return { + valid: true, + error: '' + }; + } catch (error) { + return { + valid: false, + error: error.toString() + }; + } } -} +} \ No newline at end of file