refactor: hide logical comparison rows from painting
This commit is contained in:
+134
-111
@@ -41,8 +41,8 @@ use unicode_width::UnicodeWidthStr;
|
||||
use crate::{
|
||||
chrome::{ChromeLine, PopupAnchor, SegmentConfig},
|
||||
diff::{
|
||||
ChangedFile, DiffState, DisplayRow, FileStatus, Focus, HistoryComparison, PaneLayout,
|
||||
Phase, RowKind, ViewMode,
|
||||
ChangedFile, ColumnEmpty, ColumnRow, ColumnView, DiffState, DisplayRow, FileStatus, Focus,
|
||||
HistoryComparison, PaneLayout, Phase, ViewMode,
|
||||
},
|
||||
editor::{
|
||||
Completion, Cursor, Editor, Hover, Input, Mode, Palette, ResolverLayout, ResolverStatus,
|
||||
@@ -911,7 +911,11 @@ impl BufferViewports {
|
||||
);
|
||||
if !editor.file_picker().is_some_and(|picker| picker.focused)
|
||||
&& (history.caret != Some(identity) || previous_widths != history.widths)
|
||||
&& let Some(display) = history.comparison.column(column).display_row(cursor.row)
|
||||
&& let Some(display) = history
|
||||
.comparison
|
||||
.column(column)
|
||||
.view()
|
||||
.display_row(cursor.row)
|
||||
{
|
||||
let column_offset = display_column(&editor.lines()[cursor.row], cursor.column);
|
||||
let continuation = if history.wrapping[column] {
|
||||
@@ -1012,9 +1016,8 @@ impl BufferViewports {
|
||||
self.panels.caret = paint_diff_editor(
|
||||
frame,
|
||||
view,
|
||||
projection.diff,
|
||||
projection.view(),
|
||||
text_area,
|
||||
projection.focus,
|
||||
(history.top.display, history.left[column]),
|
||||
Some(&history.visible[column]),
|
||||
)
|
||||
@@ -3225,9 +3228,8 @@ fn paint_editor_line(
|
||||
fn paint_diff_editor(
|
||||
frame: &mut Frame,
|
||||
editor: &Editor,
|
||||
diff: &DiffState,
|
||||
view: ColumnView<'_>,
|
||||
area: Rect,
|
||||
focus: Focus,
|
||||
viewport: (usize, usize),
|
||||
wrapped: Option<&[WrappedDiffRow]>,
|
||||
) -> Option<Position> {
|
||||
@@ -3237,8 +3239,11 @@ fn paint_diff_editor(
|
||||
let source_cursor = editor.cursor();
|
||||
let cursor_column = display_column(&editor.lines()[source_cursor.row], source_cursor.column);
|
||||
let mut cursor = None;
|
||||
for (screen, display, segments) in diff_screen_rows(diff, top, area.height, wrapped) {
|
||||
let Some(source) = diff.display_source_row(focus, display) else {
|
||||
let focus = view.info.focus;
|
||||
for (screen, display, segments) in
|
||||
diff_screen_rows(view.info.display_len, top, area.height, wrapped)
|
||||
{
|
||||
let Some(source) = view.native_row(display) else {
|
||||
continue;
|
||||
};
|
||||
let mut line_area = Rect::new(area.x, area.y + screen as u16, area.width, 1);
|
||||
@@ -3895,9 +3900,8 @@ fn draw_diff(
|
||||
document_caret = paint_diff_editor(
|
||||
frame,
|
||||
editor,
|
||||
diff,
|
||||
diff.column_view(Focus::Before).unwrap(),
|
||||
text_area,
|
||||
Focus::Before,
|
||||
(top, before_left),
|
||||
wrapped,
|
||||
)
|
||||
@@ -3936,9 +3940,8 @@ fn draw_diff(
|
||||
document_caret = paint_diff_editor(
|
||||
frame,
|
||||
editor,
|
||||
diff,
|
||||
diff.column_view(Focus::After).unwrap(),
|
||||
text_area,
|
||||
Focus::After,
|
||||
(top, after_left),
|
||||
wrapped,
|
||||
)
|
||||
@@ -4269,25 +4272,13 @@ fn draw_diff_pane(
|
||||
} else {
|
||||
diff_pane_panel(frame, diff, Some(file), area, before, resolver)
|
||||
};
|
||||
if diff.display_rows.is_empty() {
|
||||
frame.render_widget(
|
||||
Paragraph::new(file.notice.as_deref().map(display_text).unwrap_or_else(|| {
|
||||
if diff.rows.is_empty() {
|
||||
"Empty file"
|
||||
} else {
|
||||
"No text changes"
|
||||
}
|
||||
.into()
|
||||
})),
|
||||
content,
|
||||
);
|
||||
return content;
|
||||
}
|
||||
let view = diff.column_view(focus).expect("prepared comparison column");
|
||||
let unified = diff.view_mode == ViewMode::Unified;
|
||||
let (top, left, previous_width) = viewport;
|
||||
let gutter = diff_gutter(diff, file, usize::from(content.width), focus);
|
||||
let width = usize::from(content.width).saturating_sub(gutter);
|
||||
if !diff.word_wrap
|
||||
if view.info.display_len > 0
|
||||
&& !diff.word_wrap
|
||||
&& !content_only
|
||||
&& previous_width.is_some_and(|previous| usize::from(previous) != width)
|
||||
{
|
||||
@@ -4310,68 +4301,95 @@ fn draw_diff_pane(
|
||||
&& comment.anchor.path == *path
|
||||
&& diff.review_anchor_current(&comment.anchor)
|
||||
});
|
||||
paint_diff_content(
|
||||
frame,
|
||||
view,
|
||||
content,
|
||||
syntax,
|
||||
semantic,
|
||||
(top, left),
|
||||
wrapped,
|
||||
colors,
|
||||
highlight_trailing_whitespace,
|
||||
whitespace_characters,
|
||||
whitespace_scope,
|
||||
review_comments,
|
||||
diff.local_review.selected,
|
||||
)
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn paint_diff_content<'a>(
|
||||
frame: &mut Frame,
|
||||
view: ColumnView<'a>,
|
||||
content: Rect,
|
||||
syntax: [&SyntaxCache; 2],
|
||||
semantic: [&[SemanticToken]; 2],
|
||||
viewport: (usize, usize),
|
||||
wrapped: Option<&[WrappedDiffRow]>,
|
||||
colors: DiffColors,
|
||||
highlight_trailing_whitespace: bool,
|
||||
whitespace_characters: Option<&BTreeMap<char, char>>,
|
||||
whitespace_scope: WhitespaceScope,
|
||||
review_comments: impl Iterator<Item = &'a crate::review::Comment> + Clone,
|
||||
selected_review: Option<u64>,
|
||||
) -> Rect {
|
||||
if view.info.display_len == 0 {
|
||||
let notice = match view.info.empty {
|
||||
ColumnEmpty::Notice(text) => display_text(text),
|
||||
ColumnEmpty::EmptyFile => "Empty file".into(),
|
||||
ColumnEmpty::NoChanges => "No text changes".into(),
|
||||
};
|
||||
frame.render_widget(Paragraph::new(notice), content);
|
||||
return content;
|
||||
}
|
||||
let unified = view.info.unified;
|
||||
let (top, left) = viewport;
|
||||
let gutter = column_gutter(
|
||||
unified,
|
||||
view.info.source_extents,
|
||||
usize::from(content.width),
|
||||
view.info.focus,
|
||||
);
|
||||
let width = usize::from(content.width).saturating_sub(gutter);
|
||||
let selected_anchor = review_comments
|
||||
.clone()
|
||||
.find(|comment| diff.local_review.selected == Some(comment.id))
|
||||
.find(|comment| selected_review == Some(comment.id))
|
||||
.map(|comment| &comment.anchor);
|
||||
let review_source_row = |side, display| {
|
||||
diff.display_source_row(side, display).or_else(|| {
|
||||
let DisplayRow::Line(index) = diff.display_rows[display] else {
|
||||
return None;
|
||||
};
|
||||
let row = &diff.rows[index];
|
||||
(unified && row.kind == RowKind::Equal)
|
||||
.then_some(match side {
|
||||
Focus::Before => row.before,
|
||||
Focus::After => row.after,
|
||||
Focus::Files => None,
|
||||
})
|
||||
.flatten()
|
||||
})
|
||||
};
|
||||
for (screen, display_row, segments) in diff_screen_rows(diff, top, content.height, wrapped) {
|
||||
let row = &diff.display_rows[display_row];
|
||||
for (screen, display_row, segments) in
|
||||
diff_screen_rows(view.info.display_len, top, content.height, wrapped)
|
||||
{
|
||||
let row_area = Rect::new(content.x, content.y + screen as u16, content.width, 1);
|
||||
let aligned_row = match row {
|
||||
DisplayRow::Line(index) => *index,
|
||||
DisplayRow::Fold { start, end } => {
|
||||
let line = match view.row(display_row) {
|
||||
Some(ColumnRow::Fold { hidden }) => {
|
||||
frame.render_widget(
|
||||
Paragraph::new(format!(
|
||||
"… {} unchanged lines hidden · [/] expand context",
|
||||
end - start
|
||||
hidden
|
||||
))
|
||||
.style(Style::default().fg(Color::DarkGray)),
|
||||
row_area,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let row = &diff.rows[aligned_row];
|
||||
let before = if unified {
|
||||
diff.display_focus(display_row).unwrap_or(focus) == Focus::Before
|
||||
} else {
|
||||
before
|
||||
};
|
||||
let source = if before { &file.before } else { &file.after };
|
||||
let index = diff.display_source_row(
|
||||
if before { Focus::Before } else { Focus::After },
|
||||
display_row,
|
||||
);
|
||||
let Some(index) = index else {
|
||||
let style = Style::default().fg(Color::DarkGray).bg(Color::Reset);
|
||||
let buffer = frame.buffer_mut();
|
||||
for x in 0..content.width {
|
||||
buffer[(content.x + x, row_area.y)]
|
||||
.set_symbol("╱")
|
||||
.set_style(style);
|
||||
Some(ColumnRow::Text(line)) => line,
|
||||
Some(ColumnRow::Gap) => {
|
||||
let style = Style::default().fg(Color::DarkGray).bg(Color::Reset);
|
||||
let buffer = frame.buffer_mut();
|
||||
for x in 0..content.width {
|
||||
buffer[(content.x + x, row_area.y)]
|
||||
.set_symbol("╱")
|
||||
.set_style(style);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
continue;
|
||||
};
|
||||
let source_text = match source.get(index) {
|
||||
Some(text) => text.as_str(),
|
||||
None if resolver.is_some() && index == source.len() => "",
|
||||
None => continue,
|
||||
};
|
||||
let Some(source_text) = line.text else {
|
||||
continue;
|
||||
};
|
||||
let before = line.side == Focus::Before;
|
||||
let index = line.source_row;
|
||||
let segment = segments.and_then(|segments| segments[usize::from(!before)]);
|
||||
let left = segment.map_or(left, |segment| segment.start);
|
||||
let text_width = segment.map_or(width, |segment| segment.end - segment.start);
|
||||
@@ -4381,15 +4399,12 @@ fn draw_diff_pane(
|
||||
segment_paint_width(segment, final_segment, width)
|
||||
});
|
||||
let text_area = Rect::new(content.x + gutter as u16, row_area.y, paint_width as u16, 1);
|
||||
let changed = matches!(row.kind, RowKind::Changed)
|
||||
|| (before && row.kind == RowKind::Removed)
|
||||
|| (!before && row.kind == RowKind::Added);
|
||||
let background =
|
||||
if changed && !matches!(file.status, FileStatus::Added | FileStatus::Removed) {
|
||||
colors.row[usize::from(!before)]
|
||||
} else {
|
||||
Color::Reset
|
||||
};
|
||||
let changed = line.changed;
|
||||
let background = if changed && line.emphasize {
|
||||
colors.row[usize::from(!before)]
|
||||
} else {
|
||||
Color::Reset
|
||||
};
|
||||
frame
|
||||
.buffer_mut()
|
||||
.set_style(row_area, Style::default().bg(background));
|
||||
@@ -4413,14 +4428,11 @@ fn draw_diff_pane(
|
||||
Color::DarkGray
|
||||
};
|
||||
let label = if unified {
|
||||
let old = diff
|
||||
.display_source_row(Focus::Before, display_row)
|
||||
.map(|row| row + 1);
|
||||
let new = diff
|
||||
.display_source_row(Focus::After, display_row)
|
||||
.map(|row| row + 1);
|
||||
let old_digits = file.before.len().max(1).ilog10() as usize + 1;
|
||||
let new_digits = file.after.len().max(1).ilog10() as usize + 1;
|
||||
let [old, new] = line.numbers.map(|row| row.map(|row| row + 1));
|
||||
let [old_digits, new_digits] = view
|
||||
.info
|
||||
.stored_lengths
|
||||
.map(|len| len.max(1).ilog10() as usize + 1);
|
||||
match (old, new) {
|
||||
(Some(old), Some(new)) => {
|
||||
format!("{old:>old_digits$} {new:>new_digits$}{marker} ")
|
||||
@@ -4430,7 +4442,10 @@ fn draw_diff_pane(
|
||||
(None, None) => unreachable!("displayed diff line has a source"),
|
||||
}
|
||||
} else {
|
||||
let digits = diff_source_len(diff, file, before).max(1).ilog10() as usize + 1;
|
||||
let digits = view.info.source_extents[usize::from(!before)]
|
||||
.max(1)
|
||||
.ilog10() as usize
|
||||
+ 1;
|
||||
format!("{:>digits$}{marker} ", index + 1)
|
||||
};
|
||||
frame.render_widget(
|
||||
@@ -4462,19 +4477,13 @@ fn draw_diff_pane(
|
||||
characters,
|
||||
whitespace_scope,
|
||||
syntax[usize::from(!before)].string_spans(index),
|
||||
index + 1 < source.len()
|
||||
|| if before {
|
||||
file.before_eof
|
||||
} else {
|
||||
file.after_eof
|
||||
},
|
||||
line.newline,
|
||||
true,
|
||||
);
|
||||
}
|
||||
if !matches!(file.status, FileStatus::Added | FileStatus::Removed) {
|
||||
if line.emphasize {
|
||||
let style = Style::default().bg(colors.character[usize::from(!before)]);
|
||||
for change in &row.changes {
|
||||
let range = &change[usize::from(!before)];
|
||||
for range in line.changes() {
|
||||
if !range.is_empty() {
|
||||
paint_grapheme_range(frame, text_area, left, source_text, range.clone(), style);
|
||||
}
|
||||
@@ -4504,7 +4513,8 @@ fn draw_diff_pane(
|
||||
crate::review::Side::Before => Focus::Before,
|
||||
crate::review::Side::After => Focus::After,
|
||||
};
|
||||
if let Some(source_row) = review_source_row(side, display_row)
|
||||
if let Some(source_row) = view
|
||||
.review_row(side, display_row)
|
||||
.filter(|source_row| anchor.rows().contains(source_row))
|
||||
{
|
||||
let start = if source_row == anchor.start.row {
|
||||
@@ -4534,9 +4544,11 @@ fn draw_diff_pane(
|
||||
crate::review::Side::Before => Focus::Before,
|
||||
crate::review::Side::After => Focus::After,
|
||||
};
|
||||
let selected = diff.local_review.selected == Some(comment.id);
|
||||
for (screen, display, _) in diff_screen_rows(diff, top, content.height, wrapped) {
|
||||
let source = review_source_row(side, display);
|
||||
let selected = selected_review == Some(comment.id);
|
||||
for (screen, display, _) in
|
||||
diff_screen_rows(view.info.display_len, top, content.height, wrapped)
|
||||
{
|
||||
let source = view.review_row(side, display);
|
||||
if wrapped.is_some_and(|rows| rows[screen].position.continuation != 0)
|
||||
|| !source.is_some_and(|row| comment.anchor.rows().contains(&row))
|
||||
{
|
||||
@@ -4687,9 +4699,20 @@ fn diff_source_len(diff: &DiffState, file: &ChangedFile, before: bool) -> usize
|
||||
}
|
||||
|
||||
fn diff_gutter(diff: &DiffState, file: &ChangedFile, width: usize, focus: Focus) -> usize {
|
||||
let old = diff_source_len(diff, file, true).max(1).ilog10() as usize + 1;
|
||||
let new = diff_source_len(diff, file, false).max(1).ilog10() as usize + 1;
|
||||
let gutter = if diff.view_mode == ViewMode::Unified {
|
||||
column_gutter(
|
||||
diff.view_mode == ViewMode::Unified,
|
||||
[
|
||||
diff_source_len(diff, file, true),
|
||||
diff_source_len(diff, file, false),
|
||||
],
|
||||
width,
|
||||
focus,
|
||||
)
|
||||
}
|
||||
|
||||
fn column_gutter(unified: bool, extents: [usize; 2], width: usize, focus: Focus) -> usize {
|
||||
let [old, new] = extents.map(|len| len.max(1).ilog10() as usize + 1);
|
||||
let gutter = if unified {
|
||||
old + new + 3
|
||||
} else {
|
||||
(if focus == Focus::Before { old } else { new }) + 2
|
||||
@@ -5103,7 +5126,7 @@ impl DiffWrapViewport {
|
||||
}
|
||||
|
||||
fn diff_screen_rows<'a>(
|
||||
diff: &'a DiffState,
|
||||
display_len: usize,
|
||||
top: usize,
|
||||
height: u16,
|
||||
wrapped: Option<&'a [WrappedDiffRow]>,
|
||||
@@ -5120,7 +5143,7 @@ fn diff_screen_rows<'a>(
|
||||
)
|
||||
})
|
||||
} else {
|
||||
(top + screen < diff.display_rows.len()).then_some((screen, top + screen, None))
|
||||
(top + screen < display_len).then_some((screen, top + screen, None))
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -5268,7 +5291,7 @@ fn diff_visible_rows(
|
||||
} else {
|
||||
pane.height
|
||||
};
|
||||
for (_, display, _) in diff_screen_rows(diff, top, height, wrapped) {
|
||||
for (_, display, _) in diff_screen_rows(diff.display_rows.len(), top, height, wrapped) {
|
||||
let source =
|
||||
diff.display_source_row(if before { Focus::Before } else { Focus::After }, display);
|
||||
if ranges
|
||||
|
||||
Reference in New Issue
Block a user