diff --git a/assets/icons/debug_step_back.svg b/assets/icons/debug_step_back.svg
new file mode 100644
index 00000000000000..bc7c9b8444cda2
--- /dev/null
+++ b/assets/icons/debug_step_back.svg
@@ -0,0 +1 @@
+
diff --git a/crates/debugger_ui/src/debugger_panel_item.rs b/crates/debugger_ui/src/debugger_panel_item.rs
index 310421a54a492a..03789d152c6054 100644
--- a/crates/debugger_ui/src/debugger_panel_item.rs
+++ b/crates/debugger_ui/src/debugger_panel_item.rs
@@ -588,6 +588,18 @@ impl DebugPanelItem {
});
}
+ pub fn step_back(&mut self, cx: &mut ViewContext) {
+ self.update_thread_state_status(ThreadStatus::Running, cx);
+
+ let granularity = DebuggerSettings::get_global(cx).stepping_granularity;
+
+ self.dap_store.update(cx, |store, cx| {
+ store
+ .step_back(&self.client_id, self.thread_id, granularity, cx)
+ .detach_and_log_err(cx);
+ });
+ }
+
pub fn restart_client(&self, cx: &mut ViewContext) {
self.dap_store.update(cx, |store, cx| {
store
@@ -807,6 +819,17 @@ impl Render for DebugPanelItem {
.disabled(thread_status != ThreadStatus::Stopped)
.tooltip(move |cx| Tooltip::text("Step out", cx)),
)
+ .when(capabilities.supports_step_back.unwrap_or(false), |this| {
+ this.child(
+ IconButton::new("debug-step-back", IconName::DebugStepBack)
+ .icon_size(IconSize::Small)
+ .on_click(cx.listener(|this, _, cx| {
+ this.step_back(cx);
+ }))
+ .disabled(thread_status != ThreadStatus::Stopped)
+ .tooltip(move |cx| Tooltip::text("Step back", cx)),
+ )
+ })
.child(
IconButton::new("debug-restart", IconName::DebugRestart)
.icon_size(IconSize::Small)
diff --git a/crates/project/src/dap_store.rs b/crates/project/src/dap_store.rs
index e7b701101523ac..6454ebc7dd08b5 100644
--- a/crates/project/src/dap_store.rs
+++ b/crates/project/src/dap_store.rs
@@ -3,15 +3,15 @@ use crate::{ProjectEnvironment, ProjectItem as _, ProjectPath};
use anyhow::{anyhow, Context as _, Result};
use async_trait::async_trait;
use collections::HashMap;
-use dap::adapters::{DapDelegate, DapStatus, DebugAdapter, DebugAdapterBinary, DebugAdapterName};
use dap::{
+ adapters::{DapDelegate, DapStatus, DebugAdapter, DebugAdapterBinary, DebugAdapterName},
client::{DebugAdapterClient, DebugAdapterClientId},
messages::{Message, Response},
requests::{
Attach, Completions, ConfigurationDone, Continue, Disconnect, Evaluate, Initialize, Launch,
LoadedSources, Modules, Next, Pause, Request as _, Restart, RunInTerminal, Scopes,
- SetBreakpoints, SetExpression, SetVariable, StackTrace, StartDebugging, StepIn, StepOut,
- Terminate, TerminateThreads, Variables,
+ SetBreakpoints, SetExpression, SetVariable, StackTrace, StartDebugging, StepBack, StepIn,
+ StepOut, Terminate, TerminateThreads, Variables,
},
AttachRequestArguments, Capabilities, CompletionItem, CompletionsArguments,
ConfigurationDoneArguments, ContinueArguments, DisconnectArguments, ErrorResponse,
@@ -20,8 +20,9 @@ use dap::{
ModulesArguments, NextArguments, PauseArguments, RestartArguments, RunInTerminalResponse,
Scope, ScopesArguments, SetBreakpointsArguments, SetExpressionArguments, SetVariableArguments,
Source, SourceBreakpoint, StackFrame, StackTraceArguments, StartDebuggingRequestArguments,
- StartDebuggingRequestArgumentsRequest, StepInArguments, StepOutArguments, SteppingGranularity,
- TerminateArguments, TerminateThreadsArguments, Variable, VariablesArguments,
+ StartDebuggingRequestArgumentsRequest, StepBackArguments, StepInArguments, StepOutArguments,
+ SteppingGranularity, TerminateArguments, TerminateThreadsArguments, Variable,
+ VariablesArguments,
};
use dap_adapters::build_adapter;
use fs::Fs;
@@ -1077,6 +1078,37 @@ impl DapStore {
})
}
+ pub fn step_back(
+ &self,
+ client_id: &DebugAdapterClientId,
+ thread_id: u64,
+ granularity: SteppingGranularity,
+ cx: &mut ModelContext,
+ ) -> Task> {
+ let Some(client) = self.client_by_id(client_id) else {
+ return Task::ready(Err(anyhow!("Could not find client: {:?}", client_id)));
+ };
+
+ let capabilities = self.capabilities_by_id(client_id);
+
+ let supports_single_thread_execution_requests = capabilities
+ .supports_single_thread_execution_requests
+ .unwrap_or_default();
+ let supports_stepping_granularity = capabilities
+ .supports_stepping_granularity
+ .unwrap_or_default();
+
+ cx.background_executor().spawn(async move {
+ client
+ .request::(StepBackArguments {
+ thread_id,
+ granularity: supports_stepping_granularity.then(|| granularity),
+ single_thread: supports_single_thread_execution_requests.then(|| true),
+ })
+ .await
+ })
+ }
+
pub fn variables(
&self,
client_id: &DebugAdapterClientId,
diff --git a/crates/ui/src/components/icon.rs b/crates/ui/src/components/icon.rs
index 71bb647f5da3d8..08bee40a019973 100644
--- a/crates/ui/src/components/icon.rs
+++ b/crates/ui/src/components/icon.rs
@@ -168,6 +168,7 @@ pub enum IconName {
DebugStepOver,
DebugStepInto,
DebugStepOut,
+ DebugStepBack,
DebugRestart,
Debug,
DebugStop,