diff --git a/crates/gpui2/src/elements/uniform_list.rs b/crates/gpui2/src/elements/uniform_list.rs index 7fba7ef477..e8587683d6 100644 --- a/crates/gpui2/src/elements/uniform_list.rs +++ b/crates/gpui2/src/elements/uniform_list.rs @@ -91,6 +91,14 @@ impl UniformListScrollHandle { } } } + + pub fn scroll_top(&self) -> Pixels { + if let Some(state) = &*self.0.borrow() { + -state.scroll_offset.borrow().y + } else { + Pixels::ZERO + } + } } impl Styled for UniformList { diff --git a/crates/language_tools2/src/lsp_log.rs b/crates/language_tools2/src/lsp_log.rs index ca4f25e177..3c3df41442 100644 --- a/crates/language_tools2/src/lsp_log.rs +++ b/crates/language_tools2/src/lsp_log.rs @@ -2,17 +2,16 @@ use collections::{HashMap, VecDeque}; use editor::{Editor, EditorElement, EditorEvent, MoveToEnd}; use futures::{channel::mpsc, StreamExt}; use gpui::{ - actions, div, overlay, red, AnchorCorner, AnyElement, AppContext, Context, CursorStyle, Div, - EventEmitter, FocusHandle, FocusableView, InteractiveElement, IntoElement, Model, ModelContext, - MouseButton, OverlayFitMode, ParentElement, Render, Styled, Subscription, View, ViewContext, - VisualContext, WeakModel, WindowContext, + actions, div, overlay, red, AnyElement, AppContext, Context, CursorStyle, Div, EventEmitter, + FocusHandle, FocusableView, InteractiveElement, IntoElement, Model, ModelContext, MouseButton, + MouseDownEvent, ParentElement, Render, Styled, Subscription, View, ViewContext, VisualContext, + WeakModel, WindowContext, }; use language::{LanguageServerId, LanguageServerName}; use lsp::IoKind; use project::{search::SearchQuery, Project}; use std::{borrow::Cow, sync::Arc}; -use theme::{ActiveTheme, Theme}; -use ui::{h_stack, v_stack, Label}; +use ui::{h_stack, v_stack, Checkbox, Label}; use workspace::{ item::{Item, ItemHandle}, searchable::{SearchEvent, SearchableItem, SearchableItemHandle}, @@ -83,7 +82,7 @@ actions!(debug, [OpenLanguageServerLogs]); pub fn init(cx: &mut AppContext) { let log_store = cx.build_model(|cx| LogStore::new(cx)); - cx.observe_new_views(|workspace: &mut Workspace, cx| { + cx.observe_new_views(move |workspace: &mut Workspace, cx| { let project = workspace.project(); if project.read(cx).is_local() { log_store.update(cx, |store, cx| { @@ -91,7 +90,8 @@ pub fn init(cx: &mut AppContext) { }); } - workspace.register_action(|workspace, _: &OpenLanguageServerLogs, cx| { + let log_store = log_store.clone(); + workspace.register_action(move |workspace, _: &OpenLanguageServerLogs, cx| { let project = workspace.project().read(cx); if project.is_local() { workspace.add_item( @@ -118,7 +118,7 @@ impl LogStore { if let Some(this) = this.upgrade() { this.update(&mut cx, |this, cx| { this.on_io(project, server_id, io_kind, &message, cx); - }); + })?; } } anyhow::Ok(()) @@ -130,7 +130,7 @@ impl LogStore { pub fn add_project(&mut self, project: &Model, cx: &mut ModelContext) { let weak_project = project.downgrade(); self.projects.insert( - weak_project, + project.downgrade(), ProjectState { servers: HashMap::default(), _subscriptions: [ @@ -184,7 +184,7 @@ impl LogStore { server_state._io_logs_subscription = server.as_ref().map(|server| { server.on_io(move |io_kind, message| { io_tx - .unbounded_send((weak_project, id, io_kind, message.to_string())) + .unbounded_send((weak_project.clone(), id, io_kind, message.to_string())) .ok(); }) }); @@ -197,7 +197,8 @@ impl LogStore { if let Some((project, this)) = weak_project.upgrade().zip(this.upgrade()) { this.update(&mut cx, |this, cx| { this.add_language_server_log(&project, server_id, ¶ms.message, cx); - }); + }) + .ok(); } } }) @@ -550,10 +551,10 @@ impl LspLogView { let language = language.await.ok(); buffer.update(&mut cx, |buffer, cx| { buffer.set_language(language, cx); - }); + }) } }) - .detach(); + .detach_and_log_err(cx); }); self.editor = editor; @@ -720,7 +721,6 @@ impl Render for LspLogToolbarItemView { // } fn render(&mut self, cx: &mut ViewContext) -> Div { - let theme = cx.theme().clone(); let Some(log_view) = self.log_view.as_ref() else { return div(); }; @@ -737,60 +737,60 @@ impl Render for LspLogToolbarItemView { None } }); - let server_selected = current_server.is_some(); + // todo!() styling + let _server_selected = current_server.is_some(); - enum LspLogScroll {} - enum Menu {} let lsp_menu = h_stack() .child(Self::render_language_server_menu_header(current_server, cx)) .children(if self.menu_open { Some( - overlay() - .child( - v_stack() - .scrollable::(0, None, cx) - .children(menu_rows.into_iter().map(|row| { - Self::render_language_server_menu_item( - row.server_id, - row.server_name, - &row.worktree_root_name, - row.rpc_trace_enabled, - row.logs_selected, - row.rpc_trace_selected, - &theme, - cx, - ) - })) - .on_down_out(MouseButton::Left, |_, this, cx| { + overlay().child( + v_stack() + // todo!() + // .scrollable::(0, None, cx) + .children(menu_rows.into_iter().map(|row| { + Self::render_language_server_menu_item( + row.server_id, + row.server_name, + &row.worktree_root_name, + row.rpc_trace_enabled, + row.logs_selected, + row.rpc_trace_selected, + cx, + ) + })) + .on_mouse_down_out(cx.listener(|this, event: &MouseDownEvent, cx| { + if event.button == MouseButton::Left { this.menu_open = false; cx.notify() - }), - ) - .with_hoverable(true) - .with_fit_mode(OverlayFitMode::SwitchAnchor) - .with_anchor_corner(AnchorCorner::TopLeft) - .with_z_index(999) - .bottom() - .left(), + } + })), + ), // todo!() + // .with_hoverable(true) + // .with_fit_mode(OverlayFitMode::SwitchAnchor) + // .with_anchor_corner(AnchorCorner::TopLeft) + // .with_z_index(999), ) } else { None }); - enum LspCleanupButton {} let log_cleanup_button = div() .child(Label::new("Clear")) - .on_mouse_down(MouseButton::Left, move |_, cx| { - if let Some(log_view) = self.log_view.as_ref() { - log_view.update(cx, |log_view, cx| { - log_view.editor.update(cx, |editor, cx| { - editor.set_read_only(false); - editor.clear(cx); - editor.set_read_only(true); - }); - }) - } - }) + .on_mouse_down( + MouseButton::Left, + cx.listener(move |this, _, cx| { + if let Some(log_view) = this.log_view.as_ref() { + log_view.update(cx, |log_view, cx| { + log_view.editor.update(cx, |editor, cx| { + editor.set_read_only(false); + editor.clear(cx); + editor.set_read_only(true); + }); + }) + } + }), + ) .cursor(CursorStyle::PointingHand); h_stack() @@ -856,8 +856,6 @@ impl LspLogToolbarItemView { current_server: Option, cx: &mut ViewContext, ) -> Div { - let view = cx.view().clone(); - enum ToggleMenu {} let label: Cow = current_server .and_then(|row| { Some( @@ -878,11 +876,12 @@ impl LspLogToolbarItemView { div() .child(Label::new(label)) .cursor(CursorStyle::PointingHand) - .on_mouse_down(MouseButton::Left, move |_, cx| { - view.update(cx, |view, cx| { + .on_mouse_down( + MouseButton::Left, + cx.listener(move |view, _, cx| { view.toggle_menu(cx); - }) - }) + }), + ) .border_1() .border_color(red()) } @@ -892,53 +891,52 @@ impl LspLogToolbarItemView { name: LanguageServerName, worktree_root_name: &str, rpc_trace_enabled: bool, - logs_selected: bool, - rpc_trace_selected: bool, - theme: &Arc, + // todo!() styling + _logs_selected: bool, + _rpc_trace_selected: bool, cx: &mut ViewContext, ) -> Div { - enum ActivateLog {} - enum ActivateRpcTrace {} - enum LanguageServerCheckbox {} - - let view = cx.view().clone(); - v_stack() .child(Label::new(format!("{} ({})", name.0, worktree_root_name))) .child( div() .child(Label::new(SERVER_LOGS)) .cursor(CursorStyle::PointingHand) - .on_mouse_down(MouseButton::Left, move |_, cx| { - view.update(cx, |view, cx| { + .on_mouse_down( + MouseButton::Left, + cx.listener(move |view, _, cx| { view.show_logs_for_server(id, cx); - }) - }), + }), + ), ) .child( h_stack() .child(Label::new(RPC_MESSAGES)) .child( - ui::checkbox_with_label::( - div(), - &theme.welcome.checkbox, - rpc_trace_enabled, + Checkbox::new( id.0, - cx, - move |this, enabled, cx| { - this.toggle_logging_for_server(id, enabled, cx); + if rpc_trace_enabled { + ui::Selection::Selected + } else { + ui::Selection::Unselected }, ) - .flex_float(), + .on_click(cx.listener( + move |this, selection, cx| { + let enabled = matches!(selection, ui::Selection::Selected); + this.toggle_logging_for_server(id, enabled, cx); + }, + )), ) .border_1() .border_color(red()) .cursor(CursorStyle::PointingHand) - .on_mouse_down(MouseButton::Left, move |_, cx| { - view.update(cx, |view, cx| { + .on_mouse_down( + MouseButton::Left, + cx.listener(move |view, _, cx| { view.show_rpc_trace_for_server(id, cx); - }) - }), + }), + ), ) .border_1() .border_color(red()) diff --git a/crates/language_tools2/src/syntax_tree_view.rs b/crates/language_tools2/src/syntax_tree_view.rs index 7efd5f5723..9c8ec0ee04 100644 --- a/crates/language_tools2/src/syntax_tree_view.rs +++ b/crates/language_tools2/src/syntax_tree_view.rs @@ -2,13 +2,13 @@ use editor::{scroll::autoscroll::Autoscroll, Anchor, Editor, ExcerptId}; use gpui::{ actions, div, overlay, red, uniform_list, AnyElement, AppContext, CursorStyle, Div, EventEmitter, FocusHandle, FocusableView, Hsla, InteractiveElement, IntoElement, Model, - MouseButton, ParentElement, Render, Styled, TextStyle, UniformListState, View, ViewContext, - VisualContext, WeakView, WindowContext, + MouseButton, MouseDownEvent, MouseMoveEvent, ParentElement, Pixels, Render, Styled, TextStyle, + UniformListScrollHandle, View, ViewContext, VisualContext, WeakView, WindowContext, }; use language::{Buffer, OwnedSyntaxLayerInfo, SyntaxLayerInfo}; use settings::Settings; use std::{mem, ops::Range}; -use theme::{ActiveTheme, ThemeSettings}; +use theme::{Theme, ThemeSettings}; use tree_sitter::{Node, TreeCursor}; use ui::{h_stack, Label}; use workspace::{ @@ -35,9 +35,9 @@ pub fn init(cx: &mut AppContext) { pub struct SyntaxTreeView { workspace_handle: WeakView, editor: Option, - mouse_y: Option, - line_height: Option, - list_state: UniformListState, + mouse_y: Option, + line_height: Option, + list_scroll_handle: UniformListScrollHandle, selected_descendant_ix: Option, hovered_descendant_ix: Option, focus_handle: FocusHandle, @@ -70,7 +70,7 @@ impl SyntaxTreeView { ) -> Self { let mut this = Self { workspace_handle: workspace_handle.clone(), - list_state: UniformListState::default(), + list_scroll_handle: UniformListScrollHandle::new(), editor: None, mouse_y: None, line_height: None, @@ -204,15 +204,15 @@ impl SyntaxTreeView { let descendant_ix = cursor.descendant_index(); self.selected_descendant_ix = Some(descendant_ix); - self.list_state.scroll_to(ScrollTarget::Show(descendant_ix)); + self.list_scroll_handle.scroll_to_item(descendant_ix); cx.notify(); Some(()) } - fn handle_click(&mut self, y: f32, cx: &mut ViewContext) -> Option<()> { + fn handle_click(&mut self, y: Pixels, cx: &mut ViewContext) -> Option<()> { let line_height = self.line_height?; - let ix = ((self.list_state.scroll_top() + y) / line_height) as usize; + let ix = ((self.list_scroll_handle.scroll_top() + y) / line_height) as usize; self.update_editor_with_range_for_descendant_ix(ix, cx, |editor, mut range, cx| { // Put the cursor at the beginning of the node. @@ -227,7 +227,7 @@ impl SyntaxTreeView { fn hover_state_changed(&mut self, cx: &mut ViewContext) { if let Some((y, line_height)) = self.mouse_y.zip(self.line_height) { - let ix = ((self.list_state.scroll_top() + y) / line_height) as usize; + let ix = ((self.list_scroll_handle.scroll_top() + y) / line_height) as usize; if self.hovered_descendant_ix != Some(ix) { self.hovered_descendant_ix = Some(ix); self.update_editor_with_range_for_descendant_ix(ix, cx, |editor, range, cx| { @@ -279,29 +279,39 @@ impl SyntaxTreeView { fn render_node( cursor: &TreeCursor, - depth: u32, + _depth: u32, selected: bool, hovered: bool, list_hovered: bool, style: &TextStyle, - editor_theme: &theme::Editor, - cx: &AppContext, + editor_theme: &Theme, + _cx: &AppContext, ) -> Div { + let editor_colors = editor_theme.colors(); let node = cursor.node(); let mut range_style = style.clone(); - let em_width = style.em_width(cx.text_system()); - let gutter_padding = (em_width * editor_theme.gutter_padding_factor).round(); + // todo!() styling + // let font_id = cx.text_system().font_id(&style.text.font()).unwrap(); + // let font_size = style.text.font_size.to_pixels(cx.rem_size()); + // let line_height = style.text.line_height_in_pixels(cx.rem_size()); + // let em_width = cx + // .text_system() + // .typographic_bounds(font_id, font_size, 'm') + // .unwrap() + // .size + // .width; + // let gutter_padding = (em_width * editor_theme.gutter_padding_factor).round(); - range_style.color = editor_theme.line_number; + range_style.color = editor_colors.editor_line_number; let mut anonymous_node_style = style.clone(); let string_color = editor_theme - .syntax + .syntax() .highlights .iter() .find_map(|(name, style)| (name == "string").then(|| style.color)?); let property_color = editor_theme - .syntax + .syntax() .highlights .iter() .find_map(|(name, style)| (name == "property").then(|| style.color)?); @@ -330,9 +340,9 @@ impl SyntaxTreeView { ) .child(Label::new(format_node_range(node))) .text_bg(if selected { - editor_theme.selection.selection + editor_colors.element_selected } else if hovered && list_hovered { - editor_theme.active_line_background + editor_colors.element_active } else { Hsla::default() }) @@ -344,37 +354,25 @@ impl SyntaxTreeView { } impl Render for SyntaxTreeView { - // todo!() - // fn ui_name() -> &'static str { - // "SyntaxTreeView" - // } - type Element = Div; fn render(&mut self, cx: &mut gpui::ViewContext<'_, Self>) -> Div { let settings = ThemeSettings::get_global(cx); - let font = settings.buffer_font; + let font = settings.buffer_font.clone(); let font_size = settings.buffer_font_size(cx); - let editor_theme = settings.active_theme; + let editor_theme = settings.active_theme.clone(); + let editor_colors = editor_theme.colors(); let style = TextStyle { - color: editor_theme.text_color, - font_family_name, - font_family_id, - font_id, - font_size, - font_properties: Default::default(), - underline: Default::default(), - font_family: todo!(), - font_features: todo!(), - line_height: todo!(), - font_weight: todo!(), - font_style: todo!(), - background_color: todo!(), - white_space: todo!(), + color: editor_colors.text, + font_family: font.family, + font_features: font.features, + font_weight: font.weight, + font_style: font.style, + ..Default::default() }; - let line_height = cx.text_system().line_height(font_size); + let line_height = cx.text_style().line_height_in_pixels(font_size); if Some(line_height) != self.line_height { self.line_height = Some(line_height); self.hover_state_changed(cx); @@ -389,12 +387,15 @@ impl Render for SyntaxTreeView { let layer = layer.clone(); let theme = editor_theme.clone(); - let list_hovered = state.hovered(); + // todo!() + // let list_hovered = state.hovered(); + let list_hovered = false; uniform_list( - self.list_state.clone(), + cx.view().clone(), + "SyntaxTreeView", layer.node().descendant_count(), - cx, - move |this, range, items, cx| { + move |this, range, cx| { + let mut items = Vec::new(); let mut cursor = layer.node().walk(); let mut descendant_ix = range.start as usize; cursor.goto_descendant(descendant_ix); @@ -428,17 +429,21 @@ impl Render for SyntaxTreeView { } } } + items }, ) - .on_move(move |event, this, cx| { - let y = event.position.y() - event.region.origin_y(); - this.mouse_y = Some(y); - this.hover_state_changed(cx); - }) - .on_mouse_down(MouseButton::Left, move |event, cx| { - let y = event.position.y() - event.region.origin_y(); - self.handle_click(y, cx); - }); + .track_scroll(self.list_scroll_handle.clone()) + .on_mouse_move(cx.listener(move |tree_view, event: &MouseMoveEvent, cx| { + tree_view.mouse_y = Some(event.position.y); + tree_view.hover_state_changed(cx); + })) + .on_mouse_down( + MouseButton::Left, + cx.listener(move |tree_view, event: &MouseDownEvent, cx| { + tree_view.handle_click(event.position.y, cx); + }), + ) + .text_bg(editor_colors.background); } div() @@ -490,7 +495,6 @@ impl SyntaxTreeToolbarItemView { } fn render_menu(&mut self, cx: &mut ViewContext<'_, Self>) -> Option
{ - let theme = cx.theme().clone(); let tree_view = self.tree_view.as_ref()?; let tree_view = tree_view.read(cx); @@ -508,12 +512,12 @@ impl SyntaxTreeToolbarItemView { .children(active_buffer.syntax_layers().enumerate().map( |(ix, layer)| Self::render_menu_item(&active_layer, layer, ix, cx), )) - .on_mouse_down_out(|e, cx| { + .on_mouse_down_out(cx.listener(|this, e: &MouseDownEvent, cx| { if e.button == MouseButton::Left { - self.menu_open = false; + this.menu_open = false; cx.notify() } - }), + })), ) })), ) @@ -540,13 +544,15 @@ impl SyntaxTreeToolbarItemView { } fn render_header(active_layer: &OwnedSyntaxLayerInfo, cx: &mut ViewContext) -> Div { - let view = cx.view().clone(); h_stack() .child(Label::new(active_layer.language.name())) .child(Label::new(format_node_range(active_layer.node()))) - .on_mouse_down(MouseButton::Left, move |_, cx| { - view.update(cx, |view, cx| view.toggle_menu(cx)); - }) + .on_mouse_down( + MouseButton::Left, + cx.listener(move |view, _, cx| { + view.toggle_menu(cx); + }), + ) .cursor(CursorStyle::PointingHand) .border_1() .border_color(red()) @@ -560,16 +566,16 @@ impl SyntaxTreeToolbarItemView { ) -> Div { // todo!() styling let _is_selected = layer.node() == active_layer.node(); - let view = cx.view().clone(); h_stack() .child(Label::new(layer.language.name().to_string())) .child(Label::new(format_node_range(layer.node()))) .cursor(CursorStyle::PointingHand) - .on_mouse_down(MouseButton::Left, move |_, cx| { - view.update(cx, |view, cx| { + .on_mouse_down( + MouseButton::Left, + cx.listener(move |view, _, cx| { view.select_layer(layer_ix, cx); - }) - }) + }), + ) .border_1() .border_color(red()) }