simark updated this revision to Diff 134306.
simark added a comment.
Generate Hover by pretty-printing the AST
This new version of the patch generates the hover by pretty-printing the AST.
The unit tests are updated accordingly. There are some inconsistencies in how
things are printed. For example, I find it a bit annoying that we print empty
curly braces after class names (e.g. "class Foo {}"). Also, namespace and
enums are printed with a newline between the two braces. These are things that
could get fixed by doing changes to the clang AST printer.
The hover.test test now uses a more readable format.
Repository:
rCTE Clang Tools Extra
https://reviews.llvm.org/D35894
Files:
clangd/ClangdLSPServer.cpp
clangd/ClangdLSPServer.h
clangd/ClangdServer.cpp
clangd/ClangdServer.h
clangd/Protocol.cpp
clangd/Protocol.h
clangd/ProtocolHandlers.cpp
clangd/ProtocolHandlers.h
clangd/XRefs.cpp
clangd/XRefs.h
test/clangd/hover.test
test/clangd/initialize-params-invalid.test
test/clangd/initialize-params.test
unittests/clangd/XRefsTests.cpp
Index: unittests/clangd/XRefsTests.cpp
===================================================================
--- unittests/clangd/XRefsTests.cpp
+++ unittests/clangd/XRefsTests.cpp
@@ -257,6 +257,311 @@
SourceAnnotations.range()}));
}
+TEST(Hover, All) {
+ struct OneTest {
+ StringRef input;
+ StringRef expectedHover;
+ };
+
+ OneTest Tests[] = {
+ {
+ R"cpp(// Local variable
+ int main() {
+ int bonjour;
+ ^bonjour = 2;
+ int test1 = bonjour;
+ }
+ )cpp",
+ "Declared in function main\n\nint bonjour",
+ },
+ {
+ R"cpp(// Local variable in method
+ struct s {
+ void method() {
+ int bonjour;
+ ^bonjour = 2;
+ }
+ };
+ )cpp",
+ "Declared in function s::method\n\nint bonjour",
+ },
+ {
+ R"cpp(// Struct
+ namespace ns1 {
+ struct MyClass {};
+ } // namespace ns1
+ int main() {
+ ns1::My^Class* Params;
+ }
+ )cpp",
+ "Declared in namespace ns1\n\nstruct MyClass {}",
+ },
+ {
+ R"cpp(// Class
+ namespace ns1 {
+ class MyClass {};
+ } // namespace ns1
+ int main() {
+ ns1::My^Class* Params;
+ }
+ )cpp",
+ "Declared in namespace ns1\n\nclass MyClass {}",
+ },
+ {
+ R"cpp(// Union
+ namespace ns1 {
+ union MyUnion { int x; int y; };
+ } // namespace ns1
+ int main() {
+ ns1::My^Union Params;
+ }
+ )cpp",
+ "Declared in namespace ns1\n\nunion MyUnion {}",
+ },
+ {
+ R"cpp(// Function definition via pointer
+ int foo(int) {}
+ int main() {
+ auto *X = &^foo;
+ }
+ )cpp",
+ "Declared in global namespace\n\nint foo(int)",
+ },
+ {
+ R"cpp(// Function declaration via call
+ int foo(int);
+ int main() {
+ return ^foo(42);
+ }
+ )cpp",
+ "Declared in global namespace\n\nint foo(int)",
+ },
+ {
+ R"cpp(// Field
+ struct Foo { int x; };
+ int main() {
+ Foo bar;
+ bar.^x;
+ }
+ )cpp",
+ "Declared in struct Foo\n\nint x",
+ },
+ {
+ R"cpp(// Field with initialization
+ struct Foo { int x = 5; };
+ int main() {
+ Foo bar;
+ bar.^x;
+ }
+ )cpp",
+ "Declared in struct Foo\n\nint x = 5",
+ },
+ {
+ R"cpp(// Static field
+ struct Foo { static int x; };
+ int main() {
+ Foo::^x;
+ }
+ )cpp",
+ "Declared in struct Foo\n\nstatic int x",
+ },
+ {
+ R"cpp(// Field, member initializer
+ struct Foo {
+ int x;
+ Foo() : ^x(0) {}
+ };
+ )cpp",
+ "Declared in struct Foo\n\nint x",
+ },
+ {
+ R"cpp(// Field, GNU old-style field designator
+ struct Foo { int x; };
+ int main() {
+ Foo bar = { ^x : 1 };
+ }
+ )cpp",
+ "Declared in struct Foo\n\nint x",
+ },
+ {
+ R"cpp(// Field, field designator
+ struct Foo { int x; };
+ int main() {
+ Foo bar = { .^x = 2 };
+ }
+ )cpp",
+ "Declared in struct Foo\n\nint x",
+ },
+ {
+ R"cpp(// Method call
+ struct Foo { int x(); };
+ int main() {
+ Foo bar;
+ bar.^x();
+ }
+ )cpp",
+ "Declared in struct Foo\n\nint x()",
+ },
+ {
+ R"cpp(// Static method call
+ struct Foo { static int x(); };
+ int main() {
+ Foo::^x();
+ }
+ )cpp",
+ "Declared in struct Foo\n\nstatic int x()",
+ },
+ {
+ R"cpp(// Typedef
+ typedef int Foo;
+ int main() {
+ ^Foo bar;
+ }
+ )cpp",
+ "Declared in global namespace\n\ntypedef int Foo",
+ },
+ {
+ R"cpp(// Namespace
+ namespace ns {
+ struct Foo { static void bar(); }
+ } // namespace ns
+ int main() { ^ns::Foo::bar(); }
+ )cpp",
+ "Declared in global namespace\n\nnamespace ns {\n}",
+ },
+ {
+ R"cpp(// Anonymous namespace
+ namespace ns {
+ namespace {
+ int foo;
+ } // anonymous namespace
+ } // namespace ns
+ int main() { ns::f^oo++; }
+ )cpp",
+ "Declared in namespace ns::(anonymous)\n\nint foo",
+ },
+ {
+ R"cpp(// Macro
+ #define MACRO 0
+ #define MACRO 1
+ int main() { return ^MACRO; }
+ #define MACRO 2
+ #undef macro
+ )cpp",
+ "#define MACRO",
+ },
+ {
+ R"cpp(// Forward class declaration
+ class Foo;
+ class Foo {};
+ F^oo* foo();
+ )cpp",
+ "Declared in global namespace\n\nclass Foo {}",
+ },
+ {
+ R"cpp(// Function declaration
+ void foo();
+ void g() { f^oo(); }
+ void foo() {}
+ )cpp",
+ "Declared in global namespace\n\nvoid foo()",
+ },
+ {
+ R"cpp(// Enum declaration
+ enum Hello {
+ ONE, TWO, THREE,
+ };
+ void foo() {
+ Hel^lo hello = ONE;
+ }
+ )cpp",
+ "Declared in global namespace\n\nenum Hello {\n}",
+ },
+ {
+ R"cpp(// Enumerator
+ enum Hello {
+ ONE, TWO, THREE,
+ };
+ void foo() {
+ Hello hello = O^NE;
+ }
+ )cpp",
+ "Declared in enum Hello\n\nONE",
+ },
+ {
+ R"cpp(// Enumerator in anonymous enum
+ enum {
+ ONE, TWO, THREE,
+ };
+ void foo() {
+ int hello = O^NE;
+ }
+ )cpp",
+ "Declared in enum (anonymous)\n\nONE",
+ },
+ {
+ R"cpp(// Global variable
+ static int hey = 10;
+ void foo() {
+ he^y++;
+ }
+ )cpp",
+ "Declared in global namespace\n\nstatic int hey = 10",
+ },
+ {
+ R"cpp(// Global variable in namespace
+ namespace ns1 {
+ static int hey = 10;
+ }
+ void foo() {
+ ns1::he^y++;
+ }
+ )cpp",
+ "Declared in namespace ns1\n\nstatic int hey = 10",
+ },
+ {
+ R"cpp(// Field in anonymous struct
+ static struct {
+ int hello;
+ } s;
+ void foo() {
+ s.he^llo++;
+ }
+ )cpp",
+ "Declared in struct (anonymous)\n\nint hello",
+ },
+ {
+ R"cpp(// Templated function
+ template <typename T>
+ T foo() {
+ return 17;
+ }
+ void g() { auto x = f^oo<int>(); }
+ )cpp",
+ "Declared in global namespace\n\ntemplate <typename T> T foo()",
+ },
+ {
+ R"cpp(// Anonymous union
+ struct outer {
+ union {
+ int abc, def;
+ } v;
+ };
+ void g() { struct outer o; o.v.d^ef++; }
+ )cpp",
+ "Declared in union outer::(anonymous)\n\nint def",
+ },
+ };
+
+ for (const OneTest &Test : Tests) {
+ Annotations T(Test.input);
+ auto AST = build(T.code());
+ Hover H = getHover(AST, T.point());
+
+ EXPECT_EQ(H.contents.value, Test.expectedHover.str()) << Test.input;
+ }
+}
+
} // namespace
} // namespace clangd
} // namespace clang
Index: test/clangd/initialize-params.test
===================================================================
--- test/clangd/initialize-params.test
+++ test/clangd/initialize-params.test
@@ -27,6 +27,7 @@
# CHECK-NEXT: "clangd.applyFix"
# CHECK-NEXT: ]
# CHECK-NEXT: },
+# CHECK-NEXT: "hoverProvider": true,
# CHECK-NEXT: "renameProvider": true,
# CHECK-NEXT: "signatureHelpProvider": {
# CHECK-NEXT: "triggerCharacters": [
Index: test/clangd/initialize-params-invalid.test
===================================================================
--- test/clangd/initialize-params-invalid.test
+++ test/clangd/initialize-params-invalid.test
@@ -27,6 +27,7 @@
# CHECK-NEXT: "clangd.applyFix"
# CHECK-NEXT: ]
# CHECK-NEXT: },
+# CHECK-NEXT: "hoverProvider": true,
# CHECK-NEXT: "renameProvider": true,
# CHECK-NEXT: "signatureHelpProvider": {
# CHECK-NEXT: "triggerCharacters": [
Index: test/clangd/hover.test
===================================================================
--- /dev/null
+++ test/clangd/hover.test
@@ -0,0 +1,19 @@
+# RUN: clangd -lit-test < %s | FileCheck %s
+{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}}
+---
+{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"file:///main.cpp","languageId":"cpp","version":1,"text":"void foo(); int main() { foo(); }\n"}}}
+---
+{"jsonrpc":"2.0","id":1,"method":"textDocument/hover","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":0,"character":27}}}
+# CHECK: "id": 1,
+# CHECK-NEXT: "jsonrpc": "2.0",
+# CHECK-NEXT: "result": {
+# CHECK-NEXT: "contents": {
+# CHECK-NEXT: "kind": "plaintext",
+# CHECK-NEXT: "value": "Declared in global namespace\n\nvoid foo()"
+# CHECK-NEXT: }
+# CHECK-NEXT: }
+# CHECK-NEXT:}
+---
+{"jsonrpc":"2.0","id":3,"method":"shutdown"}
+---
+{"jsonrpc":"2.0","method":"exit"}
Index: clangd/XRefs.h
===================================================================
--- clangd/XRefs.h
+++ clangd/XRefs.h
@@ -27,6 +27,9 @@
std::vector<DocumentHighlight> findDocumentHighlights(ParsedAST &AST,
Position Pos);
+/// Get the hover information when hovering at \p Pos.
+Hover getHover(ParsedAST &AST, Position Pos);
+
} // namespace clangd
} // namespace clang
#endif
Index: clangd/XRefs.cpp
===================================================================
--- clangd/XRefs.cpp
+++ clangd/XRefs.cpp
@@ -9,6 +9,7 @@
#include "XRefs.h"
#include "Logger.h"
#include "URI.h"
+#include "clang/AST/DeclTemplate.h"
#include "clang/Index/IndexDataConsumer.h"
#include "clang/Index/IndexingAction.h"
#include "llvm/Support/Path.h"
@@ -31,10 +32,15 @@
return nullptr;
}
+struct MacroDecl {
+ StringRef Name;
+ const MacroInfo *Info;
+};
+
/// Finds declarations locations that a given source location refers to.
class DeclarationAndMacrosFinder : public index::IndexDataConsumer {
std::vector<const Decl *> Decls;
- std::vector<const MacroInfo *> MacroInfos;
+ std::vector<MacroDecl> MacroInfos;
const SourceLocation &SearchedLocation;
const ASTContext &AST;
Preprocessor &PP;
@@ -54,10 +60,17 @@
return std::move(Decls);
}
- std::vector<const MacroInfo *> takeMacroInfos() {
+ std::vector<MacroDecl> takeMacroInfos() {
// Don't keep the same Macro info multiple times.
- std::sort(MacroInfos.begin(), MacroInfos.end());
- auto Last = std::unique(MacroInfos.begin(), MacroInfos.end());
+ std::sort(MacroInfos.begin(), MacroInfos.end(),
+ [](const MacroDecl &Left, const MacroDecl &Right) {
+ return Left.Info < Right.Info;
+ });
+
+ auto Last = std::unique(MacroInfos.begin(), MacroInfos.end(),
+ [](const MacroDecl &Left, const MacroDecl &Right) {
+ return Left.Info == Right.Info;
+ });
MacroInfos.erase(Last, MacroInfos.end());
return std::move(MacroInfos);
}
@@ -111,7 +124,7 @@
PP.getMacroDefinitionAtLoc(IdentifierInfo, BeforeSearchedLocation);
MacroInfo *MacroInf = MacroDef.getMacroInfo();
if (MacroInf) {
- MacroInfos.push_back(MacroInf);
+ MacroInfos.push_back(MacroDecl{IdentifierInfo->getName(), MacroInf});
}
}
}
@@ -176,8 +189,7 @@
DeclMacrosFinder, IndexOpts);
std::vector<const Decl *> Decls = DeclMacrosFinder->takeDecls();
- std::vector<const MacroInfo *> MacroInfos =
- DeclMacrosFinder->takeMacroInfos();
+ std::vector<MacroDecl> MacroInfos = DeclMacrosFinder->takeMacroInfos();
std::vector<Location> Result;
for (auto Item : Decls) {
@@ -187,7 +199,8 @@
}
for (auto Item : MacroInfos) {
- SourceRange SR(Item->getDefinitionLoc(), Item->getDefinitionEndLoc());
+ SourceRange SR(Item.Info->getDefinitionLoc(),
+ Item.Info->getDefinitionEndLoc());
auto L = getDeclarationLocation(AST, SR);
if (L)
Result.push_back(*L);
@@ -299,5 +312,140 @@
return DocHighlightsFinder->takeHighlights();
}
+static PrintingPolicy PrintingPolicyForDecls(PrintingPolicy Base) {
+ PrintingPolicy Policy(Base);
+
+ Policy.AnonymousTagLocations = false;
+ Policy.TerseOutput = true;
+ Policy.PolishForDeclaration = true;
+ Policy.ConstantsAsWritten = true;
+ Policy.SuppressTagKeyword = false;
+
+ return Policy;
+}
+
+/// Return a string representation (e.g. "class MyNamespace::MyClass") of
+/// the type declaration \p TD.
+static std::string TypeDeclToString(const TypeDecl *TD) {
+ QualType Type = TD->getASTContext().getTypeDeclType(TD);
+
+ PrintingPolicy Policy =
+ PrintingPolicyForDecls(TD->getASTContext().getPrintingPolicy());
+
+ std::string Name;
+ llvm::raw_string_ostream Stream(Name);
+ Type.print(Stream, Policy);
+
+ return Name;
+}
+
+/// Return a string representation (e.g. "namespace ns1::ns2") of
+/// the named declaration \p ND.
+static std::string NamedDeclQualifiedName(const NamedDecl *ND,
+ StringRef Prefix) {
+ PrintingPolicy Policy =
+ PrintingPolicyForDecls(ND->getASTContext().getPrintingPolicy());
+
+ std::string Name;
+ llvm::raw_string_ostream Stream(Name);
+ Stream << Prefix << ' ';
+ ND->printQualifiedName(Stream, Policy);
+
+ return Name;
+}
+
+/// Given a declaration \p D, return a human-readable string representing the
+/// scope in which it is declared. If the declaration is in the global scope,
+/// return the string "global namespace".
+static llvm::Optional<std::string> getScopeName(const Decl *D) {
+ const DeclContext *DC = D->getDeclContext();
+
+ if (const TranslationUnitDecl *TUD = dyn_cast<TranslationUnitDecl>(DC))
+ return std::string("global namespace");
+
+ if (const TypeDecl *TD = dyn_cast<TypeDecl>(DC))
+ return TypeDeclToString(TD);
+ else if (const NamespaceDecl *ND = dyn_cast<NamespaceDecl>(DC))
+ return NamedDeclQualifiedName(ND, "namespace");
+ else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(DC))
+ return NamedDeclQualifiedName(FD, "function");
+
+ return {};
+}
+
+/// Generate a \p Hover object given the declaration \p D.
+static Hover getHoverContents(const Decl *D) {
+ Hover H;
+ llvm::Optional<std::string> 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";
+ }
+
+ // We want to include the template in the Hover.
+ if (TemplateDecl *TD = D->getDescribedTemplate())
+ D = TD;
+
+ // SourceRange SR = D->getSourceRange();
+
+ std::string DeclText;
+ llvm::raw_string_ostream OS(DeclText);
+
+ PrintingPolicy Policy =
+ PrintingPolicyForDecls(D->getASTContext().getPrintingPolicy());
+
+ D->print(OS, Policy);
+
+ OS.flush();
+
+ H.contents.value += DeclText;
+ return H;
+}
+
+/// Generate a \p Hover object given the macro \p MacroInf.
+static Hover getHoverContents(StringRef MacroName) {
+ Hover H;
+
+ H.contents.value = "#define ";
+ H.contents.value += MacroName;
+
+ return H;
+}
+
+Hover getHover(ParsedAST &AST, Position Pos) {
+ const SourceManager &SourceMgr = AST.getASTContext().getSourceManager();
+ const FileEntry *FE = SourceMgr.getFileEntryForID(SourceMgr.getMainFileID());
+ if (FE == nullptr)
+ return Hover();
+
+ SourceLocation SourceLocationBeg = getBeginningOfIdentifier(AST, Pos, FE);
+ auto DeclMacrosFinder = std::make_shared<DeclarationAndMacrosFinder>(
+ llvm::errs(), SourceLocationBeg, AST.getASTContext(),
+ AST.getPreprocessor());
+
+ index::IndexingOptions IndexOpts;
+ IndexOpts.SystemSymbolFilter =
+ index::IndexingOptions::SystemSymbolFilterKind::All;
+ IndexOpts.IndexFunctionLocals = true;
+
+ indexTopLevelDecls(AST.getASTContext(), AST.getTopLevelDecls(),
+ DeclMacrosFinder, IndexOpts);
+
+ std::vector<MacroDecl> Macros = DeclMacrosFinder->takeMacroInfos();
+ if (!Macros.empty())
+ return getHoverContents(Macros[0].Name);
+
+ std::vector<const Decl *> Decls = DeclMacrosFinder->takeDecls();
+ if (!Decls.empty())
+ return getHoverContents(Decls[0]);
+
+ return Hover();
+}
+
} // namespace clangd
} // namespace clang
Index: clangd/ProtocolHandlers.h
===================================================================
--- clangd/ProtocolHandlers.h
+++ clangd/ProtocolHandlers.h
@@ -51,6 +51,7 @@
virtual void onCommand(ExecuteCommandParams &Params) = 0;
virtual void onRename(RenameParams &Parames) = 0;
virtual void onDocumentHighlight(TextDocumentPositionParams &Params) = 0;
+ virtual void onHover(TextDocumentPositionParams &Params) = 0;
};
void registerCallbackHandlers(JSONRPCDispatcher &Dispatcher, JSONOutput &Out,
Index: clangd/ProtocolHandlers.cpp
===================================================================
--- clangd/ProtocolHandlers.cpp
+++ clangd/ProtocolHandlers.cpp
@@ -67,6 +67,7 @@
Register("textDocument/switchSourceHeader",
&ProtocolCallbacks::onSwitchSourceHeader);
Register("textDocument/rename", &ProtocolCallbacks::onRename);
+ Register("textDocument/hover", &ProtocolCallbacks::onHover);
Register("workspace/didChangeWatchedFiles", &ProtocolCallbacks::onFileEvent);
Register("workspace/executeCommand", &ProtocolCallbacks::onCommand);
Register("textDocument/documentHighlight",
Index: clangd/Protocol.h
===================================================================
--- clangd/Protocol.h
+++ clangd/Protocol.h
@@ -406,6 +406,27 @@
};
bool fromJSON(const json::Expr &, TextDocumentPositionParams &);
+enum class MarkupKind {
+ PlainText,
+ Markdown,
+};
+
+struct MarkupContent {
+ MarkupKind kind = MarkupKind::PlainText;
+ std::string value;
+};
+json::Expr toJSON(const MarkupContent &MC);
+
+struct Hover {
+ /// The hover's content
+ MarkupContent contents;
+
+ /// An optional range is a range inside a text document
+ /// that is used to visualize a hover, e.g. by changing the background color.
+ llvm::Optional<Range> range;
+};
+json::Expr toJSON(const Hover &H);
+
/// The kind of a completion entry.
enum class CompletionItemKind {
Missing = 0,
Index: clangd/Protocol.cpp
===================================================================
--- clangd/Protocol.cpp
+++ clangd/Protocol.cpp
@@ -320,6 +320,36 @@
O.map("position", R.position);
}
+static StringRef toTextKind(MarkupKind Kind)
+{
+ switch (Kind) {
+ case MarkupKind::PlainText:
+ return "plaintext";
+ case MarkupKind::Markdown:
+ return "markdown";
+ }
+ llvm_unreachable("Invalid MarkupKind");
+}
+
+json::Expr toJSON(const MarkupContent &MC) {
+ if (MC.value.empty())
+ return nullptr;
+
+ return json::obj{
+ {"kind", toTextKind(MC.kind)},
+ {"value", MC.value},
+ };
+}
+
+json::Expr toJSON(const Hover &H) {
+ json::obj Result{{"contents", toJSON(H.contents)}};
+
+ if (H.range.hasValue())
+ Result["range"] = toJSON(*H.range);
+
+ return Result;
+}
+
json::Expr toJSON(const CompletionItem &CI) {
assert(!CI.label.empty() && "completion item label is required");
json::obj Result{{"label", CI.label}};
Index: clangd/ClangdServer.h
===================================================================
--- clangd/ClangdServer.h
+++ clangd/ClangdServer.h
@@ -206,6 +206,9 @@
llvm::Expected<Tagged<std::vector<DocumentHighlight>>>
findDocumentHighlights(PathRef File, Position Pos);
+ /// Get code hover for a given position.
+ llvm::Expected<Tagged<Hover>> findHover(PathRef File, Position Pos);
+
/// Run formatting for \p Rng inside \p File with content \p Code.
llvm::Expected<tooling::Replacements> formatRange(StringRef Code,
PathRef File, Range Rng);
Index: clangd/ClangdServer.cpp
===================================================================
--- clangd/ClangdServer.cpp
+++ clangd/ClangdServer.cpp
@@ -462,6 +462,28 @@
return blockingRunWithAST<RetType>(WorkScheduler, File, Action);
}
+llvm::Expected<Tagged<Hover>> ClangdServer::findHover(PathRef File,
+ Position Pos) {
+ Hover FinalHover;
+ auto FileContents = DraftMgr.getDraft(File);
+ if (!FileContents.Draft)
+ return llvm::make_error<llvm::StringError>(
+ "findDocumentHighlights called on non-added file",
+ llvm::errc::invalid_argument);
+
+ auto TaggedFS = FSProvider.getTaggedFileSystem(File);
+
+ using RetType = llvm::Expected<Tagged<Hover>>;
+ auto Action = [=](llvm::Expected<InputsAndAST> InpAST) -> RetType {
+ if (!InpAST)
+ return InpAST.takeError();
+
+ auto Result = clangd::getHover(InpAST->AST, Pos);
+ return make_tagged(std::move(Result), TaggedFS.Tag);
+ };
+ return blockingRunWithAST<RetType>(WorkScheduler, File, Action);
+}
+
void ClangdServer::scheduleReparseAndDiags(
PathRef File, VersionedDraft Contents,
Tagged<IntrusiveRefCntPtr<vfs::FileSystem>> TaggedFS) {
Index: clangd/ClangdLSPServer.h
===================================================================
--- clangd/ClangdLSPServer.h
+++ clangd/ClangdLSPServer.h
@@ -75,6 +75,7 @@
void onFileEvent(DidChangeWatchedFilesParams &Params) override;
void onCommand(ExecuteCommandParams &Params) override;
void onRename(RenameParams &Parames) override;
+ void onHover(TextDocumentPositionParams &Params) override;
std::vector<TextEdit> getFixIts(StringRef File, const clangd::Diagnostic &D);
Index: clangd/ClangdLSPServer.cpp
===================================================================
--- clangd/ClangdLSPServer.cpp
+++ clangd/ClangdLSPServer.cpp
@@ -110,6 +110,7 @@
}},
{"definitionProvider", true},
{"documentHighlightProvider", true},
+ {"hoverProvider", true},
{"renameProvider", true},
{"executeCommandProvider",
json::obj{
@@ -315,6 +316,18 @@
reply(json::ary(Highlights->Value));
}
+void ClangdLSPServer::onHover(TextDocumentPositionParams &Params) {
+ llvm::Expected<Tagged<Hover>> H =
+ Server.findHover(Params.textDocument.uri.file, Params.position);
+
+ if (!H) {
+ replyError(ErrorCode::InternalError, llvm::toString(H.takeError()));
+ return;
+ }
+
+ reply(H->Value);
+}
+
ClangdLSPServer::ClangdLSPServer(JSONOutput &Out, unsigned AsyncThreadsCount,
bool StorePreamblesInMemory,
const clangd::CodeCompleteOptions &CCOpts,
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits