- Fix opening GoToLine from Command - Extend test to cover go_to_line behavior too Release Notes: - N/A
Unified · Split
| @@ -2046,8 +2046,10 @@ dependencies = [ · 2045 unmodified lines | ||
| 2046 | 2046 | "editor2", |
| 2047 | 2047 | "env_logger 0.9.3", |
| 2048 | 2048 | "fuzzy2", |
| 2049 | + "go_to_line2", | |
| 2049 | 2050 | "gpui2", |
| 2050 | 2051 | "language2", |
| 2052 | + "menu2", | |
| 2051 | 2053 | "picker2", |
| 2052 | 2054 | "project2", |
| 2053 | 2055 | "serde", |
| @@ -28,6 +28,8 @@ gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] } · 27 unmodified lines | ||
| 28 | 28 | editor = { package = "editor2", path = "../editor2", features = ["test-support"] } |
| 29 | 29 | language = { package="language2", path = "../language2", features = ["test-support"] } |
| 30 | 30 | project = { package="project2", path = "../project2", features = ["test-support"] } |
| 31 | +menu = { package = "menu2", path = "../menu2" } | |
| 32 | +go_to_line = { package = "go_to_line2", path = "../go_to_line2" } | |
| 31 | 33 | serde_json.workspace = true |
| 32 | 34 | workspace = { package="workspace2", path = "../workspace2", features = ["test-support"] } |
| 33 | 35 | ctor.workspace = true |
| @@ -362,7 +362,9 @@ mod tests { · 361 unmodified lines | ||
| 362 | 362 | |
| 363 | 363 | use super::*; |
| 364 | 364 | use editor::Editor; |
| 365 | + use go_to_line::GoToLine; | |
| 365 | 366 | use gpui::TestAppContext; |
| 367 | + use language::Point; | |
| 366 | 368 | use project::Project; |
| 367 | 369 | use workspace::{AppState, Workspace}; |
| 368 | 370 | |
| @@ -385,7 +387,6 @@ mod tests { · 16 unmodified lines | ||
| 385 | 387 | #[gpui::test] |
| 386 | 388 | async fn test_command_palette(cx: &mut TestAppContext) { |
| 387 | 389 | let app_state = init_test(cx); |
| 388 | − | |
| 389 | 390 | let project = Project::test(app_state.fs.clone(), [], cx).await; |
| 390 | 391 | let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project.clone(), cx)); |
| 391 | 392 | |
| @@ -455,12 +456,46 @@ mod tests { · 63 unmodified lines | ||
| 455 | 456 | }); |
| 456 | 457 | } |
| 457 | 458 | |
| 459 | + #[gpui::test] | |
| 460 | + async fn test_go_to_line(cx: &mut TestAppContext) { | |
| 461 | + let app_state = init_test(cx); | |
| 462 | + let project = Project::test(app_state.fs.clone(), [], cx).await; | |
| 463 | + let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project.clone(), cx)); | |
| 464 | + | |
| 465 | + cx.simulate_keystrokes("cmd-n"); | |
| 466 | + | |
| 467 | + let editor = workspace.update(cx, |workspace, cx| { | |
| 468 | + workspace.active_item_as::<Editor>(cx).unwrap() | |
| 469 | + }); | |
| 470 | + editor.update(cx, |editor, cx| editor.set_text("1\n2\n3\n4\n5\n6\n", cx)); | |
| 471 | + | |
| 472 | + cx.simulate_keystrokes("cmd-shift-p"); | |
| 473 | + cx.simulate_input("go to line: Toggle"); | |
| 474 | + cx.simulate_keystrokes("enter"); | |
| 475 | + | |
| 476 | + workspace.update(cx, |workspace, cx| { | |
| 477 | + assert!(workspace.active_modal::<GoToLine>(cx).is_some()) | |
| 478 | + }); | |
| 479 | + | |
| 480 | + cx.simulate_keystrokes("3 enter"); | |
| 481 | + | |
| 482 | + editor.update(cx, |editor, cx| { | |
| 483 | + assert!(editor.focus_handle(cx).is_focused(cx)); | |
| 484 | + assert_eq!( | |
| 485 | + editor.selections.last::<Point>(cx).range().start, | |
| 486 | + Point::new(2, 0) | |
| 487 | + ); | |
| 488 | + }); | |
| 489 | + } | |
| 490 | + | |
| 458 | 491 | fn init_test(cx: &mut TestAppContext) -> Arc<AppState> { |
| 459 | 492 | cx.update(|cx| { |
| 460 | 493 | let app_state = AppState::test(cx); |
| 461 | 494 | theme::init(theme::LoadThemes::JustBase, cx); |
| 462 | 495 | language::init(cx); |
| 463 | 496 | editor::init(cx); |
| 497 | + menu::init(); | |
| 498 | + go_to_line::init(cx); | |
| 464 | 499 | workspace::init(app_state.clone(), cx); |
| 465 | 500 | init(cx); |
| 466 | 501 | Project::init_settings(cx); |
| @@ -53,6 +53,7 @@ pub struct FeedbackModal { · 52 unmodified lines | ||
| 53 | 53 | email_address_editor: View<Editor>, |
| 54 | 54 | awaiting_submission: bool, |
| 55 | 55 | user_submitted: bool, |
| 56 | + discarded: bool, | |
| 56 | 57 | character_count: i32, |
| 57 | 58 | } |
| 58 | 59 | |
| @@ -64,21 +65,35 @@ impl FocusableView for FeedbackModal { · 5 unmodified lines | ||
| 64 | 65 | impl EventEmitter<DismissEvent> for FeedbackModal {} |
| 65 | 66 | |
| 66 | 67 | impl ModalView for FeedbackModal { |
| 67 | − fn dismiss(&mut self, cx: &mut ViewContext<Self>) -> Task<bool> { | |
| 68 | + fn on_before_dismiss(&mut self, cx: &mut ViewContext<Self>) -> bool { | |
| 68 | 69 | if self.user_submitted { |
| 69 | 70 | self.set_user_submitted(false, cx); |
| 70 | − return cx.spawn(|_, _| async { true }); | |
| 71 | + return true; | |
| 71 | 72 | } |
| 72 | 73 | |
| 73 | − let has_feedback = self.feedback_editor.read(cx).text_option(cx).is_some(); | |
| 74 | + if self.discarded { | |
| 75 | + return true; | |
| 76 | + } | |
| 74 | 77 | |
| 78 | + let has_feedback = self.feedback_editor.read(cx).text_option(cx).is_some(); | |
| 75 | 79 | if !has_feedback { |
| 76 | − return cx.spawn(|_, _| async { true }); | |
| 80 | + return true; | |
| 77 | 81 | } |
| 78 | 82 | |
| 79 | 83 | let answer = cx.prompt(PromptLevel::Info, "Discard feedback?", &["Yes", "No"]); |
| 80 | 84 | |
| 81 | − cx.spawn(|_, _| async { answer.await.ok() == Some(0) }) | |
| 85 | + cx.spawn(move |this, mut cx| async move { | |
| 86 | + if answer.await.ok() == Some(0) { | |
| 87 | + this.update(&mut cx, |this, cx| { | |
| 88 | + this.discarded = true; | |
| 89 | + cx.emit(DismissEvent) | |
| 90 | + }) | |
| 91 | + .log_err(); | |
| 92 | + } | |
| 93 | + }) | |
| 94 | + .detach(); | |
| 95 | + | |
| 96 | + false | |
| 82 | 97 | } |
| 83 | 98 | } |
| 84 | 99 | |
| @@ -169,6 +184,7 @@ impl FeedbackModal { · 84 unmodified lines | ||
| 169 | 184 | email_address_editor, |
| 170 | 185 | awaiting_submission: false, |
| 171 | 186 | user_submitted: false, |
| 187 | + discarded: false, | |
| 172 | 188 | character_count: 0, |
| 173 | 189 | } |
| 174 | 190 | } |
| @@ -92,7 +92,6 @@ impl GoToLine { · 91 unmodified lines | ||
| 92 | 92 | cx: &mut ViewContext<Self>, |
| 93 | 93 | ) { |
| 94 | 94 | match event { |
| 95 | − // todo!() this isn't working... | |
| 96 | 95 | editor::EditorEvent::Blurred => cx.emit(DismissEvent), |
| 97 | 96 | editor::EditorEvent::BufferEdited { .. } => self.highlight_current_line(cx), |
| 98 | 97 | _ => {} |
| @@ -1,23 +1,23 @@ | ||
| 1 | 1 | use gpui::{ |
| 2 | − div, prelude::*, px, AnyView, Div, FocusHandle, ManagedView, Render, Subscription, Task, View, | |
| 3 | − ViewContext, WindowContext, | |
| 2 | + div, prelude::*, px, AnyView, DismissEvent, Div, FocusHandle, ManagedView, Render, | |
| 3 | + Subscription, View, ViewContext, WindowContext, | |
| 4 | 4 | }; |
| 5 | 5 | use ui::{h_stack, v_stack}; |
| 6 | 6 | |
| 7 | 7 | pub trait ModalView: ManagedView { |
| 8 | − fn dismiss(&mut self, cx: &mut ViewContext<Self>) -> Task<bool> { | |
| 9 | − Task::ready(true) | |
| 8 | + fn on_before_dismiss(&mut self, cx: &mut ViewContext<Self>) -> bool { | |
| 9 | + true | |
| 10 | 10 | } |
| 11 | 11 | } |
| 12 | 12 | |
| 13 | 13 | trait ModalViewHandle { |
| 14 | − fn should_dismiss(&mut self, cx: &mut WindowContext) -> Task<bool>; | |
| 14 | + fn on_before_dismiss(&mut self, cx: &mut WindowContext) -> bool; | |
| 15 | 15 | fn view(&self) -> AnyView; |
| 16 | 16 | } |
| 17 | 17 | |
| 18 | 18 | impl<V: ModalView> ModalViewHandle for View<V> { |
| 19 | − fn should_dismiss(&mut self, cx: &mut WindowContext) -> Task<bool> { | |
| 20 | − self.update(cx, |this, cx| this.dismiss(cx)) | |
| 19 | + fn on_before_dismiss(&mut self, cx: &mut WindowContext) -> bool { | |
| 20 | + self.update(cx, |this, cx| this.on_before_dismiss(cx)) | |
| 21 | 21 | } |
| 22 | 22 | |
| 23 | 23 | fn view(&self) -> AnyView { |
| @@ -48,8 +48,8 @@ impl ModalLayer { · 24 unmodified lines | ||
| 48 | 48 | { |
| 49 | 49 | if let Some(active_modal) = &self.active_modal { |
| 50 | 50 | let is_close = active_modal.modal.view().downcast::<V>().is_ok(); |
| 51 | − self.hide_modal(cx); | |
| 52 | − if is_close { | |
| 51 | + let did_close = self.hide_modal(cx); | |
| 52 | + if is_close || !did_close { | |
| 53 | 53 | return; |
| 54 | 54 | } |
| 55 | 55 | } |
| @@ -57,13 +57,15 @@ impl ModalLayer { · 1 unmodified line | ||
| 57 | 57 | self.show_modal(new_modal, cx); |
| 58 | 58 | } |
| 59 | 59 | |
| 60 | − pub fn show_modal<V>(&mut self, new_modal: View<V>, cx: &mut ViewContext<Self>) | |
| 60 | + fn show_modal<V>(&mut self, new_modal: View<V>, cx: &mut ViewContext<Self>) | |
| 61 | 61 | where |
| 62 | 62 | V: ModalView, |
| 63 | 63 | { |
| 64 | 64 | self.active_modal = Some(ActiveModal { |
| 65 | 65 | modal: Box::new(new_modal.clone()), |
| 66 | − subscription: cx.subscribe(&new_modal, |this, modal, e, cx| this.hide_modal(cx)), | |
| 66 | + subscription: cx.subscribe(&new_modal, |this, modal, _: &DismissEvent, cx| { | |
| 67 | + this.hide_modal(cx); | |
| 68 | + }), | |
| 67 | 69 | previous_focus_handle: cx.focused(), |
| 68 | 70 | focus_handle: cx.focus_handle(), |
| 69 | 71 | }); |
| @@ -71,29 +73,25 @@ impl ModalLayer { · 1 unmodified line | ||
| 71 | 73 | cx.notify(); |
| 72 | 74 | } |
| 73 | 75 | |
| 74 | − pub fn hide_modal(&mut self, cx: &mut ViewContext<Self>) { | |
| 76 | + fn hide_modal(&mut self, cx: &mut ViewContext<Self>) -> bool { | |
| 75 | 77 | let Some(active_modal) = self.active_modal.as_mut() else { |
| 76 | − return; | |
| 78 | + return false; | |
| 77 | 79 | }; |
| 78 | 80 | |
| 79 | − let dismiss = active_modal.modal.should_dismiss(cx); | |
| 80 | − | |
| 81 | − cx.spawn(|this, mut cx| async move { | |
| 82 | − if dismiss.await { | |
| 83 | − this.update(&mut cx, |this, cx| { | |
| 84 | − if let Some(active_modal) = this.active_modal.take() { | |
| 85 | − if let Some(previous_focus) = active_modal.previous_focus_handle { | |
| 86 | − if active_modal.focus_handle.contains_focused(cx) { | |
| 87 | − previous_focus.focus(cx); | |
| 88 | − } | |
| 89 | − } | |
| 90 | − cx.notify(); | |
| 91 | − } | |
| 92 | − }) | |
| 93 | − .ok(); | |
| 81 | + let dismiss = active_modal.modal.on_before_dismiss(cx); | |
| 82 | + if !dismiss { | |
| 83 | + return false; | |
| 84 | + } | |
| 85 | + | |
| 86 | + if let Some(active_modal) = self.active_modal.take() { | |
| 87 | + if let Some(previous_focus) = active_modal.previous_focus_handle { | |
| 88 | + if active_modal.focus_handle.contains_focused(cx) { | |
| 89 | + previous_focus.focus(cx); | |
| 90 | + } | |
| 94 | 91 | } |
| 95 | − }) | |
| 96 | − .detach(); | |
| 92 | + cx.notify(); | |
| 93 | + } | |
| 94 | + true | |
| 97 | 95 | } |
| 98 | 96 | |
| 99 | 97 | pub fn active_modal<V>(&self) -> Option<View<V>> |