debugger: Fix global debug tasks not being picked up (#33664)

Piotr Osiewicz created

Release Notes:

- Fixed a bug which caused global debug scenarios (from global
.zed/debug.json) to not be picked up.

Change summary

crates/project/src/project.rs          | 14 +++++
crates/project/src/project_settings.rs | 67 ++++++++++++++++++++++++++++
2 files changed, 81 insertions(+)

Detailed changes

crates/project/src/project.rs 🔗

@@ -2975,6 +2975,20 @@ impl Project {
                 }),
                 Err(_) => {}
             },
+            SettingsObserverEvent::LocalDebugScenariosUpdated(result) => match result {
+                Err(InvalidSettingsError::Debug { message, path }) => {
+                    let message =
+                        format!("Failed to set local debug scenarios in {path:?}:\n{message}");
+                    cx.emit(Event::Toast {
+                        notification_id: format!("local-debug-scenarios-{path:?}").into(),
+                        message,
+                    });
+                }
+                Ok(path) => cx.emit(Event::HideToast {
+                    notification_id: format!("local-debug-scenarios-{path:?}").into(),
+                }),
+                Err(_) => {}
+            },
         }
     }
 

crates/project/src/project_settings.rs 🔗

@@ -554,6 +554,7 @@ pub enum SettingsObserverMode {
 pub enum SettingsObserverEvent {
     LocalSettingsUpdated(Result<PathBuf, InvalidSettingsError>),
     LocalTasksUpdated(Result<PathBuf, InvalidSettingsError>),
+    LocalDebugScenariosUpdated(Result<PathBuf, InvalidSettingsError>),
 }
 
 impl EventEmitter<SettingsObserverEvent> for SettingsObserver {}
@@ -565,6 +566,7 @@ pub struct SettingsObserver {
     project_id: u64,
     task_store: Entity<TaskStore>,
     _global_task_config_watcher: Task<()>,
+    _global_debug_config_watcher: Task<()>,
 }
 
 /// SettingsObserver observers changes to .zed/{settings, task}.json files in local worktrees
@@ -597,6 +599,11 @@ impl SettingsObserver {
                 paths::tasks_file().clone(),
                 cx,
             ),
+            _global_debug_config_watcher: Self::subscribe_to_global_debug_scenarios_changes(
+                fs.clone(),
+                paths::debug_scenarios_file().clone(),
+                cx,
+            ),
         }
     }
 
@@ -617,6 +624,11 @@ impl SettingsObserver {
                 paths::tasks_file().clone(),
                 cx,
             ),
+            _global_debug_config_watcher: Self::subscribe_to_global_debug_scenarios_changes(
+                fs.clone(),
+                paths::debug_scenarios_file().clone(),
+                cx,
+            ),
         }
     }
 
@@ -1047,6 +1059,61 @@ impl SettingsObserver {
             }
         })
     }
+    fn subscribe_to_global_debug_scenarios_changes(
+        fs: Arc<dyn Fs>,
+        file_path: PathBuf,
+        cx: &mut Context<Self>,
+    ) -> Task<()> {
+        let mut user_tasks_file_rx =
+            watch_config_file(&cx.background_executor(), fs, file_path.clone());
+        let user_tasks_content = cx.background_executor().block(user_tasks_file_rx.next());
+        let weak_entry = cx.weak_entity();
+        cx.spawn(async move |settings_observer, cx| {
+            let Ok(task_store) = settings_observer.read_with(cx, |settings_observer, _| {
+                settings_observer.task_store.clone()
+            }) else {
+                return;
+            };
+            if let Some(user_tasks_content) = user_tasks_content {
+                let Ok(()) = task_store.update(cx, |task_store, cx| {
+                    task_store
+                        .update_user_debug_scenarios(
+                            TaskSettingsLocation::Global(&file_path),
+                            Some(&user_tasks_content),
+                            cx,
+                        )
+                        .log_err();
+                }) else {
+                    return;
+                };
+            }
+            while let Some(user_tasks_content) = user_tasks_file_rx.next().await {
+                let Ok(result) = task_store.update(cx, |task_store, cx| {
+                    task_store.update_user_debug_scenarios(
+                        TaskSettingsLocation::Global(&file_path),
+                        Some(&user_tasks_content),
+                        cx,
+                    )
+                }) else {
+                    break;
+                };
+
+                weak_entry
+                    .update(cx, |_, cx| match result {
+                        Ok(()) => cx.emit(SettingsObserverEvent::LocalDebugScenariosUpdated(Ok(
+                            file_path.clone(),
+                        ))),
+                        Err(err) => cx.emit(SettingsObserverEvent::LocalDebugScenariosUpdated(
+                            Err(InvalidSettingsError::Tasks {
+                                path: file_path.clone(),
+                                message: err.to_string(),
+                            }),
+                        )),
+                    })
+                    .ok();
+            }
+        })
+    }
 }
 
 pub fn local_settings_kind_from_proto(kind: proto::LocalSettingsKind) -> LocalSettingsKind {