debugger: Make exception breakpoints persistent (#34014)

Closes #33053
Release Notes:

- Exception breakpoint state is now persisted across debugging sessions.
This commit is contained in:
Piotr Osiewicz 2025-07-07 17:40:14 +02:00 committed by GitHub
parent 966e75b610
commit 6cb382c49f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 178 additions and 116 deletions

View file

@ -409,17 +409,6 @@ impl RunningMode {
};
let configuration_done_supported = ConfigurationDone::is_supported(capabilities);
let exception_filters = capabilities
.exception_breakpoint_filters
.as_ref()
.map(|exception_filters| {
exception_filters
.iter()
.filter(|filter| filter.default == Some(true))
.cloned()
.collect::<Vec<_>>()
})
.unwrap_or_default();
// From spec (on initialization sequence):
// client sends a setExceptionBreakpoints request if one or more exceptionBreakpointFilters have been defined (or if supportsConfigurationDoneRequest is not true)
//
@ -434,10 +423,20 @@ impl RunningMode {
.unwrap_or_default();
let this = self.clone();
let worktree = self.worktree().clone();
let mut filters = capabilities
.exception_breakpoint_filters
.clone()
.unwrap_or_default();
let configuration_sequence = cx.spawn({
async move |_, cx| {
let breakpoint_store =
dap_store.read_with(cx, |dap_store, _| dap_store.breakpoint_store().clone())?;
async move |session, cx| {
let adapter_name = session.read_with(cx, |this, _| this.adapter())?;
let (breakpoint_store, adapter_defaults) =
dap_store.read_with(cx, |dap_store, _| {
(
dap_store.breakpoint_store().clone(),
dap_store.adapter_options(&adapter_name),
)
})?;
initialized_rx.await?;
let errors_by_path = cx
.update(|cx| this.send_source_breakpoints(false, &breakpoint_store, cx))?
@ -471,7 +470,25 @@ impl RunningMode {
})?;
if should_send_exception_breakpoints {
this.send_exception_breakpoints(exception_filters, supports_exception_filters)
_ = session.update(cx, |this, _| {
filters.retain(|filter| {
let is_enabled = if let Some(defaults) = adapter_defaults.as_ref() {
defaults
.exception_breakpoints
.get(&filter.filter)
.map(|options| options.enabled)
.unwrap_or_else(|| filter.default.unwrap_or_default())
} else {
filter.default.unwrap_or_default()
};
this.exception_breakpoints
.entry(filter.filter.clone())
.or_insert_with(|| (filter.clone(), is_enabled));
is_enabled
});
});
this.send_exception_breakpoints(filters, supports_exception_filters)
.await
.ok();
}
@ -1233,18 +1250,7 @@ impl Session {
Ok(capabilities) => {
this.update(cx, |session, cx| {
session.capabilities = capabilities;
let filters = session
.capabilities
.exception_breakpoint_filters
.clone()
.unwrap_or_default();
for filter in filters {
let default = filter.default.unwrap_or_default();
session
.exception_breakpoints
.entry(filter.filter.clone())
.or_insert_with(|| (filter, default));
}
cx.emit(SessionEvent::CapabilitiesLoaded);
})?;
return Ok(());