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