Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,6 @@ review-agent-test-pk.pem
src/test.ts
src/test-files.ts
src/tests
src/scripts
src/scripts
.vscode/
.idx/
12 changes: 12 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand Down
1 change: 1 addition & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ const EXTENSIONS_TO_PARSERS: Map<string, AbstractParser> = new Map([
["tsx", new JavascriptParser()],
["js", new JavascriptParser()],
["jsx", new JavascriptParser()],
["py", new JavascriptParser()],
]);

export const getParserForExtension = (filename: string) => {
Expand Down
161 changes: 153 additions & 8 deletions src/context/language/python-parser.ts
Original file line number Diff line number Diff line change
@@ -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<EnclosingContext> {
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()
};
}
}
}
}