Skip to content

Commit

Permalink
Merge branch 'master' into feature/view-mode-for-no-changed-file
Browse files Browse the repository at this point in the history
  • Loading branch information
xfbs authored Oct 30, 2024
2 parents b006a66 + 2c47a9e commit 7e379a6
Show file tree
Hide file tree
Showing 2 changed files with 114 additions and 38 deletions.
83 changes: 59 additions & 24 deletions src/components/diff_view.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::{
data::{FileDiff, VersionDiff},
data::{ChunkInfo, FileDiff, VersionDiff},
syntax::{highlight_changes, infer_syntax_for_file, syntect_style_to_css},
};
use bytes::Bytes;
Expand All @@ -16,7 +16,7 @@ struct DiffGroupInfo {
/// The actual changes
group: Vec<(ChangeTag, Vec<(Style, bytes::Bytes)>)>,
/// What range of lines the group covers (used as a Yew list key)
range: std::ops::Range<usize>,
range: ChunkInfo,
/// Whether the group contains an actual diff (and therefore shows some context)
in_context: bool,
}
Expand Down Expand Up @@ -70,31 +70,42 @@ pub fn DiffView(props: &DiffViewProps) -> Html {
// Group contiguous lines by whether they contain an actual diff +/- some context buffer.
let mut cursor = 0;
let mut stack: Vec<DiffGroupInfo> = vec![];

for next_range in ranges {
// out of context lines
if next_range.start != 0 {
if next_range.start() != 0 {
stack.push(DiffGroupInfo {
group: changes.by_ref().take(next_range.start - cursor).collect(),
range: cursor..next_range.start,
group: changes.by_ref().take(next_range.start() - cursor).collect(),
range: ChunkInfo {
range: cursor..next_range.start(),
left_start: (next_range.left_start + cursor).saturating_sub(next_range.start()),
right_start: (next_range.right_start + cursor)
.saturating_sub(next_range.start()),
},
in_context: false,
});
}
// in context lines
stack.push(DiffGroupInfo {
group: changes
.by_ref()
.take(next_range.end - next_range.start)
.take(next_range.end() - next_range.start())
.collect(),
range: next_range.clone(),
in_context: true,
});
cursor = next_range.end;
cursor = next_range.end();
}
if changes.len() > 0 {
// Trailing unchanged lines at the end of a file
stack.push(DiffGroupInfo {
group: changes.by_ref().collect(),
range: cursor..file_diff.changes.len(),
range: ChunkInfo {
range: cursor..file_diff.changes.len(),
left_start: (cursor).saturating_sub(file_diff.summary.added as usize),
right_start: (cursor).saturating_sub(file_diff.summary.removed as usize),
},

// When comparing a version of the crate to itself, this group will
// always contain the full text of the file. Don't collapse it.
in_context: is_identical_version,
Expand Down Expand Up @@ -136,10 +147,10 @@ pub fn UnifiedDiffView(props: &AnyDiffViewProps) -> Html {
.map(|DiffGroupInfo {group, range, in_context}| {
let res = html!{
<DiffLineGroup
key={format!("{:?}", range)}
key={format!("{:?}", range.range)}
group={group.clone()}
{in_context}
group_start_index={overall_index}
group_start_index={(overall_index, range.left_start, range.right_start)}
/>
};
overall_index += group.len();
Expand Down Expand Up @@ -192,7 +203,7 @@ pub fn SplitDiffView(props: &AnyDiffViewProps) -> Html {
key={format!("{:?}", range)}
group={group.clone()}
{in_context}
group_start_index={overall_index}
group_start_index={(overall_index, range.left_start, range.right_start)}
/>
};
overall_index += group.len();
Expand All @@ -218,7 +229,7 @@ fn ExpandIcon() -> Html {
pub struct DiffLineGroupProps {
group: Vec<(ChangeTag, Vec<(Style, bytes::Bytes)>)>,
in_context: bool,
group_start_index: usize,
group_start_index: (usize, usize, usize),
}

#[derive(Properties, PartialEq)]
Expand All @@ -234,8 +245,20 @@ pub fn DiffLineGroup(props: &DiffLineGroupProps) -> Html {
let folded = folded.clone();
Callback::from(move |_| folded.set(!*folded))
};
let group_start_index = props.group_start_index + 1;
let end_index = group_start_index + props.group.len() - 1;

// go from 0-indexed to 1-indexed
let start_index = (
props.group_start_index.0 + 1,
props.group_start_index.1 + 1,
props.group_start_index.2 + 1,
);

// use the fact that folded sections never contain changes
let end_index = (
start_index.0 + props.group.len() - 1,
start_index.1 + props.group.len() - 1,
start_index.2 + props.group.len() - 1,
);

if *folded {
html! {
Expand All @@ -244,33 +267,45 @@ pub fn DiffLineGroup(props: &DiffLineGroupProps) -> Html {
<ExpandIcon />
</button>
<button class={classes!("info")} {onclick}>
{format!("Show lines {group_start_index} to {end_index}")}
{
if start_index.1 == start_index.2 {
format!("Show lines {:?} to {:?}", start_index.1, end_index.1)
} else {
format!("Show lines {:?} to {:?}", (start_index.1,start_index.2), (end_index.1,end_index.2))
}
}
</button>
</div>
}
} else {
let (mut left_idx, mut right_idx) = (start_index.1, start_index.2);
html! {
<>
if !props.in_context {
}
{
props.group.iter().enumerate().map(|(index, (tag, change))| {
let overall_index = group_start_index + index;
let (sign, class) = match tag {
ChangeTag::Delete => ("-", "deletion"),
ChangeTag::Insert => ("+", "insertion"),
ChangeTag::Equal => (" ", "unchanged"),
props.group.iter().map(|(tag, change)| {
let (sign, class, left, right) = match tag {
ChangeTag::Delete => ("-", "deletion", left_idx.to_string(), String::new()),
ChangeTag::Insert => ("+", "insertion", String::new(), right_idx.to_string()),
ChangeTag::Equal => (" ", "unchanged", left_idx.to_string(), right_idx.to_string()),
};
(left_idx, right_idx) = match tag {
ChangeTag::Delete => (left_idx + 1, right_idx),
ChangeTag::Insert => (left_idx, right_idx + 1),
ChangeTag::Equal => (left_idx + 1, right_idx + 1),
};

html! {
<div class={classes!("line", class)}>
<div class="line-number">
<div class="line-number">
{
format!("{overall_index}")
format!("{left}")
}
</div>
<div class="line-number">
{
format!("{overall_index}")
format!("{right}")
}
</div>
<div class="change-icon">
Expand Down
69 changes: 55 additions & 14 deletions src/data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -390,7 +390,9 @@ pub struct FileDiff {
/// Diff in this file
pub changes: Vec<(ChangeTag, Bytes)>,
/// Ranges of lines to show for each file
pub context_ranges: Vec<Range<usize>>,
pub context_ranges: Vec<ChunkInfo>,
// Redundant - alternativly take from files
pub summary: Changes,
}

/// Precomputed diff data
Expand Down Expand Up @@ -473,37 +475,46 @@ impl VersionDiff {
match tag {
ChangeTag::Equal => {}
ChangeTag::Delete => {
// cnt for determining start idx of hunk, wanna start before this line, so do not count current line
offsets.push((index, insertions, deletions));
deletions += 1;
offsets.push(index);
}
ChangeTag::Insert => {
offsets.push((index, insertions, deletions));
insertions += 1;
offsets.push(index);
}
}
}

// compute ranges to show
let mut ranges = vec![];
let mut last_hunk = 0..0;

for offset in offsets.iter() {
let hunk = offset.saturating_sub(CONTEXT_LINES)..*offset + CONTEXT_LINES + 1;
let overlaps_with_last_hunk =
hunk.start.max(last_hunk.start) <= hunk.end.min(last_hunk.end);
let mut last_hunk = (0..0, 0, 0);

for (offset, ins, del) in offsets.iter() {
let hunk_start = offset.saturating_sub(CONTEXT_LINES);
let left_start = hunk_start.saturating_sub(*ins);
let right_start = hunk_start.saturating_sub(*del);

let hunk = (
hunk_start..*offset + CONTEXT_LINES + 1,
left_start,
right_start,
);
let overlaps_with_last_hunk = hunk.0.start.max(last_hunk.0.start)
<= hunk.0.end.min(last_hunk.0.end) + CONTEXT_LINES;
if overlaps_with_last_hunk {
last_hunk = last_hunk.start..hunk.end;
last_hunk = (last_hunk.0.start..hunk.0.end, last_hunk.1, last_hunk.2);
} else {
if last_hunk.end != 0 {
ranges.push(last_hunk.clone());
if last_hunk.0.end != 0 {
ranges.push(last_hunk.clone().into());
}
last_hunk = hunk;
}
}

// Push the last hunk we've computed if any
if last_hunk.end != 0 {
ranges.push(last_hunk)
if last_hunk.0.end != 0 {
ranges.push(last_hunk.into())
}

// compute additions
Expand All @@ -526,6 +537,10 @@ impl VersionDiff {
FileDiff {
changes,
context_ranges: ranges,
summary: Changes {
added: insertions as u64,
removed: deletions as u64,
},
},
);
}
Expand All @@ -540,6 +555,32 @@ impl VersionDiff {
}
}

#[derive(Clone, Debug, PartialEq, Eq, Default)]
pub struct ChunkInfo {
pub range: Range<usize>,
pub left_start: usize,
pub right_start: usize,
}

impl From<(Range<usize>, usize, usize)> for ChunkInfo {
fn from((range, left_start, right_start): (Range<usize>, usize, usize)) -> Self {
ChunkInfo {
range,
left_start,
right_start,
}
}
}

impl ChunkInfo {
pub fn start(&self) -> usize {
self.range.start
}
pub fn end(&self) -> usize {
self.range.end
}
}

#[derive(Default, Clone, Copy, PartialEq, Eq, Debug)]
pub struct Changes {
pub added: u64,
Expand Down

0 comments on commit 7e379a6

Please sign in to comment.