1use dap::debugger_settings::DebuggerSettings;
2use debugger_panel::{DebugPanel, ToggleFocus};
3use editor::Editor;
4use feature_flags::{DebuggerFeatureFlag, FeatureFlagViewExt};
5use gpui::{App, EntityInputHandler, actions};
6use new_session_modal::NewSessionModal;
7use project::debugger::{self, breakpoint_store::SourceBreakpoint};
8use session::DebugSession;
9use settings::Settings;
10use util::maybe;
11use workspace::{ShutdownDebugAdapters, Workspace};
12
13pub mod attach_modal;
14pub mod debugger_panel;
15mod new_session_modal;
16mod persistence;
17pub(crate) mod session;
18
19#[cfg(any(test, feature = "test-support"))]
20pub mod tests;
21
22actions!(
23 debugger,
24 [
25 Start,
26 Continue,
27 Detach,
28 Pause,
29 Restart,
30 StepInto,
31 StepOver,
32 StepOut,
33 StepBack,
34 Stop,
35 ToggleIgnoreBreakpoints,
36 ClearAllBreakpoints,
37 FocusConsole,
38 FocusVariables,
39 FocusBreakpointList,
40 FocusFrames,
41 FocusModules,
42 FocusLoadedSources,
43 FocusTerminal,
44 ]
45);
46
47pub fn init(cx: &mut App) {
48 DebuggerSettings::register(cx);
49 workspace::FollowableViewRegistry::register::<DebugSession>(cx);
50
51 cx.observe_new(|_: &mut Workspace, window, cx| {
52 let Some(window) = window else {
53 return;
54 };
55
56 cx.when_flag_enabled::<DebuggerFeatureFlag>(window, |workspace, _, _| {
57 workspace
58 .register_action(|workspace, _: &ToggleFocus, window, cx| {
59 workspace.toggle_panel_focus::<DebugPanel>(window, cx);
60 })
61 .register_action(|workspace, _: &Pause, _, cx| {
62 if let Some(debug_panel) = workspace.panel::<DebugPanel>(cx) {
63 if let Some(active_item) = debug_panel.read_with(cx, |panel, cx| {
64 panel
65 .active_session()
66 .map(|session| session.read(cx).running_state().clone())
67 }) {
68 active_item.update(cx, |item, cx| item.pause_thread(cx))
69 }
70 }
71 })
72 .register_action(|workspace, _: &Restart, _, cx| {
73 if let Some(debug_panel) = workspace.panel::<DebugPanel>(cx) {
74 if let Some(active_item) = debug_panel.read_with(cx, |panel, cx| {
75 panel
76 .active_session()
77 .map(|session| session.read(cx).running_state().clone())
78 }) {
79 active_item.update(cx, |item, cx| item.restart_session(cx))
80 }
81 }
82 })
83 .register_action(|workspace, _: &StepInto, _, cx| {
84 if let Some(debug_panel) = workspace.panel::<DebugPanel>(cx) {
85 if let Some(active_item) = debug_panel.read_with(cx, |panel, cx| {
86 panel
87 .active_session()
88 .map(|session| session.read(cx).running_state().clone())
89 }) {
90 active_item.update(cx, |item, cx| item.step_in(cx))
91 }
92 }
93 })
94 .register_action(|workspace, _: &StepOver, _, cx| {
95 if let Some(debug_panel) = workspace.panel::<DebugPanel>(cx) {
96 if let Some(active_item) = debug_panel.read_with(cx, |panel, cx| {
97 panel
98 .active_session()
99 .map(|session| session.read(cx).running_state().clone())
100 }) {
101 active_item.update(cx, |item, cx| item.step_over(cx))
102 }
103 }
104 })
105 .register_action(|workspace, _: &StepBack, _, cx| {
106 if let Some(debug_panel) = workspace.panel::<DebugPanel>(cx) {
107 if let Some(active_item) = debug_panel.read_with(cx, |panel, cx| {
108 panel
109 .active_session()
110 .map(|session| session.read(cx).running_state().clone())
111 }) {
112 active_item.update(cx, |item, cx| item.step_back(cx))
113 }
114 }
115 })
116 .register_action(|workspace, _: &Stop, _, cx| {
117 if let Some(debug_panel) = workspace.panel::<DebugPanel>(cx) {
118 if let Some(active_item) = debug_panel.read_with(cx, |panel, cx| {
119 panel
120 .active_session()
121 .map(|session| session.read(cx).running_state().clone())
122 }) {
123 cx.defer(move |cx| {
124 active_item.update(cx, |item, cx| item.stop_thread(cx))
125 })
126 }
127 }
128 })
129 .register_action(|workspace, _: &ToggleIgnoreBreakpoints, _, cx| {
130 if let Some(debug_panel) = workspace.panel::<DebugPanel>(cx) {
131 if let Some(active_item) = debug_panel.read_with(cx, |panel, cx| {
132 panel
133 .active_session()
134 .map(|session| session.read(cx).running_state().clone())
135 }) {
136 active_item.update(cx, |item, cx| item.toggle_ignore_breakpoints(cx))
137 }
138 }
139 })
140 .register_action(
141 |workspace: &mut Workspace, _: &ShutdownDebugAdapters, _window, cx| {
142 workspace.project().update(cx, |project, cx| {
143 project.dap_store().update(cx, |store, cx| {
144 store.shutdown_sessions(cx).detach();
145 })
146 })
147 },
148 )
149 .register_action(|workspace: &mut Workspace, _: &Start, window, cx| {
150 if let Some(debug_panel) = workspace.panel::<DebugPanel>(cx) {
151 let weak_panel = debug_panel.downgrade();
152 let weak_workspace = cx.weak_entity();
153 let task_store = workspace.project().read(cx).task_store().clone();
154
155 cx.spawn_in(window, async move |this, cx| {
156 let task_contexts = this
157 .update_in(cx, |workspace, window, cx| {
158 tasks_ui::task_contexts(workspace, window, cx)
159 })?
160 .await;
161
162 this.update_in(cx, |workspace, window, cx| {
163 workspace.toggle_modal(window, cx, |window, cx| {
164 NewSessionModal::new(
165 debug_panel.read(cx).past_debug_definition.clone(),
166 weak_panel,
167 weak_workspace,
168 Some(task_store),
169 task_contexts,
170 window,
171 cx,
172 )
173 });
174 })?;
175
176 anyhow::Ok(())
177 })
178 .detach()
179 }
180 });
181 })
182 })
183 .detach();
184
185 cx.observe_new({
186 move |editor: &mut Editor, _, cx| {
187 editor
188 .register_action(cx.listener(
189 move |editor, _: &editor::actions::DebuggerRunToCursor, _, cx| {
190 maybe!({
191 let debug_panel =
192 editor.workspace()?.read(cx).panel::<DebugPanel>(cx)?;
193 let cursor_point: language::Point = editor.selections.newest(cx).head();
194 let active_session = debug_panel.read(cx).active_session()?;
195
196 let (buffer, position, _) = editor
197 .buffer()
198 .read(cx)
199 .point_to_buffer_point(cursor_point, cx)?;
200
201 let path =
202 debugger::breakpoint_store::BreakpointStore::abs_path_from_buffer(
203 &buffer, cx,
204 )?;
205
206 let source_breakpoint = SourceBreakpoint {
207 row: position.row,
208 path,
209 message: None,
210 condition: None,
211 hit_condition: None,
212 state: debugger::breakpoint_store::BreakpointState::Enabled,
213 };
214
215 active_session.update(cx, |session, cx| {
216 session.running_state().update(cx, |state, cx| {
217 if let Some(thread_id) = state.selected_thread_id() {
218 state.session().update(cx, |session, cx| {
219 session.run_to_position(
220 source_breakpoint,
221 thread_id,
222 cx,
223 );
224 })
225 }
226 });
227 });
228
229 Some(())
230 });
231 },
232 ))
233 .detach();
234
235 editor
236 .register_action(cx.listener(
237 move |editor, _: &editor::actions::DebuggerEvaluateSelectedText, window, cx| {
238 maybe!({
239 let debug_panel =
240 editor.workspace()?.read(cx).panel::<DebugPanel>(cx)?;
241 let active_session = debug_panel.read(cx).active_session()?;
242
243 let text = editor.text_for_range(
244 editor.selections.newest(cx).range(),
245 &mut None,
246 window,
247 cx,
248 )?;
249
250 active_session.update(cx, |session, cx| {
251 session.running_state().update(cx, |state, cx| {
252 let stack_id = state.selected_stack_frame_id(cx);
253
254 state.session().update(cx, |session, cx| {
255 session.evaluate(text, None, stack_id, None, cx).detach();
256 });
257 });
258 });
259
260 Some(())
261 });
262 },
263 ))
264 .detach();
265 }
266 })
267 .detach();
268}