Nebiroth updated this revision to Diff 124116.
Nebiroth marked 32 inline comments as done.
Nebiroth added a comment.

Minor code cleanup and improvements
getRawCommentForDeclNoCache() now called for every Decl and only once per Decl
getHover() now properly handles templates


https://reviews.llvm.org/D35894

Files:
  clangd/ClangdLSPServer.cpp
  clangd/ClangdLSPServer.h
  clangd/ClangdServer.cpp
  clangd/ClangdServer.h
  clangd/ClangdUnit.cpp
  clangd/ClangdUnit.h
  clangd/Protocol.cpp
  clangd/Protocol.h
  clangd/ProtocolHandlers.cpp
  clangd/ProtocolHandlers.h
  test/clangd/hover.test
  test/clangd/initialize-params-invalid.test
  test/clangd/initialize-params.test

Index: test/clangd/initialize-params.test
===================================================================
--- test/clangd/initialize-params.test
+++ test/clangd/initialize-params.test
@@ -30,6 +30,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
@@ -30,6 +30,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,52 @@
+# RUN: clangd -run-synchronously < %s | FileCheck %s
+# It is absolutely vital that this file has CRLF line endings.
+#
+Content-Length: 125
+
+{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}}
+
+Content-Length: 407
+
+{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"file:///main.cpp","languageId":"cpp","version":1,"text":"#define MACRO 1\nnamespace ns1 {\nint test = 5;\nstruct MyClass {\nint xasd;\nvoid anotherOperation() {\n}\nstatic int foo(MyClass*) {\nreturn 0;\n}\n\n};}\nint main() {\nint a;\na;\nint b = ns1::test;\nns1::MyClass* Params;\nParams->anotherOperation();\nMACRO;}\n"}}}
+
+Content-Length: 144
+
+{"jsonrpc":"2.0","id":1,"method":"textDocument/hover","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":0,"character":12}}}
+# Go to local variable
+# CHECK: {"id":1,"jsonrpc":"2.0","result":{"contents":[{"language":"C++","value":"#define MACRO 1"}],"range":{"end":{"character":15,"line":0},"start":{"character":8,"line":0}}}}
+
+Content-Length: 144
+
+{"jsonrpc":"2.0","id":1,"method":"textDocument/hover","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":14,"character":1}}}
+# Go to local variable
+# CHECK: {"id":1,"jsonrpc":"2.0","result":{"contents":[{"language":"C++","value":"int a"}],"range":{"end":{"character":5,"line":13},"start":{"character":0,"line":13}}}}
+
+Content-Length: 145
+
+{"jsonrpc":"2.0","id":1,"method":"textDocument/hover","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":15,"character":15}}}
+# Go to local variable
+# CHECK: {"id":1,"jsonrpc":"2.0","result":{"contents":[{"language":"C++","value":"In ns1"},{"language":"C++","value":"int test = 5"}],"range":{"end":{"character":12,"line":2},"start":{"character":0,"line":2}}}}
+
+Content-Length: 145
+
+{"jsonrpc":"2.0","id":1,"method":"textDocument/hover","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":16,"character":10}}}
+# Go to local variable
+# CHECK: {"id":1,"jsonrpc":"2.0","result":{"contents":[{"language":"C++","value":"In ns1"},{"language":"C++","value":"struct MyClass {"}],"range":{"end":{"character":16,"line":3},"start":{"character":0,"line":3}}}}
+
+Content-Length: 145
+
+{"jsonrpc":"2.0","id":1,"method":"textDocument/hover","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":17,"character":13}}}
+# Go to local variable
+# CHECK: {"id":1,"jsonrpc":"2.0","result":{"contents":[{"language":"C++","value":"In ns1::MyClass"},{"language":"C++","value":"void anotherOperation() {"}],"range":{"end":{"character":25,"line":5},"start":{"character":0,"line":5}}}}
+
+Content-Length: 145
+
+{"jsonrpc":"2.0","id":1,"method":"textDocument/hover","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":18,"character":1}}}
+# Go to local variable
+# CHECK: {"id":1,"jsonrpc":"2.0","result":{"contents":[{"language":"C++","value":"#define MACRO 1"}],"range":{"end":{"character":15,"line":0},"start":{"character":8,"line":0}}}}
+
+Content-Length: 44
+
+{"jsonrpc":"2.0","id":3,"method":"shutdown"}
+
+
Index: clangd/ProtocolHandlers.h
===================================================================
--- clangd/ProtocolHandlers.h
+++ clangd/ProtocolHandlers.h
@@ -54,6 +54,7 @@
   virtual void onFileEvent(Ctx C, DidChangeWatchedFilesParams &Params) = 0;
   virtual void onCommand(Ctx C, ExecuteCommandParams &Params) = 0;
   virtual void onRename(Ctx C, RenameParams &Parames) = 0;
+  virtual void onCodeHover(Ctx C, TextDocumentPositionParams &Params) = 0;
 };
 
 void registerCallbackHandlers(JSONRPCDispatcher &Dispatcher, JSONOutput &Out,
Index: clangd/ProtocolHandlers.cpp
===================================================================
--- clangd/ProtocolHandlers.cpp
+++ clangd/ProtocolHandlers.cpp
@@ -72,6 +72,7 @@
   Register("textDocument/switchSourceHeader",
            &ProtocolCallbacks::onSwitchSourceHeader);
   Register("textDocument/rename", &ProtocolCallbacks::onRename);
+  Register("textDocument/hover", &ProtocolCallbacks::onCodeHover);
   Register("workspace/didChangeWatchedFiles", &ProtocolCallbacks::onFileEvent);
   Register("workspace/executeCommand", &ProtocolCallbacks::onCommand);
 }
Index: clangd/Protocol.h
===================================================================
--- clangd/Protocol.h
+++ clangd/Protocol.h
@@ -449,6 +449,50 @@
   parse(llvm::yaml::MappingNode *Params, clangd::Logger &Logger);
 };
 
+struct MarkedString {
+  /**
+   * MarkedString can be used to render human readable text. It is either a
+   * markdown string
+   * or a code-block that provides a language and a code snippet. The language
+   * identifier
+   * is sematically equal to the optional language identifier in fenced code
+   * blocks in GitHub
+   * issues. See
+   * https://help.github.com/articles/creating-and-highlighting-code-blocks/#syntax-highlighting
+   *
+   * The pair of a language and a value is an equivalent to markdown:
+   * ```
+   * ${language}
+   * ${value}
+   * ```
+   *
+   * Note that markdown strings will be sanitized - that means html will be
+   * escaped.
+   */
+
+  std::string markdownString;
+  std::string language;
+  std::string codeBlockValue;
+
+  static json::Expr unparse(const MarkedString &MS);
+};
+
+struct Hover {
+
+  /**
+   * The hover's content
+   */
+  std::vector<MarkedString> 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;
+
+  static json::Expr unparse(const Hover &H);
+};
+
 /// The kind of a completion entry.
 enum class CompletionItemKind {
   Missing = 0,
Index: clangd/Protocol.cpp
===================================================================
--- clangd/Protocol.cpp
+++ clangd/Protocol.cpp
@@ -1014,6 +1014,30 @@
   return Result;
 }
 
+json::Expr Hover::unparse(const Hover &H) {
+  if (H.range.hasValue()) {
+    return json::obj{
+        {"contents", json::ary(H.contents)},
+        {"range", H.range.getValue()},
+    };
+  } else {
+    // Default Hover values
+    Range DefaultRange;
+    Hover H;
+    return json::obj{
+        {"contents", json::ary(H.contents)},
+        {"range", DefaultRange},
+    };
+  }
+}
+
+json::Expr MarkedString::unparse(const MarkedString &MS) {
+  return json::obj{
+      {"language", MS.language},
+      {"value", MS.codeBlockValue},
+  };
+}
+
 json::Expr CompletionItem::unparse(const CompletionItem &CI) {
   assert(!CI.label.empty() && "completion item label is required");
   json::obj Result{{"label", CI.label}};
Index: clangd/ClangdUnit.h
===================================================================
--- clangd/ClangdUnit.h
+++ clangd/ClangdUnit.h
@@ -313,6 +313,13 @@
 std::vector<Location> findDefinitions(ParsedAST &AST, Position Pos,
                                       clangd::Logger &Logger);
 
+Hover getHover(ParsedAST &AST, Position Pos, clangd::Logger &Logger);
+
+std::string getScope(const NamedDecl *ND, const LangOptions &LangOpts);
+
+StringRef getDataBufferFromSourceRange(ParsedAST &AST, SourceRange SR,
+                                       Location L);
+
 /// For testing/debugging purposes. Note that this method deserializes all
 /// unserialized Decls, so use with care.
 void dumpAST(ParsedAST &AST, llvm::raw_ostream &OS);
Index: clangd/ClangdUnit.cpp
===================================================================
--- clangd/ClangdUnit.cpp
+++ clangd/ClangdUnit.cpp
@@ -209,6 +209,7 @@
   // EndSourceFile), but that won't happen if DisableFree is set to true.
   // Since createInvocationFromCommandLine sets it to true, we have to override
   // it.
+  CI->LangOpts->CommentOpts.ParseAllComments = true;
   CI->getFrontendOpts().DisableFree = false;
   return CI;
 }
@@ -939,37 +940,46 @@
   return Mgr.getMacroArgExpandedLocation(InputLoc);
 }
 
-/// Finds declarations locations that a given source location refers to.
-class DeclarationLocationsFinder : public index::IndexDataConsumer {
-  std::vector<Location> DeclarationLocations;
+/// Finds declarations  and macros that a given source location refers to.
+class DeclarationAndMacrosFinder : public index::IndexDataConsumer {
+  std::vector<const Decl *> Decls;
+  std::vector<const MacroInfo *> MacroInfos;
   const SourceLocation &SearchedLocation;
   const ASTContext &AST;
   Preprocessor &PP;
 
 public:
-  DeclarationLocationsFinder(raw_ostream &OS,
+  DeclarationAndMacrosFinder(raw_ostream &OS,
                              const SourceLocation &SearchedLocation,
                              ASTContext &AST, Preprocessor &PP)
       : SearchedLocation(SearchedLocation), AST(AST), PP(PP) {}
 
-  std::vector<Location> takeLocations() {
+  std::vector<const Decl *> takeDecls() {
     // Don't keep the same location multiple times.
     // This can happen when nodes in the AST are visited twice.
-    std::sort(DeclarationLocations.begin(), DeclarationLocations.end());
-    auto last =
-        std::unique(DeclarationLocations.begin(), DeclarationLocations.end());
-    DeclarationLocations.erase(last, DeclarationLocations.end());
-    return std::move(DeclarationLocations);
+    std::sort(Decls.begin(), Decls.end());
+    auto Last = std::unique(Decls.begin(), Decls.end());
+    Decls.erase(Last, Decls.end());
+    return Decls;
+  }
+
+  std::vector<const MacroInfo *> takeMacroInfos() {
+    // Don't keep the same Macro info multiple times.
+    // This can happen when nodes in the AST are visited twice.
+    std::sort(MacroInfos.begin(), MacroInfos.end());
+    auto Last = std::unique(MacroInfos.begin(), MacroInfos.end());
+    MacroInfos.erase(Last, MacroInfos.end());
+    return MacroInfos;
   }
 
   bool
   handleDeclOccurence(const Decl *D, index::SymbolRoleSet Roles,
                       ArrayRef<index::SymbolRelation> Relations, FileID FID,
                       unsigned Offset,
                       index::IndexDataConsumer::ASTNodeInfo ASTNode) override {
-    if (isSearchedLocation(FID, Offset)) {
-      addDeclarationLocation(D->getSourceRange());
-    }
+
+    if (isSearchedLocation(FID, Offset))
+      Decls.push_back(D);
     return true;
   }
 
@@ -980,31 +990,6 @@
            SourceMgr.getFileID(SearchedLocation) == FID;
   }
 
-  void addDeclarationLocation(const SourceRange &ValSourceRange) {
-    const SourceManager &SourceMgr = AST.getSourceManager();
-    const LangOptions &LangOpts = AST.getLangOpts();
-    SourceLocation LocStart = ValSourceRange.getBegin();
-    SourceLocation LocEnd = Lexer::getLocForEndOfToken(ValSourceRange.getEnd(),
-                                                       0, SourceMgr, LangOpts);
-    Position Begin;
-    Begin.line = SourceMgr.getSpellingLineNumber(LocStart) - 1;
-    Begin.character = SourceMgr.getSpellingColumnNumber(LocStart) - 1;
-    Position End;
-    End.line = SourceMgr.getSpellingLineNumber(LocEnd) - 1;
-    End.character = SourceMgr.getSpellingColumnNumber(LocEnd) - 1;
-    Range R = {Begin, End};
-    Location L;
-    if (const FileEntry *F =
-            SourceMgr.getFileEntryForID(SourceMgr.getFileID(LocStart))) {
-      StringRef FilePath = F->tryGetRealPathName();
-      if (FilePath.empty())
-        FilePath = F->getName();
-      L.uri = URI::fromFile(FilePath);
-      L.range = R;
-      DeclarationLocations.push_back(L);
-    }
-  }
-
   void finish() override {
     // Also handle possible macro at the searched location.
     Token Result;
@@ -1027,16 +1012,41 @@
             PP.getMacroDefinitionAtLoc(IdentifierInfo, BeforeSearchedLocation);
         MacroInfo *MacroInf = MacroDef.getMacroInfo();
         if (MacroInf) {
-          addDeclarationLocation(SourceRange(MacroInf->getDefinitionLoc(),
-                                             MacroInf->getDefinitionEndLoc()));
+          MacroInfos.push_back(MacroInf);
         }
       }
     }
   }
 };
 
 } // namespace
 
+Location getDeclarationLocation(ParsedAST &AST,
+                                const SourceRange &ValSourceRange) {
+  const SourceManager &SourceMgr = AST.getASTContext().getSourceManager();
+  const LangOptions &LangOpts = AST.getASTContext().getLangOpts();
+  SourceLocation LocStart = ValSourceRange.getBegin();
+  SourceLocation LocEnd = Lexer::getLocForEndOfToken(ValSourceRange.getEnd(), 0,
+                                                     SourceMgr, LangOpts);
+  Position Begin;
+  Begin.line = SourceMgr.getSpellingLineNumber(LocStart) - 1;
+  Begin.character = SourceMgr.getSpellingColumnNumber(LocStart) - 1;
+  Position End;
+  End.line = SourceMgr.getSpellingLineNumber(LocEnd) - 1;
+  End.character = SourceMgr.getSpellingColumnNumber(LocEnd) - 1;
+  Range R = {Begin, End};
+  Location L;
+  if (const FileEntry *F =
+          SourceMgr.getFileEntryForID(SourceMgr.getFileID(LocStart))) {
+    StringRef FilePath = F->tryGetRealPathName();
+    if (FilePath.empty())
+      FilePath = F->getName();
+    L.uri = URI::fromFile(FilePath);
+    L.range = R;
+  }
+  return L;
+}
+
 std::vector<Location> clangd::findDefinitions(ParsedAST &AST, Position Pos,
                                               clangd::Logger &Logger) {
   const SourceManager &SourceMgr = AST.getASTContext().getSourceManager();
@@ -1046,18 +1056,202 @@
 
   SourceLocation SourceLocationBeg = getBeginningOfIdentifier(AST, Pos, FE);
 
-  auto DeclLocationsFinder = std::make_shared<DeclarationLocationsFinder>(
+  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();
+  std::vector<const MacroInfo *> MacroInfos =
+      DeclMacrosFinder->takeMacroInfos();
+  std::vector<Location> Result;
+
+  for (auto Item : Decls) {
+    Location L = getDeclarationLocation(AST, Item->getSourceRange());
+    Result.push_back(L);
+  }
+
+  for (auto Item : MacroInfos) {
+    SourceRange SR(Item->getDefinitionLoc(), Item->getDefinitionEndLoc());
+    Location L = getDeclarationLocation(AST, SR);
+    Result.push_back(L);
+  }
+
+  return Result;
+}
+
+Hover clangd::getHover(ParsedAST &AST, Position Pos, clangd::Logger &Logger) {
+
+  // Default Hover values
+  std::vector<MarkedString> EmptyVector;
+  Hover H;
+  StringRef Ref;
+
+  const SourceManager &SourceMgr = AST.getASTContext().getSourceManager();
+  const LangOptions &LangOpts = AST.getASTContext().getLangOpts();
+  const FileEntry *FE = SourceMgr.getFileEntryForID(SourceMgr.getMainFileID());
+  if (!FE)
+    return H;
+
+  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(),
-                     DeclLocationsFinder, IndexOpts);
+                     DeclMacrosFinder, IndexOpts);
+
+  std::vector<const Decl *> DeclVector = DeclMacrosFinder->takeDecls();
+
+  if (!DeclVector.empty()) {
+    const Decl *LocationDecl = DeclVector[0];
+
+    if (LocationDecl) {
+      SourceRange SR = LocationDecl->getSourceRange();
+      SourceLocation NewLocation;
+
+      if (LocationDecl->getDescribedTemplate()) {
+        RawComment *RC = AST.getASTContext().getRawCommentForDeclNoCache(
+            LocationDecl->getDescribedTemplate());
+        if (RC) {
+          NewLocation = RC->getLocStart();
+        } else {
+          NewLocation =
+              LocationDecl->getDescribedTemplate()->getSourceRange().getBegin();
+        }
+      } else {
+        RawComment *RC =
+            AST.getASTContext().getRawCommentForDeclNoCache(LocationDecl);
+        if (RC) {
+          NewLocation = RC->getLocStart();
+          SR =
+              SourceRange(NewLocation, LocationDecl->getSourceRange().getEnd());
+        }
+      }
+      SourceLocation BeginLocation;
+      (NewLocation.isValid()) ? (BeginLocation = NewLocation)
+                              : (BeginLocation = SR.getBegin());
+
+      if (auto FuncDecl = dyn_cast<FunctionDecl>(LocationDecl)) {
+        SR =
+            SourceRange(BeginLocation, LocationDecl->getSourceRange().getEnd());
+        if (FuncDecl->isThisDeclarationADefinition() && FuncDecl->getBody())
+          SR = SourceRange(BeginLocation, FuncDecl->getBody()->getLocStart());
+      } else if (auto TagDeclaration = dyn_cast<TagDecl>(LocationDecl)) {
+        if (TagDeclaration->isThisDeclarationADefinition())
+          SR = SourceRange(BeginLocation,
+                           TagDeclaration->getBraceRange().getBegin());
+
+      } else if (auto NameDecl = dyn_cast<NamespaceDecl>(LocationDecl)) {
+        SourceLocation BeforeLBraceLoc = Lexer::getLocForEndOfToken(
+            LocationDecl->getLocation(), 0, SourceMgr, LangOpts);
+        if (BeforeLBraceLoc.isValid())
+          SR = SourceRange(NameDecl->getLocStart(), BeforeLBraceLoc);
+
+      } else if (dyn_cast<TypedefDecl>(LocationDecl)) {
+        SR = LocationDecl->getSourceRange();
+        // for everything else in ValueDecl, so lvalues of variables, function
+        // designations and enum constants
+      } else if (dyn_cast<ValueDecl>(LocationDecl)) {
+        SR = LocationDecl->getSourceRange();
+      }
+
+      if (SR.isValid()) {
+        Location L = getDeclarationLocation(AST, SR);
+        H.range = L.range;
+        Ref = getDataBufferFromSourceRange(AST, SR, L);
+      }
+
+      if (const NamedDecl *NamedD = dyn_cast<NamedDecl>(LocationDecl)) {
+        std::string Res =
+            clangd::getScope(NamedD, AST.getASTContext().getLangOpts());
+        if (!Res.empty()) {
+          MarkedString Info = {"", "C++", "In " + Res};
+          H.contents.push_back(Info);
+        }
+      }
+    }
+    MarkedString MS = {"", "C++", Ref};
+    H.contents.push_back(MS);
+    return H;
+  }
+
+  if (!DeclMacrosFinder->takeMacroInfos().empty()) {
+    const MacroInfo *MacroInf = DeclMacrosFinder->takeMacroInfos()[0];
+    if (MacroInf) {
+      SourceRange SR = SourceRange(MacroInf->getDefinitionLoc(),
+                                   MacroInf->getDefinitionEndLoc());
+      if (SR.isValid()) {
+        Location L = getDeclarationLocation(AST, SR);
+        H.range = L.range;
+        Ref = getDataBufferFromSourceRange(AST, SR, L);
+      }
+    }
+    MarkedString MS = {"", "C++", "#define " + Ref.str()};
+    H.contents.push_back(MS);
+    return H;
+  }
+
+  return H;
+}
+
+/// Obtains scope for in which a NamedDecl is contained
+std::string clangd::getScope(const NamedDecl *ND, const LangOptions &LangOpts) {
+  // Get the full qualified name, the non-qualified name and then diff
+  // them. If there's something left, use that as the scope in the hover,
+  // trimming the extra "::"
+  std::string Res;
+  PrintingPolicy WithScopePP(LangOpts);
+  std::string WithScopeBuf;
+  llvm::raw_string_ostream WithScopeOS(WithScopeBuf);
+  ND->printQualifiedName(WithScopeOS, WithScopePP);
+
+  std::string ResWithScope = WithScopeOS.str();
+  if (!ResWithScope.empty()) {
+    // Get non-qualified name
+    std::string PrintedNameBuf;
+    llvm::raw_string_ostream PrintedNameOS(PrintedNameBuf);
+    ND->printName(PrintedNameOS);
+    auto Last = ResWithScope.rfind(PrintedNameOS.str());
+    if (Last != std::string::npos) {
+      Res = ResWithScope.substr(0, Last);
+      if (Res.length() > 2 &&
+          Res.substr(Res.length() - 2, Res.length()) == "::")
+        Res = Res.substr(0, Res.length() - 2);
+    }
+  }
+  return Res;
+}
 
-  return DeclLocationsFinder->takeLocations();
+/// Returns the file buffer data that the given SourceRange points to.
+StringRef clangd::getDataBufferFromSourceRange(ParsedAST &AST, SourceRange SR,
+                                               Location L) {
+  StringRef Ref;
+
+  const FileEntry *FE =
+      AST.getASTContext().getSourceManager().getFileManager().getFile(
+          L.uri.file);
+  FileID FID = AST.getASTContext().getSourceManager().translateFile(FE);
+  Ref = AST.getASTContext().getSourceManager().getBufferData(FID);
+  unsigned Start = AST.getASTContext().getSourceManager().getFileOffset(
+      AST.getASTContext().getSourceManager().translateFileLineCol(
+          FE, L.range.start.line + 1, L.range.start.character + 1));
+  unsigned End = AST.getASTContext().getSourceManager().getFileOffset(
+      AST.getASTContext().getSourceManager().translateFileLineCol(
+          FE, L.range.end.line + 1, L.range.end.character + 1));
+  Ref = Ref.slice(Start, End);
+  return Ref;
 }
 
 void ParsedAST::ensurePreambleDeclsDeserialized() {
Index: clangd/ClangdServer.h
===================================================================
--- clangd/ClangdServer.h
+++ clangd/ClangdServer.h
@@ -284,6 +284,8 @@
   /// given a header file and vice versa.
   llvm::Optional<Path> switchSourceHeader(PathRef Path);
 
+  llvm::Expected<Tagged<Hover>> findHover(PathRef File, Position Pos);
+
   /// Run formatting for \p Rng inside \p File.
   std::vector<tooling::Replacement> formatRange(PathRef File, Range Rng);
   /// Run formatting for the whole \p File.
Index: clangd/ClangdServer.cpp
===================================================================
--- clangd/ClangdServer.cpp
+++ clangd/ClangdServer.cpp
@@ -445,9 +445,9 @@
       return;
     Result = clangd::findDefinitions(*AST, Pos, Logger);
   });
+
   return make_tagged(std::move(Result), TaggedFS.Tag);
 }
-
 llvm::Optional<Path> ClangdServer::switchSourceHeader(PathRef Path) {
 
   StringRef SourceExtensions[] = {".cpp", ".c", ".cc", ".cxx",
@@ -507,6 +507,23 @@
   return llvm::None;
 }
 
+llvm::Expected<Tagged<Hover>> ClangdServer::findHover(PathRef File,
+                                                      Position Pos) {
+  Hover FinalHover;
+  auto TaggedFS = FSProvider.getTaggedFileSystem(File);
+
+  std::shared_ptr<CppFile> Resources = Units.getFile(File);
+  assert(Resources && "Calling findHover on non-added file");
+  Resources->getAST().get()->runUnderLock(
+      [Pos, &FinalHover, this](ParsedAST *AST) {
+        if (!AST)
+          return;
+        FinalHover = clangd::getHover(*AST, Pos, Logger);
+      });
+
+  return make_tagged(std::move(FinalHover), TaggedFS.Tag);
+}
+
 std::future<void> ClangdServer::scheduleReparseAndDiags(
     PathRef File, VersionedDraft Contents, std::shared_ptr<CppFile> Resources,
     Tagged<IntrusiveRefCntPtr<vfs::FileSystem>> TaggedFS) {
Index: clangd/ClangdLSPServer.h
===================================================================
--- clangd/ClangdLSPServer.h
+++ clangd/ClangdLSPServer.h
@@ -72,6 +72,7 @@
   void onFileEvent(Ctx C, DidChangeWatchedFilesParams &Params) override;
   void onCommand(Ctx C, ExecuteCommandParams &Params) override;
   void onRename(Ctx C, RenameParams &Parames) override;
+  void onCodeHover(Ctx C, TextDocumentPositionParams &Params) override;
 
   std::vector<clang::tooling::Replacement>
   getFixIts(StringRef File, const clangd::Diagnostic &D);
Index: clangd/ClangdLSPServer.cpp
===================================================================
--- clangd/ClangdLSPServer.cpp
+++ clangd/ClangdLSPServer.cpp
@@ -57,6 +57,7 @@
                  {"triggerCharacters", {"(", ","}},
              }},
             {"definitionProvider", true},
+            {"hoverProvider", true},
             {"renameProvider", true},
             {"executeCommandProvider",
              json::obj{
@@ -224,6 +225,7 @@
   if (!Items)
     return C.replyError(ErrorCode::InvalidParams,
                         llvm::toString(Items.takeError()));
+
   C.reply(json::ary(Items->Value));
 }
 
@@ -234,6 +236,15 @@
   C.reply(Result ? URI::fromFile(*Result).uri : "");
 }
 
+void ClangdLSPServer::onCodeHover(Ctx C, TextDocumentPositionParams &Params) {
+
+  auto H = Server.findHover(
+      Params.textDocument.uri.file,
+      Position{Params.position.line, Params.position.character});
+
+  C.reply(Hover::unparse(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