debugger: Copy full variable value instead of trimmed preview (#63902)
# Objective Fixes #63869 — "Copy Value" in the debugger panel copied the trimmed display preview instead of the full value. ## Solution Evaluate the variable's `evaluateName` via the DAP `evaluate` request using the `clipboard` context (falling back to `variables`), and copy the returned result. Falls back to the display value if evaluation fails. ## Testing - `cargo test -p debugger_ui --lib` passes. --- Release Notes: - Fixed debugger "Copy Value" copying the truncated preview instead of the full value.
This commit is contained in:
3 files changed
+110
-6
No files matched your search
@@ -889,13 +889,30 @@ impl VariableList {
|
||||
return;
|
||||
};
|
||||
|
||||
let variable_value = match &entry.entry {
|
||||
DapEntry::Variable(dap) => dap.value.clone(),
|
||||
DapEntry::Watcher(watcher) => watcher.value.to_string(),
|
||||
match &entry.entry {
|
||||
DapEntry::Variable(dap) => {
|
||||
let fallback_value = dap.value.clone();
|
||||
let expression = dap
|
||||
.evaluate_name
|
||||
.clone()
|
||||
.unwrap_or_else(|| dap.name.clone());
|
||||
let frame_id = self.selected_stack_frame_id;
|
||||
let task = self.session.update(cx, |session, cx| {
|
||||
session.evaluate_variable_value(expression, frame_id, cx)
|
||||
});
|
||||
cx.spawn(async move |_, cx| {
|
||||
let value = task.await.unwrap_or(fallback_value);
|
||||
cx.update(|cx| {
|
||||
cx.write_to_clipboard(ClipboardItem::new_string(value));
|
||||
});
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
DapEntry::Watcher(watcher) => {
|
||||
cx.write_to_clipboard(ClipboardItem::new_string(watcher.value.to_string()));
|
||||
}
|
||||
DapEntry::Scope(_) => return,
|
||||
};
|
||||
|
||||
cx.write_to_clipboard(ClipboardItem::new_string(variable_value));
|
||||
}
|
||||
}
|
||||
|
||||
fn edit_variable(&mut self, _: &EditVariable, window: &mut Window, cx: &mut Context<Self>) {
|
||||
|
||||
@@ -2592,3 +2592,58 @@ async fn test_refresh_watchers(executor: BackgroundExecutor, cx: &mut TestAppCon
|
||||
assert_eq!(3, watcher.variables_reference);
|
||||
});
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_evaluate_variable_value_uses_clipboard_context(
|
||||
executor: BackgroundExecutor,
|
||||
cx: &mut TestAppContext,
|
||||
) {
|
||||
init_test(cx);
|
||||
|
||||
let fs = FakeFs::new(executor.clone());
|
||||
fs.insert_tree(
|
||||
path!("/project"),
|
||||
json!({
|
||||
"index.js": "const debug = { foo: 1 };",
|
||||
}),
|
||||
)
|
||||
.await;
|
||||
|
||||
let project = Project::test(fs, [path!("/project").as_ref()], cx).await;
|
||||
let workspace = init_test_workspace(&project, cx).await;
|
||||
let cx = &mut VisualTestContext::from_window(*workspace, cx);
|
||||
let session = start_debug_session(&workspace, cx, |client| {
|
||||
client.on_request::<Initialize, _>(move |_, _| {
|
||||
Ok(dap::Capabilities {
|
||||
supports_clipboard_context: Some(true),
|
||||
..Default::default()
|
||||
})
|
||||
});
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let client = session.update(cx, |session, _| session.adapter_client().unwrap());
|
||||
client.on_request::<Evaluate, _>(move |_, args| {
|
||||
assert_eq!("debug", args.expression);
|
||||
assert_eq!(Some(dap::EvaluateArgumentsContext::Clipboard), args.context);
|
||||
|
||||
Ok(dap::EvaluateResponse {
|
||||
result: "full value".to_string(),
|
||||
type_: None,
|
||||
presentation_hint: None,
|
||||
variables_reference: 0,
|
||||
named_variables: None,
|
||||
indexed_variables: None,
|
||||
memory_reference: None,
|
||||
value_location_reference: None,
|
||||
})
|
||||
});
|
||||
|
||||
let value = session
|
||||
.update(cx, |session, cx| {
|
||||
session.evaluate_variable_value("debug".to_string(), Some(1), cx)
|
||||
})
|
||||
.await;
|
||||
|
||||
assert_eq!(Some("full value".to_string()), value);
|
||||
}
|
||||
@@ -2863,6 +2863,38 @@ impl Session {
|
||||
})
|
||||
}
|
||||
|
||||
/// Evaluates an expression to obtain its full value for copying.
|
||||
pub fn evaluate_variable_value(
|
||||
&mut self,
|
||||
expression: String,
|
||||
frame_id: Option<u64>,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Task<Option<String>> {
|
||||
let context = if self
|
||||
.capabilities
|
||||
.supports_clipboard_context
|
||||
.unwrap_or_default()
|
||||
{
|
||||
EvaluateArgumentsContext::Clipboard
|
||||
} else {
|
||||
EvaluateArgumentsContext::Variables
|
||||
};
|
||||
let request = self.request(
|
||||
EvaluateCommand {
|
||||
expression,
|
||||
frame_id,
|
||||
context: Some(context),
|
||||
source: None,
|
||||
},
|
||||
|_, response, _| response.ok(),
|
||||
cx,
|
||||
);
|
||||
cx.background_spawn(async move {
|
||||
let response = request.await?;
|
||||
Some(response.result)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn location(
|
||||
&mut self,
|
||||
reference: u64,
|
||||
|
||||
Reference in new issue
Block a user