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);