Skip to content

Commit 5a0bb72

Browse files
committed
Add LSP-backed symbol indexing and feedback CI helper
1 parent 0377bb1 commit 5a0bb72

File tree

6 files changed

+581
-31
lines changed

6 files changed

+581
-31
lines changed

.diffscope.yml.example

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ review_instructions: |
1414
smart_review_summary: true # Include AI-generated PR summary in smart-review output
1515
smart_review_diagram: false # Generate a Mermaid diagram in smart-review output
1616
symbol_index: true # Build repo symbol index for cross-file context (respects .gitignore)
17+
symbol_index_provider: regex # regex | lsp
18+
symbol_index_lsp_command: rust-analyzer
1719
symbol_index_max_files: 500
1820
symbol_index_max_bytes: 200000
1921
symbol_index_max_locations: 5

README.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,8 @@ review_instructions: |
199199
smart_review_summary: true # Include AI-generated PR summary in smart-review output
200200
smart_review_diagram: false # Generate a Mermaid diagram in smart-review output
201201
symbol_index: true # Build repo symbol index for cross-file context (respects .gitignore)
202+
symbol_index_provider: regex # regex | lsp
203+
symbol_index_lsp_command: rust-analyzer
202204
symbol_index_max_files: 500
203205
symbol_index_max_bytes: 200000
204206
symbol_index_max_locations: 5
@@ -219,6 +221,8 @@ exclude_patterns:
219221
- "**/__pycache__/**"
220222
```
221223

224+
Set `symbol_index_provider: lsp` to use a language server for Rust (via `rust-analyzer`); it falls back to regex indexing if the LSP binary is missing.
225+
222226
## Plugin Development
223227

224228
Create custom analyzers:
@@ -650,6 +654,18 @@ diffscope feedback --accept review.json
650654

651655
The feedback file defaults to `.diffscope.feedback.json` and can be configured in `.diffscope.yml`.
652656

657+
**CI helper (GitHub Actions):**
658+
659+
```yaml
660+
- name: Update DiffScope feedback
661+
if: always()
662+
run: |
663+
bash scripts/update_feedback_from_review.sh \
664+
--action reject \
665+
--input review.json \
666+
--feedback .diffscope.feedback.json
667+
```
668+
653669
### 📊 PR Summary Generation
654670
655671
Generate executive summaries for pull requests:
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
usage() {
5+
echo "Usage: $0 --action accept|reject --input review.json [--feedback .diffscope.feedback.json]" >&2
6+
exit 1
7+
}
8+
9+
ACTION=""
10+
INPUT=""
11+
FEEDBACK_PATH=""
12+
13+
while [[ $# -gt 0 ]]; do
14+
case "$1" in
15+
--action)
16+
ACTION="$2"
17+
shift 2
18+
;;
19+
--input)
20+
INPUT="$2"
21+
shift 2
22+
;;
23+
--feedback)
24+
FEEDBACK_PATH="$2"
25+
shift 2
26+
;;
27+
*)
28+
usage
29+
;;
30+
esac
31+
done
32+
33+
if [[ -z "${ACTION}" || -z "${INPUT}" ]]; then
34+
usage
35+
fi
36+
37+
if [[ "${ACTION}" != "accept" && "${ACTION}" != "reject" ]]; then
38+
echo "Invalid action: ${ACTION}" >&2
39+
usage
40+
fi
41+
42+
if [[ ! -f "${INPUT}" ]]; then
43+
echo "Input file not found: ${INPUT}" >&2
44+
exit 1
45+
fi
46+
47+
CMD=()
48+
if command -v diffscope >/dev/null 2>&1; then
49+
CMD=(diffscope feedback)
50+
else
51+
CMD=(cargo run --quiet -- feedback)
52+
fi
53+
54+
if [[ "${ACTION}" == "accept" ]]; then
55+
CMD+=(--accept "${INPUT}")
56+
else
57+
CMD+=(--reject "${INPUT}")
58+
fi
59+
60+
if [[ -n "${FEEDBACK_PATH}" ]]; then
61+
CMD+=(--feedback-path "${FEEDBACK_PATH}")
62+
fi
63+
64+
echo "Running: ${CMD[*]}" >&2
65+
exec "${CMD[@]}"

src/config.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ pub struct Config {
3838
#[serde(default = "default_true")]
3939
pub symbol_index: bool,
4040

41+
#[serde(default = "default_symbol_index_provider")]
42+
pub symbol_index_provider: String,
43+
4144
#[serde(default = "default_symbol_index_max_files")]
4245
pub symbol_index_max_files: usize,
4346

@@ -47,6 +50,9 @@ pub struct Config {
4750
#[serde(default = "default_symbol_index_max_locations")]
4851
pub symbol_index_max_locations: usize,
4952

53+
#[serde(default)]
54+
pub symbol_index_lsp_command: Option<String>,
55+
5056
#[serde(default = "default_feedback_path")]
5157
pub feedback_path: PathBuf,
5258

@@ -125,9 +131,11 @@ impl Default for Config {
125131
smart_review_summary: true,
126132
smart_review_diagram: false,
127133
symbol_index: true,
134+
symbol_index_provider: default_symbol_index_provider(),
128135
symbol_index_max_files: default_symbol_index_max_files(),
129136
symbol_index_max_bytes: default_symbol_index_max_bytes(),
130137
symbol_index_max_locations: default_symbol_index_max_locations(),
138+
symbol_index_lsp_command: None,
131139
feedback_path: default_feedback_path(),
132140
system_prompt: None,
133141
api_key: None,
@@ -204,6 +212,19 @@ impl Config {
204212
self.symbol_index_max_locations = default_symbol_index_max_locations();
205213
}
206214

215+
let provider = self.symbol_index_provider.trim().to_lowercase();
216+
if provider.is_empty() || !matches!(provider.as_str(), "regex" | "lsp") {
217+
self.symbol_index_provider = default_symbol_index_provider();
218+
} else {
219+
self.symbol_index_provider = provider;
220+
}
221+
222+
if let Some(command) = &self.symbol_index_lsp_command {
223+
if command.trim().is_empty() {
224+
self.symbol_index_lsp_command = None;
225+
}
226+
}
227+
207228
if !self.min_confidence.is_finite() {
208229
self.min_confidence = default_min_confidence();
209230
} else if !(0.0..=1.0).contains(&self.min_confidence) {
@@ -342,6 +363,10 @@ fn default_symbol_index_max_locations() -> usize {
342363
5
343364
}
344365

366+
fn default_symbol_index_provider() -> String {
367+
"regex".to_string()
368+
}
369+
345370
fn default_feedback_path() -> PathBuf {
346371
PathBuf::from(".diffscope.feedback.json")
347372
}

0 commit comments

Comments
 (0)