session.rs

  1pub mod running;
  2
  3use std::sync::OnceLock;
  4
  5use dap::client::SessionId;
  6use gpui::{App, Entity, EventEmitter, FocusHandle, Focusable, Subscription, Task, WeakEntity};
  7use project::Project;
  8use project::debugger::session::Session;
  9use project::worktree_store::WorktreeStore;
 10use rpc::proto::{self, PeerId};
 11use running::RunningState;
 12use ui::{Indicator, prelude::*};
 13use workspace::{
 14    FollowableItem, ViewId, Workspace,
 15    item::{self, Item},
 16};
 17
 18use crate::debugger_panel::DebugPanel;
 19use crate::persistence::SerializedPaneLayout;
 20
 21pub struct DebugSession {
 22    remote_id: Option<workspace::ViewId>,
 23    running_state: Entity<RunningState>,
 24    label: OnceLock<SharedString>,
 25    _debug_panel: WeakEntity<DebugPanel>,
 26    _worktree_store: WeakEntity<WorktreeStore>,
 27    _workspace: WeakEntity<Workspace>,
 28    _subscriptions: [Subscription; 1],
 29}
 30
 31#[derive(Debug)]
 32pub enum DebugPanelItemEvent {
 33    Close,
 34    Stopped { go_to_stack_frame: bool },
 35}
 36
 37impl DebugSession {
 38    pub(crate) fn running(
 39        project: Entity<Project>,
 40        workspace: WeakEntity<Workspace>,
 41        session: Entity<Session>,
 42        _debug_panel: WeakEntity<DebugPanel>,
 43        serialized_pane_layout: Option<SerializedPaneLayout>,
 44        window: &mut Window,
 45        cx: &mut App,
 46    ) -> Entity<Self> {
 47        let running_state = cx.new(|cx| {
 48            RunningState::new(
 49                session.clone(),
 50                project.clone(),
 51                workspace.clone(),
 52                serialized_pane_layout,
 53                window,
 54                cx,
 55            )
 56        });
 57
 58        cx.new(|cx| Self {
 59            _subscriptions: [cx.subscribe(&running_state, |_, _, _, cx| {
 60                cx.notify();
 61            })],
 62            remote_id: None,
 63            running_state,
 64            label: OnceLock::new(),
 65            _debug_panel,
 66            _worktree_store: project.read(cx).worktree_store().downgrade(),
 67            _workspace: workspace,
 68        })
 69    }
 70
 71    pub(crate) fn session_id(&self, cx: &App) -> SessionId {
 72        self.running_state.read(cx).session_id()
 73    }
 74
 75    pub fn session(&self, cx: &App) -> Entity<Session> {
 76        self.running_state.read(cx).session().clone()
 77    }
 78
 79    pub(crate) fn shutdown(&mut self, cx: &mut Context<Self>) {
 80        self.running_state
 81            .update(cx, |state, cx| state.shutdown(cx));
 82    }
 83
 84    pub(crate) fn label(&self, cx: &App) -> SharedString {
 85        if let Some(label) = self.label.get() {
 86            return label.clone();
 87        }
 88
 89        let session = self.running_state.read(cx).session();
 90
 91        self.label
 92            .get_or_init(|| session.read(cx).label())
 93            .to_owned()
 94    }
 95
 96    pub(crate) fn running_state(&self) -> &Entity<RunningState> {
 97        &self.running_state
 98    }
 99
100    pub(crate) fn label_element(&self, cx: &App) -> AnyElement {
101        let label = self.label(cx);
102
103        let icon = {
104            if self
105                .running_state
106                .read(cx)
107                .session()
108                .read(cx)
109                .is_terminated()
110            {
111                Some(Indicator::dot().color(Color::Error))
112            } else {
113                match self
114                    .running_state
115                    .read(cx)
116                    .thread_status(cx)
117                    .unwrap_or_default()
118                {
119                    project::debugger::session::ThreadStatus::Stopped => {
120                        Some(Indicator::dot().color(Color::Conflict))
121                    }
122                    _ => Some(Indicator::dot().color(Color::Success)),
123                }
124            }
125        };
126
127        h_flex()
128            .gap_2()
129            .when_some(icon, |this, indicator| this.child(indicator))
130            .justify_between()
131            .child(Label::new(label))
132            .into_any_element()
133    }
134}
135
136impl EventEmitter<DebugPanelItemEvent> for DebugSession {}
137
138impl Focusable for DebugSession {
139    fn focus_handle(&self, cx: &App) -> FocusHandle {
140        self.running_state.focus_handle(cx)
141    }
142}
143
144impl Item for DebugSession {
145    type Event = DebugPanelItemEvent;
146    fn tab_content_text(&self, _detail: usize, _cx: &App) -> SharedString {
147        "Debugger".into()
148    }
149}
150
151impl FollowableItem for DebugSession {
152    fn remote_id(&self) -> Option<workspace::ViewId> {
153        self.remote_id
154    }
155
156    fn to_state_proto(&self, _window: &Window, _cx: &App) -> Option<proto::view::Variant> {
157        None
158    }
159
160    fn from_state_proto(
161        _workspace: Entity<Workspace>,
162        _remote_id: ViewId,
163        _state: &mut Option<proto::view::Variant>,
164        _window: &mut Window,
165        _cx: &mut App,
166    ) -> Option<gpui::Task<gpui::Result<Entity<Self>>>> {
167        None
168    }
169
170    fn add_event_to_update_proto(
171        &self,
172        _event: &Self::Event,
173        _update: &mut Option<proto::update_view::Variant>,
174        _window: &Window,
175        _cx: &App,
176    ) -> bool {
177        // update.get_or_insert_with(|| proto::update_view::Variant::DebugPanel(Default::default()));
178
179        true
180    }
181
182    fn apply_update_proto(
183        &mut self,
184        _project: &Entity<project::Project>,
185        _message: proto::update_view::Variant,
186        _window: &mut Window,
187        _cx: &mut Context<Self>,
188    ) -> gpui::Task<gpui::Result<()>> {
189        Task::ready(Ok(()))
190    }
191
192    fn set_leader_peer_id(
193        &mut self,
194        _leader_peer_id: Option<PeerId>,
195        _window: &mut Window,
196        _cx: &mut Context<Self>,
197    ) {
198    }
199
200    fn to_follow_event(_event: &Self::Event) -> Option<workspace::item::FollowEvent> {
201        None
202    }
203
204    fn dedup(&self, existing: &Self, _window: &Window, cx: &App) -> Option<workspace::item::Dedup> {
205        if existing.session_id(cx) == self.session_id(cx) {
206            Some(item::Dedup::KeepExisting)
207        } else {
208            None
209        }
210    }
211
212    fn is_project_item(&self, _window: &Window, _cx: &App) -> bool {
213        true
214    }
215}
216
217impl Render for DebugSession {
218    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
219        self.running_state
220            .update(cx, |this, cx| this.render(window, cx).into_any_element())
221    }
222}