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 NewSessionModal::show(workspace, window, cx);
151 });
152 })
153 })
154 .detach();
155
156 cx.observe_new({
157 move |editor: &mut Editor, _, cx| {
158 editor
159 .register_action(cx.listener(
160 move |editor, _: &editor::actions::DebuggerRunToCursor, _, cx| {
161 maybe!({
162 let debug_panel =
163 editor.workspace()?.read(cx).panel::<DebugPanel>(cx)?;
164 let cursor_point: language::Point = editor.selections.newest(cx).head();
165 let active_session = debug_panel.read(cx).active_session()?;
166
167 let (buffer, position, _) = editor
168 .buffer()
169 .read(cx)
170 .point_to_buffer_point(cursor_point, cx)?;
171
172 let path =
173 debugger::breakpoint_store::BreakpointStore::abs_path_from_buffer(
174 &buffer, cx,
175 )?;
176
177 let source_breakpoint = SourceBreakpoint {
178 row: position.row,
179 path,
180 message: None,
181 condition: None,
182 hit_condition: None,
183 state: debugger::breakpoint_store::BreakpointState::Enabled,
184 };
185
186 active_session.update(cx, |session, cx| {
187 session.running_state().update(cx, |state, cx| {
188 if let Some(thread_id) = state.selected_thread_id() {
189 state.session().update(cx, |session, cx| {
190 session.run_to_position(
191 source_breakpoint,
192 thread_id,
193 cx,
194 );
195 })
196 }
197 });
198 });
199
200 Some(())
201 });
202 },
203 ))
204 .detach();
205
206 editor
207 .register_action(cx.listener(
208 move |editor, _: &editor::actions::DebuggerEvaluateSelectedText, window, cx| {
209 maybe!({
210 let debug_panel =
211 editor.workspace()?.read(cx).panel::<DebugPanel>(cx)?;
212 let active_session = debug_panel.read(cx).active_session()?;
213
214 let text = editor.text_for_range(
215 editor.selections.newest(cx).range(),
216 &mut None,
217 window,
218 cx,
219 )?;
220
221 active_session.update(cx, |session, cx| {
222 session.running_state().update(cx, |state, cx| {
223 let stack_id = state.selected_stack_frame_id(cx);
224
225 state.session().update(cx, |session, cx| {
226 session.evaluate(text, None, stack_id, None, cx).detach();
227 });
228 });
229 });
230
231 Some(())
232 });
233 },
234 ))
235 .detach();
236 }
237 })
238 .detach();
239}