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:
Max Brunsfeld 2025-01-24 13:18:22 -08:00 committed by GitHub
parent 1fdae4bae0
commit d2c55cbe3d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
64 changed files with 7653 additions and 5495 deletions

View file

@ -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 {:?}",

View file

@ -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) {

View file

@ -1,7 +1,9 @@
use crate::{HighlightStyles, InlayId};
use collections::BTreeSet;
use language::{Chunk, Edit, Point, TextSummary};
use multi_buffer::{Anchor, MultiBufferRow, MultiBufferRows, MultiBufferSnapshot, ToOffset};
use multi_buffer::{
Anchor, MultiBufferRow, MultiBufferRows, MultiBufferSnapshot, RowInfo, ToOffset,
};
use std::{
cmp,
ops::{Add, AddAssign, Range, Sub, SubAssign},
@ -67,11 +69,11 @@ impl Inlay {
impl sum_tree::Item for Transform {
type Summary = TransformSummary;
fn summary(&self, _cx: &()) -> Self::Summary {
fn summary(&self, _: &()) -> Self::Summary {
match self {
Transform::Isomorphic(summary) => TransformSummary {
input: summary.clone(),
output: summary.clone(),
input: *summary,
output: *summary,
},
Transform::Inlay(inlay) => TransformSummary {
input: TextSummary::default(),
@ -362,14 +364,14 @@ impl<'a> InlayBufferRows<'a> {
}
impl<'a> Iterator for InlayBufferRows<'a> {
type Item = Option<u32>;
type Item = RowInfo;
fn next(&mut self) -> Option<Self::Item> {
let buffer_row = if self.inlay_row == 0 {
self.buffer_rows.next().unwrap()
} else {
match self.transforms.item()? {
Transform::Inlay(_) => None,
Transform::Inlay(_) => Default::default(),
Transform::Isomorphic(_) => self.buffer_rows.next().unwrap(),
}
};
@ -448,7 +450,7 @@ impl InlayMap {
new_transforms.append(cursor.slice(&buffer_edit.old.start, Bias::Left, &()), &());
if let Some(Transform::Isomorphic(transform)) = cursor.item() {
if cursor.end(&()).0 == buffer_edit.old.start {
push_isomorphic(&mut new_transforms, transform.clone());
push_isomorphic(&mut new_transforms, *transform);
cursor.next(&());
}
}
@ -892,7 +894,7 @@ impl InlaySnapshot {
}
pub fn text_summary(&self) -> TextSummary {
self.transforms.summary().output.clone()
self.transforms.summary().output
}
pub fn text_summary_for_range(&self, range: Range<InlayOffset>) -> TextSummary {
@ -945,7 +947,7 @@ impl InlaySnapshot {
summary
}
pub fn buffer_rows(&self, row: u32) -> InlayBufferRows<'_> {
pub fn row_infos(&self, row: u32) -> InlayBufferRows<'_> {
let mut cursor = self.transforms.cursor::<(InlayPoint, Point)>(&());
let inlay_point = InlayPoint::new(row, 0);
cursor.seek(&inlay_point, Bias::Left, &());
@ -967,7 +969,7 @@ impl InlaySnapshot {
InlayBufferRows {
transforms: cursor,
inlay_row: inlay_point.row(),
buffer_rows: self.buffer.buffer_rows(buffer_row),
buffer_rows: self.buffer.row_infos(buffer_row),
max_buffer_row,
}
}
@ -1477,7 +1479,10 @@ mod tests {
);
assert_eq!(inlay_snapshot.text(), "|123|\nabc\n|456|def\n|567|\n\nghi");
assert_eq!(
inlay_snapshot.buffer_rows(0).collect::<Vec<_>>(),
inlay_snapshot
.row_infos(0)
.map(|info| info.buffer_row)
.collect::<Vec<_>>(),
vec![Some(0), None, Some(1), None, None, Some(2)]
);
}
@ -1548,7 +1553,7 @@ mod tests {
}
assert_eq!(inlay_snapshot.text(), expected_text.to_string());
let expected_buffer_rows = inlay_snapshot.buffer_rows(0).collect::<Vec<_>>();
let expected_buffer_rows = inlay_snapshot.row_infos(0).collect::<Vec<_>>();
assert_eq!(
expected_buffer_rows.len() as u32,
expected_text.max_point().row + 1
@ -1556,7 +1561,7 @@ mod tests {
for row_start in 0..expected_buffer_rows.len() {
assert_eq!(
inlay_snapshot
.buffer_rows(row_start as u32)
.row_infos(row_start as u32)
.collect::<Vec<_>>(),
&expected_buffer_rows[row_start..],
"incorrect buffer rows starting at {}",

View file

@ -272,8 +272,8 @@ impl TabSnapshot {
}
}
pub fn buffer_rows(&self, row: u32) -> fold_map::FoldBufferRows<'_> {
self.fold_snapshot.buffer_rows(row)
pub fn rows(&self, row: u32) -> fold_map::FoldRows<'_> {
self.fold_snapshot.row_infos(row)
}
#[cfg(test)]

View file

@ -1,11 +1,11 @@
use super::{
fold_map::FoldBufferRows,
fold_map::FoldRows,
tab_map::{self, TabEdit, TabPoint, TabSnapshot},
Highlights,
};
use gpui::{AppContext, Context, Font, LineWrapper, Model, ModelContext, Pixels, Task};
use language::{Chunk, Point};
use multi_buffer::MultiBufferSnapshot;
use multi_buffer::{MultiBufferSnapshot, RowInfo};
use smol::future::yield_now;
use std::sync::LazyLock;
use std::{cmp, collections::VecDeque, mem, ops::Range, time::Duration};
@ -60,16 +60,16 @@ pub struct WrapChunks<'a> {
}
#[derive(Clone)]
pub struct WrapBufferRows<'a> {
input_buffer_rows: FoldBufferRows<'a>,
input_buffer_row: Option<u32>,
pub struct WrapRows<'a> {
input_buffer_rows: FoldRows<'a>,
input_buffer_row: RowInfo,
output_row: u32,
soft_wrapped: bool,
max_output_row: u32,
transforms: Cursor<'a, Transform, (WrapPoint, TabPoint)>,
}
impl<'a> WrapBufferRows<'a> {
impl<'a> WrapRows<'a> {
pub(crate) fn seek(&mut self, start_row: u32) {
self.transforms
.seek(&WrapPoint::new(start_row, 0), Bias::Left, &());
@ -717,7 +717,7 @@ impl WrapSnapshot {
self.transforms.summary().output.longest_row
}
pub fn buffer_rows(&self, start_row: u32) -> WrapBufferRows {
pub fn row_infos(&self, start_row: u32) -> WrapRows {
let mut transforms = self.transforms.cursor::<(WrapPoint, TabPoint)>(&());
transforms.seek(&WrapPoint::new(start_row, 0), Bias::Left, &());
let mut input_row = transforms.start().1.row();
@ -725,9 +725,9 @@ impl WrapSnapshot {
input_row += start_row - transforms.start().0.row();
}
let soft_wrapped = transforms.item().map_or(false, |t| !t.is_isomorphic());
let mut input_buffer_rows = self.tab_snapshot.buffer_rows(input_row);
let mut input_buffer_rows = self.tab_snapshot.rows(input_row);
let input_buffer_row = input_buffer_rows.next().unwrap();
WrapBufferRows {
WrapRows {
transforms,
input_buffer_row,
input_buffer_rows,
@ -847,7 +847,7 @@ impl WrapSnapshot {
}
let text = language::Rope::from(self.text().as_str());
let mut input_buffer_rows = self.tab_snapshot.buffer_rows(0);
let mut input_buffer_rows = self.tab_snapshot.rows(0);
let mut expected_buffer_rows = Vec::new();
let mut prev_tab_row = 0;
for display_row in 0..=self.max_point().row() {
@ -855,7 +855,7 @@ impl WrapSnapshot {
if tab_point.row() == prev_tab_row && display_row != 0 {
expected_buffer_rows.push(None);
} else {
expected_buffer_rows.push(input_buffer_rows.next().unwrap());
expected_buffer_rows.push(input_buffer_rows.next().unwrap().buffer_row);
}
prev_tab_row = tab_point.row();
@ -864,7 +864,8 @@ impl WrapSnapshot {
for start_display_row in 0..expected_buffer_rows.len() {
assert_eq!(
self.buffer_rows(start_display_row as u32)
self.row_infos(start_display_row as u32)
.map(|row_info| row_info.buffer_row)
.collect::<Vec<_>>(),
&expected_buffer_rows[start_display_row..],
"invalid buffer_rows({}..)",
@ -958,8 +959,8 @@ impl<'a> Iterator for WrapChunks<'a> {
}
}
impl<'a> Iterator for WrapBufferRows<'a> {
type Item = Option<u32>;
impl<'a> Iterator for WrapRows<'a> {
type Item = RowInfo;
fn next(&mut self) -> Option<Self::Item> {
if self.output_row > self.max_output_row {
@ -968,6 +969,7 @@ impl<'a> Iterator for WrapBufferRows<'a> {
let buffer_row = self.input_buffer_row;
let soft_wrapped = self.soft_wrapped;
let diff_status = self.input_buffer_row.diff_status;
self.output_row += 1;
self.transforms
@ -979,7 +981,15 @@ impl<'a> Iterator for WrapBufferRows<'a> {
self.soft_wrapped = true;
}
Some(if soft_wrapped { None } else { buffer_row })
Some(if soft_wrapped {
RowInfo {
buffer_row: None,
multibuffer_row: None,
diff_status,
}
} else {
buffer_row
})
}
}