[Lldb-commits] [lldb] [lldb-dap] Add clipboard context support (PR #170644)
Sergei Druzhkov via lldb-commits
lldb-commits at lists.llvm.org
Fri Dec 5 10:19:45 PST 2025
https://github.com/DrSergei updated https://github.com/llvm/llvm-project/pull/170644
>From 1c7e5cfcb144bd9ebcfc2ddaa2defe27305a6188 Mon Sep 17 00:00:00 2001
From: Druzhkov Sergei <serzhdruzhok at gmail.com>
Date: Thu, 4 Dec 2025 14:31:36 +0300
Subject: [PATCH 1/4] [lldb-dap] Add clipboard context support
---
lldb/include/lldb/API/SBValue.h | 2 +-
lldb/source/API/SBValue.cpp | 7 +++-
.../lldb-dap/evaluate/TestDAP_evaluate.py | 36 +++++++++++++++++++
lldb/tools/lldb-dap/Handler/RequestHandler.h | 3 +-
lldb/tools/lldb-dap/JSONUtils.cpp | 9 ++---
5 files changed, 50 insertions(+), 7 deletions(-)
diff --git a/lldb/include/lldb/API/SBValue.h b/lldb/include/lldb/API/SBValue.h
index 0f788ff602b70..91fa78b206cf2 100644
--- a/lldb/include/lldb/API/SBValue.h
+++ b/lldb/include/lldb/API/SBValue.h
@@ -318,7 +318,7 @@ class LLDB_API SBValue {
lldb::SBValue Persist();
- bool GetDescription(lldb::SBStream &description);
+ bool GetDescription(lldb::SBStream &description, bool short_mode = false);
bool GetExpressionPath(lldb::SBStream &description);
diff --git a/lldb/source/API/SBValue.cpp b/lldb/source/API/SBValue.cpp
index e300ecee3f8ac..5ff6f4dc301ea 100644
--- a/lldb/source/API/SBValue.cpp
+++ b/lldb/source/API/SBValue.cpp
@@ -1259,7 +1259,7 @@ lldb::SBValue SBValue::EvaluateExpression(const char *expr,
return result;
}
-bool SBValue::GetDescription(SBStream &description) {
+bool SBValue::GetDescription(SBStream &description, bool short_mode) {
LLDB_INSTRUMENT_VA(this, description);
Stream &strm = description.ref();
@@ -1270,6 +1270,11 @@ bool SBValue::GetDescription(SBStream &description) {
DumpValueObjectOptions options;
options.SetUseDynamicType(m_opaque_sp->GetUseDynamic());
options.SetUseSyntheticValue(m_opaque_sp->GetUseSynthetic());
+ if (short_mode) {
+ options.SetAllowOnelinerMode(true);
+ options.SetHideRootName(true);
+ options.SetHideRootType(true);
+ }
if (llvm::Error error = value_sp->Dump(strm, options)) {
strm << "error: " << toString(std::move(error));
return false;
diff --git a/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py b/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py
index 95573780e94bd..8e1ed858b1006 100644
--- a/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py
+++ b/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py
@@ -81,6 +81,9 @@ def assertEvaluateFailure(self, expression):
def isResultExpandedDescription(self):
return self.context == "repl"
+ def isResultShortDescription(self):
+ return self.context == "clipboard"
+
def isExpressionParsedExpected(self):
return self.context != "hover"
@@ -165,6 +168,25 @@ def run_test_evaluate_expressions(
want_type="my_struct *",
want_varref=True,
)
+ elif self.isResultShortDescription():
+ self.assertEvaluate(
+ "struct1",
+ "(foo = 15)",
+ want_type="my_struct",
+ want_varref=True,
+ )
+ self.assertEvaluate(
+ "struct2",
+ r"0x.*",
+ want_type="my_struct *",
+ want_varref=True,
+ )
+ self.assertEvaluate(
+ "struct3",
+ "nullptr",
+ want_type="my_struct *",
+ want_varref=True,
+ )
else:
self.assertEvaluate(
"struct1",
@@ -240,6 +262,13 @@ def run_test_evaluate_expressions(
want_type="my_struct",
want_varref=True,
)
+ elif self.isResultShortDescription():
+ self.assertEvaluate(
+ "struct1",
+ "(foo = 15)",
+ want_type="my_struct",
+ want_varref=True,
+ )
else:
self.assertEvaluate(
"struct1",
@@ -363,3 +392,10 @@ def test_variable_evaluate_expressions(self):
self.run_test_evaluate_expressions(
"variables", enableAutoVariableSummaries=True
)
+
+ @skipIfWindows
+ def test_variable_evaluate_expressions(self):
+ # Tests expression evaluations that are triggered when value copied in editor
+ self.run_test_evaluate_expressions(
+ "clipboard", enableAutoVariableSummaries=False
+ )
diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.h b/lldb/tools/lldb-dap/Handler/RequestHandler.h
index 5d235352b7738..a0da0c1c35014 100644
--- a/lldb/tools/lldb-dap/Handler/RequestHandler.h
+++ b/lldb/tools/lldb-dap/Handler/RequestHandler.h
@@ -301,7 +301,8 @@ class EvaluateRequestHandler
llvm::Expected<protocol::EvaluateResponseBody>
Run(const protocol::EvaluateArguments &) const override;
FeatureSet GetSupportedFeatures() const override {
- return {protocol::eAdapterFeatureEvaluateForHovers};
+ return {protocol::eAdapterFeatureEvaluateForHovers,
+ protocol::eAdapterFeatureClipboardContext};
}
};
diff --git a/lldb/tools/lldb-dap/JSONUtils.cpp b/lldb/tools/lldb-dap/JSONUtils.cpp
index 40b4f5b9f7f90..ea00eec8efdfc 100644
--- a/lldb/tools/lldb-dap/JSONUtils.cpp
+++ b/lldb/tools/lldb-dap/JSONUtils.cpp
@@ -825,9 +825,10 @@ VariableDescription::VariableDescription(lldb::SBValue v,
}
std::string VariableDescription::GetResult(protocol::EvaluateContext context) {
- // In repl context, the results can be displayed as multiple lines so more
- // detailed descriptions can be returned.
- if (context != protocol::eEvaluateContextRepl)
+ // In repl and clipboard contexts, the results can be displayed as multiple
+ // lines so more detailed descriptions can be returned.
+ if (context != protocol::eEvaluateContextRepl &&
+ context != protocol::eEvaluateContextClipboard)
return display_value;
if (!v.IsValid())
@@ -836,7 +837,7 @@ std::string VariableDescription::GetResult(protocol::EvaluateContext context) {
// Try the SBValue::GetDescription(), which may call into language runtime
// specific formatters (see ValueObjectPrinter).
lldb::SBStream stream;
- v.GetDescription(stream);
+ v.GetDescription(stream, context == protocol::eEvaluateContextClipboard);
llvm::StringRef description = stream.GetData();
return description.trim().str();
}
>From f64fb940bb5618c8bf116adfa37de686b9fd3c71 Mon Sep 17 00:00:00 2001
From: Druzhkov Sergei <serzhdruzhok at gmail.com>
Date: Fri, 5 Dec 2025 12:56:29 +0300
Subject: [PATCH 2/4] Add description level and update tests
---
lldb/include/lldb/API/SBValue.h | 5 +-
lldb/source/API/SBValue.cpp | 17 ++--
.../lldb-dap/evaluate/TestDAP_evaluate.py | 88 +++++++++++++++++--
lldb/tools/lldb-dap/JSONUtils.cpp | 5 +-
4 files changed, 101 insertions(+), 14 deletions(-)
diff --git a/lldb/include/lldb/API/SBValue.h b/lldb/include/lldb/API/SBValue.h
index 91fa78b206cf2..b87b3274efdaf 100644
--- a/lldb/include/lldb/API/SBValue.h
+++ b/lldb/include/lldb/API/SBValue.h
@@ -12,6 +12,7 @@
#include "lldb/API/SBData.h"
#include "lldb/API/SBDefines.h"
#include "lldb/API/SBType.h"
+#include "lldb/lldb-enumerations.h"
class ValueImpl;
class ValueLocker;
@@ -318,7 +319,9 @@ class LLDB_API SBValue {
lldb::SBValue Persist();
- bool GetDescription(lldb::SBStream &description, bool short_mode = false);
+ bool GetDescription(
+ lldb::SBStream &description,
+ lldb::DescriptionLevel description_level = eDescriptionLevelFull);
bool GetExpressionPath(lldb::SBStream &description);
diff --git a/lldb/source/API/SBValue.cpp b/lldb/source/API/SBValue.cpp
index 5ff6f4dc301ea..7a208023bc19f 100644
--- a/lldb/source/API/SBValue.cpp
+++ b/lldb/source/API/SBValue.cpp
@@ -45,6 +45,7 @@
#include "lldb/API/SBProcess.h"
#include "lldb/API/SBTarget.h"
#include "lldb/API/SBThread.h"
+#include "lldb/lldb-enumerations.h"
#include <memory>
@@ -1259,8 +1260,9 @@ lldb::SBValue SBValue::EvaluateExpression(const char *expr,
return result;
}
-bool SBValue::GetDescription(SBStream &description, bool short_mode) {
- LLDB_INSTRUMENT_VA(this, description);
+bool SBValue::GetDescription(SBStream &description,
+ lldb::DescriptionLevel description_level) {
+ LLDB_INSTRUMENT_VA(this, description, description_level);
Stream &strm = description.ref();
@@ -1268,12 +1270,17 @@ bool SBValue::GetDescription(SBStream &description, bool short_mode) {
lldb::ValueObjectSP value_sp(GetSP(locker));
if (value_sp) {
DumpValueObjectOptions options;
- options.SetUseDynamicType(m_opaque_sp->GetUseDynamic());
- options.SetUseSyntheticValue(m_opaque_sp->GetUseSynthetic());
- if (short_mode) {
+ if (description_level != eDescriptionLevelInitial) {
+ options.SetUseDynamicType(m_opaque_sp->GetUseDynamic());
+ options.SetUseSyntheticValue(m_opaque_sp->GetUseSynthetic());
+ }
+ if (description_level == eDescriptionLevelBrief) {
options.SetAllowOnelinerMode(true);
options.SetHideRootName(true);
options.SetHideRootType(true);
+ } else if (description_level == eDescriptionLevelVerbose) {
+ options.SetShowTypes(true);
+ options.SetShowLocation(true);
}
if (llvm::Error error = value_sp->Dump(strm, options)) {
strm << "error: " << toString(std::move(error));
diff --git a/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py b/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py
index 8e1ed858b1006..4bf3badfa0219 100644
--- a/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py
+++ b/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py
@@ -341,17 +341,91 @@ def run_test_evaluate_expressions(
# Now we check that values are updated after stepping
self.continue_to_breakpoint(breakpoint_4)
- self.assertEvaluate("my_vec", "size=2", want_varref=True)
+ if self.isResultExpandedDescription():
+ self.assertEvaluate(
+ "my_vec",
+ r"\(std::vector<int>\) \$\d+ = size=2 {\n \[0\] = 1\n \[1\] = 2\n}",
+ want_varref=True,
+ )
+ elif self.isResultShortDescription():
+ self.assertEvaluate(
+ "my_vec", r"size=2 {\n \[0\] = 1\n \[1\] = 2\n}", want_varref=True
+ )
+ else:
+ self.assertEvaluate("my_vec", "size=2", want_varref=True)
self.continue_to_breakpoint(breakpoint_5)
- self.assertEvaluate("my_vec", "size=3", want_varref=True)
+ if self.isResultExpandedDescription():
+ self.assertEvaluate(
+ "my_vec",
+ r"\(std::vector<int>\) \$\d+ = size=3 {\n \[0\] = 1\n \[1\] = 2\n \[2\] = 3\n}",
+ want_varref=True,
+ )
+ elif self.isResultShortDescription():
+ self.assertEvaluate(
+ "my_vec",
+ r"size=3 {\n \[0\] = 1\n \[1\] = 2\n \[2\] = 3\n}",
+ want_varref=True,
+ )
+ else:
+ self.assertEvaluate("my_vec", "size=3", want_varref=True)
- self.assertEvaluate("my_map", "size=2", want_varref=True)
+ if self.isResultExpandedDescription():
+ self.assertEvaluate(
+ "my_map",
+ r"\(std::map<int, int>\) \$\d+ = size=2 {\n \[0\] = \(first = 1, second = 2\)\n \[1\] = \(first = 2, second = 3\)\n}",
+ want_varref=True,
+ )
+ elif self.isResultShortDescription():
+ self.assertEvaluate(
+ "my_map",
+ r"size=2 {\n \[0\] = \(first = 1, second = 2\)\n \[1\] = \(first = 2, second = 3\)\n}",
+ want_varref=True,
+ )
+ else:
+ self.assertEvaluate("my_map", "size=2", want_varref=True)
self.continue_to_breakpoint(breakpoint_6)
- self.assertEvaluate("my_map", "size=3", want_varref=True)
+ if self.isResultExpandedDescription():
+ self.assertEvaluate(
+ "my_map",
+ r"\(std::map<int, int>\) \$\d+ = size=3 {\n \[0\] = \(first = 1, second = 2\)\n \[1\] = \(first = 2, second = 3\)\n \[2\] = \(first = 3, second = 4\)\n}",
+ want_varref=True,
+ )
+ elif self.isResultShortDescription():
+ self.assertEvaluate(
+ "my_map",
+ r"size=3 {\n \[0\] = \(first = 1, second = 2\)\n \[1\] = \(first = 2, second = 3\)\n \[2\] = \(first = 3, second = 4\)\n}",
+ want_varref=True,
+ )
+ else:
+ self.assertEvaluate("my_map", "size=3", want_varref=True)
- self.assertEvaluate("my_bool_vec", "size=1", want_varref=True)
+ if self.isResultExpandedDescription():
+ self.assertEvaluate(
+ "my_bool_vec",
+ r"\(std::vector<bool>\) \$\d+ = size=1 {\n \[0\] = true\n}",
+ want_varref=True,
+ )
+ elif self.isResultShortDescription():
+ self.assertEvaluate(
+ "my_bool_vec", r"size=1 {\n \[0\] = true\n}", want_varref=True
+ )
+ else:
+ self.assertEvaluate("my_bool_vec", "size=1", want_varref=True)
self.continue_to_breakpoint(breakpoint_7)
- self.assertEvaluate("my_bool_vec", "size=2", want_varref=True)
+ if self.isResultExpandedDescription():
+ self.assertEvaluate(
+ "my_bool_vec",
+ r"\(std::vector<bool>\) \$\d+ = size=2 {\n \[0\] = true\n \[1\] = false\n}",
+ want_varref=True,
+ )
+ elif self.isResultShortDescription():
+ self.assertEvaluate(
+ "my_bool_vec",
+ r"size=2 {\n \[0\] = true\n \[1\] = false\n}",
+ want_varref=True,
+ )
+ else:
+ self.assertEvaluate("my_bool_vec", "size=2", want_varref=True)
self.continue_to_breakpoint(breakpoint_8)
# Test memory read, especially with 'empty' repeat commands.
@@ -394,7 +468,7 @@ def test_variable_evaluate_expressions(self):
)
@skipIfWindows
- def test_variable_evaluate_expressions(self):
+ def test_clipboard_evaluate_expressions(self):
# Tests expression evaluations that are triggered when value copied in editor
self.run_test_evaluate_expressions(
"clipboard", enableAutoVariableSummaries=False
diff --git a/lldb/tools/lldb-dap/JSONUtils.cpp b/lldb/tools/lldb-dap/JSONUtils.cpp
index ea00eec8efdfc..f0d7113fbbcbe 100644
--- a/lldb/tools/lldb-dap/JSONUtils.cpp
+++ b/lldb/tools/lldb-dap/JSONUtils.cpp
@@ -837,7 +837,10 @@ std::string VariableDescription::GetResult(protocol::EvaluateContext context) {
// Try the SBValue::GetDescription(), which may call into language runtime
// specific formatters (see ValueObjectPrinter).
lldb::SBStream stream;
- v.GetDescription(stream, context == protocol::eEvaluateContextClipboard);
+ if (context == protocol::eEvaluateContextRepl)
+ v.GetDescription(stream, lldb::eDescriptionLevelFull);
+ else
+ v.GetDescription(stream, lldb::eDescriptionLevelBrief);
llvm::StringRef description = stream.GetData();
return description.trim().str();
}
>From 012c51a4f5ecc38e0a8e5c4f94ec842746a17a5f Mon Sep 17 00:00:00 2001
From: Druzhkov Sergei <serzhdruzhok at gmail.com>
Date: Fri, 5 Dec 2025 16:54:23 +0300
Subject: [PATCH 3/4] Add array test
---
.../lldb-dap/evaluate/TestDAP_evaluate.py | 19 +++++++++++++++++++
.../test/API/tools/lldb-dap/evaluate/main.cpp | 1 +
2 files changed, 20 insertions(+)
diff --git a/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py b/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py
index 4bf3badfa0219..2b4d5cb2108d8 100644
--- a/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py
+++ b/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py
@@ -438,6 +438,25 @@ def run_test_evaluate_expressions(
self.assertEvaluate("", ".* 14 .*\n", want_memref=False)
self.assertEvaluate("", ".* 19 .*\n", want_memref=False)
+ if self.isResultExpandedDescription():
+ self.assertEvaluate(
+ "my_longs",
+ r"\(long\[3\]\) \$\d+ = \(\[0\] = 5, \[1\] = 6, \[2\] = 7\)",
+ want_varref=True,
+ )
+ elif self.isResultShortDescription():
+ self.assertEvaluate(
+ "my_longs",
+ r"\(\[0\] = 5, \[1\] = 6, \[2\] = 7\)",
+ want_varref=True,
+ )
+ else:
+ self.assertEvaluate(
+ "my_longs",
+ "{5, 6, 7}" if enableAutoVariableSummaries else r"long\[3\] @ 0x",
+ want_varref=True,
+ )
+
self.continue_to_exit()
@skipIfWindows
diff --git a/lldb/test/API/tools/lldb-dap/evaluate/main.cpp b/lldb/test/API/tools/lldb-dap/evaluate/main.cpp
index 1c3d258114b1f..112726677637c 100644
--- a/lldb/test/API/tools/lldb-dap/evaluate/main.cpp
+++ b/lldb/test/API/tools/lldb-dap/evaluate/main.cpp
@@ -47,5 +47,6 @@ int main(int argc, char const *argv[]) {
my_bool_vec.push_back(true); // breakpoint 7
uint8_t my_ints[] = {5, 10, 15, 20, 25, 30};
+ long my_longs[] = {5, 6, 7};
return 0; // breakpoint 8
}
>From 1fc41084f57bffaf777e65c3eaa7022daeaa68db Mon Sep 17 00:00:00 2001
From: Druzhkov Sergei <serzhdruzhok at gmail.com>
Date: Fri, 5 Dec 2025 21:19:27 +0300
Subject: [PATCH 4/4] Fix review comments
---
lldb/include/lldb/API/SBValue.h | 8 ++++----
lldb/source/API/SBValue.cpp | 5 +++++
.../tools/lldb-dap/evaluate/TestDAP_evaluate.py | 15 +--------------
3 files changed, 10 insertions(+), 18 deletions(-)
diff --git a/lldb/include/lldb/API/SBValue.h b/lldb/include/lldb/API/SBValue.h
index b87b3274efdaf..dead11fba19fe 100644
--- a/lldb/include/lldb/API/SBValue.h
+++ b/lldb/include/lldb/API/SBValue.h
@@ -12,7 +12,6 @@
#include "lldb/API/SBData.h"
#include "lldb/API/SBDefines.h"
#include "lldb/API/SBType.h"
-#include "lldb/lldb-enumerations.h"
class ValueImpl;
class ValueLocker;
@@ -319,9 +318,10 @@ class LLDB_API SBValue {
lldb::SBValue Persist();
- bool GetDescription(
- lldb::SBStream &description,
- lldb::DescriptionLevel description_level = eDescriptionLevelFull);
+ bool GetDescription(lldb::SBStream &description);
+
+ bool GetDescription(lldb::SBStream &description,
+ lldb::DescriptionLevel description_level);
bool GetExpressionPath(lldb::SBStream &description);
diff --git a/lldb/source/API/SBValue.cpp b/lldb/source/API/SBValue.cpp
index 7a208023bc19f..67ef407ed3f9c 100644
--- a/lldb/source/API/SBValue.cpp
+++ b/lldb/source/API/SBValue.cpp
@@ -1260,6 +1260,11 @@ lldb::SBValue SBValue::EvaluateExpression(const char *expr,
return result;
}
+bool SBValue::GetDescription(SBStream &description) {
+ LLDB_INSTRUMENT_VA(this, description);
+ return GetDescription(description, eDescriptionLevelFull);
+}
+
bool SBValue::GetDescription(SBStream &description,
lldb::DescriptionLevel description_level) {
LLDB_INSTRUMENT_VA(this, description, description_level);
diff --git a/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py b/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py
index 2b4d5cb2108d8..58f89972d302d 100644
--- a/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py
+++ b/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py
@@ -384,20 +384,7 @@ def run_test_evaluate_expressions(
else:
self.assertEvaluate("my_map", "size=2", want_varref=True)
self.continue_to_breakpoint(breakpoint_6)
- if self.isResultExpandedDescription():
- self.assertEvaluate(
- "my_map",
- r"\(std::map<int, int>\) \$\d+ = size=3 {\n \[0\] = \(first = 1, second = 2\)\n \[1\] = \(first = 2, second = 3\)\n \[2\] = \(first = 3, second = 4\)\n}",
- want_varref=True,
- )
- elif self.isResultShortDescription():
- self.assertEvaluate(
- "my_map",
- r"size=3 {\n \[0\] = \(first = 1, second = 2\)\n \[1\] = \(first = 2, second = 3\)\n \[2\] = \(first = 3, second = 4\)\n}",
- want_varref=True,
- )
- else:
- self.assertEvaluate("my_map", "size=3", want_varref=True)
+ self.assertEvaluate("my_map", "size=3", want_varref=True)
if self.isResultExpandedDescription():
self.assertEvaluate(
More information about the lldb-commits
mailing list