diff --git a/zed/src/editor/buffer/selection.rs b/zed/src/editor/buffer/selection.rs index 1d7459482c22a16407bf379ddff3017357c51566..431f8224c425d6e568da07718af36827520f7e64 100644 --- a/zed/src/editor/buffer/selection.rs +++ b/zed/src/editor/buffer/selection.rs @@ -73,14 +73,18 @@ impl Selection { } } - pub fn buffer_rows_for_display_rows(&self, map: &DisplayMap, ctx: &AppContext) -> Range { + pub fn buffer_rows_for_display_rows( + &self, + map: &DisplayMap, + ctx: &AppContext, + ) -> (Range, Range) { let display_start = self.start.to_display_point(map, ctx).unwrap(); let buffer_start = DisplayPoint::new(display_start.row(), 0) .to_buffer_point(map, Bias::Left, ctx) .unwrap(); let mut display_end = self.end.to_display_point(map, ctx).unwrap(); - if display_end != map.max_point(ctx) + if display_end.row() != map.max_point(ctx).row() && display_start.row() != display_end.row() && display_end.column() == 0 { @@ -93,6 +97,9 @@ impl Selection { .to_buffer_point(map, Bias::Left, ctx) .unwrap(); - buffer_start.row..buffer_end.row + 1 + ( + buffer_start.row..buffer_end.row + 1, + display_start.row()..display_end.row() + 1, + ) } } diff --git a/zed/src/editor/buffer_view.rs b/zed/src/editor/buffer_view.rs index a6f58772e9a6784b01a5d523bbed73dcc7bff00a..f313edad0b2efcdc65505dddf0bb6062b7f78729 100644 --- a/zed/src/editor/buffer_view.rs +++ b/zed/src/editor/buffer_view.rs @@ -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) -> Result<()> + fn select_ranges(&mut self, ranges: I, autoscroll: bool, ctx: &mut ViewContext) where - T: IntoIterator>, + I: IntoIterator>, + 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>, { - 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.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.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.start_transaction(ctx); let mut text = String::new(); @@ -1313,9 +1525,11 @@ impl BufferView { } pub fn move_to_beginning(&mut self, _: &(), ctx: &mut ViewContext) { + 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) { + 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) { @@ -1521,11 +1732,7 @@ impl BufferView { start..end }) .collect::>(); - - 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(&mut self, ranges: Vec>, ctx: &mut ViewContext) { + if !ranges.is_empty() { + self.display_map.update(ctx, |map, ctx| { + map.fold(ranges, ctx).unwrap(); + }); + *self.autoscroll_requested.lock() = true; + } + } + + fn unfold_ranges(&mut self, ranges: Vec>, ctx: &mut ViewContext) { + 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 { 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!( diff --git a/zed/src/editor/display_map/fold_map.rs b/zed/src/editor/display_map/fold_map.rs index 536971a87d13cffc33fc9e63a0d64ae3e2f02099..98c47ff08a84ea1a79ded4a0edf03da7ad0a681a 100644 --- a/zed/src/editor/display_map/fold_map.rs +++ b/zed/src/editor/display_map/fold_map.rs @@ -89,6 +89,33 @@ impl FoldMap { DisplayPoint(self.transforms.summary().display.rightmost_point) } + pub fn folds_in_range(&self, range: Range, app: &AppContext) -> Result<&[Range]> + 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( &mut self, ranges: impl IntoIterator>, @@ -169,6 +196,13 @@ impl FoldMap { false } + pub fn to_buffer_offset(&self, point: DisplayPoint, app: &AppContext) -> Result { + let mut cursor = self.transforms.cursor::(); + 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::>() + }); + 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> = 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> = 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::>() + }); + 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::>(), + 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::>() - }); - - 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::>(), - 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" + ); + } } } diff --git a/zed/src/editor/display_map/mod.rs b/zed/src/editor/display_map/mod.rs index e8d2b6c6e5e199eb50111edb1d70cc43c29d7586..b69a70f3979870eb061f72c0cfdff25931165ccf 100644 --- a/zed/src/editor/display_map/mod.rs +++ b/zed/src/editor/display_map/mod.rs @@ -34,6 +34,13 @@ impl DisplayMap { } } + pub fn folds_in_range(&self, range: Range, app: &AppContext) -> Result<&[Range]> + where + T: ToOffset, + { + self.fold_map.folds_in_range(range, app) + } + pub fn fold( &mut self, ranges: impl IntoIterator>, @@ -179,6 +186,11 @@ impl DisplayPoint { .to_buffer_point(self.collapse_tabs(map, bias, app)?.0)) } + pub fn to_buffer_offset(self, map: &DisplayMap, bias: Bias, app: &AppContext) -> Result { + map.fold_map + .to_buffer_offset(self.collapse_tabs(map, bias, app)?.0, app) + } + fn expand_tabs(mut self, map: &DisplayMap, app: &AppContext) -> Result { let chars = map .fold_map