mirror of
https://github.com/zed-industries/zed.git
synced 2025-01-16 15:11:25 +00:00
Fix opening GoToLine from Command
This was broken because of the async hop introduced by should_dismiss. Change that API to instead be syncronous, and require that implementors (of which there is only one) to call dismiss again if they want to.
This commit is contained in:
parent
c9aa4a0e00
commit
a2f0accb74
6 changed files with 70 additions and 36 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -2046,8 +2046,10 @@ dependencies = [
|
||||||
"editor2",
|
"editor2",
|
||||||
"env_logger 0.9.3",
|
"env_logger 0.9.3",
|
||||||
"fuzzy2",
|
"fuzzy2",
|
||||||
|
"go_to_line2",
|
||||||
"gpui2",
|
"gpui2",
|
||||||
"language2",
|
"language2",
|
||||||
|
"menu2",
|
||||||
"picker2",
|
"picker2",
|
||||||
"project2",
|
"project2",
|
||||||
"serde",
|
"serde",
|
||||||
|
|
|
@ -28,6 +28,8 @@ gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
|
||||||
editor = { package = "editor2", path = "../editor2", features = ["test-support"] }
|
editor = { package = "editor2", path = "../editor2", features = ["test-support"] }
|
||||||
language = { package="language2", path = "../language2", features = ["test-support"] }
|
language = { package="language2", path = "../language2", features = ["test-support"] }
|
||||||
project = { package="project2", path = "../project2", features = ["test-support"] }
|
project = { package="project2", path = "../project2", features = ["test-support"] }
|
||||||
|
menu = { package = "menu2", path = "../menu2" }
|
||||||
|
go_to_line = { package = "go_to_line2", path = "../go_to_line2" }
|
||||||
serde_json.workspace = true
|
serde_json.workspace = true
|
||||||
workspace = { package="workspace2", path = "../workspace2", features = ["test-support"] }
|
workspace = { package="workspace2", path = "../workspace2", features = ["test-support"] }
|
||||||
ctor.workspace = true
|
ctor.workspace = true
|
||||||
|
|
|
@ -360,6 +360,7 @@ mod tests {
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use editor::Editor;
|
use editor::Editor;
|
||||||
|
use go_to_line::GoToLine;
|
||||||
use gpui::TestAppContext;
|
use gpui::TestAppContext;
|
||||||
use project::Project;
|
use project::Project;
|
||||||
use workspace::{AppState, Workspace};
|
use workspace::{AppState, Workspace};
|
||||||
|
@ -383,7 +384,6 @@ mod tests {
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
async fn test_command_palette(cx: &mut TestAppContext) {
|
async fn test_command_palette(cx: &mut TestAppContext) {
|
||||||
let app_state = init_test(cx);
|
let app_state = init_test(cx);
|
||||||
|
|
||||||
let project = Project::test(app_state.fs.clone(), [], cx).await;
|
let project = Project::test(app_state.fs.clone(), [], cx).await;
|
||||||
let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project.clone(), cx));
|
let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project.clone(), cx));
|
||||||
|
|
||||||
|
@ -453,12 +453,29 @@ mod tests {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[gpui::test]
|
||||||
|
async fn test_go_to_line(cx: &mut TestAppContext) {
|
||||||
|
let app_state = init_test(cx);
|
||||||
|
let project = Project::test(app_state.fs.clone(), [], cx).await;
|
||||||
|
let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project.clone(), cx));
|
||||||
|
|
||||||
|
cx.simulate_keystrokes("cmd-n cmd-shift-p");
|
||||||
|
cx.simulate_input("go to line: Toggle");
|
||||||
|
cx.simulate_keystrokes("enter");
|
||||||
|
|
||||||
|
workspace.update(cx, |workspace, cx| {
|
||||||
|
assert!(workspace.active_modal::<GoToLine>(cx).is_some())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn init_test(cx: &mut TestAppContext) -> Arc<AppState> {
|
fn init_test(cx: &mut TestAppContext) -> Arc<AppState> {
|
||||||
cx.update(|cx| {
|
cx.update(|cx| {
|
||||||
let app_state = AppState::test(cx);
|
let app_state = AppState::test(cx);
|
||||||
theme::init(theme::LoadThemes::JustBase, cx);
|
theme::init(theme::LoadThemes::JustBase, cx);
|
||||||
language::init(cx);
|
language::init(cx);
|
||||||
editor::init(cx);
|
editor::init(cx);
|
||||||
|
menu::init();
|
||||||
|
go_to_line::init(cx);
|
||||||
workspace::init(app_state.clone(), cx);
|
workspace::init(app_state.clone(), cx);
|
||||||
init(cx);
|
init(cx);
|
||||||
Project::init_settings(cx);
|
Project::init_settings(cx);
|
||||||
|
|
|
@ -53,6 +53,7 @@ pub struct FeedbackModal {
|
||||||
email_address_editor: View<Editor>,
|
email_address_editor: View<Editor>,
|
||||||
awaiting_submission: bool,
|
awaiting_submission: bool,
|
||||||
user_submitted: bool,
|
user_submitted: bool,
|
||||||
|
discarded: bool,
|
||||||
character_count: i32,
|
character_count: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,21 +65,35 @@ impl FocusableView for FeedbackModal {
|
||||||
impl EventEmitter<DismissEvent> for FeedbackModal {}
|
impl EventEmitter<DismissEvent> for FeedbackModal {}
|
||||||
|
|
||||||
impl ModalView for FeedbackModal {
|
impl ModalView for FeedbackModal {
|
||||||
fn dismiss(&mut self, cx: &mut ViewContext<Self>) -> Task<bool> {
|
fn on_before_dismiss(&mut self, cx: &mut ViewContext<Self>) -> bool {
|
||||||
if self.user_submitted {
|
if self.user_submitted {
|
||||||
self.set_user_submitted(false, cx);
|
self.set_user_submitted(false, cx);
|
||||||
return cx.spawn(|_, _| async { true });
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.discarded {
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
let has_feedback = self.feedback_editor.read(cx).text_option(cx).is_some();
|
let has_feedback = self.feedback_editor.read(cx).text_option(cx).is_some();
|
||||||
|
|
||||||
if !has_feedback {
|
if !has_feedback {
|
||||||
return cx.spawn(|_, _| async { true });
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
let answer = cx.prompt(PromptLevel::Info, "Discard feedback?", &["Yes", "No"]);
|
let answer = cx.prompt(PromptLevel::Info, "Discard feedback?", &["Yes", "No"]);
|
||||||
|
|
||||||
cx.spawn(|_, _| async { answer.await.ok() == Some(0) })
|
cx.spawn(move |this, mut cx| async move {
|
||||||
|
if answer.await.ok() == Some(0) {
|
||||||
|
this.update(&mut cx, |this, cx| {
|
||||||
|
this.discarded = true;
|
||||||
|
cx.emit(DismissEvent)
|
||||||
|
})
|
||||||
|
.log_err();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.detach();
|
||||||
|
|
||||||
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -169,6 +184,7 @@ impl FeedbackModal {
|
||||||
email_address_editor,
|
email_address_editor,
|
||||||
awaiting_submission: false,
|
awaiting_submission: false,
|
||||||
user_submitted: false,
|
user_submitted: false,
|
||||||
|
discarded: false,
|
||||||
character_count: 0,
|
character_count: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -92,7 +92,6 @@ impl GoToLine {
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
) {
|
) {
|
||||||
match event {
|
match event {
|
||||||
// todo!() this isn't working...
|
|
||||||
editor::EditorEvent::Blurred => cx.emit(DismissEvent),
|
editor::EditorEvent::Blurred => cx.emit(DismissEvent),
|
||||||
editor::EditorEvent::BufferEdited { .. } => self.highlight_current_line(cx),
|
editor::EditorEvent::BufferEdited { .. } => self.highlight_current_line(cx),
|
||||||
_ => {}
|
_ => {}
|
||||||
|
|
|
@ -1,23 +1,23 @@
|
||||||
use gpui::{
|
use gpui::{
|
||||||
div, prelude::*, px, AnyView, Div, FocusHandle, ManagedView, Render, Subscription, Task, View,
|
div, prelude::*, px, AnyView, DismissEvent, Div, FocusHandle, ManagedView, Render,
|
||||||
ViewContext, WindowContext,
|
Subscription, View, ViewContext, WindowContext,
|
||||||
};
|
};
|
||||||
use ui::{h_stack, v_stack};
|
use ui::{h_stack, v_stack};
|
||||||
|
|
||||||
pub trait ModalView: ManagedView {
|
pub trait ModalView: ManagedView {
|
||||||
fn dismiss(&mut self, cx: &mut ViewContext<Self>) -> Task<bool> {
|
fn on_before_dismiss(&mut self, cx: &mut ViewContext<Self>) -> bool {
|
||||||
Task::ready(true)
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
trait ModalViewHandle {
|
trait ModalViewHandle {
|
||||||
fn should_dismiss(&mut self, cx: &mut WindowContext) -> Task<bool>;
|
fn on_before_dismiss(&mut self, cx: &mut WindowContext) -> bool;
|
||||||
fn view(&self) -> AnyView;
|
fn view(&self) -> AnyView;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<V: ModalView> ModalViewHandle for View<V> {
|
impl<V: ModalView> ModalViewHandle for View<V> {
|
||||||
fn should_dismiss(&mut self, cx: &mut WindowContext) -> Task<bool> {
|
fn on_before_dismiss(&mut self, cx: &mut WindowContext) -> bool {
|
||||||
self.update(cx, |this, cx| this.dismiss(cx))
|
self.update(cx, |this, cx| this.on_before_dismiss(cx))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn view(&self) -> AnyView {
|
fn view(&self) -> AnyView {
|
||||||
|
@ -48,8 +48,8 @@ impl ModalLayer {
|
||||||
{
|
{
|
||||||
if let Some(active_modal) = &self.active_modal {
|
if let Some(active_modal) = &self.active_modal {
|
||||||
let is_close = active_modal.modal.view().downcast::<V>().is_ok();
|
let is_close = active_modal.modal.view().downcast::<V>().is_ok();
|
||||||
self.hide_modal(cx);
|
let did_close = self.hide_modal(cx);
|
||||||
if is_close {
|
if is_close || !did_close {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -57,13 +57,15 @@ impl ModalLayer {
|
||||||
self.show_modal(new_modal, cx);
|
self.show_modal(new_modal, cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn show_modal<V>(&mut self, new_modal: View<V>, cx: &mut ViewContext<Self>)
|
fn show_modal<V>(&mut self, new_modal: View<V>, cx: &mut ViewContext<Self>)
|
||||||
where
|
where
|
||||||
V: ModalView,
|
V: ModalView,
|
||||||
{
|
{
|
||||||
self.active_modal = Some(ActiveModal {
|
self.active_modal = Some(ActiveModal {
|
||||||
modal: Box::new(new_modal.clone()),
|
modal: Box::new(new_modal.clone()),
|
||||||
subscription: cx.subscribe(&new_modal, |this, modal, e, cx| this.hide_modal(cx)),
|
subscription: cx.subscribe(&new_modal, |this, modal, _: &DismissEvent, cx| {
|
||||||
|
this.hide_modal(cx);
|
||||||
|
}),
|
||||||
previous_focus_handle: cx.focused(),
|
previous_focus_handle: cx.focused(),
|
||||||
focus_handle: cx.focus_handle(),
|
focus_handle: cx.focus_handle(),
|
||||||
});
|
});
|
||||||
|
@ -71,29 +73,25 @@ impl ModalLayer {
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hide_modal(&mut self, cx: &mut ViewContext<Self>) {
|
fn hide_modal(&mut self, cx: &mut ViewContext<Self>) -> bool {
|
||||||
let Some(active_modal) = self.active_modal.as_mut() else {
|
let Some(active_modal) = self.active_modal.as_mut() else {
|
||||||
return;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
let dismiss = active_modal.modal.should_dismiss(cx);
|
let dismiss = active_modal.modal.on_before_dismiss(cx);
|
||||||
|
if !dismiss {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
cx.spawn(|this, mut cx| async move {
|
if let Some(active_modal) = self.active_modal.take() {
|
||||||
if dismiss.await {
|
if let Some(previous_focus) = active_modal.previous_focus_handle {
|
||||||
this.update(&mut cx, |this, cx| {
|
if active_modal.focus_handle.contains_focused(cx) {
|
||||||
if let Some(active_modal) = this.active_modal.take() {
|
previous_focus.focus(cx);
|
||||||
if let Some(previous_focus) = active_modal.previous_focus_handle {
|
}
|
||||||
if active_modal.focus_handle.contains_focused(cx) {
|
|
||||||
previous_focus.focus(cx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cx.notify();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.ok();
|
|
||||||
}
|
}
|
||||||
})
|
cx.notify();
|
||||||
.detach();
|
}
|
||||||
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn active_modal<V>(&self) -> Option<View<V>>
|
pub fn active_modal<V>(&self) -> Option<View<V>>
|
||||||
|
|
Loading…
Reference in a new issue