diff --git a/crates/editor2/src/display_map.rs b/crates/editor2/src/display_map.rs index d88daaccc1..e64d5e301c 100644 --- a/crates/editor2/src/display_map.rs +++ b/crates/editor2/src/display_map.rs @@ -31,7 +31,7 @@ pub use block_map::{ BlockDisposition, BlockId, BlockProperties, BlockStyle, RenderBlock, TransformBlock, }; -pub use self::fold_map::FoldPoint; +pub use self::fold_map::{Fold, FoldPoint}; pub use self::inlay_map::{Inlay, InlayOffset, InlayPoint}; #[derive(Copy, Clone, Debug, PartialEq, Eq)] @@ -124,7 +124,7 @@ impl DisplayMap { self.fold( other .folds_in_range(0..other.buffer_snapshot.len()) - .map(|fold| fold.to_offset(&other.buffer_snapshot)), + .map(|fold| fold.range.to_offset(&other.buffer_snapshot)), cx, ); } @@ -723,7 +723,7 @@ impl DisplaySnapshot { DisplayPoint(point) } - pub fn folds_in_range(&self, range: Range) -> impl Iterator> + pub fn folds_in_range(&self, range: Range) -> impl Iterator where T: ToOffset, { diff --git a/crates/editor2/src/display_map/fold_map.rs b/crates/editor2/src/display_map/fold_map.rs index 88cd202b08..4dad2d52ae 100644 --- a/crates/editor2/src/display_map/fold_map.rs +++ b/crates/editor2/src/display_map/fold_map.rs @@ -3,15 +3,16 @@ use super::{ Highlights, }; use crate::{Anchor, AnchorRangeExt, MultiBufferSnapshot, ToOffset}; -use gpui::{HighlightStyle, Hsla}; +use gpui::{ElementId, HighlightStyle, Hsla}; use language::{Chunk, Edit, Point, TextSummary}; use std::{ any::TypeId, cmp::{self, Ordering}, iter, - ops::{Add, AddAssign, Range, Sub}, + ops::{Add, AddAssign, Deref, DerefMut, Range, Sub}, }; use sum_tree::{Bias, Cursor, FilterCursor, SumTree}; +use util::post_inc; #[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)] pub struct FoldPoint(pub Point); @@ -90,12 +91,16 @@ impl<'a> FoldMapWriter<'a> { } // For now, ignore any ranges that span an excerpt boundary. - let fold = Fold(buffer.anchor_after(range.start)..buffer.anchor_before(range.end)); - if fold.0.start.excerpt_id != fold.0.end.excerpt_id { + let fold_range = + FoldRange(buffer.anchor_after(range.start)..buffer.anchor_before(range.end)); + if fold_range.0.start.excerpt_id != fold_range.0.end.excerpt_id { continue; } - folds.push(fold); + folds.push(Fold { + id: FoldId(post_inc(&mut self.0.next_fold_id.0)), + range: fold_range, + }); let inlay_range = snapshot.to_inlay_offset(range.start)..snapshot.to_inlay_offset(range.end); @@ -106,13 +111,13 @@ impl<'a> FoldMapWriter<'a> { } let buffer = &snapshot.buffer; - folds.sort_unstable_by(|a, b| sum_tree::SeekTarget::cmp(a, b, buffer)); + folds.sort_unstable_by(|a, b| sum_tree::SeekTarget::cmp(&a.range, &b.range, buffer)); self.0.snapshot.folds = { let mut new_tree = SumTree::new(); - let mut cursor = self.0.snapshot.folds.cursor::(); + let mut cursor = self.0.snapshot.folds.cursor::(); for fold in folds { - new_tree.append(cursor.slice(&fold, Bias::Right, buffer), buffer); + new_tree.append(cursor.slice(&fold.range, Bias::Right, buffer), buffer); new_tree.push(fold, buffer); } new_tree.append(cursor.suffix(buffer), buffer); @@ -138,7 +143,8 @@ impl<'a> FoldMapWriter<'a> { let mut folds_cursor = intersecting_folds(&snapshot, &self.0.snapshot.folds, range, inclusive); while let Some(fold) = folds_cursor.item() { - let offset_range = fold.0.start.to_offset(buffer)..fold.0.end.to_offset(buffer); + let offset_range = + fold.range.start.to_offset(buffer)..fold.range.end.to_offset(buffer); if offset_range.end > offset_range.start { let inlay_range = snapshot.to_inlay_offset(offset_range.start) ..snapshot.to_inlay_offset(offset_range.end); @@ -175,6 +181,7 @@ impl<'a> FoldMapWriter<'a> { pub struct FoldMap { snapshot: FoldSnapshot, ellipses_color: Option, + next_fold_id: FoldId, } impl FoldMap { @@ -197,6 +204,7 @@ impl FoldMap { ellipses_color: None, }, ellipses_color: None, + next_fold_id: FoldId::default(), }; let snapshot = this.snapshot.clone(); (this, snapshot) @@ -242,8 +250,8 @@ impl FoldMap { while let Some(fold) = folds.next() { if let Some(next_fold) = folds.peek() { let comparison = fold - .0 - .cmp(&next_fold.0, &self.snapshot.inlay_snapshot.buffer); + .range + .cmp(&next_fold.range, &self.snapshot.inlay_snapshot.buffer); assert!(comparison.is_le()); } } @@ -304,9 +312,9 @@ impl FoldMap { let anchor = inlay_snapshot .buffer .anchor_before(inlay_snapshot.to_buffer_offset(edit.new.start)); - let mut folds_cursor = self.snapshot.folds.cursor::(); + let mut folds_cursor = self.snapshot.folds.cursor::(); folds_cursor.seek( - &Fold(anchor..Anchor::max()), + &FoldRange(anchor..Anchor::max()), Bias::Left, &inlay_snapshot.buffer, ); @@ -315,8 +323,8 @@ impl FoldMap { let inlay_snapshot = &inlay_snapshot; move || { let item = folds_cursor.item().map(|f| { - let buffer_start = f.0.start.to_offset(&inlay_snapshot.buffer); - let buffer_end = f.0.end.to_offset(&inlay_snapshot.buffer); + let buffer_start = f.range.start.to_offset(&inlay_snapshot.buffer); + let buffer_end = f.range.end.to_offset(&inlay_snapshot.buffer); inlay_snapshot.to_inlay_offset(buffer_start) ..inlay_snapshot.to_inlay_offset(buffer_end) }); @@ -596,13 +604,13 @@ impl FoldSnapshot { self.transforms.summary().output.longest_row } - pub fn folds_in_range(&self, range: Range) -> impl Iterator> + pub fn folds_in_range(&self, range: Range) -> impl Iterator where T: ToOffset, { let mut folds = intersecting_folds(&self.inlay_snapshot, &self.folds, range, false); iter::from_fn(move || { - let item = folds.item().map(|f| &f.0); + let item = folds.item(); folds.next(&self.inlay_snapshot.buffer); item }) @@ -830,10 +838,39 @@ impl sum_tree::Summary for TransformSummary { } } -#[derive(Clone, Debug)] -struct Fold(Range); +#[derive(Copy, Clone, Eq, PartialEq, Debug, Default)] +pub struct FoldId(usize); -impl Default for Fold { +impl Into for FoldId { + fn into(self) -> ElementId { + ElementId::Integer(self.0) + } +} + +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct Fold { + pub id: FoldId, + pub range: FoldRange, +} + +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct FoldRange(Range); + +impl Deref for FoldRange { + type Target = Range; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for FoldRange { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl Default for FoldRange { fn default() -> Self { Self(Anchor::min()..Anchor::max()) } @@ -844,17 +881,17 @@ impl sum_tree::Item for Fold { fn summary(&self) -> Self::Summary { FoldSummary { - start: self.0.start.clone(), - end: self.0.end.clone(), - min_start: self.0.start.clone(), - max_end: self.0.end.clone(), + start: self.range.start.clone(), + end: self.range.end.clone(), + min_start: self.range.start.clone(), + max_end: self.range.end.clone(), count: 1, } } } #[derive(Clone, Debug)] -struct FoldSummary { +pub struct FoldSummary { start: Anchor, end: Anchor, min_start: Anchor, @@ -900,14 +937,14 @@ impl sum_tree::Summary for FoldSummary { } } -impl<'a> sum_tree::Dimension<'a, FoldSummary> for Fold { +impl<'a> sum_tree::Dimension<'a, FoldSummary> for FoldRange { fn add_summary(&mut self, summary: &'a FoldSummary, _: &MultiBufferSnapshot) { self.0.start = summary.start.clone(); self.0.end = summary.end.clone(); } } -impl<'a> sum_tree::SeekTarget<'a, FoldSummary, Fold> for Fold { +impl<'a> sum_tree::SeekTarget<'a, FoldSummary, FoldRange> for FoldRange { fn cmp(&self, other: &Self, buffer: &MultiBufferSnapshot) -> Ordering { self.0.cmp(&other.0, buffer) } @@ -1321,7 +1358,10 @@ mod tests { let (snapshot, _) = map.read(inlay_snapshot.clone(), vec![]); let fold_ranges = snapshot .folds_in_range(Point::new(1, 0)..Point::new(1, 3)) - .map(|fold| fold.start.to_point(&buffer_snapshot)..fold.end.to_point(&buffer_snapshot)) + .map(|fold| { + fold.range.start.to_point(&buffer_snapshot) + ..fold.range.end.to_point(&buffer_snapshot) + }) .collect::>(); assert_eq!( fold_ranges, @@ -1553,10 +1593,9 @@ mod tests { .filter(|fold| { let start = buffer_snapshot.anchor_before(start); let end = buffer_snapshot.anchor_after(end); - start.cmp(&fold.0.end, &buffer_snapshot) == Ordering::Less - && end.cmp(&fold.0.start, &buffer_snapshot) == Ordering::Greater + start.cmp(&fold.range.end, &buffer_snapshot) == Ordering::Less + && end.cmp(&fold.range.start, &buffer_snapshot) == Ordering::Greater }) - .map(|fold| fold.0) .collect::>(); assert_eq!( @@ -1639,10 +1678,10 @@ mod tests { let buffer = &inlay_snapshot.buffer; let mut folds = self.snapshot.folds.items(buffer); // Ensure sorting doesn't change how folds get merged and displayed. - folds.sort_by(|a, b| a.0.cmp(&b.0, buffer)); + folds.sort_by(|a, b| a.range.cmp(&b.range, buffer)); let mut fold_ranges = folds .iter() - .map(|fold| fold.0.start.to_offset(buffer)..fold.0.end.to_offset(buffer)) + .map(|fold| fold.range.start.to_offset(buffer)..fold.range.end.to_offset(buffer)) .peekable(); let mut merged_ranges = Vec::new(); diff --git a/crates/editor2/src/editor.rs b/crates/editor2/src/editor.rs index e3d9440933..af7da8e837 100644 --- a/crates/editor2/src/editor.rs +++ b/crates/editor2/src/editor.rs @@ -39,12 +39,12 @@ use futures::FutureExt; use fuzzy::{StringMatch, StringMatchCandidate}; use git::diff_hunk_to_display; use gpui::{ - action, actions, div, point, prelude::*, px, relative, rems, render_view, size, uniform_list, - AnyElement, AppContext, AsyncWindowContext, BackgroundExecutor, Bounds, ClipboardItem, - Component, Context, EventEmitter, FocusHandle, FontFeatures, FontStyle, FontWeight, - HighlightStyle, Hsla, InputHandler, KeyContext, Model, MouseButton, ParentComponent, Pixels, - Render, Styled, Subscription, Task, TextStyle, UniformListScrollHandle, View, ViewContext, - VisualContext, WeakView, WindowContext, + action, actions, div, point, prelude::*, px, relative, rems, size, uniform_list, AnyElement, + AppContext, AsyncWindowContext, BackgroundExecutor, Bounds, ClipboardItem, Component, Context, + EventEmitter, FocusHandle, FontFeatures, FontStyle, FontWeight, HighlightStyle, Hsla, + InputHandler, KeyContext, Model, MouseButton, ParentComponent, Pixels, Render, Styled, + Subscription, Task, TextStyle, UniformListScrollHandle, View, ViewContext, VisualContext, + WeakView, WindowContext, }; use highlight_matching_bracket::refresh_matching_bracket_highlights; use hover_popover::{hide_hover, HoverState}; @@ -4372,69 +4372,42 @@ impl Editor { } } - // pub fn render_fold_indicators( - // &self, - // fold_data: Vec>, - // style: &EditorStyle, - // gutter_hovered: bool, - // line_height: f32, - // gutter_margin: f32, - // cx: &mut ViewContext, - // ) -> Vec>> { - // enum FoldIndicators {} - - // let style = style.folds.clone(); - - // fold_data - // .iter() - // .enumerate() - // .map(|(ix, fold_data)| { - // fold_data - // .map(|(fold_status, buffer_row, active)| { - // (active || gutter_hovered || fold_status == FoldStatus::Folded).then(|| { - // MouseEventHandler::new::( - // ix as usize, - // cx, - // |mouse_state, _| { - // Svg::new(match fold_status { - // FoldStatus::Folded => style.folded_icon.clone(), - // FoldStatus::Foldable => style.foldable_icon.clone(), - // }) - // .with_color( - // style - // .indicator - // .in_state(fold_status == FoldStatus::Folded) - // .style_for(mouse_state) - // .color, - // ) - // .constrained() - // .with_width(gutter_margin * style.icon_margin_scale) - // .aligned() - // .constrained() - // .with_height(line_height) - // .with_width(gutter_margin) - // .aligned() - // }, - // ) - // .with_cursor_style(CursorStyle::PointingHand) - // .with_padding(Padding::uniform(3.)) - // .on_click(MouseButton::Left, { - // move |_, editor, cx| match fold_status { - // FoldStatus::Folded => { - // editor.unfold_at(&UnfoldAt { buffer_row }, cx); - // } - // FoldStatus::Foldable => { - // editor.fold_at(&FoldAt { buffer_row }, cx); - // } - // } - // }) - // .into_any() - // }) - // }) - // .flatten() - // }) - // .collect() - // } + pub fn render_fold_indicators( + &self, + fold_data: Vec>, + style: &EditorStyle, + gutter_hovered: bool, + line_height: Pixels, + gutter_margin: Pixels, + cx: &mut ViewContext, + ) -> Vec>> { + fold_data + .iter() + .enumerate() + .map(|(ix, fold_data)| { + fold_data + .map(|(fold_status, buffer_row, active)| { + (active || gutter_hovered || fold_status == FoldStatus::Folded).then(|| { + let icon = match fold_status { + FoldStatus::Folded => ui::Icon::ChevronRight, + FoldStatus::Foldable => ui::Icon::ChevronDown, + }; + IconButton::new(ix as usize, icon) + .on_click(move |editor: &mut Editor, cx| match fold_status { + FoldStatus::Folded => { + editor.unfold_at(&UnfoldAt { buffer_row }, cx); + } + FoldStatus::Foldable => { + editor.fold_at(&FoldAt { buffer_row }, cx); + } + }) + .render() + }) + }) + .flatten() + }) + .collect() + } pub fn context_menu_visible(&self) -> bool { self.context_menu @@ -5330,8 +5303,8 @@ impl Editor { buffer.anchor_before(range_to_move.start) ..buffer.anchor_after(range_to_move.end), ) { - let mut start = fold.start.to_point(&buffer); - let mut end = fold.end.to_point(&buffer); + let mut start = fold.range.start.to_point(&buffer); + let mut end = fold.range.end.to_point(&buffer); start.row -= row_delta; end.row -= row_delta; refold_ranges.push(start..end); @@ -5421,8 +5394,8 @@ impl Editor { buffer.anchor_before(range_to_move.start) ..buffer.anchor_after(range_to_move.end), ) { - let mut start = fold.start.to_point(&buffer); - let mut end = fold.end.to_point(&buffer); + let mut start = fold.range.start.to_point(&buffer); + let mut end = fold.range.end.to_point(&buffer); start.row += row_delta; end.row += row_delta; refold_ranges.push(start..end); @@ -7804,25 +7777,18 @@ impl Editor { } div() .pl(cx.anchor_x) - .child(render_view( + .child(rename_editor.render_with(EditorElement::new( &rename_editor, - EditorElement::new( - &rename_editor, - EditorStyle { - background: cx.theme().system().transparent, - local_player: cx.editor_style.local_player, - text: text_style, - scrollbar_width: cx - .editor_style - .scrollbar_width, - syntax: cx.editor_style.syntax.clone(), - diagnostic_style: cx - .editor_style - .diagnostic_style - .clone(), - }, - ), - )) + EditorStyle { + background: cx.theme().system().transparent, + local_player: cx.editor_style.local_player, + text: text_style, + scrollbar_width: cx.editor_style.scrollbar_width, + syntax: cx.editor_style.syntax.clone(), + diagnostic_style: + cx.editor_style.diagnostic_style.clone(), + }, + ))) .render() } }), diff --git a/crates/editor2/src/element.rs b/crates/editor2/src/element.rs index d11408c0ce..4d9a516f2b 100644 --- a/crates/editor2/src/element.rs +++ b/crates/editor2/src/element.rs @@ -18,11 +18,12 @@ use crate::{ use anyhow::Result; use collections::{BTreeMap, HashMap}; use gpui::{ - point, px, relative, size, transparent_black, Action, AnyElement, AvailableSpace, BorrowWindow, - Bounds, Component, ContentMask, Corners, DispatchPhase, Edges, Element, ElementId, - ElementInputHandler, Entity, EntityId, Hsla, Line, MouseButton, MouseDownEvent, MouseMoveEvent, - MouseUpEvent, ParentComponent, Pixels, ScrollWheelEvent, Size, Style, Styled, TextRun, - TextStyle, View, ViewContext, WindowContext, + div, point, px, relative, size, transparent_black, Action, AnyElement, AvailableSpace, + BorrowWindow, Bounds, Component, ContentMask, Corners, DispatchPhase, Edges, Element, + ElementId, ElementInputHandler, Entity, EntityId, Hsla, InteractiveComponent, Line, + MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, ParentComponent, Pixels, + ScrollWheelEvent, Size, StatefulInteractiveComponent, Style, Styled, TextRun, TextStyle, View, + ViewContext, WindowContext, }; use itertools::Itertools; use language::language_settings::ShowWhitespaceSetting; @@ -487,23 +488,26 @@ impl EditorElement { } } - // todo!("fold indicators") - // for (ix, fold_indicator) in layout.fold_indicators.iter_mut().enumerate() { - // if let Some(indicator) = fold_indicator.as_mut() { - // let position = point( - // bounds.width() - layout.gutter_padding, - // ix as f32 * line_height - (scroll_top % line_height), - // ); - // let centering_offset = point( - // (layout.gutter_padding + layout.gutter_margin - indicator.size().x) / 2., - // (line_height - indicator.size().y) / 2., - // ); + for (ix, fold_indicator) in layout.fold_indicators.iter_mut().enumerate() { + if let Some(fold_indicator) = fold_indicator.as_mut() { + let available_space = size( + AvailableSpace::MinContent, + AvailableSpace::Definite(line_height * 0.55), + ); + let fold_indicator_size = fold_indicator.measure(available_space, editor, cx); - // let indicator_origin = bounds.origin + position + centering_offset; - - // indicator.paint(indicator_origin, visible_bounds, editor, cx); - // } - // } + let position = point( + bounds.size.width - layout.gutter_padding, + ix as f32 * line_height - (scroll_top % line_height), + ); + let centering_offset = point( + (layout.gutter_padding + layout.gutter_margin - fold_indicator_size.width) / 2., + (line_height - fold_indicator_size.height) / 2., + ); + let origin = bounds.origin + position + centering_offset; + fold_indicator.draw(origin, available_space, editor, cx); + } + } if let Some(indicator) = layout.code_actions_indicator.as_mut() { let available_space = size( @@ -612,311 +616,341 @@ impl EditorElement { fn paint_text( &mut self, - bounds: Bounds, + text_bounds: Bounds, layout: &mut LayoutState, editor: &mut Editor, cx: &mut ViewContext, ) { let scroll_position = layout.position_map.snapshot.scroll_position(); let start_row = layout.visible_display_row_range.start; - let scroll_top = scroll_position.y * layout.position_map.line_height; - let max_glyph_width = layout.position_map.em_width; - let scroll_left = scroll_position.x * max_glyph_width; - let content_origin = bounds.origin + point(layout.gutter_margin, Pixels::ZERO); + let content_origin = text_bounds.origin + point(layout.gutter_margin, Pixels::ZERO); let line_end_overshoot = 0.15 * layout.position_map.line_height; let whitespace_setting = editor.buffer.read(cx).settings_at(0, cx).show_whitespaces; - cx.with_content_mask(Some(ContentMask { bounds }), |cx| { - // todo!("cursor region") - // cx.scene().push_cursor_region(CursorRegion { - // bounds, - // style: if !editor.link_go_to_definition_state.definitions.is_empty { - // CursorStyle::PointingHand - // } else { - // CursorStyle::IBeam - // }, - // }); + cx.with_content_mask( + Some(ContentMask { + bounds: text_bounds, + }), + |cx| { + // todo!("cursor region") + // cx.scene().push_cursor_region(CursorRegion { + // bounds, + // style: if !editor.link_go_to_definition_state.definitions.is_empty { + // CursorStyle::PointingHand + // } else { + // CursorStyle::IBeam + // }, + // }); - // todo!("fold ranges") - // let fold_corner_radius = - // self.style.folds.ellipses.corner_radius_factor * layout.position_map.line_height; - // for (id, range, color) in layout.fold_ranges.iter() { - // self.paint_highlighted_range( - // range.clone(), - // *color, - // fold_corner_radius, - // fold_corner_radius * 2., - // layout, - // content_origin, - // scroll_top, - // scroll_left, - // bounds, - // cx, - // ); + let fold_corner_radius = 0.15 * layout.position_map.line_height; + cx.with_element_id(Some("folds"), |cx| { + let snapshot = &layout.position_map.snapshot; + for fold in snapshot.folds_in_range(layout.visible_anchor_range.clone()) { + let fold_range = fold.range.clone(); + let display_range = fold.range.start.to_display_point(&snapshot) + ..fold.range.end.to_display_point(&snapshot); + debug_assert_eq!(display_range.start.row(), display_range.end.row()); + let row = display_range.start.row(); - // for bound in range_to_bounds( - // &range, - // content_origin, - // scroll_left, - // scroll_top, - // &layout.visible_display_row_range, - // line_end_overshoot, - // &layout.position_map, - // ) { - // cx.scene().push_cursor_region(CursorRegion { - // bounds: bound, - // style: CursorStyle::PointingHand, - // }); + let line_layout = &layout.position_map.line_layouts + [(row - layout.visible_display_row_range.start) as usize] + .line; + let start_x = content_origin.x + + line_layout.x_for_index(display_range.start.column() as usize) + - layout.position_map.scroll_position.x; + let start_y = content_origin.y + + row as f32 * layout.position_map.line_height + - layout.position_map.scroll_position.y; + let end_x = content_origin.x + + line_layout.x_for_index(display_range.end.column() as usize) + - layout.position_map.scroll_position.x; - // let display_row = range.start.row(); + let fold_bounds = Bounds { + origin: point(start_x, start_y), + size: size(end_x - start_x, layout.position_map.line_height), + }; - // let buffer_row = DisplayPoint::new(display_row, 0) - // .to_point(&layout.position_map.snapshot.display_snapshot) - // .row; + let fold_background = cx.with_z_index(1, |cx| { + div() + .id(fold.id) + .size_full() + .on_mouse_down(MouseButton::Left, |_, _, cx| cx.stop_propagation()) + .on_click(move |editor: &mut Editor, _, cx| { + editor.unfold_ranges( + [fold_range.start..fold_range.end], + true, + false, + cx, + ); + cx.stop_propagation(); + }) + .draw( + fold_bounds.origin, + fold_bounds.size, + editor, + cx, + |fold_element_state, cx| { + if fold_element_state.is_active() { + gpui::blue() + } else if fold_bounds.contains_point(&cx.mouse_position()) { + gpui::black() + } else { + gpui::red() + } + }, + ) + }); - // let view_id = cx.view_id(); - // cx.scene().push_mouse_region( - // MouseRegion::new::(view_id, *id as usize, bound) - // .on_click(MouseButton::Left, move |_, editor: &mut Editor, cx| { - // editor.unfold_at(&UnfoldAt { buffer_row }, cx) - // }) - // .with_notify_on_hover(true) - // .with_notify_on_click(true), - // ) - // } - // } + self.paint_highlighted_range( + display_range.clone(), + fold_background, + fold_corner_radius, + fold_corner_radius * 2., + layout, + content_origin, + text_bounds, + cx, + ); + } + }); - for (range, color) in &layout.highlighted_ranges { - self.paint_highlighted_range( - range.clone(), - *color, - Pixels::ZERO, - line_end_overshoot, - layout, - content_origin, - scroll_top, - scroll_left, - bounds, - cx, - ); - } - - let mut cursors = SmallVec::<[Cursor; 32]>::new(); - let corner_radius = 0.15 * layout.position_map.line_height; - let mut invisible_display_ranges = SmallVec::<[Range; 32]>::new(); - - for (selection_style, selections) in &layout.selections { - for selection in selections { + for (range, color) in &layout.highlighted_ranges { self.paint_highlighted_range( - selection.range.clone(), - selection_style.selection, - corner_radius, - corner_radius * 2., + range.clone(), + *color, + Pixels::ZERO, + line_end_overshoot, layout, content_origin, - scroll_top, - scroll_left, - bounds, + text_bounds, cx, ); + } - if selection.is_local && !selection.range.is_empty() { - invisible_display_ranges.push(selection.range.clone()); - } + let mut cursors = SmallVec::<[Cursor; 32]>::new(); + let corner_radius = 0.15 * layout.position_map.line_height; + let mut invisible_display_ranges = SmallVec::<[Range; 32]>::new(); - if !selection.is_local || editor.show_local_cursors(cx) { - let cursor_position = selection.head; - if layout - .visible_display_row_range - .contains(&cursor_position.row()) - { - let cursor_row_layout = &layout.position_map.line_layouts - [(cursor_position.row() - start_row) as usize] - .line; - let cursor_column = cursor_position.column() as usize; + for (selection_style, selections) in &layout.selections { + for selection in selections { + self.paint_highlighted_range( + selection.range.clone(), + selection_style.selection, + corner_radius, + corner_radius * 2., + layout, + content_origin, + text_bounds, + cx, + ); - let cursor_character_x = cursor_row_layout.x_for_index(cursor_column); - let mut block_width = cursor_row_layout.x_for_index(cursor_column + 1) - - cursor_character_x; - if block_width == Pixels::ZERO { - block_width = layout.position_map.em_width; + if selection.is_local && !selection.range.is_empty() { + invisible_display_ranges.push(selection.range.clone()); + } + + if !selection.is_local || editor.show_local_cursors(cx) { + let cursor_position = selection.head; + if layout + .visible_display_row_range + .contains(&cursor_position.row()) + { + let cursor_row_layout = &layout.position_map.line_layouts + [(cursor_position.row() - start_row) as usize] + .line; + let cursor_column = cursor_position.column() as usize; + + let cursor_character_x = + cursor_row_layout.x_for_index(cursor_column); + let mut block_width = cursor_row_layout + .x_for_index(cursor_column + 1) + - cursor_character_x; + if block_width == Pixels::ZERO { + block_width = layout.position_map.em_width; + } + let block_text = if let CursorShape::Block = selection.cursor_shape + { + layout + .position_map + .snapshot + .chars_at(cursor_position) + .next() + .and_then(|(character, _)| { + let text = character.to_string(); + cx.text_system() + .layout_text( + &text, + cursor_row_layout.font_size, + &[TextRun { + len: text.len(), + font: self.style.text.font(), + color: self.style.background, + underline: None, + }], + None, + ) + .unwrap() + .pop() + }) + } else { + None + }; + + let x = cursor_character_x - layout.position_map.scroll_position.x; + let y = cursor_position.row() as f32 + * layout.position_map.line_height + - layout.position_map.scroll_position.y; + if selection.is_newest { + editor.pixel_position_of_newest_cursor = Some(point( + text_bounds.origin.x + x + block_width / 2., + text_bounds.origin.y + + y + + layout.position_map.line_height / 2., + )); + } + cursors.push(Cursor { + color: selection_style.cursor, + block_width, + origin: point(x, y), + line_height: layout.position_map.line_height, + shape: selection.cursor_shape, + block_text, + }); } - let block_text = if let CursorShape::Block = selection.cursor_shape { - layout - .position_map - .snapshot - .chars_at(cursor_position) - .next() - .and_then(|(character, _)| { - let text = character.to_string(); - cx.text_system() - .layout_text( - &text, - cursor_row_layout.font_size, - &[TextRun { - len: text.len(), - font: self.style.text.font(), - color: self.style.background, - underline: None, - }], - None, - ) - .unwrap() - .pop() - }) - } else { - None - }; - - let x = cursor_character_x - scroll_left; - let y = cursor_position.row() as f32 * layout.position_map.line_height - - scroll_top; - if selection.is_newest { - editor.pixel_position_of_newest_cursor = Some(point( - bounds.origin.x + x + block_width / 2., - bounds.origin.y + y + layout.position_map.line_height / 2., - )); - } - cursors.push(Cursor { - color: selection_style.cursor, - block_width, - origin: point(x, y), - line_height: layout.position_map.line_height, - shape: selection.cursor_shape, - block_text, - }); } } } - } - for (ix, line_with_invisibles) in layout.position_map.line_layouts.iter().enumerate() { - let row = start_row + ix as u32; - line_with_invisibles.draw( - layout, - row, - scroll_top, - content_origin, - scroll_left, - whitespace_setting, - &invisible_display_ranges, - cx, - ) - } - - cx.with_z_index(0, |cx| { - for cursor in cursors { - cursor.paint(content_origin, cx); + for (ix, line_with_invisibles) in + layout.position_map.line_layouts.iter().enumerate() + { + let row = start_row + ix as u32; + line_with_invisibles.draw( + layout, + row, + content_origin, + whitespace_setting, + &invisible_display_ranges, + cx, + ) } - }); - if let Some((position, context_menu)) = layout.context_menu.as_mut() { - cx.with_z_index(1, |cx| { - let line_height = self.style.text.line_height_in_pixels(cx.rem_size()); - let available_space = size( - AvailableSpace::MinContent, - AvailableSpace::Definite( - (12. * line_height).min((bounds.size.height - line_height) / 2.), - ), - ); - let context_menu_size = context_menu.measure(available_space, editor, cx); - - let cursor_row_layout = &layout.position_map.line_layouts - [(position.row() - start_row) as usize] - .line; - let x = cursor_row_layout.x_for_index(position.column() as usize) - scroll_left; - let y = - (position.row() + 1) as f32 * layout.position_map.line_height - scroll_top; - let mut list_origin = content_origin + point(x, y); - let list_width = context_menu_size.width; - let list_height = context_menu_size.height; - - // Snap the right edge of the list to the right edge of the window if - // its horizontal bounds overflow. - if list_origin.x + list_width > cx.viewport_size().width { - list_origin.x = (cx.viewport_size().width - list_width).max(Pixels::ZERO); + cx.with_z_index(0, |cx| { + for cursor in cursors { + cursor.paint(content_origin, cx); } + }); - if list_origin.y + list_height > bounds.lower_right().y { - list_origin.y -= layout.position_map.line_height - list_height; - } + if let Some((position, context_menu)) = layout.context_menu.as_mut() { + cx.with_z_index(1, |cx| { + let line_height = self.style.text.line_height_in_pixels(cx.rem_size()); + let available_space = size( + AvailableSpace::MinContent, + AvailableSpace::Definite( + (12. * line_height) + .min((text_bounds.size.height - line_height) / 2.), + ), + ); + let context_menu_size = context_menu.measure(available_space, editor, cx); - context_menu.draw(list_origin, available_space, editor, cx); - }) - } + let cursor_row_layout = &layout.position_map.line_layouts + [(position.row() - start_row) as usize] + .line; + let x = cursor_row_layout.x_for_index(position.column() as usize) + - layout.position_map.scroll_position.x; + let y = (position.row() + 1) as f32 * layout.position_map.line_height + - layout.position_map.scroll_position.y; + let mut list_origin = content_origin + point(x, y); + let list_width = context_menu_size.width; + let list_height = context_menu_size.height; - // if let Some((position, hover_popovers)) = layout.hover_popovers.as_mut() { - // cx.scene().push_stacking_context(None, None); + // Snap the right edge of the list to the right edge of the window if + // its horizontal bounds overflow. + if list_origin.x + list_width > cx.viewport_size().width { + list_origin.x = + (cx.viewport_size().width - list_width).max(Pixels::ZERO); + } - // // This is safe because we check on layout whether the required row is available - // let hovered_row_layout = - // &layout.position_map.line_layouts[(position.row() - start_row) as usize].line; + if list_origin.y + list_height > text_bounds.lower_right().y { + list_origin.y -= layout.position_map.line_height - list_height; + } - // // Minimum required size: Take the first popover, and add 1.5 times the minimum popover - // // height. This is the size we will use to decide whether to render popovers above or below - // // the hovered line. - // let first_size = hover_popovers[0].size(); - // let height_to_reserve = first_size.y - // + 1.5 * MIN_POPOVER_LINE_HEIGHT as f32 * layout.position_map.line_height; + context_menu.draw(list_origin, available_space, editor, cx); + }) + } - // // Compute Hovered Point - // let x = hovered_row_layout.x_for_index(position.column() as usize) - scroll_left; - // let y = position.row() as f32 * layout.position_map.line_height - scroll_top; - // let hovered_point = content_origin + point(x, y); + // if let Some((position, hover_popovers)) = layout.hover_popovers.as_mut() { + // cx.scene().push_stacking_context(None, None); - // if hovered_point.y - height_to_reserve > 0.0 { - // // There is enough space above. Render popovers above the hovered point - // let mut current_y = hovered_point.y; - // for hover_popover in hover_popovers { - // let size = hover_popover.size(); - // let mut popover_origin = point(hovered_point.x, current_y - size.y); + // // This is safe because we check on layout whether the required row is available + // let hovered_row_layout = + // &layout.position_map.line_layouts[(position.row() - start_row) as usize].line; - // let x_out_of_bounds = bounds.max_x - (popover_origin.x + size.x); - // if x_out_of_bounds < 0.0 { - // popover_origin.set_x(popover_origin.x + x_out_of_bounds); - // } + // // Minimum required size: Take the first popover, and add 1.5 times the minimum popover + // // height. This is the size we will use to decide whether to render popovers above or below + // // the hovered line. + // let first_size = hover_popovers[0].size(); + // let height_to_reserve = first_size.y + // + 1.5 * MIN_POPOVER_LINE_HEIGHT as f32 * layout.position_map.line_height; - // hover_popover.paint( - // popover_origin, - // Bounds::::from_points( - // gpui::Point::::zero(), - // point(f32::MAX, f32::MAX), - // ), // Let content bleed outside of editor - // editor, - // cx, - // ); + // // Compute Hovered Point + // let x = hovered_row_layout.x_for_index(position.column() as usize) - scroll_left; + // let y = position.row() as f32 * layout.position_map.line_height - scroll_top; + // let hovered_point = content_origin + point(x, y); - // current_y = popover_origin.y - HOVER_POPOVER_GAP; - // } - // } else { - // // There is not enough space above. Render popovers below the hovered point - // let mut current_y = hovered_point.y + layout.position_map.line_height; - // for hover_popover in hover_popovers { - // let size = hover_popover.size(); - // let mut popover_origin = point(hovered_point.x, current_y); + // if hovered_point.y - height_to_reserve > 0.0 { + // // There is enough space above. Render popovers above the hovered point + // let mut current_y = hovered_point.y; + // for hover_popover in hover_popovers { + // let size = hover_popover.size(); + // let mut popover_origin = point(hovered_point.x, current_y - size.y); - // let x_out_of_bounds = bounds.max_x - (popover_origin.x + size.x); - // if x_out_of_bounds < 0.0 { - // popover_origin.set_x(popover_origin.x + x_out_of_bounds); - // } + // let x_out_of_bounds = bounds.max_x - (popover_origin.x + size.x); + // if x_out_of_bounds < 0.0 { + // popover_origin.set_x(popover_origin.x + x_out_of_bounds); + // } - // hover_popover.paint( - // popover_origin, - // Bounds::::from_points( - // gpui::Point::::zero(), - // point(f32::MAX, f32::MAX), - // ), // Let content bleed outside of editor - // editor, - // cx, - // ); + // hover_popover.paint( + // popover_origin, + // Bounds::::from_points( + // gpui::Point::::zero(), + // point(f32::MAX, f32::MAX), + // ), // Let content bleed outside of editor + // editor, + // cx, + // ); - // current_y = popover_origin.y + size.y + HOVER_POPOVER_GAP; - // } - // } + // current_y = popover_origin.y - HOVER_POPOVER_GAP; + // } + // } else { + // // There is not enough space above. Render popovers below the hovered point + // let mut current_y = hovered_point.y + layout.position_map.line_height; + // for hover_popover in hover_popovers { + // let size = hover_popover.size(); + // let mut popover_origin = point(hovered_point.x, current_y); - // cx.scene().pop_stacking_context(); - // } - }) + // let x_out_of_bounds = bounds.max_x - (popover_origin.x + size.x); + // if x_out_of_bounds < 0.0 { + // popover_origin.set_x(popover_origin.x + x_out_of_bounds); + // } + + // hover_popover.paint( + // popover_origin, + // Bounds::::from_points( + // gpui::Point::::zero(), + // point(f32::MAX, f32::MAX), + // ), // Let content bleed outside of editor + // editor, + // cx, + // ); + + // current_y = popover_origin.y + size.y + HOVER_POPOVER_GAP; + // } + // } + + // cx.scene().pop_stacking_context(); + // } + }, + ) } fn scrollbar_left(&self, bounds: &Bounds) -> Pixels { @@ -1130,8 +1164,6 @@ impl EditorElement { line_end_overshoot: Pixels, layout: &LayoutState, content_origin: gpui::Point, - scroll_top: Pixels, - scroll_left: Pixels, bounds: Bounds, cx: &mut ViewContext, ) { @@ -1150,7 +1182,7 @@ impl EditorElement { corner_radius, start_y: content_origin.y + row_range.start as f32 * layout.position_map.line_height - - scroll_top, + - layout.position_map.scroll_position.y, lines: row_range .into_iter() .map(|row| { @@ -1160,17 +1192,17 @@ impl EditorElement { start_x: if row == range.start.row() { content_origin.x + line_layout.x_for_index(range.start.column() as usize) - - scroll_left + - layout.position_map.scroll_position.x } else { - content_origin.x - scroll_left + content_origin.x - layout.position_map.scroll_position.x }, end_x: if row == range.end.row() { content_origin.x + line_layout.x_for_index(range.end.column() as usize) - - scroll_left + - layout.position_map.scroll_position.x } else { content_origin.x + line_layout.width + line_end_overshoot - - scroll_left + - layout.position_map.scroll_position.x }, } }) @@ -1564,7 +1596,6 @@ impl EditorElement { let mut selections: Vec<(PlayerColor, Vec)> = Vec::new(); let mut active_rows = BTreeMap::new(); - let mut fold_ranges = Vec::new(); let is_singleton = editor.is_singleton(cx); let highlighted_rows = editor.highlighted_rows(); @@ -1574,19 +1605,6 @@ impl EditorElement { cx.theme().colors(), ); - fold_ranges.extend( - snapshot - .folds_in_range(start_anchor..end_anchor) - .map(|anchor| { - let start = anchor.start.to_point(&snapshot.buffer_snapshot); - ( - start.row, - start.to_display_point(&snapshot.display_snapshot) - ..anchor.end.to_display_point(&snapshot), - ) - }), - ); - let mut newest_selection_head = None; if editor.show_local_selections { @@ -1684,36 +1702,17 @@ impl EditorElement { ShowScrollbar::Auto => { // Git (is_singleton && scrollbar_settings.git_diff && snapshot.buffer_snapshot.has_git_diffs()) - || - // Selections - (is_singleton && scrollbar_settings.selections && !highlighted_ranges.is_empty()) - // Scrollmanager - || editor.scroll_manager.scrollbars_visible() + || + // Selections + (is_singleton && scrollbar_settings.selections && !highlighted_ranges.is_empty()) + // Scrollmanager + || editor.scroll_manager.scrollbars_visible() } ShowScrollbar::System => editor.scroll_manager.scrollbars_visible(), ShowScrollbar::Always => true, ShowScrollbar::Never => false, }; - let fold_ranges: Vec<(BufferRow, Range, Hsla)> = Vec::new(); - // todo!() - - // fold_ranges - // .into_iter() - // .map(|(id, fold)| { - // // todo!("folds!") - // // let color = self - // // .style - // // .folds - // // .ellipses - // // .background - // // .style_for(&mut cx.mouse_state::(id as usize)) - // // .color; - - // // (id, fold, color) - // }) - // .collect(); - let head_for_relative = newest_selection_head.unwrap_or_else(|| { let newest = editor.selections.newest::(cx); SelectionLayout::new( @@ -1754,21 +1753,23 @@ impl EditorElement { .width; let scroll_width = longest_line_width.max(max_visible_line_width) + overscroll.width; - let (scroll_width, blocks) = self.layout_blocks( - start_row..end_row, - &snapshot, - bounds.size.width, - scroll_width, - gutter_padding, - gutter_width, - em_width, - gutter_width + gutter_margin, - line_height, - &style, - &line_layouts, - editor, - cx, - ); + let (scroll_width, blocks) = cx.with_element_id(Some("editor_blocks"), |cx| { + self.layout_blocks( + start_row..end_row, + &snapshot, + bounds.size.width, + scroll_width, + gutter_padding, + gutter_width, + em_width, + gutter_width + gutter_margin, + line_height, + &style, + &line_layouts, + editor, + cx, + ) + }); let scroll_max = point( f32::from((scroll_width - text_size.width) / em_width).max(0.0), @@ -1828,15 +1829,16 @@ impl EditorElement { // ); // let mode = editor.mode; - // todo!("fold_indicators") - // let mut fold_indicators = editor.render_fold_indicators( - // fold_statuses, - // &style, - // editor.gutter_hovered, - // line_height, - // gutter_margin, - // cx, - // ); + let mut fold_indicators = cx.with_element_id(Some("gutter_fold_indicators"), |cx| { + editor.render_fold_indicators( + fold_statuses, + &style, + editor.gutter_hovered, + line_height, + gutter_margin, + cx, + ) + }); // todo!("context_menu") // if let Some((_, context_menu)) = context_menu.as_mut() { @@ -1853,20 +1855,6 @@ impl EditorElement { // ); // } - // todo!("fold indicators") - // for fold_indicator in fold_indicators.iter_mut() { - // if let Some(indicator) = fold_indicator.as_mut() { - // indicator.layout( - // SizeConstraint::strict_along( - // Axis::Vertical, - // line_height * style.code_actions.vertical_scale, - // ), - // editor, - // cx, - // ); - // } - // } - // todo!("hover popovers") // if let Some((_, hover_popovers)) = hover.as_mut() { // for hover_popover in hover_popovers.iter_mut() { @@ -1926,6 +1914,10 @@ impl EditorElement { mode: editor_mode, position_map: Arc::new(PositionMap { size: bounds.size, + scroll_position: point( + scroll_position.x * em_width, + scroll_position.y * line_height, + ), scroll_max, line_layouts, line_height, @@ -1933,6 +1925,7 @@ impl EditorElement { em_advance, snapshot, }), + visible_anchor_range: start_anchor..end_anchor, visible_display_row_range: start_row..end_row, wrap_guides, gutter_size, @@ -1946,14 +1939,13 @@ impl EditorElement { active_rows, highlighted_rows, highlighted_ranges, - fold_ranges, line_number_layouts, display_hunks, blocks, selections, context_menu, code_actions_indicator, - // fold_indicators, + fold_indicators, tab_invisible, space_invisible, // hover_popovers: hover, @@ -2019,7 +2011,6 @@ impl EditorElement { }) } TransformBlock::ExcerptHeader { - id, buffer, range, starts_new_buffer, @@ -2041,9 +2032,7 @@ impl EditorElement { .map_or(range.context.start, |primary| primary.start); let jump_position = language::ToPoint::to_point(&jump_anchor, buffer); - // todo!("avoid ElementId collision risk here") - let icon_button_id: usize = id.clone().into(); - IconButton::new(icon_button_id, ui::Icon::ArrowUpRight) + IconButton::new(block_id, ui::Icon::ArrowUpRight) .on_click(move |editor: &mut Editor, cx| { editor.jump(jump_path.clone(), jump_position, jump_anchor, cx); }) @@ -2137,11 +2126,13 @@ impl EditorElement { bounds: Bounds, gutter_bounds: Bounds, text_bounds: Bounds, - position_map: &Arc, + layout: &LayoutState, cx: &mut ViewContext, ) { + let content_origin = text_bounds.origin + point(layout.gutter_margin, Pixels::ZERO); + cx.on_mouse_event({ - let position_map = position_map.clone(); + let position_map = layout.position_map.clone(); move |editor, event: &ScrollWheelEvent, phase, cx| { if phase != DispatchPhase::Bubble { return; @@ -2153,7 +2144,7 @@ impl EditorElement { } }); cx.on_mouse_event({ - let position_map = position_map.clone(); + let position_map = layout.position_map.clone(); move |editor, event: &MouseDownEvent, phase, cx| { if phase != DispatchPhase::Bubble { return; @@ -2165,7 +2156,7 @@ impl EditorElement { } }); cx.on_mouse_event({ - let position_map = position_map.clone(); + let position_map = layout.position_map.clone(); move |editor, event: &MouseUpEvent, phase, cx| { if phase != DispatchPhase::Bubble { return; @@ -2178,7 +2169,7 @@ impl EditorElement { }); // todo!() // on_down(MouseButton::Right, { - // let position_map = position_map.clone(); + // let position_map = layout.position_map.clone(); // move |event, editor, cx| { // if !Self::mouse_right_down( // editor, @@ -2192,7 +2183,7 @@ impl EditorElement { // } // }); cx.on_mouse_event({ - let position_map = position_map.clone(); + let position_map = layout.position_map.clone(); move |editor, event: &MouseMoveEvent, phase, cx| { if phase != DispatchPhase::Bubble { return; @@ -2322,18 +2313,16 @@ impl LineWithInvisibles { &self, layout: &LayoutState, row: u32, - scroll_top: Pixels, content_origin: gpui::Point, - scroll_left: Pixels, whitespace_setting: ShowWhitespaceSetting, selection_ranges: &[Range], cx: &mut ViewContext, ) { let line_height = layout.position_map.line_height; - let line_y = line_height * row as f32 - scroll_top; + let line_y = line_height * row as f32 - layout.position_map.scroll_position.y; self.line.paint( - content_origin + gpui::point(-scroll_left, line_y), + content_origin + gpui::point(-layout.position_map.scroll_position.x, line_y), line_height, cx, ); @@ -2342,7 +2331,6 @@ impl LineWithInvisibles { &selection_ranges, layout, content_origin, - scroll_left, line_y, row, line_height, @@ -2356,7 +2344,6 @@ impl LineWithInvisibles { selection_ranges: &[Range], layout: &LayoutState, content_origin: gpui::Point, - scroll_left: Pixels, line_y: Pixels, row: u32, line_height: Pixels, @@ -2378,8 +2365,11 @@ impl LineWithInvisibles { let x_offset = self.line.x_for_index(token_offset); let invisible_offset = (layout.position_map.em_width - invisible_symbol.width).max(Pixels::ZERO) / 2.0; - let origin = - content_origin + gpui::point(-scroll_left + x_offset + invisible_offset, line_y); + let origin = content_origin + + gpui::point( + x_offset + invisible_offset - layout.position_map.scroll_position.x, + line_y, + ); if let Some(allowed_regions) = allowed_invisibles_regions { let invisible_point = DisplayPoint::new(row, token_offset as u32); @@ -2461,13 +2451,12 @@ impl Element for EditorElement { // We call with_z_index to establish a new stacking context. cx.with_z_index(0, |cx| { cx.with_content_mask(Some(ContentMask { bounds }), |cx| { - self.paint_mouse_listeners( - bounds, - gutter_bounds, - text_bounds, - &layout.position_map, - cx, - ); + // Paint mouse listeners first, so any elements we paint on top of the editor + // take precedence. + self.paint_mouse_listeners(bounds, gutter_bounds, text_bounds, &layout, cx); + let input_handler = ElementInputHandler::new(bounds, cx); + cx.handle_input(&editor.focus_handle, input_handler); + self.paint_background(gutter_bounds, text_bounds, &layout, cx); if layout.gutter_size.width > Pixels::ZERO { self.paint_gutter(gutter_bounds, &mut layout, editor, cx); @@ -2475,11 +2464,10 @@ impl Element for EditorElement { self.paint_text(text_bounds, &mut layout, editor, cx); if !layout.blocks.is_empty() { - self.paint_blocks(bounds, &mut layout, editor, cx); + cx.with_element_id(Some("editor_blocks"), |cx| { + self.paint_blocks(bounds, &mut layout, editor, cx); + }) } - - let input_handler = ElementInputHandler::new(bounds, cx); - cx.handle_input(&editor.focus_handle, input_handler); }); }); }, @@ -3101,6 +3089,7 @@ pub struct LayoutState { text_size: gpui::Size, mode: EditorMode, wrap_guides: SmallVec<[(Pixels, bool); 2]>, + visible_anchor_range: Range, visible_display_row_range: Range, active_rows: BTreeMap, highlighted_rows: Option>, @@ -3108,7 +3097,6 @@ pub struct LayoutState { display_hunks: Vec, blocks: Vec, highlighted_ranges: Vec<(Range, Hsla)>, - fold_ranges: Vec<(BufferRow, Range, Hsla)>, selections: Vec<(PlayerColor, Vec)>, scrollbar_row_range: Range, show_scrollbars: bool, @@ -3117,7 +3105,7 @@ pub struct LayoutState { context_menu: Option<(DisplayPoint, AnyElement)>, code_actions_indicator: Option, // hover_popovers: Option<(DisplayPoint, Vec>)>, - // fold_indicators: Vec>>, + fold_indicators: Vec>>, tab_invisible: Line, space_invisible: Line, } @@ -3130,6 +3118,7 @@ struct CodeActionsIndicator { struct PositionMap { size: Size, line_height: Pixels, + scroll_position: gpui::Point, scroll_max: gpui::Point, em_width: Pixels, em_advance: Pixels, @@ -3466,58 +3455,6 @@ impl HighlightedRange { } } -// fn range_to_bounds( -// range: &Range, -// content_origin: gpui::Point, -// scroll_left: f32, -// scroll_top: f32, -// visible_row_range: &Range, -// line_end_overshoot: f32, -// position_map: &PositionMap, -// ) -> impl Iterator> { -// let mut bounds: SmallVec<[Bounds; 1]> = SmallVec::new(); - -// if range.start == range.end { -// return bounds.into_iter(); -// } - -// let start_row = visible_row_range.start; -// let end_row = visible_row_range.end; - -// let row_range = if range.end.column() == 0 { -// cmp::max(range.start.row(), start_row)..cmp::min(range.end.row(), end_row) -// } else { -// cmp::max(range.start.row(), start_row)..cmp::min(range.end.row() + 1, end_row) -// }; - -// let first_y = -// content_origin.y + row_range.start as f32 * position_map.line_height - scroll_top; - -// for (idx, row) in row_range.enumerate() { -// let line_layout = &position_map.line_layouts[(row - start_row) as usize].line; - -// let start_x = if row == range.start.row() { -// content_origin.x + line_layout.x_for_index(range.start.column() as usize) -// - scroll_left -// } else { -// content_origin.x - scroll_left -// }; - -// let end_x = if row == range.end.row() { -// content_origin.x + line_layout.x_for_index(range.end.column() as usize) - scroll_left -// } else { -// content_origin.x + line_layout.width() + line_end_overshoot - scroll_left -// }; - -// bounds.push(Bounds::::from_points( -// point(start_x, first_y + position_map.line_height * idx as f32), -// point(end_x, first_y + position_map.line_height * (idx + 1) as f32), -// )) -// } - -// bounds.into_iter() -// } - pub fn scale_vertical_mouse_autoscroll_delta(delta: Pixels) -> f32 { (delta.pow(1.5) / 100.0).into() } diff --git a/crates/editor2/src/git.rs b/crates/editor2/src/git.rs index e04372f0a7..6e408cd3a0 100644 --- a/crates/editor2/src/git.rs +++ b/crates/editor2/src/git.rs @@ -60,8 +60,8 @@ pub fn diff_hunk_to_display(hunk: DiffHunk, snapshot: &DisplaySnapshot) -> let folds_end = Point::new(hunk.buffer_range.end + 2, 0); let folds_range = folds_start..folds_end; - let containing_fold = snapshot.folds_in_range(folds_range).find(|fold_range| { - let fold_point_range = fold_range.to_point(&snapshot.buffer_snapshot); + let containing_fold = snapshot.folds_in_range(folds_range).find(|fold| { + let fold_point_range = fold.range.to_point(&snapshot.buffer_snapshot); let fold_point_range = fold_point_range.start..=fold_point_range.end; let folded_start = fold_point_range.contains(&hunk_start_point); @@ -72,7 +72,7 @@ pub fn diff_hunk_to_display(hunk: DiffHunk, snapshot: &DisplaySnapshot) -> }); if let Some(fold) = containing_fold { - let row = fold.start.to_display_point(snapshot).row(); + let row = fold.range.start.to_display_point(snapshot).row(); DisplayDiffHunk::Folded { display_row: row } } else { let start = hunk_start_point.to_display_point(snapshot).row(); diff --git a/crates/gpui2/src/element.rs b/crates/gpui2/src/element.rs index 46ea5c6cd2..9db256a572 100644 --- a/crates/gpui2/src/element.rs +++ b/crates/gpui2/src/element.rs @@ -3,7 +3,7 @@ use crate::{ }; use derive_more::{Deref, DerefMut}; pub(crate) use smallvec::SmallVec; -use std::{any::Any, mem}; +use std::{any::Any, fmt::Debug, mem}; pub trait Element { type ElementState: 'static; @@ -33,6 +33,42 @@ pub trait Element { element_state: &mut Self::ElementState, cx: &mut ViewContext, ); + + fn draw( + self, + origin: Point, + available_space: Size, + view_state: &mut V, + cx: &mut ViewContext, + f: impl FnOnce(&Self::ElementState, &mut ViewContext) -> R, + ) -> R + where + Self: Sized, + T: Clone + Default + Debug + Into, + { + let mut element = RenderedElement { + element: self, + phase: ElementRenderPhase::Start, + }; + element.draw(origin, available_space.map(Into::into), view_state, cx); + if let ElementRenderPhase::Painted { frame_state } = &element.phase { + if let Some(frame_state) = frame_state.as_ref() { + f(&frame_state, cx) + } else { + let element_id = element + .element + .element_id() + .expect("we either have some frame_state or some element_id"); + cx.with_element_state(element_id, |element_state, cx| { + let element_state = element_state.unwrap(); + let result = f(&element_state, cx); + (result, element_state) + }) + } + } else { + unreachable!() + } + } } #[derive(Deref, DerefMut, Default, Clone, Debug, Eq, PartialEq, Hash)] @@ -99,7 +135,9 @@ enum ElementRenderPhase { available_space: Size, frame_state: Option, }, - Painted, + Painted { + frame_state: Option, + }, } /// Internal struct that wraps an element to store Layout and ElementState after the element is rendered. @@ -157,7 +195,7 @@ where ElementRenderPhase::Start => panic!("must call initialize before layout"), ElementRenderPhase::LayoutRequested { .. } | ElementRenderPhase::LayoutComputed { .. } - | ElementRenderPhase::Painted => { + | ElementRenderPhase::Painted { .. } => { panic!("element rendered twice") } }; @@ -192,7 +230,7 @@ where self.element .paint(bounds, view_state, frame_state.as_mut().unwrap(), cx); } - ElementRenderPhase::Painted + ElementRenderPhase::Painted { frame_state } } _ => panic!("must call layout before paint"), diff --git a/crates/gpui2/src/elements/div.rs b/crates/gpui2/src/elements/div.rs index e76d82f36f..1f9b2b020a 100644 --- a/crates/gpui2/src/elements/div.rs +++ b/crates/gpui2/src/elements/div.rs @@ -6,15 +6,15 @@ use crate::{ SharedString, Size, Style, StyleRefinement, Styled, Task, View, ViewContext, Visibility, }; use collections::HashMap; -use parking_lot::Mutex; use refineable::Refineable; use smallvec::SmallVec; use std::{ any::{Any, TypeId}, + cell::RefCell, fmt::Debug, marker::PhantomData, mem, - sync::Arc, + rc::Rc, time::Duration, }; use taffy::style::Overflow; @@ -420,7 +420,7 @@ pub trait StatefulInteractiveComponent>: InteractiveCo self.interactivity().tooltip_builder.is_none(), "calling tooltip more than once on the same element is not supported" ); - self.interactivity().tooltip_builder = Some(Arc::new(move |view_state, cx| { + self.interactivity().tooltip_builder = Some(Rc::new(move |view_state, cx| { build_tooltip(view_state, cx).into() })); @@ -569,7 +569,7 @@ type DropListener = dyn Fn(&mut V, AnyView, &mut ViewContext) + 'static; pub type HoverListener = Box) + 'static>; -pub type TooltipBuilder = Arc) -> AnyView + 'static>; +pub type TooltipBuilder = Rc) -> AnyView + 'static>; pub type KeyDownListener = Box) + 'static>; @@ -725,6 +725,12 @@ pub struct DivState { interactive_state: InteractiveElementState, } +impl DivState { + pub fn is_active(&self) -> bool { + self.interactive_state.pending_mouse_down.borrow().is_some() + } +} + pub struct Interactivity { pub element_id: Option, pub key_context: KeyContext, @@ -890,7 +896,7 @@ where if !click_listeners.is_empty() || drag_listener.is_some() { let pending_mouse_down = element_state.pending_mouse_down.clone(); - let mouse_down = pending_mouse_down.lock().clone(); + let mouse_down = pending_mouse_down.borrow().clone(); if let Some(mouse_down) = mouse_down { if let Some(drag_listener) = drag_listener { let active_state = element_state.clicked_state.clone(); @@ -904,7 +910,7 @@ where && bounds.contains_point(&event.position) && (event.position - mouse_down.position).magnitude() > DRAG_THRESHOLD { - *active_state.lock() = ElementClickedState::default(); + *active_state.borrow_mut() = ElementClickedState::default(); let cursor_offset = event.position - bounds.origin; let drag = drag_listener(view_state, cursor_offset, cx); cx.active_drag = Some(drag); @@ -924,13 +930,13 @@ where listener(view_state, &mouse_click, cx); } } - *pending_mouse_down.lock() = None; + *pending_mouse_down.borrow_mut() = None; cx.notify(); }); } else { cx.on_mouse_event(move |_view_state, event: &MouseDownEvent, phase, cx| { if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) { - *pending_mouse_down.lock() = Some(event.clone()); + *pending_mouse_down.borrow_mut() = Some(event.clone()); cx.notify(); } }); @@ -946,8 +952,8 @@ where return; } let is_hovered = - bounds.contains_point(&event.position) && has_mouse_down.lock().is_none(); - let mut was_hovered = was_hovered.lock(); + bounds.contains_point(&event.position) && has_mouse_down.borrow().is_none(); + let mut was_hovered = was_hovered.borrow_mut(); if is_hovered != was_hovered.clone() { *was_hovered = is_hovered; @@ -968,13 +974,13 @@ where } let is_hovered = - bounds.contains_point(&event.position) && pending_mouse_down.lock().is_none(); + bounds.contains_point(&event.position) && pending_mouse_down.borrow().is_none(); if !is_hovered { - active_tooltip.lock().take(); + active_tooltip.borrow_mut().take(); return; } - if active_tooltip.lock().is_none() { + if active_tooltip.borrow().is_none() { let task = cx.spawn({ let active_tooltip = active_tooltip.clone(); let tooltip_builder = tooltip_builder.clone(); @@ -982,7 +988,7 @@ where move |view, mut cx| async move { cx.background_executor().timer(TOOLTIP_DELAY).await; view.update(&mut cx, move |view_state, cx| { - active_tooltip.lock().replace(ActiveTooltip { + active_tooltip.borrow_mut().replace(ActiveTooltip { waiting: None, tooltip: Some(AnyTooltip { view: tooltip_builder(view_state, cx), @@ -994,14 +1000,14 @@ where .ok(); } }); - active_tooltip.lock().replace(ActiveTooltip { + active_tooltip.borrow_mut().replace(ActiveTooltip { waiting: Some(task), tooltip: None, }); } }); - if let Some(active_tooltip) = element_state.active_tooltip.lock().as_ref() { + if let Some(active_tooltip) = element_state.active_tooltip.borrow().as_ref() { if active_tooltip.tooltip.is_some() { cx.active_tooltip = active_tooltip.tooltip.clone() } @@ -1009,10 +1015,10 @@ where } let active_state = element_state.clicked_state.clone(); - if !active_state.lock().is_clicked() { + if !active_state.borrow().is_clicked() { cx.on_mouse_event(move |_, _: &MouseUpEvent, phase, cx| { if phase == DispatchPhase::Capture { - *active_state.lock() = ElementClickedState::default(); + *active_state.borrow_mut() = ElementClickedState::default(); cx.notify(); } }); @@ -1027,7 +1033,7 @@ where .map_or(false, |bounds| bounds.contains_point(&down.position)); let element = bounds.contains_point(&down.position); if group || element { - *active_state.lock() = ElementClickedState { group, element }; + *active_state.borrow_mut() = ElementClickedState { group, element }; cx.notify(); } } @@ -1038,14 +1044,14 @@ where if overflow.x == Overflow::Scroll || overflow.y == Overflow::Scroll { let scroll_offset = element_state .scroll_offset - .get_or_insert_with(Arc::default) + .get_or_insert_with(Rc::default) .clone(); let line_height = cx.line_height(); let scroll_max = (content_size - bounds.size).max(&Size::default()); cx.on_mouse_event(move |_, event: &ScrollWheelEvent, phase, cx| { if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) { - let mut scroll_offset = scroll_offset.lock(); + let mut scroll_offset = scroll_offset.borrow_mut(); let old_scroll_offset = *scroll_offset; let delta = event.delta.pixel_delta(line_height); @@ -1074,7 +1080,7 @@ where let scroll_offset = element_state .scroll_offset .as_ref() - .map(|scroll_offset| *scroll_offset.lock()); + .map(|scroll_offset| *scroll_offset.borrow()); cx.with_key_dispatch( self.key_context.clone(), @@ -1173,7 +1179,7 @@ where } } - let clicked_state = element_state.clicked_state.lock(); + let clicked_state = element_state.clicked_state.borrow(); if clicked_state.group { if let Some(group) = self.group_active_style.as_ref() { style.refine(&group.style) @@ -1227,11 +1233,11 @@ impl Default for Interactivity { #[derive(Default)] pub struct InteractiveElementState { pub focus_handle: Option, - pub clicked_state: Arc>, - pub hover_state: Arc>, - pub pending_mouse_down: Arc>>, - pub scroll_offset: Option>>>, - pub active_tooltip: Arc>>, + pub clicked_state: Rc>, + pub hover_state: Rc>, + pub pending_mouse_down: Rc>>, + pub scroll_offset: Option>>>, + pub active_tooltip: Rc>>, } pub struct ActiveTooltip { diff --git a/crates/gpui2/src/elements/uniform_list.rs b/crates/gpui2/src/elements/uniform_list.rs index 84cd216275..340a2cbf87 100644 --- a/crates/gpui2/src/elements/uniform_list.rs +++ b/crates/gpui2/src/elements/uniform_list.rs @@ -3,9 +3,8 @@ use crate::{ ElementId, InteractiveComponent, InteractiveElementState, Interactivity, LayoutId, Pixels, Point, Size, StyleRefinement, Styled, ViewContext, }; -use parking_lot::Mutex; use smallvec::SmallVec; -use std::{cmp, mem, ops::Range, sync::Arc}; +use std::{cell::RefCell, cmp, mem, ops::Range, rc::Rc}; use taffy::style::Overflow; /// uniform_list provides lazy rendering for a set of items that are of uniform height. @@ -61,23 +60,23 @@ pub struct UniformList { } #[derive(Clone, Default)] -pub struct UniformListScrollHandle(Arc>>); +pub struct UniformListScrollHandle(Rc>>); #[derive(Clone, Debug)] struct ScrollHandleState { item_height: Pixels, list_height: Pixels, - scroll_offset: Arc>>, + scroll_offset: Rc>>, } impl UniformListScrollHandle { pub fn new() -> Self { - Self(Arc::new(Mutex::new(None))) + Self(Rc::new(RefCell::new(None))) } pub fn scroll_to_item(&self, ix: usize) { - if let Some(state) = &*self.0.lock() { - let mut scroll_offset = state.scroll_offset.lock(); + if let Some(state) = &*self.0.borrow() { + let mut scroll_offset = state.scroll_offset.borrow_mut(); let item_top = state.item_height * ix; let item_bottom = item_top + state.item_height; let scroll_top = -scroll_offset.y; @@ -196,7 +195,7 @@ impl Element for UniformList { let shared_scroll_offset = element_state .interactive .scroll_offset - .get_or_insert_with(Arc::default) + .get_or_insert_with(Rc::default) .clone(); interactivity.paint( @@ -222,7 +221,7 @@ impl Element for UniformList { .measure_item(view_state, Some(padded_bounds.size.width), cx) .height; if let Some(scroll_handle) = self.scroll_handle.clone() { - scroll_handle.0.lock().replace(ScrollHandleState { + scroll_handle.0.borrow_mut().replace(ScrollHandleState { item_height, list_height: padded_bounds.size.height, scroll_offset: shared_scroll_offset, diff --git a/crates/gpui2/src/view.rs b/crates/gpui2/src/view.rs index c0bbe00db6..5a2cc0f103 100644 --- a/crates/gpui2/src/view.rs +++ b/crates/gpui2/src/view.rs @@ -63,6 +63,16 @@ impl View { pub fn read<'a>(&self, cx: &'a AppContext) -> &'a V { self.model.read(cx) } + + pub fn render_with(&self, component: C) -> RenderViewWith + where + C: 'static + Component, + { + RenderViewWith { + view: self.clone(), + component: Some(component), + } + } } impl Clone for View { @@ -281,12 +291,12 @@ where } } -pub struct RenderView { +pub struct RenderViewWith { view: View, component: Option, } -impl Component for RenderView +impl Component for RenderViewWith where C: 'static + Component, ParentViewState: 'static, @@ -297,7 +307,7 @@ where } } -impl Element for RenderView +impl Element for RenderViewWith where C: 'static + Component, ParentViewState: 'static, @@ -348,17 +358,6 @@ where } } -pub fn render_view(view: &View, component: C) -> RenderView -where - C: 'static + Component, - V: 'static, -{ - RenderView { - view: view.clone(), - component: Some(component), - } -} - mod any_view { use crate::{AnyElement, AnyView, BorrowWindow, LayoutId, Render, WindowContext}; use std::any::Any; diff --git a/crates/gpui2/src/window.rs b/crates/gpui2/src/window.rs index 808f903bd0..c6361c8c6e 100644 --- a/crates/gpui2/src/window.rs +++ b/crates/gpui2/src/window.rs @@ -216,7 +216,7 @@ pub struct Window { // #[derive(Default)] pub(crate) struct Frame { - element_states: HashMap, + pub(crate) element_states: HashMap, mouse_listeners: HashMap>, pub(crate) dispatch_tree: DispatchTree, pub(crate) focus_listeners: Vec, @@ -2471,7 +2471,7 @@ impl From> for StackingOrder { #[derive(Clone, Debug, Eq, PartialEq, Hash)] pub enum ElementId { View(EntityId), - Number(usize), + Integer(usize), Name(SharedString), FocusHandle(FocusId), } @@ -2484,13 +2484,13 @@ impl From for ElementId { impl From for ElementId { fn from(id: usize) -> Self { - ElementId::Number(id) + ElementId::Integer(id) } } impl From for ElementId { fn from(id: i32) -> Self { - Self::Number(id as usize) + Self::Integer(id as usize) } }