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