use crate::compat::call_sites::{AttrCtxScanner, strip_line_comment};
use crate::compat::patch::{Hunk, HunkLine};
use crate::model::names::RelativePath;
use crate::model::symbol::LineClass;
#[derive(Debug, Clone, Copy)]
pub struct AddedLinesSubject<'a> {
pub source_path: &'a RelativePath,
pub added_text: &'a str,
pub line_map: &'a [u32],
}
pub fn added_lines_with_offsets(hunks: &[Hunk]) -> (String, Vec<u32>) {
let mut blob = String::new();
let mut line_map = Vec::new();
for hunk in hunks {
let mut new_line = hunk.new_start as u32;
for line in &hunk.lines {
match line {
HunkLine::Added(s) => {
blob.push_str(s);
blob.push('\n');
line_map.push(new_line);
new_line += 1;
}
HunkLine::Context(_) => new_line += 1,
HunkLine::Removed(_) => {}
}
}
}
(blob, line_map)
}
#[derive(Debug, Clone, Copy)]
pub enum LineContextSource<'a> {
PostImage(&'a str),
HunksOnly,
}
pub fn added_lines_with_context(
hunks: &[Hunk],
source: LineContextSource<'_>,
) -> (String, Vec<u32>, Vec<LineClass>) {
let (blob, line_map) = added_lines_with_offsets(hunks);
let ctx = match source {
LineContextSource::PostImage(text) => post_image_classes(text, &blob, &line_map),
LineContextSource::HunksOnly => None,
}
.unwrap_or_else(|| hunk_classes(hunks));
(blob, line_map, ctx)
}
fn post_image_classes(text: &str, blob: &str, line_map: &[u32]) -> Option<Vec<LineClass>> {
let mut scanner = AttrCtxScanner::new();
let classified: Vec<(&str, LineClass)> = text
.lines()
.map(|line| (line, scanner.classify(strip_line_comment(line))))
.collect();
blob.lines()
.zip(line_map)
.map(|(added, &file_line)| {
let (line, class) = classified.get((file_line as usize).checked_sub(1)?)?;
(line.trim_end() == added.trim_end()).then(|| class.clone())
})
.collect()
}
fn hunk_classes(hunks: &[Hunk]) -> Vec<LineClass> {
let mut ctx = Vec::new();
let mut scanner = AttrCtxScanner::new();
for hunk in hunks {
for line in &hunk.lines {
match line {
HunkLine::Added(s) => ctx.push(scanner.classify(strip_line_comment(s))),
HunkLine::Context(s) => {
scanner.classify(strip_line_comment(s));
}
HunkLine::Removed(_) => {}
}
}
}
ctx
}
pub fn file_line(line_map: &[u32], blob_line: u32) -> u32 {
line_map
.get(blob_line.saturating_sub(1) as usize)
.copied()
.unwrap_or(blob_line)
}