Files
galaxy/app/src/code_review/context.rs
T

131 lines
4.5 KiB
Rust

use crate::ai::agent::DiffSetHunk;
use crate::code_review::diff_state::{DiffLineType, FileDiff};
use std::collections::HashMap;
use galaxy_editor::render::model::LineCount;
cfg_if::cfg_if! {
if #[cfg(feature = "local_fs")] {
use std::path::Path;
use crate::ai::agent::{AIAgentAttachment, CurrentHead, DiffBase};
use crate::ai::blocklist::BlocklistAIContextModel;
use crate::code_review::{diff_state::DiffMode, DiffSetScope};
use galaxyui::{AppContext, ModelHandle};
}
}
/// Converts file diffs into a HashMap of file paths to DiffSetHunks
/// If repo_path is provided, file paths will be relative to the repo root
pub fn convert_file_diffs_to_diffset_hunks<'a, I>(files: I) -> HashMap<String, Vec<DiffSetHunk>>
where
I: Iterator<Item = &'a FileDiff>,
{
let mut file_diffs: HashMap<String, Vec<DiffSetHunk>> = HashMap::new();
for file_diff in files {
let file_path = file_diff.file_path.display().to_string();
let mut file_hunks = Vec::new();
for hunk in file_diff.hunks.iter() {
// Format the diff content for this hunk
let mut diff_lines = Vec::new();
let mut lines_added = 0;
let mut lines_removed = 0;
for line in &hunk.lines {
let prefix = match line.line_type {
DiffLineType::Add => {
lines_added += 1;
"+"
}
DiffLineType::Delete => {
lines_removed += 1;
"-"
}
DiffLineType::Context => "",
DiffLineType::HunkHeader => continue,
};
diff_lines.push(format!("{}{}", prefix, line.text));
}
let diff_content = diff_lines.join("\n");
// Create line range using LineCount: Note that git lines are 1-based and LineCount is 0-based
let line_range = LineCount::from(hunk.new_start_line.saturating_sub(1))
..LineCount::from(hunk.new_start_line.saturating_sub(1) + hunk.new_line_count);
file_hunks.push(DiffSetHunk {
line_range,
diff_content,
lines_added,
lines_removed,
});
}
if !file_hunks.is_empty() {
file_diffs.insert(file_path, file_hunks);
}
}
file_diffs
}
/// Creates attachment reference and key for a set of changes based on scope and diff mode
#[cfg(feature = "local_fs")]
pub fn create_attachment_reference_and_key(
scope: &DiffSetScope,
diff_mode: &DiffMode,
main_branch_name: Option<&str>,
repo_path: &Path,
) -> (String, String) {
match scope {
DiffSetScope::All => {
let diff_set_description = match diff_mode {
DiffMode::Head => "uncommitted changes".to_string(),
DiffMode::MainBranch => {
let main_branch = main_branch_name.unwrap_or("main");
format!("diffset against {main_branch}")
}
DiffMode::OtherBranch(branch_name) => {
format!("diffset against {branch_name}")
}
};
let key = diff_set_description.clone();
(format!("<change:{key}>"), key)
}
DiffSetScope::File(file_path) => {
let relative_path = if file_path.is_absolute() {
file_path
.strip_prefix(repo_path)
.unwrap_or(file_path)
.to_path_buf()
} else {
file_path.clone()
};
let key = relative_path.display().to_string();
(format!("<change:{key}>"), key)
}
}
}
/// Registers a DiffSet attachment with the AI controller
/// This encapsulates the common logic for creating and registering diff attachments
#[cfg(feature = "local_fs")]
pub fn register_diffset_attachment(
ai_context_model: &ModelHandle<BlocklistAIContextModel>,
attachment_key: String,
file_diffs: HashMap<String, Vec<DiffSetHunk>>,
current: Option<CurrentHead>,
base: DiffBase,
ctx: &mut AppContext,
) {
// Create the DiffSet attachment
let attachment = AIAgentAttachment::DiffSet {
file_diffs,
current,
base,
};
// Register the attachment with the AI controller
ai_context_model.update(ctx, |context_model, _| {
context_model.register_diff_hunk_attachment(attachment_key, attachment);
});
}