1472 lines · 58.1 KB · Rust
RawDownload
use crate::persistence::DebuggerPaneItem;use crate::session::DebugSession;use crate::session::running::RunningState;use crate::{    ClearAllBreakpoints, Continue, Detach, FocusBreakpointList, FocusConsole, FocusFrames,    FocusLoadedSources, FocusModules, FocusTerminal, FocusVariables, NewProcessModal,    NewProcessMode, Pause, Restart, ShowStackTrace, StepBack, StepInto, StepOut, StepOver, Stop,    ToggleExpandItem, ToggleIgnoreBreakpoints, ToggleSessionPicker, ToggleThreadPicker,    persistence, spawn_task_or_modal,};use anyhow::Result;use command_palette_hooks::CommandPaletteFilter;use dap::StartDebuggingRequestArguments;use dap::adapters::DebugAdapterName;use dap::debugger_settings::DebugPanelDockPosition;use dap::{    ContinuedEvent, LoadedSourceEvent, ModuleEvent, OutputEvent, StoppedEvent, ThreadEvent,    client::SessionId, debugger_settings::DebuggerSettings,};use gpui::{    Action, App, AsyncWindowContext, Context, DismissEvent, Entity, EntityId, EventEmitter,    FocusHandle, Focusable, MouseButton, MouseDownEvent, Point, Subscription, Task, WeakEntity,    actions, anchored, deferred,}; use language::Buffer;use project::debugger::session::{Session, SessionStateEvent};use project::{Fs, WorktreeId};use project::{Project, debugger::session::ThreadStatus};use rpc::proto::{self};use settings::Settings;use std::any::TypeId;use std::sync::Arc;use task::{DebugScenario, TaskContext};use ui::{ContextMenu, Divider, PopoverMenuHandle, Tooltip, prelude::*};use workspace::SplitDirection;use workspace::{    Pane, Workspace,    dock::{DockPosition, Panel, PanelEvent},}; pub enum DebugPanelEvent {    Exited(SessionId),    Terminated(SessionId),    Stopped {        client_id: SessionId,        event: StoppedEvent,        go_to_stack_frame: bool,    },    Thread((SessionId, ThreadEvent)),    Continued((SessionId, ContinuedEvent)),    Output((SessionId, OutputEvent)),    Module((SessionId, ModuleEvent)),    LoadedSource((SessionId, LoadedSourceEvent)),    ClientShutdown(SessionId),    CapabilitiesChanged(SessionId),} actions!(debug_panel, [ToggleFocus]); pub struct DebugPanel {    size: Pixels,    sessions: Vec<Entity<DebugSession>>,    active_session: Option<Entity<DebugSession>>,    project: Entity<Project>,    workspace: WeakEntity<Workspace>,    focus_handle: FocusHandle,    context_menu: Option<(Entity<ContextMenu>, Point<Pixels>, Subscription)>,    debug_scenario_scheduled_last: bool,    pub(crate) thread_picker_menu_handle: PopoverMenuHandle<ContextMenu>,    pub(crate) session_picker_menu_handle: PopoverMenuHandle<ContextMenu>,    fs: Arc<dyn Fs>,    is_zoomed: bool,    _subscriptions: [Subscription; 1],} impl DebugPanel {    pub fn new(        workspace: &Workspace,        window: &mut Window,        cx: &mut Context<Workspace>,    ) -> Entity<Self> {        cx.new(|cx| {            let project = workspace.project().clone();            let focus_handle = cx.focus_handle();            let thread_picker_menu_handle = PopoverMenuHandle::default();            let session_picker_menu_handle = PopoverMenuHandle::default();             let focus_subscription = cx.on_focus(                &focus_handle,                window,                |this: &mut DebugPanel, window, cx| {                    this.focus_active_item(window, cx);                },            );             Self {                size: px(300.),                sessions: vec![],                active_session: None,                focus_handle,                project,                workspace: workspace.weak_handle(),                context_menu: None,                fs: workspace.app_state().fs.clone(),                thread_picker_menu_handle,                session_picker_menu_handle,                is_zoomed: false,                _subscriptions: [focus_subscription],                debug_scenario_scheduled_last: true,            }        })    }     pub(crate) fn focus_active_item(&mut self, window: &mut Window, cx: &mut Context<Self>) {        let Some(session) = self.active_session.clone() else {            return;        };        let active_pane = session            .read(cx)            .running_state()            .read(cx)            .active_pane()            .clone();        active_pane.update(cx, |pane, cx| {            pane.focus_active_item(window, cx);        });    }     pub(crate) fn sessions(&self) -> Vec<Entity<DebugSession>> {        self.sessions.clone()    }     pub fn active_session(&self) -> Option<Entity<DebugSession>> {        self.active_session.clone()    }     pub(crate) fn running_state(&self, cx: &mut App) -> Option<Entity<RunningState>> {        self.active_session()            .map(|session| session.read(cx).running_state().clone())    }     pub(crate) fn filter_action_types(&self, cx: &mut App) {        let (has_active_session, supports_restart, support_step_back, status) = self            .active_session()            .map(|item| {                let running = item.read(cx).running_state().clone();                let caps = running.read(cx).capabilities(cx);                (                    !running.read(cx).session().read(cx).is_terminated(),                    caps.supports_restart_request.unwrap_or_default(),                    caps.supports_step_back.unwrap_or_default(),                    running.read(cx).thread_status(cx),                )            })            .unwrap_or((false, false, false, None));         let filter = CommandPaletteFilter::global_mut(cx);        let debugger_action_types = [            TypeId::of::<Detach>(),            TypeId::of::<Stop>(),            TypeId::of::<ToggleIgnoreBreakpoints>(),        ];         let running_action_types = [TypeId::of::<Pause>()];         let stopped_action_type = [            TypeId::of::<Continue>(),            TypeId::of::<StepOver>(),            TypeId::of::<StepInto>(),            TypeId::of::<StepOut>(),            TypeId::of::<ShowStackTrace>(),            TypeId::of::<editor::actions::DebuggerRunToCursor>(),            TypeId::of::<editor::actions::DebuggerEvaluateSelectedText>(),        ];         let step_back_action_type = [TypeId::of::<StepBack>()];        let restart_action_type = [TypeId::of::<Restart>()];         if has_active_session {            filter.show_action_types(debugger_action_types.iter());             if supports_restart {                filter.show_action_types(restart_action_type.iter());            } else {                filter.hide_action_types(&restart_action_type);            }             if support_step_back {                filter.show_action_types(step_back_action_type.iter());            } else {                filter.hide_action_types(&step_back_action_type);            }             match status {                Some(ThreadStatus::Running) => {                    filter.show_action_types(running_action_types.iter());                    filter.hide_action_types(&stopped_action_type);                }                Some(ThreadStatus::Stopped) => {                    filter.show_action_types(stopped_action_type.iter());                    filter.hide_action_types(&running_action_types);                }                _ => {                    filter.hide_action_types(&running_action_types);                    filter.hide_action_types(&stopped_action_type);                }            }        } else {            // show only the `debug: start`            filter.hide_action_types(&debugger_action_types);            filter.hide_action_types(&step_back_action_type);            filter.hide_action_types(&restart_action_type);            filter.hide_action_types(&running_action_types);            filter.hide_action_types(&stopped_action_type);        }    }     pub fn load(        workspace: WeakEntity<Workspace>,        cx: &mut AsyncWindowContext,    ) -> Task<Result<Entity<Self>>> {        cx.spawn(async move |cx| {            workspace.update_in(cx, |workspace, window, cx| {                let debug_panel = DebugPanel::new(workspace, window, cx);                 workspace.register_action(|workspace, _: &ClearAllBreakpoints, _, cx| {                    workspace.project().read(cx).breakpoint_store().update(                        cx,                        |breakpoint_store, cx| {                            breakpoint_store.clear_breakpoints(cx);                        },                    )                });                 cx.observe_new::<DebugPanel>(|debug_panel, _, cx| {                    Self::filter_action_types(debug_panel, cx);                })                .detach();                 cx.observe(&debug_panel, |_, debug_panel, cx| {                    debug_panel.update(cx, |debug_panel, cx| {                        Self::filter_action_types(debug_panel, cx);                    });                })                .detach();                workspace.set_debugger_provider(DebuggerProvider(debug_panel.clone()));                 debug_panel            })        })    }     pub fn start_session(        &mut self,        scenario: DebugScenario,        task_context: TaskContext,        active_buffer: Option<Entity<Buffer>>,        worktree_id: Option<WorktreeId>,        window: &mut Window,        cx: &mut Context<Self>,    ) {        let dap_store = self.project.read(cx).dap_store();        let session = dap_store.update(cx, |dap_store, cx| {            dap_store.new_session(                scenario.label.clone(),                DebugAdapterName(scenario.adapter.clone()),                None,                cx,            )        });        self.debug_scenario_scheduled_last = true;        if let Some(inventory) = self            .project            .read(cx)            .task_store()            .read(cx)            .task_inventory()            .cloned()        {            inventory.update(cx, |inventory, _| {                inventory.scenario_scheduled(scenario.clone());            })        }        let task = cx.spawn_in(window, {            let session = session.clone();            async move |this, cx| {                let debug_session =                    Self::register_session(this.clone(), session.clone(), true, cx).await?;                let definition = debug_session                    .update_in(cx, |debug_session, window, cx| {                        debug_session.running_state().update(cx, |running, cx| {                            running.resolve_scenario(                                scenario,                                task_context,                                active_buffer,                                worktree_id,                                window,                                cx,                            )                        })                    })?                    .await?;                dap_store                    .update(cx, |dap_store, cx| {                        dap_store.boot_session(session.clone(), definition, cx)                    })?                    .await            }        });         cx.spawn(async move |_, cx| {            if let Err(error) = task.await {                log::error!("{error}");                session                    .update(cx, |session, cx| {                        session                            .console_output(cx)                            .unbounded_send(format!("error: {}", error))                            .ok();                        session.shutdown(cx)                    })?                    .await;            }            anyhow::Ok(())        })        .detach_and_log_err(cx);    }     pub(crate) fn rerun_last_session(        &mut self,        workspace: &mut Workspace,        window: &mut Window,        cx: &mut Context<Self>,    ) {        let task_store = workspace.project().read(cx).task_store().clone();        let Some(task_inventory) = task_store.read(cx).task_inventory() else {            return;        };        let workspace = self.workspace.clone();        let Some(scenario) = task_inventory.read(cx).last_scheduled_scenario().cloned() else {            window.defer(cx, move |window, cx| {                workspace                    .update(cx, |workspace, cx| {                        NewProcessModal::show(workspace, window, NewProcessMode::Launch, None, cx);                    })                    .ok();            });            return;        };         cx.spawn_in(window, async move |this, cx| {            let task_contexts = workspace                .update_in(cx, |workspace, window, cx| {                    tasks_ui::task_contexts(workspace, window, cx)                })?                .await;             let task_context = task_contexts.active_context().cloned().unwrap_or_default();            let worktree_id = task_contexts.worktree();             this.update_in(cx, |this, window, cx| {                this.start_session(                    scenario.clone(),                    task_context,                    None,                    worktree_id,                    window,                    cx,                );            })        })        .detach();    }     pub(crate) async fn register_session(        this: WeakEntity<Self>,        session: Entity<Session>,        focus: bool,        cx: &mut AsyncWindowContext,    ) -> Result<Entity<DebugSession>> {        let debug_session = register_session_inner(&this, session, cx).await?;         let workspace = this.update_in(cx, |this, window, cx| {            if focus {                this.activate_session(debug_session.clone(), window, cx);            }             this.workspace.clone()        })?;        workspace.update_in(cx, |workspace, window, cx| {            workspace.focus_panel::<Self>(window, cx);        })?;        Ok(debug_session)    }     pub(crate) fn handle_restart_request(        &mut self,        mut curr_session: Entity<Session>,        window: &mut Window,        cx: &mut Context<Self>,    ) {        while let Some(parent_session) = curr_session.read(cx).parent_session().cloned() {            curr_session = parent_session;        }         let Some(worktree) = curr_session.read(cx).worktree() else {            log::error!("Attempted to restart a non-running session");            return;        };         let dap_store_handle = self.project.read(cx).dap_store().clone();        let label = curr_session.read(cx).label().clone();        let adapter = curr_session.read(cx).adapter().clone();        let binary = curr_session.read(cx).binary().clone();        let task = curr_session.update(cx, |session, cx| session.shutdown(cx));         cx.spawn_in(window, async move |this, cx| {            task.await;             let (session, task) = dap_store_handle.update(cx, |dap_store, cx| {                let session = dap_store.new_session(label, adapter, None, cx);                 let task = session.update(cx, |session, cx| {                    session.boot(binary, worktree, dap_store_handle.downgrade(), cx)                });                (session, task)            })?;            Self::register_session(this.clone(), session, true, cx).await?;            task.await        })        .detach_and_log_err(cx);    }     pub fn handle_start_debugging_request(        &mut self,        request: &StartDebuggingRequestArguments,        parent_session: Entity<Session>,        window: &mut Window,        cx: &mut Context<Self>,    ) {        let Some(worktree) = parent_session.read(cx).worktree() else {            log::error!("Attempted to start a child-session from a non-running session");            return;        };         let dap_store_handle = self.project.read(cx).dap_store().clone();        let mut label = parent_session.read(cx).label().clone();        if !label.ends_with("(child)") {            label = format!("{label} (child)").into();        }        let adapter = parent_session.read(cx).adapter().clone();        let mut binary = parent_session.read(cx).binary().clone();        binary.request_args = request.clone();        cx.spawn_in(window, async move |this, cx| {            let (session, task) = dap_store_handle.update(cx, |dap_store, cx| {                let session =                    dap_store.new_session(label, adapter, Some(parent_session.clone()), cx);                 let task = session.update(cx, |session, cx| {                    session.boot(binary, worktree, dap_store_handle.downgrade(), cx)                });                (session, task)            })?;            Self::register_session(this, session, false, cx).await?;            task.await        })        .detach_and_log_err(cx);    }     pub(crate) fn close_session(        &mut self,        entity_id: EntityId,        window: &mut Window,        cx: &mut Context<Self>,    ) {        let Some(session) = self            .sessions            .iter()            .find(|other| entity_id == other.entity_id())            .cloned()        else {            return;        };        session.update(cx, |this, cx| {            this.running_state().update(cx, |this, cx| {                this.serialize_layout(window, cx);            });        });        let session_id = session.update(cx, |this, cx| this.session_id(cx));        let should_prompt = self            .project            .update(cx, |this, cx| {                let session = this.dap_store().read(cx).session_by_id(session_id);                session.map(|session| !session.read(cx).is_terminated())            })            .unwrap_or_default();         cx.spawn_in(window, async move |this, cx| {            if should_prompt {

Lines 1–500 of 1472.