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