diff --git a/clang-tools-extra/clangd/ClangdLSPServer.h b/clang-tools-extra/clangd/ClangdLSPServer.h --- a/clang-tools-extra/clangd/ClangdLSPServer.h +++ b/clang-tools-extra/clangd/ClangdLSPServer.h @@ -154,7 +154,10 @@ bool SupportsHierarchicalDocumentSymbol = false; /// Whether the client supports showing file status. bool SupportFileStatus = false; - // Store of the current versions of the open documents. + /// Which kind of markup should we use in textDocument/hover responses. + MarkupKind HoverContentFormat = MarkupKind::PlainText; + + /// Store of the current versions of the open documents. DraftStore DraftMgr; // The CDB is created by the "initialize" LSP method. diff --git a/clang-tools-extra/clangd/ClangdLSPServer.cpp b/clang-tools-extra/clangd/ClangdLSPServer.cpp --- a/clang-tools-extra/clangd/ClangdLSPServer.cpp +++ b/clang-tools-extra/clangd/ClangdLSPServer.cpp @@ -8,6 +8,7 @@ #include "ClangdLSPServer.h" #include "Diagnostics.h" +#include "FormattedString.h" #include "Protocol.h" #include "SourceCode.h" #include "Trace.h" @@ -358,6 +359,7 @@ SupportsHierarchicalDocumentSymbol = Params.capabilities.HierarchicalDocumentSymbol; SupportFileStatus = Params.initializationOptions.FileStatus; + HoverContentFormat = Params.capabilities.HoverContentFormat; llvm::json::Object Result{ {{"capabilities", llvm::json::Object{ @@ -843,17 +845,27 @@ Callback> Reply) { Server->findHover(Params.textDocument.uri.file(), Params.position, Bind( - [](decltype(Reply) Reply, - llvm::Expected> HIorErr) { - if (!HIorErr) - return Reply(HIorErr.takeError()); - const auto &HI = HIorErr.get(); - if (!HI) + [this](decltype(Reply) Reply, + llvm::Expected> H) { + if (!H) + return Reply(H.takeError()); + if (!*H) return Reply(llvm::None); - Hover H; - H.range = HI->SymRange; - H.contents = HI->render(); - return Reply(H); + + Hover R; + R.contents.kind = HoverContentFormat; + R.range = (*H)->SymRange; + switch (HoverContentFormat) { + case MarkupKind::PlainText: + R.contents.value = + (*H)->present().renderAsPlainText(); + return Reply(std::move(R)); + case MarkupKind::Markdown: + R.contents.value = + (*H)->present().renderAsMarkdown(); + return Reply(std::move(R)); + }; + llvm_unreachable("unhandled MarkupKind"); }, std::move(Reply))); } diff --git a/clang-tools-extra/clangd/ClangdServer.h b/clang-tools-extra/clangd/ClangdServer.h --- a/clang-tools-extra/clangd/ClangdServer.h +++ b/clang-tools-extra/clangd/ClangdServer.h @@ -14,6 +14,7 @@ #include "ClangdUnit.h" #include "CodeComplete.h" #include "FSProvider.h" +#include "FormattedString.h" #include "Function.h" #include "GlobalCompilationDatabase.h" #include "Protocol.h" diff --git a/clang-tools-extra/clangd/ClangdServer.cpp b/clang-tools-extra/clangd/ClangdServer.cpp --- a/clang-tools-extra/clangd/ClangdServer.cpp +++ b/clang-tools-extra/clangd/ClangdServer.cpp @@ -10,11 +10,13 @@ #include "ClangdUnit.h" #include "CodeComplete.h" #include "FindSymbols.h" +#include "FormattedString.h" #include "Headers.h" #include "Protocol.h" #include "SourceCode.h" #include "TUScheduler.h" #include "Trace.h" +#include "XRefs.h" #include "index/CanonicalIncludes.h" #include "index/FileIndex.h" #include "index/Merge.h" @@ -462,7 +464,7 @@ void ClangdServer::findHover(PathRef File, Position Pos, Callback> CB) { - auto Action = [Pos](Callback> CB, Path File, + auto Action = [Pos](decltype(CB) CB, Path File, llvm::Expected InpAST) { if (!InpAST) return CB(InpAST.takeError()); diff --git a/clang-tools-extra/clangd/FormattedString.h b/clang-tools-extra/clangd/FormattedString.h --- a/clang-tools-extra/clangd/FormattedString.h +++ b/clang-tools-extra/clangd/FormattedString.h @@ -35,6 +35,7 @@ std::string renderAsMarkdown() const; std::string renderAsPlainText() const; + std::string renderForTests() const; private: enum class ChunkKind { diff --git a/clang-tools-extra/clangd/FormattedString.cpp b/clang-tools-extra/clangd/FormattedString.cpp --- a/clang-tools-extra/clangd/FormattedString.cpp +++ b/clang-tools-extra/clangd/FormattedString.cpp @@ -9,6 +9,7 @@ #include "clang/Basic/CharInfo.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/FormatVariadic.h" #include #include @@ -169,5 +170,27 @@ R.pop_back(); return R; } + +std::string FormattedString::renderForTests() const { + std::string R; + for (const auto &C : Chunks) { + switch (C.Kind) { + case ChunkKind::PlainText: + R += "text[" + C.Contents + "]"; + break; + case ChunkKind::InlineCodeBlock: + R += "code[" + C.Contents + "]"; + break; + case ChunkKind::CodeBlock: + if (!R.empty()) + R += "\n"; + R += llvm::formatv("codeblock({0}) [\n{1}\n]\n", C.Language, C.Contents); + break; + } + } + while (!R.empty() && isWhitespace(R.back())) + R.pop_back(); + return R; +} } // namespace clangd } // namespace clang diff --git a/clang-tools-extra/clangd/Protocol.h b/clang-tools-extra/clangd/Protocol.h --- a/clang-tools-extra/clangd/Protocol.h +++ b/clang-tools-extra/clangd/Protocol.h @@ -353,6 +353,15 @@ bool fromJSON(const llvm::json::Value &, OffsetEncoding &); llvm::raw_ostream &operator<<(llvm::raw_ostream &, OffsetEncoding); +// Describes the content type that a client supports in various result literals +// like `Hover`, `ParameterInfo` or `CompletionItem`. +enum class MarkupKind { + PlainText, + Markdown, +}; +bool fromJSON(const llvm::json::Value &, MarkupKind &); +llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, MarkupKind); + // This struct doesn't mirror LSP! // The protocol defines deeply nested structures for client capabilities. // Instead of mapping them all, this just parses out the bits we care about. @@ -391,6 +400,9 @@ /// Supported encodings for LSP character offsets. (clangd extension). llvm::Optional> offsetEncoding; + + /// The content format that should be used for Hover requests. + MarkupKind HoverContentFormat = MarkupKind::PlainText; }; bool fromJSON(const llvm::json::Value &, ClientCapabilities &); @@ -861,11 +873,6 @@ }; bool fromJSON(const llvm::json::Value &, CompletionParams &); -enum class MarkupKind { - PlainText, - Markdown, -}; - struct MarkupContent { MarkupKind kind = MarkupKind::PlainText; std::string value; diff --git a/clang-tools-extra/clangd/Protocol.cpp b/clang-tools-extra/clangd/Protocol.cpp --- a/clang-tools-extra/clangd/Protocol.cpp +++ b/clang-tools-extra/clangd/Protocol.cpp @@ -303,6 +303,17 @@ DocumentSymbol->getBoolean("hierarchicalDocumentSymbolSupport")) R.HierarchicalDocumentSymbol = *HierarchicalSupport; } + if (auto *Hover = TextDocument->getObject("hover")) { + if (auto *ContentFormat = Hover->getArray("contentFormat")) { + for (const auto &Format : *ContentFormat) { + MarkupKind K = MarkupKind::PlainText; + if (fromJSON(Format, K)) { + R.HoverContentFormat = K; + break; + } + } + } + } } if (auto *Workspace = O->getObject("workspace")) { if (auto *Symbol = Workspace->getObject("symbol")) { @@ -684,6 +695,27 @@ llvm_unreachable("Invalid MarkupKind"); } +bool fromJSON(const llvm::json::Value &V, MarkupKind &K) { + auto Str = V.getAsString(); + if (!Str) { + elog("Failed to parse markup kind: expected a string"); + return false; + } + if (*Str == "plaintext") + K = MarkupKind::PlainText; + else if (*Str == "markdown") + K = MarkupKind::Markdown; + else { + elog("Unknown markup kind: {0}", *Str); + return false; + } + return true; +} + +llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, MarkupKind K) { + return OS << toTextKind(K); +} + llvm::json::Value toJSON(const MarkupContent &MC) { if (MC.value.empty()) return nullptr; diff --git a/clang-tools-extra/clangd/XRefs.h b/clang-tools-extra/clangd/XRefs.h --- a/clang-tools-extra/clangd/XRefs.h +++ b/clang-tools-extra/clangd/XRefs.h @@ -14,6 +14,7 @@ #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_XREFS_H #include "ClangdUnit.h" +#include "FormattedString.h" #include "Protocol.h" #include "index/Index.h" #include "index/SymbolLocation.h" @@ -103,8 +104,8 @@ /// Set for all templates(function, class, variable). llvm::Optional> TemplateParameters; - /// Lower to LSP struct. - MarkupContent render() const; + /// Produce a user-readable information. + FormattedString present() const; }; llvm::raw_ostream &operator<<(llvm::raw_ostream &, const HoverInfo::Param &); inline bool operator==(const HoverInfo::Param &LHS, diff --git a/clang-tools-extra/clangd/XRefs.cpp b/clang-tools-extra/clangd/XRefs.cpp --- a/clang-tools-extra/clangd/XRefs.cpp +++ b/clang-tools-extra/clangd/XRefs.cpp @@ -9,6 +9,7 @@ #include "AST.h" #include "CodeCompletionStrings.h" #include "FindSymbols.h" +#include "FormattedString.h" #include "Logger.h" #include "Protocol.h" #include "SourceCode.h" @@ -1158,32 +1159,26 @@ return Result; } -MarkupContent HoverInfo::render() const { - MarkupContent Content; - Content.kind = MarkupKind::PlainText; - std::vector Output; - +FormattedString HoverInfo::present() const { + FormattedString Output; if (NamespaceScope) { - llvm::raw_string_ostream Out(Content.value); - Out << "Declared in "; + Output.appendText("Declared in"); // Drop trailing "::". if (!LocalScope.empty()) - Out << *NamespaceScope << llvm::StringRef(LocalScope).drop_back(2); + Output.appendInlineCode(llvm::StringRef(LocalScope).drop_back(2)); else if (NamespaceScope->empty()) - Out << "global namespace"; + Output.appendInlineCode("global namespace"); else - Out << llvm::StringRef(*NamespaceScope).drop_back(2); - Out << "\n\n"; + Output.appendInlineCode(llvm::StringRef(*NamespaceScope).drop_back(2)); } if (!Definition.empty()) { - Output.push_back(Definition); + Output.appendCodeBlock(Definition); } else { // Builtin types - Output.push_back(Name); + Output.appendCodeBlock(Name); } - Content.value += llvm::join(Output, " "); - return Content; + return Output; } llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, diff --git a/clang-tools-extra/clangd/unittests/XRefsTests.cpp b/clang-tools-extra/clangd/unittests/XRefsTests.cpp --- a/clang-tools-extra/clangd/unittests/XRefsTests.cpp +++ b/clang-tools-extra/clangd/unittests/XRefsTests.cpp @@ -893,7 +893,10 @@ int test1 = bonjour; } )cpp", - "Declared in main\n\nint bonjour", + "text[Declared in]code[main]\n" + "codeblock(cpp) [\n" + "int bonjour\n" + "]", }, { R"cpp(// Local variable in method @@ -904,7 +907,10 @@ } }; )cpp", - "Declared in s::method\n\nint bonjour", + "text[Declared in]code[s::method]\n" + "codeblock(cpp) [\n" + "int bonjour\n" + "]", }, { R"cpp(// Struct @@ -915,7 +921,10 @@ ns1::My^Class* Params; } )cpp", - "Declared in ns1\n\nstruct MyClass {}", + "text[Declared in]code[ns1]\n" + "codeblock(cpp) [\n" + "struct MyClass {}\n" + "]", }, { R"cpp(// Class @@ -926,7 +935,10 @@ ns1::My^Class* Params; } )cpp", - "Declared in ns1\n\nclass MyClass {}", + "text[Declared in]code[ns1]\n" + "codeblock(cpp) [\n" + "class MyClass {}\n" + "]", }, { R"cpp(// Union @@ -937,7 +949,10 @@ ns1::My^Union Params; } )cpp", - "Declared in ns1\n\nunion MyUnion {}", + "text[Declared in]code[ns1]\n" + "codeblock(cpp) [\n" + "union MyUnion {}\n" + "]", }, { R"cpp(// Function definition via pointer @@ -946,7 +961,10 @@ auto *X = &^foo; } )cpp", - "Declared in global namespace\n\nint foo(int)", + "text[Declared in]code[global namespace]\n" + "codeblock(cpp) [\n" + "int foo(int)\n" + "]", }, { R"cpp(// Function declaration via call @@ -955,7 +973,10 @@ return ^foo(42); } )cpp", - "Declared in global namespace\n\nint foo(int)", + "text[Declared in]code[global namespace]\n" + "codeblock(cpp) [\n" + "int foo(int)\n" + "]", }, { R"cpp(// Field @@ -965,7 +986,10 @@ bar.^x; } )cpp", - "Declared in Foo\n\nint x", + "text[Declared in]code[Foo]\n" + "codeblock(cpp) [\n" + "int x\n" + "]", }, { R"cpp(// Field with initialization @@ -975,7 +999,10 @@ bar.^x; } )cpp", - "Declared in Foo\n\nint x = 5", + "text[Declared in]code[Foo]\n" + "codeblock(cpp) [\n" + "int x = 5\n" + "]", }, { R"cpp(// Static field @@ -984,7 +1011,10 @@ Foo::^x; } )cpp", - "Declared in Foo\n\nstatic int x", + "text[Declared in]code[Foo]\n" + "codeblock(cpp) [\n" + "static int x\n" + "]", }, { R"cpp(// Field, member initializer @@ -993,7 +1023,10 @@ Foo() : ^x(0) {} }; )cpp", - "Declared in Foo\n\nint x", + "text[Declared in]code[Foo]\n" + "codeblock(cpp) [\n" + "int x\n" + "]", }, { R"cpp(// Field, GNU old-style field designator @@ -1002,7 +1035,10 @@ Foo bar = { ^x : 1 }; } )cpp", - "Declared in Foo\n\nint x", + "text[Declared in]code[Foo]\n" + "codeblock(cpp) [\n" + "int x\n" + "]", }, { R"cpp(// Field, field designator @@ -1011,7 +1047,10 @@ Foo bar = { .^x = 2 }; } )cpp", - "Declared in Foo\n\nint x", + "text[Declared in]code[Foo]\n" + "codeblock(cpp) [\n" + "int x\n" + "]", }, { R"cpp(// Method call @@ -1021,7 +1060,10 @@ bar.^x(); } )cpp", - "Declared in Foo\n\nint x()", + "text[Declared in]code[Foo]\n" + "codeblock(cpp) [\n" + "int x()\n" + "]", }, { R"cpp(// Static method call @@ -1030,7 +1072,10 @@ Foo::^x(); } )cpp", - "Declared in Foo\n\nstatic int x()", + "text[Declared in]code[Foo]\n" + "codeblock(cpp) [\n" + "static int x()\n" + "]", }, { R"cpp(// Typedef @@ -1039,7 +1084,10 @@ ^Foo bar; } )cpp", - "Declared in global namespace\n\ntypedef int Foo", + "text[Declared in]code[global namespace]\n" + "codeblock(cpp) [\n" + "typedef int Foo\n" + "]", }, { R"cpp(// Namespace @@ -1048,7 +1096,10 @@ } // namespace ns int main() { ^ns::Foo::bar(); } )cpp", - "Declared in global namespace\n\nnamespace ns {}", + "text[Declared in]code[global namespace]\n" + "codeblock(cpp) [\n" + "namespace ns {}\n" + "]", }, { R"cpp(// Anonymous namespace @@ -1059,7 +1110,10 @@ } // namespace ns int main() { ns::f^oo++; } )cpp", - "Declared in ns::(anonymous)\n\nint foo", + "text[Declared in]code[ns::(anonymous)]\n" + "codeblock(cpp) [\n" + "int foo\n" + "]", }, { R"cpp(// Macro @@ -1069,14 +1123,18 @@ #define MACRO 2 #undef macro )cpp", - "#define MACRO 1", + "codeblock(cpp) [\n" + "#define MACRO 1\n" + "]", }, { R"cpp(// Macro #define MACRO 0 #define MACRO2 ^MACRO )cpp", - "#define MACRO 0", + "codeblock(cpp) [\n" + "#define MACRO 0\n" + "]", }, { R"cpp(// Macro @@ -1085,8 +1143,10 @@ } int main() ^MACRO )cpp", - "#define MACRO " - " \\\n { return 0; }", + R"cpp(codeblock(cpp) [ +#define MACRO \ + { return 0; } +])cpp", }, { R"cpp(// Forward class declaration @@ -1094,7 +1154,10 @@ class Foo {}; F^oo* foo(); )cpp", - "Declared in global namespace\n\nclass Foo {}", + "text[Declared in]code[global namespace]\n" + "codeblock(cpp) [\n" + "class Foo {}\n" + "]", }, { R"cpp(// Function declaration @@ -1102,7 +1165,10 @@ void g() { f^oo(); } void foo() {} )cpp", - "Declared in global namespace\n\nvoid foo()", + "text[Declared in]code[global namespace]\n" + "codeblock(cpp) [\n" + "void foo()\n" + "]", }, { R"cpp(// Enum declaration @@ -1113,7 +1179,10 @@ Hel^lo hello = ONE; } )cpp", - "Declared in global namespace\n\nenum Hello {}", + "text[Declared in]code[global namespace]\n" + "codeblock(cpp) [\n" + "enum Hello {}\n" + "]", }, { R"cpp(// Enumerator @@ -1124,7 +1193,10 @@ Hello hello = O^NE; } )cpp", - "Declared in Hello\n\nONE", + "text[Declared in]code[Hello]\n" + "codeblock(cpp) [\n" + "ONE\n" + "]", }, { R"cpp(// Enumerator in anonymous enum @@ -1135,7 +1207,10 @@ int hello = O^NE; } )cpp", - "Declared in global namespace\n\nONE", + "text[Declared in]code[global namespace]\n" + "codeblock(cpp) [\n" + "ONE\n" + "]", }, { R"cpp(// Global variable @@ -1144,7 +1219,10 @@ he^y++; } )cpp", - "Declared in global namespace\n\nstatic int hey = 10", + "text[Declared in]code[global namespace]\n" + "codeblock(cpp) [\n" + "static int hey = 10\n" + "]", }, { R"cpp(// Global variable in namespace @@ -1155,7 +1233,10 @@ ns1::he^y++; } )cpp", - "Declared in ns1\n\nstatic int hey = 10", + "text[Declared in]code[ns1]\n" + "codeblock(cpp) [\n" + "static int hey = 10\n" + "]", }, { R"cpp(// Field in anonymous struct @@ -1166,7 +1247,10 @@ s.he^llo++; } )cpp", - "Declared in (anonymous struct)\n\nint hello", + "text[Declared in]code[(anonymous struct)]\n" + "codeblock(cpp) [\n" + "int hello\n" + "]", }, { R"cpp(// Templated function @@ -1176,7 +1260,10 @@ } void g() { auto x = f^oo(); } )cpp", - "Declared in global namespace\n\ntemplate T foo()", + "text[Declared in]code[global namespace]\n" + "codeblock(cpp) [\n" + "template T foo()\n" + "]", }, { R"cpp(// Anonymous union @@ -1187,7 +1274,10 @@ }; void g() { struct outer o; o.v.d^ef++; } )cpp", - "Declared in outer::(anonymous union)\n\nint def", + "text[Declared in]code[outer::(anonymous union)]\n" + "codeblock(cpp) [\n" + "int def\n" + "]", }, { R"cpp(// Nothing @@ -1203,7 +1293,9 @@ ^auto i = 1; } )cpp", - "int", + "codeblock(cpp) [\n" + "int\n" + "]", }, { R"cpp(// Simple initialization with const auto @@ -1211,7 +1303,9 @@ const ^auto i = 1; } )cpp", - "int", + "codeblock(cpp) [\n" + "int\n" + "]", }, { R"cpp(// Simple initialization with const auto& @@ -1219,7 +1313,9 @@ const ^auto& i = 1; } )cpp", - "int", + "codeblock(cpp) [\n" + "int\n" + "]", }, { R"cpp(// Simple initialization with auto& @@ -1227,7 +1323,9 @@ ^auto& i = 1; } )cpp", - "int", + "codeblock(cpp) [\n" + "int\n" + "]", }, { R"cpp(// Simple initialization with auto* @@ -1236,7 +1334,9 @@ ^auto* i = &a; } )cpp", - "int", + "codeblock(cpp) [\n" + "int\n" + "]", }, { R"cpp(// Auto with initializer list. @@ -1249,7 +1349,9 @@ ^auto i = {1,2}; } )cpp", - "class std::initializer_list", + "codeblock(cpp) [\n" + "class std::initializer_list\n" + "]", }, { R"cpp(// User defined conversion to auto @@ -1257,7 +1359,9 @@ operator ^auto() const { return 10; } }; )cpp", - "int", + "codeblock(cpp) [\n" + "int\n" + "]", }, { R"cpp(// Simple initialization with decltype(auto) @@ -1265,7 +1369,9 @@ ^decltype(auto) i = 1; } )cpp", - "int", + "codeblock(cpp) [\n" + "int\n" + "]", }, { R"cpp(// Simple initialization with const decltype(auto) @@ -1274,7 +1380,9 @@ ^decltype(auto) i = j; } )cpp", - "const int", + "codeblock(cpp) [\n" + "const int\n" + "]", }, { R"cpp(// Simple initialization with const& decltype(auto) @@ -1284,7 +1392,9 @@ ^decltype(auto) i = j; } )cpp", - "const int &", + "codeblock(cpp) [\n" + "const int &\n" + "]", }, { R"cpp(// Simple initialization with & decltype(auto) @@ -1294,7 +1404,9 @@ ^decltype(auto) i = j; } )cpp", - "int &", + "codeblock(cpp) [\n" + "int &\n" + "]", }, { R"cpp(// decltype with initializer list: nothing @@ -1315,7 +1427,9 @@ return 0; } )cpp", - "int", + "codeblock(cpp) [\n" + "int\n" + "]", }, { R"cpp(// auto function return with trailing type @@ -1324,7 +1438,9 @@ return Bar(); } )cpp", - "struct Bar", + "codeblock(cpp) [\n" + "struct Bar\n" + "]", }, { R"cpp(// trailing return type @@ -1333,7 +1449,9 @@ return Bar(); } )cpp", - "struct Bar", + "codeblock(cpp) [\n" + "struct Bar\n" + "]", }, { R"cpp(// auto in function return @@ -1342,7 +1460,9 @@ return Bar(); } )cpp", - "struct Bar", + "codeblock(cpp) [\n" + "struct Bar\n" + "]", }, { R"cpp(// auto& in function return @@ -1351,7 +1471,9 @@ return Bar(); } )cpp", - "struct Bar", + "codeblock(cpp) [\n" + "struct Bar\n" + "]", }, { R"cpp(// auto* in function return @@ -1361,7 +1483,9 @@ return bar; } )cpp", - "struct Bar", + "codeblock(cpp) [\n" + "struct Bar\n" + "]", }, { R"cpp(// const auto& in function return @@ -1370,7 +1494,9 @@ return Bar(); } )cpp", - "struct Bar", + "codeblock(cpp) [\n" + "struct Bar\n" + "]", }, { R"cpp(// decltype(auto) in function return @@ -1379,7 +1505,9 @@ return Bar(); } )cpp", - "struct Bar", + "codeblock(cpp) [\n" + "struct Bar\n" + "]", }, { R"cpp(// decltype(auto) reference in function return @@ -1389,7 +1517,9 @@ return (a); } )cpp", - "int &", + "codeblock(cpp) [\n" + "int &\n" + "]", }, { R"cpp(// decltype lvalue reference @@ -1398,7 +1528,9 @@ ^decltype(I) J = I; } )cpp", - "int", + "codeblock(cpp) [\n" + "int\n" + "]", }, { R"cpp(// decltype lvalue reference @@ -1408,7 +1540,9 @@ ^decltype(K) J = I; } )cpp", - "int &", + "codeblock(cpp) [\n" + "int &\n" + "]", }, { R"cpp(// decltype lvalue reference parenthesis @@ -1417,7 +1551,9 @@ ^decltype((I)) J = I; } )cpp", - "int &", + "codeblock(cpp) [\n" + "int &\n" + "]", }, { R"cpp(// decltype rvalue reference @@ -1426,7 +1562,9 @@ ^decltype(static_cast(I)) J = static_cast(I); } )cpp", - "int &&", + "codeblock(cpp) [\n" + "int &&\n" + "]", }, { R"cpp(// decltype rvalue reference function call @@ -1436,7 +1574,9 @@ ^decltype(bar()) J = bar(); } )cpp", - "int &&", + "codeblock(cpp) [\n" + "int &&\n" + "]", }, { R"cpp(// decltype of function with trailing return type. @@ -1448,7 +1588,9 @@ ^decltype(test()) i = test(); } )cpp", - "struct Bar", + "codeblock(cpp) [\n" + "struct Bar\n" + "]", }, { R"cpp(// decltype of var with decltype. @@ -1458,7 +1600,9 @@ ^decltype(J) K = J; } )cpp", - "int", + "codeblock(cpp) [\n" + "int\n" + "]", }, { R"cpp(// structured binding. Not supported yet @@ -1486,7 +1630,9 @@ int bar(); ^auto (*foo)() = bar; )cpp", - "int", + "codeblock(cpp) [\n" + "int\n" + "]", }, }; @@ -1497,7 +1643,8 @@ auto AST = TU.build(); if (auto H = getHover(AST, T.point(), format::getLLVMStyle())) { EXPECT_NE("", Test.ExpectedHover) << Test.Input; - EXPECT_EQ(H->render().value, Test.ExpectedHover.str()) << Test.Input; + EXPECT_EQ(H->present().renderForTests(), Test.ExpectedHover.str()) + << Test.Input; } else EXPECT_EQ("", Test.ExpectedHover.str()) << Test.Input; }