From a2a28af052a566090acc861caf0fa0bc93e138cf Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Mon, 6 Nov 2023 15:35:49 -0700 Subject: [PATCH 1/4] Add Modals P.S. this is all completely different now Co-Authored-By: Marshall Co-Authored-By: Julia --- Cargo.lock | 18 +++ Cargo.toml | 1 + crates/editor2/src/scroll.rs | 10 +- crates/go_to_line2/Cargo.toml | 25 +++ crates/go_to_line2/src/go_to_line.rs | 224 +++++++++++++++++++++++++++ crates/gpui2/src/window.rs | 4 +- crates/workspace2/src/modal_layer.rs | 121 +++++++++++++++ crates/workspace2/src/workspace2.rs | 94 ++--------- crates/zed2/Cargo.toml | 2 +- crates/zed2/src/main.rs | 23 +-- 10 files changed, 420 insertions(+), 102 deletions(-) create mode 100644 crates/go_to_line2/Cargo.toml create mode 100644 crates/go_to_line2/src/go_to_line.rs create mode 100644 crates/workspace2/src/modal_layer.rs diff --git a/Cargo.lock b/Cargo.lock index a110aef25c..aefa1d1260 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3529,6 +3529,23 @@ dependencies = [ "workspace", ] +[[package]] +name = "go_to_line2" +version = "0.1.0" +dependencies = [ + "editor2", + "gpui2", + "menu2", + "postage", + "serde", + "settings2", + "text2", + "theme2", + "ui2", + "util", + "workspace2", +] + [[package]] name = "gpui" version = "0.1.0" @@ -11223,6 +11240,7 @@ dependencies = [ "fsevent", "futures 0.3.28", "fuzzy", + "go_to_line2", "gpui2", "ignore", "image", diff --git a/Cargo.toml b/Cargo.toml index 268399ad3f..01d6c50643 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,6 +42,7 @@ members = [ "crates/fuzzy2", "crates/git", "crates/go_to_line", + "crates/go_to_line2", "crates/gpui", "crates/gpui_macros", "crates/gpui2", diff --git a/crates/editor2/src/scroll.rs b/crates/editor2/src/scroll.rs index 1876952ae2..d5aeb853bb 100644 --- a/crates/editor2/src/scroll.rs +++ b/crates/editor2/src/scroll.rs @@ -349,10 +349,12 @@ impl Editor { self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx); } - // pub fn scroll_position(&self, cx: &mut ViewContext) -> gpui::Point { - // let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); - // self.scroll_manager.anchor.scroll_position(&display_map) - // } + pub fn scroll_position(&self, cx: &mut ViewContext) -> gpui::Point { + let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); + // todo!() Should `self.scroll_manager.anchor.scroll_position()` return `Pixels`? + // self.scroll_manager.anchor.scroll_position(&display_map) + todo!() + } pub fn set_scroll_anchor(&mut self, scroll_anchor: ScrollAnchor, cx: &mut ViewContext) { hide_hover(self, cx); diff --git a/crates/go_to_line2/Cargo.toml b/crates/go_to_line2/Cargo.toml new file mode 100644 index 0000000000..66fb3b68ff --- /dev/null +++ b/crates/go_to_line2/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "go_to_line2" +version = "0.1.0" +edition = "2021" +publish = false + +[lib] +path = "src/go_to_line.rs" +doctest = false + +[dependencies] +editor = { package = "editor2", path = "../editor2" } +gpui = { package = "gpui2", path = "../gpui2" } +menu = { package = "menu2", path = "../menu2" } +serde.workspace = true +settings = { package = "settings2", path = "../settings2" } +text = { package = "text2", path = "../text2" } +workspace = { package = "workspace2", path = "../workspace2" } +postage.workspace = true +theme = { package = "theme2", path = "../theme2" } +ui = { package = "ui2", path = "../ui2" } +util = { path = "../util" } + +[dev-dependencies] +editor = { package = "editor2", path = "../editor2", features = ["test-support"] } diff --git a/crates/go_to_line2/src/go_to_line.rs b/crates/go_to_line2/src/go_to_line.rs new file mode 100644 index 0000000000..764602c986 --- /dev/null +++ b/crates/go_to_line2/src/go_to_line.rs @@ -0,0 +1,224 @@ +use gpui::{div, px, red, AppContext, Div, Render, Styled, ViewContext, VisualContext}; +use serde::Deserialize; +use workspace::ModalRegistry; + +// actions!(go_to_line, [Toggle]); +#[derive(Clone, Default, PartialEq, Deserialize)] +struct Toggle; + +pub fn init(cx: &mut AppContext) { + cx.global_mut::() + .register_modal(Toggle, |_, cx| { + // if let Some(editor) = workspace + // .active_item(cx) + // .and_then(|active_item| active_item.downcast::()) + // { + // cx.build_view(|cx| GoToLine::new(editor, cx)) + // } + let view = cx.build_view(|_| GoToLine); + view + }); + + // cx.add_action(GoToLine::toggle); + // cx.add_action(GoToLine::confirm); + // cx.add_action(GoToLine::cancel); +} + +pub struct GoToLine; + +impl Render for GoToLine { + type Element = Div; + + fn render(&mut self, _cx: &mut ViewContext) -> Self::Element { + dbg!("rendering GoToLine"); + div().bg(red()).w(px(100.0)).h(px(100.0)) + } +} + +// pub struct GoToLine { +// //line_editor: View, +// active_editor: View, +// prev_scroll_position: Option>, +// cursor_point: Point, +// max_point: Point, +// has_focus: bool, +// } + +// pub enum Event { +// Dismissed, +// } + +// impl GoToLine { +// pub fn new(active_editor: View, cx: &mut ViewContext) -> Self { +// // let line_editor = cx.build_view(|cx| { +// // Editor::single_line( +// // Some(Arc::new(|theme| theme.picker.input_editor.clone())), +// // cx, +// // ) +// // }); +// // cx.subscribe(&line_editor, Self::on_line_editor_event) +// // .detach(); + +// let (scroll_position, cursor_point, max_point) = active_editor.update(cx, |editor, cx| { +// let scroll_position = editor.scroll_position(cx); +// let buffer = editor.buffer().read(cx).snapshot(cx); +// ( +// Some(scroll_position), +// editor.selections.newest(cx).head(), +// buffer.max_point(), +// ) +// }); + +// cx.on_release(|_, on_release| {}).detach(); + +// Self { +// //line_editor, +// active_editor, +// prev_scroll_position: scroll_position, +// cursor_point, +// max_point, +// has_focus: false, +// } +// } + +// fn cancel(&mut self, _: &Cancel, cx: &mut ViewContext) { +// cx.emit(Event::Dismissed); +// } + +// fn confirm(&mut self, _: &Confirm, cx: &mut ViewContext) { +// self.prev_scroll_position.take(); +// if let Some(point) = self.point_from_query(cx) { +// self.active_editor.update(cx, |active_editor, cx| { +// let snapshot = active_editor.snapshot(cx).display_snapshot; +// let point = snapshot.buffer_snapshot.clip_point(point, Bias::Left); +// active_editor.change_selections(Some(Autoscroll::center()), cx, |s| { +// s.select_ranges([point..point]) +// }); +// }); +// } + +// cx.emit(Event::Dismissed); +// } + +// fn on_line_editor_event( +// &mut self, +// _: View, +// event: &editor::Event, +// cx: &mut ViewContext, +// ) { +// match event { +// editor::Event::Blurred => cx.emit(Event::Dismissed), +// editor::Event::BufferEdited { .. } => { +// if let Some(point) = self.point_from_query(cx) { +// // todo!() +// // self.active_editor.update(cx, |active_editor, cx| { +// // let snapshot = active_editor.snapshot(cx).display_snapshot; +// // let point = snapshot.buffer_snapshot.clip_point(point, Bias::Left); +// // let display_point = point.to_display_point(&snapshot); +// // let row = display_point.row(); +// // active_editor.highlight_rows(Some(row..row + 1)); +// // active_editor.request_autoscroll(Autoscroll::center(), cx); +// // }); +// cx.notify(); +// } +// } +// _ => {} +// } +// } + +// fn point_from_query(&self, cx: &ViewContext) -> Option { +// return None; +// // todo!() +// // let line_editor = self.line_editor.read(cx).text(cx); +// // let mut components = line_editor +// // .splitn(2, FILE_ROW_COLUMN_DELIMITER) +// // .map(str::trim) +// // .fuse(); +// // let row = components.next().and_then(|row| row.parse::().ok())?; +// // let column = components.next().and_then(|col| col.parse::().ok()); +// // Some(Point::new( +// // row.saturating_sub(1), +// // column.unwrap_or(0).saturating_sub(1), +// // )) +// } +// } + +// impl EventEmitter for GoToLine { +// type Event = Event; +// } + +// impl Entity for GoToLine { +// fn release(&mut self, cx: &mut AppContext) { +// let scroll_position = self.prev_scroll_position.take(); +// self.active_editor.window().update(cx, |cx| { +// self.active_editor.update(cx, |editor, cx| { +// editor.highlight_rows(None); +// if let Some(scroll_position) = scroll_position { +// editor.set_scroll_position(scroll_position, cx); +// } +// }) +// }); +// } +// } + +// impl Render for GoToLine { +// type Element = Div; + +// fn render(&mut self, cx: &mut ViewContext) -> Self::Element { +// // todo!() +// div() +// } +// } + +// impl View for GoToLine { +// fn ui_name() -> &'static str { +// "GoToLine" +// } + +// fn render(&mut self, cx: &mut ViewContext) -> AnyElement { +// let theme = &theme::current(cx).picker; + +// let label = format!( +// "{}{FILE_ROW_COLUMN_DELIMITER}{} of {} lines", +// self.cursor_point.row + 1, +// self.cursor_point.column + 1, +// self.max_point.row + 1 +// ); + +// Flex::new(Axis::Vertical) +// .with_child( +// ChildView::new(&self.line_editor, cx) +// .contained() +// .with_style(theme.input_editor.container), +// ) +// .with_child( +// Label::new(label, theme.no_matches.label.clone()) +// .contained() +// .with_style(theme.no_matches.container), +// ) +// .contained() +// .with_style(theme.container) +// .constrained() +// .with_max_width(500.0) +// .into_any_named("go to line") +// } + +// fn focus_in(&mut self, _: AnyView, cx: &mut ViewContext) { +// self.has_focus = true; +// cx.focus(&self.line_editor); +// } + +// fn focus_out(&mut self, _: AnyView, _: &mut ViewContext) { +// self.has_focus = false; +// } +// } + +// impl Modal for GoToLine { +// fn has_focus(&self) -> bool { +// self.has_focus +// } + +// fn dismiss_on_event(event: &Self::Event) -> bool { +// matches!(event, Event::Dismissed) +// } +// } diff --git a/crates/gpui2/src/window.rs b/crates/gpui2/src/window.rs index d7ee86e1c1..a98948fc96 100644 --- a/crates/gpui2/src/window.rs +++ b/crates/gpui2/src/window.rs @@ -2035,9 +2035,9 @@ impl Context for ViewContext<'_, V> { impl VisualContext for ViewContext<'_, V> { fn build_view( &mut self, - build_view: impl FnOnce(&mut ViewContext<'_, W>) -> W, + build_view_state: impl FnOnce(&mut ViewContext<'_, W>) -> W, ) -> Self::Result> { - self.window_cx.build_view(build_view) + self.window_cx.build_view(build_view_state) } fn update_view( diff --git a/crates/workspace2/src/modal_layer.rs b/crates/workspace2/src/modal_layer.rs new file mode 100644 index 0000000000..e7cee53b2b --- /dev/null +++ b/crates/workspace2/src/modal_layer.rs @@ -0,0 +1,121 @@ +use std::{any::TypeId, sync::Arc}; + +use gpui::{ + div, AnyView, AppContext, Component, DispatchPhase, Div, ParentElement, Render, + StatelessInteractive, View, ViewContext, +}; + +use crate::Workspace; + +pub struct ModalRegistry { + registered_modals: Vec<(TypeId, Box) -> Div>)>, +} + +pub trait Modal {} + +#[derive(Clone)] +pub struct ModalLayer { + open_modal: Option, +} + +pub fn init_modal_registry(cx: &mut AppContext) { + cx.set_global(ModalRegistry { + registered_modals: Vec::new(), + }); +} + +struct ToggleModal { + name: String, +} + +// complete change of plan? +// on_action(ToggleModal{ name}) +// register_modal(name, |workspace, cx| { ... }) + +impl ModalRegistry { + pub fn register_modal(&mut self, action: A, build_view: B) + where + V: Render, + B: Fn(&Workspace, &mut ViewContext) -> View + 'static, + { + let build_view = Arc::new(build_view); + + dbg!("yonder"); + self.registered_modals.push(( + TypeId::of::(), + Box::new(move |mut div| { + let build_view = build_view.clone(); + dbg!("this point"); + + div.on_action( + move |workspace: &mut Workspace, + event: &A, + phase: DispatchPhase, + cx: &mut ViewContext| { + dbg!("GOT HERE"); + if phase == DispatchPhase::Capture { + return; + } + + let new_modal = (build_view)(workspace, cx); + workspace.modal_layer.update(cx, |modal_layer, _| { + modal_layer.open_modal = Some(new_modal.into()); + }); + + cx.notify(); + }, + ) + }), + )); + } +} + +impl ModalLayer { + pub fn new() -> Self { + Self { open_modal: None } + } + + pub fn render(&self, cx: &ViewContext) -> impl Component { + dbg!("rendering ModalLayer"); + + let mut div = div(); + + // div, c workspace.toggle_modal()div.on_action()) { + // + // } + + // for (type_id, action) in cx.global::().registered_modals.iter() { + // div = div.useful_on_action(*type_id, action) + // } + + for (_, action) in cx.global::().registered_modals.iter() { + div = (action)(div); + } + + div.children(self.open_modal.clone()) + } +} + +// impl Render for ModalLayer { +// type Element = Div; + +// fn render(&mut self, cx: &mut ViewContext) -> Self::Element { +// let mut div = div(); +// for (type_id, build_view) in cx.global::().registered_modals { +// div = div.useful_on_action( +// type_id, +// Box::new(|this, _: dyn Any, phase, cx: &mut ViewContext| { +// if phase == DispatchPhase::Capture { +// return; +// } +// self.workspace.update(cx, |workspace, cx| { +// self.open_modal = Some(build_view(workspace, cx)); +// }); +// cx.notify(); +// }), +// ) +// } + +// div +// } +// } diff --git a/crates/workspace2/src/workspace2.rs b/crates/workspace2/src/workspace2.rs index 7561c903d3..6b8077cd38 100644 --- a/crates/workspace2/src/workspace2.rs +++ b/crates/workspace2/src/workspace2.rs @@ -10,6 +10,7 @@ mod persistence; pub mod searchable; // todo!() // pub mod shared_screen; +mod modal_layer; mod status_bar; mod toolbar; mod workspace_settings; @@ -45,6 +46,8 @@ use item::{FollowableItem, FollowableItemHandle, Item, ItemHandle, ItemSettings, use itertools::Itertools; use language2::LanguageRegistry; use lazy_static::lazy_static; +pub use modal_layer::ModalRegistry; +use modal_layer::{init_modal_registry, ModalLayer}; use node_runtime::NodeRuntime; use notifications::{simple_message_notification::MessageNotification, NotificationHandle}; pub use pane::*; @@ -81,26 +84,6 @@ lazy_static! { .and_then(parse_pixel_position_env_var); } -// pub trait Modal: View { -// fn has_focus(&self) -> bool; -// fn dismiss_on_event(event: &Self::Event) -> bool; -// } - -// trait ModalHandle { -// fn as_any(&self) -> &AnyViewHandle; -// fn has_focus(&self, cx: &WindowContext) -> bool; -// } - -// impl ModalHandle for View { -// fn as_any(&self) -> &AnyViewHandle { -// self -// } - -// fn has_focus(&self, cx: &WindowContext) -> bool { -// self.read(cx).has_focus() -// } -// } - // #[derive(Clone, PartialEq)] // pub struct RemoveWorktreeFromProject(pub WorktreeId); @@ -243,6 +226,7 @@ pub fn init_settings(cx: &mut AppContext) { pub fn init(app_state: Arc, cx: &mut AppContext) { init_settings(cx); + init_modal_registry(cx); pane::init(cx); notifications::init(cx); @@ -550,7 +534,6 @@ pub enum Event { pub struct Workspace { weak_self: WeakView, focus_handle: FocusHandle, - // modal: Option, zoomed: Option, zoomed_position: Option, center: PaneGroup, @@ -563,6 +546,7 @@ pub struct Workspace { last_active_center_pane: Option>, last_active_view_id: Option, status_bar: View, + modal_layer: View, // titlebar_item: Option, notifications: Vec<(TypeId, usize, Box)>, project: Model, @@ -580,11 +564,6 @@ pub struct Workspace { pane_history_timestamp: Arc, } -// struct ActiveModal { -// view: Box, -// previously_focused_view_id: Option, -// } - #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub struct ViewId { pub creator: PeerId, @@ -718,6 +697,8 @@ impl Workspace { status_bar }); + let modal_layer = cx.build_view(|cx| ModalLayer::new()); + // todo!() // cx.update_default_global::, _, _>(|drag_and_drop, _| { // drag_and_drop.register_container(weak_handle.clone()); @@ -769,7 +750,6 @@ impl Workspace { Workspace { weak_self: weak_handle.clone(), focus_handle: cx.focus_handle(), - // modal: None, zoomed: None, zoomed_position: None, center: PaneGroup::new(center_pane.clone()), @@ -779,6 +759,7 @@ impl Workspace { last_active_center_pane: Some(center_pane.downgrade()), last_active_view_id: None, status_bar, + modal_layer, // titlebar_item: None, notifications: Default::default(), left_dock, @@ -1620,63 +1601,6 @@ impl Workspace { }) } - // /// Returns the modal that was toggled closed if it was open. - // pub fn toggle_modal( - // &mut self, - // cx: &mut ViewContext, - // build_view: F, - // ) -> Option> - // where - // V: 'static + Modal, - // F: FnOnce(&mut Self, &mut ViewContext) -> View, - // { - // cx.notify(); - // // Whatever modal was visible is getting clobbered. If its the same type as V, then return - // // it. Otherwise, create a new modal and set it as active. - // if let Some(already_open_modal) = self - // .dismiss_modal(cx) - // .and_then(|modal| modal.downcast::()) - // { - // cx.focus_self(); - // Some(already_open_modal) - // } else { - // let modal = build_view(self, cx); - // cx.subscribe(&modal, |this, _, event, cx| { - // if V::dismiss_on_event(event) { - // this.dismiss_modal(cx); - // } - // }) - // .detach(); - // let previously_focused_view_id = cx.focused_view_id(); - // cx.focus(&modal); - // self.modal = Some(ActiveModal { - // view: Box::new(modal), - // previously_focused_view_id, - // }); - // None - // } - // } - - // pub fn modal(&self) -> Option> { - // self.modal - // .as_ref() - // .and_then(|modal| modal.view.as_any().clone().downcast::()) - // } - - // pub fn dismiss_modal(&mut self, cx: &mut ViewContext) -> Option { - // if let Some(modal) = self.modal.take() { - // if let Some(previously_focused_view_id) = modal.previously_focused_view_id { - // if modal.view.has_focus(cx) { - // cx.window_context().focus(Some(previously_focused_view_id)); - // } - // } - // cx.notify(); - // Some(modal.view.as_any().clone()) - // } else { - // None - // } - // } - pub fn items<'a>( &'a self, cx: &'a AppContext, @@ -3916,6 +3840,8 @@ impl Render for Workspace { // .on_click(Arc::new(|workspace, cx| workspace.toggle_debug(cx))), // ), ) + // .child(self.modal_layer.clone()) + .child(self.modal_layer.read(cx).render(cx)) } } diff --git a/crates/zed2/Cargo.toml b/crates/zed2/Cargo.toml index d510de24ee..2c93de734e 100644 --- a/crates/zed2/Cargo.toml +++ b/crates/zed2/Cargo.toml @@ -41,7 +41,7 @@ editor = { package="editor2", path = "../editor2" } fs = { package = "fs2", path = "../fs2" } fsevent = { path = "../fsevent" } fuzzy = { path = "../fuzzy" } -# go_to_line = { path = "../go_to_line" } +go_to_line = { package = "go_to_line2", path = "../go_to_line2" } gpui = { package = "gpui2", path = "../gpui2" } install_cli = { path = "../install_cli" } journal = { package = "journal2", path = "../journal2" } diff --git a/crates/zed2/src/main.rs b/crates/zed2/src/main.rs index 580afd2653..a93426eb04 100644 --- a/crates/zed2/src/main.rs +++ b/crates/zed2/src/main.rs @@ -140,17 +140,6 @@ fn main() { // command_palette::init(cx); language::init(cx); editor::init(cx); - // go_to_line::init(cx); - // file_finder::init(cx); - // outline::init(cx); - // project_symbols::init(cx); - // project_panel::init(Assets, cx); - // channel::init(&client, user_store.clone(), cx); - // diagnostics::init(cx); - // search::init(cx); - // semantic_index::init(fs.clone(), http.clone(), languages.clone(), cx); - // vim::init(cx); - // terminal_view::init(cx); copilot::init( copilot_language_server_id, http.clone(), @@ -193,6 +182,18 @@ fn main() { workspace::init(app_state.clone(), cx); // recent_projects::init(cx); + go_to_line::init(cx); + // file_finder::init(cx); + // outline::init(cx); + // project_symbols::init(cx); + // project_panel::init(Assets, cx); + // channel::init(&client, user_store.clone(), cx); + // diagnostics::init(cx); + // search::init(cx); + // semantic_index::init(fs.clone(), http.clone(), languages.clone(), cx); + // vim::init(cx); + // terminal_view::init(cx); + // journal2::init(app_state.clone(), cx); // language_selector::init(cx); // theme_selector::init(cx); From 82a018996bdd197b18d2e0c0ba80ea0c49353149 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 7 Nov 2023 17:54:14 +0100 Subject: [PATCH 2/4] WIP --- crates/editor2/src/editor.rs | 550 +++++++++++++------------- crates/editor2/src/element.rs | 80 +++- crates/editor2/src/movement.rs | 4 +- crates/gpui2/src/action.rs | 3 +- crates/gpui2/src/app.rs | 14 +- crates/gpui2/src/window.rs | 2 + crates/settings2/src/settings_file.rs | 49 ++- crates/zed2/src/main.rs | 7 +- 8 files changed, 425 insertions(+), 284 deletions(-) diff --git a/crates/editor2/src/editor.rs b/crates/editor2/src/editor.rs index 7a2511fa6d..78cbbd9101 100644 --- a/crates/editor2/src/editor.rs +++ b/crates/editor2/src/editor.rs @@ -36,8 +36,8 @@ pub use element::{ use futures::FutureExt; use fuzzy::{StringMatch, StringMatchCandidate}; use gpui::{ - div, px, AnyElement, AppContext, BackgroundExecutor, Context, Div, Element, Entity, - EventEmitter, FocusHandle, FontStyle, FontWeight, Hsla, Model, Pixels, Render, Styled, + div, px, AnyElement, AppContext, BackgroundExecutor, Context, DispatchContext, Div, Element, + Entity, EventEmitter, FocusHandle, FontStyle, FontWeight, Hsla, Model, Pixels, Render, Styled, Subscription, Task, TextStyle, View, ViewContext, VisualContext, WeakView, WindowContext, }; use highlight_matching_bracket::refresh_matching_bracket_highlights; @@ -54,6 +54,7 @@ use language::{ }; use link_go_to_definition::{GoToDefinitionLink, InlayHighlight, LinkGoToDefinitionState}; use lsp::{DiagnosticSeverity, Documentation, LanguageServerId}; +use movement::TextLayoutDetails; pub use multi_buffer::{ Anchor, AnchorRangeExt, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, ToOffset, ToPoint, @@ -397,6 +398,18 @@ pub struct GoToTypeDefinition; pub struct GoToDefinitionSplit; pub struct GoToTypeDefinitionSplit; +#[derive(PartialEq, Clone, Default, Deserialize)] +pub struct MoveLeft; + +#[derive(PartialEq, Clone, Default, Deserialize)] +pub struct MoveRight; + +#[derive(PartialEq, Clone, Default, Deserialize)] +pub struct MoveDown; + +#[derive(PartialEq, Clone, Default, Deserialize)] +pub struct MoveUp; + enum DocumentHighlightRead {} enum DocumentHighlightWrite {} enum InputComposition {} @@ -413,130 +426,130 @@ pub fn init_settings(cx: &mut AppContext) { pub fn init(cx: &mut AppContext) { init_settings(cx); - // cx.add_action(Editor::new_file); - // cx.add_action(Editor::new_file_in_direction); - // cx.add_action(Editor::cancel); - // cx.add_action(Editor::newline); - // cx.add_action(Editor::newline_above); - // cx.add_action(Editor::newline_below); - // cx.add_action(Editor::backspace); - // cx.add_action(Editor::delete); - // cx.add_action(Editor::tab); - // cx.add_action(Editor::tab_prev); - // cx.add_action(Editor::indent); - // cx.add_action(Editor::outdent); - // cx.add_action(Editor::delete_line); - // cx.add_action(Editor::join_lines); - // cx.add_action(Editor::sort_lines_case_sensitive); - // cx.add_action(Editor::sort_lines_case_insensitive); - // cx.add_action(Editor::reverse_lines); - // cx.add_action(Editor::shuffle_lines); - // cx.add_action(Editor::convert_to_upper_case); - // cx.add_action(Editor::convert_to_lower_case); - // cx.add_action(Editor::convert_to_title_case); - // cx.add_action(Editor::convert_to_snake_case); - // cx.add_action(Editor::convert_to_kebab_case); - // cx.add_action(Editor::convert_to_upper_camel_case); - // cx.add_action(Editor::convert_to_lower_camel_case); - // cx.add_action(Editor::delete_to_previous_word_start); - // cx.add_action(Editor::delete_to_previous_subword_start); - // cx.add_action(Editor::delete_to_next_word_end); - // cx.add_action(Editor::delete_to_next_subword_end); - // cx.add_action(Editor::delete_to_beginning_of_line); - // cx.add_action(Editor::delete_to_end_of_line); - // cx.add_action(Editor::cut_to_end_of_line); - // cx.add_action(Editor::duplicate_line); - // cx.add_action(Editor::move_line_up); - // cx.add_action(Editor::move_line_down); - // cx.add_action(Editor::transpose); - // cx.add_action(Editor::cut); - // cx.add_action(Editor::copy); - // cx.add_action(Editor::paste); - // cx.add_action(Editor::undo); - // cx.add_action(Editor::redo); - // cx.add_action(Editor::move_up); - // cx.add_action(Editor::move_page_up); - // cx.add_action(Editor::move_down); - // cx.add_action(Editor::move_page_down); - // cx.add_action(Editor::next_screen); - // cx.add_action(Editor::move_left); - // cx.add_action(Editor::move_right); - // cx.add_action(Editor::move_to_previous_word_start); - // cx.add_action(Editor::move_to_previous_subword_start); - // cx.add_action(Editor::move_to_next_word_end); - // cx.add_action(Editor::move_to_next_subword_end); - // cx.add_action(Editor::move_to_beginning_of_line); - // cx.add_action(Editor::move_to_end_of_line); - // cx.add_action(Editor::move_to_start_of_paragraph); - // cx.add_action(Editor::move_to_end_of_paragraph); - // cx.add_action(Editor::move_to_beginning); - // cx.add_action(Editor::move_to_end); - // cx.add_action(Editor::select_up); - // cx.add_action(Editor::select_down); - // cx.add_action(Editor::select_left); - // cx.add_action(Editor::select_right); - // cx.add_action(Editor::select_to_previous_word_start); - // cx.add_action(Editor::select_to_previous_subword_start); - // cx.add_action(Editor::select_to_next_word_end); - // cx.add_action(Editor::select_to_next_subword_end); - // cx.add_action(Editor::select_to_beginning_of_line); - // cx.add_action(Editor::select_to_end_of_line); - // cx.add_action(Editor::select_to_start_of_paragraph); - // cx.add_action(Editor::select_to_end_of_paragraph); - // cx.add_action(Editor::select_to_beginning); - // cx.add_action(Editor::select_to_end); - // cx.add_action(Editor::select_all); - // cx.add_action(Editor::select_all_matches); - // cx.add_action(Editor::select_line); - // cx.add_action(Editor::split_selection_into_lines); - // cx.add_action(Editor::add_selection_above); - // cx.add_action(Editor::add_selection_below); - // cx.add_action(Editor::select_next); - // cx.add_action(Editor::select_previous); - // cx.add_action(Editor::toggle_comments); - // cx.add_action(Editor::select_larger_syntax_node); - // cx.add_action(Editor::select_smaller_syntax_node); - // cx.add_action(Editor::move_to_enclosing_bracket); - // cx.add_action(Editor::undo_selection); - // cx.add_action(Editor::redo_selection); - // cx.add_action(Editor::go_to_diagnostic); - // cx.add_action(Editor::go_to_prev_diagnostic); - // cx.add_action(Editor::go_to_hunk); - // cx.add_action(Editor::go_to_prev_hunk); - // cx.add_action(Editor::go_to_definition); - // cx.add_action(Editor::go_to_definition_split); - // cx.add_action(Editor::go_to_type_definition); - // cx.add_action(Editor::go_to_type_definition_split); - // cx.add_action(Editor::fold); - // cx.add_action(Editor::fold_at); - // cx.add_action(Editor::unfold_lines); - // cx.add_action(Editor::unfold_at); - // cx.add_action(Editor::gutter_hover); - // cx.add_action(Editor::fold_selected_ranges); - // cx.add_action(Editor::show_completions); - // cx.add_action(Editor::toggle_code_actions); - // cx.add_action(Editor::open_excerpts); - // cx.add_action(Editor::toggle_soft_wrap); - // cx.add_action(Editor::toggle_inlay_hints); - // cx.add_action(Editor::reveal_in_finder); - // cx.add_action(Editor::copy_path); - // cx.add_action(Editor::copy_relative_path); - // cx.add_action(Editor::copy_highlight_json); + // cx.register_action_type(Editor::new_file); + // cx.register_action_type(Editor::new_file_in_direction); + // cx.register_action_type(Editor::cancel); + // cx.register_action_type(Editor::newline); + // cx.register_action_type(Editor::newline_above); + // cx.register_action_type(Editor::newline_below); + // cx.register_action_type(Editor::backspace); + // cx.register_action_type(Editor::delete); + // cx.register_action_type(Editor::tab); + // cx.register_action_type(Editor::tab_prev); + // cx.register_action_type(Editor::indent); + // cx.register_action_type(Editor::outdent); + // cx.register_action_type(Editor::delete_line); + // cx.register_action_type(Editor::join_lines); + // cx.register_action_type(Editor::sort_lines_case_sensitive); + // cx.register_action_type(Editor::sort_lines_case_insensitive); + // cx.register_action_type(Editor::reverse_lines); + // cx.register_action_type(Editor::shuffle_lines); + // cx.register_action_type(Editor::convert_to_upper_case); + // cx.register_action_type(Editor::convert_to_lower_case); + // cx.register_action_type(Editor::convert_to_title_case); + // cx.register_action_type(Editor::convert_to_snake_case); + // cx.register_action_type(Editor::convert_to_kebab_case); + // cx.register_action_type(Editor::convert_to_upper_camel_case); + // cx.register_action_type(Editor::convert_to_lower_camel_case); + // cx.register_action_type(Editor::delete_to_previous_word_start); + // cx.register_action_type(Editor::delete_to_previous_subword_start); + // cx.register_action_type(Editor::delete_to_next_word_end); + // cx.register_action_type(Editor::delete_to_next_subword_end); + // cx.register_action_type(Editor::delete_to_beginning_of_line); + // cx.register_action_type(Editor::delete_to_end_of_line); + // cx.register_action_type(Editor::cut_to_end_of_line); + // cx.register_action_type(Editor::duplicate_line); + // cx.register_action_type(Editor::move_line_up); + // cx.register_action_type(Editor::move_line_down); + // cx.register_action_type(Editor::transpose); + // cx.register_action_type(Editor::cut); + // cx.register_action_type(Editor::copy); + // cx.register_action_type(Editor::paste); + // cx.register_action_type(Editor::undo); + // cx.register_action_type(Editor::redo); + cx.register_action_type::(); + // cx.register_action_type(Editor::move_page_up); + cx.register_action_type::(); + // cx.register_action_type(Editor::move_page_down); + // cx.register_action_type(Editor::next_screen); + cx.register_action_type::(); + cx.register_action_type::(); + // cx.register_action_type(Editor::move_to_previous_word_start); + // cx.register_action_type(Editor::move_to_previous_subword_start); + // cx.register_action_type(Editor::move_to_next_word_end); + // cx.register_action_type(Editor::move_to_next_subword_end); + // cx.register_action_type(Editor::move_to_beginning_of_line); + // cx.register_action_type(Editor::move_to_end_of_line); + // cx.register_action_type(Editor::move_to_start_of_paragraph); + // cx.register_action_type(Editor::move_to_end_of_paragraph); + // cx.register_action_type(Editor::move_to_beginning); + // cx.register_action_type(Editor::move_to_end); + // cx.register_action_type(Editor::select_up); + // cx.register_action_type(Editor::select_down); + // cx.register_action_type(Editor::select_left); + // cx.register_action_type(Editor::select_right); + // cx.register_action_type(Editor::select_to_previous_word_start); + // cx.register_action_type(Editor::select_to_previous_subword_start); + // cx.register_action_type(Editor::select_to_next_word_end); + // cx.register_action_type(Editor::select_to_next_subword_end); + // cx.register_action_type(Editor::select_to_beginning_of_line); + // cx.register_action_type(Editor::select_to_end_of_line); + // cx.register_action_type(Editor::select_to_start_of_paragraph); + // cx.register_action_type(Editor::select_to_end_of_paragraph); + // cx.register_action_type(Editor::select_to_beginning); + // cx.register_action_type(Editor::select_to_end); + // cx.register_action_type(Editor::select_all); + // cx.register_action_type(Editor::select_all_matches); + // cx.register_action_type(Editor::select_line); + // cx.register_action_type(Editor::split_selection_into_lines); + // cx.register_action_type(Editor::add_selection_above); + // cx.register_action_type(Editor::add_selection_below); + // cx.register_action_type(Editor::select_next); + // cx.register_action_type(Editor::select_previous); + // cx.register_action_type(Editor::toggle_comments); + // cx.register_action_type(Editor::select_larger_syntax_node); + // cx.register_action_type(Editor::select_smaller_syntax_node); + // cx.register_action_type(Editor::move_to_enclosing_bracket); + // cx.register_action_type(Editor::undo_selection); + // cx.register_action_type(Editor::redo_selection); + // cx.register_action_type(Editor::go_to_diagnostic); + // cx.register_action_type(Editor::go_to_prev_diagnostic); + // cx.register_action_type(Editor::go_to_hunk); + // cx.register_action_type(Editor::go_to_prev_hunk); + // cx.register_action_type(Editor::go_to_definition); + // cx.register_action_type(Editor::go_to_definition_split); + // cx.register_action_type(Editor::go_to_type_definition); + // cx.register_action_type(Editor::go_to_type_definition_split); + // cx.register_action_type(Editor::fold); + // cx.register_action_type(Editor::fold_at); + // cx.register_action_type(Editor::unfold_lines); + // cx.register_action_type(Editor::unfold_at); + // cx.register_action_type(Editor::gutter_hover); + // cx.register_action_type(Editor::fold_selected_ranges); + // cx.register_action_type(Editor::show_completions); + // cx.register_action_type(Editor::toggle_code_actions); + // cx.register_action_type(Editor::open_excerpts); + // cx.register_action_type(Editor::toggle_soft_wrap); + // cx.register_action_type(Editor::toggle_inlay_hints); + // cx.register_action_type(Editor::reveal_in_finder); + // cx.register_action_type(Editor::copy_path); + // cx.register_action_type(Editor::copy_relative_path); + // cx.register_action_type(Editor::copy_highlight_json); // cx.add_async_action(Editor::format); - // cx.add_action(Editor::restart_language_server); - // cx.add_action(Editor::show_character_palette); + // cx.register_action_type(Editor::restart_language_server); + // cx.register_action_type(Editor::show_character_palette); // cx.add_async_action(Editor::confirm_completion); // cx.add_async_action(Editor::confirm_code_action); // cx.add_async_action(Editor::rename); // cx.add_async_action(Editor::confirm_rename); // cx.add_async_action(Editor::find_all_references); - // cx.add_action(Editor::next_copilot_suggestion); - // cx.add_action(Editor::previous_copilot_suggestion); - // cx.add_action(Editor::copilot_suggest); - // cx.add_action(Editor::context_menu_first); - // cx.add_action(Editor::context_menu_prev); - // cx.add_action(Editor::context_menu_next); - // cx.add_action(Editor::context_menu_last); + // cx.register_action_type(Editor::next_copilot_suggestion); + // cx.register_action_type(Editor::previous_copilot_suggestion); + // cx.register_action_type(Editor::copilot_suggest); + // cx.register_action_type(Editor::context_menu_first); + // cx.register_action_type(Editor::context_menu_prev); + // cx.register_action_type(Editor::context_menu_next); + // cx.register_action_type(Editor::context_menu_last); hover_popover::init(cx); scroll::actions::init(cx); @@ -657,7 +670,7 @@ pub struct Editor { collapse_matches: bool, autoindent_mode: Option, workspace: Option<(WeakView, i64)>, - // keymap_context_layers: BTreeMap, + keymap_context_layers: BTreeMap, input_enabled: bool, read_only: bool, leader_peer_id: Option, @@ -670,6 +683,7 @@ pub struct Editor { next_inlay_id: usize, _subscriptions: Vec, pixel_position_of_newest_cursor: Option>, + style: Option, } pub struct EditorSnapshot { @@ -1965,7 +1979,7 @@ impl Editor { autoindent_mode: Some(AutoindentMode::EachLine), collapse_matches: false, workspace: None, - // keymap_context_layers: Default::default(), + keymap_context_layers: Default::default(), input_enabled: true, read_only: false, leader_peer_id: None, @@ -1976,6 +1990,7 @@ impl Editor { inlay_hint_cache: InlayHintCache::new(inlay_hint_settings), gutter_hovered: false, pixel_position_of_newest_cursor: None, + style: None, _subscriptions: vec![ cx.observe(&buffer, Self::on_buffer_changed), cx.subscribe(&buffer, Self::on_buffer_event), @@ -2014,6 +2029,48 @@ impl Editor { this } + fn dispatch_context(&self, cx: &AppContext) -> DispatchContext { + let mut dispatch_context = DispatchContext::default(); + dispatch_context.insert("Editor"); + let mode = match self.mode { + EditorMode::SingleLine => "single_line", + EditorMode::AutoHeight { .. } => "auto_height", + EditorMode::Full => "full", + }; + dispatch_context.set("mode", mode); + if self.pending_rename.is_some() { + dispatch_context.insert("renaming"); + } + if self.context_menu_visible() { + match self.context_menu.read().as_ref() { + Some(ContextMenu::Completions(_)) => { + dispatch_context.insert("menu"); + dispatch_context.insert("showing_completions") + } + Some(ContextMenu::CodeActions(_)) => { + dispatch_context.insert("menu"); + dispatch_context.insert("showing_code_actions") + } + None => {} + } + } + + for layer in self.keymap_context_layers.values() { + dispatch_context.extend(layer); + } + + if let Some(extension) = self + .buffer + .read(cx) + .as_singleton() + .and_then(|buffer| buffer.read(cx).file()?.path().extension()?.to_str()) + { + dispatch_context.set("extension", extension.to_string()); + } + + dispatch_context + } + // pub fn new_file( // workspace: &mut Workspace, // _: &workspace::NewFile, @@ -2021,7 +2078,7 @@ impl Editor { // ) { // let project = workspace.project().clone(); // if project.read(cx).is_remote() { - // cx.propagate_action(); + // cx.propagate(); // } else if let Some(buffer) = project // .update(cx, |project, cx| project.create_buffer("", None, cx)) // .log_err() @@ -2040,7 +2097,7 @@ impl Editor { // ) { // let project = workspace.project().clone(); // if project.read(cx).is_remote() { - // cx.propagate_action(); + // cx.propagate(); // } else if let Some(buffer) = project // .update(cx, |project, cx| project.create_buffer("", None, cx)) // .log_err() @@ -2731,7 +2788,7 @@ impl Editor { // } // } - // cx.propagate_action(); + // cx.propagate(); // } // pub fn handle_input(&mut self, text: &str, cx: &mut ViewContext) { @@ -3482,13 +3539,12 @@ impl Editor { .collect() } - // pub fn text_layout_details(&self, cx: &WindowContext) -> TextLayoutDetails { - // TextLayoutDetails { - // font_cache: cx.font_cache().clone(), - // text_layout_cache: cx.text_layout_cache().clone(), - // editor_style: self.style(cx), - // } - // } + pub fn text_layout_details(&self, cx: &WindowContext) -> TextLayoutDetails { + TextLayoutDetails { + text_system: cx.text_system().clone(), + editor_style: self.style.clone().unwrap(), + } + } fn splice_inlay_hints( &self, @@ -4207,7 +4263,7 @@ impl Editor { let is_copilot_disabled = self.refresh_copilot_suggestions(false, cx).is_none(); if is_copilot_disabled { todo!(); - // cx.propagate_action(); + // cx.propagate(); } } } @@ -4429,12 +4485,14 @@ impl Editor { // .collect() // } - // pub fn context_menu_visible(&self) -> bool { - // self.context_menu - // .read() - // .as_ref() - // .map_or(false, |menu| menu.visible()) - // } + pub fn context_menu_visible(&self) -> bool { + false + // todo!("context menu") + // self.context_menu + // .read() + // .as_ref() + // .map_or(false, |menu| menu.visible()) + } // pub fn render_context_menu( // &self, @@ -5681,19 +5739,19 @@ impl Editor { // .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx)); // } - // pub fn move_left(&mut self, _: &MoveLeft, cx: &mut ViewContext) { - // self.change_selections(Some(Autoscroll::fit()), cx, |s| { - // let line_mode = s.line_mode; - // s.move_with(|map, selection| { - // let cursor = if selection.is_empty() && !line_mode { - // movement::left(map, selection.start) - // } else { - // selection.start - // }; - // selection.collapse_to(cursor, SelectionGoal::None); - // }); - // }) - // } + pub fn move_left(&mut self, _: &MoveLeft, cx: &mut ViewContext) { + self.change_selections(Some(Autoscroll::fit()), cx, |s| { + let line_mode = s.line_mode; + s.move_with(|map, selection| { + let cursor = if selection.is_empty() && !line_mode { + movement::left(map, selection.start) + } else { + selection.start + }; + selection.collapse_to(cursor, SelectionGoal::None); + }); + }) + } // pub fn select_left(&mut self, _: &SelectLeft, cx: &mut ViewContext) { // self.change_selections(Some(Autoscroll::fit()), cx, |s| { @@ -5701,19 +5759,19 @@ impl Editor { // }) // } - // pub fn move_right(&mut self, _: &MoveRight, cx: &mut ViewContext) { - // self.change_selections(Some(Autoscroll::fit()), cx, |s| { - // let line_mode = s.line_mode; - // s.move_with(|map, selection| { - // let cursor = if selection.is_empty() && !line_mode { - // movement::right(map, selection.end) - // } else { - // selection.end - // }; - // selection.collapse_to(cursor, SelectionGoal::None) - // }); - // }) - // } + pub fn move_right(&mut self, _: &MoveRight, cx: &mut ViewContext) { + self.change_selections(Some(Autoscroll::fit()), cx, |s| { + let line_mode = s.line_mode; + s.move_with(|map, selection| { + let cursor = if selection.is_empty() && !line_mode { + movement::right(map, selection.end) + } else { + selection.end + }; + selection.collapse_to(cursor, SelectionGoal::None) + }); + }) + } // pub fn select_right(&mut self, _: &SelectRight, cx: &mut ViewContext) { // self.change_selections(Some(Autoscroll::fit()), cx, |s| { @@ -5721,35 +5779,35 @@ impl Editor { // }) // } - // pub fn move_up(&mut self, _: &MoveUp, cx: &mut ViewContext) { - // if self.take_rename(true, cx).is_some() { - // return; - // } + pub fn move_up(&mut self, _: &MoveUp, cx: &mut ViewContext) { + if self.take_rename(true, cx).is_some() { + return; + } - // if matches!(self.mode, EditorMode::SingleLine) { - // cx.propagate_action(); - // return; - // } + if matches!(self.mode, EditorMode::SingleLine) { + cx.propagate(); + return; + } - // let text_layout_details = &self.text_layout_details(cx); + let text_layout_details = &self.text_layout_details(cx); - // self.change_selections(Some(Autoscroll::fit()), cx, |s| { - // let line_mode = s.line_mode; - // s.move_with(|map, selection| { - // if !selection.is_empty() && !line_mode { - // selection.goal = SelectionGoal::None; - // } - // let (cursor, goal) = movement::up( - // map, - // selection.start, - // selection.goal, - // false, - // &text_layout_details, - // ); - // selection.collapse_to(cursor, goal); - // }); - // }) - // } + self.change_selections(Some(Autoscroll::fit()), cx, |s| { + let line_mode = s.line_mode; + s.move_with(|map, selection| { + if !selection.is_empty() && !line_mode { + selection.goal = SelectionGoal::None; + } + let (cursor, goal) = movement::up( + map, + selection.start, + selection.goal, + false, + &text_layout_details, + ); + selection.collapse_to(cursor, goal); + }); + }) + } // pub fn move_page_up(&mut self, action: &MovePageUp, cx: &mut ViewContext) { // if self.take_rename(true, cx).is_some() { @@ -5757,7 +5815,7 @@ impl Editor { // } // if matches!(self.mode, EditorMode::SingleLine) { - // cx.propagate_action(); + // cx.propagate(); // return; // } @@ -5803,32 +5861,33 @@ impl Editor { // }) // } - // pub fn move_down(&mut self, _: &MoveDown, cx: &mut ViewContext) { - // self.take_rename(true, cx); + pub fn move_down(&mut self, _: &MoveDown, cx: &mut ViewContext) { + dbg!("move_down"); + self.take_rename(true, cx); - // if self.mode == EditorMode::SingleLine { - // cx.propagate_action(); - // return; - // } + if self.mode == EditorMode::SingleLine { + cx.propagate(); + return; + } - // let text_layout_details = &self.text_layout_details(cx); - // self.change_selections(Some(Autoscroll::fit()), cx, |s| { - // let line_mode = s.line_mode; - // s.move_with(|map, selection| { - // if !selection.is_empty() && !line_mode { - // selection.goal = SelectionGoal::None; - // } - // let (cursor, goal) = movement::down( - // map, - // selection.end, - // selection.goal, - // false, - // &text_layout_details, - // ); - // selection.collapse_to(cursor, goal); - // }); - // }); - // } + let text_layout_details = &self.text_layout_details(cx); + self.change_selections(Some(Autoscroll::fit()), cx, |s| { + let line_mode = s.line_mode; + s.move_with(|map, selection| { + if !selection.is_empty() && !line_mode { + selection.goal = SelectionGoal::None; + } + let (cursor, goal) = movement::down( + map, + selection.end, + selection.goal, + false, + &text_layout_details, + ); + selection.collapse_to(cursor, goal); + }); + }); + } // pub fn move_page_down(&mut self, action: &MovePageDown, cx: &mut ViewContext) { // if self.take_rename(true, cx).is_some() { @@ -5846,7 +5905,7 @@ impl Editor { // } // if matches!(self.mode, EditorMode::SingleLine) { - // cx.propagate_action(); + // cx.propagate(); // return; // } @@ -6193,7 +6252,7 @@ impl Editor { // cx: &mut ViewContext, // ) { // if matches!(self.mode, EditorMode::SingleLine) { - // cx.propagate_action(); + // cx.propagate(); // return; // } @@ -6213,7 +6272,7 @@ impl Editor { // cx: &mut ViewContext, // ) { // if matches!(self.mode, EditorMode::SingleLine) { - // cx.propagate_action(); + // cx.propagate(); // return; // } @@ -6233,7 +6292,7 @@ impl Editor { // cx: &mut ViewContext, // ) { // if matches!(self.mode, EditorMode::SingleLine) { - // cx.propagate_action(); + // cx.propagate(); // return; // } @@ -6253,7 +6312,7 @@ impl Editor { // cx: &mut ViewContext, // ) { // if matches!(self.mode, EditorMode::SingleLine) { - // cx.propagate_action(); + // cx.propagate(); // return; // } @@ -6269,7 +6328,7 @@ impl Editor { // pub fn move_to_beginning(&mut self, _: &MoveToBeginning, cx: &mut ViewContext) { // if matches!(self.mode, EditorMode::SingleLine) { - // cx.propagate_action(); + // cx.propagate(); // return; // } @@ -6289,7 +6348,7 @@ impl Editor { // pub fn move_to_end(&mut self, _: &MoveToEnd, cx: &mut ViewContext) { // if matches!(self.mode, EditorMode::SingleLine) { - // cx.propagate_action(); + // cx.propagate(); // return; // } @@ -8807,14 +8866,14 @@ impl Editor { // { // editor // } else { - // cx.propagate_action(); + // cx.propagate(); // return; // }; // let editor = editor_handle.read(cx); // let buffer = editor.buffer.read(cx); // if buffer.is_singleton() { - // cx.propagate_action(); + // cx.propagate(); // return; // } @@ -9443,46 +9502,7 @@ impl Render for Editor { // false // } - -// fn update_keymap_context(&self, keymap: &mut KeymapContext, cx: &AppContext) { -// Self::reset_to_default_keymap_context(keymap); -// let mode = match self.mode { -// EditorMode::SingleLine => "single_line", -// EditorMode::AutoHeight { .. } => "auto_height", -// EditorMode::Full => "full", -// }; -// keymap.add_key("mode", mode); -// if self.pending_rename.is_some() { -// keymap.add_identifier("renaming"); -// } -// if self.context_menu_visible() { -// match self.context_menu.read().as_ref() { -// Some(ContextMenu::Completions(_)) => { -// keymap.add_identifier("menu"); -// keymap.add_identifier("showing_completions") -// } -// Some(ContextMenu::CodeActions(_)) => { -// keymap.add_identifier("menu"); -// keymap.add_identifier("showing_code_actions") -// } -// None => {} -// } -// } - -// for layer in self.keymap_context_layers.values() { -// keymap.extend(layer); -// } - -// if let Some(extension) = self -// .buffer -// .read(cx) -// .as_singleton() -// .and_then(|buffer| buffer.read(cx).file()?.path().extension()?.to_str()) -// { -// keymap.add_key("extension", extension.to_string()); -// } -// } - +// // fn text_for_range(&self, range_utf16: Range, cx: &AppContext) -> Option { // Some( // self.buffer diff --git a/crates/editor2/src/element.rs b/crates/editor2/src/element.rs index 4d8cd57abc..918cfce43f 100644 --- a/crates/editor2/src/element.rs +++ b/crates/editor2/src/element.rs @@ -3,15 +3,15 @@ use crate::{ editor_settings::ShowScrollbar, git::{diff_hunk_to_display, DisplayDiffHunk}, CursorShape, DisplayPoint, Editor, EditorMode, EditorSettings, EditorSnapshot, EditorStyle, - Point, Selection, SoftWrap, ToPoint, MAX_LINE_LEN, + MoveDown, Point, Selection, SoftWrap, ToPoint, MAX_LINE_LEN, }; use anyhow::Result; use collections::{BTreeMap, HashMap}; use gpui::{ - black, hsla, point, px, relative, size, transparent_black, AnyElement, BorrowWindow, Bounds, - ContentMask, Corners, DispatchPhase, Edges, Element, ElementId, Hsla, Line, Pixels, - ScrollWheelEvent, ShapedGlyph, Size, StatefulInteraction, Style, TextRun, TextStyle, - TextSystem, ViewContext, WindowContext, + black, hsla, point, px, relative, size, transparent_black, Action, AnyElement, BorrowWindow, + Bounds, ContentMask, Corners, DispatchContext, DispatchPhase, Edges, Element, ElementId, + Entity, Hsla, KeyDownEvent, KeyListener, KeyMatch, Line, Pixels, ScrollWheelEvent, ShapedGlyph, + Size, StatefulInteraction, Style, TextRun, TextStyle, TextSystem, ViewContext, WindowContext, }; use itertools::Itertools; use language::language_settings::ShowWhitespaceSetting; @@ -20,6 +20,7 @@ use project::project_settings::{GitGutterSetting, ProjectSettings}; use settings::Settings; use smallvec::SmallVec; use std::{ + any::TypeId, borrow::Cow, cmp::{self, Ordering}, fmt::Write, @@ -94,14 +95,12 @@ impl SelectionLayout { } pub struct EditorElement { - style: Arc, + style: EditorStyle, } impl EditorElement { pub fn new(style: EditorStyle) -> Self { - Self { - style: Arc::new(style), - } + Self { style } } // fn attach_mouse_handlers( @@ -2554,7 +2553,38 @@ impl Element for EditorElement { element_state: Option, cx: &mut gpui::ViewContext, ) -> Self::ElementState { - () + editor.style = Some(self.style.clone()); // Long-term, we'd like to eliminate this. + + let dispatch_context = editor.dispatch_context(cx); + cx.with_element_id(cx.view().entity_id(), |global_id, cx| { + cx.with_key_dispatch_context(dispatch_context, |cx| { + cx.with_key_listeners( + [ + build_key_listener( + move |editor, key_down: &KeyDownEvent, dispatch_context, phase, cx| { + if phase == DispatchPhase::Bubble { + if let KeyMatch::Some(action) = cx.match_keystroke( + &global_id, + &key_down.keystroke, + dispatch_context, + ) { + dbg!(action.as_any()); + return Some(action); + } + } + + None + }, + ), + build_action_listener(Editor::move_left), + build_action_listener(Editor::move_right), + build_action_listener(Editor::move_down), + build_action_listener(Editor::move_up), + ], + |cx| cx.with_focus(editor.focus_handle.clone(), |_| {}), + ); + }) + }); } fn layout( @@ -4080,3 +4110,33 @@ fn scale_horizontal_mouse_autoscroll_delta(delta: f32) -> f32 { // .collect() // } // } + +fn build_key_listener( + listener: impl Fn( + &mut Editor, + &T, + &[&DispatchContext], + DispatchPhase, + &mut ViewContext, + ) -> Option> + + 'static, +) -> (TypeId, KeyListener) { + ( + TypeId::of::(), + Box::new(move |editor, event, dispatch_context, phase, cx| { + let key_event = event.downcast_ref::()?; + listener(editor, key_event, dispatch_context, phase, cx) + }), + ) +} + +fn build_action_listener( + listener: impl Fn(&mut Editor, &T, &mut ViewContext) + 'static, +) -> (TypeId, KeyListener) { + build_key_listener(move |editor, action: &T, dispatch_context, phase, cx| { + if phase == DispatchPhase::Bubble { + listener(editor, action, cx); + } + None + }) +} diff --git a/crates/editor2/src/movement.rs b/crates/editor2/src/movement.rs index 962f4f60e6..b15c484a03 100644 --- a/crates/editor2/src/movement.rs +++ b/crates/editor2/src/movement.rs @@ -3,7 +3,7 @@ use crate::{char_kind, CharKind, EditorStyle, ToOffset, ToPoint}; use gpui::{px, TextSystem}; use language::Point; use serde::de::IntoDeserializer; -use std::ops::Range; +use std::{ops::Range, sync::Arc}; #[derive(Debug, PartialEq)] pub enum FindRange { @@ -14,7 +14,7 @@ pub enum FindRange { /// TextLayoutDetails encompasses everything we need to move vertically /// taking into account variable width characters. pub struct TextLayoutDetails { - pub text_system: TextSystem, + pub text_system: Arc, pub editor_style: EditorStyle, } diff --git a/crates/gpui2/src/action.rs b/crates/gpui2/src/action.rs index 84843c9876..2139fdedcb 100644 --- a/crates/gpui2/src/action.rs +++ b/crates/gpui2/src/action.rs @@ -22,7 +22,8 @@ where A: for<'a> Deserialize<'a> + PartialEq + Clone + Default + 'static, { fn qualified_name() -> SharedString { - type_name::().into() + // todo!() remove this + type_name::().replace("2::", "::").into() } fn build(params: Option) -> Result> diff --git a/crates/gpui2/src/app.rs b/crates/gpui2/src/app.rs index 801047de5f..63d2143a67 100644 --- a/crates/gpui2/src/app.rs +++ b/crates/gpui2/src/app.rs @@ -779,11 +779,21 @@ impl AppContext { (build)(params) } - /// Halt propagation of a mouse event, keyboard event, or action. This prevents listeners - /// that have not yet been invoked from receiving the event. + /// Event handlers propagate events by default. Call this method to stop dispatching to + /// event handlers with a lower z-index (mouse) or higher in the tree (keyboard). This is + /// the opposite of [propagate]. It's also possible to cancel a call to [propagate] by + /// calling this method before effects are flushed. pub fn stop_propagation(&mut self) { self.propagate_event = false; } + + /// Action handlers stop propagation by default during the bubble phase of action dispatch + /// dispatching to action handlers higher in the element tree. This is the opposite of + /// [stop_propagation]. It's also possible to cancel a call to [stop_propagate] by calling + /// this method before effects are flushed. + pub fn propagate(&mut self) { + self.propagate_event = true; + } } impl Context for AppContext { diff --git a/crates/gpui2/src/window.rs b/crates/gpui2/src/window.rs index 9cab40082b..0b3bcd1d76 100644 --- a/crates/gpui2/src/window.rs +++ b/crates/gpui2/src/window.rs @@ -1314,6 +1314,7 @@ impl<'a> WindowContext<'a> { } = stack_frame { if action_type == *event_type { + self.app.propagate_event = false; listener(action.as_any(), &[], DispatchPhase::Bubble, self); if !self.app.propagate_event { break; @@ -1328,6 +1329,7 @@ impl<'a> WindowContext<'a> { self.app.global_action_listeners.remove(&action_type) { for listener in global_listeners.iter().rev() { + self.app.propagate_event = false; listener(action.as_ref(), DispatchPhase::Bubble, self); if !self.app.propagate_event { break; diff --git a/crates/settings2/src/settings_file.rs b/crates/settings2/src/settings_file.rs index c623ae9caf..6f2c8d374f 100644 --- a/crates/settings2/src/settings_file.rs +++ b/crates/settings2/src/settings_file.rs @@ -1,4 +1,4 @@ -use crate::{settings_store::SettingsStore, Settings}; +use crate::{settings_store::SettingsStore, KeymapFile, Settings}; use anyhow::Result; use fs::Fs; use futures::{channel::mpsc, StreamExt}; @@ -117,3 +117,50 @@ pub fn update_settings_file( }) .detach_and_log_err(cx); } + +pub fn load_default_keymap(cx: &mut AppContext) { + for path in ["keymaps/default.json", "keymaps/vim.json"] { + KeymapFile::load_asset(path, cx).unwrap(); + } + + // todo!() + // if let Some(asset_path) = settings::get::(cx).asset_path() { + // KeymapFile::load_asset(asset_path, cx).unwrap(); + // } +} + +pub fn handle_keymap_file_changes( + mut user_keymap_file_rx: mpsc::UnboundedReceiver, + cx: &mut AppContext, +) { + cx.spawn(move |cx| async move { + // let mut settings_subscription = None; + while let Some(user_keymap_content) = user_keymap_file_rx.next().await { + if let Some(keymap_content) = KeymapFile::parse(&user_keymap_content).log_err() { + cx.update(|cx| reload_keymaps(cx, &keymap_content)).ok(); + + // todo!() + // let mut old_base_keymap = cx.read(|cx| *settings::get::(cx)); + // drop(settings_subscription); + // settings_subscription = Some(cx.update(|cx| { + // cx.observe_global::(move |cx| { + // let new_base_keymap = *settings::get::(cx); + // if new_base_keymap != old_base_keymap { + // old_base_keymap = new_base_keymap.clone(); + // reload_keymaps(cx, &keymap_content); + // } + // }) + // })); + } + } + }) + .detach(); +} + +fn reload_keymaps(cx: &mut AppContext, keymap_content: &KeymapFile) { + // todo!() + // cx.clear_bindings(); + load_default_keymap(cx); + keymap_content.clone().add_to_cx(cx).log_err(); + // cx.set_menus(menus::menus()); +} diff --git a/crates/zed2/src/main.rs b/crates/zed2/src/main.rs index 580afd2653..8dad94847e 100644 --- a/crates/zed2/src/main.rs +++ b/crates/zed2/src/main.rs @@ -20,7 +20,8 @@ use node_runtime::RealNodeRuntime; use parking_lot::Mutex; use serde::{Deserialize, Serialize}; use settings::{ - default_settings, handle_settings_file_changes, watch_config_file, Settings, SettingsStore, + default_settings, handle_keymap_file_changes, handle_settings_file_changes, watch_config_file, + Settings, SettingsStore, }; use simplelog::ConfigBuilder; use smol::process::Command; @@ -76,7 +77,7 @@ fn main() { fs.clone(), paths::SETTINGS.clone(), ); - let _user_keymap_file_rx = watch_config_file( + let user_keymap_file_rx = watch_config_file( &app.background_executor(), fs.clone(), paths::KEYMAP.clone(), @@ -116,7 +117,7 @@ fn main() { .unwrap(); cx.set_global(store); handle_settings_file_changes(user_settings_file_rx, cx); - // handle_keymap_file_changes(user_keymap_file_rx, cx); + handle_keymap_file_changes(user_keymap_file_rx, cx); let client = client::Client::new(http.clone(), cx); let mut languages = LanguageRegistry::new(login_shell_env_loaded); From 25876161f86604b83a85a7ba5813d07d73c8d1c4 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Tue, 7 Nov 2023 18:45:09 +0100 Subject: [PATCH 3/4] theme_importer: Format generated themes (#3255) This PR makes the `theme_importer` format the themes that it generates automatically. Release Notes: - N/A --- crates/theme2/src/registry.rs | 7 ++++-- crates/theme2/src/themes/andromeda.rs | 3 +++ crates/theme2/src/themes/ayu.rs | 3 +++ crates/theme2/src/themes/dracula.rs | 3 +++ crates/theme2/src/themes/gruvbox.rs | 3 +++ crates/theme2/src/themes/mod.rs | 5 ++++- crates/theme2/src/themes/night_owl.rs | 3 +++ crates/theme2/src/themes/nord.rs | 3 +++ crates/theme2/src/themes/notctis.rs | 3 +++ crates/theme2/src/themes/palenight.rs | 3 +++ crates/theme2/src/themes/rose_pine.rs | 3 +++ crates/theme2/src/themes/solarized.rs | 3 +++ crates/theme2/src/themes/synthwave_84.rs | 3 +++ crates/theme_importer/src/main.rs | 27 +++++++++++++++++++++++- 14 files changed, 68 insertions(+), 4 deletions(-) diff --git a/crates/theme2/src/registry.rs b/crates/theme2/src/registry.rs index 37055da361..c9ff549c15 100644 --- a/crates/theme2/src/registry.rs +++ b/crates/theme2/src/registry.rs @@ -52,7 +52,10 @@ impl ThemeRegistry { status: StatusColors::default(), git: GitStatusColors::default(), player: PlayerColors::default(), - syntax: SyntaxTheme::default_dark(), + syntax: match user_theme.appearance { + Appearance::Light => SyntaxTheme::default_light(), + Appearance::Dark => SyntaxTheme::default_dark(), + }, }, } })); @@ -81,7 +84,7 @@ impl Default for ThemeRegistry { }; this.insert_theme_families([zed_pro_family()]); - this.insert_user_theme_familes(crate::all_imported_themes()); + this.insert_user_theme_familes(crate::all_user_themes()); this } diff --git a/crates/theme2/src/themes/andromeda.rs b/crates/theme2/src/themes/andromeda.rs index 0d58dc9d56..22a0a8741c 100644 --- a/crates/theme2/src/themes/andromeda.rs +++ b/crates/theme2/src/themes/andromeda.rs @@ -1,3 +1,6 @@ +// This file was generated by the `theme_importer`. +// Be careful when modifying it by hand. + use gpui::rgba; use crate::{ diff --git a/crates/theme2/src/themes/ayu.rs b/crates/theme2/src/themes/ayu.rs index 6298de40ee..8d79289046 100644 --- a/crates/theme2/src/themes/ayu.rs +++ b/crates/theme2/src/themes/ayu.rs @@ -1,3 +1,6 @@ +// This file was generated by the `theme_importer`. +// Be careful when modifying it by hand. + use gpui::rgba; use crate::{ diff --git a/crates/theme2/src/themes/dracula.rs b/crates/theme2/src/themes/dracula.rs index ea0e185e05..57c04851a5 100644 --- a/crates/theme2/src/themes/dracula.rs +++ b/crates/theme2/src/themes/dracula.rs @@ -1,3 +1,6 @@ +// This file was generated by the `theme_importer`. +// Be careful when modifying it by hand. + use gpui::rgba; use crate::{ diff --git a/crates/theme2/src/themes/gruvbox.rs b/crates/theme2/src/themes/gruvbox.rs index d06f53da67..2e79b250da 100644 --- a/crates/theme2/src/themes/gruvbox.rs +++ b/crates/theme2/src/themes/gruvbox.rs @@ -1,3 +1,6 @@ +// This file was generated by the `theme_importer`. +// Be careful when modifying it by hand. + use gpui::rgba; use crate::{ diff --git a/crates/theme2/src/themes/mod.rs b/crates/theme2/src/themes/mod.rs index b013a86989..c48338b184 100644 --- a/crates/theme2/src/themes/mod.rs +++ b/crates/theme2/src/themes/mod.rs @@ -1,3 +1,6 @@ +// This file was generated by the `theme_importer`. +// Be careful when modifying it by hand. + mod andromeda; mod ayu; mod dracula; @@ -24,7 +27,7 @@ pub use synthwave_84::*; use crate::UserThemeFamily; -pub(crate) fn all_imported_themes() -> Vec { +pub(crate) fn all_user_themes() -> Vec { vec![ rose_pine(), night_owl(), diff --git a/crates/theme2/src/themes/night_owl.rs b/crates/theme2/src/themes/night_owl.rs index 4227c53b7a..90ae45f879 100644 --- a/crates/theme2/src/themes/night_owl.rs +++ b/crates/theme2/src/themes/night_owl.rs @@ -1,3 +1,6 @@ +// This file was generated by the `theme_importer`. +// Be careful when modifying it by hand. + use gpui::rgba; use crate::{ diff --git a/crates/theme2/src/themes/nord.rs b/crates/theme2/src/themes/nord.rs index 981b460d10..b1dedd6b16 100644 --- a/crates/theme2/src/themes/nord.rs +++ b/crates/theme2/src/themes/nord.rs @@ -1,3 +1,6 @@ +// This file was generated by the `theme_importer`. +// Be careful when modifying it by hand. + use gpui::rgba; use crate::{ diff --git a/crates/theme2/src/themes/notctis.rs b/crates/theme2/src/themes/notctis.rs index d8673824db..f5b8f1207d 100644 --- a/crates/theme2/src/themes/notctis.rs +++ b/crates/theme2/src/themes/notctis.rs @@ -1,3 +1,6 @@ +// This file was generated by the `theme_importer`. +// Be careful when modifying it by hand. + use gpui::rgba; use crate::{ diff --git a/crates/theme2/src/themes/palenight.rs b/crates/theme2/src/themes/palenight.rs index 64df5d7ecb..7ed27d3b75 100644 --- a/crates/theme2/src/themes/palenight.rs +++ b/crates/theme2/src/themes/palenight.rs @@ -1,3 +1,6 @@ +// This file was generated by the `theme_importer`. +// Be careful when modifying it by hand. + use gpui::rgba; use crate::{ diff --git a/crates/theme2/src/themes/rose_pine.rs b/crates/theme2/src/themes/rose_pine.rs index 6fd641861a..913af127fb 100644 --- a/crates/theme2/src/themes/rose_pine.rs +++ b/crates/theme2/src/themes/rose_pine.rs @@ -1,3 +1,6 @@ +// This file was generated by the `theme_importer`. +// Be careful when modifying it by hand. + use gpui::rgba; use crate::{ diff --git a/crates/theme2/src/themes/solarized.rs b/crates/theme2/src/themes/solarized.rs index 1f1c55f081..0a78328e2e 100644 --- a/crates/theme2/src/themes/solarized.rs +++ b/crates/theme2/src/themes/solarized.rs @@ -1,3 +1,6 @@ +// This file was generated by the `theme_importer`. +// Be careful when modifying it by hand. + use gpui::rgba; use crate::{ diff --git a/crates/theme2/src/themes/synthwave_84.rs b/crates/theme2/src/themes/synthwave_84.rs index a512c087ff..8c0520ee11 100644 --- a/crates/theme2/src/themes/synthwave_84.rs +++ b/crates/theme2/src/themes/synthwave_84.rs @@ -1,3 +1,6 @@ +// This file was generated by the `theme_importer`. +// Be careful when modifying it by hand. + use gpui::rgba; use crate::{ diff --git a/crates/theme_importer/src/main.rs b/crates/theme_importer/src/main.rs index e2ece76b12..b41994a0be 100644 --- a/crates/theme_importer/src/main.rs +++ b/crates/theme_importer/src/main.rs @@ -5,6 +5,7 @@ mod vscode; use std::fs::{self, File}; use std::io::Write; use std::path::PathBuf; +use std::process::Command; use std::str::FromStr; use anyhow::{anyhow, Context, Result}; @@ -152,6 +153,9 @@ fn main() -> Result<()> { let theme_module = format!( r#" + // This file was generated by the `theme_importer`. + // Be careful when modifying it by hand. + use gpui::rgba; use crate::{{ @@ -174,7 +178,7 @@ fn main() -> Result<()> { r#" use crate::UserThemeFamily; - pub(crate) fn all_imported_themes() -> Vec {{ + pub(crate) fn all_user_themes() -> Vec {{ vec![{all_themes}] }} "#, @@ -187,6 +191,9 @@ fn main() -> Result<()> { let mod_rs_contents = format!( r#" + // This file was generated by the `theme_importer`. + // Be careful when modifying it by hand. + {mod_statements} {use_statements} @@ -208,5 +215,23 @@ fn main() -> Result<()> { mod_rs_file.write_all(mod_rs_contents.as_bytes())?; + println!("Formatting themes..."); + + let format_result = format_themes_crate() + // We need to format a second time to catch all of the formatting issues. + .and_then(|_| format_themes_crate()); + + if let Err(err) = format_result { + eprintln!("Failed to format themes: {}", err); + } + + println!("Done!"); + Ok(()) } + +fn format_themes_crate() -> std::io::Result { + Command::new("cargo") + .args(["fmt", "--package", "theme2"]) + .output() +} From d7e86eb1c1b0dd9de671f74a68d34f0804d931b2 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 7 Nov 2023 18:48:08 +0100 Subject: [PATCH 4/4] Merge remote-tracking branch 'origin/main' into editor-movement --- crates/editor2/src/display_map.rs | 90 +++--- crates/editor2/src/editor.rs | 282 +++++++++--------- crates/editor2/src/element.rs | 15 +- .../editor2/src/highlight_matching_bracket.rs | 2 +- crates/editor2/src/movement.rs | 3 +- crates/gpui2/src/action.rs | 4 +- crates/gpui2/src/window.rs | 11 +- crates/language2/src/highlight_map.rs | 2 + crates/theme2/src/colors.rs | 4 +- crates/theme2/src/default_colors.rs | 4 + crates/theme2/src/default_theme.rs | 6 +- crates/theme2/src/registry.rs | 2 +- crates/theme2/src/syntax.rs | 4 + crates/theme2/src/theme2.rs | 23 +- 14 files changed, 235 insertions(+), 217 deletions(-) diff --git a/crates/editor2/src/display_map.rs b/crates/editor2/src/display_map.rs index 93ceb4f9df..808f0d340a 100644 --- a/crates/editor2/src/display_map.rs +++ b/crates/editor2/src/display_map.rs @@ -12,7 +12,9 @@ use crate::{ pub use block_map::{BlockMap, BlockPoint}; use collections::{BTreeMap, HashMap, HashSet}; use fold_map::FoldMap; -use gpui::{Font, FontId, HighlightStyle, Hsla, Line, Model, ModelContext, Pixels, UnderlineStyle}; +use gpui::{ + Font, FontId, HighlightStyle, Hsla, Line, Model, ModelContext, Pixels, TextRun, UnderlineStyle, +}; use inlay_map::InlayMap; use language::{ language_settings::language_settings, OffsetUtf16, Point, Subscription as BufferSubscription, @@ -21,7 +23,7 @@ use lsp::DiagnosticSeverity; use std::{any::TypeId, borrow::Cow, fmt::Debug, num::NonZeroU32, ops::Range, sync::Arc}; use sum_tree::{Bias, TreeMap}; use tab_map::TabMap; -use theme::Theme; +use theme::{SyntaxTheme, Theme}; use wrap_map::WrapMap; pub use block_map::{ @@ -505,18 +507,18 @@ impl DisplaySnapshot { &'a self, display_rows: Range, language_aware: bool, - theme: &'a Theme, + editor_style: &'a EditorStyle, ) -> impl Iterator> { self.chunks( display_rows, language_aware, - None, // todo!("add inlay highlight style") - None, // todo!("add suggestion highlight style") + Some(editor_style.syntax.inlay_style), + Some(editor_style.syntax.suggestion_style), ) .map(|chunk| { let mut highlight_style = chunk .syntax_highlight_id - .and_then(|id| id.style(&theme.styles.syntax)); + .and_then(|id| id.style(&editor_style.syntax)); if let Some(chunk_highlight) = chunk.highlight_style { if let Some(highlight_style) = highlight_style.as_mut() { @@ -535,7 +537,8 @@ impl DisplaySnapshot { if let Some(severity) = chunk.diagnostic_severity { // Omit underlines for HINT/INFO diagnostics on 'unnecessary' code. if severity <= DiagnosticSeverity::WARNING || !chunk.is_unnecessary { - let diagnostic_color = super::diagnostic_style(severity, true, theme); + let diagnostic_color = + super::diagnostic_style(severity, true, &editor_style.diagnostic_style); diagnostic_highlight.underline = Some(UnderlineStyle { color: Some(diagnostic_color), thickness: 1.0.into(), @@ -564,53 +567,46 @@ impl DisplaySnapshot { TextLayoutDetails { text_system, editor_style, + rem_size, }: &TextLayoutDetails, ) -> Line { - todo!() - // let mut styles = Vec::new(); - // let mut line = String::new(); - // let mut ended_in_newline = false; + let mut runs = Vec::new(); + let mut line = String::new(); + let mut ended_in_newline = false; - // let range = display_row..display_row + 1; - // for chunk in self.highlighted_chunks(range, false, editor_style) { - // line.push_str(chunk.chunk); + let range = display_row..display_row + 1; + for chunk in self.highlighted_chunks(range, false, &editor_style) { + line.push_str(chunk.chunk); - // let text_style = if let Some(style) = chunk.style { - // editor_style - // .text - // .clone() - // .highlight(style, text_system) - // .map(Cow::Owned) - // .unwrap_or_else(|_| Cow::Borrowed(&editor_style.text)) - // } else { - // Cow::Borrowed(&editor_style.text) - // }; - // ended_in_newline = chunk.chunk.ends_with("\n"); + let text_style = if let Some(style) = chunk.style { + editor_style + .text + .clone() + .highlight(style) + .map(Cow::Owned) + .unwrap_or_else(|_| Cow::Borrowed(&editor_style.text)) + } else { + Cow::Borrowed(&editor_style.text) + }; + ended_in_newline = chunk.chunk.ends_with("\n"); - // styles.push( - // todo!(), // len: chunk.chunk.len(), - // // font_id: text_style.font_id, - // // color: text_style.color, - // // underline: text_style.underline, - // ); - // } + runs.push(text_style.to_run(chunk.chunk.len())) + } - // // our pixel positioning logic assumes each line ends in \n, - // // this is almost always true except for the last line which - // // may have no trailing newline. - // if !ended_in_newline && display_row == self.max_point().row() { - // line.push_str("\n"); + // our pixel positioning logic assumes each line ends in \n, + // this is almost always true except for the last line which + // may have no trailing newline. + if !ended_in_newline && display_row == self.max_point().row() { + line.push_str("\n"); + runs.push(editor_style.text.to_run("\n".len())); + } - // todo!(); - // // styles.push(RunStyle { - // // len: "\n".len(), - // // font_id: editor_style.text.font_id, - // // color: editor_style.text_color, - // // underline: editor_style.text.underline, - // // }); - // } - - // text_system.layout_text(&line, editor_style.text.font_size, &styles, None) + let font_size = editor_style.text.font_size.to_pixels(*rem_size); + text_system + .layout_text(&line, font_size, &runs, None) + .unwrap() + .pop() + .unwrap() } pub fn x_for_point( diff --git a/crates/editor2/src/editor.rs b/crates/editor2/src/editor.rs index 716fdcd488..13afe8aeba 100644 --- a/crates/editor2/src/editor.rs +++ b/crates/editor2/src/editor.rs @@ -36,9 +36,10 @@ pub use element::{ use futures::FutureExt; use fuzzy::{StringMatch, StringMatchCandidate}; use gpui::{ - div, px, AnyElement, AppContext, BackgroundExecutor, Context, DispatchContext, Div, Element, - Entity, EventEmitter, FocusHandle, FontStyle, FontWeight, Hsla, Model, Pixels, Render, Styled, - Subscription, Task, TextStyle, View, ViewContext, VisualContext, WeakView, WindowContext, + actions, div, px, AnyElement, AppContext, BackgroundExecutor, Context, DispatchContext, Div, + Element, Entity, EventEmitter, FocusHandle, FontStyle, FontWeight, Hsla, Model, Pixels, Render, + Styled, Subscription, Task, TextStyle, View, ViewContext, VisualContext, WeakView, + WindowContext, }; use highlight_matching_bracket::refresh_matching_bracket_highlights; use hover_popover::{hide_hover, HoverState}; @@ -82,7 +83,9 @@ use std::{ pub use sum_tree::Bias; use sum_tree::TreeMap; use text::Rope; -use theme::{ActiveTheme, PlayerColor, Theme, ThemeColors, ThemeSettings}; +use theme::{ + ActiveTheme, DiagnosticStyle, PlayerColor, SyntaxTheme, Theme, ThemeColors, ThemeSettings, +}; use util::{post_inc, RangeExt, ResultExt, TryFutureExt}; use workspace::{ItemNavHistory, SplitDirection, ViewId, Workspace}; @@ -257,118 +260,115 @@ impl InlayId { } } -// actions!( -// editor, -// [ -// Cancel, -// Backspace, -// Delete, -// Newline, -// NewlineAbove, -// NewlineBelow, -// GoToDiagnostic, -// GoToPrevDiagnostic, -// GoToHunk, -// GoToPrevHunk, -// Indent, -// Outdent, -// DeleteLine, -// DeleteToPreviousWordStart, -// DeleteToPreviousSubwordStart, -// DeleteToNextWordEnd, -// DeleteToNextSubwordEnd, -// DeleteToBeginningOfLine, -// DeleteToEndOfLine, -// CutToEndOfLine, -// DuplicateLine, -// MoveLineUp, -// MoveLineDown, -// JoinLines, -// SortLinesCaseSensitive, -// SortLinesCaseInsensitive, -// ReverseLines, -// ShuffleLines, -// ConvertToUpperCase, -// ConvertToLowerCase, -// ConvertToTitleCase, -// ConvertToSnakeCase, -// ConvertToKebabCase, -// ConvertToUpperCamelCase, -// ConvertToLowerCamelCase, -// Transpose, -// Cut, -// Copy, -// Paste, -// Undo, -// Redo, -// MoveUp, -// PageUp, -// MoveDown, -// PageDown, -// MoveLeft, -// MoveRight, -// MoveToPreviousWordStart, -// MoveToPreviousSubwordStart, -// MoveToNextWordEnd, -// MoveToNextSubwordEnd, -// MoveToBeginningOfLine, -// MoveToEndOfLine, -// MoveToStartOfParagraph, -// MoveToEndOfParagraph, -// MoveToBeginning, -// MoveToEnd, -// SelectUp, -// SelectDown, -// SelectLeft, -// SelectRight, -// SelectToPreviousWordStart, -// SelectToPreviousSubwordStart, -// SelectToNextWordEnd, -// SelectToNextSubwordEnd, -// SelectToStartOfParagraph, -// SelectToEndOfParagraph, -// SelectToBeginning, -// SelectToEnd, -// SelectAll, -// SelectLine, -// SplitSelectionIntoLines, -// AddSelectionAbove, -// AddSelectionBelow, -// Tab, -// TabPrev, -// ShowCharacterPalette, -// SelectLargerSyntaxNode, -// SelectSmallerSyntaxNode, -// GoToDefinition, -// GoToDefinitionSplit, -// GoToTypeDefinition, -// GoToTypeDefinitionSplit, -// MoveToEnclosingBracket, -// UndoSelection, -// RedoSelection, -// FindAllReferences, -// Rename, -// ConfirmRename, -// Fold, -// UnfoldLines, -// FoldSelectedRanges, -// ShowCompletions, -// OpenExcerpts, -// RestartLanguageServer, -// Hover, -// Format, -// ToggleSoftWrap, -// ToggleInlayHints, -// RevealInFinder, -// CopyPath, -// CopyRelativePath, -// CopyHighlightJson, -// ContextMenuFirst, -// ContextMenuPrev, -// ContextMenuNext, -// ContextMenuLast, -// ] -// ); +actions!( + Cancel, + Backspace, + Delete, + Newline, + NewlineAbove, + NewlineBelow, + GoToDiagnostic, + GoToPrevDiagnostic, + GoToHunk, + GoToPrevHunk, + Indent, + Outdent, + DeleteLine, + DeleteToPreviousWordStart, + DeleteToPreviousSubwordStart, + DeleteToNextWordEnd, + DeleteToNextSubwordEnd, + DeleteToBeginningOfLine, + DeleteToEndOfLine, + CutToEndOfLine, + DuplicateLine, + MoveLineUp, + MoveLineDown, + JoinLines, + SortLinesCaseSensitive, + SortLinesCaseInsensitive, + ReverseLines, + ShuffleLines, + ConvertToUpperCase, + ConvertToLowerCase, + ConvertToTitleCase, + ConvertToSnakeCase, + ConvertToKebabCase, + ConvertToUpperCamelCase, + ConvertToLowerCamelCase, + Transpose, + Cut, + Copy, + Paste, + Undo, + Redo, + MoveUp, + PageUp, + MoveDown, + PageDown, + MoveLeft, + MoveRight, + MoveToPreviousWordStart, + MoveToPreviousSubwordStart, + MoveToNextWordEnd, + MoveToNextSubwordEnd, + MoveToBeginningOfLine, + MoveToEndOfLine, + MoveToStartOfParagraph, + MoveToEndOfParagraph, + MoveToBeginning, + MoveToEnd, + SelectUp, + SelectDown, + SelectLeft, + SelectRight, + SelectToPreviousWordStart, + SelectToPreviousSubwordStart, + SelectToNextWordEnd, + SelectToNextSubwordEnd, + SelectToStartOfParagraph, + SelectToEndOfParagraph, + SelectToBeginning, + SelectToEnd, + SelectAll, + SelectLine, + SplitSelectionIntoLines, + AddSelectionAbove, + AddSelectionBelow, + Tab, + TabPrev, + ShowCharacterPalette, + SelectLargerSyntaxNode, + SelectSmallerSyntaxNode, + GoToDefinition, + GoToDefinitionSplit, + GoToTypeDefinition, + GoToTypeDefinitionSplit, + MoveToEnclosingBracket, + UndoSelection, + RedoSelection, + FindAllReferences, + Rename, + ConfirmRename, + Fold, + UnfoldLines, + FoldSelectedRanges, + ShowCompletions, + OpenExcerpts, + RestartLanguageServer, + Hover, + Format, + ToggleSoftWrap, + ToggleInlayHints, + RevealInFinder, + CopyPath, + CopyRelativePath, + CopyHighlightJson, + ContextMenuFirst, + ContextMenuPrev, + ContextMenuNext, + ContextMenuLast, +); // impl_actions!( // editor, @@ -390,26 +390,6 @@ impl InlayId { // ] // ); -// todo!(revisit these actions) -pub struct ShowCompletions; -pub struct Rename; -pub struct GoToDefinition; -pub struct GoToTypeDefinition; -pub struct GoToDefinitionSplit; -pub struct GoToTypeDefinitionSplit; - -#[derive(PartialEq, Clone, Default, Deserialize)] -pub struct MoveLeft; - -#[derive(PartialEq, Clone, Default, Deserialize)] -pub struct MoveRight; - -#[derive(PartialEq, Clone, Default, Deserialize)] -pub struct MoveDown; - -#[derive(PartialEq, Clone, Default, Deserialize)] -pub struct MoveUp; - enum DocumentHighlightRead {} enum DocumentHighlightWrite {} enum InputComposition {} @@ -615,6 +595,8 @@ pub struct EditorStyle { pub text: TextStyle, pub line_height_scalar: f32, pub scrollbar_width: Pixels, + pub syntax: Arc, + pub diagnostic_style: DiagnosticStyle, } type CompletionId = usize; @@ -3543,6 +3525,7 @@ impl Editor { TextLayoutDetails { text_system: cx.text_system().clone(), editor_style: self.style.clone().unwrap(), + rem_size: cx.rem_size(), } } @@ -5862,7 +5845,6 @@ impl Editor { // } pub fn move_down(&mut self, _: &MoveDown, cx: &mut ViewContext) { - dbg!("move_down"); self.take_rename(true, cx); if self.mode == EditorMode::SingleLine { @@ -9399,6 +9381,8 @@ impl Render for Editor { text: text_style, line_height_scalar: settings.buffer_line_height.value(), scrollbar_width: px(12.), + syntax: cx.theme().syntax().clone(), + diagnostic_style: cx.theme().diagnostic_style(), }) } } @@ -9982,17 +9966,21 @@ pub fn highlight_diagnostic_message( (message_without_backticks, highlights) } -pub fn diagnostic_style(severity: DiagnosticSeverity, valid: bool, theme: &Theme) -> Hsla { +pub fn diagnostic_style( + severity: DiagnosticSeverity, + valid: bool, + style: &DiagnosticStyle, +) -> Hsla { match (severity, valid) { - (DiagnosticSeverity::ERROR, true) => theme.status().error, - (DiagnosticSeverity::ERROR, false) => theme.status().error, - (DiagnosticSeverity::WARNING, true) => theme.status().warning, - (DiagnosticSeverity::WARNING, false) => theme.status().warning, - (DiagnosticSeverity::INFORMATION, true) => theme.status().info, - (DiagnosticSeverity::INFORMATION, false) => theme.status().info, - (DiagnosticSeverity::HINT, true) => theme.status().info, - (DiagnosticSeverity::HINT, false) => theme.status().info, - _ => theme.status().ignored, + (DiagnosticSeverity::ERROR, true) => style.error, + (DiagnosticSeverity::ERROR, false) => style.error, + (DiagnosticSeverity::WARNING, true) => style.warning, + (DiagnosticSeverity::WARNING, false) => style.warning, + (DiagnosticSeverity::INFORMATION, true) => style.info, + (DiagnosticSeverity::INFORMATION, false) => style.info, + (DiagnosticSeverity::HINT, true) => style.info, + (DiagnosticSeverity::HINT, false) => style.info, + _ => style.ignored, } } diff --git a/crates/editor2/src/element.rs b/crates/editor2/src/element.rs index 918cfce43f..09f9ef1a59 100644 --- a/crates/editor2/src/element.rs +++ b/crates/editor2/src/element.rs @@ -1577,12 +1577,10 @@ impl EditorElement { }) .collect() } else { - let style = &self.style; - let chunks = snapshot.highlighted_chunks(rows.clone(), true, cx.theme()); - + let chunks = snapshot.highlighted_chunks(rows.clone(), true, &self.style); LineWithInvisibles::from_chunks( chunks, - &style.text, + &self.style.text, MAX_LINE_LEN, rows.len() as usize, line_number_layouts, @@ -2560,6 +2558,10 @@ impl Element for EditorElement { cx.with_key_dispatch_context(dispatch_context, |cx| { cx.with_key_listeners( [ + build_action_listener(Editor::move_left), + build_action_listener(Editor::move_right), + build_action_listener(Editor::move_down), + build_action_listener(Editor::move_up), build_key_listener( move |editor, key_down: &KeyDownEvent, dispatch_context, phase, cx| { if phase == DispatchPhase::Bubble { @@ -2568,7 +2570,6 @@ impl Element for EditorElement { &key_down.keystroke, dispatch_context, ) { - dbg!(action.as_any()); return Some(action); } } @@ -2576,10 +2577,6 @@ impl Element for EditorElement { None }, ), - build_action_listener(Editor::move_left), - build_action_listener(Editor::move_right), - build_action_listener(Editor::move_down), - build_action_listener(Editor::move_up), ], |cx| cx.with_focus(editor.focus_handle.clone(), |_| {}), ); diff --git a/crates/editor2/src/highlight_matching_bracket.rs b/crates/editor2/src/highlight_matching_bracket.rs index fd8fb6b097..d7fd37745f 100644 --- a/crates/editor2/src/highlight_matching_bracket.rs +++ b/crates/editor2/src/highlight_matching_bracket.rs @@ -24,7 +24,7 @@ pub fn refresh_matching_bracket_highlights(editor: &mut Editor, cx: &mut ViewCon opening_range.to_anchors(&snapshot.buffer_snapshot), closing_range.to_anchors(&snapshot.buffer_snapshot), ], - |theme| todo!("theme.editor.document_highlight_read_background"), + |theme| theme.editor_document_highlight_read_background, cx, ) } diff --git a/crates/editor2/src/movement.rs b/crates/editor2/src/movement.rs index b15c484a03..b28af681e0 100644 --- a/crates/editor2/src/movement.rs +++ b/crates/editor2/src/movement.rs @@ -1,6 +1,6 @@ use super::{Bias, DisplayPoint, DisplaySnapshot, SelectionGoal, ToDisplayPoint}; use crate::{char_kind, CharKind, EditorStyle, ToOffset, ToPoint}; -use gpui::{px, TextSystem}; +use gpui::{px, Pixels, TextSystem}; use language::Point; use serde::de::IntoDeserializer; use std::{ops::Range, sync::Arc}; @@ -16,6 +16,7 @@ pub enum FindRange { pub struct TextLayoutDetails { pub text_system: Arc, pub editor_style: EditorStyle, + pub rem_size: Pixels, } pub fn left(map: &DisplaySnapshot, mut point: DisplayPoint) -> DisplayPoint { diff --git a/crates/gpui2/src/action.rs b/crates/gpui2/src/action.rs index 393a3feda6..90f312f502 100644 --- a/crates/gpui2/src/action.rs +++ b/crates/gpui2/src/action.rs @@ -24,12 +24,12 @@ macro_rules! actions { ( $name:ident ) => { #[derive(::std::clone::Clone, ::std::default::Default, ::std::fmt::Debug, ::std::cmp::PartialEq, $crate::serde::Deserialize)] - struct $name; + pub struct $name; }; ( $name:ident { $($token:tt)* } ) => { #[derive(::std::clone::Clone, ::std::default::Default, ::std::fmt::Debug, ::std::cmp::PartialEq, $crate::serde::Deserialize)] - struct $name { $($token)* } + pub struct $name { $($token)* } }; ( $name:ident, $($rest:tt)* ) => { diff --git a/crates/gpui2/src/window.rs b/crates/gpui2/src/window.rs index cc5d25627e..cd82eee72f 100644 --- a/crates/gpui2/src/window.rs +++ b/crates/gpui2/src/window.rs @@ -300,7 +300,8 @@ impl Window { /// When constructing the element tree, we maintain a stack of key dispatch frames until we /// find the focused element. We interleave key listeners with dispatch contexts so we can use the -/// contexts when matching key events against the keymap. +/// contexts when matching key events against the keymap. A key listener can be either an action +/// handler or a [KeyDown] / [KeyUp] event listener. enum KeyDispatchStackFrame { Listener { event_type: TypeId, @@ -1048,6 +1049,10 @@ impl<'a> WindowContext<'a> { /// Dispatch a mouse or keyboard event on the window. pub fn dispatch_event(&mut self, event: InputEvent) -> bool { + // Handlers may set this to false by calling `stop_propagation` + self.app.propagate_event = true; + self.window.default_prevented = false; + let event = match event { // Track the mouse position with our own state, since accessing the platform // API for the mouse position can only occur on the main thread. @@ -1101,10 +1106,6 @@ impl<'a> WindowContext<'a> { }; if let Some(any_mouse_event) = event.mouse_event() { - // Handlers may set this to false by calling `stop_propagation` - self.app.propagate_event = true; - self.window.default_prevented = false; - if let Some(mut handlers) = self .window .mouse_listeners diff --git a/crates/language2/src/highlight_map.rs b/crates/language2/src/highlight_map.rs index aeeda546bf..1421ef672d 100644 --- a/crates/language2/src/highlight_map.rs +++ b/crates/language2/src/highlight_map.rs @@ -95,6 +95,8 @@ mod tests { .iter() .map(|(name, color)| (name.to_string(), (*color).into())) .collect(), + inlay_style: HighlightStyle::default(), + suggestion_style: HighlightStyle::default(), }; let capture_names = &[ diff --git a/crates/theme2/src/colors.rs b/crates/theme2/src/colors.rs index e1df841c24..bb7d44e5d2 100644 --- a/crates/theme2/src/colors.rs +++ b/crates/theme2/src/colors.rs @@ -1,3 +1,5 @@ +use std::sync::Arc; + use gpui::Hsla; use refineable::Refineable; @@ -145,7 +147,7 @@ pub struct ThemeStyles { pub status: StatusColors, pub git: GitStatusColors, pub player: PlayerColors, - pub syntax: SyntaxTheme, + pub syntax: Arc, } #[cfg(test)] diff --git a/crates/theme2/src/default_colors.rs b/crates/theme2/src/default_colors.rs index f79518e769..c540fb40a9 100644 --- a/crates/theme2/src/default_colors.rs +++ b/crates/theme2/src/default_colors.rs @@ -138,6 +138,8 @@ impl SyntaxTheme { ("variable.special".into(), red().light().step_7().into()), ("variant".into(), red().light().step_7().into()), ], + inlay_style: tomato().light().step_1().into(), // todo!("nate: use a proper style") + suggestion_style: orange().light().step_1().into(), // todo!("nate: use proper style") } } @@ -193,6 +195,8 @@ impl SyntaxTheme { ("variable.special".into(), red().dark().step_7().into()), ("variant".into(), red().dark().step_7().into()), ], + inlay_style: tomato().dark().step_1().into(), // todo!("nate: use a proper style") + suggestion_style: orange().dark().step_1().into(), // todo!("nate: use a proper style") } } } diff --git a/crates/theme2/src/default_theme.rs b/crates/theme2/src/default_theme.rs index dacd26be3e..3c9634c989 100644 --- a/crates/theme2/src/default_theme.rs +++ b/crates/theme2/src/default_theme.rs @@ -1,3 +1,5 @@ +use std::sync::Arc; + use crate::{ colors::{GitStatusColors, PlayerColors, StatusColors, SystemColors, ThemeColors, ThemeStyles}, default_color_scales, Appearance, SyntaxTheme, Theme, ThemeFamily, @@ -14,7 +16,7 @@ fn zed_pro_daylight() -> Theme { status: StatusColors::default(), git: GitStatusColors::default(), player: PlayerColors::default(), - syntax: SyntaxTheme::default_light(), + syntax: Arc::new(SyntaxTheme::default_light()), }, } } @@ -30,7 +32,7 @@ pub(crate) fn zed_pro_moonlight() -> Theme { status: StatusColors::default(), git: GitStatusColors::default(), player: PlayerColors::default(), - syntax: SyntaxTheme::default_dark(), + syntax: Arc::new(SyntaxTheme::default_dark()), }, } } diff --git a/crates/theme2/src/registry.rs b/crates/theme2/src/registry.rs index 37055da361..17f154f06e 100644 --- a/crates/theme2/src/registry.rs +++ b/crates/theme2/src/registry.rs @@ -52,7 +52,7 @@ impl ThemeRegistry { status: StatusColors::default(), git: GitStatusColors::default(), player: PlayerColors::default(), - syntax: SyntaxTheme::default_dark(), + syntax: Arc::new(SyntaxTheme::default_dark()), }, } })); diff --git a/crates/theme2/src/syntax.rs b/crates/theme2/src/syntax.rs index 3a068349fb..8aac238555 100644 --- a/crates/theme2/src/syntax.rs +++ b/crates/theme2/src/syntax.rs @@ -3,6 +3,8 @@ use gpui::{HighlightStyle, Hsla}; #[derive(Clone, Default)] pub struct SyntaxTheme { pub highlights: Vec<(String, HighlightStyle)>, + pub inlay_style: HighlightStyle, + pub suggestion_style: HighlightStyle, } impl SyntaxTheme { @@ -21,6 +23,8 @@ impl SyntaxTheme { ) }) .collect(), + inlay_style: HighlightStyle::default(), + suggestion_style: HighlightStyle::default(), } } diff --git a/crates/theme2/src/theme2.rs b/crates/theme2/src/theme2.rs index 0a9b9821f6..88db3c55f4 100644 --- a/crates/theme2/src/theme2.rs +++ b/crates/theme2/src/theme2.rs @@ -77,7 +77,7 @@ impl Theme { /// Returns the [`SyntaxTheme`] for the theme. #[inline(always)] - pub fn syntax(&self) -> &SyntaxTheme { + pub fn syntax(&self) -> &Arc { &self.styles.syntax } @@ -98,4 +98,25 @@ impl Theme { pub fn syntax_color(&self, name: &str) -> Hsla { self.syntax().color(name) } + + /// Returns the [`StatusColors`] for the theme. + #[inline(always)] + pub fn diagnostic_style(&self) -> DiagnosticStyle { + DiagnosticStyle { + error: self.status().error, + warning: self.status().warning, + info: self.status().info, + hint: self.status().info, + ignored: self.status().ignored, + } + } +} + +#[derive(Clone, Debug)] +pub struct DiagnosticStyle { + pub error: Hsla, + pub warning: Hsla, + pub info: Hsla, + pub hint: Hsla, + pub ignored: Hsla, }