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:
parent
1fdae4bae0
commit
d2c55cbe3d
64 changed files with 7653 additions and 5495 deletions
|
@ -7,8 +7,8 @@ use collections::{Bound, HashMap, HashSet};
|
|||
use gpui::{AnyElement, AppContext, EntityId, Pixels, WindowContext};
|
||||
use language::{Chunk, Patch, Point};
|
||||
use multi_buffer::{
|
||||
Anchor, ExcerptId, ExcerptInfo, MultiBuffer, MultiBufferRow, MultiBufferSnapshot, ToOffset,
|
||||
ToPoint as _,
|
||||
Anchor, ExcerptId, ExcerptInfo, MultiBuffer, MultiBufferRow, MultiBufferSnapshot, RowInfo,
|
||||
ToOffset, ToPoint as _,
|
||||
};
|
||||
use parking_lot::Mutex;
|
||||
use std::{
|
||||
|
@ -399,9 +399,9 @@ pub struct BlockChunks<'a> {
|
|||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct BlockBufferRows<'a> {
|
||||
pub struct BlockRows<'a> {
|
||||
transforms: sum_tree::Cursor<'a, Transform, (BlockRow, WrapRow)>,
|
||||
input_buffer_rows: wrap_map::WrapBufferRows<'a>,
|
||||
input_rows: wrap_map::WrapRows<'a>,
|
||||
output_row: BlockRow,
|
||||
started: bool,
|
||||
}
|
||||
|
@ -777,14 +777,12 @@ impl BlockMap {
|
|||
if let Some(new_buffer_id) = new_buffer_id {
|
||||
let first_excerpt = excerpt_boundary.next.clone().unwrap();
|
||||
if folded_buffers.contains(&new_buffer_id) {
|
||||
let mut buffer_end = Point::new(excerpt_boundary.row.0, 0)
|
||||
+ excerpt_boundary.next.as_ref().unwrap().text_summary.lines;
|
||||
let mut last_excerpt_end_row = first_excerpt.end_row;
|
||||
|
||||
while let Some(next_boundary) = boundaries.peek() {
|
||||
if let Some(next_excerpt_boundary) = &next_boundary.next {
|
||||
if next_excerpt_boundary.buffer_id == new_buffer_id {
|
||||
buffer_end = Point::new(next_boundary.row.0, 0)
|
||||
+ next_excerpt_boundary.text_summary.lines;
|
||||
last_excerpt_end_row = next_excerpt_boundary.end_row;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
@ -793,7 +791,15 @@ impl BlockMap {
|
|||
boundaries.next();
|
||||
}
|
||||
|
||||
let wrap_end_row = wrap_snapshot.make_wrap_point(buffer_end, Bias::Right).row();
|
||||
let wrap_end_row = wrap_snapshot
|
||||
.make_wrap_point(
|
||||
Point::new(
|
||||
last_excerpt_end_row.0,
|
||||
buffer.line_len(last_excerpt_end_row),
|
||||
),
|
||||
Bias::Right,
|
||||
)
|
||||
.row();
|
||||
|
||||
return Some((
|
||||
BlockPlacement::Replace(WrapRow(wrap_row)..=WrapRow(wrap_end_row)),
|
||||
|
@ -1360,7 +1366,7 @@ impl BlockSnapshot {
|
|||
}
|
||||
}
|
||||
|
||||
pub(super) fn buffer_rows(&self, start_row: BlockRow) -> BlockBufferRows {
|
||||
pub(super) fn row_infos(&self, start_row: BlockRow) -> BlockRows {
|
||||
let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>(&());
|
||||
cursor.seek(&start_row, Bias::Right, &());
|
||||
let (output_start, input_start) = cursor.start();
|
||||
|
@ -1373,9 +1379,9 @@ impl BlockSnapshot {
|
|||
0
|
||||
};
|
||||
let input_start_row = input_start.0 + overshoot;
|
||||
BlockBufferRows {
|
||||
BlockRows {
|
||||
transforms: cursor,
|
||||
input_buffer_rows: self.wrap_snapshot.buffer_rows(input_start_row),
|
||||
input_rows: self.wrap_snapshot.row_infos(input_start_row),
|
||||
output_row: start_row,
|
||||
started: false,
|
||||
}
|
||||
|
@ -1480,7 +1486,7 @@ impl BlockSnapshot {
|
|||
}
|
||||
BlockId::ExcerptBoundary(next_excerpt_id) => {
|
||||
if let Some(next_excerpt_id) = next_excerpt_id {
|
||||
let excerpt_range = buffer.range_for_excerpt::<Point>(next_excerpt_id)?;
|
||||
let excerpt_range = buffer.range_for_excerpt(next_excerpt_id)?;
|
||||
self.wrap_snapshot
|
||||
.make_wrap_point(excerpt_range.start, Bias::Left)
|
||||
} else {
|
||||
|
@ -1488,10 +1494,9 @@ impl BlockSnapshot {
|
|||
.make_wrap_point(buffer.max_point(), Bias::Left)
|
||||
}
|
||||
}
|
||||
BlockId::FoldedBuffer(excerpt_id) => self.wrap_snapshot.make_wrap_point(
|
||||
buffer.range_for_excerpt::<Point>(excerpt_id)?.start,
|
||||
Bias::Left,
|
||||
),
|
||||
BlockId::FoldedBuffer(excerpt_id) => self
|
||||
.wrap_snapshot
|
||||
.make_wrap_point(buffer.range_for_excerpt(excerpt_id)?.start, Bias::Left),
|
||||
};
|
||||
let wrap_row = WrapRow(wrap_point.row());
|
||||
|
||||
|
@ -1832,8 +1837,8 @@ impl<'a> Iterator for BlockChunks<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for BlockBufferRows<'a> {
|
||||
type Item = Option<u32>;
|
||||
impl<'a> Iterator for BlockRows<'a> {
|
||||
type Item = RowInfo;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.started {
|
||||
|
@ -1862,7 +1867,7 @@ impl<'a> Iterator for BlockBufferRows<'a> {
|
|||
.as_ref()
|
||||
.map_or(true, |block| block.is_replacement())
|
||||
{
|
||||
self.input_buffer_rows.seek(self.transforms.start().1 .0);
|
||||
self.input_rows.seek(self.transforms.start().1 .0);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1870,15 +1875,15 @@ impl<'a> Iterator for BlockBufferRows<'a> {
|
|||
if let Some(block) = transform.block.as_ref() {
|
||||
if block.is_replacement() && self.transforms.start().0 == self.output_row {
|
||||
if matches!(block, Block::FoldedBuffer { .. }) {
|
||||
Some(None)
|
||||
Some(RowInfo::default())
|
||||
} else {
|
||||
Some(self.input_buffer_rows.next().unwrap())
|
||||
Some(self.input_rows.next().unwrap())
|
||||
}
|
||||
} else {
|
||||
Some(None)
|
||||
Some(RowInfo::default())
|
||||
}
|
||||
} else {
|
||||
Some(self.input_buffer_rows.next().unwrap())
|
||||
Some(self.input_rows.next().unwrap())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2153,7 +2158,10 @@ mod tests {
|
|||
);
|
||||
|
||||
assert_eq!(
|
||||
snapshot.buffer_rows(BlockRow(0)).collect::<Vec<_>>(),
|
||||
snapshot
|
||||
.row_infos(BlockRow(0))
|
||||
.map(|row_info| row_info.buffer_row)
|
||||
.collect::<Vec<_>>(),
|
||||
&[
|
||||
Some(0),
|
||||
None,
|
||||
|
@ -2603,7 +2611,10 @@ mod tests {
|
|||
"\n\n\n111\n\n\n\n\n222\n\n\n333\n\n\n444\n\n\n\n\n555\n\n\n666\n"
|
||||
);
|
||||
assert_eq!(
|
||||
blocks_snapshot.buffer_rows(BlockRow(0)).collect::<Vec<_>>(),
|
||||
blocks_snapshot
|
||||
.row_infos(BlockRow(0))
|
||||
.map(|i| i.buffer_row)
|
||||
.collect::<Vec<_>>(),
|
||||
vec![
|
||||
None,
|
||||
None,
|
||||
|
@ -2679,7 +2690,10 @@ mod tests {
|
|||
"\n\n\n111\n\n\n\n\n\n222\n\n\n\n333\n\n\n444\n\n\n\n\n\n\n555\n\n\n666\n\n"
|
||||
);
|
||||
assert_eq!(
|
||||
blocks_snapshot.buffer_rows(BlockRow(0)).collect::<Vec<_>>(),
|
||||
blocks_snapshot
|
||||
.row_infos(BlockRow(0))
|
||||
.map(|i| i.buffer_row)
|
||||
.collect::<Vec<_>>(),
|
||||
vec![
|
||||
None,
|
||||
None,
|
||||
|
@ -2754,7 +2768,10 @@ mod tests {
|
|||
"\n\n\n\n\n\n222\n\n\n\n333\n\n\n444\n\n\n\n\n\n\n555\n\n\n666\n\n"
|
||||
);
|
||||
assert_eq!(
|
||||
blocks_snapshot.buffer_rows(BlockRow(0)).collect::<Vec<_>>(),
|
||||
blocks_snapshot
|
||||
.row_infos(BlockRow(0))
|
||||
.map(|i| i.buffer_row)
|
||||
.collect::<Vec<_>>(),
|
||||
vec![
|
||||
None,
|
||||
None,
|
||||
|
@ -2819,7 +2836,10 @@ mod tests {
|
|||
);
|
||||
assert_eq!(blocks_snapshot.text(), "\n\n\n\n\n\n\n\n555\n\n\n666\n\n");
|
||||
assert_eq!(
|
||||
blocks_snapshot.buffer_rows(BlockRow(0)).collect::<Vec<_>>(),
|
||||
blocks_snapshot
|
||||
.row_infos(BlockRow(0))
|
||||
.map(|i| i.buffer_row)
|
||||
.collect::<Vec<_>>(),
|
||||
vec![
|
||||
None,
|
||||
None,
|
||||
|
@ -2873,7 +2893,10 @@ mod tests {
|
|||
"Should have extra newline for 111 buffer, due to a new block added when it was folded"
|
||||
);
|
||||
assert_eq!(
|
||||
blocks_snapshot.buffer_rows(BlockRow(0)).collect::<Vec<_>>(),
|
||||
blocks_snapshot
|
||||
.row_infos(BlockRow(0))
|
||||
.map(|i| i.buffer_row)
|
||||
.collect::<Vec<_>>(),
|
||||
vec![
|
||||
None,
|
||||
None,
|
||||
|
@ -2927,7 +2950,10 @@ mod tests {
|
|||
"Should have a single, first buffer left after folding"
|
||||
);
|
||||
assert_eq!(
|
||||
blocks_snapshot.buffer_rows(BlockRow(0)).collect::<Vec<_>>(),
|
||||
blocks_snapshot
|
||||
.row_infos(BlockRow(0))
|
||||
.map(|i| i.buffer_row)
|
||||
.collect::<Vec<_>>(),
|
||||
vec![
|
||||
None,
|
||||
None,
|
||||
|
@ -2997,7 +3023,10 @@ mod tests {
|
|||
);
|
||||
assert_eq!(blocks_snapshot.text(), "\n");
|
||||
assert_eq!(
|
||||
blocks_snapshot.buffer_rows(BlockRow(0)).collect::<Vec<_>>(),
|
||||
blocks_snapshot
|
||||
.row_infos(BlockRow(0))
|
||||
.map(|i| i.buffer_row)
|
||||
.collect::<Vec<_>>(),
|
||||
vec![None, None],
|
||||
"When fully folded, should be no buffer rows"
|
||||
);
|
||||
|
@ -3295,7 +3324,8 @@ mod tests {
|
|||
let mut sorted_blocks_iter = expected_blocks.into_iter().peekable();
|
||||
|
||||
let input_buffer_rows = buffer_snapshot
|
||||
.buffer_rows(MultiBufferRow(0))
|
||||
.row_infos(MultiBufferRow(0))
|
||||
.map(|row| row.buffer_row)
|
||||
.collect::<Vec<_>>();
|
||||
let mut expected_buffer_rows = Vec::new();
|
||||
let mut expected_text = String::new();
|
||||
|
@ -3450,7 +3480,8 @@ mod tests {
|
|||
);
|
||||
assert_eq!(
|
||||
blocks_snapshot
|
||||
.buffer_rows(BlockRow(start_row as u32))
|
||||
.row_infos(BlockRow(start_row as u32))
|
||||
.map(|row_info| row_info.buffer_row)
|
||||
.collect::<Vec<_>>(),
|
||||
&expected_buffer_rows[start_row..],
|
||||
"incorrect buffer_rows starting at row {:?}",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue