Finish all but the styling

Kirill Bulatov created

Change summary

crates/gpui2/src/elements/uniform_list.rs      |   8 
crates/language_tools2/src/lsp_log.rs          | 170 +++++++++----------
crates/language_tools2/src/syntax_tree_view.rs | 146 ++++++++--------
3 files changed, 168 insertions(+), 156 deletions(-)

Detailed changes

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 {

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<Project>, cx: &mut ModelContext<Self>) {
         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, &params.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<Self>) -> 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::<LspLogScroll>(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::<LspLogScroll>(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<LogMenuItem>,
         cx: &mut ViewContext<Self>,
     ) -> Div {
-        let view = cx.view().clone();
-        enum ToggleMenu {}
         let label: Cow<str> = 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<Theme>,
+        // todo!() styling
+        _logs_selected: bool,
+        _rpc_trace_selected: bool,
         cx: &mut ViewContext<Self>,
     ) -> 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::<LanguageServerCheckbox, _, Self, _>(
-                            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())

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<Workspace>,
     editor: Option<EditorState>,
-    mouse_y: Option<f32>,
-    line_height: Option<f32>,
-    list_state: UniformListState,
+    mouse_y: Option<Pixels>,
+    line_height: Option<Pixels>,
+    list_scroll_handle: UniformListScrollHandle,
     selected_descendant_ix: Option<usize>,
     hovered_descendant_ix: Option<usize>,
     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<SyntaxTreeView>) -> Option<()> {
+    fn handle_click(&mut self, y: Pixels, cx: &mut ViewContext<SyntaxTreeView>) -> 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<SyntaxTreeView>) {
         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();
-
-        range_style.color = editor_theme.line_number;
+        // 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_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<Div> {
-        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<Self>) -> 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())
     }