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