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
6 changes: 4 additions & 2 deletions src/constants.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Node } from "@babel/traverse";
import { JavascriptParser } from "./context/language/javascript-parser";
import { ChatCompletionMessageParam } from "groq-sdk/resources/chat/completions";
import { PythonParser } from "./context/language/python-parser";

export interface PRFile {
sha: string;
Expand Down Expand Up @@ -98,15 +99,16 @@ export interface AbstractParser {
file: string,
lineStart: number,
lineEnd: number
): EnclosingContext;
dryRun(file: string): { valid: boolean; error: string };
): Promise<EnclosingContext>;
dryRun(file: string): Promise<{ valid: boolean; error: string }>;
}

const EXTENSIONS_TO_PARSERS: Map<string, AbstractParser> = new Map([
["ts", new JavascriptParser()],
["tsx", new JavascriptParser()],
["js", new JavascriptParser()],
["jsx", new JavascriptParser()],
["py", new PythonParser()]
]);

export const getParserForExtension = (filename: string) => {
Expand Down
48 changes: 27 additions & 21 deletions src/context/language/javascript-parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,19 @@ const processNode = (
};

export class JavascriptParser implements AbstractParser {
findEnclosingContext(
async findEnclosingContext(
file: string,
lineStart: number,
lineEnd: number
): EnclosingContext {
const ast = parser.parse(file, {
sourceType: "module",
plugins: ["jsx", "typescript"], // To allow JSX and TypeScript
});
let largestEnclosingContext: Node = null;
let largestSize = 0;
): Promise<EnclosingContext> {
return new Promise ((resolve) => {
const ast = parser.parse(file, {
sourceType: "module",
plugins: ["jsx", "typescript"], // To allow JSX and TypeScript
});

let largestEnclosingContext: Node = null;
let largestSize = 0;
traverse(ast, {
Function(path) {
({ largestSize, largestEnclosingContext } = processNode(
Expand All @@ -52,26 +54,30 @@ export class JavascriptParser implements AbstractParser {
));
},
});
return {
resolve({
enclosingContext: largestEnclosingContext,
} as EnclosingContext;
} as EnclosingContext);
});
}


dryRun(file: string): { valid: boolean; error: string } {
try {
const ast = parser.parse(file, {
sourceType: "module",
plugins: ["jsx", "typescript"], // To allow JSX and TypeScript
});
return {
async dryRun(file: string): Promise<{ valid: boolean; error: string }> {
return new Promise((resolve) => {
try {
const ast = parser.parse(file, {
sourceType: "module",
plugins: ["jsx", "typescript"], // To allow JSX and TypeScript
});
resolve({
valid: true,
error: "",
};
});
} catch (exc) {
return {
resolve({
valid: false,
error: exc,
};
});
}
}
});
}
}
84 changes: 77 additions & 7 deletions src/context/language/python-parser.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,85 @@
import { AbstractParser, EnclosingContext } from "../../constants";
import { spawn } from 'child_process';
import * as path from 'path';

export class PythonParser implements AbstractParser {
findEnclosingContext(
private pythonScriptPath: string;

constructor() {
this.pythonScriptPath = path.join(__dirname, 'python_parser.py');
}

async findEnclosingContext(
file: string,
lineStart: number,
lineEnd: number
): EnclosingContext {
// TODO: Implement this method for Python
return null;
): Promise<EnclosingContext> {
return new Promise((resolve, reject) => {
const pythonProcess = spawn('python', [
this.pythonScriptPath,
file,
lineStart.toString(),
lineEnd.toString()
]);

let output = '';
let errorOutput = '';

pythonProcess.stdout.on('data', (data) => {
output += data.toString();
});

pythonProcess.stderr.on('data', (data) => {
errorOutput += data.toString();
});

pythonProcess.on('close', (code) => {
if (code === 0) {
try {
const context = JSON.parse(output);
resolve({ enclosingContext: context } as EnclosingContext);
} catch (error) {
reject(new Error(`Failed to parse Python output: ${error}`));
}
} else {
reject(new Error(`Python script exited with code ${code}: ${errorOutput}`));
}
});
});
}
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 }> {

return new Promise((resolve, reject) => {
const pythonProcess = spawn('python', ['-c', `
import ast

try:
with open('${file})', 'r') as f:
ast.parse(f.read())
print('{"valid": true, "error": ""}')
except Exception as e:
print('{"valid": false, "error": "' + str(e).replace('"', '\\"') +
'"}')`
]);

let output = '';

pythonProcess.stdout.on('data', (data) => {
output += data.toString();
});

pythonProcess.on('close', (code) => {
if (code === 0) {
try {
const result = JSON.parse(output);
resolve(result);
} catch (error) {
reject(new Error(`Failed to parse Python output: ${error}`));
}
} else {
reject(new Error(`Python script exited with code ${code}`));
}
});
});
}
}
55 changes: 55 additions & 0 deletions src/context/language/python_parser.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import ast
import sys
import json

def find_enclosing_context(file_content, line_start, line_end):
tree = ast.parse(file_content)

class ContextFinder(ast.NodeVisitor):
def __init__(self, line_start, line_end):
self.line_start = line_start
self.line_end = line_end
self.largest_context = None
self.largest_size = 0

def visit_FunctionDef(self, node):
self.check_node(node)
self.generic_visit(node)

def visit_ClassDef(self, node):
self.check_node(node)
self.generic_visit(node)

def check_node(self, node):
if node.lineno <= self.line_start and self.line_end <= node.end_lineno:
size = node.end_lineno - node.lineno
if size > self.largest_size:
self.largest_size = size
self.largest_context = node

finder = ContextFinder(line_start, line_end)
finder.visit(tree)

if finder.largest_context:
return {
"type": finder.largest_context.__class__.__name__,
"name": finder.largest_context.name,
"start_line": finder.largest_context.lineno,
"end_line": finder.largest_context.end_lineno
}

return None

def main():
file_path = sys.argv[1]
line_start = int(sys.argv[2])
line_end = int(sys.argv[3])

with open(file_path, 'r') as file:
file_content = file.read()

context = find_enclosing_context(file_content, line_start, line_end)
print(json.dumps(context))

if __name__ == "__main__":
main()
6 changes: 3 additions & 3 deletions src/context/review.ts
Original file line number Diff line number Diff line change
Expand Up @@ -205,19 +205,19 @@ const diffContextPerHunk = (file: PRFile, parser: AbstractParser) => {
});
});

hunks.forEach((hunk, idx) => {
hunks.forEach(async (hunk, idx) => {
try {
const trimmedHunk = trimHunk(hunk);
const insertions = hunk.lines.filter((line) =>
line.startsWith("+")
).length;
const lineStart = trimmedHunk.newStart;
const lineEnd = lineStart + insertions;
const largestEnclosingFunction = parser.findEnclosingContext(
const largestEnclosingFunction = (await parser.findEnclosingContext(
updatedFile,
lineStart,
lineEnd
).enclosingContext;
)).enclosingContext;

if (largestEnclosingFunction) {
const enclosingRangeKey = `${largestEnclosingFunction.loc.start.line} -> ${largestEnclosingFunction.loc.end.line}`;
Expand Down
65 changes: 65 additions & 0 deletions src/randomStuff.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import random
import string
import json
import math

def generate_random_number(min_value=1, max_value=100):
"""Generate a random number between min_value and max_value."""
return random.randint(min_value, max_value)

def generate_random_string(length=10):
"""Generate a random string of a specified length."""
letters = string.ascii_letters # Uppercase and lowercase letters
return ''.join(random.choice(letters) for i in range(length))

def create_random_list(size=5, min_value=1, max_value=50):
"""Create a list of random integers."""
return [generate_random_number(min_value, max_value) for _ in range(size)]

def calculate_factorial(n):
"""Calculate the factorial of a number."""
if n < 0:
return None
return math.factorial(n)

def write_to_json_file(data, filename='data.json'):
"""Write a dictionary to a JSON file."""
with open(filename, 'w') as file:
json.dump(data, file, indent=4)

def read_from_json_file(filename='data.json'):
"""Read data from a JSON file."""
with open(filename, 'r') as file:
return json.load(file)

def main():
# Generate some random numbers and strings
random_number = generate_random_number()
random_string = generate_random_string()
random_list = create_random_list()

print(f"Random Number: {random_number}")
print(f"Random String: {random_string}")
print(f"Random List: {random_list}")

# Calculate factorial for a random number
factorial_of = random.choice(random_list)
factorial_value = calculate_factorial(factorial_of)
print(f"Factorial of {factorial_of}: {factorial_value}")

# Write some data to a JSON file
data = {
"random_number": random_number,
"random_string": random_string,
"random_list": random_list,
"factorial_value": factorial_value
}
write_to_json_file(data)

# Read the data back from the JSON file
loaded_data = read_from_json_file()
print("Data loaded from JSON file:")
print(json.dumps(loaded_data, indent=4))

if __name__ == "__main__":
main()