From da78abd99fe41e120aed1f7685cf58a1de20395d Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Sun, 26 Feb 2023 14:17:18 -0800 Subject: [PATCH] Added DisplayRow abstraction to make folding code more readable --- crates/editor/src/display_map.rs | 119 ++++++++++++++++++++++--------- crates/editor/src/editor.rs | 6 +- crates/vim/src/normal.rs | 8 ++- 3 files changed, 95 insertions(+), 38 deletions(-) diff --git a/crates/editor/src/display_map.rs b/crates/editor/src/display_map.rs index 8f9decbcb3da4104c7b247919a81583330c0604b..936f4697c58557cb92601ae44fe9773ea2078b29 100644 --- a/crates/editor/src/display_map.rs +++ b/crates/editor/src/display_map.rs @@ -576,10 +576,10 @@ impl DisplaySnapshot { result } - pub fn line_indent(&self, display_row: u32) -> (u32, bool) { + pub fn line_indent(&self, display_row: DisplayRow) -> (u32, bool) { let mut indent = 0; let mut is_blank = true; - for (c, _) in self.chars_at(DisplayPoint::new(display_row, 0)) { + for (c, _) in self.chars_at(display_row.start(self)) { if c == ' ' { indent += 1; } else { @@ -599,7 +599,8 @@ impl DisplaySnapshot { } pub fn fold_for_line(self: &Self, display_row: u32) -> Option { - if self.is_line_foldable(display_row) { + let display_row_typed = DisplayRow::new(display_row); + if self.is_foldable(display_row_typed) { Some(FoldStatus::Foldable) } else if self.is_line_folded(display_row) { Some(FoldStatus::Folded) @@ -608,44 +609,43 @@ impl DisplaySnapshot { } } - pub fn is_line_foldable(self: &Self, display_row: u32) -> bool { - let max_point = self.max_point(); - if display_row >= max_point.row() { - false - } else { - let (start_indent, is_blank) = self.line_indent(display_row); + pub fn is_foldable(self: &Self, row: DisplayRow) -> bool { + if let Some(next_lines) = row.next_rows(self) { + let (start_indent, is_blank) = self.line_indent(row); if is_blank { - false - } else { - for display_row in display_row + 1..=max_point.row() { - let (indent, is_blank) = self.line_indent(display_row); - if !is_blank { - return indent > start_indent; - } + return false; + } + + for display_row in next_lines { + let (indent, is_blank) = self.line_indent(display_row); + if !is_blank { + return indent > start_indent; } - false } } + return false; } - pub fn foldable_range_for_line(self: &Self, start_row: u32) -> Option> { - if self.is_line_foldable(start_row) && !self.is_line_folded(start_row) { + pub fn foldable_range(self: &Self, row: DisplayRow) -> Option> { + let start = row.end(&self); + + if self.is_foldable(row) && !self.is_line_folded(start.row()) { + let (start_indent, _) = self.line_indent(row); let max_point = self.max_point(); - let (start_indent, _) = self.line_indent(start_row); - let start = DisplayPoint::new(start_row, self.line_len(start_row)); let mut end = None; - for row in start_row + 1..=max_point.row() { + + for row in row.next_rows(self).unwrap() { let (indent, is_blank) = self.line_indent(row); if !is_blank && indent <= start_indent { - end = Some(DisplayPoint::new(row - 1, self.line_len(row - 1))); + end = row.previous_row(self); break; } } - let end = end.unwrap_or(max_point); + let end = end.map(|end_row| end_row.end(self)).unwrap_or(max_point); Some(start.to_point(self)..end.to_point(self)) } else { - return None; + None } } @@ -665,19 +665,74 @@ pub struct DisplayPoint(BlockPoint); #[repr(transparent)] pub struct DisplayRow(pub u32); +// TODO: Move display_map check into new, and then remove it from everywhere else impl DisplayRow { pub fn new(display_row: u32) -> Self { DisplayRow(display_row) } - pub fn to_span(self, display_map: &DisplaySnapshot) -> Range { - let row_start = DisplayPoint::new(self.0, 0); - let row_end = DisplayPoint::new( - self.0, - display_map.buffer_snapshot.line_len(row_start.row()), - ); + pub fn to_line_span(&self, display_map: &DisplaySnapshot) -> Range { + self.start(display_map)..self.end(&display_map) + } + + pub fn previous_row(&self, _display_map: &DisplaySnapshot) -> Option { + self.0.checked_sub(1).map(|prev_row| DisplayRow(prev_row)) + } + + pub fn next_row(&self, display_map: &DisplaySnapshot) -> Option { + if self.0 + 1 > display_map.max_point().row() { + None + } else { + Some(DisplayRow(self.0 + 1)) + } + } + + // TODO: Remove and use next_row + fn next_unchecked(&self) -> Option { + Some(DisplayRow(self.0 + 1)) + } + + pub fn next_rows( + &self, + display_map: &DisplaySnapshot, + ) -> Option> { + self.next_row(display_map) + .and_then(|next_row| next_row.span_to(display_map.max_point())) + } + + pub fn span_to>( + &self, + end_row: I, + ) -> Option> { + let end_row = end_row.into(); + if self.0 <= end_row.0 { + let start = *self; + let mut current = None; + + Some(std::iter::from_fn(move || { + if current == None { + current = Some(start); + } else { + current = current.unwrap().next_unchecked(); + } + if current.unwrap().0 > end_row.0 { + None + } else { + current + } + })) + } else { + None + } + } + + // Force users to think about the display map when using this type + pub fn start(&self, _display_map: &DisplaySnapshot) -> DisplayPoint { + DisplayPoint::new(self.0, 0) + } - row_start..row_end + pub fn end(&self, display_map: &DisplaySnapshot) -> DisplayPoint { + DisplayPoint::new(self.0, display_map.line_len(self.0)) } } diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 6b86ec9afac403f5e5f1c700fb1601641171e49a..05075636aae1d24029d3332f91ca1b2449a048c7 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -5715,7 +5715,7 @@ impl Editor { let buffer_start_row = range.start.to_point(&display_map).row; for row in (0..=range.end.row()).rev() { - if let Some(fold_range) = display_map.foldable_range_for_line(row) { + if let Some(fold_range) = display_map.foldable_range(DisplayRow::new(row)) { if fold_range.end.row >= buffer_start_row { fold_ranges.push(fold_range); if row <= range.start.row() { @@ -5734,7 +5734,7 @@ impl Editor { let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); - if let Some(fold_range) = display_map.foldable_range_for_line(display_row.0) { + if let Some(fold_range) = display_map.foldable_range(display_row) { self.fold_ranges(std::iter::once(fold_range), true, cx); } } @@ -5762,7 +5762,7 @@ impl Editor { let display_range = fold_at .display_row - .to_span(&display_map) + .to_line_span(&display_map) .map_endpoints(|endpoint| endpoint.to_point(&display_map)); self.unfold_ranges(std::iter::once(display_range), true, true, cx) diff --git a/crates/vim/src/normal.rs b/crates/vim/src/normal.rs index 55134e21494bea5e840fa4f6067f26c8bd8b72d7..92ff9e5fb9808865da1f0377df0f2e69f0cfa159 100644 --- a/crates/vim/src/normal.rs +++ b/crates/vim/src/normal.rs @@ -12,7 +12,7 @@ use crate::{ }; use collections::{HashMap, HashSet}; use editor::{ - display_map::ToDisplayPoint, + display_map::{DisplayRow, ToDisplayPoint}, scroll::{autoscroll::Autoscroll, scroll_amount::ScrollAmount}, Anchor, Bias, ClipboardSelection, DisplayPoint, Editor, }; @@ -195,7 +195,7 @@ fn insert_line_above(_: &mut Workspace, _: &InsertLineAbove, cx: &mut ViewContex .map(|selection| selection.start.row()) .collect(); let edits = selection_start_rows.into_iter().map(|row| { - let (indent, _) = map.line_indent(row); + let (indent, _) = map.line_indent(DisplayRow::new(row)); let start_of_line = map .clip_point(DisplayPoint::new(row, 0), Bias::Left) .to_point(&map); @@ -216,6 +216,8 @@ fn insert_line_above(_: &mut Workspace, _: &InsertLineAbove, cx: &mut ViewContex }); } +// TODO: FIGURE OUT WHY PANIC WHEN CLICKING ON FOLDS + fn insert_line_below(_: &mut Workspace, _: &InsertLineBelow, cx: &mut ViewContext) { Vim::update(cx, |vim, cx| { vim.switch_mode(Mode::Insert, false, cx); @@ -227,7 +229,7 @@ fn insert_line_below(_: &mut Workspace, _: &InsertLineBelow, cx: &mut ViewContex .map(|selection| selection.end.row()) .collect(); let edits = selection_end_rows.into_iter().map(|row| { - let (indent, _) = map.line_indent(row); + let (indent, _) = map.line_indent(DisplayRow::new(row)); let end_of_line = map .clip_point(DisplayPoint::new(row, map.line_len(row)), Bias::Left) .to_point(&map);