[clang-tools-extra] 30d17d8 - [clangd] Parse `foo` in documentation comments and render as code.

Sam McCall via cfe-commits cfe-commits at lists.llvm.org
Wed Apr 29 15:22:36 PDT 2020


Author: Sam McCall
Date: 2020-04-30T00:22:15+02:00
New Revision: 30d17d88528329558b6df22cc9595eae95a9f77c

URL: https://github.com/llvm/llvm-project/commit/30d17d88528329558b6df22cc9595eae95a9f77c
DIFF: https://github.com/llvm/llvm-project/commit/30d17d88528329558b6df22cc9595eae95a9f77c.diff

LOG: [clangd] Parse `foo` in documentation comments and render as code.

Reviewers: kadircet

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

Tags: #clang

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

Added: 
    

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

Removed: 
    


################################################################################
diff  --git a/clang-tools-extra/clangd/FormattedString.cpp b/clang-tools-extra/clangd/FormattedString.cpp
index d3dbdbba17bc..81de6a8be93c 100644
--- a/clang-tools-extra/clangd/FormattedString.cpp
+++ b/clang-tools-extra/clangd/FormattedString.cpp
@@ -216,23 +216,10 @@ std::string getMarkerForCodeBlock(llvm::StringRef Input) {
 }
 
 // Trims the input and concatenates whitespace blocks into a single ` `.
-std::string canonicalizeSpaces(std::string Input) {
-  // Goes over the string and preserves only a single ` ` for any whitespace
-  // chunks, the rest is moved to the end of the string and dropped in the end.
-  auto WritePtr = Input.begin();
+std::string canonicalizeSpaces(llvm::StringRef Input) {
   llvm::SmallVector<llvm::StringRef, 4> Words;
   llvm::SplitString(Input, Words);
-  if (Words.empty())
-    return "";
-  // Go over each word and add it to the string.
-  for (llvm::StringRef Word : Words) {
-    if (WritePtr > Input.begin())
-      *WritePtr++ = ' '; // Separate from previous block.
-    llvm::for_each(Word, [&WritePtr](const char C) { *WritePtr++ = C; });
-  }
-  // Get rid of extra spaces.
-  Input.resize(WritePtr - Input.begin());
-  return Input;
+  return llvm::join(Words, " ");
 }
 
 std::string renderBlocks(llvm::ArrayRef<std::unique_ptr<Block>> Children,
@@ -398,24 +385,24 @@ void BulletList::renderPlainText(llvm::raw_ostream &OS) const {
   }
 }
 
-Paragraph &Paragraph::appendText(std::string Text) {
-  Text = canonicalizeSpaces(std::move(Text));
-  if (Text.empty())
+Paragraph &Paragraph::appendText(llvm::StringRef Text) {
+  std::string Norm = canonicalizeSpaces(Text);
+  if (Norm.empty())
     return *this;
   Chunks.emplace_back();
   Chunk &C = Chunks.back();
-  C.Contents = std::move(Text);
+  C.Contents = std::move(Norm);
   C.Kind = Chunk::PlainText;
   return *this;
 }
 
-Paragraph &Paragraph::appendCode(std::string Code) {
-  Code = canonicalizeSpaces(std::move(Code));
-  if (Code.empty())
+Paragraph &Paragraph::appendCode(llvm::StringRef Code) {
+  std::string Norm = canonicalizeSpaces(std::move(Code));
+  if (Norm.empty())
     return *this;
   Chunks.emplace_back();
   Chunk &C = Chunks.back();
-  C.Contents = std::move(Code);
+  C.Contents = std::move(Norm);
   C.Kind = Chunk::InlineCode;
   return *this;
 }

diff  --git a/clang-tools-extra/clangd/FormattedString.h b/clang-tools-extra/clangd/FormattedString.h
index fe864ba57568..8c522d060c4f 100644
--- a/clang-tools-extra/clangd/FormattedString.h
+++ b/clang-tools-extra/clangd/FormattedString.h
@@ -46,10 +46,10 @@ class Paragraph : public Block {
   void renderPlainText(llvm::raw_ostream &OS) const override;
 
   /// Append plain text to the end of the string.
-  Paragraph &appendText(std::string Text);
+  Paragraph &appendText(llvm::StringRef Text);
 
   /// Append inline code, this translates to the ` block in markdown.
-  Paragraph &appendCode(std::string Code);
+  Paragraph &appendCode(llvm::StringRef Code);
 
 private:
   struct Chunk {

diff  --git a/clang-tools-extra/clangd/Hover.cpp b/clang-tools-extra/clangd/Hover.cpp
index b0ac041abe5f..3a66130ea153 100644
--- a/clang-tools-extra/clangd/Hover.cpp
+++ b/clang-tools-extra/clangd/Hover.cpp
@@ -772,7 +772,7 @@ markup::Document HoverInfo::present() const {
   // https://github.com/microsoft/vscode/issues/88417 for details.
   markup::Paragraph &Header = Output.addHeading(3);
   if (Kind != index::SymbolKind::Unknown)
-    Header.appendText(std::string(index::getSymbolKindString(Kind)));
+    Header.appendText(index::getSymbolKindString(Kind));
   assert(!Name.empty() && "hover triggered on a nameless symbol");
   Header.appendCode(Name);
 
@@ -809,10 +809,11 @@ markup::Document HoverInfo::present() const {
 
   if (Offset)
     Output.addParagraph().appendText(
-        llvm::formatv("Offset: {0} byte{1}", *Offset, *Offset == 1 ? "" : "s"));
+        llvm::formatv("Offset: {0} byte{1}", *Offset, *Offset == 1 ? "" : "s")
+            .str());
   if (Size)
     Output.addParagraph().appendText(
-        llvm::formatv("Size: {0} byte{1}", *Size, *Size == 1 ? "" : "s"));
+        llvm::formatv("Size: {0} byte{1}", *Size, *Size == 1 ? "" : "s").str());
 
   if (!Documentation.empty())
     parseDocumentation(Documentation, Output);
@@ -838,6 +839,52 @@ markup::Document HoverInfo::present() const {
   return Output;
 }
 
+// If the backtick at `Offset` starts a probable quoted range, return the range
+// (including the quotes).
+llvm::Optional<llvm::StringRef> getBacktickQuoteRange(llvm::StringRef Line,
+                                                      unsigned Offset) {
+  assert(Line[Offset] == '`');
+
+  // The open-quote is usually preceded by whitespace.
+  llvm::StringRef Prefix = Line.substr(0, Offset);
+  constexpr llvm::StringLiteral BeforeStartChars = " \t(=";
+  if (!Prefix.empty() && !BeforeStartChars.contains(Prefix.back()))
+    return llvm::None;
+
+  // The quoted string must be nonempty and usually has no leading/trailing ws.
+  auto Next = Line.find('`', Offset + 1);
+  if (Next == llvm::StringRef::npos)
+    return llvm::None;
+  llvm::StringRef Contents = Line.slice(Offset + 1, Next);
+  if (Contents.empty() || isWhitespace(Contents.front()) ||
+      isWhitespace(Contents.back()))
+    return llvm::None;
+
+  // The close-quote is usually followed by whitespace or punctuation.
+  llvm::StringRef Suffix = Line.substr(Next + 1);
+  constexpr llvm::StringLiteral AfterEndChars = " \t)=.,;:";
+  if (!Suffix.empty() && !AfterEndChars.contains(Suffix.front()))
+    return llvm::None;
+
+  return Line.slice(Offset, Next+1);
+}
+
+void parseDocumentationLine(llvm::StringRef Line, markup::Paragraph &Out) {
+  // Probably this is appendText(Line), but scan for something interesting.
+  for (unsigned I = 0; I < Line.size(); ++I) {
+    switch (Line[I]) {
+      case '`':
+        if (auto Range = getBacktickQuoteRange(Line, I)) {
+          Out.appendText(Line.substr(0, I));
+          Out.appendCode(Range->trim("`"));
+          return parseDocumentationLine(Line.substr(I+Range->size()), Out);
+        }
+        break;
+    }
+  }
+  Out.appendText(Line);
+}
+
 void parseDocumentation(llvm::StringRef Input, markup::Document &Output) {
   std::vector<llvm::StringRef> ParagraphLines;
   auto FlushParagraph = [&] {
@@ -845,7 +892,7 @@ void parseDocumentation(llvm::StringRef Input, markup::Document &Output) {
       return;
     auto &P = Output.addParagraph();
     for (llvm::StringRef Line : ParagraphLines)
-      P.appendText(Line.str());
+      parseDocumentationLine(Line, P);
     ParagraphLines.clear();
   };
 

diff  --git a/clang-tools-extra/clangd/unittests/HoverTests.cpp b/clang-tools-extra/clangd/unittests/HoverTests.cpp
index 4b988488b611..45c027102590 100644
--- a/clang-tools-extra/clangd/unittests/HoverTests.cpp
+++ b/clang-tools-extra/clangd/unittests/HoverTests.cpp
@@ -1958,7 +1958,7 @@ def)",
   }
 }
 
-TEST(Hover, DocCommentLineBreakConversion) {
+TEST(Hover, ParseDocumentation) {
   struct Case {
     llvm::StringRef Documentation;
     llvm::StringRef ExpectedRenderMarkdown;
@@ -2017,6 +2017,22 @@ TEST(Hover, DocCommentLineBreakConversion) {
                    "foo\nbar",
                    "foo bar",
                    "foo bar",
+               },
+               {
+                   // FIXME: we insert spaces between code and text chunk.
+                   "Tests primality of `p`.",
+                   "Tests primality of `p` .",
+                   "Tests primality of p .",
+               },
+               {
+                   "'`' should not occur in `Code`",
+                   "'\\`' should not occur in `Code`",
+                   "'`' should not occur in Code",
+               },
+               {
+                   "`not\nparsed`",
+                   "\\`not parsed\\`",
+                   "`not parsed`",
                }};
 
   for (const auto &C : Cases) {


        


More information about the cfe-commits mailing list