Skip to content
This repository was archived by the owner on Mar 27, 2026. It is now read-only.

Commit d962536

Browse files
committed
implement include method and signature context
1 parent 40abf45 commit d962536

16 files changed

Lines changed: 1091 additions & 164 deletions

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,3 +162,4 @@ cython_debug/
162162
#.idea/
163163
repomix-output.txt
164164
.vscode/
165+
.idea/

Cargo.lock

Lines changed: 37 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[package]
22
name = "repodiff"
33
version = "0.3.0"
4-
edition = "2021"
4+
edition = "2024"
55
authors = ["RepoDiff Team"]
66
description = "A tool for generating optimized git diffs for LLM analysis"
77

@@ -14,3 +14,5 @@ tempfile = "3.10.0"
1414
tiktoken-rs = "0.5.8"
1515
fnmatch-regex = "0.2.0"
1616
thiserror = "1.0.57"
17+
tree-sitter = "0.20.10"
18+
tree-sitter-c-sharp = "0.20.0"

ai-workspace/core-prd.md

Lines changed: 118 additions & 131 deletions
Large diffs are not rendered by default.

ai-workspace/progress.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Progress Report
2+
3+
## Implemented Features
4+
5+
### C# Method-Aware Filtering
6+
- Added `include_method_body` and `include_signatures` options to `FilterRule`
7+
- Implemented C# parser using tree-sitter for method detection
8+
- Added Change Context Mode for C# files:
9+
- Includes full method bodies for changed methods
10+
- Includes method signatures within context range
11+
- Preserves standard context lines around changes
12+
- Added dependencies:
13+
- tree-sitter v0.20.10
14+
- tree-sitter-c-sharp v0.20.0
15+
16+
### Next Steps
17+
1. Improve file content retrieval:
18+
- Currently reconstructing file content from hunks
19+
- Need to implement proper file content retrieval from Git
20+
2. Add tests for C# method-aware filtering
21+
3. Add API Signature Mode implementation
22+
4. Add Combined Mode implementation

config.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
{
22
"tiktoken_model": "gpt-4o",
33
"filters": [
4-
{
5-
"file_pattern": "*.cs",
6-
"context_lines": 999
7-
},
84
{
95
"file_pattern": "*Test*.cs",
106
"context_lines": 200
117
},
8+
{
9+
"file_pattern": "*.cs",
10+
"context_lines": 999
11+
},
1212
{
1313
"file_pattern": "*.xml",
1414
"context_lines": 10

src/cli.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ pub fn run() -> Result<()> {
3131
let args = Args::parse();
3232

3333
// Initialize the RepoDiff tool
34-
let repodiff = RepoDiff::new("config.json")?;
34+
let mut repodiff = RepoDiff::new("config.json")?;
3535
let git_ops = GitOperations::new();
3636

3737
// Determine the commit hashes

src/filters/csharp_parser.rs

Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
use tree_sitter::{Parser, Node};
2+
use crate::utils::diff_parser::Hunk;
3+
4+
/// Represents a C# method in the code
5+
#[derive(Debug, PartialEq)]
6+
pub struct CSharpMethod {
7+
/// Start line of the method (1-indexed)
8+
pub start_line: usize,
9+
/// End line of the method (1-indexed)
10+
pub end_line: usize,
11+
/// Line containing the method signature
12+
pub signature_line: usize,
13+
/// Full method text
14+
pub text: String,
15+
/// Whether this method contains changes
16+
pub has_changes: bool,
17+
}
18+
19+
/// Represents a C# file in the code
20+
#[derive(Debug)]
21+
pub struct CSharpFile {
22+
/// Methods in the file
23+
pub methods: Vec<CSharpMethod>,
24+
/// Using statements in the file
25+
pub using_statements: Vec<(usize, usize)>, // (start_line, end_line)
26+
/// Class declarations in the file
27+
pub class_declarations: Vec<(usize, usize)>, // (start_line, end_line)
28+
/// Namespace declarations in the file
29+
pub namespace_declarations: Vec<(usize, usize)>, // (start_line, end_line)
30+
}
31+
32+
/// Parser for C# code that extracts method information
33+
pub struct CSharpParser {
34+
parser: Parser,
35+
}
36+
37+
impl CSharpParser {
38+
/// Create a new C# parser
39+
pub fn new() -> Self {
40+
let mut parser = Parser::new();
41+
parser.set_language(tree_sitter_c_sharp::language()).expect("Error loading C# grammar");
42+
CSharpParser { parser }
43+
}
44+
45+
/// Parse C# code and extract method information
46+
///
47+
/// # Arguments
48+
///
49+
/// * `code` - The C# code to parse
50+
/// * `hunks` - The diff hunks to identify changed methods
51+
pub fn parse_file(&mut self, code: &str, hunks: &[Hunk]) -> CSharpFile {
52+
let tree = self.parser.parse(code, None).expect("Failed to parse C# code");
53+
let root_node = tree.root_node();
54+
55+
let mut file = CSharpFile {
56+
methods: Vec::new(),
57+
using_statements: Vec::new(),
58+
class_declarations: Vec::new(),
59+
namespace_declarations: Vec::new(),
60+
};
61+
62+
self.find_nodes(root_node, code, &mut file);
63+
64+
// Mark methods that contain changes or have changes in their body
65+
for method in &mut file.methods {
66+
method.has_changes = self.method_contains_changes(method, hunks);
67+
}
68+
69+
file
70+
}
71+
72+
/// Find all method declarations in the AST
73+
fn find_nodes(&self, node: Node, code: &str, file: &mut CSharpFile) {
74+
match node.kind() {
75+
"method_declaration" => {
76+
let start_line = node.start_position().row + 1;
77+
let end_line = node.end_position().row + 1;
78+
79+
// Find the signature line by looking for the first child that's a method header
80+
let signature_line = node.child_by_field_name("header")
81+
.map(|n| n.start_position().row + 1)
82+
.unwrap_or(start_line);
83+
84+
let text = node.utf8_text(code.as_bytes())
85+
.unwrap_or_default()
86+
.to_string();
87+
88+
file.methods.push(CSharpMethod {
89+
start_line,
90+
end_line,
91+
signature_line,
92+
text,
93+
has_changes: false,
94+
});
95+
},
96+
"property_declaration" => {
97+
let start_line = node.start_position().row + 1;
98+
let end_line = node.end_position().row + 1;
99+
let signature_line = start_line;
100+
101+
// Check if this is an arrow expression property (=>)
102+
let is_arrow_expr = node.child_by_field_name("value")
103+
.map(|n| n.kind() == "arrow_expression_clause")
104+
.unwrap_or(false);
105+
106+
if is_arrow_expr {
107+
// For arrow expression properties, treat the whole thing as one method
108+
let text = node.utf8_text(code.as_bytes())
109+
.unwrap_or_default()
110+
.to_string();
111+
112+
file.methods.push(CSharpMethod {
113+
start_line,
114+
end_line,
115+
signature_line,
116+
text,
117+
has_changes: false,
118+
});
119+
} else {
120+
// For regular properties, first add the property declaration itself
121+
let text = node.utf8_text(code.as_bytes())
122+
.unwrap_or_default()
123+
.to_string();
124+
125+
file.methods.push(CSharpMethod {
126+
start_line,
127+
end_line,
128+
signature_line,
129+
text,
130+
has_changes: false,
131+
});
132+
133+
// Then look for accessors within the property
134+
let mut cursor: tree_sitter::TreeCursor<'_> = node.walk();
135+
for child in node.children(&mut cursor) {
136+
if child.kind() == "accessor_declaration" {
137+
let accessor_start = child.start_position().row + 1;
138+
let accessor_end = child.end_position().row + 1;
139+
let accessor_text = child.utf8_text(code.as_bytes())
140+
.unwrap_or_default()
141+
.to_string();
142+
143+
file.methods.push(CSharpMethod {
144+
start_line: accessor_start,
145+
end_line: accessor_end,
146+
signature_line: accessor_start,
147+
text: accessor_text,
148+
has_changes: false,
149+
});
150+
}
151+
}
152+
}
153+
},
154+
"using_directive" => {
155+
let start_line = node.start_position().row + 1;
156+
let end_line = node.end_position().row + 1;
157+
file.using_statements.push((start_line, end_line));
158+
},
159+
"namespace_declaration" => {
160+
let start_line = node.start_position().row + 1;
161+
let end_line = node.end_position().row + 1;
162+
file.namespace_declarations.push((start_line, end_line));
163+
},
164+
"class_declaration" => {
165+
let start_line = node.start_position().row + 1;
166+
let end_line = node.end_position().row + 1;
167+
file.class_declarations.push((start_line, end_line));
168+
},
169+
_ => {}
170+
}
171+
172+
let mut cursor = node.walk();
173+
for child in node.children(&mut cursor) {
174+
self.find_nodes(child, code, file);
175+
}
176+
}
177+
178+
/// Check if a method contains any changes from the diff hunks
179+
fn method_contains_changes(&self, method: &CSharpMethod, hunks: &[Hunk]) -> bool {
180+
for hunk in hunks {
181+
let mut current_line = hunk.new_start;
182+
183+
// Check if any line in the hunk is within this method's body
184+
for line in &hunk.lines {
185+
if current_line >= method.start_line && current_line <= method.end_line {
186+
// If it's a change line (+ or -) within the method body, mark the method as changed
187+
if line.starts_with('+') || line.starts_with('-') {
188+
return true;
189+
}
190+
}
191+
192+
// Only increment line count for non-deletion lines
193+
if !line.starts_with('-') {
194+
current_line += 1;
195+
}
196+
}
197+
}
198+
false
199+
}
200+
201+
/// Check if a node contains any changes from the diff hunks
202+
pub fn node_contains_changes(&self, start_line: usize, end_line: usize, hunks: &[Hunk]) -> bool {
203+
for hunk in hunks {
204+
let mut current_line = hunk.new_start;
205+
206+
for line in &hunk.lines {
207+
if current_line >= start_line && current_line <= end_line {
208+
if line.starts_with('+') || line.starts_with('-') {
209+
return true;
210+
}
211+
}
212+
213+
if !line.starts_with('-') {
214+
current_line += 1;
215+
}
216+
}
217+
}
218+
false
219+
}
220+
}

0 commit comments

Comments
 (0)