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 window,
162 cx,
163 )
164 });
165 }
166 },
167 )
168 .register_action(|workspace: &mut Workspace, _: &Start, window, cx| {
169 tasks_ui::toggle_modal(
170 workspace,
171 None,
172 task::TaskModal::DebugModal,
173 window,
174 cx,
175 )
176 .detach();
177 });
178 })
179 })
180 .detach();
181
182 cx.observe_new({
183 move |editor: &mut Editor, _, cx| {
184 editor
185 .register_action(cx.listener(
186 move |editor, _: &editor::actions::DebuggerRunToCursor, _, cx| {
187 maybe!({
188 let debug_panel =
189 editor.workspace()?.read(cx).panel::<DebugPanel>(cx)?;
190 let cursor_point: language::Point = editor.selections.newest(cx).head();
191 let active_session = debug_panel.read(cx).active_session()?;
192
193 let (buffer, position, _) = editor
194 .buffer()
195 .read(cx)
196 .point_to_buffer_point(cursor_point, cx)?;
197
198 let path =
199 debugger::breakpoint_store::BreakpointStore::abs_path_from_buffer(
200 &buffer, cx,
201 )?;
202
203 let source_breakpoint = SourceBreakpoint {
204 row: position.row,
205 path,
206 message: None,
207 condition: None,
208 hit_condition: None,
209 state: debugger::breakpoint_store::BreakpointState::Enabled,
210 };
211
212 active_session.update(cx, |session, cx| {
213 session.running_state().update(cx, |state, cx| {
214 if let Some(thread_id) = state.selected_thread_id() {
215 state.session().update(cx, |session, cx| {
216 session.run_to_position(
217 source_breakpoint,
218 thread_id,
219 cx,
220 );
221 })
222 }
223 });
224 });
225
226 Some(())
227 });
228 },
229 ))
230 .detach();
231
232 editor
233 .register_action(cx.listener(
234 move |editor, _: &editor::actions::DebuggerEvaluateSelectedText, window, cx| {
235 maybe!({
236 let debug_panel =
237 editor.workspace()?.read(cx).panel::<DebugPanel>(cx)?;
238 let active_session = debug_panel.read(cx).active_session()?;
239
240 let text = editor.text_for_range(
241 editor.selections.newest(cx).range(),
242 &mut None,
243 window,
244 cx,
245 )?;
246
247 active_session.update(cx, |session, cx| {
248 session.running_state().update(cx, |state, cx| {
249 let stack_id = state.selected_stack_frame_id(cx);
250
251 state.session().update(cx, |session, cx| {
252 session.evaluate(text, None, stack_id, None, cx).detach();
253 });
254 });
255 });
256
257 Some(())
258 });
259 },
260 ))
261 .detach();
262 }
263 })
264 .detach();
265}