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