debugger_ui.rs

  1use dap::debugger_settings::DebuggerSettings;
  2use debugger_panel::{DebugPanel, ToggleFocus};
  3use editor::Editor;
  4use feature_flags::{Debugger, 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    ]
 39);
 40
 41pub fn init(cx: &mut App) {
 42    DebuggerSettings::register(cx);
 43    workspace::FollowableViewRegistry::register::<DebugSession>(cx);
 44
 45    cx.observe_new(|_: &mut Workspace, window, cx| {
 46        let Some(window) = window else {
 47            return;
 48        };
 49
 50        cx.when_flag_enabled::<Debugger>(window, |workspace, _, _| {
 51            workspace
 52                .register_action(|workspace, _: &ToggleFocus, window, cx| {
 53                    workspace.toggle_panel_focus::<DebugPanel>(window, cx);
 54                })
 55                .register_action(|workspace, _: &Pause, _, cx| {
 56                    if let Some(debug_panel) = workspace.panel::<DebugPanel>(cx) {
 57                        if let Some(active_item) = debug_panel.read_with(cx, |panel, cx| {
 58                            panel
 59                                .active_session()
 60                                .and_then(|session| session.read(cx).mode().as_running().cloned())
 61                        }) {
 62                            active_item.update(cx, |item, cx| item.pause_thread(cx))
 63                        }
 64                    }
 65                })
 66                .register_action(|workspace, _: &Restart, _, cx| {
 67                    if let Some(debug_panel) = workspace.panel::<DebugPanel>(cx) {
 68                        if let Some(active_item) = debug_panel.read_with(cx, |panel, cx| {
 69                            panel
 70                                .active_session()
 71                                .and_then(|session| session.read(cx).mode().as_running().cloned())
 72                        }) {
 73                            active_item.update(cx, |item, cx| item.restart_session(cx))
 74                        }
 75                    }
 76                })
 77                .register_action(|workspace, _: &StepInto, _, cx| {
 78                    if let Some(debug_panel) = workspace.panel::<DebugPanel>(cx) {
 79                        if let Some(active_item) = debug_panel.read_with(cx, |panel, cx| {
 80                            panel
 81                                .active_session()
 82                                .and_then(|session| session.read(cx).mode().as_running().cloned())
 83                        }) {
 84                            active_item.update(cx, |item, cx| item.step_in(cx))
 85                        }
 86                    }
 87                })
 88                .register_action(|workspace, _: &StepOver, _, cx| {
 89                    if let Some(debug_panel) = workspace.panel::<DebugPanel>(cx) {
 90                        if let Some(active_item) = debug_panel.read_with(cx, |panel, cx| {
 91                            panel
 92                                .active_session()
 93                                .and_then(|session| session.read(cx).mode().as_running().cloned())
 94                        }) {
 95                            active_item.update(cx, |item, cx| item.step_over(cx))
 96                        }
 97                    }
 98                })
 99                .register_action(|workspace, _: &StepBack, _, cx| {
100                    if let Some(debug_panel) = workspace.panel::<DebugPanel>(cx) {
101                        if let Some(active_item) = debug_panel.read_with(cx, |panel, cx| {
102                            panel
103                                .active_session()
104                                .and_then(|session| session.read(cx).mode().as_running().cloned())
105                        }) {
106                            active_item.update(cx, |item, cx| item.step_back(cx))
107                        }
108                    }
109                })
110                .register_action(|workspace, _: &Stop, _, cx| {
111                    if let Some(debug_panel) = workspace.panel::<DebugPanel>(cx) {
112                        if let Some(active_item) = debug_panel.read_with(cx, |panel, cx| {
113                            panel
114                                .active_session()
115                                .and_then(|session| session.read(cx).mode().as_running().cloned())
116                        }) {
117                            cx.defer(move |cx| {
118                                active_item.update(cx, |item, cx| item.stop_thread(cx))
119                            })
120                        }
121                    }
122                })
123                .register_action(|workspace, _: &ToggleIgnoreBreakpoints, _, cx| {
124                    if let Some(debug_panel) = workspace.panel::<DebugPanel>(cx) {
125                        if let Some(active_item) = debug_panel.read_with(cx, |panel, cx| {
126                            panel
127                                .active_session()
128                                .and_then(|session| session.read(cx).mode().as_running().cloned())
129                        }) {
130                            active_item.update(cx, |item, cx| item.toggle_ignore_breakpoints(cx))
131                        }
132                    }
133                })
134                .register_action(
135                    |workspace: &mut Workspace, _: &ShutdownDebugAdapters, _window, cx| {
136                        workspace.project().update(cx, |project, cx| {
137                            project.dap_store().update(cx, |store, cx| {
138                                store.shutdown_sessions(cx).detach();
139                            })
140                        })
141                    },
142                )
143                .register_action(
144                    |workspace: &mut Workspace, _: &CreateDebuggingSession, window, cx| {
145                        if let Some(debug_panel) = workspace.panel::<DebugPanel>(cx) {
146                            let weak_panel = debug_panel.downgrade();
147                            let weak_workspace = cx.weak_entity();
148
149                            workspace.toggle_modal(window, cx, |window, cx| {
150                                NewSessionModal::new(
151                                    debug_panel.read(cx).past_debug_definition.clone(),
152                                    weak_panel,
153                                    weak_workspace,
154                                    window,
155                                    cx,
156                                )
157                            });
158                        }
159                    },
160                )
161                .register_action(|workspace: &mut Workspace, _: &Start, window, cx| {
162                    tasks_ui::toggle_modal(
163                        workspace,
164                        None,
165                        task::TaskModal::DebugModal,
166                        window,
167                        cx,
168                    )
169                    .detach();
170                });
171        })
172    })
173    .detach();
174
175    cx.observe_new({
176        move |editor: &mut Editor, _, cx| {
177            editor
178                .register_action(cx.listener(
179                    move |editor, _: &editor::actions::DebuggerRunToCursor, _, cx| {
180                        maybe!({
181                            let debug_panel =
182                                editor.workspace()?.read(cx).panel::<DebugPanel>(cx)?;
183                            let cursor_point: language::Point = editor.selections.newest(cx).head();
184                            let active_session = debug_panel.read(cx).active_session()?;
185
186                            let (buffer, position, _) = editor
187                                .buffer()
188                                .read(cx)
189                                .point_to_buffer_point(cursor_point, cx)?;
190
191                            let path =
192                                debugger::breakpoint_store::BreakpointStore::abs_path_from_buffer(
193                                    &buffer, cx,
194                                )?;
195
196                            let source_breakpoint = SourceBreakpoint {
197                                row: position.row,
198                                path,
199                                message: None,
200                                condition: None,
201                                hit_condition: None,
202                                state: debugger::breakpoint_store::BreakpointState::Enabled,
203                            };
204
205                            active_session
206                                .update(cx, |session_item, _| {
207                                    session_item.mode().as_running().cloned()
208                                })?
209                                .update(cx, |state, cx| {
210                                    if let Some(thread_id) = state.selected_thread_id() {
211                                        state.session().update(cx, |session, cx| {
212                                            session.run_to_position(
213                                                source_breakpoint,
214                                                thread_id,
215                                                cx,
216                                            );
217                                        })
218                                    }
219                                });
220
221                            Some(())
222                        });
223                    },
224                ))
225                .detach();
226
227            editor
228                .register_action(cx.listener(
229                    move |editor, _: &editor::actions::DebuggerEvaluateSelectedText, window, cx| {
230                        maybe!({
231                            let debug_panel =
232                                editor.workspace()?.read(cx).panel::<DebugPanel>(cx)?;
233                            let active_session = debug_panel.read(cx).active_session()?;
234
235                            let text = editor.text_for_range(
236                                editor.selections.newest(cx).range(),
237                                &mut None,
238                                window,
239                                cx,
240                            )?;
241
242                            active_session
243                                .update(cx, |session_item, _| {
244                                    session_item.mode().as_running().cloned()
245                                })?
246                                .update(cx, |state, cx| {
247                                    let stack_id = state.selected_stack_frame_id(cx);
248
249                                    state.session().update(cx, |session, cx| {
250                                        session.evaluate(text, None, stack_id, None, cx).detach();
251                                    });
252                                });
253                            Some(())
254                        });
255                    },
256                ))
257                .detach();
258        }
259    })
260    .detach();
261}