simark updated this revision to Diff 133367.
simark added a comment.
Herald added subscribers: ioeric, jkorous-apple.

New version

Here's a new version of the patch, quite reworked.  I scaled down the scope a
little bit to make it simpler (see commit log).  We'll add more features later
on.


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
@@ -227,6 +227,316 @@
   }
 }
 
+TEST(Hover, All) {
+
+  struct OneTest {
+    const char *input;
+    const char *expectedHover;
+  };
+
+  OneTest Tests[] = {
+      {
+          R"cpp(// Local variable
+            int main() {
+              int bonjour;
+              ^bonjour = 2;
+              int test1 = bonjour;
+            }
+          )cpp",
+          "Declared in main\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",
+      },
+      {
+          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 1",
+      },
+      {
+          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",
+      },
+      {
+          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>\nT foo()",
+      },
+      {
+          R"cpp(// Alternative indentation
+            namespace {
+              template <typename T>
+              T
+              foo()
+              {
+                return 17;
+              }
+            } // anonymous namespace
+            void g() { auto x = f^oo<int>(); }
+          )cpp",
+          "Declared in namespace (anonymous)\n\ntemplate <typename "
+          "T>\nT\nfoo()",
+      },
+      {
+          R"cpp(// Alternative indentation
+            struct outer {
+              union {
+                int abc, def;
+              } v;
+            };
+            void g() { struct outer o; o.v.d^ef++; }
+          )cpp",
+          "Declared in union outer::(anonymous)\n\nint abc, 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) << 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 -run-synchronously < %s | FileCheck %s
+# It is absolutely vital that this file has CRLF line endings.
+#
+Content-Length: 127
+
+{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}}
+Content-Length: 180
+
+{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"file:///main.cpp","languageId":"cpp","version":1,"text":"void foo(); int main() { foo(); }\n"}}}
+Content-Length: 146
+
+{"jsonrpc":"2.0","id":1,"method":"textDocument/hover","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":0,"character":27}}}
+# CHECK: {"id":1,"jsonrpc":"2.0","result":{"contents":{"kind":"plaintext","value":"Declared in global namespace\n\nvoid foo()"}}}
+Content-Length: 46
+
+{"jsonrpc":"2.0","id":3,"method":"shutdown"}
+Content-Length: 35
+
+{"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"
 namespace clang {
@@ -291,5 +292,277 @@
   return DocHighlightsFinder->takeHighlights();
 }
 
+/// Returns the file buffer data that the given Location \p L points to.
+static Optional<StringRef> getStringFromSourceRange(const ParsedAST &AST,
+                                                    Location L) {
+  const SourceManager &SourceMgr = AST.getASTContext().getSourceManager();
+  const FileEntry *FE = SourceMgr.getFileManager().getFile(L.uri.file);
+
+  if (FE == nullptr)
+    return {};
+
+  FileID FID = SourceMgr.translateFile(FE);
+  StringRef TextRef = SourceMgr.getBufferData(FID);
+
+  SourceLocation StartLoc = SourceMgr.translateFileLineCol(
+      FE, L.range.start.line + 1, L.range.start.character + 1);
+  SourceLocation EndLoc = SourceMgr.translateFileLineCol(
+      FE, L.range.end.line + 1, L.range.end.character + 1);
+
+  unsigned Start = SourceMgr.getFileOffset(StartLoc);
+  unsigned End = SourceMgr.getFileOffset(EndLoc);
+
+  return TextRef.slice(Start, End);
+}
+
+/// Return a string representation (e.g. "class MyNamespace::MyClass") of
+/// the named declaration \p ND.
+static std::string NamedDeclToString(const NamedDecl *ND) {
+  std::string Name;
+  llvm::raw_string_ostream Stream(Name);
+  const char *keyword = nullptr;
+
+  if (const TagDecl *TD = dyn_cast<TagDecl>(ND)) {
+    switch (TD->getTagKind()) {
+    case TTK_Class:
+      keyword = "class";
+      break;
+    case TTK_Enum:
+      keyword = "enum";
+      break;
+    case TTK_Struct:
+      keyword = "struct";
+      break;
+    case TTK_Union:
+      keyword = "union";
+      break;
+    case TTK_Interface:
+      keyword = "__interface";
+      break;
+    }
+  } else if (const NamespaceDecl *NSD = dyn_cast<NamespaceDecl>(ND))
+    keyword = "namespace";
+
+  if (keyword != nullptr)
+    Stream << keyword << ' ';
+
+  ND->printQualifiedName(Stream);
+  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();
+
+  const TranslationUnitDecl *TUD = dyn_cast<TranslationUnitDecl>(DC);
+  if (TUD != nullptr)
+    return std::string("global namespace");
+
+  const NamedDecl *ND = dyn_cast<NamedDecl>(DC);
+  if (ND != nullptr)
+    return NamedDeclToString(ND);
+
+  return {};
+}
+
+/// Find the indentation present before the start of the declaration at
+/// \p DeclLocation and remove it from subsequent lines in \p DeclText.
+static void tryFixupIndentation(std::string *DeclText,
+                                const Location &DeclLocation,
+                                const ParsedAST &AST) {
+  Location IndentationLoc = DeclLocation;
+
+  IndentationLoc.range.end = IndentationLoc.range.start;
+  IndentationLoc.range.start.character = 0;
+
+  Optional<StringRef> Indentation =
+      getStringFromSourceRange(AST, IndentationLoc);
+
+  if (!Indentation)
+    return;
+
+  // Be conservative, bail out if the declaration is preceded by anything else
+  // than tabs and spaces.
+  for (unsigned char c : *Indentation) {
+    if (c != ' ' && c != '\t')
+      return;
+  }
+
+  std::string Needle = "\n";
+  Needle += *Indentation;
+
+  std::string::size_type pos = DeclText->find(Needle);
+  while (pos != std::string::npos) {
+    // Remove the indentation (but don't remove the \n!).
+    DeclText->erase(pos + 1, Needle.length() - 1);
+    pos = DeclText->find(Needle, pos + 1);
+  }
+}
+
+/// Strip whitespaces and the opening bracket from the end of \p DeclText.  For
+/// example, this:
+///
+///   int foo(int x) {
+///
+/// will become
+///
+///  int foo(int x)
+///
+static void stripOpeningBracket(std::string *DeclText) {
+  while (!DeclText->empty() &&
+         (DeclText->back() == ' ' || DeclText->back() == '\t' ||
+          DeclText->back() == '\n' || DeclText->back() == '{'))
+    DeclText->pop_back();
+}
+
+/// Generate a \p Hover object given the declaration \p D.
+static Hover getHoverContents(const Decl *D, ParsedAST &AST) {
+  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";
+  }
+
+  SourceRange SR = D->getSourceRange();
+
+  // We want to include the template in the Hover.
+  TemplateDecl *TD = D->getDescribedTemplate();
+  if (TD != nullptr)
+    SR.setBegin(TD->getSourceRange().getBegin());
+
+  // If this is a function, class, namespace, etc., we don't want to include the
+  // body.  Adjust the end of the source range so it doesn't include it.
+  if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
+    if (FD->isThisDeclarationADefinition() && FD->getBody() != NULL)
+      SR.setEnd(FD->getBody()->getLocStart());
+  } else if (const RecordDecl *RD = dyn_cast<RecordDecl>(D)) {
+    if (RD->isThisDeclarationADefinition())
+      SR.setEnd(RD->getBraceRange().getBegin());
+  } else if (const EnumDecl *ED = dyn_cast<EnumDecl>(D)) {
+    // C++ doesn't allow enum declarations that are not definitions, so this
+    // should always be true.
+    assert(ED->isThisDeclarationADefinition());
+    SR.setEnd(ED->getBraceRange().getBegin());
+  } else if (const NamespaceDecl *ND = dyn_cast<NamespaceDecl>(D)) {
+    // The AST doesn't seem the contain the location of the opening bracket as
+    // for the other Decl types.  Work around this by building the string
+    // "namespace foo" by hand.
+    H.contents.value += "namespace ";
+    H.contents.value += ND->getName();
+    return H;
+  }
+
+  if (!SR.isValid())
+    return Hover();
+
+  llvm::Optional<Location> L = getDeclarationLocation(AST, SR);
+  if (!L.hasValue())
+    return Hover();
+
+  Optional<StringRef> TextRef = getStringFromSourceRange(AST, *L);
+  if (!TextRef)
+    return Hover();
+
+  std::string DeclText = *TextRef;
+
+  // If the declaration spans multiple lines and is indented, the first line
+  // won't contain the indentation but the subsequent ones will.  A declaration
+  // indented like this:
+  //
+  //         template <typename T>
+  //         void foo (int x,
+  //                   int y)
+  //
+  // will give a result like this:
+  //
+  // template <typename T>
+  //         void foo (int x,
+  //                   int y)
+  //
+  // Try to fix it by removing the indentation found before the declaration
+  // from the subsequent lines.  The result will look like:
+  //
+  // template <typename T>
+  // void foo (int x,
+  //           int y)
+  //
+  tryFixupIndentation(&DeclText, *L, AST);
+
+  // If the referenced source text contains an opening bracket, strip it.
+  stripOpeningBracket(&DeclText);
+
+  H.contents.value += DeclText;
+
+  return H;
+}
+
+/// Generate a \p Hover object given the macro \p MacroInf.
+static Hover getHoverContents(const MacroInfo *MacroInf, ParsedAST &AST) {
+  SourceLocation begin = MacroInf->getDefinitionLoc();
+  SourceLocation end = MacroInf->getDefinitionEndLoc();
+
+  if (!begin.isValid() || !end.isValid())
+    return Hover();
+
+  llvm::Optional<Location> L =
+      getDeclarationLocation(AST, SourceRange(begin, end));
+  if (!L.hasValue())
+    return Hover();
+
+  Optional<StringRef> TextRef = getStringFromSourceRange(AST, *L);
+  if (!TextRef)
+    return Hover();
+
+  Hover H;
+  H.contents.value = "#define ";
+  H.contents.value += *TextRef;
+
+  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<const Decl *> Decls = DeclMacrosFinder->takeDecls();
+  if (!Decls.empty()) {
+    assert(Decls[0] != nullptr);
+    if (Decls[0] != nullptr)
+      return getHoverContents(Decls[0], AST);
+  }
+
+  std::vector<const MacroInfo *> Macros = DeclMacrosFinder->takeMacroInfos();
+  if (!Macros.empty()) {
+    assert(Macros[0] != nullptr);
+    if (Macros[0] != nullptr)
+      return getHoverContents(Macros[0], AST);
+  }
+
+  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
@@ -407,6 +407,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,38 @@
          O.map("position", R.position);
 }
 
+json::Expr toJSON(const MarkupContent &MC) {
+  const char *KindStr = NULL;
+
+  if (MC.value.empty())
+    return nullptr;
+
+  assert(MC.kind == MarkupKind::PlainText || MC.kind == MarkupKind::Markdown);
+
+  switch (MC.kind) {
+  case MarkupKind::PlainText:
+    KindStr = "plaintext";
+    break;
+  case MarkupKind::Markdown:
+    KindStr = "markdown";
+    break;
+  }
+
+  return json::obj{
+      {"kind", KindStr},
+      {"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
@@ -223,6 +223,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
@@ -494,6 +494,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);
+}
+
 std::future<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{
@@ -320,6 +321,19 @@
   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
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to