Skip to content

Commit

Permalink
[clangd] Support go-to-definition on type hints. The protocol part (#…
Browse files Browse the repository at this point in the history
…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`.
  • Loading branch information
zyn0217 authored Mar 26, 2024
1 parent 64a7114 commit c6a65e4
Show file tree
Hide file tree
Showing 7 changed files with 98 additions and 10 deletions.
2 changes: 1 addition & 1 deletion clang-tools-extra/clangd/ClangdLSPServer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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()},
});
Expand Down
5 changes: 3 additions & 2 deletions clang-tools-extra/clangd/InlayHints.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -977,8 +977,9 @@ 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.
Expand Down
31 changes: 31 additions & 0 deletions clang-tools-extra/clangd/Protocol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1501,6 +1501,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) {
Expand All @@ -1519,6 +1523,33 @@ 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;
if (L.command)
Result["command"] = *L.command;
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:
Expand Down
47 changes: 46 additions & 1 deletion clang-tools-extra/clangd/Protocol.h
Original file line number Diff line number Diff line change
Expand Up @@ -1689,6 +1689,48 @@ 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() = default;

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;

/// 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 &);
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.
Expand All @@ -1698,7 +1740,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.
Expand All @@ -1724,6 +1766,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 &);
Expand Down
6 changes: 5 additions & 1 deletion clang-tools-extra/clangd/test/inlayHints.test
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand Down
8 changes: 7 additions & 1 deletion clang-tools-extra/clangd/tool/Check.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}),
", ");
}());
}
}

Expand Down
9 changes: 5 additions & 4 deletions clang-tools-extra/clangd/unittests/InlayHintTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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)) {
Expand All @@ -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;
Expand Down

0 comments on commit c6a65e4

Please sign in to comment.