debugger_ui.rs

  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                            cx.spawn_in(window, async move |this, cx| {
157                                let task_contexts = this
158                                    .update_in(cx, |workspace, window, cx| {
159                                        tasks_ui::task_contexts(workspace, window, cx)
160                                    })?
161                                    .await;
162                                this.update_in(cx, |workspace, window, cx| {
163                                    workspace.toggle_modal(window, cx, |window, cx| {
164                                        NewSessionModal::new(
165                                            debug_panel.read(cx).past_debug_definition.clone(),
166                                            weak_panel,
167                                            weak_workspace,
168                                            None,
169                                            task_contexts,
170                                            window,
171                                            cx,
172                                        )
173                                    });
174                                })?;
175
176                                Result::<_, anyhow::Error>::Ok(())
177                            })
178                            .detach();
179                        }
180                    },
181                )
182                .register_action(|workspace: &mut Workspace, _: &Start, window, cx| {
183                    if let Some(debug_panel) = workspace.panel::<DebugPanel>(cx) {
184                        let weak_panel = debug_panel.downgrade();
185                        let weak_workspace = cx.weak_entity();
186                        let task_store = workspace.project().read(cx).task_store().clone();
187
188                        cx.spawn_in(window, async move |this, cx| {
189                            let task_contexts = this
190                                .update_in(cx, |workspace, window, cx| {
191                                    tasks_ui::task_contexts(workspace, window, cx)
192                                })?
193                                .await;
194
195                            this.update_in(cx, |workspace, window, cx| {
196                                workspace.toggle_modal(window, cx, |window, cx| {
197                                    NewSessionModal::new(
198                                        debug_panel.read(cx).past_debug_definition.clone(),
199                                        weak_panel,
200                                        weak_workspace,
201                                        Some(task_store),
202                                        task_contexts,
203                                        window,
204                                        cx,
205                                    )
206                                });
207                            })?;
208
209                            anyhow::Ok(())
210                        })
211                        .detach()
212                    }
213                });
214        })
215    })
216    .detach();
217
218    cx.observe_new({
219        move |editor: &mut Editor, _, cx| {
220            editor
221                .register_action(cx.listener(
222                    move |editor, _: &editor::actions::DebuggerRunToCursor, _, cx| {
223                        maybe!({
224                            let debug_panel =
225                                editor.workspace()?.read(cx).panel::<DebugPanel>(cx)?;
226                            let cursor_point: language::Point = editor.selections.newest(cx).head();
227                            let active_session = debug_panel.read(cx).active_session()?;
228
229                            let (buffer, position, _) = editor
230                                .buffer()
231                                .read(cx)
232                                .point_to_buffer_point(cursor_point, cx)?;
233
234                            let path =
235                                debugger::breakpoint_store::BreakpointStore::abs_path_from_buffer(
236                                    &buffer, cx,
237                                )?;
238
239                            let source_breakpoint = SourceBreakpoint {
240                                row: position.row,
241                                path,
242                                message: None,
243                                condition: None,
244                                hit_condition: None,
245                                state: debugger::breakpoint_store::BreakpointState::Enabled,
246                            };
247
248                            active_session.update(cx, |session, cx| {
249                                session.running_state().update(cx, |state, cx| {
250                                    if let Some(thread_id) = state.selected_thread_id() {
251                                        state.session().update(cx, |session, cx| {
252                                            session.run_to_position(
253                                                source_breakpoint,
254                                                thread_id,
255                                                cx,
256                                            );
257                                        })
258                                    }
259                                });
260                            });
261
262                            Some(())
263                        });
264                    },
265                ))
266                .detach();
267
268            editor
269                .register_action(cx.listener(
270                    move |editor, _: &editor::actions::DebuggerEvaluateSelectedText, window, cx| {
271                        maybe!({
272                            let debug_panel =
273                                editor.workspace()?.read(cx).panel::<DebugPanel>(cx)?;
274                            let active_session = debug_panel.read(cx).active_session()?;
275
276                            let text = editor.text_for_range(
277                                editor.selections.newest(cx).range(),
278                                &mut None,
279                                window,
280                                cx,
281                            )?;
282
283                            active_session.update(cx, |session, cx| {
284                                session.running_state().update(cx, |state, cx| {
285                                    let stack_id = state.selected_stack_frame_id(cx);
286
287                                    state.session().update(cx, |session, cx| {
288                                        session.evaluate(text, None, stack_id, None, cx).detach();
289                                    });
290                                });
291                            });
292
293                            Some(())
294                        });
295                    },
296                ))
297                .detach();
298        }
299    })
300    .detach();
301}