1077 lines · 36.2 KB · Rust
RawDownload
pub(crate) mod breakpoint_list;pub(crate) mod console;pub(crate) mod loaded_source_list;pub(crate) mod module_list;pub mod stack_frame_list;pub mod variable_list; use std::{any::Any, ops::ControlFlow, sync::Arc, time::Duration}; use crate::persistence::{self, DebuggerPaneItem, SerializedPaneLayout}; use super::DebugPanelItemEvent;use breakpoint_list::BreakpointList;use collections::{HashMap, IndexMap};use console::Console;use dap::{Capabilities, Thread, client::SessionId, debugger_settings::DebuggerSettings};use gpui::{    Action as _, AnyView, AppContext, Entity, EntityId, EventEmitter, FocusHandle, Focusable,    NoAction, Pixels, Point, Subscription, Task, WeakEntity,};use loaded_source_list::LoadedSourceList;use module_list::ModuleList;use project::{    Project,    debugger::session::{Session, SessionEvent, ThreadId, ThreadStatus},};use rpc::proto::ViewId;use settings::Settings;use stack_frame_list::StackFrameList;use ui::{    ActiveTheme, AnyElement, App, Context, ContextMenu, DropdownMenu, FluentBuilder,    InteractiveElement, IntoElement, Label, LabelCommon as _, ParentElement, Render, SharedString,    StatefulInteractiveElement, Styled, Tab, Window, div, h_flex, v_flex,};use util::ResultExt;use variable_list::VariableList;use workspace::{    ActivePaneDecorator, DraggedTab, Item, Member, Pane, PaneGroup, Workspace,    item::TabContentParams, move_item, pane::Event,}; pub struct RunningState {    session: Entity<Session>,    thread_id: Option<ThreadId>,    focus_handle: FocusHandle,    _remote_id: Option<ViewId>,    workspace: WeakEntity<Workspace>,    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>,    module_list: Entity<module_list::ModuleList>,    _console: Entity<Console>,    breakpoint_list: Entity<BreakpointList>,    panes: PaneGroup,    pane_close_subscriptions: HashMap<EntityId, Subscription>,    _schedule_serialize: Option<Task<()>>,} impl Render for RunningState {    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {        let active = self.panes.panes().into_iter().next();        let x = 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(x))    }} pub(crate) struct SubView {    inner: AnyView,    pane_focus_handle: FocusHandle,    kind: DebuggerPaneItem,    show_indicator: Box<dyn Fn(&App) -> bool>,} impl SubView {    pub(crate) fn new(        pane_focus_handle: FocusHandle,        view: AnyView,        kind: DebuggerPaneItem,        show_indicator: Option<Box<dyn Fn(&App) -> bool>>,        cx: &mut App,    ) -> Entity<Self> {        cx.new(|_| Self {            kind,            inner: view,            pane_focus_handle,            show_indicator: show_indicator.unwrap_or(Box::new(|_| false)),        })    }     pub(crate) fn view_kind(&self) -> DebuggerPaneItem {        self.kind    }}impl Focusable for SubView {    fn focus_handle(&self, _: &App) -> FocusHandle {        self.pane_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, _window: &Window, _cx: &App) -> Option<SharedString> {        Some(self.kind.to_shared_string())    }     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()    }} impl Render for SubView {    fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {        v_flex().size_full().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();    let custom_drop_handle = {        let workspace = workspace.clone();        let project = project.downgrade();        let weak_running = weak_running.clone();        move |pane: &mut Pane, any: &dyn Any, window: &mut Window, cx: &mut Context<Pane>| {            let Some(tab) = any.downcast_ref::<DraggedTab>() else {                return ControlFlow::Break(());            };            let Some(project) = project.upgrade() else {                return ControlFlow::Break(());            };            let this_pane = cx.entity().clone();            let item = if tab.pane == this_pane {                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 ControlFlow::Break(());            };             let source = tab.pane.clone();            let item_id_to_move = item.item_id();             let Ok(new_split_pane) = pane                .drag_split_direction()                .map(|split_direction| {                    weak_running.update(cx, |running, cx| {                        let new_pane =                            new_debugger_pane(workspace.clone(), project.clone(), 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)?;                        anyhow::Ok(new_pane)                    })                })                .transpose()            else {                return ControlFlow::Break(());            };             match new_split_pane.transpose() {                // Source pane may be the one currently updated, so defer the move.                Ok(Some(new_pane)) => cx                    .spawn_in(window, async move |_, cx| {                        cx.update(|window, cx| {                            move_item(                                &source,                                &new_pane,                                item_id_to_move,                                new_pane.read(cx).active_item_index(),                                window,                                cx,                            );                        })                        .ok();                    })                    .detach(),                // If we drop into existing pane or current pane,                // regular pane drop handler will take care of it,                // using the right tab index for the operation.                Ok(None) => return ControlFlow::Continue(()),                err @ Err(_) => {                    err.log_err();                    return ControlFlow::Break(());                }            };             ControlFlow::Break(())        }    };     let ret = cx.new(move |cx| {        let mut pane = Pane::new(            workspace.clone(),            project.clone(),            Default::default(),            None,            NoAction.boxed_clone(),            window,            cx,        );        pane.set_can_split(Some(Arc::new(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                    .update(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.display_nav_history_buttons(None);        pane.set_custom_drop_handle(cx, custom_drop_handle);        pane.set_should_display_tab_bar(|_, _| true);        pane.set_render_tab_bar_buttons(cx, |_, _, _| (None, None));        pane.set_render_tab_bar(cx, |pane, window, cx| {            let active_pane_item = pane.active_item();            h_flex()                .w_full()                .px_2()                .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| {                        this.drag_split_direction = None;                        this.handle_tab_drop(dragged_tab, this.items_len(), window, cx)                    }),                )                .bg(cx.theme().colors().tab_bar_background)                .border_b_1()                .border_color(cx.theme().colors().border)                .children(pane.items().enumerate().map(|(ix, item)| {                    let selected = active_pane_item                        .as_ref()                        .map_or(false, |active| active.item_id() == item.item_id());                    let item_ = item.boxed_clone();                    div()                        .id(SharedString::from(format!(                            "debugger_tab_{}",                            item.item_id().as_u64()                        )))                        .p_1()                        .rounded_md()                        .cursor_pointer()                        .map(|this| {                            if selected {                                this.bg(cx.theme().colors().tab_active_background)                            } else {                                let hover_color = cx.theme().colors().element_hover;                                this.hover(|style| style.bg(hover_color))                            }                        })                        .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);                            }                        }))                        .child(item.tab_content(                            TabContentParams {                                selected,                                ..Default::default()                            },                            window,                            cx,                        ))                        .on_drop(                            cx.listener(move |this, dragged_tab: &DraggedTab, window, cx| {                                this.drag_split_direction = None;                                this.handle_tab_drop(dragged_tab, ix, window, cx)                            }),                        )                        .on_drag(                            DraggedTab {                                item: item.boxed_clone(),                                pane: cx.entity().clone(),                                detail: 0,                                is_active: selected,                                ix,                            },                            |tab, _, _, cx| cx.new(|_| tab.clone()),                        )                }))                .into_any_element()        });        pane    });     ret}impl RunningState {    pub fn new(        session: Entity<Session>,        project: Entity<Project>,        workspace: WeakEntity<Workspace>,        serialized_pane_layout: Option<SerializedPaneLayout>,        window: &mut Window,        cx: &mut Context<Self>,    ) -> Self {        let focus_handle = cx.focus_handle();        let session_id = session.read(cx).session_id();        let weak_state = cx.weak_entity();        let stack_frame_list = cx.new(|cx| {            StackFrameList::new(workspace.clone(), session.clone(), weak_state, window, cx)        });         let variable_list =            cx.new(|cx| VariableList::new(session.clone(), stack_frame_list.clone(), window, cx));         let module_list = cx.new(|cx| ModuleList::new(session.clone(), workspace.clone(), cx));         let loaded_source_list = cx.new(|cx| LoadedSourceList::new(session.clone(), cx));         let console = cx.new(|cx| {            Console::new(                session.clone(),                stack_frame_list.clone(),                variable_list.clone(),                window,                cx,            )        });         let breakpoint_list = BreakpointList::new(session.clone(), workspace.clone(), &project, cx);         let _subscriptions = vec![            cx.observe(&module_list, |_, _, cx| cx.notify()),            cx.subscribe_in(&session, window, |this, _, event, window, cx| {                match event {                    SessionEvent::Stopped(thread_id) => {                        this.workspace                            .update(cx, |workspace, cx| {                                workspace.open_panel::<crate::DebugPanel>(window, cx);                            })                            .log_err();                         if let Some(thread_id) = thread_id {                            this.select_thread(*thread_id, cx);                        }                    }                    SessionEvent::Threads => {                        let threads = this.session.update(cx, |this, cx| this.threads(cx));                        this.select_current_thread(&threads, cx);                    }                    SessionEvent::CapabilitiesLoaded => {                        let capabilities = this.capabilities(cx);                        if !capabilities.supports_modules_request.unwrap_or(false) {                            this.remove_pane_item(DebuggerPaneItem::Modules, window, cx);                        }                        if !capabilities                            .supports_loaded_sources_request                            .unwrap_or(false)                        {                            this.remove_pane_item(DebuggerPaneItem::LoadedSources, window, cx);                        }                    }                     _ => {}                }                cx.notify()            }),            cx.on_focus_out(&focus_handle, window, |this, _, window, cx| {                this.serialize_layout(window, cx);            }),        ];         let mut pane_close_subscriptions = HashMap::default();        let panes = if let Some(root) = serialized_pane_layout.and_then(|serialized_layout| {            persistence::deserialize_pane_layout(                serialized_layout,                &workspace,                &project,                &stack_frame_list,                &variable_list,                &module_list,                &console,                &breakpoint_list,                &loaded_source_list,                &mut pane_close_subscriptions,                window,                cx,            )        }) {            workspace::PaneGroup::with_root(root)        } else {            pane_close_subscriptions.clear();             let root = Self::default_pane_layout(                project,                &workspace,                &stack_frame_list,                &variable_list,                &module_list,                &loaded_source_list,                &console,                &breakpoint_list,                &mut pane_close_subscriptions,                window,                cx,            );             workspace::PaneGroup::with_root(root)        };         Self {            session,            workspace,            focus_handle,            variable_list,            _subscriptions,            thread_id: None,            _remote_id: None,            stack_frame_list,            session_id,            panes,            module_list,            _console: console,            breakpoint_list,            loaded_sources_list: loaded_source_list,            pane_close_subscriptions,            _schedule_serialize: None,        }    } 

Lines 1–500 of 1077.