From cfee1401ed5e19fff04e3b970b685cd02d540c53 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 9 Nov 2023 17:30:41 +0100 Subject: [PATCH 01/15] Extract `AnyElement::{measure,draw}` Co-Authored-By: Nathan Sobo --- crates/gpui2/src/element.rs | 31 +++++++++++++++++- crates/gpui2/src/elements/uniform_list.rs | 38 +++++++++-------------- 2 files changed, 44 insertions(+), 25 deletions(-) diff --git a/crates/gpui2/src/element.rs b/crates/gpui2/src/element.rs index 8fdc17de07296d95def915f3a605f3988913eb2a..775b3b8a895215c49476e06d34913b3aa311c695 100644 --- a/crates/gpui2/src/element.rs +++ b/crates/gpui2/src/element.rs @@ -1,4 +1,6 @@ -use crate::{BorrowWindow, Bounds, ElementId, LayoutId, Pixels, ViewContext}; +use crate::{ + AvailableSpace, BorrowWindow, Bounds, ElementId, LayoutId, Pixels, Point, Size, ViewContext, +}; use derive_more::{Deref, DerefMut}; pub(crate) use smallvec::SmallVec; use std::{any::Any, mem}; @@ -196,6 +198,33 @@ impl AnyElement { pub fn paint(&mut self, view_state: &mut V, cx: &mut ViewContext) { self.0.paint(view_state, cx) } + + /// Initializes this element and performs layout within the given available space to determine its size. + pub fn measure( + &mut self, + available_space: Size, + view_state: &mut V, + cx: &mut ViewContext, + ) -> Size { + self.initialize(view_state, cx); + let layout_id = self.layout(view_state, cx); + cx.compute_layout(layout_id, available_space); + cx.layout_bounds(layout_id).size + } + + /// Initializes this element and performs layout in the available space, then paints it at the given origin. + pub fn draw( + &mut self, + origin: Point, + available_space: Size, + view_state: &mut V, + cx: &mut ViewContext, + ) { + self.initialize(view_state, cx); + let layout_id = self.layout(view_state, cx); + cx.compute_layout(layout_id, available_space); + cx.with_element_offset(Some(origin), |cx| self.paint(view_state, cx)) + } } pub trait Component { diff --git a/crates/gpui2/src/elements/uniform_list.rs b/crates/gpui2/src/elements/uniform_list.rs index e1160227637c8374fa47e922a0fabb509308ec1e..a4524d5496b63cacd631ff1ab414dede29bc6086 100644 --- a/crates/gpui2/src/elements/uniform_list.rs +++ b/crates/gpui2/src/elements/uniform_list.rs @@ -1,6 +1,6 @@ use crate::{ - point, px, AnyElement, AvailableSpace, BorrowWindow, Bounds, Component, Element, ElementId, - ElementInteractivity, InteractiveElementState, LayoutId, Pixels, Point, Size, + point, px, size, AnyElement, AvailableSpace, BorrowWindow, Bounds, Component, Element, + ElementId, ElementInteractivity, InteractiveElementState, LayoutId, Pixels, Point, Size, StatefulInteractive, StatefulInteractivity, StatelessInteractive, StatelessInteractivity, StyleRefinement, Styled, ViewContext, }; @@ -165,19 +165,13 @@ impl Element for UniformList { cx.with_z_index(1, |cx| { for (item, ix) in items.iter_mut().zip(visible_range) { - item.initialize(view_state, cx); - - let layout_id = item.layout(view_state, cx); - cx.compute_layout( - layout_id, - Size { - width: AvailableSpace::Definite(bounds.size.width), - height: AvailableSpace::Definite(item_height), - }, - ); - let offset = + let item_origin = padded_bounds.origin + point(px(0.), item_height * ix + scroll_offset); - cx.with_element_offset(Some(offset), |cx| item.paint(view_state, cx)) + let available_space = size( + AvailableSpace::Definite(bounds.size.width), + AvailableSpace::Definite(item_height), + ); + item.draw(item_origin, available_space, view_state, cx); } }); } else { @@ -205,18 +199,14 @@ impl UniformList { cx: &mut ViewContext, ) -> Pixels { let mut items = (self.render_items)(view_state, 0..1, cx); - debug_assert!(items.len() == 1); + debug_assert_eq!(items.len(), 1); let mut item_to_measure = items.pop().unwrap(); - item_to_measure.initialize(view_state, cx); - let layout_id = item_to_measure.layout(view_state, cx); - cx.compute_layout( - layout_id, - Size { - width: AvailableSpace::Definite(list_bounds.size.width), - height: AvailableSpace::MinContent, - }, + let available_space = size( + AvailableSpace::Definite(list_bounds.size.width), + AvailableSpace::MinContent, ); - cx.layout_bounds(layout_id).size.height + let size = item_to_measure.measure(available_space, view_state, cx); + size.height } pub fn track_scroll(mut self, handle: UniformListScrollHandle) -> Self { From 5d158866752d7dd3c92665f66166ac4c7955a74c Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 9 Nov 2023 18:43:26 +0100 Subject: [PATCH 02/15] Render code actions indicator Co-Authored-By: Nathan --- Cargo.lock | 1 + crates/editor2/Cargo.toml | 1 + crates/editor2/src/editor.rs | 137 +++++++++++------------ crates/editor2/src/element.rs | 89 ++++++++------- crates/gpui2/src/element.rs | 93 +++++++++++++-- crates/gpui2/src/taffy.rs | 31 ++++- crates/gpui2/src/window.rs | 2 + crates/ui2/src/components/icon_button.rs | 1 + 8 files changed, 227 insertions(+), 128 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ded64052c8e165e4b4ca3955e3382a38874d88bf..ed06172fd8f495bf04e0b09340b7557d5e1ef739 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2781,6 +2781,7 @@ dependencies = [ "tree-sitter-html", "tree-sitter-rust", "tree-sitter-typescript", + "ui2", "unindent", "util", "workspace2", diff --git a/crates/editor2/Cargo.toml b/crates/editor2/Cargo.toml index b897110966709ecef886caf8adef36f1973cc4bd..493f10006f734f5c0fd907935981d2cb9a17b9e1 100644 --- a/crates/editor2/Cargo.toml +++ b/crates/editor2/Cargo.toml @@ -44,6 +44,7 @@ snippet = { path = "../snippet" } sum_tree = { path = "../sum_tree" } text = { package="text2", path = "../text2" } theme = { package="theme2", path = "../theme2" } +ui2 = { package = "ui2", path = "../ui2" } util = { path = "../util" } sqlez = { path = "../sqlez" } workspace = { package = "workspace2", path = "../workspace2" } diff --git a/crates/editor2/src/editor.rs b/crates/editor2/src/editor.rs index 51cd549923827ce21f9220e153f1f983bf5aa95d..752696bcaef3752a8acf0df0516c1ed742978a9e 100644 --- a/crates/editor2/src/editor.rs +++ b/crates/editor2/src/editor.rs @@ -40,9 +40,9 @@ use fuzzy::{StringMatch, StringMatchCandidate}; use git::diff_hunk_to_display; use gpui::{ action, actions, point, px, relative, rems, size, AnyElement, AppContext, BackgroundExecutor, - Bounds, ClipboardItem, Context, DispatchContext, EventEmitter, FocusHandle, FontFeatures, - FontStyle, FontWeight, HighlightStyle, Hsla, InputHandler, Model, Pixels, Render, Subscription, - Task, TextStyle, View, ViewContext, VisualContext, WeakView, WindowContext, + Bounds, ClipboardItem, Component, Context, DispatchContext, EventEmitter, FocusHandle, + FontFeatures, FontStyle, FontWeight, HighlightStyle, Hsla, InputHandler, Model, Pixels, Render, + Subscription, Task, TextStyle, View, ViewContext, VisualContext, WeakView, WindowContext, }; use highlight_matching_bracket::refresh_matching_bracket_highlights; use hover_popover::{hide_hover, HoverState}; @@ -95,6 +95,7 @@ use text::{OffsetUtf16, Rope}; use theme::{ ActiveTheme, DiagnosticStyle, PlayerColor, SyntaxTheme, Theme, ThemeColors, ThemeSettings, }; +use ui2::IconButton; use util::{post_inc, RangeExt, ResultExt, TryFutureExt}; use workspace::{ item::ItemEvent, searchable::SearchEvent, ItemNavHistory, SplitDirection, ViewId, Workspace, @@ -3846,44 +3847,44 @@ impl Editor { // })) // } - // pub fn toggle_code_actions(&mut self, action: &ToggleCodeActions, cx: &mut ViewContext) { - // let mut context_menu = self.context_menu.write(); - // if matches!(context_menu.as_ref(), Some(ContextMenu::CodeActions(_))) { - // *context_menu = None; - // cx.notify(); - // return; - // } - // drop(context_menu); - - // let deployed_from_indicator = action.deployed_from_indicator; - // let mut task = self.code_actions_task.take(); - // cx.spawn(|this, mut cx| async move { - // while let Some(prev_task) = task { - // prev_task.await; - // task = this.update(&mut cx, |this, _| this.code_actions_task.take())?; - // } + pub fn toggle_code_actions(&mut self, action: &ToggleCodeActions, cx: &mut ViewContext) { + let mut context_menu = self.context_menu.write(); + if matches!(context_menu.as_ref(), Some(ContextMenu::CodeActions(_))) { + *context_menu = None; + cx.notify(); + return; + } + drop(context_menu); - // this.update(&mut cx, |this, cx| { - // if this.focused { - // if let Some((buffer, actions)) = this.available_code_actions.clone() { - // this.completion_tasks.clear(); - // this.discard_copilot_suggestion(cx); - // *this.context_menu.write() = - // Some(ContextMenu::CodeActions(CodeActionsMenu { - // buffer, - // actions, - // selected_item: Default::default(), - // list: Default::default(), - // deployed_from_indicator, - // })); - // } - // } - // })?; + let deployed_from_indicator = action.deployed_from_indicator; + let mut task = self.code_actions_task.take(); + cx.spawn(|this, mut cx| async move { + while let Some(prev_task) = task { + prev_task.await; + task = this.update(&mut cx, |this, _| this.code_actions_task.take())?; + } - // Ok::<_, anyhow::Error>(()) - // }) - // .detach_and_log_err(cx); - // } + this.update(&mut cx, |this, cx| { + if this.focus_handle.is_focused(cx) { + if let Some((buffer, actions)) = this.available_code_actions.clone() { + this.completion_tasks.clear(); + this.discard_copilot_suggestion(cx); + *this.context_menu.write() = + Some(ContextMenu::CodeActions(CodeActionsMenu { + buffer, + actions, + selected_item: Default::default(), + list: Default::default(), + deployed_from_indicator, + })); + } + } + })?; + + Ok::<_, anyhow::Error>(()) + }) + .detach_and_log_err(cx); + } // pub fn confirm_code_action( // workspace: &mut Workspace, @@ -4390,41 +4391,29 @@ impl Editor { self.discard_copilot_suggestion(cx); } - // pub fn render_code_actions_indicator( - // &self, - // style: &EditorStyle, - // is_active: bool, - // cx: &mut ViewContext, - // ) -> Option> { - // if self.available_code_actions.is_some() { - // enum CodeActions {} - // Some( - // MouseEventHandler::new::(0, cx, |state, _| { - // Svg::new("icons/bolt.svg").with_color( - // style - // .code_actions - // .indicator - // .in_state(is_active) - // .style_for(state) - // .color, - // ) - // }) - // .with_cursor_style(CursorStyle::PointingHand) - // .with_padding(Padding::uniform(3.)) - // .on_down(MouseButton::Left, |_, this, cx| { - // this.toggle_code_actions( - // &ToggleCodeActions { - // deployed_from_indicator: true, - // }, - // cx, - // ); - // }) - // .into_any(), - // ) - // } else { - // None - // } - // } + pub fn render_code_actions_indicator( + &self, + style: &EditorStyle, + is_active: bool, + cx: &mut ViewContext, + ) -> Option> { + if self.available_code_actions.is_some() { + Some( + IconButton::new("code_actions", ui2::Icon::Bolt) + .on_click(|editor: &mut Editor, cx| { + editor.toggle_code_actions( + &ToggleCodeActions { + deployed_from_indicator: true, + }, + cx, + ); + }) + .render(), + ) + } else { + None + } + } // pub fn render_fold_indicators( // &self, diff --git a/crates/editor2/src/element.rs b/crates/editor2/src/element.rs index 3e77a66936443aa872fc6f196fb71257bfede82f..a447b5647c48eefd9b9c10f9e130d7d8e4d705a6 100644 --- a/crates/editor2/src/element.rs +++ b/crates/editor2/src/element.rs @@ -15,7 +15,7 @@ use crate::{ use anyhow::Result; use collections::{BTreeMap, HashMap}; use gpui::{ - black, hsla, point, px, relative, size, transparent_black, Action, AnyElement, + black, hsla, point, px, relative, size, transparent_black, Action, AnyElement, AvailableSpace, BorrowAppContext, BorrowWindow, Bounds, ContentMask, Corners, DispatchContext, DispatchPhase, Edges, Element, ElementId, ElementInputHandler, Entity, FocusHandle, GlobalElementId, Hsla, InputHandler, KeyDownEvent, KeyListener, KeyMatch, Line, LineLayout, Modifiers, MouseButton, @@ -447,7 +447,7 @@ impl EditorElement { fn paint_gutter( &mut self, bounds: Bounds, - layout: &LayoutState, + layout: &mut LayoutState, editor: &mut Editor, cx: &mut ViewContext, ) { @@ -495,14 +495,21 @@ impl EditorElement { // } // } - // todo!("code actions indicator") - // if let Some((row, indicator)) = layout.code_actions_indicator.as_mut() { - // let mut x = 0.; - // let mut y = *row as f32 * line_height - scroll_top; - // x += ((layout.gutter_padding + layout.gutter_margin) - indicator.size().x) / 2.; - // y += (line_height - indicator.size().y) / 2.; - // indicator.paint(bounds.origin + point(x, y), visible_bounds, editor, cx); - // } + if let Some(indicator) = layout.code_actions_indicator.as_mut() { + let available_space = size( + AvailableSpace::MinContent, + AvailableSpace::Definite(line_height), + ); + let indicator_size = indicator.element.measure(available_space, editor, cx); + let mut x = Pixels::ZERO; + let mut y = indicator.row as f32 * line_height - scroll_top; + // Center indicator. + x += ((layout.gutter_padding + layout.gutter_margin) - indicator_size.width) / 2.; + y += (line_height - indicator_size.height) / 2.; + indicator + .element + .draw(bounds.origin + point(x, y), available_space, editor, cx); + } } fn paint_diff_hunks( @@ -1776,24 +1783,27 @@ impl EditorElement { // todo!("context menu") // let mut context_menu = None; - // let mut code_actions_indicator = None; - // if let Some(newest_selection_head) = newest_selection_head { - // if (start_row..end_row).contains(&newest_selection_head.row()) { - // if editor.context_menu_visible() { - // context_menu = - // editor.render_context_menu(newest_selection_head, style.clone(), cx); - // } - - // let active = matches!( - // editor.context_menu.read().as_ref(), - // Some(crate::ContextMenu::CodeActions(_)) - // ); + let mut code_actions_indicator = None; + if let Some(newest_selection_head) = newest_selection_head { + if (start_row..end_row).contains(&newest_selection_head.row()) { + // if editor.context_menu_visible() { + // context_menu = + // editor.render_context_menu(newest_selection_head, style.clone(), cx); + // } + + let active = matches!( + editor.context_menu.read().as_ref(), + Some(crate::ContextMenu::CodeActions(_)) + ); - // code_actions_indicator = editor - // .render_code_actions_indicator(&style, active, cx) - // .map(|indicator| (newest_selection_head.row(), indicator)); - // } - // } + code_actions_indicator = editor + .render_code_actions_indicator(&style, active, cx) + .map(|element| CodeActionsIndicator { + row: newest_selection_head.row(), + element, + }); + } + } let visible_rows = start_row..start_row + line_layouts.len() as u32; // todo!("hover") @@ -1831,18 +1841,6 @@ impl EditorElement { // ); // } - // todo!("code actions") - // if let Some((_, indicator)) = code_actions_indicator.as_mut() { - // indicator.layout( - // SizeConstraint::strict_along( - // Axis::Vertical, - // line_height * style.code_actions.vertical_scale, - // ), - // editor, - // cx, - // ); - // } - // todo!("fold indicators") // for fold_indicator in fold_indicators.iter_mut() { // if let Some(indicator) = fold_indicator.as_mut() { @@ -1942,7 +1940,7 @@ impl EditorElement { // blocks, selections, // context_menu, - // code_actions_indicator, + code_actions_indicator, // fold_indicators, tab_invisible, space_invisible, @@ -2493,7 +2491,7 @@ impl Element for EditorElement { element_state: &mut Self::ElementState, cx: &mut gpui::ViewContext, ) { - let layout = self.compute_layout(editor, cx, bounds); + let mut layout = self.compute_layout(editor, cx, bounds); let gutter_bounds = Bounds { origin: bounds.origin, size: layout.gutter_size, @@ -2513,7 +2511,7 @@ impl Element for EditorElement { ); self.paint_background(gutter_bounds, text_bounds, &layout, cx); if layout.gutter_size.width > Pixels::ZERO { - self.paint_gutter(gutter_bounds, &layout, editor, cx); + self.paint_gutter(gutter_bounds, &mut layout, editor, cx); } self.paint_text(text_bounds, &layout, editor, cx); let input_handler = ElementInputHandler::new(bounds, cx); @@ -3144,13 +3142,18 @@ pub struct LayoutState { is_singleton: bool, max_row: u32, // context_menu: Option<(DisplayPoint, AnyElement)>, - // code_actions_indicator: Option<(u32, AnyElement)>, + code_actions_indicator: Option, // hover_popovers: Option<(DisplayPoint, Vec>)>, // fold_indicators: Vec>>, tab_invisible: Line, space_invisible: Line, } +struct CodeActionsIndicator { + row: u32, + element: AnyElement, +} + struct PositionMap { size: Size, line_height: Pixels, diff --git a/crates/gpui2/src/element.rs b/crates/gpui2/src/element.rs index 775b3b8a895215c49476e06d34913b3aa311c695..e7526dfa3a74d444bf6747d51a8b98a1de85d9cf 100644 --- a/crates/gpui2/src/element.rs +++ b/crates/gpui2/src/element.rs @@ -63,6 +63,19 @@ trait ElementObject { fn initialize(&mut self, view_state: &mut V, cx: &mut ViewContext); fn layout(&mut self, view_state: &mut V, cx: &mut ViewContext) -> LayoutId; fn paint(&mut self, view_state: &mut V, cx: &mut ViewContext); + fn measure( + &mut self, + available_space: Size, + view_state: &mut V, + cx: &mut ViewContext, + ) -> Size; + fn draw( + &mut self, + origin: Point, + available_space: Size, + view_state: &mut V, + cx: &mut ViewContext, + ); } struct RenderedElement> { @@ -81,6 +94,11 @@ enum ElementRenderPhase { layout_id: LayoutId, frame_state: Option, }, + LayoutComputed { + layout_id: LayoutId, + available_space: Size, + frame_state: Option, + }, Painted, } @@ -137,7 +155,9 @@ where } } ElementRenderPhase::Start => panic!("must call initialize before layout"), - ElementRenderPhase::LayoutRequested { .. } | ElementRenderPhase::Painted => { + ElementRenderPhase::LayoutRequested { .. } + | ElementRenderPhase::LayoutComputed { .. } + | ElementRenderPhase::Painted => { panic!("element rendered twice") } }; @@ -154,6 +174,11 @@ where ElementRenderPhase::LayoutRequested { layout_id, mut frame_state, + } + | ElementRenderPhase::LayoutComputed { + layout_id, + mut frame_state, + .. } => { let bounds = cx.layout_bounds(layout_id); if let Some(id) = self.element.id() { @@ -173,6 +198,62 @@ where _ => panic!("must call layout before paint"), }; } + + fn measure( + &mut self, + available_space: Size, + view_state: &mut V, + cx: &mut ViewContext, + ) -> Size { + if matches!(&self.phase, ElementRenderPhase::Start) { + self.initialize(view_state, cx); + } + + if matches!(&self.phase, ElementRenderPhase::Initialized { .. }) { + self.layout(view_state, cx); + } + + let layout_id = match &mut self.phase { + ElementRenderPhase::LayoutRequested { + layout_id, + frame_state, + } => { + cx.compute_layout(*layout_id, available_space); + let layout_id = *layout_id; + self.phase = ElementRenderPhase::LayoutComputed { + layout_id, + available_space, + frame_state: frame_state.take(), + }; + layout_id + } + ElementRenderPhase::LayoutComputed { + layout_id, + available_space: prev_available_space, + .. + } => { + if available_space != *prev_available_space { + cx.compute_layout(*layout_id, available_space); + *prev_available_space = available_space; + } + *layout_id + } + _ => panic!("cannot measure after painting"), + }; + + cx.layout_bounds(layout_id).size + } + + fn draw( + &mut self, + origin: Point, + available_space: Size, + view_state: &mut V, + cx: &mut ViewContext, + ) { + self.measure(available_space, view_state, cx); + cx.with_element_offset(Some(origin), |cx| self.paint(view_state, cx)) + } } pub struct AnyElement(Box>); @@ -206,10 +287,7 @@ impl AnyElement { view_state: &mut V, cx: &mut ViewContext, ) -> Size { - self.initialize(view_state, cx); - let layout_id = self.layout(view_state, cx); - cx.compute_layout(layout_id, available_space); - cx.layout_bounds(layout_id).size + self.0.measure(available_space, view_state, cx) } /// Initializes this element and performs layout in the available space, then paints it at the given origin. @@ -220,10 +298,7 @@ impl AnyElement { view_state: &mut V, cx: &mut ViewContext, ) { - self.initialize(view_state, cx); - let layout_id = self.layout(view_state, cx); - cx.compute_layout(layout_id, available_space); - cx.with_element_offset(Some(origin), |cx| self.paint(view_state, cx)) + self.0.draw(origin, available_space, view_state, cx) } } diff --git a/crates/gpui2/src/taffy.rs b/crates/gpui2/src/taffy.rs index 9724179eed4735b7959f93865bd234bc3d246823..ea87f73872cd445ee37e530d973d5e0e054a76fd 100644 --- a/crates/gpui2/src/taffy.rs +++ b/crates/gpui2/src/taffy.rs @@ -1,5 +1,6 @@ use super::{AbsoluteLength, Bounds, DefiniteLength, Edges, Length, Pixels, Point, Size, Style}; -use collections::HashMap; +use collections::{HashMap, HashSet}; +use smallvec::SmallVec; use std::fmt::Debug; use taffy::{ geometry::{Point as TaffyPoint, Rect as TaffyRect, Size as TaffySize}, @@ -12,6 +13,7 @@ pub struct TaffyLayoutEngine { taffy: Taffy, children_to_parents: HashMap, absolute_layout_bounds: HashMap>, + computed_layouts: HashSet, } static EXPECT_MESSAGE: &'static str = @@ -23,9 +25,17 @@ impl TaffyLayoutEngine { taffy: Taffy::new(), children_to_parents: HashMap::default(), absolute_layout_bounds: HashMap::default(), + computed_layouts: HashSet::default(), } } + pub fn clear(&mut self) { + self.taffy.clear(); + self.children_to_parents.clear(); + self.absolute_layout_bounds.clear(); + self.computed_layouts.clear(); + } + pub fn request_layout( &mut self, style: &Style, @@ -115,6 +125,7 @@ impl TaffyLayoutEngine { } pub fn compute_layout(&mut self, id: LayoutId, available_space: Size) { + // Leaving this here until we have a better instrumentation approach. // println!("Laying out {} children", self.count_all_children(id)?); // println!("Max layout depth: {}", self.max_depth(0, id)?); @@ -124,6 +135,22 @@ impl TaffyLayoutEngine { // println!("N{} --> N{}", u64::from(a), u64::from(b)); // } // println!(""); + // + + if !self.computed_layouts.insert(id) { + let mut stack = SmallVec::<[LayoutId; 64]>::new(); + stack.push(id); + while let Some(id) = stack.pop() { + self.absolute_layout_bounds.remove(&id); + stack.extend( + self.taffy + .children(id.into()) + .expect(EXPECT_MESSAGE) + .into_iter() + .map(Into::into), + ); + } + } // let started_at = std::time::Instant::now(); self.taffy @@ -397,7 +424,7 @@ where } } -#[derive(Copy, Clone, Default, Debug)] +#[derive(Copy, Clone, Default, Debug, Eq, PartialEq)] pub enum AvailableSpace { /// The amount of space available is the specified number of pixels Definite(Pixels), diff --git a/crates/gpui2/src/window.rs b/crates/gpui2/src/window.rs index ac7dcf02569e4db6c3c8f92658d95088dcdc4ba5..7b5ce5b7fc418d612a90f64c23ebf44ecc6a1b10 100644 --- a/crates/gpui2/src/window.rs +++ b/crates/gpui2/src/window.rs @@ -1060,6 +1060,8 @@ impl<'a> WindowContext<'a> { self.text_system().start_frame(); let window = &mut *self.window; + window.layout_engine.clear(); + mem::swap(&mut window.previous_frame, &mut window.current_frame); let frame = &mut window.current_frame; frame.element_states.clear(); diff --git a/crates/ui2/src/components/icon_button.rs b/crates/ui2/src/components/icon_button.rs index f0dc85b445c326f801d5579d5f4002690e617b75..91653ea8cdd0158d88294886b826c680b5cbf9b3 100644 --- a/crates/ui2/src/components/icon_button.rs +++ b/crates/ui2/src/components/icon_button.rs @@ -98,6 +98,7 @@ impl IconButton { if let Some(click_handler) = self.handlers.click.clone() { button = button.on_mouse_down(MouseButton::Left, move |state, event, cx| { + cx.stop_propagation(); click_handler(state, cx); }); } From b029083441bcb130762467e14f931d206498d489 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 9 Nov 2023 19:03:10 +0100 Subject: [PATCH 03/15] Start on rendering context menu in editor2 Co-Authored-By: Nathan Co-Authored-By: Mikayla --- crates/editor2/src/editor.rs | 59 ++++++++--------- crates/editor2/src/element.rs | 118 +++++++++++++++++----------------- crates/gpui2/src/window.rs | 22 ++++--- 3 files changed, 100 insertions(+), 99 deletions(-) diff --git a/crates/editor2/src/editor.rs b/crates/editor2/src/editor.rs index 752696bcaef3752a8acf0df0516c1ed742978a9e..11ec3e0879c0c0f2a4071e76e0949ab869bc9927 100644 --- a/crates/editor2/src/editor.rs +++ b/crates/editor2/src/editor.rs @@ -920,15 +920,14 @@ impl ContextMenu { fn render( &self, cursor_position: DisplayPoint, - style: EditorStyle, + style: &EditorStyle, workspace: Option>, cx: &mut ViewContext, ) -> (DisplayPoint, AnyElement) { - todo!() - // match self { - // ContextMenu::Completions(menu) => (cursor_position, menu.render(style, workspace, cx)), - // ContextMenu::CodeActions(menu) => menu.render(cursor_position, style, cx), - // } + match self { + ContextMenu::Completions(menu) => (cursor_position, menu.render(style, workspace, cx)), + ContextMenu::CodeActions(menu) => menu.render(cursor_position, style, cx), + } } } @@ -1253,13 +1252,13 @@ impl CompletionsMenu { fn render( &self, - style: EditorStyle, + style: &EditorStyle, workspace: Option>, cx: &mut ViewContext, - ) { + ) -> AnyElement { todo!("old implementation below") } - // ) -> AnyElement { + // enum CompletionTag {} // let settings = EditorSettings>(cx); @@ -1572,7 +1571,7 @@ impl CodeActionsMenu { fn render( &self, mut cursor_position: DisplayPoint, - style: EditorStyle, + style: &EditorStyle, cx: &mut ViewContext, ) -> (DisplayPoint, AnyElement) { todo!("old version below") @@ -4480,29 +4479,27 @@ impl Editor { // } pub fn context_menu_visible(&self) -> bool { - false - // todo!("context menu") - // self.context_menu - // .read() - // .as_ref() - // .map_or(false, |menu| menu.visible()) + self.context_menu + .read() + .as_ref() + .map_or(false, |menu| menu.visible()) } - // pub fn render_context_menu( - // &self, - // cursor_position: DisplayPoint, - // style: EditorStyle, - // cx: &mut ViewContext, - // ) -> Option<(DisplayPoint, AnyElement)> { - // self.context_menu.read().as_ref().map(|menu| { - // menu.render( - // cursor_position, - // style, - // self.workspace.as_ref().map(|(w, _)| w.clone()), - // cx, - // ) - // }) - // } + pub fn render_context_menu( + &self, + cursor_position: DisplayPoint, + style: &EditorStyle, + cx: &mut ViewContext, + ) -> Option<(DisplayPoint, AnyElement)> { + self.context_menu.read().as_ref().map(|menu| { + menu.render( + cursor_position, + style, + self.workspace.as_ref().map(|(w, _)| w.clone()), + cx, + ) + }) + } fn hide_context_menu(&mut self, cx: &mut ViewContext) -> Option { cx.notify(); diff --git a/crates/editor2/src/element.rs b/crates/editor2/src/element.rs index a447b5647c48eefd9b9c10f9e130d7d8e4d705a6..c444da718cbccb177c91b5241f53cc125c6c6430 100644 --- a/crates/editor2/src/element.rs +++ b/crates/editor2/src/element.rs @@ -603,7 +603,7 @@ impl EditorElement { fn paint_text( &mut self, bounds: Bounds, - layout: &LayoutState, + layout: &mut LayoutState, editor: &mut Editor, cx: &mut ViewContext, ) { @@ -794,48 +794,46 @@ impl EditorElement { ) } - cx.stack(0, |cx| { + cx.with_z_index(0, |cx| { for cursor in cursors { cursor.paint(content_origin, cx); } }); - // cx.scene().push_layer(Some(bounds)); - - // cx.scene().pop_layer(); - - // if let Some((position, context_menu)) = layout.context_menu.as_mut() { - // cx.scene().push_stacking_context(None, None); - // 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().x; - // let list_height = context_menu.size().y; - - // // 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.window_size().x { - // list_origin.set_x((cx.window_size().x - list_width).max(0.)); - // } - // if list_origin.y + list_height > bounds.max_y { - // list_origin - // .set_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::Definite(cx.viewport_size().width * 0.7), + 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); + } - // context_menu.paint( - // list_origin, - // Bounds::::from_points( - // gpui::Point::::zero(), - // point(f32::MAX, f32::MAX), - // ), // Let content bleed outside of editor - // editor, - // cx, - // ); + if list_origin.y + list_height > bounds.lower_right().y { + list_origin.y -= layout.position_map.line_height - list_height; + } - // cx.scene().pop_stacking_context(); - // } + context_menu.draw(list_origin, available_space, editor, cx); + }) + } // if let Some((position, hover_popovers)) = layout.hover_popovers.as_mut() { // cx.scene().push_stacking_context(None, None); @@ -1781,15 +1779,14 @@ impl EditorElement { snapshot = editor.snapshot(cx); } - // todo!("context menu") - // let mut context_menu = None; + let mut context_menu = None; let mut code_actions_indicator = None; if let Some(newest_selection_head) = newest_selection_head { if (start_row..end_row).contains(&newest_selection_head.row()) { - // if editor.context_menu_visible() { - // context_menu = - // editor.render_context_menu(newest_selection_head, style.clone(), cx); - // } + if editor.context_menu_visible() { + context_menu = + editor.render_context_menu(newest_selection_head, &self.style, cx); + } let active = matches!( editor.context_menu.read().as_ref(), @@ -1939,7 +1936,7 @@ impl EditorElement { display_hunks, // blocks, selections, - // context_menu, + context_menu, code_actions_indicator, // fold_indicators, tab_invisible, @@ -2501,21 +2498,24 @@ impl Element for EditorElement { size: layout.text_size, }; - cx.with_content_mask(ContentMask { bounds }, |cx| { - self.paint_mouse_listeners( - bounds, - gutter_bounds, - text_bounds, - &layout.position_map, - cx, - ); - 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); - } - self.paint_text(text_bounds, &layout, editor, cx); - let input_handler = ElementInputHandler::new(bounds, cx); - cx.handle_input(&editor.focus_handle, input_handler); + // We call with_z_index to establish a new stacking context. + cx.with_z_index(0, |cx| { + cx.with_content_mask(ContentMask { bounds }, |cx| { + self.paint_mouse_listeners( + bounds, + gutter_bounds, + text_bounds, + &layout.position_map, + cx, + ); + 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); + } + self.paint_text(text_bounds, &mut layout, editor, cx); + let input_handler = ElementInputHandler::new(bounds, cx); + cx.handle_input(&editor.focus_handle, input_handler); + }); }); } } @@ -3141,7 +3141,7 @@ pub struct LayoutState { show_scrollbars: bool, is_singleton: bool, max_row: u32, - // context_menu: Option<(DisplayPoint, AnyElement)>, + context_menu: Option<(DisplayPoint, AnyElement)>, code_actions_indicator: Option, // hover_popovers: Option<(DisplayPoint, Vec>)>, // fold_indicators: Vec>>, diff --git a/crates/gpui2/src/window.rs b/crates/gpui2/src/window.rs index 7b5ce5b7fc418d612a90f64c23ebf44ecc6a1b10..4c81bae5f329358fe6b9ae4349a636f56fc34b6c 100644 --- a/crates/gpui2/src/window.rs +++ b/crates/gpui2/src/window.rs @@ -200,7 +200,7 @@ pub struct Window { display_id: DisplayId, sprite_atlas: Arc, rem_size: Pixels, - content_size: Size, + viewport_size: Size, pub(crate) layout_engine: TaffyLayoutEngine, pub(crate) root_view: Option, pub(crate) element_id_stack: GlobalElementId, @@ -299,7 +299,7 @@ impl Window { display_id, sprite_atlas, rem_size: px(16.), - content_size, + viewport_size: content_size, layout_engine: TaffyLayoutEngine::new(), root_view: None, element_id_stack: GlobalElementId::default(), @@ -609,7 +609,7 @@ impl<'a> WindowContext<'a> { fn window_bounds_changed(&mut self) { self.window.scale_factor = self.window.platform_window.scale_factor(); - self.window.content_size = self.window.platform_window.content_size(); + self.window.viewport_size = self.window.platform_window.content_size(); self.window.bounds = self.window.platform_window.bounds(); self.window.display_id = self.window.platform_window.display().id(); self.window.dirty = true; @@ -624,6 +624,10 @@ impl<'a> WindowContext<'a> { self.window.bounds } + pub fn viewport_size(&self) -> Size { + self.window.viewport_size + } + pub fn is_window_active(&self) -> bool { self.window.active } @@ -717,7 +721,7 @@ impl<'a> WindowContext<'a> { /// Called during painting to invoke the given closure in a new stacking context. The given /// z-index is interpreted relative to the previous call to `stack`. - pub fn stack(&mut self, z_index: u32, f: impl FnOnce(&mut Self) -> R) -> R { + pub fn with_z_index(&mut self, z_index: u32, f: impl FnOnce(&mut Self) -> R) -> R { self.window.current_frame.z_index_stack.push(z_index); let result = f(self); self.window.current_frame.z_index_stack.pop(); @@ -1015,13 +1019,13 @@ impl<'a> WindowContext<'a> { self.start_frame(); - self.stack(0, |cx| { - let available_space = cx.window.content_size.map(Into::into); + self.with_z_index(0, |cx| { + let available_space = cx.window.viewport_size.map(Into::into); root_view.draw(available_space, cx); }); if let Some(active_drag) = self.app.active_drag.take() { - self.stack(1, |cx| { + self.with_z_index(1, |cx| { let offset = cx.mouse_position() - active_drag.cursor_offset; cx.with_element_offset(Some(offset), |cx| { let available_space = @@ -1031,7 +1035,7 @@ impl<'a> WindowContext<'a> { }); }); } else if let Some(active_tooltip) = self.app.active_tooltip.take() { - self.stack(1, |cx| { + self.with_z_index(1, |cx| { cx.with_element_offset(Some(active_tooltip.cursor_offset), |cx| { let available_space = size(AvailableSpace::MinContent, AvailableSpace::MinContent); @@ -1686,7 +1690,7 @@ pub trait BorrowWindow: BorrowMut + BorrowMut { .unwrap_or_else(|| ContentMask { bounds: Bounds { origin: Point::default(), - size: self.window().content_size, + size: self.window().viewport_size, }, }) } From 1a0ddc424bd21f2e2e9de02a73f776054d87c13e Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 9 Nov 2023 19:11:17 +0100 Subject: [PATCH 04/15] WIP --- crates/editor2/src/editor.rs | 43 ++++++++--------------- crates/gpui2/src/elements/uniform_list.rs | 2 +- 2 files changed, 15 insertions(+), 30 deletions(-) diff --git a/crates/editor2/src/editor.rs b/crates/editor2/src/editor.rs index 11ec3e0879c0c0f2a4071e76e0949ab869bc9927..5185df9b2f67255824a10fbc8b8023e1e25f615f 100644 --- a/crates/editor2/src/editor.rs +++ b/crates/editor2/src/editor.rs @@ -42,7 +42,8 @@ use gpui::{ action, actions, point, px, relative, rems, size, AnyElement, AppContext, BackgroundExecutor, Bounds, ClipboardItem, Component, Context, DispatchContext, EventEmitter, FocusHandle, FontFeatures, FontStyle, FontWeight, HighlightStyle, Hsla, InputHandler, Model, Pixels, Render, - Subscription, Task, TextStyle, View, ViewContext, VisualContext, WeakView, WindowContext, + Subscription, Task, TextStyle, UniformListScrollHandle, View, ViewContext, VisualContext, + WeakView, WindowContext, }; use highlight_matching_bracket::refresh_matching_bracket_highlights; use hover_popover::{hide_hover, HoverState}; @@ -940,29 +941,13 @@ struct CompletionsMenu { match_candidates: Arc<[StringMatchCandidate]>, matches: Arc<[StringMatch]>, selected_item: usize, - list: UniformListState, -} - -// todo!(this is fake) -#[derive(Clone, Default)] -struct UniformListState; - -// todo!(this is fake) -impl UniformListState { - pub fn scroll_to(&mut self, target: ScrollTarget) {} -} - -// todo!(this is somewhat fake) -#[derive(Debug)] -pub enum ScrollTarget { - Show(usize), - Center(usize), + scroll_handle: UniformListScrollHandle, } impl CompletionsMenu { fn select_first(&mut self, project: Option<&Model>, cx: &mut ViewContext) { self.selected_item = 0; - self.list.scroll_to(ScrollTarget::Show(self.selected_item)); + self.scroll_handle.scroll_to_item(self.selected_item); self.attempt_resolve_selected_completion_documentation(project, cx); cx.notify(); } @@ -973,7 +958,7 @@ impl CompletionsMenu { } else { self.selected_item = self.matches.len() - 1; } - self.list.scroll_to(ScrollTarget::Show(self.selected_item)); + self.scroll_handle.scroll_to_item(self.selected_item); self.attempt_resolve_selected_completion_documentation(project, cx); cx.notify(); } @@ -984,14 +969,14 @@ impl CompletionsMenu { } else { self.selected_item = 0; } - self.list.scroll_to(ScrollTarget::Show(self.selected_item)); + self.scroll_handle.scroll_to_item(self.selected_item); self.attempt_resolve_selected_completion_documentation(project, cx); cx.notify(); } fn select_last(&mut self, project: Option<&Model>, cx: &mut ViewContext) { self.selected_item = self.matches.len() - 1; - self.list.scroll_to(ScrollTarget::Show(self.selected_item)); + self.scroll_handle.scroll_to_item(self.selected_item); self.attempt_resolve_selected_completion_documentation(project, cx); cx.notify(); } @@ -1527,14 +1512,14 @@ struct CodeActionsMenu { actions: Arc<[CodeAction]>, buffer: Model, selected_item: usize, - list: UniformListState, + scroll_handle: UniformListScrollHandle, deployed_from_indicator: bool, } impl CodeActionsMenu { fn select_first(&mut self, cx: &mut ViewContext) { self.selected_item = 0; - self.list.scroll_to(ScrollTarget::Show(self.selected_item)); + self.scroll_handle.scroll_to_item(self.selected_item); cx.notify() } @@ -1544,7 +1529,7 @@ impl CodeActionsMenu { } else { self.selected_item = self.actions.len() - 1; } - self.list.scroll_to(ScrollTarget::Show(self.selected_item)); + self.scroll_handle.scroll_to_item(self.selected_item); cx.notify(); } @@ -1554,13 +1539,13 @@ impl CodeActionsMenu { } else { self.selected_item = 0; } - self.list.scroll_to(ScrollTarget::Show(self.selected_item)); + self.scroll_handle.scroll_to_item(self.selected_item); cx.notify(); } fn select_last(&mut self, cx: &mut ViewContext) { self.selected_item = self.actions.len() - 1; - self.list.scroll_to(ScrollTarget::Show(self.selected_item)); + self.scroll_handle.scroll_to_item(self.selected_item); cx.notify() } @@ -3660,7 +3645,7 @@ impl Editor { completions: Arc::new(RwLock::new(completions.into())), matches: Vec::new().into(), selected_item: 0, - list: Default::default(), + scroll_handle: UniformListScrollHandle::new(), }; menu.filter(query.as_deref(), cx.background_executor().clone()) .await; @@ -3873,7 +3858,7 @@ impl Editor { buffer, actions, selected_item: Default::default(), - list: Default::default(), + scroll_handle: UniformListScrollHandle::default(), deployed_from_indicator, })); } diff --git a/crates/gpui2/src/elements/uniform_list.rs b/crates/gpui2/src/elements/uniform_list.rs index a4524d5496b63cacd631ff1ab414dede29bc6086..679c420cc33edef9658326239a6e18d8c4182e98 100644 --- a/crates/gpui2/src/elements/uniform_list.rs +++ b/crates/gpui2/src/elements/uniform_list.rs @@ -50,7 +50,7 @@ pub struct UniformList { scroll_handle: Option, } -#[derive(Clone)] +#[derive(Clone, Default)] pub struct UniformListScrollHandle(Arc>>); #[derive(Clone, Debug)] From 23fd1e19dc4e083d50661e1b2aa64b85e4cd21d1 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 10 Nov 2023 11:35:57 +0100 Subject: [PATCH 05/15] Ignore element offset when manually drawing `AnyElement` --- crates/gpui2/src/element.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/crates/gpui2/src/element.rs b/crates/gpui2/src/element.rs index e7526dfa3a74d444bf6747d51a8b98a1de85d9cf..9ee9eaa7c335960f3e3c6974b0a8798c3d13f9c4 100644 --- a/crates/gpui2/src/element.rs +++ b/crates/gpui2/src/element.rs @@ -246,12 +246,15 @@ where fn draw( &mut self, - origin: Point, + mut origin: Point, available_space: Size, view_state: &mut V, cx: &mut ViewContext, ) { self.measure(available_space, view_state, cx); + // Ignore the element offset when drawing this element, as the origin is already specified + // in absolute terms. + origin -= cx.element_offset(); cx.with_element_offset(Some(origin), |cx| self.paint(view_state, cx)) } } From a30b47aa5f963b8ac04820cd11dc4c3e611308c2 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 10 Nov 2023 12:26:35 +0100 Subject: [PATCH 06/15] Show a very basic code actions menu --- crates/editor2/src/editor.rs | 37 ++++++++++++++++++++++++++++------- crates/editor2/src/element.rs | 2 +- 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/crates/editor2/src/editor.rs b/crates/editor2/src/editor.rs index 5185df9b2f67255824a10fbc8b8023e1e25f615f..c3a4059e6c26d46befb0901074fb342d57251340 100644 --- a/crates/editor2/src/editor.rs +++ b/crates/editor2/src/editor.rs @@ -39,11 +39,11 @@ use futures::FutureExt; use fuzzy::{StringMatch, StringMatchCandidate}; use git::diff_hunk_to_display; use gpui::{ - action, actions, point, px, relative, rems, size, AnyElement, AppContext, BackgroundExecutor, - Bounds, ClipboardItem, Component, Context, DispatchContext, EventEmitter, FocusHandle, - FontFeatures, FontStyle, FontWeight, HighlightStyle, Hsla, InputHandler, Model, Pixels, Render, - Subscription, Task, TextStyle, UniformListScrollHandle, View, ViewContext, VisualContext, - WeakView, WindowContext, + action, actions, div, point, px, relative, rems, size, uniform_list, AnyElement, AppContext, + BackgroundExecutor, Bounds, ClipboardItem, Component, Context, DispatchContext, EventEmitter, + FocusHandle, FontFeatures, FontStyle, FontWeight, HighlightStyle, Hsla, InputHandler, Model, + ParentElement, 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}; @@ -1559,7 +1559,30 @@ impl CodeActionsMenu { style: &EditorStyle, cx: &mut ViewContext, ) -> (DisplayPoint, AnyElement) { - todo!("old version below") + let actions = self.actions.clone(); + let selected_item = self.selected_item; + let element = uniform_list( + "code_actions_menu", + self.actions.len(), + move |editor, range, cx| { + actions[range.clone()] + .iter() + .enumerate() + .map(|(ix, action)| { + let item_ix = range.start + ix; + div().child(action.lsp_action.title.clone()) + }) + .collect() + }, + ) + .bg(gpui::red()) + .render(); + + if self.deployed_from_indicator { + *cursor_position.column_mut() = 0; + } + + (cursor_position, element) } // enum ActionTag {} @@ -4383,7 +4406,7 @@ impl Editor { ) -> Option> { if self.available_code_actions.is_some() { Some( - IconButton::new("code_actions", ui2::Icon::Bolt) + IconButton::new("code_actions_indicator", ui2::Icon::Bolt) .on_click(|editor: &mut Editor, cx| { editor.toggle_code_actions( &ToggleCodeActions { diff --git a/crates/editor2/src/element.rs b/crates/editor2/src/element.rs index c444da718cbccb177c91b5241f53cc125c6c6430..ed38bb8a7972a086c7cc05c5949eb731535139f1 100644 --- a/crates/editor2/src/element.rs +++ b/crates/editor2/src/element.rs @@ -804,7 +804,7 @@ impl EditorElement { cx.with_z_index(1, |cx| { let line_height = self.style.text.line_height_in_pixels(cx.rem_size()); let available_space = size( - AvailableSpace::Definite(cx.viewport_size().width * 0.7), + AvailableSpace::MinContent, AvailableSpace::Definite( (12. * line_height).min((bounds.size.height - line_height) / 2.), ), From 6929a7182751acbb57ca4cb91d713d875a47480e Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 10 Nov 2023 13:03:39 +0100 Subject: [PATCH 07/15] Ceil measured width for Text element --- crates/gpui2/src/elements/text.rs | 7 ++++++- crates/gpui2/src/geometry.rs | 4 ++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/crates/gpui2/src/elements/text.rs b/crates/gpui2/src/elements/text.rs index e258d3e7dc6dba8c7d7c625981b7ef340c1dc96c..5c5709d32e5a12e247726789a35dfd21806c6c7c 100644 --- a/crates/gpui2/src/elements/text.rs +++ b/crates/gpui2/src/elements/text.rs @@ -101,7 +101,12 @@ impl Element for Text { .map(|line| line.wrap_count() + 1) .sum::(); let size = Size { - width: lines.iter().map(|line| line.layout.width).max().unwrap(), + width: lines + .iter() + .map(|line| line.layout.width) + .max() + .unwrap() + .ceil(), height: line_height * line_count, }; diff --git a/crates/gpui2/src/geometry.rs b/crates/gpui2/src/geometry.rs index f290c6a81c1b147c78940465e6fa2ffb63c9964f..e07300951ec61429ba617927649406409e74b531 100644 --- a/crates/gpui2/src/geometry.rs +++ b/crates/gpui2/src/geometry.rs @@ -785,6 +785,10 @@ impl Pixels { Self(self.0.round()) } + pub fn ceil(&self) -> Self { + Self(self.0.ceil()) + } + pub fn scale(&self, factor: f32) -> ScaledPixels { ScaledPixels(self.0 * factor) } From 1d371913202f3f72f01fbd3e432644802fc72e56 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 10 Nov 2023 14:35:55 +0100 Subject: [PATCH 08/15] Ensure UniformList style is painted beneath its items --- crates/gpui2/src/elements/uniform_list.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/gpui2/src/elements/uniform_list.rs b/crates/gpui2/src/elements/uniform_list.rs index 6436d73d6849a9793d2500eb0c8ba3aec70adc8e..bc5b807b202dcde26e7a4c39a1d9397c70ddb7e3 100644 --- a/crates/gpui2/src/elements/uniform_list.rs +++ b/crates/gpui2/src/elements/uniform_list.rs @@ -158,7 +158,6 @@ impl Element for UniformList { cx: &mut ViewContext, ) { let style = self.computed_style(); - style.paint(bounds, cx); let border = style.border_widths.to_pixels(cx.rem_size()); let padding = style.padding.to_pixels(bounds.size.into(), cx.rem_size()); @@ -170,6 +169,8 @@ impl Element for UniformList { ); cx.with_z_index(style.z_index.unwrap_or(0), |cx| { + style.paint(bounds, cx); + let content_size; if self.item_count > 0 { let item_height = self From c76fd930150a4c721966d2f969d89e5d4e75ff86 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 10 Nov 2023 15:17:17 +0100 Subject: [PATCH 09/15] Use padded bounds to draw uniform list items --- crates/gpui2/src/elements/uniform_list.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/gpui2/src/elements/uniform_list.rs b/crates/gpui2/src/elements/uniform_list.rs index bc5b807b202dcde26e7a4c39a1d9397c70ddb7e3..342cd05471ae0daba23adc0371bcc2823829bcd7 100644 --- a/crates/gpui2/src/elements/uniform_list.rs +++ b/crates/gpui2/src/elements/uniform_list.rs @@ -211,7 +211,7 @@ impl Element for UniformList { let item_origin = padded_bounds.origin + point(px(0.), item_height * ix + scroll_offset); let available_space = size( - AvailableSpace::Definite(bounds.size.width), + AvailableSpace::Definite(padded_bounds.size.width), AvailableSpace::Definite(item_height), ); item.draw(item_origin, available_space, view_state, cx); From bf576d47b1ede8744c750589996fe810a1416380 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 10 Nov 2023 15:17:32 +0100 Subject: [PATCH 10/15] Make code actions menu prettier --- crates/editor2/src/editor.rs | 65 +++++++++++++++++++++++------------- 1 file changed, 41 insertions(+), 24 deletions(-) diff --git a/crates/editor2/src/editor.rs b/crates/editor2/src/editor.rs index c3a4059e6c26d46befb0901074fb342d57251340..26ec5279be7a5d952e434f0e2a516af9e392cb83 100644 --- a/crates/editor2/src/editor.rs +++ b/crates/editor2/src/editor.rs @@ -42,8 +42,8 @@ use gpui::{ action, actions, div, point, px, relative, rems, size, uniform_list, AnyElement, AppContext, BackgroundExecutor, Bounds, ClipboardItem, Component, Context, DispatchContext, EventEmitter, FocusHandle, FontFeatures, FontStyle, FontWeight, HighlightStyle, Hsla, InputHandler, Model, - ParentElement, Pixels, Render, Styled, Subscription, Task, TextStyle, UniformListScrollHandle, - View, ViewContext, VisualContext, WeakView, WindowContext, + ParentElement, Pixels, Render, StatelessInteractive, Styled, Subscription, Task, TextStyle, + UniformListScrollHandle, View, ViewContext, VisualContext, WeakView, WindowContext, }; use highlight_matching_bracket::refresh_matching_bracket_highlights; use hover_popover::{hide_hover, HoverState}; @@ -1570,12 +1570,29 @@ impl CodeActionsMenu { .enumerate() .map(|(ix, action)| { let item_ix = range.start + ix; - div().child(action.lsp_action.title.clone()) + let selected = selected_item == item_ix; + let colors = cx.theme().colors(); + div() + .px_2() + .text_color(colors.text) + .when(selected, |style| { + style + .bg(colors.element_active) + .text_color(colors.text_accent) + }) + .hover(|style| { + style + .bg(colors.element_hover) + .text_color(colors.text_accent) + }) + .child(action.lsp_action.title.clone()) }) .collect() }, ) - .bg(gpui::red()) + .bg(cx.theme().colors().element_background) + .px_2() + .py_1() .render(); if self.deployed_from_indicator { @@ -5948,29 +5965,29 @@ impl Editor { }); } - // pub fn context_menu_first(&mut self, _: &ContextMenuFirst, cx: &mut ViewContext) { - // if let Some(context_menu) = self.context_menu.write().as_mut() { - // context_menu.select_first(self.project.as_ref(), cx); - // } - // } + pub fn context_menu_first(&mut self, _: &ContextMenuFirst, cx: &mut ViewContext) { + if let Some(context_menu) = self.context_menu.write().as_mut() { + context_menu.select_first(self.project.as_ref(), cx); + } + } - // pub fn context_menu_prev(&mut self, _: &ContextMenuPrev, cx: &mut ViewContext) { - // if let Some(context_menu) = self.context_menu.write().as_mut() { - // context_menu.select_prev(self.project.as_ref(), cx); - // } - // } + pub fn context_menu_prev(&mut self, _: &ContextMenuPrev, cx: &mut ViewContext) { + if let Some(context_menu) = self.context_menu.write().as_mut() { + context_menu.select_prev(self.project.as_ref(), cx); + } + } - // pub fn context_menu_next(&mut self, _: &ContextMenuNext, cx: &mut ViewContext) { - // if let Some(context_menu) = self.context_menu.write().as_mut() { - // context_menu.select_next(self.project.as_ref(), cx); - // } - // } + pub fn context_menu_next(&mut self, _: &ContextMenuNext, cx: &mut ViewContext) { + if let Some(context_menu) = self.context_menu.write().as_mut() { + context_menu.select_next(self.project.as_ref(), cx); + } + } - // pub fn context_menu_last(&mut self, _: &ContextMenuLast, cx: &mut ViewContext) { - // if let Some(context_menu) = self.context_menu.write().as_mut() { - // context_menu.select_last(self.project.as_ref(), cx); - // } - // } + pub fn context_menu_last(&mut self, _: &ContextMenuLast, cx: &mut ViewContext) { + if let Some(context_menu) = self.context_menu.write().as_mut() { + context_menu.select_last(self.project.as_ref(), cx); + } + } pub fn move_to_previous_word_start( &mut self, From fb450e35f760bf64fe33521de16a96851060f909 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 10 Nov 2023 15:40:23 +0100 Subject: [PATCH 11/15] Wire up keyboard interaction in code actions menu --- crates/editor2/src/editor.rs | 226 +++++++++++++++++----------------- crates/editor2/src/element.rs | 12 +- 2 files changed, 126 insertions(+), 112 deletions(-) diff --git a/crates/editor2/src/editor.rs b/crates/editor2/src/editor.rs index 26ec5279be7a5d952e434f0e2a516af9e392cb83..4fafedbfcce2c27c0dbde0e12dad5da921d2d406 100644 --- a/crates/editor2/src/editor.rs +++ b/crates/editor2/src/editor.rs @@ -40,10 +40,11 @@ use fuzzy::{StringMatch, StringMatchCandidate}; use git::diff_hunk_to_display; use gpui::{ action, actions, div, point, px, relative, rems, size, uniform_list, AnyElement, AppContext, - BackgroundExecutor, Bounds, ClipboardItem, Component, Context, DispatchContext, EventEmitter, - FocusHandle, FontFeatures, FontStyle, FontWeight, HighlightStyle, Hsla, InputHandler, Model, - ParentElement, Pixels, Render, StatelessInteractive, Styled, Subscription, Task, TextStyle, - UniformListScrollHandle, View, ViewContext, VisualContext, WeakView, WindowContext, + AsyncWindowContext, BackgroundExecutor, Bounds, ClipboardItem, Component, Context, + DispatchContext, EventEmitter, FocusHandle, FontFeatures, FontStyle, FontWeight, + HighlightStyle, Hsla, InputHandler, Model, ParentElement, Pixels, Render, StatelessInteractive, + Styled, Subscription, Task, TextStyle, UniformListScrollHandle, View, ViewContext, + VisualContext, WeakView, WindowContext, }; use highlight_matching_bracket::refresh_matching_bracket_highlights; use hover_popover::{hide_hover, HoverState}; @@ -68,7 +69,7 @@ pub use multi_buffer::{ }; use ordered_float::OrderedFloat; use parking_lot::{Mutex, RwLock}; -use project::{FormatTrigger, Location, Project}; +use project::{FormatTrigger, Location, Project, ProjectTransaction}; use rand::prelude::*; use rpc::proto::*; use scroll::{ @@ -3901,6 +3902,7 @@ impl Editor { scroll_handle: UniformListScrollHandle::default(), deployed_from_indicator, })); + cx.notify(); } } })?; @@ -3910,117 +3912,121 @@ impl Editor { .detach_and_log_err(cx); } - // pub fn confirm_code_action( - // workspace: &mut Workspace, - // action: &ConfirmCodeAction, - // cx: &mut ViewContext, - // ) -> Option>> { - // let editor = workspace.active_item(cx)?.act_as::(cx)?; - // let actions_menu = if let ContextMenu::CodeActions(menu) = - // editor.update(cx, |editor, cx| editor.hide_context_menu(cx))? - // { - // menu - // } else { - // return None; - // }; - // let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item); - // let action = actions_menu.actions.get(action_ix)?.clone(); - // let title = action.lsp_action.title.clone(); - // let buffer = actions_menu.buffer; - - // let apply_code_actions = workspace.project().clone().update(cx, |project, cx| { - // project.apply_code_action(buffer, action, true, cx) - // }); - // let editor = editor.downgrade(); - // Some(cx.spawn(|workspace, cx| async move { - // let project_transaction = apply_code_actions.await?; - // Self::open_project_transaction(&editor, workspace, project_transaction, title, cx).await - // })) - // } - - // async fn open_project_transaction( - // this: &WeakViewHandle Result<()> { - // let replica_id = this.read_with(&cx, |this, cx| this.replica_id(cx))?; - - // let mut entries = transaction.0.into_iter().collect::>(); - // entries.sort_unstable_by_key(|(buffer, _)| { - // buffer.read_with(&cx, |buffer, _| buffer.file().map(|f| f.path().clone())) - // }); + pub fn confirm_code_action( + &mut self, + action: &ConfirmCodeAction, + cx: &mut ViewContext, + ) -> Option>> { + let actions_menu = if let ContextMenu::CodeActions(menu) = self.hide_context_menu(cx)? { + menu + } else { + return None; + }; + let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item); + let action = actions_menu.actions.get(action_ix)?.clone(); + let title = action.lsp_action.title.clone(); + let buffer = actions_menu.buffer; + let workspace = self.workspace()?; - // // If the project transaction's edits are all contained within this editor, then - // // avoid opening a new editor to display them. + let apply_code_actions = workspace + .read(cx) + .project() + .clone() + .update(cx, |project, cx| { + project.apply_code_action(buffer, action, true, cx) + }); + let workspace = workspace.downgrade(); + Some(cx.spawn(|editor, cx| async move { + let project_transaction = apply_code_actions.await?; + Self::open_project_transaction(&editor, workspace, project_transaction, title, cx).await + })) + } - // if let Some((buffer, transaction)) = entries.first() { - // if entries.len() == 1 { - // let excerpt = this.read_with(&cx, |editor, cx| { - // editor - // .buffer() - // .read(cx) - // .excerpt_containing(editor.selections.newest_anchor().head(), cx) - // })?; - // if let Some((_, excerpted_buffer, excerpt_range)) = excerpt { - // if excerpted_buffer == *buffer { - // let all_edits_within_excerpt = buffer.read_with(&cx, |buffer, _| { - // let excerpt_range = excerpt_range.to_offset(buffer); - // buffer - // .edited_ranges_for_transaction::(transaction) - // .all(|range| { - // excerpt_range.start <= range.start - // && excerpt_range.end >= range.end - // }) - // }); + async fn open_project_transaction( + this: &WeakView, + workspace: WeakView, + transaction: ProjectTransaction, + title: String, + mut cx: AsyncWindowContext, + ) -> Result<()> { + let replica_id = this.update(&mut cx, |this, cx| this.replica_id(cx))?; - // if all_edits_within_excerpt { - // return Ok(()); - // } - // } - // } - // } - // } else { - // return Ok(()); - // } + let mut entries = transaction.0.into_iter().collect::>(); + cx.update(|_, cx| { + entries.sort_unstable_by_key(|(buffer, _)| { + buffer.read(cx).file().map(|f| f.path().clone()) + }); + })?; + + // If the project transaction's edits are all contained within this editor, then + // avoid opening a new editor to display them. + + if let Some((buffer, transaction)) = entries.first() { + if entries.len() == 1 { + let excerpt = this.update(&mut cx, |editor, cx| { + editor + .buffer() + .read(cx) + .excerpt_containing(editor.selections.newest_anchor().head(), cx) + })?; + if let Some((_, excerpted_buffer, excerpt_range)) = excerpt { + if excerpted_buffer == *buffer { + let all_edits_within_excerpt = buffer.read_with(&cx, |buffer, _| { + let excerpt_range = excerpt_range.to_offset(buffer); + buffer + .edited_ranges_for_transaction::(transaction) + .all(|range| { + excerpt_range.start <= range.start + && excerpt_range.end >= range.end + }) + })?; - // let mut ranges_to_highlight = Vec::new(); - // let excerpt_buffer = cx.build_model(|cx| { - // let mut multibuffer = MultiBuffer::new(replica_id).with_title(title); - // for (buffer_handle, transaction) in &entries { - // let buffer = buffer_handle.read(cx); - // ranges_to_highlight.extend( - // multibuffer.push_excerpts_with_context_lines( - // buffer_handle.clone(), - // buffer - // .edited_ranges_for_transaction::(transaction) - // .collect(), - // 1, - // cx, - // ), - // ); - // } - // multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx); - // multibuffer - // }); + if all_edits_within_excerpt { + return Ok(()); + } + } + } + } + } else { + return Ok(()); + } - // workspace.update(&mut cx, |workspace, cx| { - // let project = workspace.project().clone(); - // let editor = - // cx.add_view(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), cx)); - // workspace.add_item(Box::new(editor.clone()), cx); - // editor.update(cx, |editor, cx| { - // editor.highlight_background::( - // ranges_to_highlight, - // |theme| theme.editor.highlighted_line_background, - // cx, - // ); - // }); - // })?; + let mut ranges_to_highlight = Vec::new(); + let excerpt_buffer = cx.build_model(|cx| { + let mut multibuffer = MultiBuffer::new(replica_id).with_title(title); + for (buffer_handle, transaction) in &entries { + let buffer = buffer_handle.read(cx); + ranges_to_highlight.extend( + multibuffer.push_excerpts_with_context_lines( + buffer_handle.clone(), + buffer + .edited_ranges_for_transaction::(transaction) + .collect(), + 1, + cx, + ), + ); + } + multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx); + multibuffer + })?; + + workspace.update(&mut cx, |workspace, cx| { + let project = workspace.project().clone(); + let editor = + cx.build_view(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), cx)); + workspace.add_item(Box::new(editor.clone()), cx); + editor.update(cx, |editor, cx| { + editor.highlight_background::( + ranges_to_highlight, + |theme| theme.editor_highlighted_line_background, + cx, + ); + }); + })?; - // Ok(()) - // } + Ok(()) + } fn refresh_code_actions(&mut self, cx: &mut ViewContext) -> Option<()> { let project = self.project.clone()?; diff --git a/crates/editor2/src/element.rs b/crates/editor2/src/element.rs index ed38bb8a7972a086c7cc05c5949eb731535139f1..67fcbaa4ba11acf260ffad4c29f7f9c217d1f727 100644 --- a/crates/editor2/src/element.rs +++ b/crates/editor2/src/element.rs @@ -4126,7 +4126,7 @@ fn build_key_listeners( build_action_listener(Editor::unfold_at), build_action_listener(Editor::fold_selected_ranges), build_action_listener(Editor::show_completions), - // build_action_listener(Editor::toggle_code_actions), todo!() + build_action_listener(Editor::toggle_code_actions), // build_action_listener(Editor::open_excerpts), todo!() build_action_listener(Editor::toggle_soft_wrap), build_action_listener(Editor::toggle_inlay_hints), @@ -4142,13 +4142,21 @@ fn build_key_listeners( build_action_listener(Editor::restart_language_server), build_action_listener(Editor::show_character_palette), // build_action_listener(Editor::confirm_completion), todo!() - // build_action_listener(Editor::confirm_code_action), todo!() + build_action_listener(|editor, action, cx| { + editor + .confirm_code_action(action, cx) + .map(|task| task.detach_and_log_err(cx)); + }), // build_action_listener(Editor::rename), todo!() // build_action_listener(Editor::confirm_rename), todo!() // build_action_listener(Editor::find_all_references), todo!() build_action_listener(Editor::next_copilot_suggestion), build_action_listener(Editor::previous_copilot_suggestion), build_action_listener(Editor::copilot_suggest), + build_action_listener(Editor::context_menu_first), + build_action_listener(Editor::context_menu_prev), + build_action_listener(Editor::context_menu_next), + build_action_listener(Editor::context_menu_last), build_key_listener( move |editor, key_down: &KeyDownEvent, dispatch_context, phase, cx| { if phase == DispatchPhase::Bubble { From c44db3b7ecf1a4ead6190a62964911c80334a263 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 10 Nov 2023 15:57:02 +0100 Subject: [PATCH 12/15] Confirm code action on mouse down --- crates/editor2/Cargo.toml | 2 +- crates/editor2/src/editor.rs | 19 +++++++++++++++---- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/crates/editor2/Cargo.toml b/crates/editor2/Cargo.toml index 493f10006f734f5c0fd907935981d2cb9a17b9e1..e45c33d91759f2e7d21283e45d1011cdd26abcb8 100644 --- a/crates/editor2/Cargo.toml +++ b/crates/editor2/Cargo.toml @@ -44,7 +44,7 @@ snippet = { path = "../snippet" } sum_tree = { path = "../sum_tree" } text = { package="text2", path = "../text2" } theme = { package="theme2", path = "../theme2" } -ui2 = { package = "ui2", path = "../ui2" } +ui = { package = "ui2", path = "../ui2" } util = { path = "../util" } sqlez = { path = "../sqlez" } workspace = { package = "workspace2", path = "../workspace2" } diff --git a/crates/editor2/src/editor.rs b/crates/editor2/src/editor.rs index 4fafedbfcce2c27c0dbde0e12dad5da921d2d406..5e00faf3d20d8e0afcbdb3cfc9a679dab2fed8e6 100644 --- a/crates/editor2/src/editor.rs +++ b/crates/editor2/src/editor.rs @@ -42,9 +42,9 @@ use gpui::{ action, actions, div, point, px, relative, rems, size, uniform_list, AnyElement, AppContext, AsyncWindowContext, BackgroundExecutor, Bounds, ClipboardItem, Component, Context, DispatchContext, EventEmitter, FocusHandle, FontFeatures, FontStyle, FontWeight, - HighlightStyle, Hsla, InputHandler, Model, ParentElement, Pixels, Render, StatelessInteractive, - Styled, Subscription, Task, TextStyle, UniformListScrollHandle, View, ViewContext, - VisualContext, WeakView, WindowContext, + HighlightStyle, Hsla, InputHandler, Model, MouseButton, ParentElement, Pixels, Render, + StatelessInteractive, Styled, Subscription, Task, TextStyle, UniformListScrollHandle, View, + ViewContext, VisualContext, WeakView, WindowContext, }; use highlight_matching_bracket::refresh_matching_bracket_highlights; use hover_popover::{hide_hover, HoverState}; @@ -1586,6 +1586,17 @@ impl CodeActionsMenu { .bg(colors.element_hover) .text_color(colors.text_accent) }) + .on_mouse_down(MouseButton::Left, move |editor: &mut Editor, _, cx| { + cx.stop_propagation(); + editor + .confirm_code_action( + &ConfirmCodeAction { + item_ix: Some(item_ix), + }, + cx, + ) + .map(|task| task.detach_and_log_err(cx)); + }) .child(action.lsp_action.title.clone()) }) .collect() @@ -4429,7 +4440,7 @@ impl Editor { ) -> Option> { if self.available_code_actions.is_some() { Some( - IconButton::new("code_actions_indicator", ui2::Icon::Bolt) + IconButton::new("code_actions_indicator", ui::Icon::Bolt) .on_click(|editor: &mut Editor, cx| { editor.toggle_code_actions( &ToggleCodeActions { From 468a014bfc0dfc51cc17210e02c7949a06e393c0 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 10 Nov 2023 15:57:48 +0100 Subject: [PATCH 13/15] Allow measuring arbitrary items in UniformList --- crates/editor2/src/editor.rs | 79 ++--------------------- crates/gpui2/src/elements/uniform_list.rs | 21 ++++-- 2 files changed, 23 insertions(+), 77 deletions(-) diff --git a/crates/editor2/src/editor.rs b/crates/editor2/src/editor.rs index 5e00faf3d20d8e0afcbdb3cfc9a679dab2fed8e6..5317e4ac2ba238f6eebeb73be250218b456497e1 100644 --- a/crates/editor2/src/editor.rs +++ b/crates/editor2/src/editor.rs @@ -1605,6 +1605,13 @@ impl CodeActionsMenu { .bg(cx.theme().colors().element_background) .px_2() .py_1() + .with_width_from_item( + self.actions + .iter() + .enumerate() + .max_by_key(|(_, action)| action.lsp_action.title.chars().count()) + .map(|(ix, _)| ix), + ) .render(); if self.deployed_from_indicator { @@ -1613,78 +1620,6 @@ impl CodeActionsMenu { (cursor_position, element) } - // enum ActionTag {} - - // let container_style = style.autocomplete.container; - // let actions = self.actions.clone(); - // let selected_item = self.selected_item; - // let element = UniformList::new( - // self.list.clone(), - // actions.len(), - // cx, - // move |_, range, items, cx| { - // let start_ix = range.start; - // for (ix, action) in actions[range].iter().enumerate() { - // let item_ix = start_ix + ix; - // items.push( - // MouseEventHandler::new::(item_ix, cx, |state, _| { - // let item_style = if item_ix == selected_item { - // style.autocomplete.selected_item - // } else if state.hovered() { - // style.autocomplete.hovered_item - // } else { - // style.autocomplete.item - // }; - - // Text::new(action.lsp_action.title.clone(), style.text.clone()) - // .with_soft_wrap(false) - // .contained() - // .with_style(item_style) - // }) - // .with_cursor_style(CursorStyle::PointingHand) - // .on_down(MouseButton::Left, move |_, this, cx| { - // let workspace = this - // .workspace - // .as_ref() - // .and_then(|(workspace, _)| workspace.upgrade(cx)); - // cx.window_context().defer(move |cx| { - // if let Some(workspace) = workspace { - // workspace.update(cx, |workspace, cx| { - // if let Some(task) = Editor::confirm_code_action( - // workspace, - // &ConfirmCodeAction { - // item_ix: Some(item_ix), - // }, - // cx, - // ) { - // task.detach_and_log_err(cx); - // } - // }); - // } - // }); - // }) - // .into_any(), - // ); - // } - // }, - // ) - // .with_width_from_item( - // self.actions - // .iter() - // .enumerate() - // .max_by_key(|(_, action)| action.lsp_action.title.chars().count()) - // .map(|(ix, _)| ix), - // ) - // .contained() - // .with_style(container_style) - // .into_any(); - - // if self.deployed_from_indicator { - // *cursor_position.column_mut() = 0; - // } - - // (cursor_position, element) - // } } pub struct CopilotState { diff --git a/crates/gpui2/src/elements/uniform_list.rs b/crates/gpui2/src/elements/uniform_list.rs index 342cd05471ae0daba23adc0371bcc2823829bcd7..6687559d1c811349a3f18a5585f2d2d4110eb16e 100644 --- a/crates/gpui2/src/elements/uniform_list.rs +++ b/crates/gpui2/src/elements/uniform_list.rs @@ -30,6 +30,7 @@ where id: id.clone(), style, item_count, + item_to_measure_index: 0, render_items: Box::new(move |view, visible_range, cx| { f(view, visible_range, cx) .into_iter() @@ -45,6 +46,7 @@ pub struct UniformList { id: ElementId, style: StyleRefinement, item_count: usize, + item_to_measure_index: usize, render_items: Box< dyn for<'a> Fn( &'a mut V, @@ -112,7 +114,7 @@ impl Element for UniformList { cx: &mut ViewContext, ) -> Self::ElementState { element_state.unwrap_or_else(|| { - let item_size = self.measure_first_item(view_state, None, cx); + let item_size = self.measure_item(view_state, None, cx); UniformListState { interactive: InteractiveElementState::default(), item_size, @@ -174,7 +176,7 @@ impl Element for UniformList { let content_size; if self.item_count > 0 { let item_height = self - .measure_first_item(view_state, Some(padded_bounds.size.width), cx) + .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 { @@ -240,14 +242,23 @@ impl Element for UniformList { } impl UniformList { - fn measure_first_item( + pub fn with_width_from_item(mut self, item_index: Option) -> Self { + self.item_to_measure_index = item_index.unwrap_or(0); + self + } + + fn measure_item( &self, view_state: &mut V, list_width: Option, cx: &mut ViewContext, ) -> Size { - let mut items = (self.render_items)(view_state, 0..1, cx); - debug_assert_eq!(items.len(), 1); + if self.item_count == 0 { + return Size::default(); + } + + let item_ix = cmp::min(self.item_to_measure_index, self.item_count - 1); + let mut items = (self.render_items)(view_state, item_ix..item_ix + 1, cx); let mut item_to_measure = items.pop().unwrap(); let available_space = size( list_width.map_or(AvailableSpace::MinContent, |width| { From 198a85437088787d01028be665f73bd5f2fcceeb Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 10 Nov 2023 16:02:08 +0100 Subject: [PATCH 14/15] Fix bad import --- crates/editor2/src/editor.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/editor2/src/editor.rs b/crates/editor2/src/editor.rs index 5317e4ac2ba238f6eebeb73be250218b456497e1..062ceeb9d98218136d4b793adfd983a2a00f8add 100644 --- a/crates/editor2/src/editor.rs +++ b/crates/editor2/src/editor.rs @@ -97,7 +97,7 @@ use text::{OffsetUtf16, Rope}; use theme::{ ActiveTheme, DiagnosticStyle, PlayerColor, SyntaxTheme, Theme, ThemeColors, ThemeSettings, }; -use ui2::IconButton; +use ui::IconButton; use util::{post_inc, RangeExt, ResultExt, TryFutureExt}; use workspace::{ item::ItemEvent, searchable::SearchEvent, ItemNavHistory, SplitDirection, ViewId, Workspace, From c2c6921734a2b768f1d43f169c6de94f23b6c76c Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 10 Nov 2023 16:03:00 +0100 Subject: [PATCH 15/15] :fire: --- crates/editor2/src/editor.rs | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/crates/editor2/src/editor.rs b/crates/editor2/src/editor.rs index 062ceeb9d98218136d4b793adfd983a2a00f8add..5bc67a57b61feb5074852bb4a4b567c121a0f968 100644 --- a/crates/editor2/src/editor.rs +++ b/crates/editor2/src/editor.rs @@ -387,26 +387,6 @@ actions!( UnfoldLines, ); -// impl_actions!( -// editor, -// [ -// SelectNext, -// SelectPrevious, -// SelectAllMatches, -// SelectToBeginningOfLine, -// SelectToEndOfLine, -// ToggleCodeActions, -// MovePageUp, -// MovePageDown, -// ConfirmCompletion, -// ConfirmCodeAction, -// ToggleComments, -// FoldAt, -// UnfoldAt, -// GutterHover -// ] -// ); - enum DocumentHighlightRead {} enum DocumentHighlightWrite {} enum InputComposition {}