Nebiroth updated this revision to Diff 123662.
Nebiroth marked 10 inline comments as done.
Nebiroth added a comment.

Removed all std::pair objects
Fixed and updated all tests
findHover doesn't call findDefinitions anymore
DeclarationLocationsFinder fills two Decl and MacroInfos vectors instead of 
giving out Location objects
Removed constructors for structs MarkedString and Hover
Moved code modifying SourceRange for hover into getHover()


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/MyClass.cpp
  clangd/Protocol.cpp
  clangd/Protocol.h
  clangd/ProtocolHandlers.cpp
  clangd/ProtocolHandlers.h
  clangd/impl.cpp
  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
@@ -5,6 +5,7 @@
 Content-Length: 143
 
 {"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootUri":"file:///path/to/workspace","capabilities":{},"trace":"off"}}
+
 #      CHECK:  "id": 0,
 # CHECK-NEXT:  "jsonrpc": "2.0",
 # CHECK-NEXT:  "result": {
@@ -30,6 +31,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": [
@@ -40,6 +41,7 @@
 # CHECK-NEXT:      "textDocumentSync": 1
 # CHECK-NEXT:    }
 # CHECK-NEXT:  }
+
 Content-Length: 44
 
 {"jsonrpc":"2.0","id":3,"method":"shutdown"}
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/impl.cpp
===================================================================
--- /dev/null
+++ clangd/impl.cpp
@@ -0,0 +1,5 @@
+#include "header.h"
+
+void function() {
+
+}
\ No newline at end of file
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
@@ -23,6 +23,8 @@
 
 #include "JSONExpr.h"
 #include "llvm/ADT/Optional.h"
+#include "clang/Basic/SourceLocation.h"
+#include "llvm/ADT/Optional.h"
 #include "llvm/Support/YAMLParser.h"
 #include <string>
 #include <vector>
@@ -449,6 +451,53 @@
   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 {
+
+  Hover(std::vector<MarkedString> contents, Range r)
+      : contents(contents), range(r) {}
+
+  /**
+   * 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,34 @@
   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
+    std::vector<MarkedString> EmptyVector;
+    Position BeginPosition = {0,0};
+    Position EndPosition = {0,0};
+    Range DefaultRange  = {BeginPosition, EndPosition};
+    Hover H = {EmptyVector, DefaultRange};
+    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/MyClass.cpp
===================================================================
--- /dev/null
+++ clangd/MyClass.cpp
@@ -0,0 +1,19 @@
+#include "MyClass.h"
+
+MyClass::MyClass() {
+}
+
+void MyClass::doOperation() {
+
+}
+
+void bar()
+{
+
+}
+
+void test () {
+	MyClass c;
+	c.doOperation();
+	bar();
+}
\ No newline at end of file
Index: clangd/ClangdUnit.h
===================================================================
--- clangd/ClangdUnit.h
+++ clangd/ClangdUnit.h
@@ -319,6 +319,14 @@
 std::vector<Location> findDefinitions(ParsedAST &AST, Position Pos,
                                       clangd::Logger &Logger);
 
+
+Location getDeclarationLocation(ParsedAST &AST, const SourceRange &ValSourceRange);
+
+// Updates SourceRange of given Decl to take into account function comments
+SourceLocation getFunctionComments(ParsedAST &AST, const Decl *D);
+
+Hover getHover(ParsedAST &AST, Position Pos, clangd::Logger &Logger);
+
 /// 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
@@ -940,7 +940,8 @@
 
 /// Finds declarations locations that a given source location refers to.
 class DeclarationLocationsFinder : public index::IndexDataConsumer {
-  std::vector<Location> DeclarationLocations;
+  std::vector<const Decl *> DeclarationDecls;
+  std::vector<const MacroInfo *> DeclarationMacroInfs;
   const SourceLocation &SearchedLocation;
   const ASTContext &AST;
   Preprocessor &PP;
@@ -951,59 +952,48 @@
                              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());
+    std::sort(DeclarationDecls.begin(), DeclarationDecls.end());
+    auto last = std::unique(DeclarationDecls.begin(), DeclarationDecls.end());
+    DeclarationDecls.erase(last, DeclarationDecls.end());
+    return std::move(DeclarationDecls);
+  }
+
+  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(DeclarationMacroInfs.begin(), DeclarationMacroInfs.end());
     auto last =
-        std::unique(DeclarationLocations.begin(), DeclarationLocations.end());
-    DeclarationLocations.erase(last, DeclarationLocations.end());
-    return std::move(DeclarationLocations);
+        std::unique(DeclarationMacroInfs.begin(), DeclarationMacroInfs.end());
+    DeclarationMacroInfs.erase(last, DeclarationMacroInfs.end());
+    return std::move(DeclarationMacroInfs);
   }
 
   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))
+      DeclarationDecls.push_back(D);
     return true;
   }
 
+  std::vector<const Decl *> getDeclarationDecls() { return DeclarationDecls; };
+  std::vector<const MacroInfo *> getMacroInfos() {
+    return DeclarationMacroInfs;
+  };
+
 private:
   bool isSearchedLocation(FileID FID, unsigned Offset) const {
     const SourceManager &SourceMgr = AST.getSourceManager();
     return SourceMgr.getFileOffset(SearchedLocation) == Offset &&
            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;
@@ -1026,8 +1016,7 @@
             PP.getMacroDefinitionAtLoc(IdentifierInfo, BeforeSearchedLocation);
         MacroInfo *MacroInf = MacroDef.getMacroInfo();
         if (MacroInf) {
-          addDeclarationLocation(SourceRange(MacroInf->getDefinitionLoc(),
-                                             MacroInf->getDefinitionEndLoc()));
+          DeclarationMacroInfs.push_back(MacroInf);
         }
       }
     }
@@ -1056,7 +1045,257 @@
   indexTopLevelDecls(AST.getASTContext(), AST.getTopLevelDecls(),
                      DeclLocationsFinder, IndexOpts);
 
-  return DeclLocationsFinder->takeLocations();
+  std::vector<const Decl *> Decls = DeclLocationsFinder->takeDecls();
+  std::vector<const MacroInfo *> MacroInfos =
+      DeclLocationsFinder->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 =
+        SourceRange(Item->getDefinitionLoc(), Item->getDefinitionEndLoc());
+    Location L = getDeclarationLocation(AST, SR);
+    Result.push_back(L);
+  }
+
+  return Result;
+}
+
+Location clangd::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;
+}
+
+// Returns SourceLocation pointing to the beginning of function comments when
+// hovering on a function. This function backtracks from the starting location
+// one line at a time to determine what will be included.
+
+SourceLocation clangd::getFunctionComments(ParsedAST &AST, const Decl *D) {
+
+  const SourceManager &SM = AST.getASTContext().getSourceManager();
+  const LangOptions &LangOpts = AST.getASTContext().getLangOpts();
+
+  SourceLocation LocStart = D->getSourceRange().getBegin();
+  SourceLocation LocEnd =
+      Lexer::getLocForEndOfToken(D->getSourceRange().getEnd(), 0, SM, LangOpts);
+  Position Begin;
+  Begin.line = SM.getSpellingLineNumber(LocStart) - 1;
+  Begin.character = SM.getSpellingColumnNumber(LocStart) - 1;
+  Position End;
+  End.line = SM.getSpellingLineNumber(LocEnd) - 1;
+  End.character = SM.getSpellingColumnNumber(LocEnd) - 1;
+  Range R = {Begin, End};
+  Location StartLocation;
+  StartLocation.uri =
+      URI::fromFile(SM.getFilename(SM.getSpellingLoc(LocStart)));
+  StartLocation.range = R;
+
+  SourceLocation LocationCandidate;
+
+  const FileEntry *FE = SM.getFileManager().getFile(StartLocation.uri.file);
+  FileID ID = SM.translateFile(FE);
+  StringRef Ref = SM.getBufferData(ID);
+  int StartLine =
+      StartLocation.range.start.line; // previous line from the actual start
+  bool done = false;
+  while (!done && StartLine > 0) {
+    unsigned Start =
+        SM.getFileOffset(SM.translateFileLineCol(FE, StartLine, 1));
+    unsigned End = SM.getFileOffset(SM.translateFileLineCol(FE, StartLine, 50));
+    StringRef CurrentBuffer = Ref.slice(Start, End);
+    if (CurrentBuffer.find("//") != std::string::npos &&
+        CurrentBuffer.find("}") == std::string::npos)
+      LocationCandidate = SM.translateFileLineCol(FE, StartLine, 1);
+    else
+      done = true;
+
+    StartLine--;
+  }
+
+  return LocationCandidate;
+}
+
+Hover clangd::getHover(ParsedAST &AST, Position Pos, clangd::Logger &Logger) {
+
+  // Default Hover values
+  std::vector<MarkedString> EmptyVector;
+  Position BeginPosition = {0, 0};
+  Position EndPosition = {0, 0};
+  Range DefaultRange = {BeginPosition, EndPosition};
+  Hover H = {EmptyVector, DefaultRange};
+  unsigned Start, End;
+  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 DeclLocationsFinder = std::make_shared<DeclarationLocationsFinder>(
+      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);
+
+  if (!DeclLocationsFinder->getDeclarationDecls().empty()) {
+    const Decl *LocationDecl = DeclLocationsFinder->takeDecls()[0];
+
+    if (LocationDecl) {
+      SourceRange SR = LocationDecl->getSourceRange();
+      if (auto FuncDecl = dyn_cast<FunctionDecl>(LocationDecl)) {
+        SourceLocation NewLocation = getFunctionComments(AST, LocationDecl);
+        if (NewLocation.isValid())
+          SR =
+              SourceRange(NewLocation, LocationDecl->getSourceRange().getEnd());
+        if (FuncDecl->isThisDeclarationADefinition() && FuncDecl->getBody())
+          SR = SourceRange(LocationDecl->getSourceRange().getBegin(),
+                           FuncDecl->getBody()->getLocStart());
+      } else if (auto ClassDecl = dyn_cast<TagDecl>(LocationDecl)) {
+        if (!ClassDecl->isInterface() && !ClassDecl->isUnion() &&
+            ClassDecl->isThisDeclarationADefinition())
+          SR = SourceRange(LocationDecl->getSourceRange().getBegin(),
+                           ClassDecl->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)) {
+        // TODO: find a way to get the hover for the type that is being aliased
+        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;
+        const FileEntry *FE = SourceMgr.getFileManager().getFile(L.uri.file);
+        FileID FID = SourceMgr.translateFile(FE);
+        Ref = SourceMgr.getBufferData(FID);
+        Start = SourceMgr.getFileOffset(SourceMgr.translateFileLineCol(
+            FE, L.range.start.line + 1, L.range.start.character + 1));
+        End = SourceMgr.getFileOffset(SourceMgr.translateFileLineCol(
+            FE, L.range.end.line + 1, L.range.end.character + 1));
+        Ref = Ref.slice(Start, End);
+      }
+
+      if (const NamedDecl *NamedD = dyn_cast<NamedDecl>(LocationDecl)) {
+
+        // 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 "::"
+        PrintingPolicy WithScopePP(AST.getASTContext().getLangOpts());
+        std::string WithScopeBuf;
+        llvm::raw_string_ostream WithScopeOS(WithScopeBuf);
+        NamedD->printQualifiedName(WithScopeOS, WithScopePP);
+
+        // Get all contexts for current NamedDecl
+        const DeclContext *Ctx = NamedD->getDeclContext();
+
+        // For ObjC methods, look through categories and use the interface as
+        // context.
+        if (auto *MD = dyn_cast<ObjCMethodDecl>(NamedD))
+          if (auto *ID = MD->getClassInterface())
+            Ctx = ID;
+
+        typedef SmallVector<const DeclContext *, 8> ContextsTy;
+        ContextsTy Contexts;
+
+        // Collect contexts.
+        while (Ctx && isa<NamedDecl>(Ctx)) {
+          Contexts.push_back(Ctx);
+          Ctx = Ctx->getParent();
+        }
+
+        std::string ResWithScope = WithScopeOS.str();
+        if (!ResWithScope.empty()) {
+          // Get non-qualified name
+          std::string PrintedNameBuf;
+          llvm::raw_string_ostream PrintedNameOS(PrintedNameBuf);
+          NamedD->printName(PrintedNameOS);
+          auto Last = ResWithScope.rfind(PrintedNameOS.str());
+          if (Last != std::string::npos) {
+            std::string Res = ResWithScope.substr(0, Last);
+            if (Res.length() > 2 &&
+                Res.substr(Res.length() - 2, Res.length()) == "::")
+              Res = Res.substr(0, Res.length() - 2);
+
+            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 (!DeclLocationsFinder->getMacroInfos().empty()) {
+    const MacroInfo *MacroInf = DeclLocationsFinder->takeMacroInfos()[0];
+    if (MacroInf) {
+      SourceRange SR = SourceRange(MacroInf->getDefinitionLoc(),
+                                   MacroInf->getDefinitionEndLoc());
+      if (SR.isValid()) {
+        Location L = getDeclarationLocation(AST, SR);
+        H.range = L.range;
+        const FileEntry *FE = SourceMgr.getFileManager().getFile(L.uri.file);
+        FileID FID = SourceMgr.translateFile(FE);
+        Ref = SourceMgr.getBufferData(FID);
+        Start = SourceMgr.getFileOffset(SourceMgr.translateFileLineCol(
+            FE, L.range.start.line + 1, L.range.start.character + 1));
+        End = SourceMgr.getFileOffset(SourceMgr.translateFileLineCol(
+            FE, L.range.end.line + 1, L.range.end.character + 1));
+        Ref = Ref.slice(Start, End);
+      }
+    }
+    MarkedString MS = {"", "C++", "#define " + Ref.str()};
+    H.contents.push_back(MS);
+    return H;
+  }
+
+  return H;
 }
 
 void ParsedAST::ensurePreambleDeclsDeserialized() {
Index: clangd/ClangdServer.h
===================================================================
--- clangd/ClangdServer.h
+++ clangd/ClangdServer.h
@@ -62,8 +62,7 @@
   Tagged(const Tagged<U> &Other) : Value(Other.Value), Tag(Other.Tag) {}
 
   template <class U>
-  Tagged(Tagged<U> &&Other)
-      : Value(std::move(Other.Value)), Tag(std::move(Other.Tag)) {}
+  Tagged(Tagged<U> &&Other) : Value(std::move(Other.Value)), Tag(std::move(Other.Tag)) {}
 
   T Value = T();
   VFSTag Tag = VFSTag();
@@ -283,10 +282,13 @@
   llvm::Expected<Tagged<std::vector<Location>>> findDefinitions(PathRef File,
                                                                 Position Pos);
 
+
   /// Helper function that returns a path to the corresponding source file when
   /// given a header file and vice versa.
   llvm::Optional<Path> switchSourceHeader(PathRef Path);
 
+  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
@@ -8,6 +8,7 @@
 //===-------------------------------------------------------------------===//
 
 #include "ClangdServer.h"
+#include "Protocol.h"
 #include "clang/Format/Format.h"
 #include "clang/Frontend/CompilerInstance.h"
 #include "clang/Frontend/CompilerInvocation.h"
@@ -428,6 +429,7 @@
 
 llvm::Expected<Tagged<std::vector<Location>>>
 ClangdServer::findDefinitions(PathRef File, Position Pos) {
+
   auto TaggedFS = FSProvider.getTaggedFileSystem(File);
 
   std::shared_ptr<CppFile> Resources = Units.getFile(File);
@@ -442,6 +444,7 @@
       return;
     Result = clangd::findDefinitions(*AST, Pos, Logger);
   });
+
   return make_tagged(std::move(Result), TaggedFS.Tag);
 }
 
@@ -504,6 +507,29 @@
   return llvm::None;
 }
 
+Tagged<Hover> ClangdServer::findHover(PathRef File, Position Pos) {
+
+  //Default Hover values
+  std::vector<MarkedString> EmptyVector;
+  Position BeginPosition = {0,0};
+  Position EndPosition = {0,0};
+  Range DefaultRange  = {BeginPosition, EndPosition};
+  Hover FinalHover = {EmptyVector, DefaultRange};
+
+  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;
+    // Only show hover for the first found definition, we could potentially expand this in a later patch.
+    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
@@ -71,6 +71,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,7 +225,13 @@
   if (!Items)
     return C.replyError(ErrorCode::InvalidParams,
                         llvm::toString(Items.takeError()));
-  C.reply(json::ary(Items->Value));
+
+  std::vector<Location> LocationVector;
+  for (auto Item : Items->Value) {
+    LocationVector.push_back(Item);
+  }
+
+  C.reply(json::ary(LocationVector));
 }
 
 void ClangdLSPServer::onSwitchSourceHeader(Ctx C,
@@ -234,6 +241,17 @@
   C.reply(Result ? URI::fromFile(*Result).uri : "");
 }
 
+void ClangdLSPServer::onCodeHover(Ctx C, TextDocumentPositionParams &Params) {
+
+  Hover H =
+      Server
+          .findHover(Params.textDocument.uri.file,
+                     Position{Params.position.line, Params.position.character})
+          .Value;
+
+  C.reply(Hover::unparse(H));
+}
+
 ClangdLSPServer::ClangdLSPServer(JSONOutput &Out, unsigned AsyncThreadsCount,
                                  bool StorePreamblesInMemory,
                                  bool SnippetCompletions,
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to