[clangd] Render header of hover card as a heading
authorKadir Cetinkaya <kadircet@google.com>
Mon, 13 Jan 2020 16:50:23 +0000 (17:50 +0100)
committerKadir Cetinkaya <kadircet@google.com>
Mon, 13 Jan 2020 19:24:09 +0000 (20:24 +0100)
Reviewers: sammccall

Subscribers: ilya-biryukov, MaskRay, jkorous, arphaman, usaxena95, cfe-commits

Tags: #clang

Differential Revision: https://reviews.llvm.org/D72625

clang-tools-extra/clangd/FormattedString.cpp
clang-tools-extra/clangd/FormattedString.h
clang-tools-extra/clangd/Hover.cpp
clang-tools-extra/clangd/unittests/FormattedStringTests.cpp
clang-tools-extra/clangd/unittests/HoverTests.cpp

index 1e61b8b..881c34e 100644 (file)
@@ -164,6 +164,19 @@ std::string indentLines(llvm::StringRef Input) {
   }
   return IndentedR;
 }
+
+class Heading : public Paragraph {
+public:
+  Heading(size_t Level) : Level(Level) {}
+  void renderMarkdown(llvm::raw_ostream &OS) const override {
+    OS << std::string(Level, '#') << ' ';
+    Paragraph::renderMarkdown(OS);
+  }
+
+private:
+  size_t Level;
+};
+
 } // namespace
 
 std::string Block::asMarkdown() const {
@@ -278,6 +291,12 @@ BulletList &Document::addBulletList() {
   Children.emplace_back(std::make_unique<BulletList>());
   return *static_cast<BulletList *>(Children.back().get());
 }
+
+Paragraph &Document::addHeading(size_t Level) {
+  assert(Level > 0);
+  Children.emplace_back(std::make_unique<Heading>(Level));
+  return *static_cast<Paragraph *>(Children.back().get());
+}
 } // namespace markup
 } // namespace clangd
 } // namespace clang
index 2d1f681..1b7e9fc 100644 (file)
@@ -14,6 +14,7 @@
 #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_FORMATTEDSTRING_H
 
 #include "llvm/Support/raw_ostream.h"
+#include <cstddef>
 #include <memory>
 #include <string>
 #include <vector>
@@ -86,6 +87,9 @@ public:
   /// Adds a block of code. This translates to a ``` block in markdown. In plain
   /// text representation, the code block will be surrounded by newlines.
   void addCodeBlock(std::string Code, std::string Language = "cpp");
+  /// Heading is a special type of paragraph that will be prepended with \p
+  /// Level many '#'s in markdown.
+  Paragraph &addHeading(size_t Level);
 
   BulletList &addBulletList();
 
index a9fa644..526f222 100644 (file)
@@ -469,7 +469,10 @@ markup::Document HoverInfo::present() const {
   // class `X`
   //
   // function `foo` → `int`
-  markup::Paragraph &Header = Output.addParagraph();
+  // Note that we are making use of a level-3 heading because VSCode renders
+  // level 1 and 2 headers in a huge font, see
+  // https://github.com/microsoft/vscode/issues/88417 for details.
+  markup::Paragraph &Header = Output.addHeading(3);
   Header.appendText(index::getSymbolKindString(Kind));
   assert(!Name.empty() && "hover triggered on a nameless symbol");
   Header.appendCode(Name);
index 80d0934..0a85a4c 100644 (file)
@@ -145,6 +145,15 @@ TEST(Document, Spacer) {
   EXPECT_EQ(D.asPlainText(), "foo\n\nbar");
 }
 
+TEST(Document, Heading) {
+  Document D;
+  D.addHeading(1).appendText("foo");
+  D.addHeading(2).appendText("bar");
+  D.addParagraph().appendText("baz");
+  EXPECT_EQ(D.asMarkdown(), "# foo  \n## bar  \nbaz");
+  EXPECT_EQ(D.asPlainText(), "foo\nbar\nbaz");
+}
+
 TEST(CodeBlock, Render) {
   Document D;
   // Code blocks preserves any extra spaces.
index 91eb5a0..866ff97 100644 (file)
@@ -1631,6 +1631,7 @@ TEST(Hover, DocsFromMostSpecial) {
     }
   }
 }
+
 TEST(Hover, Present) {
   struct {
     const std::function<void(HoverInfo &)> Builder;
@@ -1720,6 +1721,17 @@ def)",
   }
 }
 
+// This is a separate test as headings don't create any differences in plaintext
+// mode.
+TEST(Hover, PresentHeadings) {
+  HoverInfo HI;
+  HI.Kind = index::SymbolKind::Variable;
+  HI.Name = "foo";
+  HI.Type = "type";
+
+  EXPECT_EQ(HI.present().asMarkdown(), "### variable `foo` \\: `type`");
+}
+
 } // namespace
 } // namespace clangd
 } // namespace clang