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                    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}