Skip to content

Commit c6a65e4

Browse files
authored
[clangd] Support go-to-definition on type hints. The protocol part (#85497)
This is in preparation for implementing go-to-definition support on type inlay hints, switching the `label` field within the InlayHint protocol from a string to an array of `InlayHintLabelPart`.
1 parent 64a7114 commit c6a65e4

File tree

7 files changed

+98
-10
lines changed

7 files changed

+98
-10
lines changed

clang-tools-extra/clangd/ClangdLSPServer.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1390,7 +1390,7 @@ void ClangdLSPServer::onClangdInlayHints(const InlayHintsParams &Params,
13901390
// Extension doesn't have paddingLeft/Right so adjust the label
13911391
// accordingly.
13921392
{"label",
1393-
((Hint.paddingLeft ? " " : "") + llvm::StringRef(Hint.label) +
1393+
((Hint.paddingLeft ? " " : "") + llvm::StringRef(Hint.joinLabels()) +
13941394
(Hint.paddingRight ? " " : ""))
13951395
.str()},
13961396
});

clang-tools-extra/clangd/InlayHints.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -977,8 +977,9 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
977977
return;
978978
bool PadLeft = Prefix.consume_front(" ");
979979
bool PadRight = Suffix.consume_back(" ");
980-
Results.push_back(InlayHint{LSPPos, (Prefix + Label + Suffix).str(), Kind,
981-
PadLeft, PadRight, LSPRange});
980+
Results.push_back(InlayHint{LSPPos,
981+
/*label=*/{(Prefix + Label + Suffix).str()},
982+
Kind, PadLeft, PadRight, LSPRange});
982983
}
983984

984985
// Get the range of the main file that *exactly* corresponds to R.

clang-tools-extra/clangd/Protocol.cpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1501,6 +1501,10 @@ bool operator<(const InlayHint &A, const InlayHint &B) {
15011501
return std::tie(A.position, A.range, A.kind, A.label) <
15021502
std::tie(B.position, B.range, B.kind, B.label);
15031503
}
1504+
std::string InlayHint::joinLabels() const {
1505+
return llvm::join(llvm::map_range(label, [](auto &L) { return L.value; }),
1506+
"");
1507+
}
15041508

15051509
llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, InlayHintKind Kind) {
15061510
auto ToString = [](InlayHintKind K) {
@@ -1519,6 +1523,33 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, InlayHintKind Kind) {
15191523
return OS << ToString(Kind);
15201524
}
15211525

1526+
llvm::json::Value toJSON(const InlayHintLabelPart &L) {
1527+
llvm::json::Object Result{{"value", L.value}};
1528+
if (L.tooltip)
1529+
Result["tooltip"] = *L.tooltip;
1530+
if (L.location)
1531+
Result["location"] = *L.location;
1532+
if (L.command)
1533+
Result["command"] = *L.command;
1534+
return Result;
1535+
}
1536+
1537+
bool operator==(const InlayHintLabelPart &LHS, const InlayHintLabelPart &RHS) {
1538+
return std::tie(LHS.value, LHS.location) == std::tie(RHS.value, RHS.location);
1539+
}
1540+
1541+
bool operator<(const InlayHintLabelPart &LHS, const InlayHintLabelPart &RHS) {
1542+
return std::tie(LHS.value, LHS.location) < std::tie(RHS.value, RHS.location);
1543+
}
1544+
1545+
llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
1546+
const InlayHintLabelPart &L) {
1547+
OS << L.value;
1548+
if (L.location)
1549+
OS << " (" << L.location << ")";
1550+
return OS;
1551+
}
1552+
15221553
static const char *toString(OffsetEncoding OE) {
15231554
switch (OE) {
15241555
case OffsetEncoding::UTF8:

clang-tools-extra/clangd/Protocol.h

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1689,6 +1689,48 @@ enum class InlayHintKind {
16891689
};
16901690
llvm::json::Value toJSON(const InlayHintKind &);
16911691

1692+
/// An inlay hint label part allows for interactive and composite labels
1693+
/// of inlay hints.
1694+
struct InlayHintLabelPart {
1695+
1696+
InlayHintLabelPart() = default;
1697+
1698+
InlayHintLabelPart(std::string value,
1699+
std::optional<Location> location = std::nullopt)
1700+
: value(std::move(value)), location(std::move(location)) {}
1701+
1702+
/// The value of this label part.
1703+
std::string value;
1704+
1705+
/// The tooltip text when you hover over this label part. Depending on
1706+
/// the client capability `inlayHint.resolveSupport`, clients might resolve
1707+
/// this property late using the resolve request.
1708+
std::optional<MarkupContent> tooltip;
1709+
1710+
/// An optional source code location that represents this
1711+
/// label part.
1712+
///
1713+
/// The editor will use this location for the hover and for code navigation
1714+
/// features: This part will become a clickable link that resolves to the
1715+
/// definition of the symbol at the given location (not necessarily the
1716+
/// location itself), it shows the hover that shows at the given location,
1717+
/// and it shows a context menu with further code navigation commands.
1718+
///
1719+
/// Depending on the client capability `inlayHint.resolveSupport` clients
1720+
/// might resolve this property late using the resolve request.
1721+
std::optional<Location> location;
1722+
1723+
/// An optional command for this label part.
1724+
///
1725+
/// Depending on the client capability `inlayHint.resolveSupport` clients
1726+
/// might resolve this property late using the resolve request.
1727+
std::optional<Command> command;
1728+
};
1729+
llvm::json::Value toJSON(const InlayHintLabelPart &);
1730+
bool operator==(const InlayHintLabelPart &, const InlayHintLabelPart &);
1731+
bool operator<(const InlayHintLabelPart &, const InlayHintLabelPart &);
1732+
llvm::raw_ostream &operator<<(llvm::raw_ostream &, const InlayHintLabelPart &);
1733+
16921734
/// Inlay hint information.
16931735
struct InlayHint {
16941736
/// The position of this hint.
@@ -1698,7 +1740,7 @@ struct InlayHint {
16981740
/// InlayHintLabelPart label parts.
16991741
///
17001742
/// *Note* that neither the string nor the label part can be empty.
1701-
std::string label;
1743+
std::vector<InlayHintLabelPart> label;
17021744

17031745
/// The kind of this hint. Can be omitted in which case the client should fall
17041746
/// back to a reasonable default.
@@ -1724,6 +1766,9 @@ struct InlayHint {
17241766
/// The range allows clients more flexibility of when/how to display the hint.
17251767
/// This is an (unserialized) clangd extension.
17261768
Range range;
1769+
1770+
/// Join the label[].value together.
1771+
std::string joinLabels() const;
17271772
};
17281773
llvm::json::Value toJSON(const InlayHint &);
17291774
bool operator==(const InlayHint &, const InlayHint &);

clang-tools-extra/clangd/test/inlayHints.test

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,11 @@
5151
# CHECK-NEXT: "result": [
5252
# CHECK-NEXT: {
5353
# CHECK-NEXT: "kind": 2,
54-
# CHECK-NEXT: "label": "bar:",
54+
# CHECK-NEXT: "label": [
55+
# CHECK-NEXT: {
56+
# CHECK-NEXT: "value": "bar:"
57+
# CHECK-NEXT: }
58+
# CHECK-NEXT: ],
5559
# CHECK-NEXT: "paddingLeft": false,
5660
# CHECK-NEXT: "paddingRight": true,
5761
# CHECK-NEXT: "position": {

clang-tools-extra/clangd/tool/Check.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -367,7 +367,13 @@ class Checker {
367367
auto Hints = inlayHints(*AST, LineRange);
368368

369369
for (const auto &Hint : Hints) {
370-
vlog(" {0} {1} {2}", Hint.kind, Hint.position, Hint.label);
370+
vlog(" {0} {1} [{2}]", Hint.kind, Hint.position, [&] {
371+
return llvm::join(llvm::map_range(Hint.label,
372+
[&](auto &L) {
373+
return llvm::formatv("{{{0}}", L);
374+
}),
375+
", ");
376+
}());
371377
}
372378
}
373379

clang-tools-extra/clangd/unittests/InlayHintTests.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ namespace clangd {
2525

2626
llvm::raw_ostream &operator<<(llvm::raw_ostream &Stream,
2727
const InlayHint &Hint) {
28-
return Stream << Hint.label << "@" << Hint.range;
28+
return Stream << Hint.joinLabels() << "@" << Hint.range;
2929
}
3030

3131
namespace {
@@ -57,10 +57,11 @@ struct ExpectedHint {
5757

5858
MATCHER_P2(HintMatcher, Expected, Code, llvm::to_string(Expected)) {
5959
llvm::StringRef ExpectedView(Expected.Label);
60-
if (arg.label != ExpectedView.trim(" ") ||
60+
std::string ResultLabel = arg.joinLabels();
61+
if (ResultLabel != ExpectedView.trim(" ") ||
6162
arg.paddingLeft != ExpectedView.starts_with(" ") ||
6263
arg.paddingRight != ExpectedView.ends_with(" ")) {
63-
*result_listener << "label is '" << arg.label << "'";
64+
*result_listener << "label is '" << ResultLabel << "'";
6465
return false;
6566
}
6667
if (arg.range != Code.range(Expected.RangeName)) {
@@ -72,7 +73,7 @@ MATCHER_P2(HintMatcher, Expected, Code, llvm::to_string(Expected)) {
7273
return true;
7374
}
7475

75-
MATCHER_P(labelIs, Label, "") { return arg.label == Label; }
76+
MATCHER_P(labelIs, Label, "") { return arg.joinLabels() == Label; }
7677

7778
Config noHintsConfig() {
7879
Config C;

0 commit comments

Comments
 (0)