kadircet created this revision.
kadircet added a reviewer: sammccall.
Herald added subscribers: cfe-commits, usaxena95, arphaman, jkorous, MaskRay, 
ilya-biryukov.
Herald added a project: clang.

Improves basic hover presentation logic to include more info.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D71555

Files:
  clang-tools-extra/clangd/Hover.cpp
  clang-tools-extra/clangd/Protocol.cpp
  clang-tools-extra/clangd/test/hover.test
  clang-tools-extra/clangd/unittests/HoverTests.cpp

Index: clang-tools-extra/clangd/unittests/HoverTests.cpp
===================================================================
--- clang-tools-extra/clangd/unittests/HoverTests.cpp
+++ clang-tools-extra/clangd/unittests/HoverTests.cpp
@@ -1439,6 +1439,207 @@
   }
 }
 
+TEST(Hover, Present) {
+  struct {
+    const std::function<void(HoverInfo &)> Builder;
+    llvm::StringRef ExpectedRender;
+  } Cases[] = {
+      // Basic coverage tests.
+      {
+          [](HoverInfo &HI) { HI.Kind = index::SymbolKind::Unknown; },
+          R"pt(<unknown>)pt",
+      },
+      {
+          [](HoverInfo &HI) {
+            HI.Kind = index::SymbolKind::Class;
+            HI.Name = "foo";
+          },
+          R"pt(Class foo)pt",
+      },
+      {
+          [](HoverInfo &HI) {
+            HI.Kind = index::SymbolKind::NamespaceAlias;
+            HI.Name = "foo";
+          },
+          R"pt(Namespace Alias foo)pt",
+      },
+      {
+          [](HoverInfo &HI) {
+            HI.Kind = index::SymbolKind::Class;
+            HI.Type = "type";
+          },
+          R"pt(Class type)pt",
+      },
+      {
+          [](HoverInfo &HI) {
+            HI.Kind = index::SymbolKind::Class;
+            HI.Name = "foo";
+            HI.Type = "type";
+          },
+          R"pt(Class foo : type)pt",
+      },
+      {
+          [](HoverInfo &HI) {
+            HI.Kind = index::SymbolKind::Class;
+            HI.Name = "foo";
+            HI.Type = "type";
+            HI.ReturnType = "ret_type";
+          },
+          R"pt(Class foo
+Returns: ret_type)pt",
+      },
+      {
+          [](HoverInfo &HI) { HI.Parameters.emplace(); },
+          R"pt(<unknown>)pt",
+      },
+      {
+          [](HoverInfo &HI) {
+            HI.Parameters.emplace();
+            HoverInfo::Param P;
+            HI.Parameters->push_back(P);
+            P.Type = "type";
+            HI.Parameters->push_back(P);
+            P.Name = "foo";
+            HI.Parameters->push_back(P);
+            P.Default = "default";
+            HI.Parameters->push_back(P);
+          },
+          R"pt(<unknown>
+- 
+- type
+- type foo
+- type foo = default)pt",
+      },
+      {
+          [](HoverInfo &HI) { HI.Value = "value"; },
+          R"pt(<unknown>
+Value: value)pt",
+      },
+      {
+          [](HoverInfo &HI) { HI.Documentation = "doc"; },
+          R"pt(<unknown>
+doc)pt",
+      },
+      {
+          [](HoverInfo &HI) { HI.NamespaceScope = ""; },
+          R"pt(<unknown>)pt",
+      },
+      {
+          [](HoverInfo &HI) { HI.Definition = "def"; },
+          R"pt(<unknown>
+
+def)pt",
+      },
+      {
+          [](HoverInfo &HI) {
+            HI.Definition = "def";
+            HI.NamespaceScope = "";
+          },
+          R"pt(<unknown>
+
+// global namespace
+def)pt",
+      },
+      {
+          [](HoverInfo &HI) {
+            HI.Definition = "def";
+            HI.NamespaceScope = "ns::";
+          },
+          R"pt(<unknown>
+
+// namespace ns
+def)pt",
+      },
+      {
+          [](HoverInfo &HI) {
+            HI.Definition = "def";
+            HI.NamespaceScope = "";
+            HI.LocalScope = "test::bar::";
+          },
+          R"pt(<unknown>
+
+// In test::bar
+def)pt",
+      },
+      // Behaviour checks.
+      {
+          [](HoverInfo &HI) {
+            HI.Kind = index::SymbolKind::Class;
+            HI.Name = "Foo";
+            HI.NamespaceScope = "";
+            HI.TemplateParameters = {
+                {std::string("typename"), std::string("T"), llvm::None},
+                {std::string("typename"), std::string("C"),
+                 std::string("bool")},
+            };
+            HI.Definition =
+                "template <typename T, typename C = bool> class Foo {}";
+          },
+          R"pt(Class Foo
+
+// global namespace
+template <typename T, typename C = bool> class Foo {})pt",
+      },
+      {
+          [](HoverInfo &HI) {
+            HI.Kind = index::SymbolKind::InstanceMethod;
+            HI.Name = "baz";
+            HI.NamespaceScope = "";
+            HI.LocalScope = "Foo<T, C>::";
+            HI.Parameters = {
+                {std::string("int"), std::string("foo"), std::string("3")},
+            };
+            HI.ReturnType = "int";
+            HI.Definition = "constexpr int baz(int foo = 3)";
+          },
+          R"pt(Instance Method baz
+Returns: int
+- int foo = 3
+
+// In Foo<T, C>
+constexpr int baz(int foo = 3))pt",
+      },
+      {
+          [](HoverInfo &HI) {
+            HI.Kind = index::SymbolKind::Parameter;
+            HI.Name = "foo";
+            HI.NamespaceScope = "";
+            HI.LocalScope = "Foo<T, C>::baz";
+            HI.Type = "int";
+            HI.Value = "3";
+            HI.Definition = "int foo = 3";
+          },
+          R"pt(Parameter foo : int
+Value: 3
+
+// In Foo<T, C>::baz
+int foo = 3)pt",
+      },
+      {
+          [](HoverInfo &HI) {
+            HI.Kind = index::SymbolKind::Variable;
+            HI.Name = "x";
+            HI.NamespaceScope = "";
+            HI.LocalScope = "foo";
+            HI.Type = "const int";
+            HI.Value = "6";
+            HI.Definition = "const int x = f.baz()";
+          },
+          R"pt(Variable x : const int
+Value: 6
+
+// In foo
+const int x = f.baz())pt",
+      },
+  };
+
+  for (const auto &C : Cases) {
+    HoverInfo HI;
+    C.Builder(HI);
+    EXPECT_EQ(HI.present().asPlainText(), C.ExpectedRender);
+  }
+}
+
 } // namespace
 } // namespace clangd
 } // namespace clang
Index: clang-tools-extra/clangd/test/hover.test
===================================================================
--- clang-tools-extra/clangd/test/hover.test
+++ clang-tools-extra/clangd/test/hover.test
@@ -9,7 +9,7 @@
 # CHECK-NEXT:  "result": {
 # CHECK-NEXT:    "contents": {
 # CHECK-NEXT:      "kind": "plaintext",
-# CHECK-NEXT:      "value": "Declared in global namespace\n\nvoid foo()"
+# CHECK-NEXT:      "value": "Function foo\nReturns: void\n\n// global namespace\nvoid foo()"
 # CHECK-NEXT:    },
 # CHECK-NEXT:    "range": {
 # CHECK-NEXT:      "end": {
@@ -37,7 +37,7 @@
 # CHECK-NEXT:  "result": {
 # CHECK-NEXT:    "contents": {
 # CHECK-NEXT:      "kind": "plaintext",
-# CHECK-NEXT:      "value": "Declared in global namespace\n\nenum foo {}"
+# CHECK-NEXT:      "value": "Enum foo\n\n// global namespace\nenum foo {}"
 # CHECK-NEXT:    },
 # CHECK-NEXT:    "range": {
 # CHECK-NEXT:      "end": {
Index: clang-tools-extra/clangd/Protocol.cpp
===================================================================
--- clang-tools-extra/clangd/Protocol.cpp
+++ clang-tools-extra/clangd/Protocol.cpp
@@ -268,6 +268,90 @@
   llvm_unreachable("invalid symbol kind");
 }
 
+llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, SymbolKind Kind) {
+  switch (Kind) {
+  case SymbolKind::File:
+    OS << "File";
+    break;
+  case SymbolKind::Module:
+    OS << "Module";
+    break;
+  case SymbolKind::Namespace:
+    OS << "Namespace";
+    break;
+  case SymbolKind::Package:
+    OS << "Package";
+    break;
+  case SymbolKind::Class:
+    OS << "Class";
+    break;
+  case SymbolKind::Method:
+    OS << "Method";
+    break;
+  case SymbolKind::Property:
+    OS << "Property";
+    break;
+  case SymbolKind::Field:
+    OS << "Field";
+    break;
+  case SymbolKind::Constructor:
+    OS << "Constructor";
+    break;
+  case SymbolKind::Enum:
+    OS << "Enum";
+    break;
+  case SymbolKind::Interface:
+    OS << "Interface";
+    break;
+  case SymbolKind::Function:
+    OS << "Function";
+    break;
+  case SymbolKind::Variable:
+    OS << "Variable";
+    break;
+  case SymbolKind::Constant:
+    OS << "Constant";
+    break;
+  case SymbolKind::String:
+    OS << "String";
+    break;
+  case SymbolKind::Number:
+    OS << "Number";
+    break;
+  case SymbolKind::Boolean:
+    OS << "Boolean";
+    break;
+  case SymbolKind::Array:
+    OS << "Array";
+    break;
+  case SymbolKind::Object:
+    OS << "Object";
+    break;
+  case SymbolKind::Key:
+    OS << "Key";
+    break;
+  case SymbolKind::Null:
+    OS << "Null";
+    break;
+  case SymbolKind::EnumMember:
+    OS << "EnumMember";
+    break;
+  case SymbolKind::Struct:
+    OS << "Struct";
+    break;
+  case SymbolKind::Event:
+    OS << "Event";
+    break;
+  case SymbolKind::Operator:
+    OS << "Operator";
+    break;
+  case SymbolKind::TypeParameter:
+    OS << "TypeParameter";
+    break;
+  }
+  return OS;
+}
+
 bool fromJSON(const llvm::json::Value &Params, ClientCapabilities &R) {
   const llvm::json::Object *O = Params.getAsObject();
   if (!O)
Index: clang-tools-extra/clangd/Hover.cpp
===================================================================
--- clang-tools-extra/clangd/Hover.cpp
+++ clang-tools-extra/clangd/Hover.cpp
@@ -22,6 +22,10 @@
 #include "clang/AST/DeclTemplate.h"
 #include "clang/AST/PrettyPrinter.h"
 #include "clang/Index/IndexSymbol.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringRef.h"
 #include "llvm/Support/raw_ostream.h"
 
 namespace clang {
@@ -200,8 +204,8 @@
 
 // Populates Type, ReturnType, and Parameters for function-like decls.
 void fillFunctionTypeAndParams(HoverInfo &HI, const Decl *D,
-                                      const FunctionDecl *FD,
-                                      const PrintingPolicy &Policy) {
+                               const FunctionDecl *FD,
+                               const PrintingPolicy &Policy) {
   HI.Parameters.emplace();
   for (const ParmVarDecl *PVD : FD->parameters()) {
     HI.Parameters->emplace_back();
@@ -226,11 +230,11 @@
     }
   }
 
-  if (const auto* CCD = llvm::dyn_cast<CXXConstructorDecl>(FD)) {
+  if (const auto *CCD = llvm::dyn_cast<CXXConstructorDecl>(FD)) {
     // Constructor's "return type" is the class type.
     HI.ReturnType = declaredType(CCD->getParent()).getAsString(Policy);
     // Don't provide any type for the constructor itself.
-  } else if (llvm::isa<CXXDestructorDecl>(FD)){
+  } else if (llvm::isa<CXXDestructorDecl>(FD)) {
     HI.ReturnType = "void";
   } else {
     HI.ReturnType = FD->getReturnType().getAsString(Policy);
@@ -343,7 +347,7 @@
 
 /// Generate a \p Hover object given the type \p T.
 HoverInfo getHoverContents(QualType T, const Decl *D, ASTContext &ASTCtx,
-                                  const SymbolIndex *Index) {
+                           const SymbolIndex *Index) {
   HoverInfo HI;
   llvm::raw_string_ostream OS(HI.Name);
   PrintingPolicy Policy = printingPolicyForDecls(ASTCtx.getPrintingPolicy());
@@ -385,6 +389,20 @@
   return HI;
 }
 
+// Converts a string of the form `word1-word2-...` into `Word1 Word2 ...`.
+std::string beautify(llvm::StringRef Input) {
+  std::string Res;
+  llvm::SmallVector<llvm::StringRef, 2> Out;
+  llvm::SplitString(Input, Out, "-");
+  llvm::for_each(Out, [&Res](llvm::StringRef Word) {
+    if (!Res.empty())
+      Res += ' ';
+    Res += llvm::toUpper(Word.front());
+    Res.append(Word.drop_front());
+  });
+  return Res;
+}
+
 } // namespace
 
 llvm::Optional<HoverInfo> getHover(ParsedAST &AST, Position Pos,
@@ -437,34 +455,70 @@
           tooling::applyAllReplacements(HI->Definition, Replacements))
     HI->Definition = *Formatted;
 
-  HI->SymRange = getTokenRange(AST.getSourceManager(),
-                               AST.getLangOpts(), SourceLocationBeg);
+  HI->SymRange = getTokenRange(AST.getSourceManager(), AST.getLangOpts(),
+                               SourceLocationBeg);
   return HI;
 }
 
 markup::Document HoverInfo::present() const {
   markup::Document Output;
-  if (NamespaceScope) {
-    auto &P = Output.addParagraph();
-    P.appendText("Declared in");
-    // Drop trailing "::".
-    if (!LocalScope.empty())
-      P.appendCode(llvm::StringRef(LocalScope).drop_back(2));
-    else if (NamespaceScope->empty())
-      P.appendCode("global namespace");
-    else
-      P.appendCode(llvm::StringRef(*NamespaceScope).drop_back(2));
+  markup::Paragraph &P = Output.addParagraph();
+  P.appendText(beautify(index::getSymbolKindString(Kind)));
+  if (!Name.empty()) {
+    P.appendCode(Name);
+    if (Type && !ReturnType) {
+      P.appendText(":");
+      P.appendCode(*Type);
+    }
+  } else if (Type) {
+    P.appendCode(*Type);
   }
 
-  if (!Definition.empty()) {
-    Output.addCodeBlock(Definition);
-  } else {
-    // Builtin types
-    Output.addCodeBlock(Name);
+  if (ReturnType) {
+    markup::Paragraph &P = Output.addParagraph();
+    P.appendText("Returns: ");
+    P.appendCode(*ReturnType);
+  }
+
+  if (Parameters && !Parameters->empty()) {
+    markup::BulletList &L = Output.addBulletList();
+    for (const auto &Param : *Parameters) {
+      std::string Buffer;
+      llvm::raw_string_ostream OS(Buffer);
+      OS << Param;
+      L.addItem().addParagraph().appendCode(std::move(OS.str()));
+    }
+  }
+
+  if (Value) {
+    markup::Paragraph &P = Output.addParagraph();
+    P.appendText("Value: ");
+    P.appendCode(*Value);
   }
 
   if (!Documentation.empty())
     Output.addParagraph().appendText(Documentation);
+
+  if (!Definition.empty()) {
+    std::string Code;
+    llvm::raw_string_ostream OS(Code);
+    if (NamespaceScope) {
+      // Drop trailing "::".
+      if (!LocalScope.empty()) {
+        // Container name, e.g. class, method, function.
+        // We might want to propogate some info about container type to print
+        // function foo, class X, method X::bar, etc.
+        OS << "// In " << llvm::StringRef(LocalScope).drop_back(2);
+      } else if (!NamespaceScope->empty()) {
+        OS << "// namespace " << llvm::StringRef(*NamespaceScope).drop_back(2);
+      } else {
+        OS << "// global namespace";
+      }
+      OS << '\n';
+    }
+    OS << Definition;
+    Output.addCodeBlock(OS.str());
+  }
   return Output;
 }
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to