Rework diff rendering to allow putting the cursor into deleted text, soft-wrapping and scrolling deleted text correctly (#22994)
Closes #12553 * [x] Fix `diff_hunk_before` * [x] Fix failure to show deleted text when expanding hunk w/ cursor on second line of the hunk * [x] Failure to expand diff hunk below the cursor. * [x] Delete the whole file, and expand the diff. Backspace over the deleted hunk, panic! * [x] Go-to-line now counts the diff hunks, but it should not * [x] backspace at the beginning of a deleted hunk deletes too much text * [x] Indent guides are rendered incorrectly * [ ] Fix randomized multi buffer tests Maybe: * [ ] Buffer search should include deleted text (in vim mode it turns out I use `/x` all the time to jump to the next x I can see). * [ ] vim: should refuse to switch into insert mode if selection is fully within a diff. * [ ] vim `o` command when cursor is on last line of deleted hunk. * [ ] vim `shift-o` on first line of deleted hunk moves cursor but doesn't insert line * [x] `enter` at end of diff hunk inserts a new line but doesn't move cursor * [x] (`shift-enter` at start of diff hunk does nothing) * [ ] Inserting a line just before an expanded hunk collapses it Release Notes: - Improved diff rendering, allowing you to navigate with your cursor inside of deleted text in diff hunks. --------- Co-authored-by: Conrad <conrad@zed.dev> Co-authored-by: Cole <cole@zed.dev> Co-authored-by: Mikayla <mikayla@zed.dev> Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com> Co-authored-by: Michael <michael@zed.dev> Co-authored-by: Agus <agus@zed.dev> Co-authored-by: João <joao@zed.dev>
This commit is contained in:
co-authored by
Conrad
Cole
Mikayla
Conrad Irwin
Michael
Agus
João
parent
1fdae4bae0
commit
d2c55cbe3d
@@ -4,7 +4,9 @@ use super::{
|
||||
};
|
||||
use gpui::{AnyElement, ElementId, WindowContext};
|
||||
use language::{Chunk, ChunkRenderer, Edit, Point, TextSummary};
|
||||
use multi_buffer::{Anchor, AnchorRangeExt, MultiBufferRow, MultiBufferSnapshot, ToOffset};
|
||||
use multi_buffer::{
|
||||
Anchor, AnchorRangeExt, MultiBufferRow, MultiBufferSnapshot, RowInfo, ToOffset,
|
||||
};
|
||||
use std::{
|
||||
any::TypeId,
|
||||
cmp::{self, Ordering},
|
||||
@@ -336,9 +338,7 @@ impl FoldMap {
|
||||
let mut folds = self.snapshot.folds.iter().peekable();
|
||||
while let Some(fold) = folds.next() {
|
||||
if let Some(next_fold) = folds.peek() {
|
||||
let comparison = fold
|
||||
.range
|
||||
.cmp(&next_fold.range, &self.snapshot.inlay_snapshot.buffer);
|
||||
let comparison = fold.range.cmp(&next_fold.range, self.snapshot.buffer());
|
||||
assert!(comparison.is_le());
|
||||
}
|
||||
}
|
||||
@@ -578,6 +578,10 @@ pub struct FoldSnapshot {
|
||||
}
|
||||
|
||||
impl FoldSnapshot {
|
||||
pub fn buffer(&self) -> &MultiBufferSnapshot {
|
||||
&self.inlay_snapshot.buffer
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn text(&self) -> String {
|
||||
self.chunks(FoldOffset(0)..self.len(), false, Highlights::default())
|
||||
@@ -673,7 +677,7 @@ impl FoldSnapshot {
|
||||
(line_end - line_start) as u32
|
||||
}
|
||||
|
||||
pub fn buffer_rows(&self, start_row: u32) -> FoldBufferRows {
|
||||
pub fn row_infos(&self, start_row: u32) -> FoldRows {
|
||||
if start_row > self.transforms.summary().output.lines.row {
|
||||
panic!("invalid display row {}", start_row);
|
||||
}
|
||||
@@ -684,11 +688,11 @@ impl FoldSnapshot {
|
||||
|
||||
let overshoot = fold_point.0 - cursor.start().0 .0;
|
||||
let inlay_point = InlayPoint(cursor.start().1 .0 + overshoot);
|
||||
let input_buffer_rows = self.inlay_snapshot.buffer_rows(inlay_point.row());
|
||||
let input_rows = self.inlay_snapshot.row_infos(inlay_point.row());
|
||||
|
||||
FoldBufferRows {
|
||||
FoldRows {
|
||||
fold_point,
|
||||
input_buffer_rows,
|
||||
input_rows,
|
||||
cursor,
|
||||
}
|
||||
}
|
||||
@@ -843,8 +847,8 @@ fn push_isomorphic(transforms: &mut SumTree<Transform>, summary: TextSummary) {
|
||||
transforms.update_last(
|
||||
|last| {
|
||||
if !last.is_fold() {
|
||||
last.summary.input += summary.clone();
|
||||
last.summary.output += summary.clone();
|
||||
last.summary.input += summary;
|
||||
last.summary.output += summary;
|
||||
did_merge = true;
|
||||
}
|
||||
},
|
||||
@@ -854,7 +858,7 @@ fn push_isomorphic(transforms: &mut SumTree<Transform>, summary: TextSummary) {
|
||||
transforms.push(
|
||||
Transform {
|
||||
summary: TransformSummary {
|
||||
input: summary.clone(),
|
||||
input: summary,
|
||||
output: summary,
|
||||
},
|
||||
placeholder: None,
|
||||
@@ -1134,25 +1138,25 @@ impl<'a> sum_tree::Dimension<'a, FoldSummary> for usize {
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct FoldBufferRows<'a> {
|
||||
pub struct FoldRows<'a> {
|
||||
cursor: Cursor<'a, Transform, (FoldPoint, InlayPoint)>,
|
||||
input_buffer_rows: InlayBufferRows<'a>,
|
||||
input_rows: InlayBufferRows<'a>,
|
||||
fold_point: FoldPoint,
|
||||
}
|
||||
|
||||
impl<'a> FoldBufferRows<'a> {
|
||||
impl<'a> FoldRows<'a> {
|
||||
pub(crate) fn seek(&mut self, row: u32) {
|
||||
let fold_point = FoldPoint::new(row, 0);
|
||||
self.cursor.seek(&fold_point, Bias::Left, &());
|
||||
let overshoot = fold_point.0 - self.cursor.start().0 .0;
|
||||
let inlay_point = InlayPoint(self.cursor.start().1 .0 + overshoot);
|
||||
self.input_buffer_rows.seek(inlay_point.row());
|
||||
self.input_rows.seek(inlay_point.row());
|
||||
self.fold_point = fold_point;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for FoldBufferRows<'a> {
|
||||
type Item = Option<u32>;
|
||||
impl<'a> Iterator for FoldRows<'a> {
|
||||
type Item = RowInfo;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let mut traversed_fold = false;
|
||||
@@ -1166,11 +1170,11 @@ impl<'a> Iterator for FoldBufferRows<'a> {
|
||||
|
||||
if self.cursor.item().is_some() {
|
||||
if traversed_fold {
|
||||
self.input_buffer_rows.seek(self.cursor.start().1.row());
|
||||
self.input_buffer_rows.next();
|
||||
self.input_rows.seek(self.cursor.start().1 .0.row);
|
||||
self.input_rows.next();
|
||||
}
|
||||
*self.fold_point.row_mut() += 1;
|
||||
self.input_buffer_rows.next()
|
||||
self.input_rows.next()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@@ -1683,12 +1687,12 @@ mod tests {
|
||||
.row();
|
||||
expected_buffer_rows.extend(
|
||||
inlay_snapshot
|
||||
.buffer_rows(prev_row)
|
||||
.row_infos(prev_row)
|
||||
.take((1 + fold_start - prev_row) as usize),
|
||||
);
|
||||
prev_row = 1 + fold_end;
|
||||
}
|
||||
expected_buffer_rows.extend(inlay_snapshot.buffer_rows(prev_row));
|
||||
expected_buffer_rows.extend(inlay_snapshot.row_infos(prev_row));
|
||||
|
||||
assert_eq!(
|
||||
expected_buffer_rows.len(),
|
||||
@@ -1777,7 +1781,7 @@ mod tests {
|
||||
let mut fold_row = 0;
|
||||
while fold_row < expected_buffer_rows.len() as u32 {
|
||||
assert_eq!(
|
||||
snapshot.buffer_rows(fold_row).collect::<Vec<_>>(),
|
||||
snapshot.row_infos(fold_row).collect::<Vec<_>>(),
|
||||
expected_buffer_rows[(fold_row as usize)..],
|
||||
"wrong buffer rows starting at fold row {}",
|
||||
fold_row,
|
||||
@@ -1892,10 +1896,19 @@ mod tests {
|
||||
let (snapshot, _) = map.read(inlay_snapshot, vec![]);
|
||||
assert_eq!(snapshot.text(), "aa⋯cccc\nd⋯eeeee\nffffff\n");
|
||||
assert_eq!(
|
||||
snapshot.buffer_rows(0).collect::<Vec<_>>(),
|
||||
snapshot
|
||||
.row_infos(0)
|
||||
.map(|info| info.buffer_row)
|
||||
.collect::<Vec<_>>(),
|
||||
[Some(0), Some(3), Some(5), Some(6)]
|
||||
);
|
||||
assert_eq!(snapshot.buffer_rows(3).collect::<Vec<_>>(), [Some(6)]);
|
||||
assert_eq!(
|
||||
snapshot
|
||||
.row_infos(3)
|
||||
.map(|info| info.buffer_row)
|
||||
.collect::<Vec<_>>(),
|
||||
[Some(6)]
|
||||
);
|
||||
}
|
||||
|
||||
fn init_test(cx: &mut gpui::AppContext) {
|
||||
|
||||
Reference in New Issue
Block a user