From a0428667d063340c862fee0631ec5c0cf10494d2 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 4 May 2021 11:24:33 +0200 Subject: [PATCH 1/7] Fix `folds_in_range` and add a test for it With the current ordering, a linear scan is required in order to determine which folds intersect the given range. --- zed/src/editor/buffer_view.rs | 40 +++++----------- zed/src/editor/display_map/fold_map.rs | 64 ++++++++++++++++++-------- zed/src/editor/display_map/mod.rs | 6 ++- 3 files changed, 61 insertions(+), 49 deletions(-) diff --git a/zed/src/editor/buffer_view.rs b/zed/src/editor/buffer_view.rs index f313edad0b2efcdc65505dddf0bb6062b7f78729..c89af4c0b59a72eb39f027f88d9d03f6e2c61e37 100644 --- a/zed/src/editor/buffer_view.rs +++ b/zed/src/editor/buffer_view.rs @@ -863,9 +863,8 @@ impl BufferView { // 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( + let start = Point::new(buffer_rows.start, 0).to_offset(buffer).unwrap(); + let end = Point::new( buffer_rows.end - 1, buffer.line_len(buffer_rows.end - 1).unwrap(), ) @@ -878,14 +877,10 @@ impl BufferView { .unwrap(); let mut text = String::new(); - text.extend( - buffer - .text_for_range(selection_row_start..selection_row_end) - .unwrap(), - ); + text.extend(buffer.text_for_range(start..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())); + edits.push((start - 1..end, String::new())); let row_delta = buffer_rows.start - prev_row_display_start @@ -900,11 +895,8 @@ impl BufferView { } // 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() - { + old_folds.push(start..end); + for fold in map.folds_in_range(start..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; @@ -962,9 +954,8 @@ impl BufferView { // 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( + let start = Point::new(buffer_rows.start, 0).to_offset(buffer).unwrap(); + let end = Point::new( buffer_rows.end - 1, buffer.line_len(buffer_rows.end - 1).unwrap(), ) @@ -981,12 +972,8 @@ impl BufferView { 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())); + text.extend(buffer.text_for_range(start..end).unwrap()); + edits.push((start..end + 1, String::new())); edits.push((next_row_end..next_row_end, text)); let row_delta = next_row_display_end @@ -1003,11 +990,8 @@ impl BufferView { } // 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() - { + old_folds.push(start..end); + for fold in map.folds_in_range(start..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; diff --git a/zed/src/editor/display_map/fold_map.rs b/zed/src/editor/display_map/fold_map.rs index 98c47ff08a84ea1a79ded4a0edf03da7ad0a681a..da3eeb9d2ffdc125fdc431337ca5206fcf394ab7 100644 --- a/zed/src/editor/display_map/fold_map.rs +++ b/zed/src/editor/display_map/fold_map.rs @@ -89,31 +89,20 @@ impl FoldMap { DisplayPoint(self.transforms.summary().display.rightmost_point) } - pub fn folds_in_range(&self, range: Range, app: &AppContext) -> Result<&[Range]> + pub fn folds_in_range<'a, T>( + &'a self, + range: Range, + app: &'a AppContext, + ) -> Result>> 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]) + Ok(self.folds.iter().filter(move |fold| { + range.start.cmp(&fold.end, buffer).unwrap() == Ordering::Less + && range.end.cmp(&fold.start, buffer).unwrap() == Ordering::Greater + })) } pub fn fold( @@ -500,6 +489,7 @@ impl<'a> Dimension<'a, TransformSummary> for usize { mod tests { use super::*; use crate::test::sample_text; + use buffer::ToPoint; use gpui::App; #[test] @@ -645,6 +635,40 @@ mod tests { }); } + #[test] + fn test_folds_in_range() { + App::test((), |app| { + let buffer = app.add_model(|ctx| Buffer::new(0, sample_text(5, 6), ctx)); + let mut map = FoldMap::new(buffer.clone(), app.as_ref()); + let buffer = buffer.read(app); + + map.fold( + vec![ + Point::new(0, 2)..Point::new(2, 2), + Point::new(0, 4)..Point::new(1, 0), + Point::new(1, 2)..Point::new(3, 2), + Point::new(3, 1)..Point::new(4, 1), + ], + app.as_ref(), + ) + .unwrap(); + let fold_ranges = map + .folds_in_range(Point::new(1, 0)..Point::new(1, 3), app.as_ref()) + .unwrap() + .map(|fold| { + fold.start.to_point(buffer).unwrap()..fold.end.to_point(buffer).unwrap() + }) + .collect::>(); + assert_eq!( + fold_ranges, + vec![ + Point::new(0, 2)..Point::new(2, 2), + Point::new(1, 2)..Point::new(3, 2) + ] + ); + }); + } + #[test] fn test_random_folds() { use crate::editor::ToPoint; diff --git a/zed/src/editor/display_map/mod.rs b/zed/src/editor/display_map/mod.rs index b69a70f3979870eb061f72c0cfdff25931165ccf..d026d6eb54cc486ed698957371897e223b750048 100644 --- a/zed/src/editor/display_map/mod.rs +++ b/zed/src/editor/display_map/mod.rs @@ -34,7 +34,11 @@ impl DisplayMap { } } - pub fn folds_in_range(&self, range: Range, app: &AppContext) -> Result<&[Range]> + pub fn folds_in_range<'a, T>( + &'a self, + range: Range, + app: &'a AppContext, + ) -> Result>> where T: ToOffset, { From d326e806575cf18e942d861696f164be833ba2c8 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 4 May 2021 11:27:32 +0200 Subject: [PATCH 2/7] Rework randomized test, ensuring order of folds is maintained correctly --- zed/src/editor/display_map/fold_map.rs | 57 +++++++++++++++----------- 1 file changed, 32 insertions(+), 25 deletions(-) diff --git a/zed/src/editor/display_map/fold_map.rs b/zed/src/editor/display_map/fold_map.rs index da3eeb9d2ffdc125fdc431337ca5206fcf394ab7..9b083f62b57789fd880e23583b92fa7d27a38e11 100644 --- a/zed/src/editor/display_map/fold_map.rs +++ b/zed/src/editor/display_map/fold_map.rs @@ -487,6 +487,8 @@ impl<'a> Dimension<'a, TransformSummary> for usize { #[cfg(test)] mod tests { + use std::cmp::Reverse; + use super::*; use crate::test::sample_text; use buffer::ToPoint; @@ -703,10 +705,10 @@ mod tests { for _ in 0..operations { log::info!("text: {:?}", buffer.read(app).text()); - { + if rng.gen() { let buffer = buffer.read(app); - let fold_count = rng.gen_range(0..=2); + let fold_count = rng.gen_range(1..=5); let mut fold_ranges: Vec> = Vec::new(); for _ in 0..fold_count { let end = rng.gen_range(0..buffer.len() + 1); @@ -715,29 +717,16 @@ mod tests { } 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())); - } + } else { + let edits = buffer.update(app, |buffer, ctx| { + let start_version = buffer.version.clone(); + let edit_count = rng.gen_range(1..=5); + 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(); } - - 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); @@ -764,6 +753,12 @@ mod tests { expected_buffer_rows[idx..], ); } + + 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())); + } } }); } @@ -832,11 +827,23 @@ mod tests { } fn check_invariants(&self, app: &AppContext) { + let buffer = self.buffer.read(app); assert_eq!( self.transforms.summary().buffer.chars, - self.buffer.read(app).len(), + buffer.len(), "transform tree does not match buffer's length" ); + + let mut fold_ranges = Vec::new(); + let mut sorted_fold_ranges = Vec::new(); + for fold in &self.folds { + let start = fold.start.to_offset(buffer).unwrap(); + let end = fold.end.to_offset(buffer).unwrap(); + fold_ranges.push(start..end); + sorted_fold_ranges.push(start..end); + } + sorted_fold_ranges.sort_by_key(|fold| (fold.start, Reverse(fold.end))); + assert_eq!(fold_ranges, sorted_fold_ranges); } } } From 3ba463020c0753916a1f2694256e15ac9d0b6035 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 4 May 2021 12:11:44 +0200 Subject: [PATCH 3/7] Test FoldMap `to_display_offset`, `to_display_point`, `to_buffer_point` --- zed/src/editor/display_map/fold_map.rs | 29 ++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/zed/src/editor/display_map/fold_map.rs b/zed/src/editor/display_map/fold_map.rs index 9b083f62b57789fd880e23583b92fa7d27a38e11..3447e2697a568bf553110f9b4b583920a312d7bf 100644 --- a/zed/src/editor/display_map/fold_map.rs +++ b/zed/src/editor/display_map/fold_map.rs @@ -746,6 +746,35 @@ mod tests { assert_eq!(map.text(app.as_ref()), expected_text); + for (display_row, line) in expected_text.lines().enumerate() { + let line_len = map.line_len(display_row as u32, app.as_ref()).unwrap(); + assert_eq!(line_len, line.chars().count() as u32); + } + + let mut display_point = DisplayPoint::new(0, 0); + let mut display_offset = DisplayOffset(0); + for c in expected_text.chars() { + let buffer_point = map.to_buffer_point(display_point); + let buffer_offset = buffer_point.to_offset(buffer).unwrap(); + assert_eq!(map.to_display_point(buffer_point), display_point); + assert_eq!( + map.to_buffer_offset(display_point, app.as_ref()).unwrap(), + buffer_offset + ); + assert_eq!( + map.to_display_offset(display_point, app.as_ref()).unwrap(), + display_offset + ); + + if c == '\n' { + *display_point.row_mut() += 1; + *display_point.column_mut() = 0; + } else { + *display_point.column_mut() += 1; + } + display_offset.0 += 1; + } + 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!( From 7bd192bdfb8eac1d24546e05c7d4411d1150ae02 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 4 May 2021 14:13:28 +0200 Subject: [PATCH 4/7] Add `FoldMap::chars_at` to randomized test --- zed/src/editor/display_map/fold_map.rs | 38 ++++++++++++++++---------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/zed/src/editor/display_map/fold_map.rs b/zed/src/editor/display_map/fold_map.rs index 3447e2697a568bf553110f9b4b583920a312d7bf..9ae149c3eb2cbe1d09a9857ae3b60964d94a1178 100644 --- a/zed/src/editor/display_map/fold_map.rs +++ b/zed/src/editor/display_map/fold_map.rs @@ -487,8 +487,6 @@ impl<'a> Dimension<'a, TransformSummary> for usize { #[cfg(test)] mod tests { - use std::cmp::Reverse; - use super::*; use crate::test::sample_text; use buffer::ToPoint; @@ -775,6 +773,25 @@ mod tests { display_offset.0 += 1; } + for _ in 0..5 { + let row = rng.gen_range(0..=map.max_point().row()); + let column = rng.gen_range(0..=map.line_len(row, app.as_ref()).unwrap()); + let point = DisplayPoint::new(row, column); + let offset = map.to_display_offset(point, app.as_ref()).unwrap().0; + let len = rng.gen_range(0..=map.len() - offset); + assert_eq!( + map.chars_at(point, app.as_ref()) + .unwrap() + .take(len) + .collect::(), + expected_text + .chars() + .skip(offset) + .take(len) + .collect::() + ); + } + 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!( @@ -828,8 +845,10 @@ mod tests { fn merged_fold_ranges(&self, app: &AppContext) -> Vec> { let buffer = self.buffer.read(app); - let mut fold_ranges = self - .folds + let mut folds = self.folds.clone(); + // Ensure sorting doesn't change how folds get merged and displayed. + folds.sort_by(|a, b| a.cmp(b, buffer).unwrap()); + let mut fold_ranges = folds .iter() .map(|fold| { fold.start.to_offset(buffer).unwrap()..fold.end.to_offset(buffer).unwrap() @@ -862,17 +881,6 @@ mod tests { buffer.len(), "transform tree does not match buffer's length" ); - - let mut fold_ranges = Vec::new(); - let mut sorted_fold_ranges = Vec::new(); - for fold in &self.folds { - let start = fold.start.to_offset(buffer).unwrap(); - let end = fold.end.to_offset(buffer).unwrap(); - fold_ranges.push(start..end); - sorted_fold_ranges.push(start..end); - } - sorted_fold_ranges.sort_by_key(|fold| (fold.start, Reverse(fold.end))); - assert_eq!(fold_ranges, sorted_fold_ranges); } } } From c135c84ef04882a42c4798747ef9a8645dd0c4fd Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 4 May 2021 16:13:01 +0200 Subject: [PATCH 5/7] Use correct bias when calculating next_row_end --- zed/src/editor/buffer_view.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zed/src/editor/buffer_view.rs b/zed/src/editor/buffer_view.rs index c89af4c0b59a72eb39f027f88d9d03f6e2c61e37..cd43133efe58b33e1372cecb136959f14c849624 100644 --- a/zed/src/editor/buffer_view.rs +++ b/zed/src/editor/buffer_view.rs @@ -967,7 +967,7 @@ impl BufferView { map.line_len(display_rows.end, app).unwrap(), ); let next_row_end = next_row_display_end - .to_buffer_offset(map, Bias::Left, app) + .to_buffer_offset(map, Bias::Right, app) .unwrap(); let mut text = String::new(); From 05ab1bdddfda720cf4dcb534d605e4f9e701d7fe Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 4 May 2021 18:27:56 +0200 Subject: [PATCH 6/7] Synchronize `FoldMap` with buffer's contents lazily Co-Authored-By: Nathan Sobo --- zed/src/editor/buffer/mod.rs | 84 +---- zed/src/editor/buffer_view.rs | 476 +++++++++++++++---------- zed/src/editor/display_map/fold_map.rs | 268 ++++++++------ zed/src/editor/display_map/mod.rs | 200 ++++++----- zed/src/editor/movement.rs | 4 +- 5 files changed, 574 insertions(+), 458 deletions(-) diff --git a/zed/src/editor/buffer/mod.rs b/zed/src/editor/buffer/mod.rs index 55ffaec02809f4234beca977fcd17f8069ae1c3f..5e142b6f3387c9475df5e42f2731a7c66ccb261e 100644 --- a/zed/src/editor/buffer/mod.rs +++ b/zed/src/editor/buffer/mod.rs @@ -679,9 +679,8 @@ impl Buffer { if let Some(ctx) = ctx { ctx.notify(); - let changes = self.edits_since(since).collect::>(); - if !changes.is_empty() { - self.did_edit(changes, was_dirty, ctx); + if self.edits_since(since).next().is_some() { + self.did_edit(was_dirty, ctx); } } } @@ -742,8 +741,8 @@ impl Buffer { Ok(ops) } - fn did_edit(&self, changes: Vec, was_dirty: bool, ctx: &mut ModelContext) { - ctx.emit(Event::Edited(changes)); + fn did_edit(&self, was_dirty: bool, ctx: &mut ModelContext) { + ctx.emit(Event::Edited); if !was_dirty { ctx.emit(Event::Dirtied); } @@ -892,9 +891,8 @@ impl Buffer { if let Some(ctx) = ctx { ctx.notify(); - let changes = self.edits_since(old_version).collect::>(); - if !changes.is_empty() { - self.did_edit(changes, was_dirty, ctx); + if self.edits_since(old_version).next().is_some() { + self.did_edit(was_dirty, ctx); } } @@ -1086,9 +1084,8 @@ impl Buffer { if let Some(ctx) = ctx { ctx.notify(); - let changes = self.edits_since(old_version).collect::>(); - if !changes.is_empty() { - self.did_edit(changes, was_dirty, ctx); + if self.edits_since(old_version).next().is_some() { + self.did_edit(was_dirty, ctx); } } @@ -1113,9 +1110,8 @@ impl Buffer { if let Some(ctx) = ctx { ctx.notify(); - let changes = self.edits_since(old_version).collect::>(); - if !changes.is_empty() { - self.did_edit(changes, was_dirty, ctx); + if self.edits_since(old_version).next().is_some() { + self.did_edit(was_dirty, ctx); } } @@ -1794,7 +1790,7 @@ impl Snapshot { #[derive(Clone, Debug, Eq, PartialEq)] pub enum Event { - Edited(Vec), + Edited, Dirtied, Saved, FileHandleChanged, @@ -2403,34 +2399,11 @@ mod tests { let buffer_1_events = buffer_1_events.borrow(); assert_eq!( *buffer_1_events, - vec![ - Event::Edited(vec![Edit { - old_range: 2..4, - new_range: 2..5 - }]), - Event::Dirtied, - Event::Edited(vec![Edit { - old_range: 5..5, - new_range: 5..7 - }]), - Event::Edited(vec![Edit { - old_range: 5..7, - new_range: 5..5 - }]), - ] + vec![Event::Edited, Event::Dirtied, Event::Edited, Event::Edited] ); let buffer_2_events = buffer_2_events.borrow(); - assert_eq!( - *buffer_2_events, - vec![ - Event::Edited(vec![Edit { - old_range: 2..4, - new_range: 2..5 - },]), - Event::Dirtied - ] - ); + assert_eq!(*buffer_2_events, vec![Event::Edited, Event::Dirtied]); }); } @@ -2970,16 +2943,7 @@ mod tests { model.update(app, |buffer, ctx| { assert!(buffer.text() == "ac"); assert!(buffer.is_dirty()); - assert_eq!( - *events.borrow(), - &[ - Event::Edited(vec![Edit { - old_range: 1..2, - new_range: 1..1 - }]), - Event::Dirtied - ] - ); + assert_eq!(*events.borrow(), &[Event::Edited, Event::Dirtied]); events.borrow_mut().clear(); buffer.did_save(buffer.version(), ctx); @@ -3001,17 +2965,7 @@ mod tests { assert!(buffer.is_dirty()); assert_eq!( *events.borrow(), - &[ - Event::Edited(vec![Edit { - old_range: 1..1, - new_range: 1..2 - }]), - Event::Dirtied, - Event::Edited(vec![Edit { - old_range: 2..2, - new_range: 2..3 - }]), - ], + &[Event::Edited, Event::Dirtied, Event::Edited], ); events.borrow_mut().clear(); @@ -3023,13 +2977,7 @@ mod tests { }); model.update(app, |_, _| { - assert_eq!( - *events.borrow(), - &[Event::Edited(vec![Edit { - old_range: 1..3, - new_range: 1..1 - },])] - ); + assert_eq!(*events.borrow(), &[Event::Edited]); }); }); } diff --git a/zed/src/editor/buffer_view.rs b/zed/src/editor/buffer_view.rs index cd43133efe58b33e1372cecb136959f14c849624..effcb30662d8c85ad050b536e7e5ad1bc43bb994 100644 --- a/zed/src/editor/buffer_view.rs +++ b/zed/src/editor/buffer_view.rs @@ -254,7 +254,7 @@ pub enum SelectAction { pub struct BufferView { handle: WeakViewHandle, buffer: ModelHandle, - display_map: ModelHandle, + display_map: DisplayMap, selection_set_id: SelectionSetId, pending_selection: Option, scroll_position: Mutex, @@ -290,14 +290,11 @@ impl BufferView { ctx.observe(&buffer, Self::on_buffer_changed); ctx.subscribe_to_model(&buffer, Self::on_buffer_event); - let display_map = ctx.add_model(|ctx| { - DisplayMap::new( - buffer.clone(), - smol::block_on(settings.read()).tab_size, - ctx, - ) - }); - ctx.observe(&display_map, Self::on_display_map_changed); + let display_map = DisplayMap::new( + buffer.clone(), + smol::block_on(settings.read()).tab_size, + ctx.as_ref(), + ); let (selection_set_id, _) = buffer.update(ctx, |buffer, ctx| { buffer.add_selection_set( @@ -367,14 +364,13 @@ impl BufferView { return false; } - let map = self.display_map.read(app); let visible_lines = viewport_height / line_height; let first_cursor_top = self .selections(app) .first() .unwrap() .head() - .to_display_point(map, app) + .to_display_point(&self.display_map, app) .unwrap() .row() as f32; let last_cursor_bottom = self @@ -382,7 +378,7 @@ impl BufferView { .last() .unwrap() .head() - .to_display_point(map, app) + .to_display_point(&self.display_map, app) .unwrap() .row() as f32 + 1.0; @@ -415,16 +411,20 @@ impl BufferView { scroll_width: f32, max_glyph_width: f32, layouts: &[Arc], - app: &AppContext, + ctx: &AppContext, ) { - let map = self.display_map.read(app); - let mut target_left = std::f32::INFINITY; let mut target_right = 0.0_f32; - for selection in self.selections(app) { - let head = selection.head().to_display_point(map, app).unwrap(); + for selection in self.selections(ctx) { + let head = selection + .head() + .to_display_point(&self.display_map, ctx) + .unwrap(); let start_column = head.column().saturating_sub(3); - let end_column = cmp::min(map.line_len(head.row(), app).unwrap(), head.column() + 3); + let end_column = cmp::min( + self.display_map.line_len(head.row(), ctx).unwrap(), + head.column() + 3, + ); target_left = target_left .min(layouts[(head.row() - start_row) as usize].x_for_index(start_column as usize)); target_right = target_right.max( @@ -466,8 +466,8 @@ impl BufferView { ctx.emit(Event::Activate); } - let display_map = self.display_map.read(ctx); - let cursor = display_map + let cursor = self + .display_map .anchor_before(position, Bias::Left, ctx.as_ref()) .unwrap(); let selection = Selection { @@ -492,8 +492,8 @@ impl BufferView { ctx: &mut ViewContext, ) { let buffer = self.buffer.read(ctx); - let map = self.display_map.read(ctx); - let cursor = map + let cursor = self + .display_map .anchor_before(position, Bias::Left, ctx.as_ref()) .unwrap(); if let Some(selection) = self.pending_selection.as_mut() { @@ -554,7 +554,6 @@ impl BufferView { where T: IntoIterator>, { - let map = self.display_map.read(ctx); let mut selections = Vec::new(); for range in ranges { let mut start = range.start; @@ -567,8 +566,12 @@ impl BufferView { }; selections.push(Selection { - start: map.anchor_before(start, Bias::Left, ctx.as_ref())?, - end: map.anchor_before(end, Bias::Left, ctx.as_ref())?, + start: self + .display_map + .anchor_before(start, Bias::Left, ctx.as_ref())?, + end: self + .display_map + .anchor_before(end, Bias::Left, ctx.as_ref())?, reversed, goal_column: None, }); @@ -634,17 +637,17 @@ impl BufferView { let mut selections = self.selections(ctx.as_ref()).to_vec(); { let buffer = self.buffer.read(ctx); - let map = self.display_map.read(ctx); for selection in &mut selections { let range = selection.range(buffer); if range.start == range.end { let head = selection .head() - .to_display_point(map, ctx.as_ref()) + .to_display_point(&self.display_map, ctx.as_ref()) .unwrap(); - let cursor = map + let cursor = self + .display_map .anchor_before( - movement::left(map, head, ctx.as_ref()).unwrap(), + movement::left(&self.display_map, head, ctx.as_ref()).unwrap(), Bias::Left, ctx.as_ref(), ) @@ -665,17 +668,17 @@ impl BufferView { let mut selections = self.selections(ctx.as_ref()).to_vec(); { let buffer = self.buffer.read(ctx); - let map = self.display_map.read(ctx); for selection in &mut selections { let range = selection.range(buffer); if range.start == range.end { let head = selection .head() - .to_display_point(map, ctx.as_ref()) + .to_display_point(&self.display_map, ctx.as_ref()) .unwrap(); - let cursor = map + let cursor = self + .display_map .anchor_before( - movement::right(map, head, ctx.as_ref()).unwrap(), + movement::right(&self.display_map, head, ctx.as_ref()).unwrap(), Bias::Right, ctx.as_ref(), ) @@ -695,7 +698,6 @@ impl BufferView { self.start_transaction(ctx); let app = ctx.as_ref(); - let map = self.display_map.read(app); let buffer = self.buffer.read(app); let mut new_cursors = Vec::new(); @@ -703,16 +705,17 @@ 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(&self.display_map, app); let goal_display_column = selection .head() - .to_display_point(map, app) + .to_display_point(&self.display_map, app) .unwrap() .column(); // 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(&self.display_map, app); if next_rows.start <= rows.end { rows.end = next_rows.end; selections.next().unwrap(); @@ -738,14 +741,18 @@ impl BufferView { } let mut cursor = Point::new(cursor_buffer_row, 0) - .to_display_point(map, app) + .to_display_point(&self.display_map, app) .unwrap(); *cursor.column_mut() = cmp::min( goal_display_column, - map.line_len(cursor.row(), app).unwrap(), + self.display_map.line_len(cursor.row(), app).unwrap(), ); - new_cursors.push(cursor.to_buffer_point(map, Bias::Left, app).unwrap()); + new_cursors.push( + cursor + .to_buffer_point(&self.display_map, Bias::Left, app) + .unwrap(), + ); edit_ranges.push(edit_start..edit_end); } @@ -784,15 +791,15 @@ impl BufferView { 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 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(&self.display_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(&self.display_map, app); if next_rows.start <= rows.end - 1 { rows.end = next_rows.end; selections_iter.next().unwrap(); @@ -834,7 +841,6 @@ impl BufferView { 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(); @@ -847,10 +853,10 @@ impl BufferView { // 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); + selection.buffer_rows_for_display_rows(&self.display_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); + next_selection.buffer_rows_for_display_rows(&self.display_map, app); if next_buffer_rows.start <= buffer_rows.end { buffer_rows.end = next_buffer_rows.end; display_rows.end = next_display_rows.end; @@ -873,7 +879,7 @@ impl BufferView { 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) + .to_buffer_offset(&self.display_map, Bias::Left, app) .unwrap(); let mut text = String::new(); @@ -884,7 +890,7 @@ impl BufferView { let row_delta = buffer_rows.start - prev_row_display_start - .to_buffer_point(map, Bias::Left, app) + .to_buffer_point(&self.display_map, Bias::Left, app) .unwrap() .row; @@ -896,7 +902,7 @@ impl BufferView { // Move folds up. old_folds.push(start..end); - for fold in map.folds_in_range(start..end, app).unwrap() { + for fold in self.display_map.folds_in_range(start..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; @@ -925,7 +931,6 @@ impl BufferView { 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(); @@ -938,10 +943,10 @@ impl BufferView { // 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); + selection.buffer_rows_for_display_rows(&self.display_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); + next_selection.buffer_rows_for_display_rows(&self.display_map, app); if next_buffer_rows.start <= buffer_rows.end { buffer_rows.end = next_buffer_rows.end; display_rows.end = next_display_rows.end; @@ -953,7 +958,7 @@ impl BufferView { } // 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() { + if display_rows.end <= self.display_map.max_point(app).row() { let start = Point::new(buffer_rows.start, 0).to_offset(buffer).unwrap(); let end = Point::new( buffer_rows.end - 1, @@ -964,10 +969,10 @@ impl BufferView { let next_row_display_end = DisplayPoint::new( display_rows.end, - map.line_len(display_rows.end, app).unwrap(), + self.display_map.line_len(display_rows.end, app).unwrap(), ); let next_row_end = next_row_display_end - .to_buffer_offset(map, Bias::Right, app) + .to_buffer_offset(&self.display_map, Bias::Right, app) .unwrap(); let mut text = String::new(); @@ -977,7 +982,7 @@ impl BufferView { edits.push((next_row_end..next_row_end, text)); let row_delta = next_row_display_end - .to_buffer_point(map, Bias::Right, app) + .to_buffer_point(&self.display_map, Bias::Right, app) .unwrap() .row - buffer_rows.end @@ -991,7 +996,7 @@ impl BufferView { // Move folds down. old_folds.push(start..end); - for fold in map.folds_in_range(start..end, app).unwrap() { + for fold in self.display_map.folds_in_range(start..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; @@ -1155,16 +1160,26 @@ impl BufferView { let app = ctx.as_ref(); let mut selections = self.selections(app).to_vec(); { - let map = self.display_map.read(app); for selection in &mut selections { - let start = selection.start.to_display_point(map, app).unwrap(); - let end = selection.end.to_display_point(map, app).unwrap(); + let start = selection + .start + .to_display_point(&self.display_map, app) + .unwrap(); + let end = selection + .end + .to_display_point(&self.display_map, app) + .unwrap(); if start != end { selection.end = selection.start.clone(); } else { - let cursor = map - .anchor_before(movement::left(map, start, app).unwrap(), Bias::Left, app) + let cursor = self + .display_map + .anchor_before( + movement::left(&self.display_map, start, app).unwrap(), + Bias::Left, + app, + ) .unwrap(); selection.start = cursor.clone(); selection.end = cursor; @@ -1180,15 +1195,15 @@ impl BufferView { let mut selections = self.selections(ctx.as_ref()).to_vec(); { let buffer = self.buffer.read(ctx); - let map = self.display_map.read(ctx); for selection in &mut selections { let head = selection .head() - .to_display_point(map, ctx.as_ref()) + .to_display_point(&self.display_map, ctx.as_ref()) .unwrap(); - let cursor = map + let cursor = self + .display_map .anchor_before( - movement::left(map, head, ctx.as_ref()).unwrap(), + movement::left(&self.display_map, head, ctx.as_ref()).unwrap(), Bias::Left, ctx.as_ref(), ) @@ -1204,16 +1219,26 @@ impl BufferView { let mut selections = self.selections(ctx.as_ref()).to_vec(); { let app = ctx.as_ref(); - let map = self.display_map.read(app); for selection in &mut selections { - let start = selection.start.to_display_point(map, app).unwrap(); - let end = selection.end.to_display_point(map, app).unwrap(); + let start = selection + .start + .to_display_point(&self.display_map, app) + .unwrap(); + let end = selection + .end + .to_display_point(&self.display_map, app) + .unwrap(); if start != end { selection.start = selection.end.clone(); } else { - let cursor = map - .anchor_before(movement::right(map, end, app).unwrap(), Bias::Right, app) + let cursor = self + .display_map + .anchor_before( + movement::right(&self.display_map, end, app).unwrap(), + Bias::Right, + app, + ) .unwrap(); selection.start = cursor.clone(); selection.end = cursor; @@ -1230,14 +1255,18 @@ impl BufferView { { let app = ctx.as_ref(); let buffer = self.buffer.read(app); - let map = self.display_map.read(app); for selection in &mut selections { let head = selection .head() - .to_display_point(map, ctx.as_ref()) + .to_display_point(&self.display_map, ctx.as_ref()) .unwrap(); - let cursor = map - .anchor_before(movement::right(map, head, app).unwrap(), Bias::Right, app) + let cursor = self + .display_map + .anchor_before( + movement::right(&self.display_map, head, app).unwrap(), + Bias::Right, + app, + ) .unwrap(); selection.set_head(&buffer, cursor); selection.goal_column = None; @@ -1253,17 +1282,25 @@ impl BufferView { let mut selections = self.selections(ctx.as_ref()).to_vec(); { let app = ctx.as_ref(); - let map = self.display_map.read(app); for selection in &mut selections { - let start = selection.start.to_display_point(map, app).unwrap(); - let end = selection.end.to_display_point(map, app).unwrap(); + let start = selection + .start + .to_display_point(&self.display_map, app) + .unwrap(); + let end = selection + .end + .to_display_point(&self.display_map, app) + .unwrap(); if start != end { selection.goal_column = None; } let (start, goal_column) = - movement::up(map, start, selection.goal_column, app).unwrap(); - let cursor = map.anchor_before(start, Bias::Left, app).unwrap(); + movement::up(&self.display_map, start, selection.goal_column, app).unwrap(); + let cursor = self + .display_map + .anchor_before(start, Bias::Left, app) + .unwrap(); selection.start = cursor.clone(); selection.end = cursor; selection.goal_column = goal_column; @@ -1279,12 +1316,19 @@ impl BufferView { { let app = ctx.as_ref(); let buffer = self.buffer.read(app); - let map = self.display_map.read(app); for selection in &mut selections { - let head = selection.head().to_display_point(map, app).unwrap(); + let head = selection + .head() + .to_display_point(&self.display_map, app) + .unwrap(); let (head, goal_column) = - movement::up(map, head, selection.goal_column, app).unwrap(); - selection.set_head(&buffer, map.anchor_before(head, Bias::Left, app).unwrap()); + movement::up(&self.display_map, head, selection.goal_column, app).unwrap(); + selection.set_head( + &buffer, + self.display_map + .anchor_before(head, Bias::Left, app) + .unwrap(), + ); selection.goal_column = goal_column; } } @@ -1298,17 +1342,25 @@ impl BufferView { let mut selections = self.selections(ctx.as_ref()).to_vec(); { let app = ctx.as_ref(); - let map = self.display_map.read(app); for selection in &mut selections { - let start = selection.start.to_display_point(map, app).unwrap(); - let end = selection.end.to_display_point(map, app).unwrap(); + let start = selection + .start + .to_display_point(&self.display_map, app) + .unwrap(); + let end = selection + .end + .to_display_point(&self.display_map, app) + .unwrap(); if start != end { selection.goal_column = None; } let (start, goal_column) = - movement::down(map, end, selection.goal_column, app).unwrap(); - let cursor = map.anchor_before(start, Bias::Right, app).unwrap(); + movement::down(&self.display_map, end, selection.goal_column, app).unwrap(); + let cursor = self + .display_map + .anchor_before(start, Bias::Right, app) + .unwrap(); selection.start = cursor.clone(); selection.end = cursor; selection.goal_column = goal_column; @@ -1324,12 +1376,19 @@ impl BufferView { { let app = ctx.as_ref(); let buffer = self.buffer.read(app); - let map = self.display_map.read(app); for selection in &mut selections { - let head = selection.head().to_display_point(map, app).unwrap(); + let head = selection + .head() + .to_display_point(&self.display_map, app) + .unwrap(); let (head, goal_column) = - movement::down(map, head, selection.goal_column, app).unwrap(); - selection.set_head(&buffer, map.anchor_before(head, Bias::Right, app).unwrap()); + movement::down(&self.display_map, head, selection.goal_column, app).unwrap(); + selection.set_head( + &buffer, + self.display_map + .anchor_before(head, Bias::Right, app) + .unwrap(), + ); selection.goal_column = goal_column; } } @@ -1340,11 +1399,16 @@ impl BufferView { let app = ctx.as_ref(); let mut selections = self.selections(app).to_vec(); { - let map = self.display_map.read(app); for selection in &mut selections { - let head = selection.head().to_display_point(map, app).unwrap(); - let new_head = movement::prev_word_boundary(map, head, app).unwrap(); - let anchor = map.anchor_before(new_head, Bias::Left, app).unwrap(); + let head = selection + .head() + .to_display_point(&self.display_map, app) + .unwrap(); + let new_head = movement::prev_word_boundary(&self.display_map, head, app).unwrap(); + let anchor = self + .display_map + .anchor_before(new_head, Bias::Left, app) + .unwrap(); selection.start = anchor.clone(); selection.end = anchor; selection.reversed = false; @@ -1359,11 +1423,16 @@ impl BufferView { let mut selections = self.selections(app).to_vec(); { let buffer = self.buffer.read(ctx); - let map = self.display_map.read(app); for selection in &mut selections { - let head = selection.head().to_display_point(map, app).unwrap(); - let new_head = movement::prev_word_boundary(map, head, app).unwrap(); - let anchor = map.anchor_before(new_head, Bias::Left, app).unwrap(); + let head = selection + .head() + .to_display_point(&self.display_map, app) + .unwrap(); + let new_head = movement::prev_word_boundary(&self.display_map, head, app).unwrap(); + let anchor = self + .display_map + .anchor_before(new_head, Bias::Left, app) + .unwrap(); selection.set_head(buffer, anchor); selection.goal_column = None; } @@ -1382,11 +1451,16 @@ impl BufferView { let app = ctx.as_ref(); let mut selections = self.selections(app).to_vec(); { - let map = self.display_map.read(app); for selection in &mut selections { - let head = selection.head().to_display_point(map, app).unwrap(); - let new_head = movement::next_word_boundary(map, head, app).unwrap(); - let anchor = map.anchor_before(new_head, Bias::Left, app).unwrap(); + let head = selection + .head() + .to_display_point(&self.display_map, app) + .unwrap(); + let new_head = movement::next_word_boundary(&self.display_map, head, app).unwrap(); + let anchor = self + .display_map + .anchor_before(new_head, Bias::Left, app) + .unwrap(); selection.start = anchor.clone(); selection.end = anchor; selection.reversed = false; @@ -1401,11 +1475,16 @@ impl BufferView { let mut selections = self.selections(app).to_vec(); { let buffer = self.buffer.read(ctx); - let map = self.display_map.read(app); for selection in &mut selections { - let head = selection.head().to_display_point(map, app).unwrap(); - let new_head = movement::next_word_boundary(map, head, app).unwrap(); - let anchor = map.anchor_before(new_head, Bias::Left, app).unwrap(); + let head = selection + .head() + .to_display_point(&self.display_map, app) + .unwrap(); + let new_head = movement::next_word_boundary(&self.display_map, head, app).unwrap(); + let anchor = self + .display_map + .anchor_before(new_head, Bias::Left, app) + .unwrap(); selection.set_head(buffer, anchor); selection.goal_column = None; } @@ -1424,11 +1503,17 @@ impl BufferView { let app = ctx.as_ref(); let mut selections = self.selections(app).to_vec(); { - let map = self.display_map.read(app); for selection in &mut selections { - let head = selection.head().to_display_point(map, app).unwrap(); - let new_head = movement::line_beginning(map, head, true, app).unwrap(); - let anchor = map.anchor_before(new_head, Bias::Left, app).unwrap(); + let head = selection + .head() + .to_display_point(&self.display_map, app) + .unwrap(); + let new_head = + movement::line_beginning(&self.display_map, head, true, app).unwrap(); + let anchor = self + .display_map + .anchor_before(new_head, Bias::Left, app) + .unwrap(); selection.start = anchor.clone(); selection.end = anchor; selection.reversed = false; @@ -1447,11 +1532,17 @@ impl BufferView { let mut selections = self.selections(app).to_vec(); { let buffer = self.buffer.read(ctx); - let map = self.display_map.read(app); for selection in &mut selections { - let head = selection.head().to_display_point(map, app).unwrap(); - let new_head = movement::line_beginning(map, head, *toggle_indent, app).unwrap(); - let anchor = map.anchor_before(new_head, Bias::Left, app).unwrap(); + let head = selection + .head() + .to_display_point(&self.display_map, app) + .unwrap(); + let new_head = + movement::line_beginning(&self.display_map, head, *toggle_indent, app).unwrap(); + let anchor = self + .display_map + .anchor_before(new_head, Bias::Left, app) + .unwrap(); selection.set_head(buffer, anchor); selection.goal_column = None; } @@ -1470,11 +1561,16 @@ impl BufferView { let app = ctx.as_ref(); let mut selections = self.selections(app).to_vec(); { - let map = self.display_map.read(app); for selection in &mut selections { - let head = selection.head().to_display_point(map, app).unwrap(); - let new_head = movement::line_end(map, head, app).unwrap(); - let anchor = map.anchor_before(new_head, Bias::Left, app).unwrap(); + let head = selection + .head() + .to_display_point(&self.display_map, app) + .unwrap(); + let new_head = movement::line_end(&self.display_map, head, app).unwrap(); + let anchor = self + .display_map + .anchor_before(new_head, Bias::Left, app) + .unwrap(); selection.start = anchor.clone(); selection.end = anchor; selection.reversed = false; @@ -1489,11 +1585,16 @@ impl BufferView { let mut selections = self.selections(app).to_vec(); { let buffer = self.buffer.read(ctx); - let map = self.display_map.read(app); for selection in &mut selections { - let head = selection.head().to_display_point(map, app).unwrap(); - let new_head = movement::line_end(map, head, app).unwrap(); - let anchor = map.anchor_before(new_head, Bias::Left, app).unwrap(); + let head = selection + .head() + .to_display_point(&self.display_map, app) + .unwrap(); + let new_head = movement::line_end(&self.display_map, head, app).unwrap(); + let anchor = self + .display_map + .anchor_before(new_head, Bias::Left, app) + .unwrap(); selection.set_head(buffer, anchor); selection.goal_column = None; } @@ -1559,12 +1660,13 @@ impl BufferView { range: Range, app: &'a AppContext, ) -> impl 'a + Iterator> { - let map = self.display_map.read(app); - - let start = map.anchor_before(range.start, Bias::Left, app).unwrap(); + let start = self + .display_map + .anchor_before(range.start, Bias::Left, app) + .unwrap(); let start_index = self.selection_insertion_index(&start, app); let pending_selection = self.pending_selection.as_ref().and_then(|s| { - let selection_range = s.display_range(map, app); + let selection_range = s.display_range(&self.display_map, app); if selection_range.start <= range.end || selection_range.end <= range.end { Some(selection_range) } else { @@ -1573,7 +1675,7 @@ impl BufferView { }); self.selections(app)[start_index..] .iter() - .map(move |s| s.display_range(map, app)) + .map(move |s| s.display_range(&self.display_map, app)) .take_while(move |r| r.start <= range.end || r.end <= range.end) .chain(pending_selection) } @@ -1673,17 +1775,18 @@ impl BufferView { let mut fold_ranges = Vec::new(); let app = ctx.as_ref(); - let map = self.display_map.read(app); for selection in self.selections(app) { - let range = selection.display_range(map, app).sorted(); + let range = selection.display_range(&self.display_map, app).sorted(); let buffer_start_row = range .start - .to_buffer_point(map, Bias::Left, app) + .to_buffer_point(&self.display_map, Bias::Left, app) .unwrap() .row; for row in (0..=range.end.row()).rev() { - if self.is_line_foldable(row, app) && !map.is_line_folded(row) { + if self.is_line_foldable(row, app) + && !self.display_map.is_line_folded(row, ctx.as_ref()) + { let fold_range = self.foldable_range_for_line(row, app).unwrap(); if fold_range.end.row >= buffer_start_row { fold_ranges.push(fold_range); @@ -1702,15 +1805,20 @@ impl BufferView { use super::RangeExt; let app = ctx.as_ref(); - let map = self.display_map.read(app); let buffer = self.buffer.read(app); let ranges = self .selections(app) .iter() .map(|s| { - let range = s.display_range(map, app).sorted(); - let mut start = range.start.to_buffer_point(map, Bias::Left, app).unwrap(); - let mut end = range.end.to_buffer_point(map, Bias::Left, app).unwrap(); + let range = s.display_range(&self.display_map, app).sorted(); + let mut start = range + .start + .to_buffer_point(&self.display_map, Bias::Left, app) + .unwrap(); + let mut end = range + .end + .to_buffer_point(&self.display_map, Bias::Left, app) + .unwrap(); start.column = 0; end.column = buffer.line_len(end.row).unwrap(); start..end @@ -1720,17 +1828,17 @@ impl BufferView { } fn is_line_foldable(&self, display_row: u32, app: &AppContext) -> bool { - let map = self.display_map.read(app); let max_point = self.max_point(app); if display_row >= max_point.row() { false } else { - let (start_indent, is_blank) = map.line_indent(display_row, app).unwrap(); + let (start_indent, is_blank) = self.display_map.line_indent(display_row, app).unwrap(); if is_blank { false } else { for display_row in display_row + 1..=max_point.row() { - let (indent, is_blank) = map.line_indent(display_row, app).unwrap(); + let (indent, is_blank) = + self.display_map.line_indent(display_row, app).unwrap(); if !is_blank { return indent > start_indent; } @@ -1741,14 +1849,13 @@ impl BufferView { } fn foldable_range_for_line(&self, start_row: u32, app: &AppContext) -> Result> { - let map = self.display_map.read(app); let max_point = self.max_point(app); - let (start_indent, _) = map.line_indent(start_row, app)?; + let (start_indent, _) = self.display_map.line_indent(start_row, app)?; let start = DisplayPoint::new(start_row, self.line_len(start_row, app)?); let mut end = None; for row in start_row + 1..=max_point.row() { - let (indent, is_blank) = map.line_indent(row, app)?; + let (indent, is_blank) = self.display_map.line_indent(row, app)?; if !is_blank && indent <= start_indent { end = Some(DisplayPoint::new(row - 1, self.line_len(row - 1, app)?)); break; @@ -1756,60 +1863,57 @@ impl BufferView { } let end = end.unwrap_or(max_point); - return Ok(start.to_buffer_point(map, Bias::Left, app)? - ..end.to_buffer_point(map, Bias::Left, app)?); + return Ok(start.to_buffer_point(&self.display_map, Bias::Left, app)? + ..end.to_buffer_point(&self.display_map, Bias::Left, app)?); } pub fn fold_selected_ranges(&mut self, _: &(), ctx: &mut ViewContext) { use super::RangeExt; - self.display_map.update(ctx, |map, ctx| { - let buffer = self.buffer.read(ctx); - let ranges = self - .selections(ctx.as_ref()) - .iter() - .map(|s| s.range(buffer).sorted()) - .collect::>(); - map.fold(ranges, ctx).unwrap(); - }); + let buffer = self.buffer.read(ctx); + let ranges = self + .selections(ctx.as_ref()) + .iter() + .map(|s| s.range(buffer).sorted()) + .collect::>(); + self.display_map.fold(ranges, ctx.as_ref()).unwrap(); + ctx.notify(); } 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.display_map.fold(ranges, ctx.as_ref()).unwrap(); *self.autoscroll_requested.lock() = true; + ctx.notify(); } } 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.display_map.unfold(ranges, ctx.as_ref()).unwrap(); *self.autoscroll_requested.lock() = true; + ctx.notify(); } } - pub fn line(&self, display_row: u32, app: &AppContext) -> Result { - self.display_map.read(app).line(display_row, app) + pub fn line(&self, display_row: u32, ctx: &AppContext) -> Result { + self.display_map.line(display_row, ctx) } - pub fn line_len(&self, display_row: u32, app: &AppContext) -> Result { - self.display_map.read(app).line_len(display_row, app) + pub fn line_len(&self, display_row: u32, ctx: &AppContext) -> Result { + self.display_map.line_len(display_row, ctx) } - pub fn rightmost_point(&self, app: &AppContext) -> DisplayPoint { - self.display_map.read(app).rightmost_point() + pub fn rightmost_point(&self, ctx: &AppContext) -> DisplayPoint { + self.display_map.rightmost_point(ctx) } - pub fn max_point(&self, app: &AppContext) -> DisplayPoint { - self.display_map.read(app).max_point(app) + pub fn max_point(&self, ctx: &AppContext) -> DisplayPoint { + self.display_map.max_point(ctx) } - pub fn text(&self, app: &AppContext) -> String { - self.display_map.read(app).text(app) + pub fn text(&self, ctx: &AppContext) -> String { + self.display_map.text(ctx) } pub fn font_size(&self) -> f32 { @@ -1872,10 +1976,8 @@ impl BufferView { viewport_height: f32, font_cache: &FontCache, layout_cache: &TextLayoutCache, - app: &AppContext, + ctx: &AppContext, ) -> Result>> { - let display_map = self.display_map.read(app); - let settings = smol::block_on(self.settings.read()); let font_size = settings.buffer_font_size; let font_id = @@ -1883,14 +1985,19 @@ impl BufferView { let start_row = self.scroll_position().y() as usize; let end_row = cmp::min( - self.max_point(app).row() as usize, + self.max_point(ctx).row() as usize, start_row + (viewport_height / self.line_height(font_cache)).ceil() as usize, ); let line_count = end_row - start_row + 1; let mut layouts = Vec::with_capacity(line_count); let mut line_number = String::new(); - for buffer_row in display_map.buffer_rows(start_row as u32)?.take(line_count) { + for buffer_row in self + .display_map + .snapshot(ctx) + .buffer_rows(start_row as u32)? + .take(line_count) + { line_number.clear(); write!(&mut line_number, "{}", buffer_row + 1).unwrap(); layouts.push(layout_cache.layout_str( @@ -1908,11 +2015,9 @@ impl BufferView { mut rows: Range, font_cache: &FontCache, layout_cache: &TextLayoutCache, - app: &AppContext, + ctx: &AppContext, ) -> Result>> { - let display_map = self.display_map.read(app); - - rows.end = cmp::min(rows.end, display_map.max_point(app).row() + 1); + rows.end = cmp::min(rows.end, self.display_map.max_point(ctx).row() + 1); if rows.start >= rows.end { return Ok(Vec::new()); } @@ -1926,8 +2031,9 @@ impl BufferView { let mut line = String::new(); let mut line_len = 0; let mut row = rows.start; - let chars = display_map - .chars_at(DisplayPoint::new(rows.start, 0), app) + let snapshot = self.display_map.snapshot(ctx); + let chars = snapshot + .chars_at(DisplayPoint::new(rows.start, 0), ctx) .unwrap(); for char in chars.chain(Some('\n')) { if char == '\n' { @@ -2019,10 +2125,6 @@ impl BufferView { ctx.notify(); } - fn on_display_map_changed(&mut self, _: ModelHandle, ctx: &mut ViewContext) { - ctx.notify(); - } - fn on_buffer_event( &mut self, _: ModelHandle, @@ -2030,7 +2132,7 @@ impl BufferView { ctx: &mut ViewContext, ) { match event { - buffer::Event::Edited(_) => ctx.emit(Event::Edited), + buffer::Event::Edited => ctx.emit(Event::Edited), buffer::Event::Dirtied => ctx.emit(Event::Dirtied), buffer::Event::Saved => ctx.emit(Event::Saved), buffer::Event::FileHandleChanged => ctx.emit(Event::FileHandleChanged), diff --git a/zed/src/editor/display_map/fold_map.rs b/zed/src/editor/display_map/fold_map.rs index 9ae149c3eb2cbe1d09a9857ae3b60964d94a1178..8240954345dc5c86f12f46e602e1e8499828ed88 100644 --- a/zed/src/editor/display_map/fold_map.rs +++ b/zed/src/editor/display_map/fold_map.rs @@ -3,10 +3,12 @@ use super::{ }; use crate::{ sum_tree::{self, Cursor, SumTree}, + time, util::find_insertion_index, }; use anyhow::{anyhow, Result}; use gpui::{AppContext, ModelHandle}; +use parking_lot::{Mutex, MutexGuard}; use std::{ cmp::{self, Ordering}, iter::Take, @@ -16,49 +18,44 @@ use sum_tree::{Dimension, SeekBias}; pub struct FoldMap { buffer: ModelHandle, - transforms: SumTree, + transforms: Mutex>, folds: Vec>, + last_sync: Mutex, } impl FoldMap { - pub fn new(buffer: ModelHandle, app: &AppContext) -> Self { - let text_summary = buffer.read(app).text_summary(); + pub fn new(buffer_handle: ModelHandle, ctx: &AppContext) -> Self { + let buffer = buffer_handle.read(ctx); + let text_summary = buffer.text_summary(); Self { - buffer, + buffer: buffer_handle, folds: Vec::new(), - transforms: SumTree::from_item(Transform { + transforms: Mutex::new(SumTree::from_item(Transform { summary: TransformSummary { buffer: text_summary.clone(), display: text_summary, }, display_text: None, - }), + })), + last_sync: Mutex::new(buffer.version()), } } - pub fn buffer_rows(&self, start_row: u32) -> Result { - if start_row > self.transforms.summary().display.lines.row { - return Err(anyhow!("invalid display row {}", start_row)); + pub fn snapshot(&self, ctx: &AppContext) -> FoldMapSnapshot { + FoldMapSnapshot { + transforms: self.sync(ctx).clone(), + buffer: self.buffer.clone(), } - - let display_point = Point::new(start_row, 0); - let mut cursor = self.transforms.cursor(); - cursor.seek(&DisplayPoint(display_point), SeekBias::Left); - - Ok(BufferRows { - display_point, - cursor, - }) } - pub fn len(&self) -> usize { - self.transforms.summary().display.chars + pub fn len(&self, ctx: &AppContext) -> usize { + self.sync(ctx).summary().display.chars } pub fn line_len(&self, row: u32, ctx: &AppContext) -> Result { let line_start = self.to_display_offset(DisplayPoint::new(row, 0), ctx)?.0; - let line_end = if row >= self.max_point().row() { - self.len() + let line_end = if row >= self.max_point(ctx).row() { + self.len(ctx) } else { self.to_display_offset(DisplayPoint::new(row + 1, 0), ctx)? .0 @@ -68,25 +65,12 @@ impl FoldMap { Ok((line_end - line_start) as u32) } - pub fn chars_at<'a>(&'a self, point: DisplayPoint, app: &'a AppContext) -> Result> { - let offset = self.to_display_offset(point, app)?; - let mut cursor = self.transforms.cursor(); - cursor.seek(&offset, SeekBias::Right); - let buffer = self.buffer.read(app); - Ok(Chars { - cursor, - offset: offset.0, - buffer, - buffer_chars: None, - }) + pub fn max_point(&self, ctx: &AppContext) -> DisplayPoint { + DisplayPoint(self.sync(ctx).summary().display.lines) } - pub fn max_point(&self) -> DisplayPoint { - DisplayPoint(self.transforms.summary().display.lines) - } - - pub fn rightmost_point(&self) -> DisplayPoint { - DisplayPoint(self.transforms.summary().display.rightmost_point) + pub fn rightmost_point(&self, ctx: &AppContext) -> DisplayPoint { + DisplayPoint(self.sync(ctx).summary().display.rightmost_point) } pub fn folds_in_range<'a, T>( @@ -108,10 +92,12 @@ impl FoldMap { pub fn fold( &mut self, ranges: impl IntoIterator>, - app: &AppContext, + ctx: &AppContext, ) -> Result<()> { + let _ = self.sync(ctx); + let mut edits = Vec::new(); - let buffer = self.buffer.read(app); + let buffer = self.buffer.read(ctx); for range in ranges.into_iter() { let start = range.start.to_offset(buffer)?; let end = range.end.to_offset(buffer)?; @@ -131,16 +117,18 @@ impl FoldMap { .then_with(|| b.old_range.end.cmp(&a.old_range.end)) }); - self.apply_edits(&edits, app)?; + self.apply_edits(edits, ctx); Ok(()) } pub fn unfold( &mut self, ranges: impl IntoIterator>, - app: &AppContext, + ctx: &AppContext, ) -> Result<()> { - let buffer = self.buffer.read(app); + let _ = self.sync(ctx); + + let buffer = self.buffer.read(ctx); let mut edits = Vec::new(); for range in ranges.into_iter() { @@ -165,12 +153,13 @@ impl FoldMap { }); } - self.apply_edits(&edits, app)?; + self.apply_edits(edits, ctx); Ok(()) } - pub fn is_line_folded(&self, display_row: u32) -> bool { - let mut cursor = self.transforms.cursor::(); + pub fn is_line_folded(&self, display_row: u32, ctx: &AppContext) -> bool { + let transforms = self.sync(ctx); + let mut cursor = transforms.cursor::(); cursor.seek(&DisplayPoint::new(display_row, 0), SeekBias::Right); while let Some(transform) = cursor.item() { if transform.display_text.is_some() { @@ -185,43 +174,33 @@ impl FoldMap { false } - pub fn to_buffer_offset(&self, point: DisplayPoint, app: &AppContext) -> Result { - let mut cursor = self.transforms.cursor::(); + pub fn to_buffer_offset(&self, point: DisplayPoint, ctx: &AppContext) -> Result { + let transforms = self.sync(ctx); + let mut cursor = 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)) + (cursor.start().buffer.lines + overshoot).to_offset(self.buffer.read(ctx)) } pub fn to_display_offset( &self, point: DisplayPoint, - app: &AppContext, + ctx: &AppContext, ) -> Result { - let mut cursor = self.transforms.cursor::(); - cursor.seek(&point, SeekBias::Right); - let overshoot = point.0 - cursor.start().display.lines; - let mut offset = cursor.start().display.chars; - if !overshoot.is_zero() { - let transform = cursor - .item() - .ok_or_else(|| anyhow!("display point {:?} is out of range", point))?; - assert!(transform.display_text.is_none()); - let end_buffer_offset = - (cursor.start().buffer.lines + overshoot).to_offset(self.buffer.read(app))?; - offset += end_buffer_offset - cursor.start().buffer.chars; - } - Ok(DisplayOffset(offset)) + self.snapshot(ctx).to_display_offset(point, ctx) } - pub fn to_buffer_point(&self, display_point: DisplayPoint) -> Point { - let mut cursor = self.transforms.cursor::(); + pub fn to_buffer_point(&self, display_point: DisplayPoint, ctx: &AppContext) -> Point { + let transforms = self.sync(ctx); + let mut cursor = transforms.cursor::(); cursor.seek(&display_point, SeekBias::Right); let overshoot = display_point.0 - cursor.start().display.lines; cursor.start().buffer.lines + overshoot } - pub fn to_display_point(&self, point: Point) -> DisplayPoint { - let mut cursor = self.transforms.cursor::(); + pub fn to_display_point(&self, point: Point, ctx: &AppContext) -> DisplayPoint { + let transforms = self.sync(ctx); + let mut cursor = transforms.cursor::(); cursor.seek(&point, SeekBias::Right); let overshoot = point - cursor.start().buffer.lines; DisplayPoint(cmp::min( @@ -230,12 +209,23 @@ impl FoldMap { )) } - pub fn apply_edits(&mut self, edits: &[Edit], app: &AppContext) -> Result<()> { - let buffer = self.buffer.read(app); - let mut edits = edits.iter().cloned().peekable(); + fn sync(&self, ctx: &AppContext) -> MutexGuard> { + let buffer = self.buffer.read(ctx); + let mut edits = buffer.edits_since(self.last_sync.lock().clone()).peekable(); + if edits.peek().is_some() { + self.apply_edits(edits, ctx); + } + *self.last_sync.lock() = buffer.version(); + self.transforms.lock() + } + + fn apply_edits(&self, edits: impl IntoIterator, ctx: &AppContext) { + let buffer = self.buffer.read(ctx); + let mut edits = edits.into_iter().peekable(); let mut new_transforms = SumTree::new(); - let mut cursor = self.transforms.cursor::(); + let mut transforms = self.transforms.lock(); + let mut cursor = transforms.cursor::(); cursor.seek(&0, SeekBias::Right); while let Some(mut edit) = edits.next() { @@ -271,9 +261,10 @@ impl FoldMap { edit.new_range.end = ((edit.new_range.start + edit.old_extent()) as isize + delta) as usize; - let anchor = buffer.anchor_before(edit.new_range.start)?; + let anchor = buffer.anchor_before(edit.new_range.start).unwrap(); let folds_start = - find_insertion_index(&self.folds, |probe| probe.start.cmp(&anchor, buffer))?; + find_insertion_index(&self.folds, |probe| probe.start.cmp(&anchor, buffer)) + .unwrap(); let mut folds = self.folds[folds_start..] .iter() .map(|fold| { @@ -355,9 +346,58 @@ impl FoldMap { } drop(cursor); - self.transforms = new_transforms; + *transforms = new_transforms; + } +} - Ok(()) +pub struct FoldMapSnapshot { + transforms: SumTree, + buffer: ModelHandle, +} + +impl FoldMapSnapshot { + pub fn buffer_rows(&self, start_row: u32) -> Result { + if start_row > self.transforms.summary().display.lines.row { + return Err(anyhow!("invalid display row {}", start_row)); + } + + let display_point = Point::new(start_row, 0); + let mut cursor = self.transforms.cursor(); + cursor.seek(&DisplayPoint(display_point), SeekBias::Left); + + Ok(BufferRows { + display_point, + cursor, + }) + } + + pub fn chars_at<'a>(&'a self, point: DisplayPoint, ctx: &'a AppContext) -> Result> { + let offset = self.to_display_offset(point, ctx)?; + let mut cursor = self.transforms.cursor(); + cursor.seek(&offset, SeekBias::Right); + Ok(Chars { + cursor, + offset: offset.0, + buffer: self.buffer.read(ctx), + buffer_chars: None, + }) + } + + fn to_display_offset(&self, point: DisplayPoint, ctx: &AppContext) -> Result { + let mut cursor = self.transforms.cursor::(); + cursor.seek(&point, SeekBias::Right); + let overshoot = point.0 - cursor.start().display.lines; + let mut offset = cursor.start().display.chars; + if !overshoot.is_zero() { + let transform = cursor + .item() + .ok_or_else(|| anyhow!("display point {:?} is out of range", point))?; + assert!(transform.display_text.is_none()); + let end_buffer_offset = + (cursor.start().buffer.lines + overshoot).to_offset(self.buffer.read(ctx))?; + offset += end_buffer_offset - cursor.start().buffer.chars; + } + Ok(DisplayOffset(offset)) } } @@ -508,8 +548,7 @@ mod tests { .unwrap(); assert_eq!(map.text(app.as_ref()), "aa…cc…eeeee"); - let edits = buffer.update(app, |buffer, ctx| { - let start_version = buffer.version.clone(); + buffer.update(app, |buffer, ctx| { buffer .edit( vec![ @@ -520,21 +559,16 @@ mod tests { Some(ctx), ) .unwrap(); - buffer.edits_since(start_version).collect::>() }); - - map.apply_edits(&edits, app.as_ref()).unwrap(); assert_eq!(map.text(app.as_ref()), "123a…c123c…eeeee"); - let edits = buffer.update(app, |buffer, ctx| { + buffer.update(app, |buffer, ctx| { let start_version = buffer.version.clone(); buffer .edit(Some(Point::new(2, 6)..Point::new(4, 3)), "456", Some(ctx)) .unwrap(); buffer.edits_since(start_version).collect::>() }); - - map.apply_edits(&edits, app.as_ref()).unwrap(); assert_eq!(map.text(app.as_ref()), "123a…c123456eee"); map.unfold(Some(Point::new(0, 4)..Point::new(0, 4)), app.as_ref()) @@ -575,12 +609,11 @@ mod tests { assert_eq!(map.text(app.as_ref()), "…fghijkl"); // Edit within one of the folds. - let edits = buffer.update(app, |buffer, ctx| { + 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"); } @@ -622,15 +655,11 @@ mod tests { .unwrap(); assert_eq!(map.text(app.as_ref()), "aa…cccc\nd…eeeee"); - let edits = buffer.update(app, |buffer, ctx| { - let start_version = buffer.version.clone(); + buffer.update(app, |buffer, ctx| { buffer .edit(Some(Point::new(2, 2)..Point::new(3, 1)), "", Some(ctx)) .unwrap(); - buffer.edits_since(start_version).collect::>() }); - - map.apply_edits(&edits, app.as_ref()).unwrap(); assert_eq!(map.text(app.as_ref()), "aa…eeeee"); }); } @@ -723,7 +752,6 @@ mod tests { buffer.edits_since(start_version).collect::>() }); log::info!("editing {:?}", edits); - map.apply_edits(&edits, app.as_ref()).unwrap(); } map.check_invariants(app.as_ref()); @@ -752,9 +780,12 @@ mod tests { let mut display_point = DisplayPoint::new(0, 0); let mut display_offset = DisplayOffset(0); for c in expected_text.chars() { - let buffer_point = map.to_buffer_point(display_point); + let buffer_point = map.to_buffer_point(display_point, app.as_ref()); let buffer_offset = buffer_point.to_offset(buffer).unwrap(); - assert_eq!(map.to_display_point(buffer_point), display_point); + assert_eq!( + map.to_display_point(buffer_point, app.as_ref()), + display_point + ); assert_eq!( map.to_buffer_offset(display_point, app.as_ref()).unwrap(), buffer_offset @@ -774,13 +805,14 @@ mod tests { } for _ in 0..5 { - let row = rng.gen_range(0..=map.max_point().row()); + let row = rng.gen_range(0..=map.max_point(app.as_ref()).row()); let column = rng.gen_range(0..=map.line_len(row, app.as_ref()).unwrap()); let point = DisplayPoint::new(row, column); let offset = map.to_display_offset(point, app.as_ref()).unwrap().0; - let len = rng.gen_range(0..=map.len() - offset); + let len = rng.gen_range(0..=map.len(app.as_ref()) - offset); assert_eq!( - map.chars_at(point, app.as_ref()) + map.snapshot(app.as_ref()) + .chars_at(point, app.as_ref()) .unwrap() .take(len) .collect::(), @@ -793,17 +825,24 @@ mod tests { } for (idx, buffer_row) in expected_buffer_rows.iter().enumerate() { - let display_row = map.to_display_point(Point::new(*buffer_row, 0)).row(); + let display_row = map + .to_display_point(Point::new(*buffer_row, 0), app.as_ref()) + .row(); assert_eq!( - map.buffer_rows(display_row).unwrap().collect::>(), + map.snapshot(app.as_ref()) + .buffer_rows(display_row) + .unwrap() + .collect::>(), expected_buffer_rows[idx..], ); } 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())); + let display_point = map.to_display_point( + fold_range.start.to_point(buffer).unwrap(), + app.as_ref(), + ); + assert!(map.is_line_folded(display_point.row(), app.as_ref())); } } }); @@ -829,16 +868,26 @@ mod tests { assert_eq!(map.text(app.as_ref()), "aa…cccc\nd…eeeee\nffffff\n"); assert_eq!( - map.buffer_rows(0).unwrap().collect::>(), + map.snapshot(app.as_ref()) + .buffer_rows(0) + .unwrap() + .collect::>(), vec![0, 3, 5, 6] ); - assert_eq!(map.buffer_rows(3).unwrap().collect::>(), vec![6]); + assert_eq!( + map.snapshot(app.as_ref()) + .buffer_rows(3) + .unwrap() + .collect::>(), + vec![6] + ); }); } impl FoldMap { fn text(&self, app: &AppContext) -> String { - self.chars_at(DisplayPoint(Point::zero()), app) + self.snapshot(app) + .chars_at(DisplayPoint(Point::zero()), app) .unwrap() .collect() } @@ -874,10 +923,11 @@ mod tests { merged_ranges } - fn check_invariants(&self, app: &AppContext) { - let buffer = self.buffer.read(app); + fn check_invariants(&self, ctx: &AppContext) { + let transforms = self.sync(ctx); + let buffer = self.buffer.read(ctx); assert_eq!( - self.transforms.summary().buffer.chars, + transforms.summary().buffer.chars, buffer.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 d026d6eb54cc486ed698957371897e223b750048..96392610d093861cfebeafbd6fdd42b6bbda797c 100644 --- a/zed/src/editor/display_map/mod.rs +++ b/zed/src/editor/display_map/mod.rs @@ -3,8 +3,8 @@ mod fold_map; use super::{buffer, Anchor, AnchorRangeExt, Buffer, Edit, Point, TextSummary, ToOffset, ToPoint}; use anyhow::Result; pub use fold_map::BufferRows; -use fold_map::FoldMap; -use gpui::{AppContext, Entity, ModelContext, ModelHandle}; +use fold_map::{FoldMap, FoldMapSnapshot}; +use gpui::{AppContext, ModelHandle}; use std::ops::Range; #[derive(Copy, Clone)] @@ -19,21 +19,22 @@ pub struct DisplayMap { tab_size: usize, } -impl Entity for DisplayMap { - type Event = (); -} - impl DisplayMap { - pub fn new(buffer: ModelHandle, tab_size: usize, ctx: &mut ModelContext) -> Self { - ctx.subscribe(&buffer, Self::handle_buffer_event); - + pub fn new(buffer: ModelHandle, tab_size: usize, ctx: &AppContext) -> Self { DisplayMap { buffer: buffer.clone(), - fold_map: FoldMap::new(buffer, ctx.as_ref()), + fold_map: FoldMap::new(buffer, ctx), tab_size, } } + pub fn snapshot(&self, ctx: &AppContext) -> DisplayMapSnapshot { + DisplayMapSnapshot { + folds_snapshot: self.fold_map.snapshot(ctx), + tab_size: self.tab_size, + } + } + pub fn folds_in_range<'a, T>( &'a self, range: Range, @@ -48,40 +49,45 @@ impl DisplayMap { pub fn fold( &mut self, ranges: impl IntoIterator>, - ctx: &mut ModelContext, + ctx: &AppContext, ) -> Result<()> { - self.fold_map.fold(ranges, ctx.as_ref())?; - ctx.notify(); - Ok(()) + self.fold_map.fold(ranges, ctx) } pub fn unfold( &mut self, ranges: impl IntoIterator>, - ctx: &mut ModelContext, + ctx: &AppContext, ) -> Result<()> { - self.fold_map.unfold(ranges, ctx.as_ref())?; - ctx.notify(); - Ok(()) + self.fold_map.unfold(ranges, ctx) } - pub fn is_line_folded(&self, display_row: u32) -> bool { - self.fold_map.is_line_folded(display_row) + pub fn is_line_folded(&self, display_row: u32, ctx: &AppContext) -> bool { + self.fold_map.is_line_folded(display_row, ctx) } - pub fn text(&self, app: &AppContext) -> String { - self.chars_at(DisplayPoint::zero(), app).unwrap().collect() + pub fn text(&self, ctx: &AppContext) -> String { + self.snapshot(ctx) + .chars_at(DisplayPoint::zero(), ctx) + .unwrap() + .collect() } - pub fn line(&self, display_row: u32, app: &AppContext) -> Result { - let chars = self.chars_at(DisplayPoint::new(display_row, 0), app)?; - Ok(chars.take_while(|c| *c != '\n').collect()) + pub fn line(&self, display_row: u32, ctx: &AppContext) -> Result { + Ok(self + .snapshot(ctx) + .chars_at(DisplayPoint::new(display_row, 0), ctx)? + .take_while(|c| *c != '\n') + .collect()) } - pub fn line_indent(&self, display_row: u32, app: &AppContext) -> Result<(u32, bool)> { + pub fn line_indent(&self, display_row: u32, ctx: &AppContext) -> Result<(u32, bool)> { let mut indent = 0; let mut is_blank = true; - for c in self.chars_at(DisplayPoint::new(display_row, 0), app)? { + for c in self + .snapshot(ctx) + .chars_at(DisplayPoint::new(display_row, 0), ctx)? + { if c == ' ' { indent += 1; } else { @@ -92,38 +98,18 @@ impl DisplayMap { Ok((indent, is_blank)) } - pub fn chars_at<'a>(&'a self, point: DisplayPoint, app: &'a AppContext) -> Result> { - let column = point.column() as usize; - let (point, to_next_stop) = point.collapse_tabs(self, Bias::Left, app)?; - let mut fold_chars = self.fold_map.chars_at(point, app)?; - if to_next_stop > 0 { - fold_chars.next(); - } - - Ok(Chars { - fold_chars, - column, - to_next_stop, - tab_size: self.tab_size, - }) - } - - pub fn buffer_rows(&self, start_row: u32) -> Result { - self.fold_map.buffer_rows(start_row) - } - pub fn line_len(&self, row: u32, ctx: &AppContext) -> Result { DisplayPoint::new(row, self.fold_map.line_len(row, ctx)?) .expand_tabs(self, ctx) .map(|point| point.column()) } - pub fn max_point(&self, app: &AppContext) -> DisplayPoint { - self.fold_map.max_point().expand_tabs(self, app).unwrap() + pub fn max_point(&self, ctx: &AppContext) -> DisplayPoint { + self.fold_map.max_point(ctx).expand_tabs(self, ctx).unwrap() } - pub fn rightmost_point(&self) -> DisplayPoint { - self.fold_map.rightmost_point() + pub fn rightmost_point(&self, ctx: &AppContext) -> DisplayPoint { + self.fold_map.rightmost_point(ctx) } pub fn anchor_before( @@ -147,12 +133,57 @@ impl DisplayMap { .read(app) .anchor_after(point.to_buffer_point(self, bias, app)?) } +} - fn handle_buffer_event(&mut self, event: &buffer::Event, ctx: &mut ModelContext) { - match event { - buffer::Event::Edited(edits) => self.fold_map.apply_edits(edits, ctx.as_ref()).unwrap(), - _ => {} +pub struct DisplayMapSnapshot { + folds_snapshot: FoldMapSnapshot, + tab_size: usize, +} + +impl DisplayMapSnapshot { + pub fn buffer_rows(&self, start_row: u32) -> Result { + self.folds_snapshot.buffer_rows(start_row) + } + + pub fn chars_at<'a>(&'a self, point: DisplayPoint, app: &'a AppContext) -> Result> { + let column = point.column() as usize; + let (point, to_next_stop) = self.collapse_tabs(point, Bias::Left, app)?; + let mut fold_chars = self.folds_snapshot.chars_at(point, app)?; + if to_next_stop > 0 { + fold_chars.next(); } + + Ok(Chars { + fold_chars, + column, + to_next_stop, + tab_size: self.tab_size, + }) + } + + fn expand_tabs(&self, mut point: DisplayPoint, ctx: &AppContext) -> Result { + let chars = self + .folds_snapshot + .chars_at(DisplayPoint(Point::new(point.row(), 0)), ctx)?; + let expanded = expand_tabs(chars, point.column() as usize, self.tab_size); + *point.column_mut() = expanded as u32; + Ok(point) + } + + fn collapse_tabs( + &self, + mut point: DisplayPoint, + bias: Bias, + ctx: &AppContext, + ) -> Result<(DisplayPoint, usize)> { + let chars = self + .folds_snapshot + .chars_at(DisplayPoint(Point::new(point.row(), 0)), ctx)?; + let expanded = point.column() as usize; + let (collapsed, to_next_stop) = collapse_tabs(chars, expanded, bias, self.tab_size); + *point.column_mut() = collapsed as u32; + + Ok((point, to_next_stop)) } } @@ -184,50 +215,36 @@ impl DisplayPoint { &mut self.0.column } - pub fn to_buffer_point(self, map: &DisplayMap, bias: Bias, app: &AppContext) -> Result { + pub fn to_buffer_point(self, map: &DisplayMap, bias: Bias, ctx: &AppContext) -> Result { Ok(map .fold_map - .to_buffer_point(self.collapse_tabs(map, bias, app)?.0)) + .to_buffer_point(self.collapse_tabs(map, bias, ctx)?.0, ctx)) } - pub fn to_buffer_offset(self, map: &DisplayMap, bias: Bias, app: &AppContext) -> Result { + pub fn to_buffer_offset(self, map: &DisplayMap, bias: Bias, ctx: &AppContext) -> Result { map.fold_map - .to_buffer_offset(self.collapse_tabs(map, bias, app)?.0, app) + .to_buffer_offset(self.collapse_tabs(&map, bias, ctx)?.0, ctx) } - fn expand_tabs(mut self, map: &DisplayMap, app: &AppContext) -> Result { - let chars = map - .fold_map - .chars_at(DisplayPoint(Point::new(self.row(), 0)), app)?; - let expanded = expand_tabs(chars, self.column() as usize, map.tab_size); - *self.column_mut() = expanded as u32; - - Ok(self) + fn expand_tabs(self, map: &DisplayMap, ctx: &AppContext) -> Result { + map.snapshot(ctx).expand_tabs(self, ctx) } fn collapse_tabs( - mut self, + self, map: &DisplayMap, bias: Bias, - app: &AppContext, + ctx: &AppContext, ) -> Result<(Self, usize)> { - let chars = map - .fold_map - .chars_at(DisplayPoint(Point::new(self.0.row, 0)), app)?; - let expanded = self.column() as usize; - let (collapsed, to_next_stop) = collapse_tabs(chars, expanded, bias, map.tab_size); - *self.column_mut() = collapsed as u32; - - Ok((self, to_next_stop)) + map.snapshot(ctx).collapse_tabs(self, bias, ctx) } } impl Point { - pub fn to_display_point(self, map: &DisplayMap, app: &AppContext) -> Result { - let mut display_point = map.fold_map.to_display_point(self); - let chars = map - .fold_map - .chars_at(DisplayPoint::new(display_point.row(), 0), app)?; + pub fn to_display_point(self, map: &DisplayMap, ctx: &AppContext) -> Result { + let mut display_point = map.fold_map.to_display_point(self, ctx); + let snapshot = map.fold_map.snapshot(ctx); + let chars = snapshot.chars_at(DisplayPoint::new(display_point.row(), 0), ctx)?; *display_point.column_mut() = expand_tabs(chars, display_point.column() as usize, map.tab_size) as u32; Ok(display_point) @@ -329,7 +346,7 @@ mod tests { App::test((), |app| { let text = sample_text(6, 6); let buffer = app.add_model(|ctx| Buffer::new(0, text, ctx)); - let map = app.add_model(|ctx| DisplayMap::new(buffer.clone(), 4, ctx)); + let map = DisplayMap::new(buffer.clone(), 4, app.as_ref()); buffer .update(app, |buffer, ctx| { buffer.edit( @@ -344,23 +361,25 @@ mod tests { }) .unwrap(); - let map = map.read(app); assert_eq!( - map.chars_at(DisplayPoint::new(1, 0), app.as_ref()) + map.snapshot(app.as_ref()) + .chars_at(DisplayPoint::new(1, 0), app.as_ref()) .unwrap() .take(10) .collect::(), " b bb" ); assert_eq!( - map.chars_at(DisplayPoint::new(1, 2), app.as_ref()) + map.snapshot(app.as_ref()) + .chars_at(DisplayPoint::new(1, 2), app.as_ref()) .unwrap() .take(10) .collect::(), " b bbbb" ); assert_eq!( - map.chars_at(DisplayPoint::new(1, 6), app.as_ref()) + map.snapshot(app.as_ref()) + .chars_at(DisplayPoint::new(1, 6), app.as_ref()) .unwrap() .take(13) .collect::(), @@ -396,11 +415,8 @@ mod tests { fn test_max_point() { App::test((), |app| { let buffer = app.add_model(|ctx| Buffer::new(0, "aaa\n\t\tbbb", ctx)); - let map = app.add_model(|ctx| DisplayMap::new(buffer.clone(), 4, ctx)); - assert_eq!( - map.read(app).max_point(app.as_ref()), - DisplayPoint::new(1, 11) - ) + let map = DisplayMap::new(buffer.clone(), 4, app.as_ref()); + assert_eq!(map.max_point(app.as_ref()), DisplayPoint::new(1, 11)) }); } } diff --git a/zed/src/editor/movement.rs b/zed/src/editor/movement.rs index f9ba5fe33d4189fef21a66f1284f2c5d9f133bb4..742218553822ebd36211b2f9a406984effa49116 100644 --- a/zed/src/editor/movement.rs +++ b/zed/src/editor/movement.rs @@ -96,7 +96,7 @@ pub fn prev_word_boundary( let mut boundary = DisplayPoint::new(point.row(), 0); let mut column = 0; let mut prev_c = None; - for c in map.chars_at(boundary, app)? { + for c in map.snapshot(app).chars_at(boundary, app)? { if column >= point.column() { break; } @@ -118,7 +118,7 @@ pub fn next_word_boundary( app: &AppContext, ) -> Result { let mut prev_c = None; - for c in map.chars_at(point, app)? { + for c in map.snapshot(app).chars_at(point, app)? { if prev_c.is_some() && (c == '\n' || char_kind(prev_c.unwrap()) != char_kind(c)) { break; } From e771e7078852c07547dc130ad8feb9287b098b83 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 5 May 2021 10:23:35 +0200 Subject: [PATCH 7/7] Add test for moving lines up and down --- zed/src/editor/buffer_view.rs | 94 +++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/zed/src/editor/buffer_view.rs b/zed/src/editor/buffer_view.rs index 65dacacae950ad8effa22959eb66fc5f030d7076..7d80c68217ac454d22684f582f4ab6b71ec3140d 100644 --- a/zed/src/editor/buffer_view.rs +++ b/zed/src/editor/buffer_view.rs @@ -3034,6 +3034,100 @@ mod tests { }); } + #[test] + fn test_move_line_up_down() { + App::test((), |app| { + let settings = settings::channel(&app.font_cache()).unwrap().1; + let buffer = app.add_model(|_| Buffer::new(0, sample_text(10, 5))); + let (_, view) = + app.add_window(|ctx| BufferView::for_buffer(buffer, None, settings, ctx)); + view.update(app, |view, ctx| { + view.fold_ranges( + vec![ + Point::new(0, 2)..Point::new(1, 2), + Point::new(2, 3)..Point::new(4, 1), + Point::new(7, 0)..Point::new(8, 4), + ], + ctx, + ); + view.select_display_ranges( + &[ + DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1), + DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1), + DisplayPoint::new(3, 2)..DisplayPoint::new(4, 2), + DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2), + ], + ctx, + ) + .unwrap(); + }); + assert_eq!( + view.read(app).text(app.as_ref()), + "aa…bbb\nccc…eeee\nfffff\nggggg\n…i\njjjjj" + ); + + view.update(app, |view, ctx| view.move_line_up(&(), ctx)); + assert_eq!( + view.read(app).text(app.as_ref()), + "aa…bbb\nccc…eeee\nggggg\n…i\njjjjj\nfffff" + ); + assert_eq!( + view.read(app).selection_ranges(app.as_ref()), + vec![ + DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1), + DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1), + DisplayPoint::new(2, 2)..DisplayPoint::new(3, 2), + DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2) + ] + ); + + view.update(app, |view, ctx| view.move_line_down(&(), ctx)); + assert_eq!( + view.read(app).text(app.as_ref()), + "ccc…eeee\naa…bbb\nfffff\nggggg\n…i\njjjjj" + ); + assert_eq!( + view.read(app).selection_ranges(app.as_ref()), + vec![ + DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1), + DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1), + DisplayPoint::new(3, 2)..DisplayPoint::new(4, 2), + DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2) + ] + ); + + view.update(app, |view, ctx| view.move_line_down(&(), ctx)); + assert_eq!( + view.read(app).text(app.as_ref()), + "ccc…eeee\nfffff\naa…bbb\nggggg\n…i\njjjjj" + ); + assert_eq!( + view.read(app).selection_ranges(app.as_ref()), + vec![ + DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1), + DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1), + DisplayPoint::new(3, 2)..DisplayPoint::new(4, 2), + DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2) + ] + ); + + view.update(app, |view, ctx| view.move_line_up(&(), ctx)); + assert_eq!( + view.read(app).text(app.as_ref()), + "ccc…eeee\naa…bbb\nggggg\n…i\njjjjj\nfffff" + ); + assert_eq!( + view.read(app).selection_ranges(app.as_ref()), + vec![ + DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1), + DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1), + DisplayPoint::new(2, 2)..DisplayPoint::new(3, 2), + DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2) + ] + ); + }); + } + #[test] fn test_clipboard() { App::test((), |app| {