Index: clangd/ClangdLSPServer.h =================================================================== --- clangd/ClangdLSPServer.h +++ clangd/ClangdLSPServer.h @@ -146,6 +146,9 @@ bool SupportsHierarchicalDocumentSymbol = false; /// Whether the client supports showing file status. bool SupportFileStatus = false; + /// From capabilities of textDocument/hover. + bool HoverSupportsMarkdown = false; + // Store of the current versions of the open documents. DraftStore DraftMgr; Index: clangd/ClangdLSPServer.cpp =================================================================== --- clangd/ClangdLSPServer.cpp +++ clangd/ClangdLSPServer.cpp @@ -331,6 +331,8 @@ SupportsHierarchicalDocumentSymbol = Params.capabilities.HierarchicalDocumentSymbol; SupportFileStatus = Params.initializationOptions.FileStatus; + HoverSupportsMarkdown = Params.capabilities.HoverSupportsMarkdown; + Reply(llvm::json::Object{ {{"capabilities", llvm::json::Object{ @@ -802,8 +804,24 @@ void ClangdLSPServer::onHover(const TextDocumentPositionParams &Params, Callback> Reply) { - Server->findHover(Params.textDocument.uri.file(), Params.position, - std::move(Reply)); + + Server->findHover( + Params.textDocument.uri.file(), Params.position, + Bind( + [this](decltype(Reply) Reply, Expected> H) { + if (!H) + return Reply(H.takeError()); + + // If the client supports Markdown, convert from plaintext here. + if (*H && HoverSupportsMarkdown) { + (*H)->contents.kind = MarkupKind::Markdown; + (*H)->contents.value = + llvm::formatv("```\n{0}\n```", (*H)->contents.value); + } + + Reply(std::move(*H)); + }, + std::move(Reply))); } void ClangdLSPServer::applyConfiguration( Index: clangd/Protocol.h =================================================================== --- clangd/Protocol.h +++ clangd/Protocol.h @@ -362,6 +362,10 @@ /// Client supports CodeAction return value for textDocument/codeAction. /// textDocument.codeAction.codeActionLiteralSupport. bool CodeActionStructure = false; + + /// The supported set of MarkupKinds for hover. + /// textDocument.hover.contentFormat. + bool HoverSupportsMarkdown = false; }; bool fromJSON(const llvm::json::Value &, ClientCapabilities &); @@ -822,6 +826,7 @@ PlainText, Markdown, }; +bool fromJSON(const llvm::json::Value &, MarkupKind &); struct MarkupContent { MarkupKind kind = MarkupKind::PlainText; Index: clangd/Protocol.cpp =================================================================== --- clangd/Protocol.cpp +++ clangd/Protocol.cpp @@ -245,6 +245,24 @@ DocumentSymbol->getBoolean("hierarchicalDocumentSymbolSupport")) R.HierarchicalDocumentSymbol = *HierarchicalSupport; } + if (auto *Hover = TextDocument->getObject("hover")) { + if (auto HoverMarkupKinds = Hover->get("contentFormat")) { + auto *A = HoverMarkupKinds->getAsArray(); + if (!A) { + return false; + } + + for (size_t I = 0; I < A->size(); ++I) { + MarkupKind KindOut; + if (fromJSON((*A)[I], KindOut)) { + if (KindOut == MarkupKind::Markdown) { + R.HoverSupportsMarkdown = true; + break; + } + } + } + } + } } if (auto *Workspace = O->getObject("workspace")) { if (auto *Symbol = Workspace->getObject("symbol")) { @@ -616,6 +634,19 @@ }; } +bool fromJSON(const llvm::json::Value &E, MarkupKind &Out) { + if (auto T = E.getAsString()) { + if (*T == "plaintext") + Out = MarkupKind::PlainText; + else if (*T == "markdown") + Out = MarkupKind::Markdown; + else + return false; + return true; + } + return false; +} + llvm::json::Value toJSON(const Hover &H) { llvm::json::Object Result{{"contents", toJSON(H.contents)}}; Index: clangd/XRefs.cpp =================================================================== --- clangd/XRefs.cpp +++ clangd/XRefs.cpp @@ -537,13 +537,30 @@ return H; } -/// Generate a \p Hover object given the macro \p MacroInf. -static Hover getHoverContents(llvm::StringRef MacroName) { - Hover H; - - H.contents.value = "#define "; - H.contents.value += MacroName; +/// Generate a \p Hover object given the macro \p MacroDecl. +static Hover getHoverContents(MacroDecl Decl, ParsedAST &AST) { + SourceManager &SM = AST.getASTContext().getSourceManager(); + std::string Definition = Decl.Name; + + // Try to get the full definition, not just the name + SourceLocation StartLoc = Decl.Info->getDefinitionLoc(); + SourceLocation EndLoc = Decl.Info->getDefinitionEndLoc(); + if (EndLoc.isValid()) { + EndLoc = Lexer::getLocForEndOfToken(EndLoc, 0, SM, + AST.getASTContext().getLangOpts()); + bool Invalid; + StringRef Buffer = SM.getBufferData(SM.getFileID(StartLoc), &Invalid); + if (!Invalid) { + unsigned StartOffset = SM.getFileOffset(StartLoc); + unsigned EndOffset = SM.getFileOffset(EndLoc); + if (EndOffset <= Buffer.size() && StartOffset < EndOffset) + Definition = Buffer.substr(StartOffset, EndOffset - StartOffset).str(); + } + } + Hover H; + H.contents.kind = MarkupKind::PlainText; + H.contents.value = "#define " + Definition; return H; } @@ -667,7 +684,7 @@ auto Symbols = getSymbolAtPosition(AST, SourceLocationBeg); if (!Symbols.Macros.empty()) - return getHoverContents(Symbols.Macros[0].Name); + return getHoverContents(Symbols.Macros[0], AST); if (!Symbols.Decls.empty()) return getHoverContents(Symbols.Decls[0].D); Index: unittests/clangd/XRefsTests.cpp =================================================================== --- unittests/clangd/XRefsTests.cpp +++ unittests/clangd/XRefsTests.cpp @@ -648,7 +648,25 @@ #define MACRO 2 #undef macro )cpp", - "#define MACRO", + "#define MACRO 1", + }, + { + R"cpp(// Macro + #define MACRO 0 + #define MACRO2 ^MACRO + )cpp", + "#define MACRO 0", + }, + { + R"cpp(// Macro + #define MACRO {\ + return 0;\ + } + int main() ^MACRO + )cpp", + R"cpp(#define MACRO {\ + return 0;\ + })cpp", }, { R"cpp(// Forward class declaration