[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 03:20:46 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/2] [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/2] 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();
 }



More information about the lldb-commits mailing list