debugger: Add variable watchers (#32743)
### This PR introduces support for adding watchers to specific expressions (such as variable names or evaluated expressions). This feature is useful in scenarios where many variables are in scope, but only a few are of interest—especially when tracking variables that change frequently. By allowing users to add watchers, it becomes easier to monitor the values of selected expressions across stack frames without having to sift through a large list of variables. https://github.com/user-attachments/assets/c49b470a-d912-4182-8419-7406ba4c8f1e ------ **TODO**: - [x] make render variable code reusable for render watch method - [x] use SharedString for watches because of a lot of cloning - [x] add tests - [x] basic test - [x] test step debugging Release Notes: - Debugger Beta: Add support for variable watchers --------- Co-authored-by: Anthony Eid <hello@anthonyeid.me> Co-authored-by: Anthony <anthony@zed.dev>
This commit is contained in:
parent
9f2c541ab0
commit
ad76db7244
7 changed files with 1243 additions and 123 deletions
|
@ -26,7 +26,7 @@ use dap::{
|
|||
use dap::{
|
||||
ExceptionBreakpointsFilter, ExceptionFilterOptions, OutputEvent, OutputEventCategory,
|
||||
RunInTerminalRequestArguments, StackFramePresentationHint, StartDebuggingRequestArguments,
|
||||
StartDebuggingRequestArgumentsRequest,
|
||||
StartDebuggingRequestArgumentsRequest, VariablePresentationHint,
|
||||
};
|
||||
use futures::SinkExt;
|
||||
use futures::channel::mpsc::UnboundedSender;
|
||||
|
@ -126,6 +126,14 @@ impl From<dap::Thread> for Thread {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Watcher {
|
||||
pub expression: SharedString,
|
||||
pub value: SharedString,
|
||||
pub variables_reference: u64,
|
||||
pub presentation_hint: Option<VariablePresentationHint>,
|
||||
}
|
||||
|
||||
pub enum Mode {
|
||||
Building,
|
||||
Running(RunningMode),
|
||||
|
@ -630,6 +638,7 @@ pub struct Session {
|
|||
output: Box<circular_buffer::CircularBuffer<MAX_TRACKED_OUTPUT_EVENTS, dap::OutputEvent>>,
|
||||
threads: IndexMap<ThreadId, Thread>,
|
||||
thread_states: ThreadStates,
|
||||
watchers: HashMap<SharedString, Watcher>,
|
||||
variables: HashMap<VariableReference, Vec<dap::Variable>>,
|
||||
stack_frames: IndexMap<StackFrameId, StackFrame>,
|
||||
locations: HashMap<u64, dap::LocationsResponse>,
|
||||
|
@ -721,6 +730,7 @@ pub enum SessionEvent {
|
|||
Stopped(Option<ThreadId>),
|
||||
StackTrace,
|
||||
Variables,
|
||||
Watchers,
|
||||
Threads,
|
||||
InvalidateInlineValue,
|
||||
CapabilitiesLoaded,
|
||||
|
@ -788,6 +798,7 @@ impl Session {
|
|||
child_session_ids: HashSet::default(),
|
||||
parent_session,
|
||||
capabilities: Capabilities::default(),
|
||||
watchers: HashMap::default(),
|
||||
variables: Default::default(),
|
||||
stack_frames: Default::default(),
|
||||
thread_states: ThreadStates::default(),
|
||||
|
@ -2155,6 +2166,53 @@ impl Session {
|
|||
.collect()
|
||||
}
|
||||
|
||||
pub fn watchers(&self) -> &HashMap<SharedString, Watcher> {
|
||||
&self.watchers
|
||||
}
|
||||
|
||||
pub fn add_watcher(
|
||||
&mut self,
|
||||
expression: SharedString,
|
||||
frame_id: u64,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Task<Result<()>> {
|
||||
let request = self.mode.request_dap(EvaluateCommand {
|
||||
expression: expression.to_string(),
|
||||
context: Some(EvaluateArgumentsContext::Watch),
|
||||
frame_id: Some(frame_id),
|
||||
source: None,
|
||||
});
|
||||
|
||||
cx.spawn(async move |this, cx| {
|
||||
let response = request.await?;
|
||||
|
||||
this.update(cx, |session, cx| {
|
||||
session.watchers.insert(
|
||||
expression.clone(),
|
||||
Watcher {
|
||||
expression,
|
||||
value: response.result.into(),
|
||||
variables_reference: response.variables_reference,
|
||||
presentation_hint: response.presentation_hint,
|
||||
},
|
||||
);
|
||||
cx.emit(SessionEvent::Watchers);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub fn refresh_watchers(&mut self, frame_id: u64, cx: &mut Context<Self>) {
|
||||
let watches = self.watchers.clone();
|
||||
for (_, watch) in watches.into_iter() {
|
||||
self.add_watcher(watch.expression.clone(), frame_id, cx)
|
||||
.detach();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remove_watcher(&mut self, expression: SharedString) {
|
||||
self.watchers.remove(&expression);
|
||||
}
|
||||
|
||||
pub fn variables(
|
||||
&mut self,
|
||||
variables_reference: VariableReference,
|
||||
|
@ -2191,6 +2249,7 @@ impl Session {
|
|||
|
||||
pub fn set_variable_value(
|
||||
&mut self,
|
||||
stack_frame_id: u64,
|
||||
variables_reference: u64,
|
||||
name: String,
|
||||
value: String,
|
||||
|
@ -2206,12 +2265,13 @@ impl Session {
|
|||
move |this, response, cx| {
|
||||
let response = response.log_err()?;
|
||||
this.invalidate_command_type::<VariablesCommand>();
|
||||
this.refresh_watchers(stack_frame_id, cx);
|
||||
cx.emit(SessionEvent::Variables);
|
||||
Some(response)
|
||||
},
|
||||
cx,
|
||||
)
|
||||
.detach()
|
||||
.detach();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue