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