session.rs

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