1979 lines · 69.8 KB · Rust
RawDownload
pub(crate) mod breakpoint_list;pub(crate) mod console;pub(crate) mod loaded_source_list;pub(crate) mod memory_view;pub(crate) mod module_list;pub mod stack_frame_list;pub mod variable_list;use std::{    any::Any,    path::PathBuf,    sync::{Arc, LazyLock},    time::Duration,}; use crate::{    ToggleExpandItem,    attach_modal::{AttachModal, ModalIntent},    new_process_modal::resolve_path,    persistence::{self, DebuggerPaneItem, SerializedLayout},    session::running::memory_view::MemoryView,}; use anyhow::{Context as _, Result, anyhow, bail};use breakpoint_list::BreakpointList;use collections::{HashMap, IndexMap};use console::Console;use dap::{    Capabilities, DapRegistry, RunInTerminalRequestArguments, Thread,    adapters::{DebugAdapterName, DebugTaskDefinition},    client::SessionId,    debugger_settings::DebuggerSettings,};use futures::{SinkExt, channel::mpsc};use gpui::{    Action as _, AnyView, AppContext, Axis, Entity, EntityId, EventEmitter, FocusHandle, Focusable,    NoAction, Pixels, Point, Subscription, Task, WeakEntity,};use language::Buffer;use loaded_source_list::LoadedSourceList;use module_list::ModuleList;use project::{    DebugScenarioContext, Project, WorktreeId,    debugger::session::{self, Session, SessionEvent, SessionStateEvent, ThreadId, ThreadStatus},};use rpc::proto::ViewId;use serde_json::Value;use settings::Settings;use stack_frame_list::StackFrameList;use task::{    BuildTaskDefinition, DebugScenario, SharedTaskContext, Shell, ShellBuilder, SpawnInTerminal,    TaskContext, ZedDebugConfig, substitute_variables_in_str,};use terminal_view::TerminalView;use ui::{    FluentBuilder, IntoElement, Render, StatefulInteractiveElement, Tab, Tooltip, VisibleOnHover,    VisualContext, prelude::*,};use util::ResultExt;use variable_list::VariableList;use workspace::{    ActivePaneDecorator, DraggedTab, Item, ItemHandle, Member, Pane, PaneGroup, SplitDirection,    Workspace, item::TabContentParams, move_item, pane::Event,}; static PROCESS_ID_PLACEHOLDER: LazyLock<String> =    LazyLock::new(|| task::VariableName::PickProcessId.template_value()); pub struct RunningState {    session: Entity<Session>,    thread_id: Option<ThreadId>,    focus_handle: FocusHandle,    _remote_id: Option<ViewId>,    workspace: WeakEntity<Workspace>,    project: WeakEntity<Project>,    session_id: SessionId,    variable_list: Entity<variable_list::VariableList>,    _subscriptions: Vec<Subscription>,    stack_frame_list: Entity<stack_frame_list::StackFrameList>,    loaded_sources_list: Entity<LoadedSourceList>,    pub debug_terminal: Entity<DebugTerminal>,    module_list: Entity<module_list::ModuleList>,    console: Entity<Console>,    breakpoint_list: Entity<BreakpointList>,    panes: PaneGroup,    active_pane: Entity<Pane>,    pane_close_subscriptions: HashMap<EntityId, Subscription>,    dock_axis: Axis,    _schedule_serialize: Option<Task<()>>,    pub(crate) scenario: Option<DebugScenario>,    pub(crate) scenario_context: Option<DebugScenarioContext>,    memory_view: Entity<MemoryView>,} impl RunningState {    pub(crate) fn thread_id(&self) -> Option<ThreadId> {        self.thread_id    }     pub(crate) fn active_pane(&self) -> &Entity<Pane> {        &self.active_pane    }} impl Render for RunningState {    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {        let zoomed_pane = self            .panes            .panes()            .into_iter()            .find(|pane| pane.read(cx).is_zoomed());         let active = self.panes.panes().into_iter().next();        let pane = if let Some(zoomed_pane) = zoomed_pane {            zoomed_pane.update(cx, |pane, cx| pane.render(window, cx).into_any_element())        } else if let Some(active) = active {            self.panes                .render(                    None,                    &ActivePaneDecorator::new(active, &self.workspace),                    window,                    cx,                )                .into_any_element()        } else {            div().into_any_element()        };        let thread_status = self            .thread_id            .map(|thread_id| self.session.read(cx).thread_status(thread_id))            .unwrap_or(ThreadStatus::Exited);         self.variable_list.update(cx, |this, cx| {            this.disabled(thread_status != ThreadStatus::Stopped, cx);        });        v_flex()            .size_full()            .key_context("DebugSessionItem")            .track_focus(&self.focus_handle(cx))            .child(h_flex().flex_1().child(pane))    }} pub(crate) struct SubView {    inner: AnyView,    item_focus_handle: FocusHandle,    kind: DebuggerPaneItem,    running_state: WeakEntity<RunningState>,    host_pane: WeakEntity<Pane>,    show_indicator: Box<dyn Fn(&App) -> bool>,    actions: Option<Box<dyn FnMut(&mut Window, &mut App) -> AnyElement>>,    hovered: bool,} impl SubView {    pub(crate) fn new(        item_focus_handle: FocusHandle,        view: AnyView,        kind: DebuggerPaneItem,        running_state: WeakEntity<RunningState>,        host_pane: WeakEntity<Pane>,        cx: &mut App,    ) -> Entity<Self> {        cx.new(|_| Self {            kind,            inner: view,            item_focus_handle,            running_state,            host_pane,            show_indicator: Box::new(|_| false),            actions: None,            hovered: false,        })    }     pub(crate) fn stack_frame_list(        stack_frame_list: Entity<StackFrameList>,        running_state: WeakEntity<RunningState>,        host_pane: WeakEntity<Pane>,        cx: &mut App,    ) -> Entity<Self> {        let weak_list = stack_frame_list.downgrade();        let this = Self::new(            stack_frame_list.focus_handle(cx),            stack_frame_list.into(),            DebuggerPaneItem::Frames,            running_state,            host_pane,            cx,        );         this.update(cx, |this, _| {            this.with_actions(Box::new(move |_, cx| {                weak_list                    .update(cx, |this, _| this.render_control_strip())                    .unwrap_or_else(|_| div().into_any_element())            }));        });         this    }     pub(crate) fn console(        console: Entity<Console>,        running_state: WeakEntity<RunningState>,        host_pane: WeakEntity<Pane>,        cx: &mut App,    ) -> Entity<Self> {        let weak_console = console.downgrade();        let this = Self::new(            console.focus_handle(cx),            console.into(),            DebuggerPaneItem::Console,            running_state,            host_pane,            cx,        );        this.update(cx, |this, _| {            this.with_indicator(Box::new(move |cx| {                weak_console                    .read_with(cx, |console, cx| console.show_indicator(cx))                    .unwrap_or_default()            }))        });        this    }     pub(crate) fn breakpoint_list(        list: Entity<BreakpointList>,        running_state: WeakEntity<RunningState>,        host_pane: WeakEntity<Pane>,        cx: &mut App,    ) -> Entity<Self> {        let weak_list = list.downgrade();        let focus_handle = list.focus_handle(cx);        let this = Self::new(            focus_handle,            list.into(),            DebuggerPaneItem::BreakpointList,            running_state,            host_pane,            cx,        );         this.update(cx, |this, _| {            this.with_actions(Box::new(move |_, cx| {                weak_list                    .update(cx, |this, _| this.render_control_strip())                    .unwrap_or_else(|_| div().into_any_element())            }));        });        this    }     pub(crate) fn view_kind(&self) -> DebuggerPaneItem {        self.kind    }    pub(crate) fn with_indicator(&mut self, indicator: Box<dyn Fn(&App) -> bool>) {        self.show_indicator = indicator;    }    pub(crate) fn with_actions(        &mut self,        actions: Box<dyn FnMut(&mut Window, &mut App) -> AnyElement>,    ) {        self.actions = Some(actions);    }     fn set_host_pane(&mut self, host_pane: WeakEntity<Pane>) {        self.host_pane = host_pane;    }}impl Focusable for SubView {    fn focus_handle(&self, _: &App) -> FocusHandle {        self.item_focus_handle.clone()    }}impl EventEmitter<()> for SubView {}impl Item for SubView {    type Event = ();     /// This is used to serialize debugger pane layouts    /// A SharedString gets converted to a enum and back during serialization/deserialization.    fn tab_content_text(&self, _detail: usize, _cx: &App) -> SharedString {        self.kind.to_shared_string()    }     fn tab_tooltip_text(&self, _: &App) -> Option<SharedString> {        Some(self.kind.tab_tooltip())    }     fn tab_content(        &self,        params: workspace::item::TabContentParams,        _: &Window,        cx: &App,    ) -> AnyElement {        let label = Label::new(self.kind.to_shared_string())            .size(ui::LabelSize::Small)            .color(params.text_color())            .line_height_style(ui::LineHeightStyle::UiLabel);         if !params.selected && self.show_indicator.as_ref()(cx) {            return h_flex()                .justify_between()                .child(ui::Indicator::dot())                .gap_2()                .child(label)                .into_any_element();        }         label.into_any_element()    }     fn handle_drop(        &self,        active_pane: &Pane,        dropped: &dyn Any,        window: &mut Window,        cx: &mut App,    ) -> bool {        let Some(tab) = dropped.downcast_ref::<DraggedTab>() else {            return true;        };        let Some(this_pane) = self.host_pane.upgrade() else {            return true;        };        let item = if tab.pane == this_pane {            active_pane.item_for_index(tab.ix)        } else {            tab.pane.read(cx).item_for_index(tab.ix)        };        let Some(item) = item.filter(|item| item.downcast::<SubView>().is_some()) else {            return true;        };        let Some(split_direction) = active_pane.drag_split_direction() else {            return false;        };         let source = tab.pane.clone();        let item_id_to_move = item.item_id();        let weak_running = self.running_state.clone();         // Source pane may be the one currently updated, so defer the move.        window.defer(cx, move |window, cx| {            let new_pane = weak_running.update(cx, |running, cx| {                let Some(project) = running.project.upgrade() else {                    return Err(anyhow!("Debugger project has been dropped"));                };                 let new_pane = new_debugger_pane(running.workspace.clone(), project, window, cx);                let _previous_subscription = running.pane_close_subscriptions.insert(                    new_pane.entity_id(),                    cx.subscribe_in(&new_pane, window, RunningState::handle_pane_event),                );                debug_assert!(_previous_subscription.is_none());                running                    .panes                    .split(&this_pane, &new_pane, split_direction, cx);                anyhow::Ok(new_pane)            });             match new_pane.and_then(|result| result) {                Ok(new_pane) => {                    move_item(                        &source,                        &new_pane,                        item_id_to_move,                        new_pane.read(cx).active_item_index(),                        true,                        window,                        cx,                    );                }                Err(err) => {                    log::error!("{err:?}");                }            }        });         true    }} impl Render for SubView {    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {        v_flex()            .id(format!(                "subview-container-{}",                self.kind.to_shared_string()            ))            .on_hover(cx.listener(|this, hovered, _, cx| {                this.hovered = *hovered;                cx.notify();            }))            .size_full()            // Add border unconditionally to prevent layout shifts on focus changes.            .border_1()            .when(self.item_focus_handle.contains_focused(window, cx), |el| {                el.border_color(cx.theme().colors().pane_focused_border)            })            .child(self.inner.clone())    }} pub(crate) fn new_debugger_pane(    workspace: WeakEntity<Workspace>,    project: Entity<Project>,    window: &mut Window,    cx: &mut Context<RunningState>,) -> Entity<Pane> {    let weak_running = cx.weak_entity();     cx.new(move |cx| {        let can_drop_predicate: Arc<dyn Fn(&dyn Any, &mut Window, &mut App) -> bool> =            Arc::new(|any, _window, _cx| {                any.downcast_ref::<DraggedTab>()                    .is_some_and(|dragged_tab| dragged_tab.item.downcast::<SubView>().is_some())            });        let mut pane = Pane::new(            workspace.clone(),            project.clone(),            Default::default(),            Some(can_drop_predicate),            NoAction.boxed_clone(),            true,            window,            cx,        );        let focus_handle = pane.focus_handle(cx);        pane.set_can_split(Some(Arc::new({            let weak_running = weak_running.clone();            move |pane, dragged_item, _window, cx| {                if let Some(tab) = dragged_item.downcast_ref::<DraggedTab>() {                    let is_current_pane = tab.pane == cx.entity();                    let Some(can_drag_away) = weak_running                        .read_with(cx, |running_state, _| {                            let current_panes = running_state.panes.panes();                            !current_panes.contains(&&tab.pane)                                || current_panes.len() > 1                                || (!is_current_pane || pane.items_len() > 1)                        })                        .ok()                    else {                        return false;                    };                    if can_drag_away {                        let item = if is_current_pane {                            pane.item_for_index(tab.ix)                        } else {                            tab.pane.read(cx).item_for_index(tab.ix)                        };                        if let Some(item) = item {                            return item.downcast::<SubView>().is_some();                        }                    }                }                false            }        })));        pane.set_can_toggle_zoom(false, cx);        pane.display_nav_history_buttons(None);        pane.set_should_display_tab_bar(|_, _| true);        pane.set_render_tab_bar_buttons(cx, |_, _, _| (None, None));        pane.set_render_tab_bar(cx, {            move |pane, window, cx| {                let active_pane_item = pane.active_item();                let pane_group_id: SharedString =                    format!("pane-zoom-button-hover-{}", cx.entity_id()).into();                let as_subview = active_pane_item                    .as_ref()                    .and_then(|item| item.downcast::<SubView>());                let is_hovered = as_subview                    .as_ref()                    .is_some_and(|item| item.read(cx).hovered);                 h_flex()                    .track_focus(&focus_handle)                    .group(pane_group_id.clone())                    .pl_1p5()                    .pr_1()                    .justify_between()                    .border_b_1()                    .border_color(cx.theme().colors().border)                    .bg(cx.theme().colors().tab_bar_background)                    .on_action(|_: &menu::Cancel, window, cx| {                        if cx.stop_active_drag(window) {                        } else {                            cx.propagate();                        }                    })                    .child(                        h_flex()                            .w_full()                            .gap_1()                            .h(Tab::container_height(cx))                            .drag_over::<DraggedTab>(|bar, _, _, cx| {                                bar.bg(cx.theme().colors().drop_target_background)                            })                            .on_drop(cx.listener(                                move |this, dragged_tab: &DraggedTab, window, cx| {                                    if dragged_tab.item.downcast::<SubView>().is_none() {

Lines 1–500 of 1979.