use collections::FxHashMap;use language::LanguageRegistry;use paths::local_debug_file_relative_path;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, EditorElement, EditorStyle};use fuzzy::{StringMatch, StringMatchCandidate};use gpui::{ App, AppContext, DismissEvent, Entity, EventEmitter, FocusHandle, Focusable, HighlightStyle, InteractiveText, KeyContext, PromptButton, PromptLevel, Render, StyledText, Subscription, TextStyle, UnderlineStyle, WeakEntity,};use picker::{Picker, PickerDelegate, highlighted_match_with_paths::HighlightedMatch};use project::{ProjectPath, TaskContexts, TaskSourceKind, task_store::TaskStore};use settings::{Settings, initial_local_debug_tasks_content};use task::{DebugScenario, RevealTarget, ZedDebugConfig};use theme::ThemeSettings;use ui::{ ActiveTheme, Button, ButtonCommon, ButtonSize, CheckboxWithLabel, Clickable, Color, Context, ContextMenu, Disableable, DropdownMenu, FluentBuilder, Icon, IconName, IconSize, IconWithIndicator, Indicator, InteractiveElement, IntoElement, Label, LabelCommon as _, ListItem, ListItemSpacing, ParentElement, RenderOnce, SharedString, Styled, StyledExt, StyledTypography, ToggleButton, ToggleState, Toggleable, Tooltip, Window, div, h_flex, px, relative, rems, v_flex,};use util::ResultExt;use workspace::{ModalView, Workspace, pane}; use crate::{attach_modal::AttachModal, debugger_panel::DebugPanel}; // enum SaveScenarioState {// Saving,// Saved((ProjectPath, SharedString)),// Failed(SharedString),// } pub(super) struct NewProcessModal { workspace: WeakEntity<Workspace>, debug_panel: WeakEntity<DebugPanel>, mode: NewProcessMode, debug_picker: Entity<Picker<DebugDelegate>>, attach_mode: Entity<AttachMode>, launch_mode: Entity<LaunchMode>, task_mode: TaskMode, debugger: Option<DebugAdapterName>, // save_scenario_state: Option<SaveScenarioState>, _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| { tasks_ui::task_contexts(workspace, window, cx) })?; workspace.update_in(cx, |workspace, window, cx| { let workspace_handle = workspace.weak_handle(); workspace.toggle_modal(window, cx, |window, cx| { let attach_mode = AttachMode::new(None, workspace_handle.clone(), window, cx); let launch_picker = cx.new(|cx| { let delegate = DebugDelegate::new(debug_panel.downgrade(), task_store.clone()); Picker::uniform_list(delegate, window, cx).modal(false) }); let configure_mode = LaunchMode::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(&launch_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 launch_picker = launch_picker.downgrade(); let configure_mode = configure_mode.downgrade(); let task_modal = task_mode.task_modal.downgrade(); async move |this, cx| { let task_contexts = task_contexts.await; let task_contexts = Arc::new(task_contexts); launch_picker .update_in(cx, |picker, window, cx| { picker.delegate.task_contexts_loaded( task_contexts.clone(), languages, 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.task_contexts_loaded(task_contexts, window, cx); }) .ok(); this.update(cx, |_, cx| { cx.notify(); }) .ok(); } }) .detach(); Self { debug_picker: launch_picker, attach_mode, launch_mode: configure_mode, task_mode, debugger: None, mode, debug_panel: debug_panel.downgrade(), workspace: workspace_handle, // save_scenario_state: None, _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.launch_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.launch_mode.read(cx).program.focus_handle(cx), NewProcessMode::Debug => self.debug_picker.focus_handle(cx), } } fn debug_scenario(&self, debugger: &str, cx: &App) -> Option<DebugScenario> { let request = match self.mode { NewProcessMode::Launch => Some(DebugRequest::Launch( self.launch_mode.read(cx).debug_request(cx), )), NewProcessMode::Attach => Some(DebugRequest::Attach( self.attach_mode.read(cx).debug_request(), )), _ => None, }?; let label = suggested_label(&request, debugger); let stop_on_entry = if let NewProcessMode::Launch = &self.mode { Some(self.launch_mode.read(cx).stop_on_entry.selected()) } else { None }; let session_scenario = ZedDebugConfig { adapter: debugger.to_owned().into(), label, request: request, stop_on_entry, }; cx.global::<DapRegistry>() .adapter(&session_scenario.adapter) .and_then(|adapter| adapter.config_from_zed_format(session_scenario).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; } // TODO: Restore once we have proper, comment preserving edits // if let NewProcessMode::Launch = &self.mode { // if self.launch_mode.read(cx).save_to_debug_json.selected() { // self.save_debug_scenario(window, cx); // } // } let Some(debugger) = self.debugger.as_ref() else { return; }; let Some(config) = self.debug_scenario(debugger, cx) else { log::error!("debug config not found in mode: {}", self.mode); return; }; let debug_panel = self.debug_panel.clone(); let Some(task_contexts) = self.task_contexts(cx) else { return; }; send_telemetry(&config, TelemetrySpawnLocation::Custom, cx); let task_context = task_contexts.active_context().cloned().unwrap_or_default(); let worktree_id = task_contexts.worktree(); cx.spawn_in(window, async move |this, cx| { debug_panel.update_in(cx, |debug_panel, window, 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| { this.delegate.definition.adapter = adapter.0.clone(); this.focus(window, cx); }) }); } cx.notify(); }) } fn task_contexts(&self, cx: &App) -> Option<Arc<TaskContexts>> { self.debug_picker.read(cx).delegate.task_contexts.clone() } // fn save_debug_scenario(&mut self, window: &mut Window, cx: &mut Context<Self>) { // let Some((save_scenario, scenario_label)) = self // .debugger // .as_ref() // .and_then(|debugger| self.debug_scenario(&debugger, cx)) // .zip(self.task_contexts(cx).and_then(|tcx| tcx.worktree())) // .and_then(|(scenario, worktree_id)| { // self.debug_panel // .update(cx, |panel, cx| { // panel.save_scenario(&scenario, worktree_id, window, cx) // }) // .ok() // .zip(Some(scenario.label.clone())) // }) // else { // return; // }; // self.save_scenario_state = Some(SaveScenarioState::Saving); // cx.spawn(async move |this, cx| { // let res = save_scenario.await; // this.update(cx, |this, _| match res { // Ok(saved_file) => { // this.save_scenario_state = // Some(SaveScenarioState::Saved((saved_file, scenario_label))) // } // Err(error) => { // this.save_scenario_state = // Some(SaveScenarioState::Failed(error.to_string().into())) // } // }) // .ok(); // cx.background_executor().timer(Duration::from_secs(3)).await; // this.update(cx, |this, _| this.save_scenario_state.take()) // .ok(); // }) // .detach(); // } fn adapter_drop_down_menu( &mut self, window: &mut Window, cx: &mut Context<Self>, ) -> ui::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 = 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", label, ContextMenu::build(window, cx, move |mut menu, _, _| { let setter_for_name = |name: DebugAdapterName| { let weak = weak.clone(); move |window: &mut Window, cx: &mut App| { weak.update(cx, |this, cx| { this.debugger = Some(name.clone()); cx.notify(); if let NewProcessMode::Attach = &this.mode { Self::update_attach_picker(&this.attach_mode, &name, window, cx); } }) .ok(); } }; for adapter in available_adapters.into_iter() { menu = menu.entry(adapter.0.clone(), None, setter_for_name(adapter.clone())); } menu }), ) } fn open_debug_json(&self, window: &mut Window, cx: &mut Context<NewProcessModal>) { let this = cx.entity(); window .spawn(cx, async move |cx| { let worktree_id = this.update(cx, |this, cx| { let tcx = this.task_contexts(cx); tcx?.worktree() })?; let Some(worktree_id) = worktree_id else { let _ = cx.prompt( PromptLevel::Critical, "Cannot open debug.json", Some("You must have at least one project open"), &[PromptButton::ok("Ok")], ); return Ok(()); }; let editor = this .update_in(cx, |this, window, cx| { this.workspace.update(cx, |workspace, cx| { workspace.open_path( ProjectPath { worktree_id, path: local_debug_file_relative_path().into(), }, None, true, window, cx, ) }) })?? .await?; cx.update(|_window, cx| { if let Some(editor) = editor.act_as::<Editor>(cx) { editor.update(cx, |editor, cx| { editor.buffer().update(cx, |buffer, cx| { if let Some(singleton) = buffer.as_singleton() { singleton.update(cx, |buffer, cx| { if buffer.is_empty() { buffer.edit( [(0..0, initial_local_debug_tasks_content())], None, cx, ); } }) }Lines 1–500 of 1327. Load more
use collections::FxHashMap; use language::LanguageRegistry; use paths::local_debug_file_relative_path; 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, EditorElement, EditorStyle}; use fuzzy::{StringMatch, StringMatchCandidate}; use gpui::{ App, AppContext, DismissEvent, Entity, EventEmitter, FocusHandle, Focusable, HighlightStyle, InteractiveText, KeyContext, PromptButton, PromptLevel, Render, StyledText, Subscription, TextStyle, UnderlineStyle, WeakEntity, }; use picker::{Picker, PickerDelegate, highlighted_match_with_paths::HighlightedMatch}; use project::{ProjectPath, TaskContexts, TaskSourceKind, task_store::TaskStore}; use settings::{Settings, initial_local_debug_tasks_content}; use task::{DebugScenario, RevealTarget, ZedDebugConfig}; use theme::ThemeSettings; use ui::{ ActiveTheme, Button, ButtonCommon, ButtonSize, CheckboxWithLabel, Clickable, Color, Context, ContextMenu, Disableable, DropdownMenu, FluentBuilder, Icon, IconName, IconSize, IconWithIndicator, Indicator, InteractiveElement, IntoElement, Label, LabelCommon as _, ListItem, ListItemSpacing, ParentElement, RenderOnce, SharedString, Styled, StyledExt, StyledTypography, ToggleButton, ToggleState, Toggleable, Tooltip, Window, div, h_flex, px, relative, rems, v_flex, }; use util::ResultExt; use workspace::{ModalView, Workspace, pane}; use crate::{attach_modal::AttachModal, debugger_panel::DebugPanel}; // enum SaveScenarioState { // Saving, // Saved((ProjectPath, SharedString)), // Failed(SharedString), // } pub(super) struct NewProcessModal { workspace: WeakEntity<Workspace>, debug_panel: WeakEntity<DebugPanel>, mode: NewProcessMode, debug_picker: Entity<Picker<DebugDelegate>>, attach_mode: Entity<AttachMode>, launch_mode: Entity<LaunchMode>, task_mode: TaskMode, debugger: Option<DebugAdapterName>, // save_scenario_state: Option<SaveScenarioState>, _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| { tasks_ui::task_contexts(workspace, window, cx) })?; workspace.update_in(cx, |workspace, window, cx| { let workspace_handle = workspace.weak_handle(); workspace.toggle_modal(window, cx, |window, cx| { let attach_mode = AttachMode::new(None, workspace_handle.clone(), window, cx); let launch_picker = cx.new(|cx| { let delegate = DebugDelegate::new(debug_panel.downgrade(), task_store.clone()); Picker::uniform_list(delegate, window, cx).modal(false) }); let configure_mode = LaunchMode::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(&launch_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 launch_picker = launch_picker.downgrade(); let configure_mode = configure_mode.downgrade(); let task_modal = task_mode.task_modal.downgrade(); async move |this, cx| { let task_contexts = task_contexts.await; let task_contexts = Arc::new(task_contexts); launch_picker .update_in(cx, |picker, window, cx| { picker.delegate.task_contexts_loaded( task_contexts.clone(), languages, 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.task_contexts_loaded(task_contexts, window, cx); }) .ok(); this.update(cx, |_, cx| { cx.notify(); }) .ok(); } }) .detach(); Self { debug_picker: launch_picker, attach_mode, launch_mode: configure_mode, task_mode, debugger: None, mode, debug_panel: debug_panel.downgrade(), workspace: workspace_handle, // save_scenario_state: None, _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.launch_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.launch_mode.read(cx).program.focus_handle(cx), NewProcessMode::Debug => self.debug_picker.focus_handle(cx), } } fn debug_scenario(&self, debugger: &str, cx: &App) -> Option<DebugScenario> { let request = match self.mode { NewProcessMode::Launch => Some(DebugRequest::Launch( self.launch_mode.read(cx).debug_request(cx), )), NewProcessMode::Attach => Some(DebugRequest::Attach( self.attach_mode.read(cx).debug_request(), )), _ => None, }?; let label = suggested_label(&request, debugger); let stop_on_entry = if let NewProcessMode::Launch = &self.mode { Some(self.launch_mode.read(cx).stop_on_entry.selected()) } else { None }; let session_scenario = ZedDebugConfig { adapter: debugger.to_owned().into(), label, request: request, stop_on_entry, }; cx.global::<DapRegistry>() .adapter(&session_scenario.adapter) .and_then(|adapter| adapter.config_from_zed_format(session_scenario).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; } // TODO: Restore once we have proper, comment preserving edits // if let NewProcessMode::Launch = &self.mode { // if self.launch_mode.read(cx).save_to_debug_json.selected() { // self.save_debug_scenario(window, cx); // } // } let Some(debugger) = self.debugger.as_ref() else { return; }; let Some(config) = self.debug_scenario(debugger, cx) else { log::error!("debug config not found in mode: {}", self.mode); return; }; let debug_panel = self.debug_panel.clone(); let Some(task_contexts) = self.task_contexts(cx) else { return; }; send_telemetry(&config, TelemetrySpawnLocation::Custom, cx); let task_context = task_contexts.active_context().cloned().unwrap_or_default(); let worktree_id = task_contexts.worktree(); cx.spawn_in(window, async move |this, cx| { debug_panel.update_in(cx, |debug_panel, window, 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| { this.delegate.definition.adapter = adapter.0.clone(); this.focus(window, cx); }) }); } cx.notify(); }) } fn task_contexts(&self, cx: &App) -> Option<Arc<TaskContexts>> { self.debug_picker.read(cx).delegate.task_contexts.clone() } // fn save_debug_scenario(&mut self, window: &mut Window, cx: &mut Context<Self>) { // let Some((save_scenario, scenario_label)) = self // .debugger // .as_ref() // .and_then(|debugger| self.debug_scenario(&debugger, cx)) // .zip(self.task_contexts(cx).and_then(|tcx| tcx.worktree())) // .and_then(|(scenario, worktree_id)| { // self.debug_panel // .update(cx, |panel, cx| { // panel.save_scenario(&scenario, worktree_id, window, cx) // }) // .ok() // .zip(Some(scenario.label.clone())) // }) // else { // return; // }; // self.save_scenario_state = Some(SaveScenarioState::Saving); // cx.spawn(async move |this, cx| { // let res = save_scenario.await; // this.update(cx, |this, _| match res { // Ok(saved_file) => { // this.save_scenario_state = // Some(SaveScenarioState::Saved((saved_file, scenario_label))) // } // Err(error) => { // this.save_scenario_state = // Some(SaveScenarioState::Failed(error.to_string().into())) // } // }) // .ok(); // cx.background_executor().timer(Duration::from_secs(3)).await; // this.update(cx, |this, _| this.save_scenario_state.take()) // .ok(); // }) // .detach(); // } fn adapter_drop_down_menu( &mut self, window: &mut Window, cx: &mut Context<Self>, ) -> ui::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 = 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", label, ContextMenu::build(window, cx, move |mut menu, _, _| { let setter_for_name = |name: DebugAdapterName| { let weak = weak.clone(); move |window: &mut Window, cx: &mut App| { weak.update(cx, |this, cx| { this.debugger = Some(name.clone()); cx.notify(); if let NewProcessMode::Attach = &this.mode { Self::update_attach_picker(&this.attach_mode, &name, window, cx); } }) .ok(); } }; for adapter in available_adapters.into_iter() { menu = menu.entry(adapter.0.clone(), None, setter_for_name(adapter.clone())); } menu }), ) } fn open_debug_json(&self, window: &mut Window, cx: &mut Context<NewProcessModal>) { let this = cx.entity(); window .spawn(cx, async move |cx| { let worktree_id = this.update(cx, |this, cx| { let tcx = this.task_contexts(cx); tcx?.worktree() })?; let Some(worktree_id) = worktree_id else { let _ = cx.prompt( PromptLevel::Critical, "Cannot open debug.json", Some("You must have at least one project open"), &[PromptButton::ok("Ok")], ); return Ok(()); }; let editor = this .update_in(cx, |this, window, cx| { this.workspace.update(cx, |workspace, cx| { workspace.open_path( ProjectPath { worktree_id, path: local_debug_file_relative_path().into(), }, None, true, window, cx, ) }) })?? .await?; cx.update(|_window, cx| { if let Some(editor) = editor.act_as::<Editor>(cx) { editor.update(cx, |editor, cx| { editor.buffer().update(cx, |buffer, cx| { if let Some(singleton) = buffer.as_singleton() { singleton.update(cx, |buffer, cx| { if buffer.is_empty() { buffer.edit( [(0..0, initial_local_debug_tasks_content())], None, cx, ); } }) }