2123 lines · 71.9 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, TaskExt, 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,                    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;        };        if tab.item.downcast::<SubView>().is_none() {            return true;        }        let Some(split_direction) = active_pane.drag_split_direction() else {            return false;        };         let source = tab.pane.clone();        let item_id_to_move = tab.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()            ))            .size_full()            .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())            .on_hover(cx.listener(|this, hovered, _, cx| {                this.hovered = *hovered;                cx.notify();            }))    }} struct DraggedTabPreview {    label: SharedString,} impl Render for DraggedTabPreview {    fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {        let ui_font = theme_settings::ThemeSettings::get_global(cx)            .ui_font            .clone();        let colors = cx.theme().colors();         h_flex()            .font(ui_font)            .h_6()            .px_1()            .rounded_sm()            .shadow_md()            .border_1()            .border_color(colors.border)            .bg(colors.elevated_surface_background)            .child(Label::new(self.label.clone()).size(LabelSize::Small))    }} fn render_debugger_tab(    ix: usize,    item: &dyn ItemHandle,    selected: bool,    deemphasized: bool,    window: &mut Window,    cx: &mut Context<Pane>,) -> impl IntoElement + use<> {    let item_ = item.boxed_clone();    let colors = cx.theme().colors();     div()        .border_l_2()        .border_color(gpui::transparent_black())        .drag_over::<DraggedTab>(|wrapper, _, _, cx| wrapper.border_color(cx.theme().colors().text))        .child(            div()                .cursor_pointer()                .id(format!("debugger_tab_{}", item.item_id().as_u64()))                .p_1()                .rounded_sm()                .map(|s| {                    if selected {                        s.bg(colors.text_accent.opacity(0.08))                            .hover(|s| s.bg(colors.text_accent.opacity(0.25)))                            .text_color(colors.text_accent)                    } else {                        s.hover(|s| s.bg(colors.element_hover))                    }                })                .when(deemphasized, |s| s.opacity(0.8))                .child(item.tab_content(                    TabContentParams {                        selected,                        deemphasized,                        ..Default::default()                    },                    window,                    cx,                ))                .when_some(item.tab_tooltip_text(cx), |this, tooltip| {                    this.tooltip(Tooltip::text(tooltip))                })                .on_click(cx.listener(move |this, _, window, cx| {                    let index = this.index_for_item(&*item_);                    if let Some(index) = index {                        this.activate_item(index, true, true, window, cx);                    }                }))                .on_drop(                    cx.listener(move |this, dragged_tab: &DraggedTab, window, cx| {                        if dragged_tab.item.downcast::<SubView>().is_none() {                            return;                        }                        this.drag_split_direction = None;                        this.handle_tab_drop(dragged_tab, ix, false, window, cx)                    }),                )                .on_drag(                    DraggedTab {                        item: item.boxed_clone(),                        pane: cx.entity(),                        detail: 0,                        is_active: selected,                        ix,                    },                    |tab, _, _, cx| {                        let label = tab.item.tab_content_text(0, cx);                        cx.new(|_| DraggedTabPreview { label })                    },                ),        )} fn render_debugger_tab_bar(    pane: &mut Pane,    focus_handle: &FocusHandle,    window: &mut Window,

Lines 1–500 of 2123.