Apply more fixes to the visual part

This commit is contained in:
Kirill Bulatov 2023-12-15 15:32:09 +02:00
parent 676a07270e
commit 75074c3297
5 changed files with 335 additions and 429 deletions

1
Cargo.lock generated
View file

@ -5008,6 +5008,7 @@ dependencies = [
"settings2", "settings2",
"theme2", "theme2",
"tree-sitter", "tree-sitter",
"ui2",
"unindent", "unindent",
"util", "util",
"workspace2", "workspace2",

View file

@ -1652,14 +1652,11 @@ impl Editor {
Self::new(EditorMode::SingleLine, buffer, None, cx) Self::new(EditorMode::SingleLine, buffer, None, cx)
} }
// pub fn multi_line( pub fn multi_line(cx: &mut ViewContext<Self>) -> Self {
// field_editor_style: Option<Arc<GetFieldEditorTheme>>, let buffer = cx.build_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), String::new()));
// cx: &mut ViewContext<Self>, let buffer = cx.build_model(|cx| MultiBuffer::singleton(buffer, cx));
// ) -> Self { Self::new(EditorMode::Full, buffer, None, cx)
// let buffer = cx.build_model(|cx| Buffer::new(0, cx.model_id() as u64, String::new())); }
// let buffer = cx.build_model(|cx| MultiBuffer::singleton(buffer, cx));
// Self::new(EditorMode::Full, buffer, None, field_editor_style, cx)
// }
pub fn auto_height(max_lines: usize, cx: &mut ViewContext<Self>) -> Self { pub fn auto_height(max_lines: usize, cx: &mut ViewContext<Self>) -> Self {
let buffer = cx.build_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), String::new())); let buffer = cx.build_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), String::new()));

View file

@ -17,6 +17,7 @@ language = { package = "language2", path = "../language2" }
project = { package = "project2", path = "../project2" } project = { package = "project2", path = "../project2" }
workspace = { package = "workspace2", path = "../workspace2" } workspace = { package = "workspace2", path = "../workspace2" }
gpui = { package = "gpui2", path = "../gpui2" } gpui = { package = "gpui2", path = "../gpui2" }
ui = { package = "ui2", path = "../ui2" }
util = { path = "../util" } util = { path = "../util" }
lsp = { package = "lsp2", path = "../lsp2" } lsp = { package = "lsp2", path = "../lsp2" }
futures.workspace = true futures.workspace = true

View file

@ -1,20 +1,22 @@
use collections::{HashMap, VecDeque}; use collections::{HashMap, VecDeque};
use editor::{Editor, MoveToEnd}; use editor::{Editor, EditorElement, EditorEvent, MoveToEnd};
use futures::{channel::mpsc, StreamExt}; use futures::{channel::mpsc, StreamExt};
use gpui::{ use gpui::{
actions, AnchorCorner, AnyElement, AppContext, Context, CursorStyle, Element, Empty, Entity, actions, div, overlay, red, AnchorCorner, AnyElement, AppContext, Context, CursorStyle, Div,
Model, ModelContext, MouseButton, Overlay, OverlayFitMode, Subscription, View, ViewContext, EventEmitter, FocusHandle, FocusableView, InteractiveElement, IntoElement, Model, ModelContext,
VisualContext, WeakModel, MouseButton, OverlayFitMode, ParentElement, Render, Styled, Subscription, View, ViewContext,
VisualContext, WeakModel, WindowContext,
}; };
use language::{LanguageServerId, LanguageServerName}; use language::{LanguageServerId, LanguageServerName};
use lsp::IoKind; use lsp::IoKind;
use project::{search::SearchQuery, Project}; use project::{search::SearchQuery, Project};
use std::{borrow::Cow, sync::Arc}; use std::{borrow::Cow, sync::Arc};
use theme::Theme; use theme::{ActiveTheme, Theme};
use ui::{h_stack, v_stack, Label};
use workspace::{ use workspace::{
item::{Item, ItemHandle}, item::{Item, ItemHandle},
searchable::{SearchableItem, SearchableItemHandle}, searchable::{SearchEvent, SearchableItem, SearchableItemHandle},
ToolbarItemLocation, ToolbarItemView, Workspace, ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView, Workspace,
}; };
const SEND_LINE: &str = "// Send:"; const SEND_LINE: &str = "// Send:";
@ -50,6 +52,7 @@ pub struct LspLogView {
current_server_id: Option<LanguageServerId>, current_server_id: Option<LanguageServerId>,
is_showing_rpc_trace: bool, is_showing_rpc_trace: bool,
project: Model<Project>, project: Model<Project>,
focus_handle: FocusHandle,
_log_store_subscriptions: Vec<Subscription>, _log_store_subscriptions: Vec<Subscription>,
} }
@ -110,9 +113,9 @@ impl LogStore {
projects: HashMap::default(), projects: HashMap::default(),
io_tx, io_tx,
}; };
cx.spawn_weak(|this, mut cx| async move { cx.spawn(|this, mut cx| async move {
while let Some((project, server_id, io_kind, message)) = io_rx.next().await { while let Some((project, server_id, io_kind, message)) = io_rx.next().await {
if let Some(this) = this.upgrade(&cx) { if let Some(this) = this.upgrade() {
this.update(&mut cx, |this, cx| { this.update(&mut cx, |this, cx| {
this.on_io(project, server_id, io_kind, &message, cx); this.on_io(project, server_id, io_kind, &message, cx);
}); });
@ -120,7 +123,7 @@ impl LogStore {
} }
anyhow::Ok(()) anyhow::Ok(())
}) })
.detach(); .detach_and_log_err(cx);
this this
} }
@ -131,7 +134,7 @@ impl LogStore {
ProjectState { ProjectState {
servers: HashMap::default(), servers: HashMap::default(),
_subscriptions: [ _subscriptions: [
cx.observe_release(&project, move |this, _, _| { cx.observe_release(project, move |this, _, _| {
this.projects.remove(&weak_project); this.projects.remove(&weak_project);
}), }),
cx.subscribe(project, |this, project, event, cx| match event { cx.subscribe(project, |this, project, event, cx| match event {
@ -185,14 +188,13 @@ impl LogStore {
.ok(); .ok();
}) })
}); });
let this = cx.weak_handle(); let this = cx.handle().downgrade();
let weak_project = project.downgrade(); let weak_project = project.downgrade();
server_state._lsp_logs_subscription = server.map(|server| { server_state._lsp_logs_subscription = server.map(|server| {
let server_id = server.server_id(); let server_id = server.server_id();
server.on_notification::<lsp::notification::LogMessage, _>({ server.on_notification::<lsp::notification::LogMessage, _>({
move |params, mut cx| { move |params, mut cx| {
if let Some((project, this)) = weak_project.upgrade().zip(this.upgrade(&mut cx)) if let Some((project, this)) = weak_project.upgrade().zip(this.upgrade()) {
{
this.update(&mut cx, |this, cx| { this.update(&mut cx, |this, cx| {
this.add_language_server_log(&project, server_id, &params.message, cx); this.add_language_server_log(&project, server_id, &params.message, cx);
}); });
@ -413,14 +415,25 @@ impl LspLogView {
} }
}); });
let (editor, editor_subscription) = Self::editor_for_logs(String::new(), cx); let (editor, editor_subscription) = Self::editor_for_logs(String::new(), cx);
let focus_handle = cx.focus_handle();
let focus_subscription = cx.on_focus(&focus_handle, |log_view, cx| {
cx.focus_view(&log_view.editor);
});
let mut this = Self { let mut this = Self {
focus_handle,
editor, editor,
editor_subscription, editor_subscription,
project, project,
log_store, log_store,
current_server_id: None, current_server_id: None,
is_showing_rpc_trace: false, is_showing_rpc_trace: false,
_log_store_subscriptions: vec![model_changes_subscription, events_subscriptions], _log_store_subscriptions: vec![
model_changes_subscription,
events_subscriptions,
focus_subscription,
],
}; };
if let Some(server_id) = server_id { if let Some(server_id) = server_id {
this.show_logs_for_server(server_id, cx); this.show_logs_for_server(server_id, cx);
@ -433,13 +446,18 @@ impl LspLogView {
cx: &mut ViewContext<Self>, cx: &mut ViewContext<Self>,
) -> (View<Editor>, Subscription) { ) -> (View<Editor>, Subscription) {
let editor = cx.build_view(|cx| { let editor = cx.build_view(|cx| {
let mut editor = Editor::multi_line(None, cx); let mut editor = Editor::multi_line(cx);
editor.set_text(log_contents, cx); editor.set_text(log_contents, cx);
editor.move_to_end(&MoveToEnd, cx); editor.move_to_end(&MoveToEnd, cx);
editor.set_read_only(true); editor.set_read_only(true);
editor editor
}); });
let editor_subscription = cx.subscribe(&editor, |_, _, event, cx| cx.emit(event.clone())); let editor_subscription = cx.subscribe(
&editor,
|_, _, event: &EditorEvent, cx: &mut ViewContext<'_, LspLogView>| {
cx.emit(event.clone())
},
);
(editor, editor_subscription) (editor, editor_subscription)
} }
@ -526,7 +544,7 @@ impl LspLogView {
.as_singleton() .as_singleton()
.expect("log buffer should be a singleton") .expect("log buffer should be a singleton")
.update(cx, |_, cx| { .update(cx, |_, cx| {
cx.spawn_weak({ cx.spawn({
let buffer = cx.handle(); let buffer = cx.handle();
|_, mut cx| async move { |_, mut cx| async move {
let language = language.await.ok(); let language = language.await.ok();
@ -574,30 +592,34 @@ fn log_contents(lines: &VecDeque<String>) -> String {
} }
} }
impl View for LspLogView { impl Render for LspLogView {
fn ui_name() -> &'static str { // todo!()
"LspLogView" // fn ui_name() -> &'static str {
// "LspLogView"
// }
type Element = EditorElement;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
self.editor.update(cx, |editor, cx| editor.render(cx))
}
} }
fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> { impl FocusableView for LspLogView {
ChildView::new(&self.editor, cx).into_any() fn focus_handle(&self, _: &AppContext) -> FocusHandle {
} self.focus_handle.clone()
fn focus_in(&mut self, _: gpui::AnyView, cx: &mut ViewContext<Self>) {
if cx.is_self_focused() {
cx.focus(&self.editor);
}
} }
} }
impl Item for LspLogView { impl Item for LspLogView {
fn tab_content<V: 'static>( type Event = EditorEvent;
&self,
_: Option<usize>, fn to_item_events(event: &Self::Event, f: impl FnMut(workspace::item::ItemEvent)) {
style: &theme::Tab, Editor::to_item_events(event, f)
_: &AppContext, }
) -> AnyElement<V> {
Label::new("LSP Logs", style.label.clone()).into_any() fn tab_content(&self, _: Option<usize>, _: bool, _: &WindowContext<'_>) -> AnyElement {
Label::new("LSP Logs").into_any_element()
} }
fn as_searchable(&self, handle: &View<Self>) -> Option<Box<dyn SearchableItemHandle>> { fn as_searchable(&self, handle: &View<Self>) -> Option<Box<dyn SearchableItemHandle>> {
@ -608,15 +630,6 @@ impl Item for LspLogView {
impl SearchableItem for LspLogView { impl SearchableItem for LspLogView {
type Match = <Editor as SearchableItem>::Match; type Match = <Editor as SearchableItem>::Match;
fn to_search_event(
&mut self,
event: &Self::Event,
cx: &mut ViewContext<Self>,
) -> Option<workspace::searchable::SearchEvent> {
self.editor
.update(cx, |editor, cx| editor.to_search_event(event, cx))
}
fn clear_matches(&mut self, cx: &mut ViewContext<Self>) { fn clear_matches(&mut self, cx: &mut ViewContext<Self>) {
self.editor.update(cx, |e, cx| e.clear_matches(cx)) self.editor.update(cx, |e, cx| e.clear_matches(cx))
} }
@ -675,6 +688,8 @@ impl SearchableItem for LspLogView {
} }
} }
impl EventEmitter<ToolbarItemEvent> for LspLogToolbarItemView {}
impl ToolbarItemView for LspLogToolbarItemView { impl ToolbarItemView for LspLogToolbarItemView {
fn set_active_pane_item( fn set_active_pane_item(
&mut self, &mut self,
@ -688,9 +703,7 @@ impl ToolbarItemView for LspLogToolbarItemView {
self._log_view_subscription = Some(cx.observe(&log_view, |_, _, cx| { self._log_view_subscription = Some(cx.observe(&log_view, |_, _, cx| {
cx.notify(); cx.notify();
})); }));
return ToolbarItemLocation::PrimaryLeft { return ToolbarItemLocation::PrimaryLeft;
flex: Some((1., false)),
};
} }
} }
self.log_view = None; self.log_view = None;
@ -699,15 +712,17 @@ impl ToolbarItemView for LspLogToolbarItemView {
} }
} }
impl View for LspLogToolbarItemView { impl Render for LspLogToolbarItemView {
fn ui_name() -> &'static str { type Element = Div;
"LspLogView" // todo!()
} // fn ui_name() -> &'static str {
// "LspLogView"
// }
fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> { fn render(&mut self, cx: &mut ViewContext<Self>) -> Div {
let theme = theme::current(cx).clone(); let theme = cx.theme().clone();
let Some(log_view) = self.log_view.as_ref() else { let Some(log_view) = self.log_view.as_ref() else {
return Empty::new().into_any(); return div();
}; };
let (menu_rows, current_server_id) = log_view.update(cx, |log_view, cx| { let (menu_rows, current_server_id) = log_view.update(cx, |log_view, cx| {
let menu_rows = log_view.menu_items(cx).unwrap_or_default(); let menu_rows = log_view.menu_items(cx).unwrap_or_default();
@ -726,19 +741,15 @@ impl View for LspLogToolbarItemView {
enum LspLogScroll {} enum LspLogScroll {}
enum Menu {} enum Menu {}
let lsp_menu = Stack::new() let lsp_menu = h_stack()
.with_child(Self::render_language_server_menu_header( .child(Self::render_language_server_menu_header(current_server, cx))
current_server, .children(if self.menu_open {
&theme,
cx,
))
.with_children(if self.menu_open {
Some( Some(
Overlay::new( overlay()
MouseEventHandler::new::<Menu, _>(0, cx, move |_, cx| { .child(
Flex::column() v_stack()
.scrollable::<LspLogScroll>(0, None, cx) .scrollable::<LspLogScroll>(0, None, cx)
.with_children(menu_rows.into_iter().map(|row| { .children(menu_rows.into_iter().map(|row| {
Self::render_language_server_menu_item( Self::render_language_server_menu_item(
row.server_id, row.server_id,
row.server_name, row.server_name,
@ -750,12 +761,6 @@ impl View for LspLogToolbarItemView {
cx, cx,
) )
})) }))
.contained()
.with_style(theme.toolbar_dropdown_menu.container)
.constrained()
.with_width(400.)
.with_height(400.)
})
.on_down_out(MouseButton::Left, |_, this, cx| { .on_down_out(MouseButton::Left, |_, this, cx| {
this.menu_open = false; this.menu_open = false;
cx.notify() cx.notify()
@ -765,36 +770,18 @@ impl View for LspLogToolbarItemView {
.with_fit_mode(OverlayFitMode::SwitchAnchor) .with_fit_mode(OverlayFitMode::SwitchAnchor)
.with_anchor_corner(AnchorCorner::TopLeft) .with_anchor_corner(AnchorCorner::TopLeft)
.with_z_index(999) .with_z_index(999)
.aligned()
.bottom() .bottom()
.left(), .left(),
) )
} else { } else {
None None
}) });
.aligned()
.left()
.clipped();
enum LspCleanupButton {} enum LspCleanupButton {}
let log_cleanup_button = let log_cleanup_button = div()
MouseEventHandler::new::<LspCleanupButton, _>(1, cx, |state, cx| { .child(Label::new("Clear"))
let theme = theme::current(cx).clone(); .on_mouse_down(MouseButton::Left, move |_, cx| {
let style = theme if let Some(log_view) = self.log_view.as_ref() {
.workspace
.toolbar
.toggleable_text_tool
.in_state(server_selected)
.style_for(state);
Label::new("Clear", style.text.clone())
.aligned()
.contained()
.with_style(style.container)
.constrained()
.with_height(theme.toolbar_dropdown_menu.row_height / 6.0 * 5.0)
})
.on_click(MouseButton::Left, move |_, this, cx| {
if let Some(log_view) = this.log_view.as_ref() {
log_view.update(cx, |log_view, cx| { log_view.update(cx, |log_view, cx| {
log_view.editor.update(cx, |editor, cx| { log_view.editor.update(cx, |editor, cx| {
editor.set_read_only(false); editor.set_read_only(false);
@ -804,17 +791,13 @@ impl View for LspLogToolbarItemView {
}) })
} }
}) })
.with_cursor_style(CursorStyle::PointingHand) .cursor(CursorStyle::PointingHand);
.aligned()
.right();
Flex::row() h_stack()
.with_child(lsp_menu) .child(lsp_menu)
.with_child(log_cleanup_button) .child(log_cleanup_button)
.contained() .border_1()
.aligned() .border_color(red())
.left()
.into_any_named("lsp log controls")
} }
} }
@ -871,11 +854,10 @@ impl LspLogToolbarItemView {
fn render_language_server_menu_header( fn render_language_server_menu_header(
current_server: Option<LogMenuItem>, current_server: Option<LogMenuItem>,
theme: &Arc<Theme>,
cx: &mut ViewContext<Self>, cx: &mut ViewContext<Self>,
) -> impl Element<Self> { ) -> Div {
let view = cx.view().clone();
enum ToggleMenu {} enum ToggleMenu {}
MouseEventHandler::new::<ToggleMenu, _>(0, cx, move |state, _| {
let label: Cow<str> = current_server let label: Cow<str> = current_server
.and_then(|row| { .and_then(|row| {
Some( Some(
@ -893,15 +875,16 @@ impl LspLogToolbarItemView {
) )
}) })
.unwrap_or_else(|| "No server selected".into()); .unwrap_or_else(|| "No server selected".into());
let style = theme.toolbar_dropdown_menu.header.style_for(state); div()
Label::new(label, style.text.clone()) .child(Label::new(label))
.contained() .cursor(CursorStyle::PointingHand)
.with_style(style.container) .on_mouse_down(MouseButton::Left, move |_, cx| {
}) view.update(cx, |view, cx| {
.with_cursor_style(CursorStyle::PointingHand)
.on_click(MouseButton::Left, move |_, view, cx| {
view.toggle_menu(cx); view.toggle_menu(cx);
}) })
})
.border_1()
.border_color(red())
} }
fn render_language_server_menu_item( fn render_language_server_menu_item(
@ -913,57 +896,31 @@ impl LspLogToolbarItemView {
rpc_trace_selected: bool, rpc_trace_selected: bool,
theme: &Arc<Theme>, theme: &Arc<Theme>,
cx: &mut ViewContext<Self>, cx: &mut ViewContext<Self>,
) -> impl Element<Self> { ) -> Div {
enum ActivateLog {} enum ActivateLog {}
enum ActivateRpcTrace {} enum ActivateRpcTrace {}
enum LanguageServerCheckbox {} enum LanguageServerCheckbox {}
Flex::column() let view = cx.view().clone();
.with_child({
let style = &theme.toolbar_dropdown_menu.section_header; v_stack()
Label::new( .child(Label::new(format!("{} ({})", name.0, worktree_root_name)))
format!("{} ({})", name.0, worktree_root_name), .child(
style.text.clone(), div()
) .child(Label::new(SERVER_LOGS))
.contained() .cursor(CursorStyle::PointingHand)
.with_style(style.container) .on_mouse_down(MouseButton::Left, move |_, cx| {
.constrained() view.update(cx, |view, cx| {
.with_height(theme.toolbar_dropdown_menu.row_height)
})
.with_child(
MouseEventHandler::new::<ActivateLog, _>(id.0, cx, move |state, _| {
let style = theme
.toolbar_dropdown_menu
.item
.in_state(logs_selected)
.style_for(state);
Label::new(SERVER_LOGS, style.text.clone())
.contained()
.with_style(style.container)
.constrained()
.with_height(theme.toolbar_dropdown_menu.row_height)
})
.with_cursor_style(CursorStyle::PointingHand)
.on_click(MouseButton::Left, move |_, view, cx| {
view.show_logs_for_server(id, cx); view.show_logs_for_server(id, cx);
})
}), }),
) )
.with_child( .child(
MouseEventHandler::new::<ActivateRpcTrace, _>(id.0, cx, move |state, cx| { h_stack()
let style = theme .child(Label::new(RPC_MESSAGES))
.toolbar_dropdown_menu .child(
.item
.in_state(rpc_trace_selected)
.style_for(state);
Flex::row()
.with_child(
Label::new(RPC_MESSAGES, style.text.clone())
.constrained()
.with_height(theme.toolbar_dropdown_menu.row_height),
)
.with_child(
ui::checkbox_with_label::<LanguageServerCheckbox, _, Self, _>( ui::checkbox_with_label::<LanguageServerCheckbox, _, Self, _>(
Empty::new(), div(),
&theme.welcome.checkbox, &theme.welcome.checkbox,
rpc_trace_enabled, rpc_trace_enabled,
id.0, id.0,
@ -974,17 +931,17 @@ impl LspLogToolbarItemView {
) )
.flex_float(), .flex_float(),
) )
.align_children_center() .border_1()
.contained() .border_color(red())
.with_style(style.container) .cursor(CursorStyle::PointingHand)
.constrained() .on_mouse_down(MouseButton::Left, move |_, cx| {
.with_height(theme.toolbar_dropdown_menu.row_height) view.update(cx, |view, cx| {
})
.with_cursor_style(CursorStyle::PointingHand)
.on_click(MouseButton::Left, move |_, view, cx| {
view.show_rpc_trace_for_server(id, cx); view.show_rpc_trace_for_server(id, cx);
})
}), }),
) )
.border_1()
.border_color(red())
} }
} }
@ -996,14 +953,7 @@ pub enum Event {
}, },
} }
impl Entity for LogStore { impl EventEmitter<Event> for LogStore {}
type Event = Event; impl EventEmitter<Event> for LspLogView {}
} impl EventEmitter<EditorEvent> for LspLogView {}
impl EventEmitter<SearchEvent> for LspLogView {}
impl Entity for LspLogView {
type Event = editor::Event;
}
impl Entity for LspLogToolbarItemView {
type Event = ();
}

View file

@ -1,17 +1,20 @@
use editor::{scroll::autoscroll::Autoscroll, Anchor, Editor, ExcerptId}; use editor::{scroll::autoscroll::Autoscroll, Anchor, Editor, ExcerptId};
use gpui::{ use gpui::{
actions, AnchorCorner, AppContext, CursorStyle, Div, Element, Empty, Entity, Focusable, Model, actions, div, overlay, red, uniform_list, AnyElement, AppContext, CursorStyle, Div,
MouseButton, Overlay, OverlayFitMode, ParentElement, Render, TextStyle, UniformList, EventEmitter, FocusHandle, FocusableView, Hsla, InteractiveElement, IntoElement, Model,
UniformListState, View, ViewContext, VisualContext, WeakView, MouseButton, ParentElement, Render, Styled, TextStyle, UniformListState, View, ViewContext,
VisualContext, WeakView, WindowContext,
}; };
use language::{Buffer, OwnedSyntaxLayerInfo, SyntaxLayerInfo}; use language::{Buffer, OwnedSyntaxLayerInfo, SyntaxLayerInfo};
use std::{mem, ops::Range, sync::Arc}; use settings::Settings;
use theme::{ActiveTheme, Theme, ThemeSettings}; use std::{mem, ops::Range};
use theme::{ActiveTheme, ThemeSettings};
use tree_sitter::{Node, TreeCursor}; use tree_sitter::{Node, TreeCursor};
use ui::{h_stack, Label};
use workspace::{ use workspace::{
item::{Item, ItemHandle}, item::{Item, ItemHandle},
ui::v_stack, ui::v_stack,
ToolbarItemLocation, ToolbarItemView, Workspace, ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView, Workspace,
}; };
actions!(debug, [OpenSyntaxTreeView]); actions!(debug, [OpenSyntaxTreeView]);
@ -37,6 +40,7 @@ pub struct SyntaxTreeView {
list_state: UniformListState, list_state: UniformListState,
selected_descendant_ix: Option<usize>, selected_descendant_ix: Option<usize>,
hovered_descendant_ix: Option<usize>, hovered_descendant_ix: Option<usize>,
focus_handle: FocusHandle,
} }
pub struct SyntaxTreeToolbarItemView { pub struct SyntaxTreeToolbarItemView {
@ -72,6 +76,7 @@ impl SyntaxTreeView {
line_height: None, line_height: None,
hovered_descendant_ix: None, hovered_descendant_ix: None,
selected_descendant_ix: None, selected_descendant_ix: None,
focus_handle: cx.focus_handle(),
}; };
this.workspace_updated(active_item, cx); this.workspace_updated(active_item, cx);
@ -229,7 +234,7 @@ impl SyntaxTreeView {
editor.clear_background_highlights::<Self>(cx); editor.clear_background_highlights::<Self>(cx);
editor.highlight_background::<Self>( editor.highlight_background::<Self>(
vec![range], vec![range],
|theme| theme.editor.document_highlight_write_background, |theme| theme.editor_document_highlight_write_background,
cx, cx,
); );
}); });
@ -281,10 +286,10 @@ impl SyntaxTreeView {
style: &TextStyle, style: &TextStyle,
editor_theme: &theme::Editor, editor_theme: &theme::Editor,
cx: &AppContext, cx: &AppContext,
) -> gpui::AnyElement<SyntaxTreeView> { ) -> Div {
let node = cursor.node(); let node = cursor.node();
let mut range_style = style.clone(); let mut range_style = style.clone();
let em_width = style.em_width(cx.font_cache()); let em_width = style.em_width(cx.text_system());
let gutter_padding = (em_width * editor_theme.gutter_padding_factor).round(); let gutter_padding = (em_width * editor_theme.gutter_padding_factor).round();
range_style.color = editor_theme.line_number; range_style.color = editor_theme.line_number;
@ -304,64 +309,54 @@ impl SyntaxTreeView {
anonymous_node_style.color = color; anonymous_node_style.color = color;
} }
let mut row = Flex::row(); let mut row = h_stack();
if let Some(field_name) = cursor.field_name() { if let Some(field_name) = cursor.field_name() {
let mut field_style = style.clone(); let mut field_style = style.clone();
if let Some(color) = property_color { if let Some(color) = property_color {
field_style.color = color; field_style.color = color;
} }
row.add_children([ row = row.children([Label::new(field_name), Label::new(": ")]);
Label::new(field_name, field_style),
Label::new(": ", style.clone()),
]);
} }
return row return row
.with_child( .child(
if node.is_named() { if node.is_named() {
Label::new(node.kind(), style.clone()) Label::new(node.kind())
} else { } else {
Label::new(format!("\"{}\"", node.kind()), anonymous_node_style) Label::new(format!("\"{}\"", node.kind()))
} }, // todo!()
.contained() // .margin(em_width),
.with_margin_right(em_width),
) )
.with_child(Label::new(format_node_range(node), range_style)) .child(Label::new(format_node_range(node)))
.contained() .text_bg(if selected {
.with_background_color(if selected {
editor_theme.selection.selection editor_theme.selection.selection
} else if hovered && list_hovered { } else if hovered && list_hovered {
editor_theme.active_line_background editor_theme.active_line_background
} else { } else {
Default::default() Hsla::default()
}) })
.with_padding_left(gutter_padding + depth as f32 * 18.0) // todo!()
.into_any(); // .padding(gutter_padding + depth as f32 * 18.0)
.border_1()
.border_color(red());
} }
} }
impl Entity for SyntaxTreeView { impl Render for SyntaxTreeView {
type Event = (); // todo!()
} // fn ui_name() -> &'static str {
// "SyntaxTreeView"
// }
impl View for SyntaxTreeView { type Element = Div;
fn ui_name() -> &'static str {
"SyntaxTreeView"
}
fn render(&mut self, cx: &mut gpui::ViewContext<'_, '_, Self>) -> gpui::AnyElement<Self> { fn render(&mut self, cx: &mut gpui::ViewContext<'_, Self>) -> Div {
let settings = settings::get::<ThemeSettings>(cx); let settings = ThemeSettings::get_global(cx);
let font_family_id = settings.buffer_font_family; let font = settings.buffer_font;
let font_family_name = cx.font_cache().family_name(font_family_id).unwrap();
let font_properties = Default::default();
let font_id = cx
.font_cache()
.select_font(font_family_id, &font_properties)
.unwrap();
let font_size = settings.buffer_font_size(cx); let font_size = settings.buffer_font_size(cx);
let editor_theme = settings.theme.editor.clone(); let editor_theme = settings.active_theme;
let style = TextStyle { let style = TextStyle {
color: editor_theme.text_color, color: editor_theme.text_color,
font_family_name, font_family_name,
@ -370,10 +365,16 @@ impl View for SyntaxTreeView {
font_size, font_size,
font_properties: Default::default(), font_properties: Default::default(),
underline: Default::default(), underline: Default::default(),
soft_wrap: false, font_family: todo!(),
font_features: todo!(),
line_height: todo!(),
font_weight: todo!(),
font_style: todo!(),
background_color: todo!(),
white_space: todo!(),
}; };
let line_height = cx.font_cache().line_height(font_size); let line_height = cx.text_system().line_height(font_size);
if Some(line_height) != self.line_height { if Some(line_height) != self.line_height {
self.line_height = Some(line_height); self.line_height = Some(line_height);
self.hover_state_changed(cx); self.hover_state_changed(cx);
@ -387,9 +388,9 @@ impl View for SyntaxTreeView {
{ {
let layer = layer.clone(); let layer = layer.clone();
let theme = editor_theme.clone(); let theme = editor_theme.clone();
return MouseEventHandler::new::<Self, _>(0, cx, move |state, cx| {
let list_hovered = state.hovered(); let list_hovered = state.hovered();
UniformList::new( uniform_list(
self.list_state.clone(), self.list_state.clone(),
layer.node().descendant_count(), layer.node().descendant_count(),
cx, cx,
@ -429,48 +430,53 @@ impl View for SyntaxTreeView {
} }
}, },
) )
})
.on_move(move |event, this, cx| { .on_move(move |event, this, cx| {
let y = event.position.y() - event.region.origin_y(); let y = event.position.y() - event.region.origin_y();
this.mouse_y = Some(y); this.mouse_y = Some(y);
this.hover_state_changed(cx); this.hover_state_changed(cx);
}) })
.on_click(MouseButton::Left, move |event, this, cx| { .on_mouse_down(MouseButton::Left, move |event, cx| {
let y = event.position.y() - event.region.origin_y(); let y = event.position.y() - event.region.origin_y();
this.handle_click(y, cx); self.handle_click(y, cx);
}) });
.contained()
.with_background_color(editor_theme.background)
.into_any();
} }
Empty::new().into_any() div()
}
}
impl EventEmitter<()> for SyntaxTreeView {}
impl FocusableView for SyntaxTreeView {
fn focus_handle(&self, _: &AppContext) -> gpui::FocusHandle {
self.focus_handle.clone()
} }
} }
impl Item for SyntaxTreeView { impl Item for SyntaxTreeView {
fn tab_content<V: 'static>( type Event = ();
&self,
_: Option<usize>, fn to_item_events(_: &Self::Event, _: impl FnMut(workspace::item::ItemEvent)) {}
style: &theme::Tab,
_: &AppContext, fn tab_content(&self, _: Option<usize>, _: bool, _: &WindowContext<'_>) -> AnyElement {
) -> gpui::AnyElement<V> { Label::new("Syntax Tree").into_any_element()
Label::new("Syntax Tree", style.label.clone()).into_any()
} }
fn clone_on_split( fn clone_on_split(
&self, &self,
_workspace_id: workspace::WorkspaceId, _: workspace::WorkspaceId,
cx: &mut ViewContext<Self>, cx: &mut ViewContext<Self>,
) -> Option<Self> ) -> Option<View<Self>>
where where
Self: Sized, Self: Sized,
{ {
Some(cx.build_view(|cx| {
let mut clone = Self::new(self.workspace_handle.clone(), None, cx); let mut clone = Self::new(self.workspace_handle.clone(), None, cx);
if let Some(editor) = &self.editor { if let Some(editor) = &self.editor {
clone.set_editor(editor.editor.clone(), cx) clone.set_editor(editor.editor.clone(), cx)
} }
Some(clone) clone
}))
} }
} }
@ -483,10 +489,7 @@ impl SyntaxTreeToolbarItemView {
} }
} }
fn render_menu( fn render_menu(&mut self, cx: &mut ViewContext<'_, Self>) -> Option<Div> {
&mut self,
cx: &mut ViewContext<'_, '_, Self>,
) -> Option<gpui::AnyElement<Self>> {
let theme = cx.theme().clone(); let theme = cx.theme().clone();
let tree_view = self.tree_view.as_ref()?; let tree_view = self.tree_view.as_ref()?;
let tree_view = tree_view.read(cx); let tree_view = tree_view.read(cx);
@ -496,36 +499,23 @@ impl SyntaxTreeToolbarItemView {
let active_layer = buffer_state.active_layer.clone()?; let active_layer = buffer_state.active_layer.clone()?;
let active_buffer = buffer_state.buffer.read(cx).snapshot(); let active_buffer = buffer_state.buffer.read(cx).snapshot();
enum Menu {}
Some( Some(
v_stack() v_stack()
.child(Self::render_header(&theme, &active_layer, cx)) .child(Self::render_header(&active_layer, cx))
.children(self.menu_open.then(|| { .children(self.menu_open.then(|| {
overlay( overlay().child(
mouse_event_handler::<Menu, _>(0, cx, move |_, cx| {
v_stack() v_stack()
.with_children(active_buffer.syntax_layers().enumerate().map( .children(active_buffer.syntax_layers().enumerate().map(
|(ix, layer)| { |(ix, layer)| Self::render_menu_item(&active_layer, layer, ix, cx),
Self::render_menu_item(&theme, &active_layer, layer, ix, cx)
},
)) ))
.contained() .on_mouse_down_out(|e, cx| {
.with_style(theme.toolbar_dropdown_menu.container) if e.button == MouseButton::Left {
.constrained() self.menu_open = false;
.with_width(400.)
.with_height(400.)
})
.on_down_out(MouseButton::Left, |_, this, cx| {
this.menu_open = false;
cx.notify() cx.notify()
}
}), }),
) )
.with_hoverable(true) })),
.with_fit_content()
.into_any()
}))
.into_any(),
) )
} }
@ -549,71 +539,39 @@ impl SyntaxTreeToolbarItemView {
}) })
} }
fn render_header( fn render_header(active_layer: &OwnedSyntaxLayerInfo, cx: &mut ViewContext<Self>) -> Div {
theme: &Arc<Theme>, let view = cx.view().clone();
active_layer: &OwnedSyntaxLayerInfo, h_stack()
cx: &mut ViewContext<Self>, .child(Label::new(active_layer.language.name()))
) -> impl Element<Self> { .child(Label::new(format_node_range(active_layer.node())))
enum ToggleMenu {} .on_mouse_down(MouseButton::Left, move |_, cx| {
MouseEventHandler::new::<ToggleMenu, _>(0, cx, move |state, _| { view.update(cx, |view, cx| view.toggle_menu(cx));
let style = theme.toolbar_dropdown_menu.header.style_for(state);
Flex::row()
.with_child(
Label::new(active_layer.language.name().to_string(), style.text.clone())
.contained()
.with_margin_right(style.secondary_text_spacing),
)
.with_child(Label::new(
format_node_range(active_layer.node()),
style
.secondary_text
.clone()
.unwrap_or_else(|| style.text.clone()),
))
.contained()
.with_style(style.container)
})
.with_cursor_style(CursorStyle::PointingHand)
.on_click(MouseButton::Left, move |_, view, cx| {
view.toggle_menu(cx);
}) })
.cursor(CursorStyle::PointingHand)
.border_1()
.border_color(red())
} }
fn render_menu_item( fn render_menu_item(
theme: &Arc<Theme>,
active_layer: &OwnedSyntaxLayerInfo, active_layer: &OwnedSyntaxLayerInfo,
layer: SyntaxLayerInfo, layer: SyntaxLayerInfo,
layer_ix: usize, layer_ix: usize,
cx: &mut ViewContext<Self>, cx: &mut ViewContext<Self>,
) -> impl Element<Self> { ) -> Div {
enum ActivateLayer {} // todo!() styling
MouseEventHandler::new::<ActivateLayer, _>(layer_ix, cx, move |state, _| { let _is_selected = layer.node() == active_layer.node();
let is_selected = layer.node() == active_layer.node(); let view = cx.view().clone();
let style = theme h_stack()
.toolbar_dropdown_menu .child(Label::new(layer.language.name().to_string()))
.item .child(Label::new(format_node_range(layer.node())))
.in_state(is_selected) .cursor(CursorStyle::PointingHand)
.style_for(state); .on_mouse_down(MouseButton::Left, move |_, cx| {
Flex::row() view.update(cx, |view, cx| {
.with_child(
Label::new(layer.language.name().to_string(), style.text.clone())
.contained()
.with_margin_right(style.secondary_text_spacing),
)
.with_child(Label::new(
format_node_range(layer.node()),
style
.secondary_text
.clone()
.unwrap_or_else(|| style.text.clone()),
))
.contained()
.with_style(style.container)
})
.with_cursor_style(CursorStyle::PointingHand)
.on_click(MouseButton::Left, move |_, view, cx| {
view.select_layer(layer_ix, cx); view.select_layer(layer_ix, cx);
}) })
})
.border_1()
.border_color(red())
} }
} }
@ -630,33 +588,32 @@ fn format_node_range(node: Node) -> String {
} }
impl Render for SyntaxTreeToolbarItemView { impl Render for SyntaxTreeToolbarItemView {
type Element = Focusable<Div>; type Element = Div;
// todo!() // todo!()
// fn ui_name() -> &'static str { // fn ui_name() -> &'static str {
// "SyntaxTreeToolbarItemView" // "SyntaxTreeToolbarItemView"
// } // }
fn render(&mut self, cx: &mut ViewContext<'_, '_, Self>) -> gpui::AnyElement<Self> { fn render(&mut self, cx: &mut ViewContext<'_, Self>) -> Div {
self.render_menu(cx) self.render_menu(cx).unwrap_or_else(|| div())
.unwrap_or_else(|| Empty::new().into_any())
} }
} }
impl EventEmitter<ToolbarItemEvent> for SyntaxTreeToolbarItemView {}
impl ToolbarItemView for SyntaxTreeToolbarItemView { impl ToolbarItemView for SyntaxTreeToolbarItemView {
fn set_active_pane_item( fn set_active_pane_item(
&mut self, &mut self,
active_pane_item: Option<&dyn ItemHandle>, active_pane_item: Option<&dyn ItemHandle>,
cx: &mut ViewContext<Self>, cx: &mut ViewContext<Self>,
) -> workspace::ToolbarItemLocation { ) -> ToolbarItemLocation {
self.menu_open = false; self.menu_open = false;
if let Some(item) = active_pane_item { if let Some(item) = active_pane_item {
if let Some(view) = item.downcast::<SyntaxTreeView>() { if let Some(view) = item.downcast::<SyntaxTreeView>() {
self.tree_view = Some(view.clone()); self.tree_view = Some(view.clone());
self.subscription = Some(cx.observe(&view, |_, _, cx| cx.notify())); self.subscription = Some(cx.observe(&view, |_, _, cx| cx.notify()));
return ToolbarItemLocation::PrimaryLeft { return ToolbarItemLocation::PrimaryLeft;
flex: Some((1., false)),
};
} }
} }
self.tree_view = None; self.tree_view = None;