[clang-tools-extra] [clangd] Support go-to-definition on type hints. The protocol part (PR #85497)
Younan Zhang via cfe-commits
cfe-commits at lists.llvm.org
Sat Mar 16 08:29:04 PDT 2024
https://github.com/zyn0217 updated https://github.com/llvm/llvm-project/pull/85497
>From 6d61aa1e43bb522412904bdd77c7f1cfc4b42889 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Sat, 16 Mar 2024 12:33:58 +0800
Subject: [PATCH 1/3] [clangd] Support go-to-definition on type hints. The
protocol part
This is in preparation for implementing go-to-definition support
on type inlay hints, switching the label field within the InlayHint
protocol to an array of InlayHintLabelPart.
---
clang-tools-extra/clangd/ClangdLSPServer.cpp | 2 +-
clang-tools-extra/clangd/InlayHints.cpp | 8 +++-
clang-tools-extra/clangd/Protocol.cpp | 40 +++++++++++++++++-
clang-tools-extra/clangd/Protocol.h | 41 ++++++++++++++++++-
clang-tools-extra/clangd/test/inlayHints.test | 6 ++-
clang-tools-extra/clangd/tool/Check.cpp | 8 +++-
.../clangd/unittests/InlayHintTests.cpp | 9 ++--
7 files changed, 103 insertions(+), 11 deletions(-)
diff --git a/clang-tools-extra/clangd/ClangdLSPServer.cpp b/clang-tools-extra/clangd/ClangdLSPServer.cpp
index f29dadde2b86d5..7fd599d4e1a0b0 100644
--- a/clang-tools-extra/clangd/ClangdLSPServer.cpp
+++ b/clang-tools-extra/clangd/ClangdLSPServer.cpp
@@ -1390,7 +1390,7 @@ void ClangdLSPServer::onClangdInlayHints(const InlayHintsParams &Params,
// Extension doesn't have paddingLeft/Right so adjust the label
// accordingly.
{"label",
- ((Hint.paddingLeft ? " " : "") + llvm::StringRef(Hint.label) +
+ ((Hint.paddingLeft ? " " : "") + llvm::StringRef(Hint.joinLabels()) +
(Hint.paddingRight ? " " : ""))
.str()},
});
diff --git a/clang-tools-extra/clangd/InlayHints.cpp b/clang-tools-extra/clangd/InlayHints.cpp
index a0ebc631ef8285..5a9ec9ab6762fa 100644
--- a/clang-tools-extra/clangd/InlayHints.cpp
+++ b/clang-tools-extra/clangd/InlayHints.cpp
@@ -977,8 +977,12 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
return;
bool PadLeft = Prefix.consume_front(" ");
bool PadRight = Suffix.consume_back(" ");
- Results.push_back(InlayHint{LSPPos, (Prefix + Label + Suffix).str(), Kind,
- PadLeft, PadRight, LSPRange});
+ Results.push_back(InlayHint{LSPPos,
+ /*label=*/{(Prefix + Label + Suffix).str()},
+ Kind,
+ PadLeft,
+ PadRight,
+ LSPRange});
}
// Get the range of the main file that *exactly* corresponds to R.
diff --git a/clang-tools-extra/clangd/Protocol.cpp b/clang-tools-extra/clangd/Protocol.cpp
index 8aa18bb0058abe..b1e29953337256 100644
--- a/clang-tools-extra/clangd/Protocol.cpp
+++ b/clang-tools-extra/clangd/Protocol.cpp
@@ -1483,9 +1483,18 @@ llvm::json::Value toJSON(const InlayHintKind &Kind) {
llvm_unreachable("Unknown clang.clangd.InlayHintKind");
}
+namespace {
+
+llvm::json::Array toJSON(const std::vector<InlayHintLabelPart> &Labels) {
+ return llvm::json::Array{
+ llvm::map_range(Labels, [](auto &Label) { return toJSON(Label); })};
+}
+
+} // namespace
+
llvm::json::Value toJSON(const InlayHint &H) {
llvm::json::Object Result{{"position", H.position},
- {"label", H.label},
+ {"label", toJSON(H.label)},
{"paddingLeft", H.paddingLeft},
{"paddingRight", H.paddingRight}};
auto K = toJSON(H.kind);
@@ -1501,6 +1510,10 @@ bool operator<(const InlayHint &A, const InlayHint &B) {
return std::tie(A.position, A.range, A.kind, A.label) <
std::tie(B.position, B.range, B.kind, B.label);
}
+std::string InlayHint::joinLabels() const {
+ return llvm::join(llvm::map_range(label, [](auto &L) { return L.value; }),
+ "");
+}
llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, InlayHintKind Kind) {
auto ToString = [](InlayHintKind K) {
@@ -1519,6 +1532,31 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, InlayHintKind Kind) {
return OS << ToString(Kind);
}
+llvm::json::Value toJSON(const InlayHintLabelPart &L) {
+ llvm::json::Object Result{{"value", L.value}};
+ if (L.tooltip)
+ Result["tooltip"] = *L.tooltip;
+ if (L.location)
+ Result["location"] = *L.location;
+ return Result;
+}
+
+bool operator==(const InlayHintLabelPart &LHS, const InlayHintLabelPart &RHS) {
+ return std::tie(LHS.value, LHS.location) == std::tie(RHS.value, RHS.location);
+}
+
+bool operator<(const InlayHintLabelPart &LHS, const InlayHintLabelPart &RHS) {
+ return std::tie(LHS.value, LHS.location) < std::tie(RHS.value, RHS.location);
+}
+
+llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
+ const InlayHintLabelPart &L) {
+ OS << L.value;
+ if (L.location)
+ OS << " (" << L.location << ")";
+ return OS;
+}
+
static const char *toString(OffsetEncoding OE) {
switch (OE) {
case OffsetEncoding::UTF8:
diff --git a/clang-tools-extra/clangd/Protocol.h b/clang-tools-extra/clangd/Protocol.h
index 358d4c6feaf87d..1f9c5625521b63 100644
--- a/clang-tools-extra/clangd/Protocol.h
+++ b/clang-tools-extra/clangd/Protocol.h
@@ -1689,6 +1689,42 @@ enum class InlayHintKind {
};
llvm::json::Value toJSON(const InlayHintKind &);
+/// An inlay hint label part allows for interactive and composite labels
+/// of inlay hints.
+struct InlayHintLabelPart {
+
+ InlayHintLabelPart(std::string value,
+ std::optional<Location> location = std::nullopt)
+ : value(std::move(value)), location(std::move(location)) {}
+
+ /// The value of this label part.
+ std::string value;
+
+ /// The tooltip text when you hover over this label part. Depending on
+ /// the client capability `inlayHint.resolveSupport`, clients might resolve
+ /// this property late using the resolve request.
+ std::optional<MarkupContent> tooltip;
+
+ /// An optional source code location that represents this
+ /// label part.
+ ///
+ /// The editor will use this location for the hover and for code navigation
+ /// features: This part will become a clickable link that resolves to the
+ /// definition of the symbol at the given location (not necessarily the
+ /// location itself), it shows the hover that shows at the given location,
+ /// and it shows a context menu with further code navigation commands.
+ ///
+ /// Depending on the client capability `inlayHint.resolveSupport` clients
+ /// might resolve this property late using the resolve request.
+ std::optional<Location> location;
+
+ /// The command field is not used for now, hence omitted.
+};
+llvm::json::Value toJSON(const InlayHintLabelPart &);
+bool operator==(const InlayHintLabelPart &, const InlayHintLabelPart &);
+bool operator<(const InlayHintLabelPart &, const InlayHintLabelPart &);
+llvm::raw_ostream &operator<<(llvm::raw_ostream &, const InlayHintLabelPart &);
+
/// Inlay hint information.
struct InlayHint {
/// The position of this hint.
@@ -1698,7 +1734,7 @@ struct InlayHint {
/// InlayHintLabelPart label parts.
///
/// *Note* that neither the string nor the label part can be empty.
- std::string label;
+ std::vector<InlayHintLabelPart> label;
/// The kind of this hint. Can be omitted in which case the client should fall
/// back to a reasonable default.
@@ -1724,6 +1760,9 @@ struct InlayHint {
/// The range allows clients more flexibility of when/how to display the hint.
/// This is an (unserialized) clangd extension.
Range range;
+
+ /// Join the label[].value together.
+ std::string joinLabels() const;
};
llvm::json::Value toJSON(const InlayHint &);
bool operator==(const InlayHint &, const InlayHint &);
diff --git a/clang-tools-extra/clangd/test/inlayHints.test b/clang-tools-extra/clangd/test/inlayHints.test
index 8f302dd17a5494..e5b3c0fb0b960a 100644
--- a/clang-tools-extra/clangd/test/inlayHints.test
+++ b/clang-tools-extra/clangd/test/inlayHints.test
@@ -51,7 +51,11 @@
# CHECK-NEXT: "result": [
# CHECK-NEXT: {
# CHECK-NEXT: "kind": 2,
-# CHECK-NEXT: "label": "bar:",
+# CHECK-NEXT: "label": [
+# CHECK-NEXT: {
+# CHECK-NEXT: "value": "bar:"
+# CHECK-NEXT: }
+# CHECK-NEXT: ],
# CHECK-NEXT: "paddingLeft": false,
# CHECK-NEXT: "paddingRight": true,
# CHECK-NEXT: "position": {
diff --git a/clang-tools-extra/clangd/tool/Check.cpp b/clang-tools-extra/clangd/tool/Check.cpp
index b5c4d145619df3..a4d53b7fc91321 100644
--- a/clang-tools-extra/clangd/tool/Check.cpp
+++ b/clang-tools-extra/clangd/tool/Check.cpp
@@ -367,7 +367,13 @@ class Checker {
auto Hints = inlayHints(*AST, LineRange);
for (const auto &Hint : Hints) {
- vlog(" {0} {1} {2}", Hint.kind, Hint.position, Hint.label);
+ vlog(" {0} {1} [{2}]", Hint.kind, Hint.position, [&] {
+ return llvm::join(llvm::map_range(Hint.label,
+ [&](auto &L) {
+ return llvm::formatv("{{{0}}", L);
+ }),
+ ", ");
+ }());
}
}
diff --git a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
index 0fff0dfca6c9b8..5b1531eb2fa60b 100644
--- a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
+++ b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
@@ -25,7 +25,7 @@ namespace clangd {
llvm::raw_ostream &operator<<(llvm::raw_ostream &Stream,
const InlayHint &Hint) {
- return Stream << Hint.label << "@" << Hint.range;
+ return Stream << Hint.joinLabels() << "@" << Hint.range;
}
namespace {
@@ -57,10 +57,11 @@ struct ExpectedHint {
MATCHER_P2(HintMatcher, Expected, Code, llvm::to_string(Expected)) {
llvm::StringRef ExpectedView(Expected.Label);
- if (arg.label != ExpectedView.trim(" ") ||
+ std::string ResultLabel = arg.joinLabels();
+ if (ResultLabel != ExpectedView.trim(" ") ||
arg.paddingLeft != ExpectedView.starts_with(" ") ||
arg.paddingRight != ExpectedView.ends_with(" ")) {
- *result_listener << "label is '" << arg.label << "'";
+ *result_listener << "label is '" << ResultLabel << "'";
return false;
}
if (arg.range != Code.range(Expected.RangeName)) {
@@ -72,7 +73,7 @@ MATCHER_P2(HintMatcher, Expected, Code, llvm::to_string(Expected)) {
return true;
}
-MATCHER_P(labelIs, Label, "") { return arg.label == Label; }
+MATCHER_P(labelIs, Label, "") { return arg.joinLabels() == Label; }
Config noHintsConfig() {
Config C;
>From 383b751ca9332f6245d00ca92b068d3b05470b63 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Sat, 16 Mar 2024 12:47:30 +0800
Subject: [PATCH 2/3] Format
---
clang-tools-extra/clangd/InlayHints.cpp | 5 +----
clang-tools-extra/clangd/Protocol.h | 26 ++++++++++++-------------
2 files changed, 14 insertions(+), 17 deletions(-)
diff --git a/clang-tools-extra/clangd/InlayHints.cpp b/clang-tools-extra/clangd/InlayHints.cpp
index 5a9ec9ab6762fa..cd4f1931b3ce1d 100644
--- a/clang-tools-extra/clangd/InlayHints.cpp
+++ b/clang-tools-extra/clangd/InlayHints.cpp
@@ -979,10 +979,7 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
bool PadRight = Suffix.consume_back(" ");
Results.push_back(InlayHint{LSPPos,
/*label=*/{(Prefix + Label + Suffix).str()},
- Kind,
- PadLeft,
- PadRight,
- LSPRange});
+ Kind, PadLeft, PadRight, LSPRange});
}
// Get the range of the main file that *exactly* corresponds to R.
diff --git a/clang-tools-extra/clangd/Protocol.h b/clang-tools-extra/clangd/Protocol.h
index 1f9c5625521b63..6fd6b9955bfb91 100644
--- a/clang-tools-extra/clangd/Protocol.h
+++ b/clang-tools-extra/clangd/Protocol.h
@@ -1701,21 +1701,21 @@ struct InlayHintLabelPart {
std::string value;
/// The tooltip text when you hover over this label part. Depending on
- /// the client capability `inlayHint.resolveSupport`, clients might resolve
- /// this property late using the resolve request.
+ /// the client capability `inlayHint.resolveSupport`, clients might resolve
+ /// this property late using the resolve request.
std::optional<MarkupContent> tooltip;
- /// An optional source code location that represents this
- /// label part.
- ///
- /// The editor will use this location for the hover and for code navigation
- /// features: This part will become a clickable link that resolves to the
- /// definition of the symbol at the given location (not necessarily the
- /// location itself), it shows the hover that shows at the given location,
- /// and it shows a context menu with further code navigation commands.
- ///
- /// Depending on the client capability `inlayHint.resolveSupport` clients
- /// might resolve this property late using the resolve request.
+ /// An optional source code location that represents this
+ /// label part.
+ ///
+ /// The editor will use this location for the hover and for code navigation
+ /// features: This part will become a clickable link that resolves to the
+ /// definition of the symbol at the given location (not necessarily the
+ /// location itself), it shows the hover that shows at the given location,
+ /// and it shows a context menu with further code navigation commands.
+ ///
+ /// Depending on the client capability `inlayHint.resolveSupport` clients
+ /// might resolve this property late using the resolve request.
std::optional<Location> location;
/// The command field is not used for now, hence omitted.
>From 5fc28c72b28c832874598c9b6212c0d38e87d75c Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Sat, 16 Mar 2024 23:28:43 +0800
Subject: [PATCH 3/3] Add 'command' field
---
clang-tools-extra/clangd/Protocol.cpp | 2 ++
clang-tools-extra/clangd/Protocol.h | 8 +++++++-
2 files changed, 9 insertions(+), 1 deletion(-)
diff --git a/clang-tools-extra/clangd/Protocol.cpp b/clang-tools-extra/clangd/Protocol.cpp
index b1e29953337256..e09c667b28d6c7 100644
--- a/clang-tools-extra/clangd/Protocol.cpp
+++ b/clang-tools-extra/clangd/Protocol.cpp
@@ -1538,6 +1538,8 @@ llvm::json::Value toJSON(const InlayHintLabelPart &L) {
Result["tooltip"] = *L.tooltip;
if (L.location)
Result["location"] = *L.location;
+ if (L.command)
+ Result["command"] = *L.command;
return Result;
}
diff --git a/clang-tools-extra/clangd/Protocol.h b/clang-tools-extra/clangd/Protocol.h
index 6fd6b9955bfb91..cf2fd278264bf6 100644
--- a/clang-tools-extra/clangd/Protocol.h
+++ b/clang-tools-extra/clangd/Protocol.h
@@ -1693,6 +1693,8 @@ llvm::json::Value toJSON(const InlayHintKind &);
/// of inlay hints.
struct InlayHintLabelPart {
+ InlayHintLabelPart() = default;
+
InlayHintLabelPart(std::string value,
std::optional<Location> location = std::nullopt)
: value(std::move(value)), location(std::move(location)) {}
@@ -1718,7 +1720,11 @@ struct InlayHintLabelPart {
/// might resolve this property late using the resolve request.
std::optional<Location> location;
- /// The command field is not used for now, hence omitted.
+ /// An optional command for this label part.
+ ///
+ /// Depending on the client capability `inlayHint.resolveSupport` clients
+ /// might resolve this property late using the resolve request.
+ std::optional<Command> command;
};
llvm::json::Value toJSON(const InlayHintLabelPart &);
bool operator==(const InlayHintLabelPart &, const InlayHintLabelPart &);
More information about the cfe-commits
mailing list