debugger: Make exception breakpoints persistent (#34014)
Closes #33053 Release Notes: - Exception breakpoint state is now persisted across debugging sessions.
This commit is contained in:
parent
966e75b610
commit
6cb382c49f
3 changed files with 178 additions and 116 deletions
|
@ -14,15 +14,13 @@ use anyhow::{Context as _, Result, anyhow};
|
|||
use async_trait::async_trait;
|
||||
use collections::HashMap;
|
||||
use dap::{
|
||||
Capabilities, CompletionItem, CompletionsArguments, DapRegistry, DebugRequest,
|
||||
EvaluateArguments, EvaluateArgumentsContext, EvaluateResponse, Source, StackFrameId,
|
||||
Capabilities, DapRegistry, DebugRequest, EvaluateArgumentsContext, StackFrameId,
|
||||
adapters::{
|
||||
DapDelegate, DebugAdapterBinary, DebugAdapterName, DebugTaskDefinition, TcpArguments,
|
||||
},
|
||||
client::SessionId,
|
||||
inline_value::VariableLookupKind,
|
||||
messages::Message,
|
||||
requests::{Completions, Evaluate},
|
||||
};
|
||||
use fs::Fs;
|
||||
use futures::{
|
||||
|
@ -40,6 +38,7 @@ use rpc::{
|
|||
AnyProtoClient, TypedEnvelope,
|
||||
proto::{self},
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use settings::{Settings, SettingsLocation, WorktreeId};
|
||||
use std::{
|
||||
borrow::Borrow,
|
||||
|
@ -93,10 +92,23 @@ pub struct DapStore {
|
|||
worktree_store: Entity<WorktreeStore>,
|
||||
sessions: BTreeMap<SessionId, Entity<Session>>,
|
||||
next_session_id: u32,
|
||||
adapter_options: BTreeMap<DebugAdapterName, Arc<PersistedAdapterOptions>>,
|
||||
}
|
||||
|
||||
impl EventEmitter<DapStoreEvent> for DapStore {}
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
pub struct PersistedExceptionBreakpoint {
|
||||
pub enabled: bool,
|
||||
}
|
||||
|
||||
/// Represents best-effort serialization of adapter state during last session (e.g. watches)
|
||||
#[derive(Clone, Default, Serialize, Deserialize)]
|
||||
pub struct PersistedAdapterOptions {
|
||||
/// Which exception breakpoints were enabled during the last session with this adapter?
|
||||
pub exception_breakpoints: BTreeMap<String, PersistedExceptionBreakpoint>,
|
||||
}
|
||||
|
||||
impl DapStore {
|
||||
pub fn init(client: &AnyProtoClient, cx: &mut App) {
|
||||
static ADD_LOCATORS: Once = Once::new();
|
||||
|
@ -173,6 +185,7 @@ impl DapStore {
|
|||
breakpoint_store,
|
||||
worktree_store,
|
||||
sessions: Default::default(),
|
||||
adapter_options: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -520,65 +533,6 @@ impl DapStore {
|
|||
))
|
||||
}
|
||||
|
||||
pub fn evaluate(
|
||||
&self,
|
||||
session_id: &SessionId,
|
||||
stack_frame_id: u64,
|
||||
expression: String,
|
||||
context: EvaluateArgumentsContext,
|
||||
source: Option<Source>,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Task<Result<EvaluateResponse>> {
|
||||
let Some(client) = self
|
||||
.session_by_id(session_id)
|
||||
.and_then(|client| client.read(cx).adapter_client())
|
||||
else {
|
||||
return Task::ready(Err(anyhow!("Could not find client: {:?}", session_id)));
|
||||
};
|
||||
|
||||
cx.background_executor().spawn(async move {
|
||||
client
|
||||
.request::<Evaluate>(EvaluateArguments {
|
||||
expression: expression.clone(),
|
||||
frame_id: Some(stack_frame_id),
|
||||
context: Some(context),
|
||||
format: None,
|
||||
line: None,
|
||||
column: None,
|
||||
source,
|
||||
})
|
||||
.await
|
||||
})
|
||||
}
|
||||
|
||||
pub fn completions(
|
||||
&self,
|
||||
session_id: &SessionId,
|
||||
stack_frame_id: u64,
|
||||
text: String,
|
||||
completion_column: u64,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Task<Result<Vec<CompletionItem>>> {
|
||||
let Some(client) = self
|
||||
.session_by_id(session_id)
|
||||
.and_then(|client| client.read(cx).adapter_client())
|
||||
else {
|
||||
return Task::ready(Err(anyhow!("Could not find client: {:?}", session_id)));
|
||||
};
|
||||
|
||||
cx.background_executor().spawn(async move {
|
||||
Ok(client
|
||||
.request::<Completions>(CompletionsArguments {
|
||||
frame_id: Some(stack_frame_id),
|
||||
line: None,
|
||||
text,
|
||||
column: completion_column,
|
||||
})
|
||||
.await?
|
||||
.targets)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn resolve_inline_value_locations(
|
||||
&self,
|
||||
session: Entity<Session>,
|
||||
|
@ -853,6 +807,45 @@ impl DapStore {
|
|||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub fn sync_adapter_options(
|
||||
&mut self,
|
||||
session: &Entity<Session>,
|
||||
cx: &App,
|
||||
) -> Arc<PersistedAdapterOptions> {
|
||||
let session = session.read(cx);
|
||||
let adapter = session.adapter();
|
||||
let exceptions = session.exception_breakpoints();
|
||||
let exception_breakpoints = exceptions
|
||||
.map(|(exception, enabled)| {
|
||||
(
|
||||
exception.filter.clone(),
|
||||
PersistedExceptionBreakpoint { enabled: *enabled },
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
let options = Arc::new(PersistedAdapterOptions {
|
||||
exception_breakpoints,
|
||||
});
|
||||
self.adapter_options.insert(adapter, options.clone());
|
||||
options
|
||||
}
|
||||
|
||||
pub fn set_adapter_options(
|
||||
&mut self,
|
||||
adapter: DebugAdapterName,
|
||||
options: PersistedAdapterOptions,
|
||||
) {
|
||||
self.adapter_options.insert(adapter, Arc::new(options));
|
||||
}
|
||||
|
||||
pub fn adapter_options(&self, name: &str) -> Option<Arc<PersistedAdapterOptions>> {
|
||||
self.adapter_options.get(name).cloned()
|
||||
}
|
||||
|
||||
pub fn all_adapter_options(&self) -> &BTreeMap<DebugAdapterName, Arc<PersistedAdapterOptions>> {
|
||||
&self.adapter_options
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue