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{ @@ -839,7 +841,31 @@ void ClangdLSPServer::onHover(const TextDocumentPositionParams &Params, Callback> Reply) { Server->findHover(Params.textDocument.uri.file(), Params.position, - std::move(Reply)); + Bind( + [this](decltype(Reply) Reply, + llvm::Expected> H) { + if (!H) + return Reply(H.takeError()); + if (!*H) + return Reply(llvm::None); + + Hover R; + switch (HoverContentFormat) { + case MarkupKind::Plaintext: + R.contents.kind = MarkupKind::Plaintext; + R.contents.value = + (*H)->Content.renderAsPlainText(); + R.range = (*H)->Range; + return Reply(std::move(R)); + case MarkupKind::Markdown: + R.contents.kind = MarkupKind::Markdown; + R.contents.value = (*H)->Content.renderAsMarkdown(); + R.range = (*H)->Range; + return Reply(std::move(R)); + }; + llvm_unreachable("unhandled MarkupKind"); + }, + std::move(Reply))); } void ClangdLSPServer::onTypeHierarchy( 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" @@ -180,7 +181,7 @@ /// Get code hover for a given position. void findHover(PathRef File, Position Pos, - Callback> CB); + Callback> CB); /// Get information about type hierarchy for a given position. void typeHierarchy(PathRef File, Position Pos, int Resolve, 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" @@ -523,14 +525,12 @@ } void ClangdServer::findHover(PathRef File, Position Pos, - Callback> CB) { - auto Action = [Pos](Callback> CB, - llvm::Expected InpAST) { + Callback> CB) { + auto Action = [Pos](decltype(CB) CB, llvm::Expected InpAST) { if (!InpAST) return CB(InpAST.takeError()); CB(clangd::getHover(InpAST->AST, Pos)); }; - WorkScheduler.runWithAST("Hover", File, Bind(Action, std::move(CB))); } 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 @@ -36,6 +36,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,13 +873,8 @@ }; bool fromJSON(const llvm::json::Value &, CompletionParams &); -enum class MarkupKind { - PlainText, - Markdown, -}; - struct MarkupContent { - MarkupKind kind = MarkupKind::PlainText; + MarkupKind kind = MarkupKind::Plaintext; std::string value; }; llvm::json::Value toJSON(const MarkupContent &MC); 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 @@ -17,6 +17,7 @@ #include "llvm/ADT/Hashing.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringSwitch.h" +#include "llvm/Support/ErrorHandling.h" #include "llvm/Support/Format.h" #include "llvm/Support/FormatVariadic.h" #include "llvm/Support/JSON.h" @@ -302,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")) { @@ -675,7 +687,7 @@ static llvm::StringRef toTextKind(MarkupKind Kind) { switch (Kind) { - case MarkupKind::PlainText: + case MarkupKind::Plaintext: return "plaintext"; case MarkupKind::Markdown: return "markdown"; @@ -683,6 +695,23 @@ llvm_unreachable("Invalid MarkupKind"); } +bool fromJSON(const llvm::json::Value &V, MarkupKind &K) { + auto Str = V.getAsString(); + if (!Str) + return false; + if (*Str == "plaintext") + K = MarkupKind::Plaintext; + else if (*Str == "markdown") + K = MarkupKind::Markdown; + else + 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 "llvm/ADT/Optional.h" @@ -46,8 +47,12 @@ std::vector findDocumentHighlights(ParsedAST &AST, Position Pos); +struct HoverInfo { + FormattedString Content; + llvm::Optional Range; +}; /// Get the hover information when hovering at \p Pos. -llvm::Optional getHover(ParsedAST &AST, Position Pos); +llvm::Optional getHover(ParsedAST &AST, Position Pos); /// Returns reference locations of the symbol at a specified \p Pos. /// \p Limit limits the number of results returned (0 means no limit). 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 @@ -8,6 +8,7 @@ #include "XRefs.h" #include "AST.h" #include "FindSymbols.h" +#include "FormattedString.h" #include "Logger.h" #include "SourceCode.h" #include "URI.h" @@ -526,17 +527,16 @@ } /// Generate a \p Hover object given the declaration \p D. -static Hover getHoverContents(const Decl *D) { - Hover H; +static FormattedString getHoverContents(const Decl *D) { + FormattedString R; llvm::Optional NamedScope = getScopeName(D); // Generate the "Declared in" section. if (NamedScope) { assert(!NamedScope->empty()); - H.contents.value += "Declared in "; - H.contents.value += *NamedScope; - H.contents.value += "\n\n"; + R.appendText("Declared in"); + R.appendInlineCode(*NamedScope); } // We want to include the template in the Hover. @@ -548,29 +548,26 @@ PrintingPolicy Policy = printingPolicyForDecls(D->getASTContext().getPrintingPolicy()); - D->print(OS, Policy); - OS.flush(); - - H.contents.value += DeclText; - return H; + R.appendCodeBlock(OS.str()); + return R; } /// Generate a \p Hover object given the type \p T. -static Hover getHoverContents(QualType T, ASTContext &ASTCtx) { - Hover H; - std::string TypeText; - llvm::raw_string_ostream OS(TypeText); +static FormattedString getHoverContents(QualType T, ASTContext &ASTCtx) { + std::string Code; + llvm::raw_string_ostream OS(Code); PrintingPolicy Policy = printingPolicyForDecls(ASTCtx.getPrintingPolicy()); T.print(OS, Policy); - OS.flush(); - H.contents.value += TypeText; - return H; + + FormattedString R; + R.appendCodeBlock(OS.str()); + return R; } /// Generate a \p Hover object given the macro \p MacroDecl. -static Hover getHoverContents(MacroDecl Decl, ParsedAST &AST) { +static FormattedString getHoverContents(MacroDecl Decl, ParsedAST &AST) { SourceManager &SM = AST.getASTContext().getSourceManager(); std::string Definition = Decl.Name; @@ -590,10 +587,9 @@ } } - Hover H; - H.contents.kind = MarkupKind::PlainText; - H.contents.value = "#define " + Definition; - return H; + FormattedString S; + S.appendCodeBlock("#define " + std::move(Definition)); + return S; } namespace { @@ -708,22 +704,31 @@ return V.getDeducedType(); } -llvm::Optional getHover(ParsedAST &AST, Position Pos) { +llvm::Optional getHover(ParsedAST &AST, Position Pos) { const SourceManager &SourceMgr = AST.getASTContext().getSourceManager(); SourceLocation SourceLocationBeg = getBeginningOfIdentifier(AST, Pos, SourceMgr.getMainFileID()); // Identified symbols at a specific position. auto Symbols = getSymbolAtPosition(AST, SourceLocationBeg); - if (!Symbols.Macros.empty()) - return getHoverContents(Symbols.Macros[0], AST); + if (!Symbols.Macros.empty()) { + HoverInfo H; + H.Content = getHoverContents(Symbols.Macros[0], AST); + return H; + } - if (!Symbols.Decls.empty()) - return getHoverContents(Symbols.Decls[0]); + if (!Symbols.Decls.empty()) { + HoverInfo H; + H.Content = getHoverContents(Symbols.Decls[0]); + return H; + } auto DeducedType = getDeducedType(AST, SourceLocationBeg); - if (DeducedType && !DeducedType->isNull()) - return getHoverContents(*DeducedType, AST.getASTContext()); + if (DeducedType && !DeducedType->isNull()) { + HoverInfo H; + H.Content = getHoverContents(*DeducedType, AST.getASTContext()); + return H; + } return None; } 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 @@ -580,7 +580,10 @@ int test1 = bonjour; } )cpp", - "Declared in function main\n\nint bonjour", + "text[Declared in]code[function main]\n" + "codeblock(cpp) [\n" + "int bonjour\n" + "]", }, { R"cpp(// Local variable in method @@ -591,7 +594,10 @@ } }; )cpp", - "Declared in function s::method\n\nint bonjour", + "text[Declared in]code[function s::method]\n" + "codeblock(cpp) [\n" + "int bonjour\n" + "]", }, { R"cpp(// Struct @@ -602,7 +608,10 @@ ns1::My^Class* Params; } )cpp", - "Declared in namespace ns1\n\nstruct MyClass {}", + "text[Declared in]code[namespace ns1]\n" + "codeblock(cpp) [\n" + "struct MyClass {}\n" + "]", }, { R"cpp(// Class @@ -613,7 +622,10 @@ ns1::My^Class* Params; } )cpp", - "Declared in namespace ns1\n\nclass MyClass {}", + "text[Declared in]code[namespace ns1]\n" + "codeblock(cpp) [\n" + "class MyClass {}\n" + "]", }, { R"cpp(// Union @@ -624,7 +636,10 @@ ns1::My^Union Params; } )cpp", - "Declared in namespace ns1\n\nunion MyUnion {}", + "text[Declared in]code[namespace ns1]\n" + "codeblock(cpp) [\n" + "union MyUnion {}\n" + "]", }, { R"cpp(// Function definition via pointer @@ -633,7 +648,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 @@ -642,7 +660,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 @@ -652,7 +673,10 @@ bar.^x; } )cpp", - "Declared in struct Foo\n\nint x", + "text[Declared in]code[struct Foo]\n" + "codeblock(cpp) [\n" + "int x\n" + "]", }, { R"cpp(// Field with initialization @@ -662,7 +686,10 @@ bar.^x; } )cpp", - "Declared in struct Foo\n\nint x = 5", + "text[Declared in]code[struct Foo]\n" + "codeblock(cpp) [\n" + "int x = 5\n" + "]", }, { R"cpp(// Static field @@ -671,7 +698,10 @@ Foo::^x; } )cpp", - "Declared in struct Foo\n\nstatic int x", + "text[Declared in]code[struct Foo]\n" + "codeblock(cpp) [\n" + "static int x\n" + "]", }, { R"cpp(// Field, member initializer @@ -680,7 +710,10 @@ Foo() : ^x(0) {} }; )cpp", - "Declared in struct Foo\n\nint x", + "text[Declared in]code[struct Foo]\n" + "codeblock(cpp) [\n" + "int x\n" + "]", }, { R"cpp(// Field, GNU old-style field designator @@ -689,7 +722,10 @@ Foo bar = { ^x : 1 }; } )cpp", - "Declared in struct Foo\n\nint x", + "text[Declared in]code[struct Foo]\n" + "codeblock(cpp) [\n" + "int x\n" + "]", }, { R"cpp(// Field, field designator @@ -698,7 +734,10 @@ Foo bar = { .^x = 2 }; } )cpp", - "Declared in struct Foo\n\nint x", + "text[Declared in]code[struct Foo]\n" + "codeblock(cpp) [\n" + "int x\n" + "]", }, { R"cpp(// Method call @@ -708,7 +747,10 @@ bar.^x(); } )cpp", - "Declared in struct Foo\n\nint x()", + "text[Declared in]code[struct Foo]\n" + "codeblock(cpp) [\n" + "int x()\n" + "]", }, { R"cpp(// Static method call @@ -717,7 +759,10 @@ Foo::^x(); } )cpp", - "Declared in struct Foo\n\nstatic int x()", + "text[Declared in]code[struct Foo]\n" + "codeblock(cpp) [\n" + "static int x()\n" + "]", }, { R"cpp(// Typedef @@ -726,7 +771,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 @@ -735,7 +783,10 @@ } // namespace ns int main() { ^ns::Foo::bar(); } )cpp", - "Declared in global namespace\n\nnamespace ns {\n}", + "text[Declared in]code[global namespace]\n" + "codeblock(cpp) [\n" + "namespace ns {\n}\n" + "]", }, { R"cpp(// Anonymous namespace @@ -746,7 +797,10 @@ } // namespace ns int main() { ns::f^oo++; } )cpp", - "Declared in namespace ns::(anonymous)\n\nint foo", + "text[Declared in]code[namespace ns::(anonymous)]\n" + "codeblock(cpp) [\n" + "int foo\n" + "]", }, { R"cpp(// Macro @@ -756,14 +810,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 @@ -772,9 +830,11 @@ } int main() ^MACRO )cpp", - R"cpp(#define MACRO {\ + R"cpp(codeblock(cpp) [ +#define MACRO {\ return 0;\ - })cpp", + } +])cpp", }, { R"cpp(// Forward class declaration @@ -782,7 +842,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 @@ -790,7 +853,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 @@ -801,7 +867,10 @@ Hel^lo hello = ONE; } )cpp", - "Declared in global namespace\n\nenum Hello {\n}", + "text[Declared in]code[global namespace]\n" + "codeblock(cpp) [\n" + "enum Hello {\n}\n" + "]", }, { R"cpp(// Enumerator @@ -812,7 +881,10 @@ Hello hello = O^NE; } )cpp", - "Declared in enum Hello\n\nONE", + "text[Declared in]code[enum Hello]\n" + "codeblock(cpp) [\n" + "ONE\n" + "]", }, { R"cpp(// Enumerator in anonymous enum @@ -823,7 +895,10 @@ int hello = O^NE; } )cpp", - "Declared in enum (anonymous)\n\nONE", + "text[Declared in]code[enum (anonymous)]\n" + "codeblock(cpp) [\n" + "ONE\n" + "]", }, { R"cpp(// Global variable @@ -832,7 +907,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 @@ -843,7 +921,10 @@ ns1::he^y++; } )cpp", - "Declared in namespace ns1\n\nstatic int hey = 10", + "text[Declared in]code[namespace ns1]\n" + "codeblock(cpp) [\n" + "static int hey = 10\n" + "]", }, { R"cpp(// Field in anonymous struct @@ -854,7 +935,10 @@ s.he^llo++; } )cpp", - "Declared in struct (anonymous)\n\nint hello", + "text[Declared in]code[struct (anonymous)]\n" + "codeblock(cpp) [\n" + "int hello\n" + "]", }, { R"cpp(// Templated function @@ -864,7 +948,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 @@ -875,7 +962,10 @@ }; void g() { struct outer o; o.v.d^ef++; } )cpp", - "Declared in union outer::(anonymous)\n\nint def", + "text[Declared in]code[union outer::(anonymous)]\n" + "codeblock(cpp) [\n" + "int def\n" + "]", }, { R"cpp(// Nothing @@ -891,7 +981,9 @@ ^auto i = 1; } )cpp", - "int", + "codeblock(cpp) [\n" + "int\n" + "]", }, { R"cpp(// Simple initialization with const auto @@ -899,7 +991,9 @@ const ^auto i = 1; } )cpp", - "int", + "codeblock(cpp) [\n" + "int\n" + "]", }, { R"cpp(// Simple initialization with const auto& @@ -907,7 +1001,9 @@ const ^auto& i = 1; } )cpp", - "int", + "codeblock(cpp) [\n" + "int\n" + "]", }, { R"cpp(// Simple initialization with auto& @@ -915,7 +1011,9 @@ ^auto& i = 1; } )cpp", - "int", + "codeblock(cpp) [\n" + "int\n" + "]", }, { R"cpp(// Simple initialization with auto* @@ -924,7 +1022,9 @@ ^auto* i = &a; } )cpp", - "int", + "codeblock(cpp) [\n" + "int\n" + "]", }, { R"cpp(// Auto with initializer list. @@ -937,7 +1037,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 @@ -945,7 +1047,9 @@ operator ^auto() const { return 10; } }; )cpp", - "int", + "codeblock(cpp) [\n" + "int\n" + "]", }, { R"cpp(// Simple initialization with decltype(auto) @@ -953,7 +1057,9 @@ ^decltype(auto) i = 1; } )cpp", - "int", + "codeblock(cpp) [\n" + "int\n" + "]", }, { R"cpp(// Simple initialization with const decltype(auto) @@ -962,7 +1068,9 @@ ^decltype(auto) i = j; } )cpp", - "const int", + "codeblock(cpp) [\n" + "const int\n" + "]", }, { R"cpp(// Simple initialization with const& decltype(auto) @@ -972,7 +1080,9 @@ ^decltype(auto) i = j; } )cpp", - "const int &", + "codeblock(cpp) [\n" + "const int &\n" + "]", }, { R"cpp(// Simple initialization with & decltype(auto) @@ -982,7 +1092,9 @@ ^decltype(auto) i = j; } )cpp", - "int &", + "codeblock(cpp) [\n" + "int &\n" + "]", }, { R"cpp(// decltype with initializer list: nothing @@ -1003,7 +1115,9 @@ return 0; } )cpp", - "int", + "codeblock(cpp) [\n" + "int\n" + "]", }, { R"cpp(// auto function return with trailing type @@ -1012,7 +1126,9 @@ return Bar(); } )cpp", - "struct Bar", + "codeblock(cpp) [\n" + "struct Bar\n" + "]", }, { R"cpp(// trailing return type @@ -1021,7 +1137,9 @@ return Bar(); } )cpp", - "struct Bar", + "codeblock(cpp) [\n" + "struct Bar\n" + "]", }, { R"cpp(// auto in function return @@ -1030,7 +1148,9 @@ return Bar(); } )cpp", - "struct Bar", + "codeblock(cpp) [\n" + "struct Bar\n" + "]", }, { R"cpp(// auto& in function return @@ -1039,7 +1159,9 @@ return Bar(); } )cpp", - "struct Bar", + "codeblock(cpp) [\n" + "struct Bar\n" + "]", }, { R"cpp(// auto* in function return @@ -1049,7 +1171,9 @@ return bar; } )cpp", - "struct Bar", + "codeblock(cpp) [\n" + "struct Bar\n" + "]", }, { R"cpp(// const auto& in function return @@ -1058,7 +1182,9 @@ return Bar(); } )cpp", - "struct Bar", + "codeblock(cpp) [\n" + "struct Bar\n" + "]", }, { R"cpp(// decltype(auto) in function return @@ -1067,7 +1193,9 @@ return Bar(); } )cpp", - "struct Bar", + "codeblock(cpp) [\n" + "struct Bar\n" + "]", }, { R"cpp(// decltype(auto) reference in function return @@ -1077,7 +1205,9 @@ return (a); } )cpp", - "int &", + "codeblock(cpp) [\n" + "int &\n" + "]", }, { R"cpp(// decltype lvalue reference @@ -1086,7 +1216,9 @@ ^decltype(I) J = I; } )cpp", - "int", + "codeblock(cpp) [\n" + "int\n" + "]", }, { R"cpp(// decltype lvalue reference @@ -1096,7 +1228,9 @@ ^decltype(K) J = I; } )cpp", - "int &", + "codeblock(cpp) [\n" + "int &\n" + "]", }, { R"cpp(// decltype lvalue reference parenthesis @@ -1105,7 +1239,9 @@ ^decltype((I)) J = I; } )cpp", - "int &", + "codeblock(cpp) [\n" + "int &\n" + "]", }, { R"cpp(// decltype rvalue reference @@ -1114,7 +1250,9 @@ ^decltype(static_cast(I)) J = static_cast(I); } )cpp", - "int &&", + "codeblock(cpp) [\n" + "int &&\n" + "]", }, { R"cpp(// decltype rvalue reference function call @@ -1124,7 +1262,9 @@ ^decltype(bar()) J = bar(); } )cpp", - "int &&", + "codeblock(cpp) [\n" + "int &&\n" + "]", }, { R"cpp(// decltype of function with trailing return type. @@ -1136,7 +1276,9 @@ ^decltype(test()) i = test(); } )cpp", - "struct Bar", + "codeblock(cpp) [\n" + "struct Bar\n" + "]", }, { R"cpp(// decltype of var with decltype. @@ -1146,7 +1288,9 @@ ^decltype(J) K = J; } )cpp", - "int", + "codeblock(cpp) [\n" + "int\n" + "]", }, { R"cpp(// structured binding. Not supported yet @@ -1174,7 +1318,9 @@ int bar(); ^auto (*foo)() = bar; )cpp", - "int", + "codeblock(cpp) [\n" + "int\n" + "]", }, }; @@ -1185,7 +1331,8 @@ auto AST = TU.build(); if (auto H = getHover(AST, T.point())) { EXPECT_NE("", Test.ExpectedHover) << Test.Input; - EXPECT_EQ(H->contents.value, Test.ExpectedHover.str()) << Test.Input; + EXPECT_EQ(H->Content.renderForTests(), Test.ExpectedHover.str()) + << Test.Input; } else EXPECT_EQ("", Test.ExpectedHover.str()) << Test.Input; }