@@ -19,6 +19,7 @@ use std::{
cmp::{self, Ordering},
fmt::Write,
iter::FromIterator,
+ mem,
ops::Range,
path::Path,
sync::Arc,
@@ -56,6 +57,8 @@ pub fn init(app: &mut MutableAppContext) {
Some("BufferView"),
),
Binding::new("cmd-shift-D", "buffer:duplicate_line", Some("BufferView")),
+ Binding::new("ctrl-cmd-up", "buffer:move_line_up", Some("BufferView")),
+ Binding::new("ctrl-cmd-down", "buffer:move_line_down", Some("BufferView")),
Binding::new("cmd-x", "buffer:cut", Some("BufferView")),
Binding::new("cmd-c", "buffer:copy", Some("BufferView")),
Binding::new("cmd-v", "buffer:paste", Some("BufferView")),
@@ -171,6 +174,8 @@ pub fn init(app: &mut MutableAppContext) {
BufferView::delete_to_end_of_line,
);
app.add_action("buffer:duplicate_line", BufferView::duplicate_line);
+ app.add_action("buffer:move_line_up", BufferView::move_line_up);
+ app.add_action("buffer:move_line_down", BufferView::move_line_down);
app.add_action("buffer:cut", BufferView::cut);
app.add_action("buffer:copy", BufferView::copy);
app.add_action("buffer:paste", BufferView::paste);
@@ -518,23 +523,30 @@ impl BufferView {
self.pending_selection.is_some()
}
- #[cfg(test)]
- fn select_ranges<'a, T>(&mut self, ranges: T, ctx: &mut ViewContext<Self>) -> Result<()>
+ fn select_ranges<I, T>(&mut self, ranges: I, autoscroll: bool, ctx: &mut ViewContext<Self>)
where
- T: IntoIterator<Item = &'a Range<usize>>,
+ I: IntoIterator<Item = Range<T>>,
+ T: ToOffset,
{
let buffer = self.buffer.read(ctx);
let mut selections = Vec::new();
for range in ranges {
+ let mut start = range.start.to_offset(buffer).unwrap();
+ let mut end = range.end.to_offset(buffer).unwrap();
+ let reversed = if start > end {
+ mem::swap(&mut start, &mut end);
+ true
+ } else {
+ false
+ };
selections.push(Selection {
- start: buffer.anchor_before(range.start)?,
- end: buffer.anchor_before(range.end)?,
- reversed: false,
+ start: buffer.anchor_before(start).unwrap(),
+ end: buffer.anchor_before(end).unwrap(),
+ reversed,
goal_column: None,
});
}
- self.update_selections(selections, false, ctx);
- Ok(())
+ self.update_selections(selections, autoscroll, ctx);
}
#[cfg(test)]
@@ -542,8 +554,6 @@ impl BufferView {
where
T: IntoIterator<Item = &'a Range<DisplayPoint>>,
{
- use std::mem;
-
let map = self.display_map.read(ctx);
let mut selections = Vec::new();
for range in ranges {
@@ -693,7 +703,7 @@ impl BufferView {
let mut selections = self.selections(app).iter().peekable();
while let Some(selection) = selections.next() {
- let mut rows = selection.buffer_rows_for_display_rows(map, app);
+ let (mut rows, _) = selection.buffer_rows_for_display_rows(map, app);
let goal_display_column = selection
.head()
.to_display_point(map, app)
@@ -702,7 +712,7 @@ impl BufferView {
// Accumulate contiguous regions of rows that we want to delete.
while let Some(next_selection) = selections.peek() {
- let next_rows = next_selection.buffer_rows_for_display_rows(map, app);
+ let (next_rows, _) = next_selection.buffer_rows_for_display_rows(map, app);
if next_rows.start <= rows.end {
rows.end = next_rows.end;
selections.next().unwrap();
@@ -750,10 +760,10 @@ impl BufferView {
goal_column: None,
})
.collect();
- self.update_selections(new_selections, true, ctx);
self.buffer
.update(ctx, |buffer, ctx| buffer.edit(edit_ranges, "", Some(ctx)))
.unwrap();
+ self.update_selections(new_selections, true, ctx);
self.end_transaction(ctx);
}
@@ -780,9 +790,9 @@ impl BufferView {
let mut selections_iter = selections.iter_mut().peekable();
while let Some(selection) = selections_iter.next() {
// Avoid duplicating the same lines twice.
- let mut rows = selection.buffer_rows_for_display_rows(map, app);
+ let (mut rows, _) = selection.buffer_rows_for_display_rows(map, app);
while let Some(next_selection) = selections_iter.peek() {
- let next_rows = next_selection.buffer_rows_for_display_rows(map, app);
+ let (next_rows, _) = next_selection.buffer_rows_for_display_rows(map, app);
if next_rows.start <= rows.end - 1 {
rows.end = next_rows.end;
selections_iter.next().unwrap();
@@ -811,14 +821,216 @@ impl BufferView {
// Restore bias on selections.
let buffer = self.buffer.read(ctx);
for selection in &mut selections {
- selection.start = selection.start.bias_right(buffer).unwrap();
- selection.end = selection.end.bias_right(buffer).unwrap();
+ selection.start = selection.start.bias_left(buffer).unwrap();
+ selection.end = selection.end.bias_left(buffer).unwrap();
}
self.update_selections(selections, true, ctx);
self.end_transaction(ctx);
}
+ pub fn move_line_up(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
+ self.start_transaction(ctx);
+
+ let app = ctx.as_ref();
+ let buffer = self.buffer.read(ctx);
+ let map = self.display_map.read(ctx);
+
+ let mut edits = Vec::new();
+ let mut new_selection_ranges = Vec::new();
+ let mut old_folds = Vec::new();
+ let mut new_folds = Vec::new();
+
+ let mut selections = self.selections(app).iter().peekable();
+ let mut contiguous_selections = Vec::new();
+ while let Some(selection) = selections.next() {
+ // Accumulate contiguous regions of rows that we want to move.
+ contiguous_selections.push(selection.range(buffer));
+ let (mut buffer_rows, mut display_rows) =
+ selection.buffer_rows_for_display_rows(map, app);
+ while let Some(next_selection) = selections.peek() {
+ let (next_buffer_rows, next_display_rows) =
+ next_selection.buffer_rows_for_display_rows(map, app);
+ if next_buffer_rows.start <= buffer_rows.end {
+ buffer_rows.end = next_buffer_rows.end;
+ display_rows.end = next_display_rows.end;
+ contiguous_selections.push(next_selection.range(buffer));
+ selections.next().unwrap();
+ } else {
+ break;
+ }
+ }
+
+ // Cut the text from the selected rows and paste it at the start of the previous line.
+ if display_rows.start != 0 {
+ let selection_row_start =
+ Point::new(buffer_rows.start, 0).to_offset(buffer).unwrap();
+ let selection_row_end = Point::new(
+ buffer_rows.end - 1,
+ buffer.line_len(buffer_rows.end - 1).unwrap(),
+ )
+ .to_offset(buffer)
+ .unwrap();
+
+ let prev_row_display_start = DisplayPoint::new(display_rows.start - 1, 0);
+ let prev_row_start = prev_row_display_start
+ .to_buffer_offset(map, Bias::Left, app)
+ .unwrap();
+
+ let mut text = String::new();
+ text.extend(
+ buffer
+ .text_for_range(selection_row_start..selection_row_end)
+ .unwrap(),
+ );
+ text.push('\n');
+ edits.push((prev_row_start..prev_row_start, text));
+ edits.push((selection_row_start - 1..selection_row_end, String::new()));
+
+ let row_delta = buffer_rows.start
+ - prev_row_display_start
+ .to_buffer_point(map, Bias::Left, app)
+ .unwrap()
+ .row;
+
+ // Move selections up.
+ for range in &mut contiguous_selections {
+ range.start.row -= row_delta;
+ range.end.row -= row_delta;
+ }
+
+ // Move folds up.
+ old_folds.push(selection_row_start..selection_row_end);
+ for fold in map
+ .folds_in_range(selection_row_start..selection_row_end, app)
+ .unwrap()
+ {
+ let mut start = fold.start.to_point(buffer).unwrap();
+ let mut end = fold.end.to_point(buffer).unwrap();
+ start.row -= row_delta;
+ end.row -= row_delta;
+ new_folds.push(start..end);
+ }
+ }
+
+ new_selection_ranges.extend(contiguous_selections.drain(..));
+ }
+
+ self.unfold_ranges(old_folds, ctx);
+ self.buffer.update(ctx, |buffer, ctx| {
+ for (range, text) in edits.into_iter().rev() {
+ buffer.edit(Some(range), text, Some(ctx)).unwrap();
+ }
+ });
+ self.fold_ranges(new_folds, ctx);
+ self.select_ranges(new_selection_ranges, true, ctx);
+
+ self.end_transaction(ctx);
+ }
+
+ pub fn move_line_down(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
+ self.start_transaction(ctx);
+
+ let app = ctx.as_ref();
+ let buffer = self.buffer.read(ctx);
+ let map = self.display_map.read(ctx);
+
+ let mut edits = Vec::new();
+ let mut new_selection_ranges = Vec::new();
+ let mut old_folds = Vec::new();
+ let mut new_folds = Vec::new();
+
+ let mut selections = self.selections(app).iter().peekable();
+ let mut contiguous_selections = Vec::new();
+ while let Some(selection) = selections.next() {
+ // Accumulate contiguous regions of rows that we want to move.
+ contiguous_selections.push(selection.range(buffer));
+ let (mut buffer_rows, mut display_rows) =
+ selection.buffer_rows_for_display_rows(map, app);
+ while let Some(next_selection) = selections.peek() {
+ let (next_buffer_rows, next_display_rows) =
+ next_selection.buffer_rows_for_display_rows(map, app);
+ if next_buffer_rows.start <= buffer_rows.end {
+ buffer_rows.end = next_buffer_rows.end;
+ display_rows.end = next_display_rows.end;
+ contiguous_selections.push(next_selection.range(buffer));
+ selections.next().unwrap();
+ } else {
+ break;
+ }
+ }
+
+ // Cut the text from the selected rows and paste it at the end of the next line.
+ if display_rows.end <= map.max_point(app).row() {
+ let selection_row_start =
+ Point::new(buffer_rows.start, 0).to_offset(buffer).unwrap();
+ let selection_row_end = Point::new(
+ buffer_rows.end - 1,
+ buffer.line_len(buffer_rows.end - 1).unwrap(),
+ )
+ .to_offset(buffer)
+ .unwrap();
+
+ let next_row_display_end = DisplayPoint::new(
+ display_rows.end,
+ map.line_len(display_rows.end, app).unwrap(),
+ );
+ let next_row_end = next_row_display_end
+ .to_buffer_offset(map, Bias::Left, app)
+ .unwrap();
+
+ let mut text = String::new();
+ text.push('\n');
+ text.extend(
+ buffer
+ .text_for_range(selection_row_start..selection_row_end)
+ .unwrap(),
+ );
+ edits.push((selection_row_start..selection_row_end + 1, String::new()));
+ edits.push((next_row_end..next_row_end, text));
+
+ let row_delta = next_row_display_end
+ .to_buffer_point(map, Bias::Right, app)
+ .unwrap()
+ .row
+ - buffer_rows.end
+ + 1;
+
+ // Move selections down.
+ for range in &mut contiguous_selections {
+ range.start.row += row_delta;
+ range.end.row += row_delta;
+ }
+
+ // Move folds down.
+ old_folds.push(selection_row_start..selection_row_end);
+ for fold in map
+ .folds_in_range(selection_row_start..selection_row_end, app)
+ .unwrap()
+ {
+ let mut start = fold.start.to_point(buffer).unwrap();
+ let mut end = fold.end.to_point(buffer).unwrap();
+ start.row += row_delta;
+ end.row += row_delta;
+ new_folds.push(start..end);
+ }
+ }
+
+ new_selection_ranges.extend(contiguous_selections.drain(..));
+ }
+
+ self.unfold_ranges(old_folds, ctx);
+ self.buffer.update(ctx, |buffer, ctx| {
+ for (range, text) in edits.into_iter().rev() {
+ buffer.edit(Some(range), text, Some(ctx)).unwrap();
+ }
+ });
+ self.fold_ranges(new_folds, ctx);
+ self.select_ranges(new_selection_ranges, true, ctx);
+
+ self.end_transaction(ctx);
+ }
+
pub fn cut(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
self.start_transaction(ctx);
let mut text = String::new();
@@ -1313,9 +1525,11 @@ impl BufferView {
}
pub fn move_to_beginning(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
+ let buffer = self.buffer.read(ctx);
+ let cursor = buffer.anchor_before(Point::new(0, 0)).unwrap();
let selection = Selection {
- start: Anchor::Start,
- end: Anchor::Start,
+ start: cursor.clone(),
+ end: cursor,
reversed: false,
goal_column: None,
};
@@ -1329,9 +1543,11 @@ impl BufferView {
}
pub fn move_to_end(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
+ let buffer = self.buffer.read(ctx);
+ let cursor = buffer.anchor_before(buffer.max_point()).unwrap();
let selection = Selection {
- start: Anchor::End,
- end: Anchor::End,
+ start: cursor.clone(),
+ end: cursor,
reversed: false,
goal_column: None,
};
@@ -1495,12 +1711,7 @@ impl BufferView {
}
}
- if !fold_ranges.is_empty() {
- self.display_map.update(ctx, |map, ctx| {
- map.fold(fold_ranges, ctx).unwrap();
- });
- *self.autoscroll_requested.lock() = true;
- }
+ self.fold_ranges(fold_ranges, ctx);
}
pub fn unfold(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
@@ -1521,11 +1732,7 @@ impl BufferView {
start..end
})
.collect::<Vec<_>>();
-
- self.display_map.update(ctx, |map, ctx| {
- map.unfold(ranges, ctx).unwrap();
- });
- *self.autoscroll_requested.lock() = true;
+ self.unfold_ranges(ranges, ctx);
}
fn is_line_foldable(&self, display_row: u32, app: &AppContext) -> bool {
@@ -1583,6 +1790,24 @@ impl BufferView {
});
}
+ fn fold_ranges<T: ToOffset>(&mut self, ranges: Vec<Range<T>>, ctx: &mut ViewContext<Self>) {
+ if !ranges.is_empty() {
+ self.display_map.update(ctx, |map, ctx| {
+ map.fold(ranges, ctx).unwrap();
+ });
+ *self.autoscroll_requested.lock() = true;
+ }
+ }
+
+ fn unfold_ranges<T: ToOffset>(&mut self, ranges: Vec<Range<T>>, ctx: &mut ViewContext<Self>) {
+ if !ranges.is_empty() {
+ self.display_map.update(ctx, |map, ctx| {
+ map.unfold(ranges, ctx).unwrap();
+ });
+ *self.autoscroll_requested.lock() = true;
+ }
+ }
+
pub fn line(&self, display_row: u32, app: &AppContext) -> Result<String> {
self.display_map.read(app).line(display_row, app)
}
@@ -2732,14 +2957,14 @@ mod tests {
// Cut with three selections. Clipboard text is divided into three slices.
view.update(app, |view, ctx| {
- view.select_ranges(&[0..4, 8..14, 19..24], ctx).unwrap();
+ view.select_ranges(vec![0..4, 8..14, 19..24], false, ctx);
view.cut(&(), ctx);
});
assert_eq!(view.read(app).text(app.as_ref()), "two four six ");
// Paste with three cursors. Each cursor pastes one slice of the clipboard text.
view.update(app, |view, ctx| {
- view.select_ranges(&[4..4, 9..9, 13..13], ctx).unwrap();
+ view.select_ranges(vec![4..4, 9..9, 13..13], false, ctx);
view.paste(&(), ctx);
});
assert_eq!(
@@ -2759,7 +2984,7 @@ mod tests {
// match the number of slices in the clipboard, the entire clipboard text
// is pasted at each cursor.
view.update(app, |view, ctx| {
- view.select_ranges(&[0..0, 28..28], ctx).unwrap();
+ view.select_ranges(vec![0..0, 28..28], false, ctx);
view.insert(&"( ".to_string(), ctx);
view.paste(&(), ctx);
view.insert(&") ".to_string(), ctx);
@@ -2770,7 +2995,7 @@ mod tests {
);
view.update(app, |view, ctx| {
- view.select_ranges(&[0..0], ctx).unwrap();
+ view.select_ranges(vec![0..0], false, ctx);
view.insert(&"123\n4567\n89\n".to_string(), ctx);
});
assert_eq!(
@@ -89,6 +89,33 @@ impl FoldMap {
DisplayPoint(self.transforms.summary().display.rightmost_point)
}
+ pub fn folds_in_range<T>(&self, range: Range<T>, app: &AppContext) -> Result<&[Range<Anchor>]>
+ where
+ T: ToOffset,
+ {
+ let buffer = self.buffer.read(app);
+ let range = buffer.anchor_before(range.start)?..buffer.anchor_before(range.end)?;
+ let mut start_ix = find_insertion_index(&self.folds, |probe| probe.cmp(&range, buffer))?;
+ let mut end_ix = start_ix;
+
+ for fold in self.folds[..start_ix].iter().rev() {
+ if fold.end.cmp(&range.start, buffer)? == Ordering::Greater {
+ start_ix -= 1;
+ } else {
+ break;
+ }
+ }
+ for fold in &self.folds[end_ix..] {
+ if range.end.cmp(&fold.start, buffer)? == Ordering::Greater {
+ end_ix += 1;
+ } else {
+ break;
+ }
+ }
+
+ Ok(&self.folds[start_ix..end_ix])
+ }
+
pub fn fold<T: ToOffset>(
&mut self,
ranges: impl IntoIterator<Item = Range<T>>,
@@ -169,6 +196,13 @@ impl FoldMap {
false
}
+ pub fn to_buffer_offset(&self, point: DisplayPoint, app: &AppContext) -> Result<usize> {
+ let mut cursor = self.transforms.cursor::<DisplayPoint, TransformSummary>();
+ cursor.seek(&point, SeekBias::Right);
+ let overshoot = point.0 - cursor.start().display.lines;
+ (cursor.start().buffer.lines + overshoot).to_offset(self.buffer.read(app))
+ }
+
pub fn to_display_offset(
&self,
point: DisplayPoint,
@@ -235,7 +269,7 @@ impl FoldMap {
let next_edit = edits.next().unwrap();
delta += next_edit.delta();
- if next_edit.old_range.end > edit.old_range.end {
+ if next_edit.old_range.end >= edit.old_range.end {
edit.old_range.end = next_edit.old_range.end;
cursor.seek(&edit.old_range.end, SeekBias::Right);
cursor.next();
@@ -415,7 +449,7 @@ impl<'a> Iterator for Chars<'a> {
return Some(c);
}
- if self.offset == self.cursor.end().display.chars {
+ while self.offset == self.cursor.end().display.chars && self.cursor.item().is_some() {
self.cursor.next();
}
@@ -519,6 +553,50 @@ mod tests {
});
}
+ #[test]
+ fn test_adjacent_folds() {
+ App::test((), |app| {
+ let buffer = app.add_model(|ctx| Buffer::new(0, "abcdefghijkl", ctx));
+
+ {
+ let mut map = FoldMap::new(buffer.clone(), app.as_ref());
+
+ map.fold(vec![5..8], app.as_ref()).unwrap();
+ map.check_invariants(app.as_ref());
+ assert_eq!(map.text(app.as_ref()), "abcde…ijkl");
+
+ // Create an fold adjacent to the start of the first fold.
+ map.fold(vec![0..1, 2..5], app.as_ref()).unwrap();
+ map.check_invariants(app.as_ref());
+ assert_eq!(map.text(app.as_ref()), "…b…ijkl");
+
+ // Create an fold adjacent to the end of the first fold.
+ map.fold(vec![11..11, 8..10], app.as_ref()).unwrap();
+ map.check_invariants(app.as_ref());
+ assert_eq!(map.text(app.as_ref()), "…b…kl");
+ }
+
+ {
+ let mut map = FoldMap::new(buffer.clone(), app.as_ref());
+
+ // Create two adjacent folds.
+ map.fold(vec![0..2, 2..5], app.as_ref()).unwrap();
+ map.check_invariants(app.as_ref());
+ assert_eq!(map.text(app.as_ref()), "…fghijkl");
+
+ // Edit within one of the folds.
+ let edits = buffer.update(app, |buffer, ctx| {
+ let version = buffer.version();
+ buffer.edit(vec![0..1], "12345", Some(ctx)).unwrap();
+ buffer.edits_since(version).collect::<Vec<_>>()
+ });
+ map.apply_edits(edits.as_slice(), app.as_ref()).unwrap();
+ map.check_invariants(app.as_ref());
+ assert_eq!(map.text(app.as_ref()), "12345…fghijkl");
+ }
+ });
+ }
+
#[test]
fn test_overlapping_folds() {
App::test((), |app| {
@@ -577,6 +655,9 @@ mod tests {
let iterations = env::var("ITERATIONS")
.map(|i| i.parse().expect("invalid `ITERATIONS` variable"))
.unwrap_or(100);
+ let operations = env::var("OPERATIONS")
+ .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
+ .unwrap_or(10);
let seed_range = if let Ok(seed) = env::var("SEED") {
let seed = seed.parse().expect("invalid `SEED` variable");
seed..seed + 1
@@ -596,66 +677,70 @@ mod tests {
});
let mut map = FoldMap::new(buffer.clone(), app.as_ref());
- {
- let buffer = buffer.read(app);
-
- let fold_count = rng.gen_range(0..10);
- let mut fold_ranges: Vec<Range<usize>> = Vec::new();
- for _ in 0..fold_count {
- let end = rng.gen_range(0..buffer.len() + 1);
- let start = rng.gen_range(0..end + 1);
- fold_ranges.push(start..end);
+ for _ in 0..operations {
+ log::info!("text: {:?}", buffer.read(app).text());
+ {
+ let buffer = buffer.read(app);
+
+ let fold_count = rng.gen_range(0..=2);
+ let mut fold_ranges: Vec<Range<usize>> = Vec::new();
+ for _ in 0..fold_count {
+ let end = rng.gen_range(0..buffer.len() + 1);
+ let start = rng.gen_range(0..end + 1);
+ fold_ranges.push(start..end);
+ }
+ log::info!("folding {:?}", fold_ranges);
+ map.fold(fold_ranges.clone(), app.as_ref()).unwrap();
+ map.check_invariants(app.as_ref());
+
+ let mut expected_text = buffer.text();
+ for fold_range in map.merged_fold_ranges(app.as_ref()).into_iter().rev() {
+ expected_text.replace_range(fold_range.start..fold_range.end, "…");
+ }
+ assert_eq!(map.text(app.as_ref()), expected_text);
+
+ for fold_range in map.merged_fold_ranges(app.as_ref()) {
+ let display_point =
+ map.to_display_point(fold_range.start.to_point(buffer).unwrap());
+ assert!(map.is_line_folded(display_point.row()));
+ }
}
- map.fold(fold_ranges, app.as_ref()).unwrap();
+ let edits = buffer.update(app, |buffer, ctx| {
+ let start_version = buffer.version.clone();
+ let edit_count = rng.gen_range(0..=2);
+ buffer.randomly_edit(&mut rng, edit_count, Some(ctx));
+ buffer.edits_since(start_version).collect::<Vec<_>>()
+ });
+ log::info!("editing {:?}", edits);
+ map.apply_edits(&edits, app.as_ref()).unwrap();
+ map.check_invariants(app.as_ref());
+ let buffer = map.buffer.read(app);
let mut expected_text = buffer.text();
+ let mut expected_buffer_rows = Vec::new();
+ let mut next_row = buffer.max_point().row;
for fold_range in map.merged_fold_ranges(app.as_ref()).into_iter().rev() {
+ let fold_start = buffer.point_for_offset(fold_range.start).unwrap();
+ let fold_end = buffer.point_for_offset(fold_range.end).unwrap();
+ expected_buffer_rows.extend((fold_end.row + 1..=next_row).rev());
+ next_row = fold_start.row;
+
expected_text.replace_range(fold_range.start..fold_range.end, "…");
}
+ expected_buffer_rows.extend((0..=next_row).rev());
+ expected_buffer_rows.reverse();
assert_eq!(map.text(app.as_ref()), expected_text);
- for fold_range in map.merged_fold_ranges(app.as_ref()) {
- let display_point =
- map.to_display_point(fold_range.start.to_point(buffer).unwrap());
- assert!(map.is_line_folded(display_point.row()));
+ for (idx, buffer_row) in expected_buffer_rows.iter().enumerate() {
+ let display_row = map.to_display_point(Point::new(*buffer_row, 0)).row();
+ assert_eq!(
+ map.buffer_rows(display_row).unwrap().collect::<Vec<_>>(),
+ expected_buffer_rows[idx..],
+ );
}
}
-
- let edits = buffer.update(app, |buffer, ctx| {
- let start_version = buffer.version.clone();
- let edit_count = rng.gen_range(1..10);
- buffer.randomly_edit(&mut rng, edit_count, Some(ctx));
- buffer.edits_since(start_version).collect::<Vec<_>>()
- });
-
- map.apply_edits(&edits, app.as_ref()).unwrap();
-
- let buffer = map.buffer.read(app);
- let mut expected_text = buffer.text();
- let mut expected_buffer_rows = Vec::new();
- let mut next_row = buffer.max_point().row;
- for fold_range in map.merged_fold_ranges(app.as_ref()).into_iter().rev() {
- let fold_start = buffer.point_for_offset(fold_range.start).unwrap();
- let fold_end = buffer.point_for_offset(fold_range.end).unwrap();
- expected_buffer_rows.extend((fold_end.row + 1..=next_row).rev());
- next_row = fold_start.row;
-
- expected_text.replace_range(fold_range.start..fold_range.end, "…");
- }
- expected_buffer_rows.extend((0..=next_row).rev());
- expected_buffer_rows.reverse();
-
- assert_eq!(map.text(app.as_ref()), expected_text);
-
- for (idx, buffer_row) in expected_buffer_rows.iter().enumerate() {
- let display_row = map.to_display_point(Point::new(*buffer_row, 0)).row();
- assert_eq!(
- map.buffer_rows(display_row).unwrap().collect::<Vec<_>>(),
- expected_buffer_rows[idx..],
- );
- }
});
}
}
@@ -721,5 +806,13 @@ mod tests {
}
merged_ranges
}
+
+ fn check_invariants(&self, app: &AppContext) {
+ assert_eq!(
+ self.transforms.summary().buffer.chars,
+ self.buffer.read(app).len(),
+ "transform tree does not match buffer's length"
+ );
+ }
}
}