Detailed changes
@@ -19,11 +19,11 @@ use std::{collections::BTreeMap, sync::Arc};
pub trait DapLocator: Send + Sync {
fn name(&self) -> SharedString;
/// Determines whether this locator can generate debug target for given task.
- fn create_scenario(
+ async fn create_scenario(
&self,
build_config: &TaskTemplate,
resolved_label: &str,
- adapter: DebugAdapterName,
+ adapter: &DebugAdapterName,
) -> Option<DebugScenario>;
async fn run(&self, build_config: SpawnInTerminal) -> Result<DebugRequest>;
@@ -17,7 +17,7 @@ use fuzzy::{StringMatch, StringMatchCandidate};
use gpui::{
Action, App, AppContext, DismissEvent, Entity, EventEmitter, FocusHandle, Focusable,
HighlightStyle, InteractiveText, KeyContext, PromptButton, PromptLevel, Render, StyledText,
- Subscription, TextStyle, UnderlineStyle, WeakEntity,
+ Subscription, Task, TextStyle, UnderlineStyle, WeakEntity,
};
use itertools::Itertools as _;
use picker::{Picker, PickerDelegate, highlighted_match_with_paths::HighlightedMatch};
@@ -201,20 +201,24 @@ impl NewProcessModal {
})?
.await;
- debug_picker
- .update_in(cx, |picker, window, cx| {
- picker.delegate.tasks_loaded(
- task_contexts.clone(),
- languages,
- lsp_tasks.clone(),
- current_resolved_tasks.clone(),
- add_current_language_tasks,
- cx,
- );
- picker.refresh(window, cx);
- cx.notify();
- })
- .ok();
+ if let Ok(task) = debug_picker.update(cx, |picker, cx| {
+ picker.delegate.tasks_loaded(
+ task_contexts.clone(),
+ languages,
+ lsp_tasks.clone(),
+ current_resolved_tasks.clone(),
+ add_current_language_tasks,
+ cx,
+ )
+ }) {
+ task.await;
+ debug_picker
+ .update_in(cx, |picker, window, cx| {
+ picker.refresh(window, cx);
+ cx.notify();
+ })
+ .ok();
+ }
if let Some(active_cwd) = task_contexts
.active_context()
@@ -1143,61 +1147,67 @@ impl DebugDelegate {
current_resolved_tasks: Vec<(TaskSourceKind, task::ResolvedTask)>,
add_current_language_tasks: bool,
cx: &mut Context<Picker<Self>>,
- ) {
+ ) -> Task<()> {
self.task_contexts = Some(task_contexts.clone());
-
- let (recent, scenarios) = self
- .task_store
- .update(cx, |task_store, cx| {
- task_store.task_inventory().map(|inventory| {
- inventory.update(cx, |inventory, cx| {
- inventory.list_debug_scenarios(
- &task_contexts,
- lsp_tasks,
- current_resolved_tasks,
- add_current_language_tasks,
- cx,
- )
- })
+ let task = self.task_store.update(cx, |task_store, cx| {
+ task_store.task_inventory().map(|inventory| {
+ inventory.update(cx, |inventory, cx| {
+ inventory.list_debug_scenarios(
+ &task_contexts,
+ lsp_tasks,
+ current_resolved_tasks,
+ add_current_language_tasks,
+ cx,
+ )
})
})
- .unwrap_or_default();
+ });
+ cx.spawn(async move |this, cx| {
+ let (recent, scenarios) = if let Some(task) = task {
+ task.await
+ } else {
+ (Vec::new(), Vec::new())
+ };
- if !recent.is_empty() {
- self.last_used_candidate_index = Some(recent.len() - 1);
- }
+ this.update(cx, |this, cx| {
+ if !recent.is_empty() {
+ this.delegate.last_used_candidate_index = Some(recent.len() - 1);
+ }
- let dap_registry = cx.global::<DapRegistry>();
- let hide_vscode = scenarios.iter().any(|(kind, _)| match kind {
- TaskSourceKind::Worktree {
- id: _,
- directory_in_worktree: dir,
- id_base: _,
- } => dir.ends_with(".zed"),
- _ => false,
- });
+ let dap_registry = cx.global::<DapRegistry>();
+ let hide_vscode = scenarios.iter().any(|(kind, _)| match kind {
+ TaskSourceKind::Worktree {
+ id: _,
+ directory_in_worktree: dir,
+ id_base: _,
+ } => dir.ends_with(".zed"),
+ _ => false,
+ });
- self.candidates = recent
- .into_iter()
- .map(|scenario| Self::get_scenario_kind(&languages, &dap_registry, scenario))
- .chain(
- scenarios
+ this.delegate.candidates = recent
.into_iter()
- .filter(|(kind, _)| match kind {
- TaskSourceKind::Worktree {
- id: _,
- directory_in_worktree: dir,
- id_base: _,
- } => !(hide_vscode && dir.ends_with(".vscode")),
- _ => true,
- })
- .map(|(kind, scenario)| {
- let (language, scenario) =
- Self::get_scenario_kind(&languages, &dap_registry, scenario);
- (language.or(Some(kind)), scenario)
- }),
- )
- .collect();
+ .map(|scenario| Self::get_scenario_kind(&languages, &dap_registry, scenario))
+ .chain(
+ scenarios
+ .into_iter()
+ .filter(|(kind, _)| match kind {
+ TaskSourceKind::Worktree {
+ id: _,
+ directory_in_worktree: dir,
+ id_base: _,
+ } => !(hide_vscode && dir.ends_with(".vscode")),
+ _ => true,
+ })
+ .map(|(kind, scenario)| {
+ let (language, scenario) =
+ Self::get_scenario_kind(&languages, &dap_registry, scenario);
+ (language.or(Some(kind)), scenario)
+ }),
+ )
+ .collect();
+ })
+ .ok();
+ })
}
}
@@ -1355,24 +1365,44 @@ impl PickerDelegate for DebugDelegate {
else {
return;
};
- let Some(debug_scenario) = cx
- .global::<DapRegistry>()
- .locators()
- .iter()
- .find_map(|locator| locator.1.create_scenario(&task, "one-off", adapter.clone()))
- else {
- return;
- };
-
- send_telemetry(&debug_scenario, TelemetrySpawnLocation::ScenarioList, cx);
+ let locators = cx.global::<DapRegistry>().locators();
+ cx.spawn_in(window, async move |this, cx| {
+ let Some(debug_scenario) = cx
+ .background_spawn(async move {
+ for locator in locators {
+ if let Some(scenario) =
+ locator.1.create_scenario(&task, "one-off", &adapter).await
+ {
+ return Some(scenario);
+ }
+ }
+ None
+ })
+ .await
+ else {
+ return;
+ };
- self.debug_panel
- .update(cx, |panel, cx| {
- panel.start_session(debug_scenario, task_context, None, worktree_id, window, cx);
+ this.update_in(cx, |this, window, cx| {
+ send_telemetry(&debug_scenario, TelemetrySpawnLocation::ScenarioList, cx);
+ this.delegate
+ .debug_panel
+ .update(cx, |panel, cx| {
+ panel.start_session(
+ debug_scenario,
+ task_context,
+ None,
+ worktree_id,
+ window,
+ cx,
+ );
+ })
+ .ok();
+ cx.emit(DismissEvent);
})
.ok();
-
- cx.emit(DismissEvent);
+ })
+ .detach();
}
fn confirm(&mut self, _: bool, window: &mut Window, cx: &mut Context<picker::Picker<Self>>) {
@@ -855,7 +855,7 @@ impl RunningState {
debug_assert!(!config_is_valid);
Some(locator_name)
} else if !config_is_valid {
- dap_store
+ let task = dap_store
.update(cx, |this, cx| {
this.debug_scenario_for_build_task(
task.original_task().clone(),
@@ -863,17 +863,21 @@ impl RunningState {
task.display_label().to_owned().into(),
cx,
)
- .and_then(|scenario| {
- match scenario.build {
- Some(BuildTaskDefinition::Template {
- locator_name, ..
- }) => locator_name,
- _ => None,
- }
- })
+
+ });
+ if let Ok(t) = task {
+ t.await.and_then(|scenario| {
+ match scenario.build {
+ Some(BuildTaskDefinition::Template {
+ locator_name, ..
+ }) => locator_name,
+ _ => None,
+ }
})
- .ok()
- .flatten()
+ } else {
+ None
+ }
+
} else {
None
};
@@ -5801,9 +5801,11 @@ impl Editor {
tasks.column,
)),
});
- let debug_scenarios = editor.update(cx, |editor, cx| {
- editor.debug_scenarios(&resolved_tasks, &buffer, cx)
- })?;
+ let debug_scenarios = editor
+ .update(cx, |editor, cx| {
+ editor.debug_scenarios(&resolved_tasks, &buffer, cx)
+ })?
+ .await;
anyhow::Ok((resolved_tasks, debug_scenarios, task_context))
}
})
@@ -5859,7 +5861,7 @@ impl Editor {
resolved_tasks: &Option<ResolvedTasks>,
buffer: &Entity<Buffer>,
cx: &mut App,
- ) -> Vec<task::DebugScenario> {
+ ) -> Task<Vec<task::DebugScenario>> {
if cx.has_flag::<DebuggerFeatureFlag>() {
maybe!({
let project = self.project.as_ref()?;
@@ -5877,21 +5879,27 @@ impl Editor {
dap_store.update(cx, |dap_store, cx| {
for (_, task) in &resolved_tasks.templates {
- if let Some(scenario) = dap_store.debug_scenario_for_build_task(
+ let maybe_scenario = dap_store.debug_scenario_for_build_task(
task.original_task().clone(),
debug_adapter.clone().into(),
task.display_label().to_owned().into(),
cx,
- ) {
- scenarios.push(scenario);
- }
+ );
+ scenarios.push(maybe_scenario);
}
});
- Some(scenarios)
+ Some(cx.background_spawn(async move {
+ let scenarios = futures::future::join_all(scenarios)
+ .await
+ .into_iter()
+ .flatten()
+ .collect::<Vec<_>>();
+ scenarios
+ }))
})
- .unwrap_or_default()
+ .unwrap_or_else(|| Task::ready(vec![]))
} else {
- vec![]
+ Task::ready(vec![])
}
}
@@ -287,11 +287,17 @@ impl DapStore {
adapter: DebugAdapterName,
label: SharedString,
cx: &mut App,
- ) -> Option<DebugScenario> {
- DapRegistry::global(cx)
- .locators()
- .values()
- .find_map(|locator| locator.create_scenario(&build, &label, adapter.clone()))
+ ) -> Task<Option<DebugScenario>> {
+ let locators = DapRegistry::global(cx).locators();
+
+ cx.background_spawn(async move {
+ for locator in locators.values() {
+ if let Some(scenario) = locator.create_scenario(&build, &label, &adapter).await {
+ return Some(scenario);
+ }
+ }
+ None
+ })
}
pub fn run_debug_locator(
@@ -41,11 +41,11 @@ impl DapLocator for CargoLocator {
fn name(&self) -> SharedString {
SharedString::new_static("rust-cargo-locator")
}
- fn create_scenario(
+ async fn create_scenario(
&self,
build_config: &TaskTemplate,
resolved_label: &str,
- adapter: DebugAdapterName,
+ adapter: &DebugAdapterName,
) -> Option<DebugScenario> {
if build_config.command != "cargo" {
return None;
@@ -77,7 +77,7 @@ impl DapLocator for CargoLocator {
}
Some(DebugScenario {
- adapter: adapter.0,
+ adapter: adapter.0.clone(),
label: resolved_label.to_string().into(),
build: Some(BuildTaskDefinition::Template {
task_template,
@@ -89,11 +89,11 @@ impl DapLocator for GoLocator {
SharedString::new_static("go-debug-locator")
}
- fn create_scenario(
+ async fn create_scenario(
&self,
build_config: &TaskTemplate,
resolved_label: &str,
- adapter: DebugAdapterName,
+ adapter: &DebugAdapterName,
) -> Option<DebugScenario> {
if build_config.command != "go" {
return None;
@@ -170,7 +170,7 @@ impl DapLocator for GoLocator {
Some(DebugScenario {
label: resolved_label.to_string().into(),
- adapter: adapter.0,
+ adapter: adapter.0.clone(),
build: None,
config: config,
tcp_connection: None,
@@ -214,7 +214,7 @@ impl DapLocator for GoLocator {
Some(DebugScenario {
label: resolved_label.to_string().into(),
- adapter: adapter.0,
+ adapter: adapter.0.clone(),
build: None,
config,
tcp_connection: None,
@@ -232,10 +232,11 @@ impl DapLocator for GoLocator {
#[cfg(test)]
mod tests {
use super::*;
+ use gpui::TestAppContext;
use task::{HideStrategy, RevealStrategy, RevealTarget, Shell, TaskTemplate};
- #[test]
- fn test_create_scenario_for_go_build() {
+ #[gpui::test]
+ async fn test_create_scenario_for_go_build(_: &mut TestAppContext) {
let locator = GoLocator;
let task = TaskTemplate {
label: "go build".into(),
@@ -254,14 +255,15 @@ mod tests {
show_command: true,
};
- let scenario =
- locator.create_scenario(&task, "test label", DebugAdapterName("Delve".into()));
+ let scenario = locator
+ .create_scenario(&task, "test label", &DebugAdapterName("Delve".into()))
+ .await;
assert!(scenario.is_none());
}
- #[test]
- fn test_skip_non_go_commands_with_non_delve_adapter() {
+ #[gpui::test]
+ async fn test_skip_non_go_commands_with_non_delve_adapter(_: &mut TestAppContext) {
let locator = GoLocator;
let task = TaskTemplate {
label: "cargo build".into(),
@@ -280,19 +282,22 @@ mod tests {
show_command: true,
};
- let scenario = locator.create_scenario(
- &task,
- "test label",
- DebugAdapterName("SomeOtherAdapter".into()),
- );
+ let scenario = locator
+ .create_scenario(
+ &task,
+ "test label",
+ &DebugAdapterName("SomeOtherAdapter".into()),
+ )
+ .await;
assert!(scenario.is_none());
- let scenario =
- locator.create_scenario(&task, "test label", DebugAdapterName("Delve".into()));
+ let scenario = locator
+ .create_scenario(&task, "test label", &DebugAdapterName("Delve".into()))
+ .await;
assert!(scenario.is_none());
}
- #[test]
- fn test_go_locator_run() {
+ #[gpui::test]
+ async fn test_go_locator_run(_: &mut TestAppContext) {
let locator = GoLocator;
let delve = DebugAdapterName("Delve".into());
@@ -319,7 +324,8 @@ mod tests {
};
let scenario = locator
- .create_scenario(&task, "test run label", delve)
+ .create_scenario(&task, "test run label", &delve)
+ .await
.unwrap();
let config: DelveLaunchRequest = serde_json::from_value(scenario.config).unwrap();
@@ -350,8 +356,8 @@ mod tests {
);
}
- #[test]
- fn test_go_locator_test() {
+ #[gpui::test]
+ async fn test_go_locator_test(_: &mut TestAppContext) {
let locator = GoLocator;
let delve = DebugAdapterName("Delve".into());
@@ -370,7 +376,8 @@ mod tests {
..Default::default()
};
let result = locator
- .create_scenario(&task_with_tags, "", delve.clone())
+ .create_scenario(&task_with_tags, "", &delve)
+ .await
.unwrap();
let config: DelveLaunchRequest = serde_json::from_value(result.config).unwrap();
@@ -393,8 +400,8 @@ mod tests {
);
}
- #[test]
- fn test_skip_unsupported_go_commands() {
+ #[gpui::test]
+ async fn test_skip_unsupported_go_commands(_: &mut TestAppContext) {
let locator = GoLocator;
let task = TaskTemplate {
label: "go clean".into(),
@@ -413,8 +420,9 @@ mod tests {
show_command: true,
};
- let scenario =
- locator.create_scenario(&task, "test label", DebugAdapterName("Delve".into()));
+ let scenario = locator
+ .create_scenario(&task, "test label", &DebugAdapterName("Delve".into()))
+ .await;
assert!(scenario.is_none());
}
}
@@ -19,11 +19,11 @@ impl DapLocator for NodeLocator {
}
/// Determines whether this locator can generate debug target for given task.
- fn create_scenario(
+ async fn create_scenario(
&self,
build_config: &TaskTemplate,
resolved_label: &str,
- adapter: DebugAdapterName,
+ adapter: &DebugAdapterName,
) -> Option<DebugScenario> {
if adapter.0.as_ref() != "JavaScript" {
return None;
@@ -68,7 +68,7 @@ impl DapLocator for NodeLocator {
});
Some(DebugScenario {
- adapter: adapter.0,
+ adapter: adapter.0.clone(),
label: resolved_label.to_string().into(),
build: None,
config,
@@ -16,11 +16,11 @@ impl DapLocator for PythonLocator {
}
/// Determines whether this locator can generate debug target for given task.
- fn create_scenario(
+ async fn create_scenario(
&self,
build_config: &TaskTemplate,
resolved_label: &str,
- adapter: DebugAdapterName,
+ adapter: &DebugAdapterName,
) -> Option<DebugScenario> {
if adapter.0.as_ref() != "Debugpy" {
return None;
@@ -92,7 +92,7 @@ impl DapLocator for PythonLocator {
}
Some(DebugScenario {
- adapter: adapter.0,
+ adapter: adapter.0.clone(),
label: resolved_label.to_string().into(),
build: None,
config,
@@ -265,7 +265,7 @@ impl Inventory {
current_resolved_tasks: Vec<(TaskSourceKind, task::ResolvedTask)>,
add_current_language_tasks: bool,
cx: &mut App,
- ) -> (Vec<DebugScenario>, Vec<(TaskSourceKind, DebugScenario)>) {
+ ) -> Task<(Vec<DebugScenario>, Vec<(TaskSourceKind, DebugScenario)>)> {
let mut scenarios = Vec::new();
if let Some(worktree_id) = task_contexts
@@ -279,9 +279,13 @@ impl Inventory {
}
scenarios.extend(self.global_debug_scenarios_from_settings());
- if let Some(location) = task_contexts.location() {
- let file = location.buffer.read(cx).file();
- let language = location.buffer.read(cx).language();
+ let last_scheduled_scenarios = self.last_scheduled_scenarios.iter().cloned().collect();
+
+ let adapter = task_contexts.location().and_then(|location| {
+ let (file, language) = {
+ let buffer = location.buffer.read(cx);
+ (buffer.file(), buffer.language())
+ };
let language_name = language.as_ref().map(|l| l.name());
let adapter = language_settings(language_name, file, cx)
.debuggers
@@ -290,7 +294,10 @@ impl Inventory {
.or_else(|| {
language.and_then(|l| l.config().debuggers.first().map(SharedString::from))
});
- if let Some(adapter) = adapter {
+ adapter.map(|adapter| (adapter, DapRegistry::global(cx).locators()))
+ });
+ cx.background_spawn(async move {
+ if let Some((adapter, locators)) = adapter {
for (kind, task) in
lsp_tasks
.into_iter()
@@ -299,28 +306,21 @@ impl Inventory {
|| !matches!(kind, TaskSourceKind::Language { .. })
}))
{
- if let Some(scenario) =
- DapRegistry::global(cx)
- .locators()
- .values()
- .find_map(|locator| {
- locator.create_scenario(
- &task.original_task().clone(),
- &task.display_label(),
- adapter.clone().into(),
- )
- })
- {
- scenarios.push((kind, scenario));
+ let adapter = adapter.clone().into();
+
+ for locator in locators.values() {
+ if let Some(scenario) = locator
+ .create_scenario(&task.original_task(), &task.display_label(), &adapter)
+ .await
+ {
+ scenarios.push((kind, scenario));
+ break;
+ }
}
}
}
- }
-
- (
- self.last_scheduled_scenarios.iter().cloned().collect(),
- scenarios,
- )
+ (last_scheduled_scenarios, scenarios)
+ })
}
pub fn task_template_by_label(
@@ -1275,7 +1275,7 @@ mod tests {
init_test(cx);
let fs = FakeFs::new(cx.executor());
let inventory = cx.update(|cx| Inventory::new(fs, cx));
- inventory.update(cx, |inventory, cx| {
+ inventory.update(cx, |inventory, _| {
inventory
.update_file_based_scenarios(
TaskSettingsLocation::Global(Path::new("")),
@@ -1291,31 +1291,40 @@ mod tests {
),
)
.unwrap();
+ });
+
+ let (_, scenario) = inventory
+ .update(cx, |this, cx| {
+ this.list_debug_scenarios(&TaskContexts::default(), vec![], vec![], false, cx)
+ })
+ .await
+ .1
+ .first()
+ .unwrap()
+ .clone();
- let (_, scenario) = inventory
- .list_debug_scenarios(&TaskContexts::default(), vec![], vec![], false, cx)
- .1
+ inventory.update(cx, |this, _| {
+ this.scenario_scheduled(scenario.clone());
+ });
+
+ assert_eq!(
+ inventory
+ .update(cx, |this, cx| {
+ this.list_debug_scenarios(&TaskContexts::default(), vec![], vec![], false, cx)
+ })
+ .await
+ .0
.first()
.unwrap()
- .clone();
-
- inventory.scenario_scheduled(scenario.clone());
-
- assert_eq!(
- inventory
- .list_debug_scenarios(&TaskContexts::default(), vec![], vec![], false, cx)
- .0
- .first()
- .unwrap()
- .clone(),
- scenario
- );
+ .clone(),
+ scenario
+ );
- inventory
- .update_file_based_scenarios(
- TaskSettingsLocation::Global(Path::new("")),
- Some(
- r#"
+ inventory.update(cx, |this, _| {
+ this.update_file_based_scenarios(
+ TaskSettingsLocation::Global(Path::new("")),
+ Some(
+ r#"
[{
"label": "test scenario",
"adapter": "Delve",
@@ -1323,25 +1332,29 @@ mod tests {
"program": "wowzer",
}]
"#,
- ),
- )
- .unwrap();
-
- assert_eq!(
- inventory
- .list_debug_scenarios(&TaskContexts::default(), vec![], vec![], false, cx)
- .0
- .first()
- .unwrap()
- .adapter,
- "Delve",
- );
+ ),
+ )
+ .unwrap();
+ });
+ assert_eq!(
inventory
- .update_file_based_scenarios(
- TaskSettingsLocation::Global(Path::new("")),
- Some(
- r#"
+ .update(cx, |this, cx| {
+ this.list_debug_scenarios(&TaskContexts::default(), vec![], vec![], false, cx)
+ })
+ .await
+ .0
+ .first()
+ .unwrap()
+ .adapter,
+ "Delve",
+ );
+
+ inventory.update(cx, |this, _| {
+ this.update_file_based_scenarios(
+ TaskSettingsLocation::Global(Path::new("")),
+ Some(
+ r#"
[{
"label": "testing scenario",
"adapter": "Delve",
@@ -1349,18 +1362,21 @@ mod tests {
"program": "wowzer",
}]
"#,
- ),
- )
- .unwrap();
-
- assert_eq!(
- inventory
- .list_debug_scenarios(&TaskContexts::default(), vec![], vec![], false, cx)
- .0
- .first(),
- None
- );
+ ),
+ )
+ .unwrap();
});
+
+ assert_eq!(
+ inventory
+ .update(cx, |this, cx| {
+ this.list_debug_scenarios(&TaskContexts::default(), vec![], vec![], false, cx)
+ })
+ .await
+ .0
+ .first(),
+ None
+ );
}
#[gpui::test]