use anyhow::{Context as _, bail};use collections::{FxHashMap, HashMap, HashSet};use language::{LanguageName, LanguageRegistry};use std::{ borrow::Cow, path::{Path, PathBuf}, sync::Arc, usize,};use tasks_ui::{TaskOverrides, TasksModal}; use dap::{ DapRegistry, DebugRequest, TelemetrySpawnLocation, adapters::DebugAdapterName, send_telemetry,};use editor::Editor;use fuzzy::{StringMatch, StringMatchCandidate};use gpui::{ Action, App, AppContext, DismissEvent, Entity, EventEmitter, FocusHandle, Focusable, KeyContext, Render, Subscription, Task, TaskExt, WeakEntity, actions,};use itertools::Itertools as _;use picker::{Picker, PickerDelegate, highlighted_match_with_paths::HighlightedMatch};use project::{DebugScenarioContext, Project, TaskContexts, TaskSourceKind, task_store::TaskStore};use task::{DebugScenario, RevealTarget, SharedTaskContext, VariableName, ZedDebugConfig};use ui::{ ContextMenu, DropdownMenu, IconWithIndicator, Indicator, KeyBinding, ListItem, ListItemSpacing, Switch, SwitchLabelPosition, ToggleButtonGroup, ToggleButtonSimple, ToggleState, Tooltip, prelude::*,};use ui_input::InputField;use util::{ResultExt, debug_panic, rel_path::RelPath, shell::ShellKind};use workspace::{ModalView, Workspace, notifications::DetachAndPromptErr, pane}; use crate::{ attach_modal::{AttachModal, ModalIntent}, debugger_panel::DebugPanel,}; actions!( new_process_modal, [ ActivateTaskTab, ActivateDebugTab, ActivateAttachTab, ActivateLaunchTab ]); pub(super) struct NewProcessModal { workspace: WeakEntity<Workspace>, debug_panel: WeakEntity<DebugPanel>, mode: NewProcessMode, debug_picker: Entity<Picker<DebugDelegate>>, attach_mode: Entity<AttachMode>, configure_mode: Entity<ConfigureMode>, task_mode: TaskMode, debugger: Option<DebugAdapterName>, _subscriptions: [Subscription; 3],} fn suggested_label(request: &DebugRequest, debugger: &str) -> SharedString { match request { DebugRequest::Launch(config) => { let last_path_component = Path::new(&config.program) .file_name() .map(|name| name.to_string_lossy()) .unwrap_or_else(|| Cow::Borrowed(&config.program)); format!("{} ({debugger})", last_path_component).into() } DebugRequest::Attach(config) => format!( "pid: {} ({debugger})", config.process_id.unwrap_or(u32::MAX) ) .into(), }} impl NewProcessModal { pub(super) fn show( workspace: &mut Workspace, window: &mut Window, mode: NewProcessMode, reveal_target: Option<RevealTarget>, cx: &mut Context<Workspace>, ) { let Some(debug_panel) = workspace.panel::<DebugPanel>(cx) else { return; }; let task_store = workspace.project().read(cx).task_store().clone(); let languages = workspace.app_state().languages.clone(); cx.spawn_in(window, async move |workspace, cx| { let task_contexts = workspace.update_in(cx, |workspace, window, cx| { // todo(debugger): get the buffer here (if the active item is an editor) and store it so we can pass it to start_session later tasks_ui::task_contexts(workspace, window, cx) })?; workspace.update_in(cx, |workspace, window, cx| { let workspace_handle = workspace.weak_handle(); let project = workspace.project().clone(); workspace.toggle_modal(window, cx, |window, cx| { let attach_mode = AttachMode::new(None, workspace_handle.clone(), project, window, cx); let debug_picker = cx.new(|cx| { let delegate = DebugDelegate::new(debug_panel.downgrade(), task_store.clone()); Picker::list(delegate, window, cx) .embedded() .list_measure_all() }); let configure_mode = ConfigureMode::new(window, cx); let task_overrides = Some(TaskOverrides { reveal_target }); let task_mode = TaskMode { task_modal: cx.new(|cx| { TasksModal::new( task_store.clone(), Arc::new(TaskContexts::default()), task_overrides, false, workspace_handle.clone(), window, cx, ) }), }; let _subscriptions = [ cx.subscribe(&debug_picker, |_, _, _, cx| { cx.emit(DismissEvent); }), cx.subscribe( &attach_mode.read(cx).attach_picker.clone(), |_, _, _, cx| { cx.emit(DismissEvent); }, ), cx.subscribe(&task_mode.task_modal, |_, _, _: &DismissEvent, cx| { cx.emit(DismissEvent) }), ]; cx.spawn_in(window, { let debug_picker = debug_picker.downgrade(); let configure_mode = configure_mode.downgrade(); let task_modal = task_mode.task_modal.downgrade(); let workspace = workspace_handle.clone(); async move |this, cx| { let task_contexts = task_contexts.await; let task_contexts = Arc::new(task_contexts); let lsp_task_sources = task_contexts.lsp_task_sources.clone(); let task_position = task_contexts.latest_selection; // Get LSP tasks and filter out based on language vs lsp preference let (lsp_tasks, prefer_lsp) = workspace.update(cx, |workspace, cx| { let lsp_tasks = editor::lsp_tasks( workspace.project().clone(), &lsp_task_sources, task_position, cx, ); let prefer_lsp = workspace .active_item(cx) .and_then(|item| item.downcast::<Editor>()) .map(|editor| { editor .read(cx) .buffer() .read(cx) .language_settings(cx) .tasks .prefer_lsp }) .unwrap_or(false); (lsp_tasks, prefer_lsp) })?; let lsp_tasks = lsp_tasks.await; let add_current_language_tasks = !prefer_lsp || lsp_tasks.is_empty(); let lsp_tasks = lsp_tasks .into_iter() .flat_map(|(kind, tasks_with_locations)| { tasks_with_locations .into_iter() .sorted_by_key(|(location, task)| { (location.is_none(), task.resolved_label.clone()) }) .map(move |(_, task)| (kind.clone(), task)) }) .collect::<Vec<_>>(); let Some(task_inventory) = task_store .update(cx, |task_store, _| task_store.task_inventory().cloned()) else { return Ok(()); }; let (used_tasks, current_resolved_tasks) = task_inventory .update(cx, |task_inventory, cx| { task_inventory .used_and_current_resolved_tasks(task_contexts.clone(), cx) }) .await; 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() .and_then(|context| context.cwd.clone()) { configure_mode .update_in(cx, |configure_mode, window, cx| { configure_mode.load(active_cwd, window, cx); }) .ok(); } task_modal .update_in(cx, |task_modal, window, cx| { task_modal.tasks_loaded( task_contexts, lsp_tasks, used_tasks, current_resolved_tasks, add_current_language_tasks, window, cx, ); }) .ok(); this.update(cx, |_, cx| { cx.notify(); }) .ok(); anyhow::Ok(()) } }) .detach(); Self { debug_picker, attach_mode, configure_mode, task_mode, debugger: None, mode, debug_panel: debug_panel.downgrade(), workspace: workspace_handle, _subscriptions, } }); })?; anyhow::Ok(()) }) .detach(); } fn render_mode(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl ui::IntoElement { let dap_menu = self.adapter_drop_down_menu(window, cx); match self.mode { NewProcessMode::Task => self .task_mode .task_modal .read(cx) .picker .clone() .into_any_element(), NewProcessMode::Attach => self.attach_mode.update(cx, |this, cx| { this.clone().render(window, cx).into_any_element() }), NewProcessMode::Launch => self.configure_mode.update(cx, |this, cx| { this.clone().render(dap_menu, window, cx).into_any_element() }), NewProcessMode::Debug => v_flex() .w(rems(34.)) .child(self.debug_picker.clone()) .into_any_element(), } } fn mode_focus_handle(&self, cx: &App) -> FocusHandle { match self.mode { NewProcessMode::Task => self.task_mode.task_modal.focus_handle(cx), NewProcessMode::Attach => self.attach_mode.read(cx).attach_picker.focus_handle(cx), NewProcessMode::Launch => self.configure_mode.read(cx).program.focus_handle(cx), NewProcessMode::Debug => self.debug_picker.focus_handle(cx), } } fn debug_scenario(&self, debugger: &str, cx: &App) -> Task<Option<DebugScenario>> { let request = match self.mode { NewProcessMode::Launch => { DebugRequest::Launch(self.configure_mode.read(cx).debug_request(cx)) } NewProcessMode::Attach => { DebugRequest::Attach(self.attach_mode.read(cx).debug_request()) } _ => return Task::ready(None), }; let label = suggested_label(&request, debugger); let stop_on_entry = if let NewProcessMode::Launch = &self.mode { Some(self.configure_mode.read(cx).stop_on_entry.selected()) } else { None }; let session_scenario = ZedDebugConfig { adapter: debugger.to_owned().into(), label, request, stop_on_entry, }; let adapter = cx .global::<DapRegistry>() .adapter(&session_scenario.adapter); cx.spawn(async move |_| adapter?.config_from_zed_format(session_scenario).await.ok()) } fn start_new_session(&mut self, window: &mut Window, cx: &mut Context<Self>) { if self.debugger.as_ref().is_none() { return; } if let NewProcessMode::Debug = &self.mode { self.debug_picker.update(cx, |picker, cx| { picker.delegate.confirm(false, window, cx); }); return; } if let NewProcessMode::Launch = &self.mode && self.configure_mode.read(cx).save_to_debug_json.selected() { self.save_debug_scenario(window, cx); } let Some(debugger) = self.debugger.clone() else { return; }; let debug_panel = self.debug_panel.clone(); let Some(task_contexts) = self.task_contexts(cx) else { return; }; let task_context = task_contexts .active_context() .cloned() .unwrap_or_default() .into(); let worktree_id = task_contexts.worktree(); let mode = self.mode; cx.spawn_in(window, async move |this, cx| { let Some(config) = this .update(cx, |this, cx| this.debug_scenario(&debugger, cx))? .await else { bail!("debug config not found in mode: {mode}"); }; debug_panel.update_in(cx, |debug_panel, window, cx| { send_telemetry(&config, TelemetrySpawnLocation::Custom, cx); debug_panel.start_session(config, task_context, None, worktree_id, window, cx) })?; this.update(cx, |_, cx| { cx.emit(DismissEvent); }) .ok(); anyhow::Ok(()) }) .detach_and_log_err(cx); } fn update_attach_picker( attach: &Entity<AttachMode>, adapter: &DebugAdapterName, window: &mut Window, cx: &mut App, ) { attach.update(cx, |this, cx| { if adapter.0 != this.definition.adapter { this.definition.adapter = adapter.0.clone(); this.attach_picker.update(cx, |this, cx| { this.picker.update(cx, |this, cx| { match &mut this.delegate.intent { ModalIntent::AttachToProcess(definition) => { definition.adapter = adapter.0.clone(); this.focus(window, cx); }, ModalIntent::ResolveProcessId(_) => { debug_panic!("Attach picker attempted to update config when in resolve Process ID mode"); } } }) }); } cx.notify(); }) } fn task_contexts(&self, cx: &App) -> Option<Arc<TaskContexts>> { self.debug_picker.read(cx).delegate.task_contexts.clone() } pub fn save_debug_scenario(&mut self, window: &mut Window, cx: &mut Context<Self>) { let task_contexts = self.task_contexts(cx); let Some(adapter) = self.debugger.as_ref() else { return; }; let scenario = self.debug_scenario(adapter, cx); cx.spawn_in(window, async move |this, cx| { let scenario = scenario.await.context("no scenario to save")?; let worktree_id = task_contexts .context("no task contexts")? .worktree() .context("no active worktree")?; this.update_in(cx, |this, window, cx| { this.debug_panel.update(cx, |panel, cx| { panel.save_scenario(scenario, worktree_id, window, cx) }) })?? .await?; this.update_in(cx, |_, _, cx| { cx.emit(DismissEvent); }) }) .detach_and_prompt_err("Failed to edit debug.json", window, cx, |_, _, _| None); } fn adapter_drop_down_menu( &mut self, window: &mut Window, cx: &mut Context<Self>, ) -> DropdownMenu { let workspace = self.workspace.clone(); let weak = cx.weak_entity(); let active_buffer = self.task_contexts(cx).and_then(|tc| { tc.active_item_context .as_ref() .and_then(|aic| aic.1.as_ref().map(|l| l.buffer.clone())) }); let active_buffer_language = active_buffer .and_then(|buffer| buffer.read(cx).language()) .cloned(); let mut available_adapters: Vec<_> = workspace .update(cx, |_, cx| DapRegistry::global(cx).enumerate_adapters()) .unwrap_or_default(); if let Some(language) = active_buffer_language { available_adapters.sort_by_key(|adapter| { language .config() .debuggers .get_index_of(adapter.0.as_ref()) .unwrap_or(usize::MAX) }); if self.debugger.is_none() { self.debugger = available_adapters.first().cloned(); } } let label = self .debugger .as_ref() .map(|d| d.0.clone()) .unwrap_or_else(|| SELECT_DEBUGGER_LABEL.clone()); DropdownMenu::new( "dap-adapter-picker",Lines 1–500 of 1669. Load more
use anyhow::{Context as _, bail}; use collections::{FxHashMap, HashMap, HashSet}; use language::{LanguageName, LanguageRegistry}; use std::{ borrow::Cow, path::{Path, PathBuf}, sync::Arc, usize, }; use tasks_ui::{TaskOverrides, TasksModal}; use dap::{ DapRegistry, DebugRequest, TelemetrySpawnLocation, adapters::DebugAdapterName, send_telemetry, }; use editor::Editor; use fuzzy::{StringMatch, StringMatchCandidate}; use gpui::{ Action, App, AppContext, DismissEvent, Entity, EventEmitter, FocusHandle, Focusable, KeyContext, Render, Subscription, Task, TaskExt, WeakEntity, actions, }; use itertools::Itertools as _; use picker::{Picker, PickerDelegate, highlighted_match_with_paths::HighlightedMatch}; use project::{DebugScenarioContext, Project, TaskContexts, TaskSourceKind, task_store::TaskStore}; use task::{DebugScenario, RevealTarget, SharedTaskContext, VariableName, ZedDebugConfig}; use ui::{ ContextMenu, DropdownMenu, IconWithIndicator, Indicator, KeyBinding, ListItem, ListItemSpacing, Switch, SwitchLabelPosition, ToggleButtonGroup, ToggleButtonSimple, ToggleState, Tooltip, prelude::*, }; use ui_input::InputField; use util::{ResultExt, debug_panic, rel_path::RelPath, shell::ShellKind}; use workspace::{ModalView, Workspace, notifications::DetachAndPromptErr, pane}; use crate::{ attach_modal::{AttachModal, ModalIntent}, debugger_panel::DebugPanel, }; actions!( new_process_modal, [ ActivateTaskTab, ActivateDebugTab, ActivateAttachTab, ActivateLaunchTab ] ); pub(super) struct NewProcessModal { workspace: WeakEntity<Workspace>, debug_panel: WeakEntity<DebugPanel>, mode: NewProcessMode, debug_picker: Entity<Picker<DebugDelegate>>, attach_mode: Entity<AttachMode>, configure_mode: Entity<ConfigureMode>, task_mode: TaskMode, debugger: Option<DebugAdapterName>, _subscriptions: [Subscription; 3], } fn suggested_label(request: &DebugRequest, debugger: &str) -> SharedString { match request { DebugRequest::Launch(config) => { let last_path_component = Path::new(&config.program) .file_name() .map(|name| name.to_string_lossy()) .unwrap_or_else(|| Cow::Borrowed(&config.program)); format!("{} ({debugger})", last_path_component).into() } DebugRequest::Attach(config) => format!( "pid: {} ({debugger})", config.process_id.unwrap_or(u32::MAX) ) .into(), } } impl NewProcessModal { pub(super) fn show( workspace: &mut Workspace, window: &mut Window, mode: NewProcessMode, reveal_target: Option<RevealTarget>, cx: &mut Context<Workspace>, ) { let Some(debug_panel) = workspace.panel::<DebugPanel>(cx) else { return; }; let task_store = workspace.project().read(cx).task_store().clone(); let languages = workspace.app_state().languages.clone(); cx.spawn_in(window, async move |workspace, cx| { let task_contexts = workspace.update_in(cx, |workspace, window, cx| { // todo(debugger): get the buffer here (if the active item is an editor) and store it so we can pass it to start_session later tasks_ui::task_contexts(workspace, window, cx) })?; workspace.update_in(cx, |workspace, window, cx| { let workspace_handle = workspace.weak_handle(); let project = workspace.project().clone(); workspace.toggle_modal(window, cx, |window, cx| { let attach_mode = AttachMode::new(None, workspace_handle.clone(), project, window, cx); let debug_picker = cx.new(|cx| { let delegate = DebugDelegate::new(debug_panel.downgrade(), task_store.clone()); Picker::list(delegate, window, cx) .embedded() .list_measure_all() }); let configure_mode = ConfigureMode::new(window, cx); let task_overrides = Some(TaskOverrides { reveal_target }); let task_mode = TaskMode { task_modal: cx.new(|cx| { TasksModal::new( task_store.clone(), Arc::new(TaskContexts::default()), task_overrides, false, workspace_handle.clone(), window, cx, ) }), }; let _subscriptions = [ cx.subscribe(&debug_picker, |_, _, _, cx| { cx.emit(DismissEvent); }), cx.subscribe( &attach_mode.read(cx).attach_picker.clone(), |_, _, _, cx| { cx.emit(DismissEvent); }, ), cx.subscribe(&task_mode.task_modal, |_, _, _: &DismissEvent, cx| { cx.emit(DismissEvent) }), ]; cx.spawn_in(window, { let debug_picker = debug_picker.downgrade(); let configure_mode = configure_mode.downgrade(); let task_modal = task_mode.task_modal.downgrade(); let workspace = workspace_handle.clone(); async move |this, cx| { let task_contexts = task_contexts.await; let task_contexts = Arc::new(task_contexts); let lsp_task_sources = task_contexts.lsp_task_sources.clone(); let task_position = task_contexts.latest_selection; // Get LSP tasks and filter out based on language vs lsp preference let (lsp_tasks, prefer_lsp) = workspace.update(cx, |workspace, cx| { let lsp_tasks = editor::lsp_tasks( workspace.project().clone(), &lsp_task_sources, task_position, cx, ); let prefer_lsp = workspace .active_item(cx) .and_then(|item| item.downcast::<Editor>()) .map(|editor| { editor .read(cx) .buffer() .read(cx) .language_settings(cx) .tasks .prefer_lsp }) .unwrap_or(false); (lsp_tasks, prefer_lsp) })?; let lsp_tasks = lsp_tasks.await; let add_current_language_tasks = !prefer_lsp || lsp_tasks.is_empty(); let lsp_tasks = lsp_tasks .into_iter() .flat_map(|(kind, tasks_with_locations)| { tasks_with_locations .into_iter() .sorted_by_key(|(location, task)| { (location.is_none(), task.resolved_label.clone()) }) .map(move |(_, task)| (kind.clone(), task)) }) .collect::<Vec<_>>(); let Some(task_inventory) = task_store .update(cx, |task_store, _| task_store.task_inventory().cloned()) else { return Ok(()); }; let (used_tasks, current_resolved_tasks) = task_inventory .update(cx, |task_inventory, cx| { task_inventory .used_and_current_resolved_tasks(task_contexts.clone(), cx) }) .await; 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() .and_then(|context| context.cwd.clone()) { configure_mode .update_in(cx, |configure_mode, window, cx| { configure_mode.load(active_cwd, window, cx); }) .ok(); } task_modal .update_in(cx, |task_modal, window, cx| { task_modal.tasks_loaded( task_contexts, lsp_tasks, used_tasks, current_resolved_tasks, add_current_language_tasks, window, cx, ); }) .ok(); this.update(cx, |_, cx| { cx.notify(); }) .ok(); anyhow::Ok(()) } }) .detach(); Self { debug_picker, attach_mode, configure_mode, task_mode, debugger: None, mode, debug_panel: debug_panel.downgrade(), workspace: workspace_handle, _subscriptions, } }); })?; anyhow::Ok(()) }) .detach(); } fn render_mode(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl ui::IntoElement { let dap_menu = self.adapter_drop_down_menu(window, cx); match self.mode { NewProcessMode::Task => self .task_mode .task_modal .read(cx) .picker .clone() .into_any_element(), NewProcessMode::Attach => self.attach_mode.update(cx, |this, cx| { this.clone().render(window, cx).into_any_element() }), NewProcessMode::Launch => self.configure_mode.update(cx, |this, cx| { this.clone().render(dap_menu, window, cx).into_any_element() }), NewProcessMode::Debug => v_flex() .w(rems(34.)) .child(self.debug_picker.clone()) .into_any_element(), } } fn mode_focus_handle(&self, cx: &App) -> FocusHandle { match self.mode { NewProcessMode::Task => self.task_mode.task_modal.focus_handle(cx), NewProcessMode::Attach => self.attach_mode.read(cx).attach_picker.focus_handle(cx), NewProcessMode::Launch => self.configure_mode.read(cx).program.focus_handle(cx), NewProcessMode::Debug => self.debug_picker.focus_handle(cx), } } fn debug_scenario(&self, debugger: &str, cx: &App) -> Task<Option<DebugScenario>> { let request = match self.mode { NewProcessMode::Launch => { DebugRequest::Launch(self.configure_mode.read(cx).debug_request(cx)) } NewProcessMode::Attach => { DebugRequest::Attach(self.attach_mode.read(cx).debug_request()) } _ => return Task::ready(None), }; let label = suggested_label(&request, debugger); let stop_on_entry = if let NewProcessMode::Launch = &self.mode { Some(self.configure_mode.read(cx).stop_on_entry.selected()) } else { None }; let session_scenario = ZedDebugConfig { adapter: debugger.to_owned().into(), label, request, stop_on_entry, }; let adapter = cx .global::<DapRegistry>() .adapter(&session_scenario.adapter); cx.spawn(async move |_| adapter?.config_from_zed_format(session_scenario).await.ok()) } fn start_new_session(&mut self, window: &mut Window, cx: &mut Context<Self>) { if self.debugger.as_ref().is_none() { return; } if let NewProcessMode::Debug = &self.mode { self.debug_picker.update(cx, |picker, cx| { picker.delegate.confirm(false, window, cx); }); return; } if let NewProcessMode::Launch = &self.mode && self.configure_mode.read(cx).save_to_debug_json.selected() { self.save_debug_scenario(window, cx); } let Some(debugger) = self.debugger.clone() else { return; }; let debug_panel = self.debug_panel.clone(); let Some(task_contexts) = self.task_contexts(cx) else { return; }; let task_context = task_contexts .active_context() .cloned() .unwrap_or_default() .into(); let worktree_id = task_contexts.worktree(); let mode = self.mode; cx.spawn_in(window, async move |this, cx| { let Some(config) = this .update(cx, |this, cx| this.debug_scenario(&debugger, cx))? .await else { bail!("debug config not found in mode: {mode}"); }; debug_panel.update_in(cx, |debug_panel, window, cx| { send_telemetry(&config, TelemetrySpawnLocation::Custom, cx); debug_panel.start_session(config, task_context, None, worktree_id, window, cx) })?; this.update(cx, |_, cx| { cx.emit(DismissEvent); }) .ok(); anyhow::Ok(()) }) .detach_and_log_err(cx); } fn update_attach_picker( attach: &Entity<AttachMode>, adapter: &DebugAdapterName, window: &mut Window, cx: &mut App, ) { attach.update(cx, |this, cx| { if adapter.0 != this.definition.adapter { this.definition.adapter = adapter.0.clone(); this.attach_picker.update(cx, |this, cx| { this.picker.update(cx, |this, cx| { match &mut this.delegate.intent { ModalIntent::AttachToProcess(definition) => { definition.adapter = adapter.0.clone(); this.focus(window, cx); }, ModalIntent::ResolveProcessId(_) => { debug_panic!("Attach picker attempted to update config when in resolve Process ID mode"); } } }) }); } cx.notify(); }) } fn task_contexts(&self, cx: &App) -> Option<Arc<TaskContexts>> { self.debug_picker.read(cx).delegate.task_contexts.clone() } pub fn save_debug_scenario(&mut self, window: &mut Window, cx: &mut Context<Self>) { let task_contexts = self.task_contexts(cx); let Some(adapter) = self.debugger.as_ref() else { return; }; let scenario = self.debug_scenario(adapter, cx); cx.spawn_in(window, async move |this, cx| { let scenario = scenario.await.context("no scenario to save")?; let worktree_id = task_contexts .context("no task contexts")? .worktree() .context("no active worktree")?; this.update_in(cx, |this, window, cx| { this.debug_panel.update(cx, |panel, cx| { panel.save_scenario(scenario, worktree_id, window, cx) }) })?? .await?; this.update_in(cx, |_, _, cx| { cx.emit(DismissEvent); }) }) .detach_and_prompt_err("Failed to edit debug.json", window, cx, |_, _, _| None); } fn adapter_drop_down_menu( &mut self, window: &mut Window, cx: &mut Context<Self>, ) -> DropdownMenu { let workspace = self.workspace.clone(); let weak = cx.weak_entity(); let active_buffer = self.task_contexts(cx).and_then(|tc| { tc.active_item_context .as_ref() .and_then(|aic| aic.1.as_ref().map(|l| l.buffer.clone())) }); let active_buffer_language = active_buffer .and_then(|buffer| buffer.read(cx).language()) .cloned(); let mut available_adapters: Vec<_> = workspace .update(cx, |_, cx| DapRegistry::global(cx).enumerate_adapters()) .unwrap_or_default(); if let Some(language) = active_buffer_language { available_adapters.sort_by_key(|adapter| { language .config() .debuggers .get_index_of(adapter.0.as_ref()) .unwrap_or(usize::MAX) }); if self.debugger.is_none() { self.debugger = available_adapters.first().cloned(); } } let label = self .debugger .as_ref() .map(|d| d.0.clone()) .unwrap_or_else(|| SELECT_DEBUGGER_LABEL.clone()); DropdownMenu::new( "dap-adapter-picker",