diff --git a/crates/editor/src/display_map.rs b/crates/editor/src/display_map.rs index 936f4697c58557cb92601ae44fe9773ea2078b29..4aed3e5f5c30e64dd36d937f66ff49331ae0368a 100644 --- a/crates/editor/src/display_map.rs +++ b/crates/editor/src/display_map.rs @@ -579,7 +579,7 @@ impl DisplaySnapshot { 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(display_row.start(self)) { + for (c, _) in self.chars_at(display_row.start()) { if c == ' ' { indent += 1; } else { @@ -626,7 +626,7 @@ impl DisplaySnapshot { return false; } - pub fn foldable_range(self: &Self, row: DisplayRow) -> Option> { + 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()) { @@ -637,13 +637,13 @@ impl DisplaySnapshot { for row in row.next_rows(self).unwrap() { let (indent, is_blank) = self.line_indent(row); if !is_blank && indent <= start_indent { - end = row.previous_row(self); + end = row.previous_row(); break; } } let end = end.map(|end_row| end_row.end(self)).unwrap_or(max_point); - Some(start.to_point(self)..end.to_point(self)) + Some(start..end) } else { None } @@ -661,6 +661,81 @@ impl DisplaySnapshot { #[derive(Copy, Clone, Default, Eq, Ord, PartialOrd, PartialEq)] pub struct DisplayPoint(BlockPoint); +impl Debug for DisplayPoint { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_fmt(format_args!( + "DisplayPoint({}, {})", + self.row(), + self.column() + )) + } +} + +impl DisplayPoint { + pub fn new(row: u32, column: u32) -> Self { + Self(BlockPoint(Point::new(row, column))) + } + + pub fn zero() -> Self { + Self::new(0, 0) + } + + pub fn is_zero(&self) -> bool { + self.0.is_zero() + } + + pub fn row(self) -> u32 { + self.0.row + } + + pub fn column(self) -> u32 { + self.0.column + } + + pub fn row_mut(&mut self) -> &mut u32 { + &mut self.0.row + } + + pub fn column_mut(&mut self) -> &mut u32 { + &mut self.0.column + } + + pub fn to_point(self, map: &DisplaySnapshot) -> Point { + map.display_point_to_point(self, Bias::Left) + } + + pub fn to_offset(self, map: &DisplaySnapshot, bias: Bias) -> usize { + let unblocked_point = map.blocks_snapshot.to_wrap_point(self.0); + let unwrapped_point = map.wraps_snapshot.to_tab_point(unblocked_point); + let unexpanded_point = map.tabs_snapshot.to_fold_point(unwrapped_point, bias).0; + unexpanded_point.to_buffer_offset(&map.folds_snapshot) + } +} + +impl ToDisplayPoint for usize { + fn to_display_point(&self, map: &DisplaySnapshot) -> DisplayPoint { + map.point_to_display_point(self.to_point(&map.buffer_snapshot), Bias::Left) + } +} + +impl ToDisplayPoint for OffsetUtf16 { + fn to_display_point(&self, map: &DisplaySnapshot) -> DisplayPoint { + self.to_offset(&map.buffer_snapshot).to_display_point(map) + } +} + +impl ToDisplayPoint for Point { + fn to_display_point(&self, map: &DisplaySnapshot) -> DisplayPoint { + map.point_to_display_point(*self, Bias::Left) + } +} + +impl ToDisplayPoint for Anchor { + fn to_display_point(&self, map: &DisplaySnapshot) -> DisplayPoint { + self.to_point(&map.buffer_snapshot).to_display_point(map) + } +} + #[derive(Copy, Clone, Default, Eq, Ord, PartialOrd, PartialEq, Deserialize, Serialize)] #[repr(transparent)] pub struct DisplayRow(pub u32); @@ -672,10 +747,10 @@ impl DisplayRow { } pub fn to_line_span(&self, display_map: &DisplaySnapshot) -> Range { - self.start(display_map)..self.end(&display_map) + self.start()..self.end(&display_map) } - pub fn previous_row(&self, _display_map: &DisplaySnapshot) -> Option { + pub fn previous_row(&self) -> Option { self.0.checked_sub(1).map(|prev_row| DisplayRow(prev_row)) } @@ -727,7 +802,7 @@ impl DisplayRow { } // Force users to think about the display map when using this type - pub fn start(&self, _display_map: &DisplaySnapshot) -> DisplayPoint { + pub fn start(&self) -> DisplayPoint { DisplayPoint::new(self.0, 0) } @@ -742,81 +817,6 @@ impl From for DisplayRow { } } -impl Debug for DisplayPoint { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_fmt(format_args!( - "DisplayPoint({}, {})", - self.row(), - self.column() - )) - } -} - -impl DisplayPoint { - pub fn new(row: u32, column: u32) -> Self { - Self(BlockPoint(Point::new(row, column))) - } - - pub fn zero() -> Self { - Self::new(0, 0) - } - - pub fn is_zero(&self) -> bool { - self.0.is_zero() - } - - pub fn row(self) -> u32 { - self.0.row - } - - pub fn column(self) -> u32 { - self.0.column - } - - pub fn row_mut(&mut self) -> &mut u32 { - &mut self.0.row - } - - pub fn column_mut(&mut self) -> &mut u32 { - &mut self.0.column - } - - pub fn to_point(self, map: &DisplaySnapshot) -> Point { - map.display_point_to_point(self, Bias::Left) - } - - pub fn to_offset(self, map: &DisplaySnapshot, bias: Bias) -> usize { - let unblocked_point = map.blocks_snapshot.to_wrap_point(self.0); - let unwrapped_point = map.wraps_snapshot.to_tab_point(unblocked_point); - let unexpanded_point = map.tabs_snapshot.to_fold_point(unwrapped_point, bias).0; - unexpanded_point.to_buffer_offset(&map.folds_snapshot) - } -} - -impl ToDisplayPoint for usize { - fn to_display_point(&self, map: &DisplaySnapshot) -> DisplayPoint { - map.point_to_display_point(self.to_point(&map.buffer_snapshot), Bias::Left) - } -} - -impl ToDisplayPoint for OffsetUtf16 { - fn to_display_point(&self, map: &DisplaySnapshot) -> DisplayPoint { - self.to_offset(&map.buffer_snapshot).to_display_point(map) - } -} - -impl ToDisplayPoint for Point { - fn to_display_point(&self, map: &DisplaySnapshot) -> DisplayPoint { - map.point_to_display_point(*self, Bias::Left) - } -} - -impl ToDisplayPoint for Anchor { - fn to_display_point(&self, map: &DisplaySnapshot) -> DisplayPoint { - self.to_point(&map.buffer_snapshot).to_display_point(map) - } -} - #[cfg(test)] pub mod tests { use super::*; diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 05075636aae1d24029d3332f91ca1b2449a048c7..0586669624d4017835671f91d46d0c048ba32e1c 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -2686,47 +2686,51 @@ impl Editor { pub fn render_fold_indicators( &self, - fold_data: Vec<(u32, FoldStatus)>, - fold_indicators: &mut Vec<(u32, ElementBox)>, + fold_data: Option>, style: &EditorStyle, cx: &mut RenderContext, - ) { + ) -> Option> { enum FoldIndicators {} - for (fold_location, fold_status) in fold_data.iter() { - fold_indicators.push(( - *fold_location, - MouseEventHandler::::new( - *fold_location as usize, - cx, - |_, _| -> ElementBox { - Svg::new(match *fold_status { - FoldStatus::Folded => "icons/chevron_right_8.svg", - FoldStatus::Foldable => "icons/chevron_down_8.svg", + fold_data.map(|fold_data| { + fold_data + .iter() + .map(|(fold_location, fold_status)| { + ( + *fold_location, + MouseEventHandler::::new( + *fold_location as usize, + cx, + |_, _| -> ElementBox { + Svg::new(match *fold_status { + FoldStatus::Folded => "icons/chevron_right_8.svg", + FoldStatus::Foldable => "icons/chevron_down_8.svg", + }) + .with_color(style.folds.indicator) + .boxed() + }, + ) + .with_cursor_style(CursorStyle::PointingHand) + .with_padding(Padding::uniform(3.)) + .on_down(MouseButton::Left, { + let fold_location = *fold_location; + let fold_status = *fold_status; + move |_, cx| { + cx.dispatch_any_action(match fold_status { + FoldStatus::Folded => Box::new(UnfoldAt { + display_row: DisplayRow::new(fold_location), + }), + FoldStatus::Foldable => Box::new(FoldAt { + display_row: DisplayRow::new(fold_location), + }), + }); + } }) - .with_color(style.folds.indicator) - .boxed() - }, - ) - .with_cursor_style(CursorStyle::PointingHand) - .with_padding(Padding::uniform(3.)) - .on_down(MouseButton::Left, { - let fold_location = *fold_location; - let fold_status = *fold_status; - move |_, cx| { - cx.dispatch_any_action(match fold_status { - FoldStatus::Folded => Box::new(UnfoldAt { - display_row: DisplayRow::new(fold_location), - }), - FoldStatus::Foldable => Box::new(FoldAt { - display_row: DisplayRow::new(fold_location), - }), - }); - } + .boxed(), + ) }) - .boxed(), - )) - } + .collect() + }) } pub fn context_menu_visible(&self) -> bool { @@ -5715,7 +5719,13 @@ 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(DisplayRow::new(row)) { + let fold_range = display_map + .foldable_range(DisplayRow::new(row)) + .map(|range| { + range.map_range(|display_point| display_point.to_point(&display_map)) + }); + + if let Some(fold_range) = fold_range { if fold_range.end.row >= buffer_start_row { fold_ranges.push(fold_range); if row <= range.start.row() { @@ -5735,10 +5745,29 @@ impl Editor { let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); if let Some(fold_range) = display_map.foldable_range(display_row) { - self.fold_ranges(std::iter::once(fold_range), true, cx); + let autoscroll = self.selections_intersect(&fold_range, &display_map, cx); + + let point_range = + fold_range.map_range(|display_point| display_point.to_point(&display_map)); + + self.fold_ranges(std::iter::once(point_range), autoscroll, cx); } } + fn selections_intersect( + &mut self, + range: &Range, + display_map: &DisplaySnapshot, + cx: &mut ViewContext, + ) -> bool { + let selections = self.selections.all::(cx); + + selections.iter().any(|selection| { + let display_range = selection.display_range(display_map); + range.contains(&display_range.start) || range.contains(&display_range.end) + }) + } + pub fn unfold_lines(&mut self, _: &UnfoldLines, cx: &mut ViewContext) { let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); let buffer = &display_map.buffer_snapshot; @@ -5760,12 +5789,13 @@ impl Editor { pub fn unfold_at(&mut self, fold_at: &UnfoldAt, cx: &mut ViewContext) { let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); - let display_range = fold_at - .display_row - .to_line_span(&display_map) - .map_endpoints(|endpoint| endpoint.to_point(&display_map)); + let unfold_range = fold_at.display_row.to_line_span(&display_map); + + let autoscroll = self.selections_intersect(&unfold_range, &display_map, cx); + + let unfold_range = unfold_range.map_range(|endpoint| endpoint.to_point(&display_map)); - self.unfold_ranges(std::iter::once(display_range), true, true, cx) + self.unfold_ranges(std::iter::once(unfold_range), true, autoscroll, cx) } pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext) { diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 68f136bddb7a89beea69fb95dec51c4192f3b64f..5b1e1273c6ff61563bf5cea9ad73de763a4119b9 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -48,6 +48,7 @@ use std::{ ops::{DerefMut, Range}, sync::Arc, }; +use workspace::item::Item; struct SelectionLayout { head: DisplayPoint, @@ -576,15 +577,18 @@ impl EditorElement { indicator.paint(bounds.origin() + vec2f(x, y), visible_bounds, cx); } - for (line, fold_indicator) in layout.fold_indicators.iter_mut() { - let mut x = bounds.width() - layout.gutter_padding; - let mut y = *line as f32 * line_height - scroll_top; + layout.fold_indicators.as_mut().map(|fold_indicators| { + for (line, fold_indicator) in fold_indicators.iter_mut() { + let mut x = bounds.width() - layout.gutter_padding; + let mut y = *line as f32 * line_height - scroll_top; - x += ((layout.gutter_padding + layout.gutter_margin) - fold_indicator.size().x()) / 2.; - y += (line_height - fold_indicator.size().y()) / 2.; + x += ((layout.gutter_padding + layout.gutter_margin) - fold_indicator.size().x()) + / 2.; + y += (line_height - fold_indicator.size().y()) / 2.; - fold_indicator.paint(bounds.origin() + vec2f(x, y), visible_bounds, cx); - } + fold_indicator.paint(bounds.origin() + vec2f(x, y), visible_bounds, cx); + } + }); } fn paint_diff_hunks(bounds: RectF, layout: &mut LayoutState, cx: &mut PaintContext) { @@ -1130,17 +1134,20 @@ impl EditorElement { fn get_fold_indicators( &self, + is_singleton: bool, display_rows: Range, snapshot: &EditorSnapshot, - ) -> Vec<(u32, FoldStatus)> { - display_rows - .into_iter() - .filter_map(|display_row| { - snapshot - .fold_for_line(display_row) - .map(|fold_status| (display_row, fold_status)) - }) - .collect() + ) -> Option> { + is_singleton.then(|| { + display_rows + .into_iter() + .filter_map(|display_row| { + snapshot + .fold_for_line(display_row) + .map(|fold_status| (display_row, fold_status)) + }) + .collect() + }) } //Folds contained in a hunk are ignored apart from shrinking visual size @@ -1633,7 +1640,10 @@ impl Element for EditorElement { let mut highlighted_ranges = Vec::new(); let mut show_scrollbars = false; let mut include_root = false; + let mut is_singleton = false; self.update_view(cx.app, |view, cx| { + is_singleton = view.is_singleton(cx); + let display_map = view.display_map.update(cx, |map, cx| map.snapshot(cx)); highlighted_rows = view.highlighted_rows(); @@ -1714,7 +1724,7 @@ impl Element for EditorElement { let display_hunks = self.layout_git_gutters(start_row..end_row, &snapshot); - let folds = self.get_fold_indicators(start_row..end_row, &snapshot); + let folds = self.get_fold_indicators(is_singleton, start_row..end_row, &snapshot); let scrollbar_row_range = scroll_position.y()..(scroll_position.y() + height_in_lines); @@ -1778,12 +1788,11 @@ impl Element for EditorElement { } }); - let mut fold_indicators = Vec::with_capacity(folds.len()); let mut context_menu = None; let mut code_actions_indicator = None; let mut hover = None; let mut mode = EditorMode::Full; - cx.render(&self.view.upgrade(cx).unwrap(), |view, cx| { + let mut fold_indicators = cx.render(&self.view.upgrade(cx).unwrap(), |view, cx| { let newest_selection_head = view .selections .newest::(cx) @@ -1802,11 +1811,11 @@ impl Element for EditorElement { .map(|indicator| (newest_selection_head.row(), indicator)); } - view.render_fold_indicators(folds, &mut fold_indicators, &style, cx); - let visible_rows = start_row..start_row + line_layouts.len() as u32; hover = view.hover_state.render(&snapshot, &style, visible_rows, cx); mode = view.mode; + + view.render_fold_indicators(folds, &style, cx) }); if let Some((_, context_menu)) = context_menu.as_mut() { @@ -1832,15 +1841,17 @@ impl Element for EditorElement { ); } - for (_, indicator) in fold_indicators.iter_mut() { - indicator.layout( - SizeConstraint::strict_along( - Axis::Vertical, - line_height * style.code_actions.vertical_scale, - ), - cx, - ); - } + fold_indicators.as_mut().map(|fold_indicators| { + for (_, indicator) in fold_indicators.iter_mut() { + indicator.layout( + SizeConstraint::strict_along( + Axis::Vertical, + line_height * style.code_actions.vertical_scale, + ), + cx, + ); + } + }); if let Some((_, hover_popovers)) = hover.as_mut() { for hover_popover in hover_popovers.iter_mut() { @@ -2020,7 +2031,7 @@ pub struct LayoutState { context_menu: Option<(DisplayPoint, ElementBox)>, code_actions_indicator: Option<(u32, ElementBox)>, hover_popovers: Option<(DisplayPoint, Vec)>, - fold_indicators: Vec<(u32, ElementBox)>, + fold_indicators: Option>, } pub struct PositionMap { diff --git a/crates/util/src/util.rs b/crates/util/src/util.rs index 339cc53bf81f9a23939a31b3c455a91ce5ffa689..c0f08ac32e578159e7906955193685de88609988 100644 --- a/crates/util/src/util.rs +++ b/crates/util/src/util.rs @@ -146,14 +146,14 @@ where } pub trait MapRangeEndsExt { - fn map_endpoints(self, f: F) -> Range + fn map_range(self, f: F) -> Range where Self: Sized, F: Fn(R) -> T; } impl MapRangeEndsExt for Range { - fn map_endpoints(self, f: F) -> Range + fn map_range(self, f: F) -> Range where Self: Sized, F: Fn(R) -> T,