[PATCH] D76094: [clangd] Change line break behaviour for hoverinfo

2020-03-23 Thread Lorenz Junglas via Phabricator via cfe-commits
lolleko updated this revision to Diff 252006.
lolleko added a comment.

Adressed 2nd Review

The problem with the changes you proposed is that it doesn't handle paragraphs 
(\n\n).
I think the conditionals required to make that work wouldn't be much cleaner 
than the current solution.

And I do think parahraphs in comments should be retained. I agree that not 
every hard line break should be a pargraph.
But actual parahraphs should be retained e.g.:

/**
 * Condition for calling UpdateOverlaps() to initialize overlap state 
when loaded in during level streaming.
 * If set to 'UseConfigDefault', the default specified in ini 
(displayed in 'DefaultUpdateOverlapsMethodDuringLevelStreaming') will be used.
 * If overlaps are not initialized, this actor and attached components 
will not have an initial state of what objects are touching it,
 * and overlap events may only come in once one of those objects update 
overlaps themselves (for example when moving).
 * However if an object touching it *does* initialize state, both 
objects will know about their touching state with each other.
 * This can be a potentially large performance savings during level 
streaming, and is safe if the object and others initially
 * overlapping it do not need the overlap state because they will not 
trigger overlap notifications.
 * 
 * Note that if 'bGenerateOverlapEventsDuringLevelStreaming' is true, 
overlaps are always updated in this case, but that flag
 * determines whether the Begin/End overlap events are triggered.
 * 
 * @see bGenerateOverlapEventsDuringLevelStreaming, 
DefaultUpdateOverlapsMethodDuringLevelStreaming, 
GetUpdateOverlapsMethodDuringLevelStreaming()
 */

I think once proper paragraphs are in. The linebreak parsing could be rewritten 
into 2 steps:

1. Split on `\n\n` to extract actual paragraphs.
2. Use the solution you proposed to split and join new lines.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D76094/new/

https://reviews.llvm.org/D76094

Files:
  clang-tools-extra/clangd/Hover.cpp
  clang-tools-extra/clangd/Hover.h
  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
@@ -1883,6 +1883,119 @@
   }
 }
 
+TEST(Hover, DocCommentLineBreakConversion) {
+  struct Case {
+llvm::StringRef Documentation;
+llvm::StringRef ExpectedRenderMarkdown;
+llvm::StringRef ExpectedRenderPlainText;
+  } Cases[] = {{
+   "foo\n\n\nbar",
+   "foo  \nbar",
+   "foo\nbar",
+   },
+   {
+   "foo\n\n\n\tbar",
+   "foo  \nbar",
+   "foo\nbar",
+   },
+   {
+   "foo\n\n\n bar",
+   "foo  \nbar",
+   "foo\nbar",
+   },
+   {
+   "foo.\nbar",
+   "foo.  \nbar",
+   "foo.\nbar",
+   },
+   {
+   "foo:\nbar",
+   "foo:  \nbar",
+   "foo:\nbar",
+   },
+   {
+   "foo,\nbar",
+   "foo,  \nbar",
+   "foo,\nbar",
+   },
+   {
+   "foo;\nbar",
+   "foo;  \nbar",
+   "foo;\nbar",
+   },
+   {
+   "foo!\nbar",
+   "foo!  \nbar",
+   "foo!\nbar",
+   },
+   {
+   "foo?\nbar",
+   "foo?  \nbar",
+   "foo?\nbar",
+   },
+   {
+   "foo\n-bar",
+   "foo  \n-bar",
+   "foo\n-bar",
+   },
+   {
+   "foo\n*bar",
+   // TODO `*` should probably not be escaped after line break
+   "foo  \n\\*bar",
+   "foo\n*bar",
+   },
+   {
+   "foo\n@bar",
+   "foo  \n@bar",
+   "foo\n@bar",
+   },
+   {
+   "foo\n\\bar",
+   // TODO `\` should probably not be escaped after line break
+   "foo  \nbar",
+   "foo\n\\bar",
+   },
+   {
+   "foo\n>bar",
+   // TODO `>` should probably not be escaped after line break
+   "foo  \n\\>bar",
+   "foo\n>bar",
+   },
+   {
+   "foo\n#bar",
+   "foo  \n#bar"

[PATCH] D76094: [clangd] Change line break behaviour for hoverinfo

2020-03-23 Thread Lorenz Junglas via Phabricator via cfe-commits
lolleko updated this revision to Diff 252153.
lolleko added a comment.

typo


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D76094/new/

https://reviews.llvm.org/D76094

Files:
  clang-tools-extra/clangd/Hover.cpp
  clang-tools-extra/clangd/Hover.h
  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
@@ -1889,6 +1889,26 @@
 llvm::StringRef ExpectedRenderMarkdown;
 llvm::StringRef ExpectedRenderPlainText;
   } Cases[] = {{
+   " \n foo\nbar",
+   "foo bar",
+   "foo bar",
+   },
+   {
+   "foo\nbar \n  ",
+   "foo bar",
+   "foo bar",
+   },
+   {
+   "foo  \nbar",
+   "foo bar",
+   "foo bar",
+   },
+   {
+   "foo\nbar",
+   "foo bar",
+   "foo bar",
+   },
+   {
"foo\n\n\nbar",
"foo  \nbar",
"foo\nbar",
@@ -1908,79 +1928,11 @@
"foo.  \nbar",
"foo.\nbar",
},
-   {
-   "foo:\nbar",
-   "foo:  \nbar",
-   "foo:\nbar",
-   },
-   {
-   "foo,\nbar",
-   "foo,  \nbar",
-   "foo,\nbar",
-   },
-   {
-   "foo;\nbar",
-   "foo;  \nbar",
-   "foo;\nbar",
-   },
-   {
-   "foo!\nbar",
-   "foo!  \nbar",
-   "foo!\nbar",
-   },
-   {
-   "foo?\nbar",
-   "foo?  \nbar",
-   "foo?\nbar",
-   },
-   {
-   "foo\n-bar",
-   "foo  \n-bar",
-   "foo\n-bar",
-   },
{
"foo\n*bar",
-   // TODO `*` should probably not be escaped after line break
"foo  \n\\*bar",
"foo\n*bar",
},
-   {
-   "foo\n@bar",
-   "foo  \n@bar",
-   "foo\n@bar",
-   },
-   {
-   "foo\n\\bar",
-   // TODO `\` should probably not be escaped after line break
-   "foo  \nbar",
-   "foo\n\\bar",
-   },
-   {
-   "foo\n>bar",
-   // TODO `>` should probably not be escaped after line break
-   "foo  \n\\>bar",
-   "foo\n>bar",
-   },
-   {
-   "foo\n#bar",
-   "foo  \n#bar",
-   "foo\n#bar",
-   },
-   {
-   "foo  \nbar",
-   "foo  \nbar",
-   "foo\nbar",
-   },
-   {
-   "foo\nbar",
-   "foo  \nbar",
-   "foo\nbar",
-   },
-   {
-   "foo\\\nbar",
-   "foo  \nbar",
-   "foo\nbar",
-   },
{
"foo\nbar",
"foo bar",
@@ -1989,7 +1941,7 @@
 
   for (const auto &C : Cases) {
 markup::Document Output;
-parseLineBreaks(C.Documentation, Output);
+parseDoucmentation(C.Documentation, Output);
 
 EXPECT_EQ(Output.asMarkdown(), C.ExpectedRenderMarkdown);
 EXPECT_EQ(Output.asPlainText(), C.ExpectedRenderPlainText);
Index: clang-tools-extra/clangd/Hover.h
===
--- clang-tools-extra/clangd/Hover.h
+++ clang-tools-extra/clangd/Hover.h
@@ -75,7 +75,7 @@
   markup::Document present() const;
 };
 
-void parseLineBreaks(llvm::StringRef Input, markup::Document &Output);
+void parseDocumentation(llvm::StringRef Input, markup::Document &Output);
 
 llvm::raw_ostream &operator<<(llvm::raw_ostream &, const HoverInfo::Param &);
 inline bool operator==(const HoverInfo::Param &LHS,
Index: clang-tools-extra/clangd/Hover.cpp
===
--- clang-tools-extra/clangd/Hover.cpp
+++ clang-tools-extra/clangd/Hover.cpp
@@ -522,26 +522,15 @@
 }
 
 bool isParagraphLineBreak(llvm::StringRef Str, size_t LineBreakIndex) {
-  if (LineBreakIndex + 1 >= Str.size()) {
-return false;
-  }
-  auto NextNonSpaceCharIndex = Str.find_first_not_of(' ', LineBreakIndex + 1);

[PATCH] D76094: [clangd] Change line break behaviour for hoverinfo

2020-03-23 Thread Lorenz Junglas via Phabricator via cfe-commits
lolleko updated this revision to Diff 252151.
lolleko added a comment.

Final Cleanup
Removed markdown linebreak parsing for now.

No I don't have commit rights yet. Feel fre to merge this for me.

Thanks for the thorough review.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D76094/new/

https://reviews.llvm.org/D76094

Files:
  clang-tools-extra/clangd/Hover.cpp
  clang-tools-extra/clangd/Hover.h
  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
@@ -1889,6 +1889,26 @@
 llvm::StringRef ExpectedRenderMarkdown;
 llvm::StringRef ExpectedRenderPlainText;
   } Cases[] = {{
+   " \n foo\nbar",
+   "foo bar",
+   "foo bar",
+   },
+   {
+   "foo\nbar \n  ",
+   "foo bar",
+   "foo bar",
+   },
+   {
+   "foo  \nbar",
+   "foo bar",
+   "foo bar",
+   },
+   {
+   "foo\nbar",
+   "foo bar",
+   "foo bar",
+   },
+   {
"foo\n\n\nbar",
"foo  \nbar",
"foo\nbar",
@@ -1908,79 +1928,11 @@
"foo.  \nbar",
"foo.\nbar",
},
-   {
-   "foo:\nbar",
-   "foo:  \nbar",
-   "foo:\nbar",
-   },
-   {
-   "foo,\nbar",
-   "foo,  \nbar",
-   "foo,\nbar",
-   },
-   {
-   "foo;\nbar",
-   "foo;  \nbar",
-   "foo;\nbar",
-   },
-   {
-   "foo!\nbar",
-   "foo!  \nbar",
-   "foo!\nbar",
-   },
-   {
-   "foo?\nbar",
-   "foo?  \nbar",
-   "foo?\nbar",
-   },
-   {
-   "foo\n-bar",
-   "foo  \n-bar",
-   "foo\n-bar",
-   },
{
"foo\n*bar",
-   // TODO `*` should probably not be escaped after line break
"foo  \n\\*bar",
"foo\n*bar",
},
-   {
-   "foo\n@bar",
-   "foo  \n@bar",
-   "foo\n@bar",
-   },
-   {
-   "foo\n\\bar",
-   // TODO `\` should probably not be escaped after line break
-   "foo  \nbar",
-   "foo\n\\bar",
-   },
-   {
-   "foo\n>bar",
-   // TODO `>` should probably not be escaped after line break
-   "foo  \n\\>bar",
-   "foo\n>bar",
-   },
-   {
-   "foo\n#bar",
-   "foo  \n#bar",
-   "foo\n#bar",
-   },
-   {
-   "foo  \nbar",
-   "foo  \nbar",
-   "foo\nbar",
-   },
-   {
-   "foo\nbar",
-   "foo  \nbar",
-   "foo\nbar",
-   },
-   {
-   "foo\\\nbar",
-   "foo  \nbar",
-   "foo\nbar",
-   },
{
"foo\nbar",
"foo bar",
@@ -1989,7 +1941,7 @@
 
   for (const auto &C : Cases) {
 markup::Document Output;
-parseLineBreaks(C.Documentation, Output);
+parseDoucmentation(C.Documentation, Output);
 
 EXPECT_EQ(Output.asMarkdown(), C.ExpectedRenderMarkdown);
 EXPECT_EQ(Output.asPlainText(), C.ExpectedRenderPlainText);
Index: clang-tools-extra/clangd/Hover.h
===
--- clang-tools-extra/clangd/Hover.h
+++ clang-tools-extra/clangd/Hover.h
@@ -75,7 +75,7 @@
   markup::Document present() const;
 };
 
-void parseLineBreaks(llvm::StringRef Input, markup::Document &Output);
+void parseDoucmentation(llvm::StringRef Input, markup::Document &Output);
 
 llvm::raw_ostream &operator<<(llvm::raw_ostream &, const HoverInfo::Param &);
 inline bool operator==(const HoverInfo::Param &LHS,
Index: clang-tools-extra/clangd/Hover.cpp
===
--- clang-tools-extra/clangd/Hover.cpp
+++ clang-tools-extra/clangd/Hover.cpp
@@ -522,26 +522,15 @@
 }
 
 bool isParagraphLineBreak(llvm::StringRef Str, size_t LineBreakIn

[PATCH] D76094: [clangd] Change line break behaviour for hoverinfo

2020-03-23 Thread Lorenz Junglas via Phabricator via cfe-commits
lolleko updated this revision to Diff 252159.
lolleko added a comment.

Final Cleanup
Removed markdown linebreak parsing for now.

No I don't have commit rights yet. Feel fre to merge this for me.

Thanks for the thorough review.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D76094/new/

https://reviews.llvm.org/D76094

Files:
  clang-tools-extra/clangd/Hover.cpp
  clang-tools-extra/clangd/Hover.h
  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
@@ -1889,6 +1889,26 @@
 llvm::StringRef ExpectedRenderMarkdown;
 llvm::StringRef ExpectedRenderPlainText;
   } Cases[] = {{
+   " \n foo\nbar",
+   "foo bar",
+   "foo bar",
+   },
+   {
+   "foo\nbar \n  ",
+   "foo bar",
+   "foo bar",
+   },
+   {
+   "foo  \nbar",
+   "foo bar",
+   "foo bar",
+   },
+   {
+   "foo\nbar",
+   "foo bar",
+   "foo bar",
+   },
+   {
"foo\n\n\nbar",
"foo  \nbar",
"foo\nbar",
@@ -1908,79 +1928,11 @@
"foo.  \nbar",
"foo.\nbar",
},
-   {
-   "foo:\nbar",
-   "foo:  \nbar",
-   "foo:\nbar",
-   },
-   {
-   "foo,\nbar",
-   "foo,  \nbar",
-   "foo,\nbar",
-   },
-   {
-   "foo;\nbar",
-   "foo;  \nbar",
-   "foo;\nbar",
-   },
-   {
-   "foo!\nbar",
-   "foo!  \nbar",
-   "foo!\nbar",
-   },
-   {
-   "foo?\nbar",
-   "foo?  \nbar",
-   "foo?\nbar",
-   },
-   {
-   "foo\n-bar",
-   "foo  \n-bar",
-   "foo\n-bar",
-   },
{
"foo\n*bar",
-   // TODO `*` should probably not be escaped after line break
"foo  \n\\*bar",
"foo\n*bar",
},
-   {
-   "foo\n@bar",
-   "foo  \n@bar",
-   "foo\n@bar",
-   },
-   {
-   "foo\n\\bar",
-   // TODO `\` should probably not be escaped after line break
-   "foo  \nbar",
-   "foo\n\\bar",
-   },
-   {
-   "foo\n>bar",
-   // TODO `>` should probably not be escaped after line break
-   "foo  \n\\>bar",
-   "foo\n>bar",
-   },
-   {
-   "foo\n#bar",
-   "foo  \n#bar",
-   "foo\n#bar",
-   },
-   {
-   "foo  \nbar",
-   "foo  \nbar",
-   "foo\nbar",
-   },
-   {
-   "foo\nbar",
-   "foo  \nbar",
-   "foo\nbar",
-   },
-   {
-   "foo\\\nbar",
-   "foo  \nbar",
-   "foo\nbar",
-   },
{
"foo\nbar",
"foo bar",
@@ -1989,7 +1941,7 @@
 
   for (const auto &C : Cases) {
 markup::Document Output;
-parseLineBreaks(C.Documentation, Output);
+parseDocumentation(C.Documentation, Output);
 
 EXPECT_EQ(Output.asMarkdown(), C.ExpectedRenderMarkdown);
 EXPECT_EQ(Output.asPlainText(), C.ExpectedRenderPlainText);
Index: clang-tools-extra/clangd/Hover.h
===
--- clang-tools-extra/clangd/Hover.h
+++ clang-tools-extra/clangd/Hover.h
@@ -75,7 +75,7 @@
   markup::Document present() const;
 };
 
-void parseLineBreaks(llvm::StringRef Input, markup::Document &Output);
+void parseDocumentation(llvm::StringRef Input, markup::Document &Output);
 
 llvm::raw_ostream &operator<<(llvm::raw_ostream &, const HoverInfo::Param &);
 inline bool operator==(const HoverInfo::Param &LHS,
Index: clang-tools-extra/clangd/Hover.cpp
===
--- clang-tools-extra/clangd/Hover.cpp
+++ clang-tools-extra/clangd/Hover.cpp
@@ -522,26 +522,15 @@
 }
 
 bool isParagraphLineBreak(llvm::StringRef Str, size_t LineBreakIn

[PATCH] D76094: [clangd] Change line break behaviour for hoverinfo

2020-03-12 Thread Lorenz Junglas via Phabricator via cfe-commits
lolleko created this revision.
lolleko added a reviewer: sammccall.
lolleko added a project: clang.
Herald added subscribers: cfe-commits, usaxena95, kadircet, arphaman, jkorous, 
MaskRay, ilya-biryukov.

`convertLineBreaks` retains hard line breaks and removes soft line breaks.
Wether a line break is hard or soft is determined by the following rules (some 
of which have been discussed in https://github.com/clangd/clangd/issues/95):

- Hard Markdown line breaks (https://github.github.com/gfm/#hard-line-breaks) 
are retained
- Line breaks that are preceded by a punctuation are retained
- Line breaks that are followed by "interesting characters" (e.g. Markdown 
syntax, doxygen commands) are retained
- All other line breaks are removed

Even though markdown doc comments in cpp are still rare, I removed the markdown 
escaping.
I think the chance that markdown constructs are introduced by accident is 
fairly low especially with proper line breaks.
That means that doc comments which use markdown are now supported and should be 
rendered properly (in supported editors).

In addition `canonicalizeSpaces` has been removed.
`canonicalizeSpaces` removed duplicate whitespaces anywhere in a string. Now 
only (duplicate) whitespaces before and after a line break are trimmed.

Since this is my first contribution feel free to be extra thorough.

Fixes https://github.com/clangd/clangd/issues/301, Potentially resolves 
https://github.com/clangd/clangd/issues/95


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D76094

Files:
  clang-tools-extra/clangd/FormattedString.cpp
  clang-tools-extra/clangd/unittests/FormattedStringTests.cpp
  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
@@ -1905,7 +1905,7 @@
   llvm::StringRef ExpectedMarkdown = R"md(### variable `foo`  
 
 ---
-Value \= `val`  
+Value = `val`  
 
 ---
 ```cpp
Index: clang-tools-extra/clangd/unittests/FormattedStringTests.cpp
===
--- clang-tools-extra/clangd/unittests/FormattedStringTests.cpp
+++ clang-tools-extra/clangd/unittests/FormattedStringTests.cpp
@@ -20,19 +20,6 @@
 TEST(Render, Escaping) {
   // Check some ASCII punctuation
   Paragraph P;
-  P.appendText("*!`");
-  EXPECT_EQ(P.asMarkdown(), "\\*\\!\\`");
-
-  // Check all ASCII punctuation.
-  P = Paragraph();
-  std::string Punctuation = R"txt(!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~)txt";
-  // Same text, with each character escaped.
-  std::string EscapedPunctuation;
-  EscapedPunctuation.reserve(2 * Punctuation.size());
-  for (char C : Punctuation)
-EscapedPunctuation += std::string("\\") + C;
-  P.appendText(Punctuation);
-  EXPECT_EQ(P.asMarkdown(), EscapedPunctuation);
 
   // In code blocks we don't need to escape ASCII punctuation.
   P = Paragraph();
@@ -98,7 +85,7 @@
 }
 
 TEST(Paragraph, ExtraSpaces) {
-  // Make sure spaces inside chunks are dropped.
+  // Make sure spaces after linebreaks are dropped.
   Paragraph P;
   P.appendText("foo\n   \t   baz");
   P.appendCode(" bar\n");
@@ -106,15 +93,92 @@
   EXPECT_EQ(P.asPlainText(), "foo baz bar");
 }
 
-TEST(Paragraph, NewLines) {
+TEST(Paragraph, LineBreakTrim) {
   // New lines before and after chunks are dropped.
   Paragraph P;
   P.appendText(" \n foo\nbar\n ");
   P.appendCode(" \n foo\nbar \n ");
-  EXPECT_EQ(P.asMarkdown(), "foo bar `foo bar`");
+  EXPECT_EQ(P.asMarkdown(), "foo bar `foo\nbar`");
   EXPECT_EQ(P.asPlainText(), "foo bar foo bar");
 }
 
+TEST(Paragraph, LineBreakConversionParagraphs) {
+  // Paragraphs are eindicated by double linebreaks
+  // additional linebreaks and spaces should be dropped
+  constexpr auto Expected = "foo\n\nbar";
+
+  Paragraph P1;
+  P1.appendText("foo\n\n\n\nbar");
+  EXPECT_EQ(P1.asMarkdown(), Expected);
+  EXPECT_EQ(P1.asPlainText(), Expected);
+
+  Paragraph P2;
+  P2.appendText("foo\n\n\n\tbar");
+  EXPECT_EQ(P2.asMarkdown(), Expected);
+  EXPECT_EQ(P2.asPlainText(), Expected);
+
+  Paragraph P3;
+  P3.appendText("foo\n\n\n bar");
+  EXPECT_EQ(P3.asMarkdown(), Expected);
+  EXPECT_EQ(P3.asPlainText(), Expected);
+}
+
+TEST(Paragraph, LineBreakConversionPunctuation) {
+  // Keep linebreaks after punctuation
+  constexpr llvm::StringLiteral Punctuation = R"txt(.:,;!?)txt";
+
+  for (auto Char : Punctuation) {
+const auto TestString = std::string("foo") + Char + "\nbar";
+const auto MarkdownExpected = std::string("foo") + Char + "  \nbar";
+
+Paragraph P;
+P.appendText(TestString);
+EXPECT_EQ(P.asMarkdown(), MarkdownExpected);
+EXPECT_EQ(P.asPlainText(), TestString);
+  }
+}
+
+TEST(Paragraph, LineBreakConversionIndicator) {
+  // Keep linebreaks before interesting characters
+  constexpr llvm::StringLiteral LinbreakIdenticators = R"txt(-*@\>#`)txt";
+
+  for (au

[PATCH] D76094: [clangd] Change line break behaviour for hoverinfo

2020-03-19 Thread Lorenz Junglas via Phabricator via cfe-commits
lolleko updated this revision to Diff 251334.
lolleko edited the summary of this revision.
lolleko added a comment.

Addressed review.

Paragraphs (lines separated by a double line break `\n\n`) are currently not 
retained. Because the current implementation of markup::Paragraph only appends 
a single white space after it's contents 
(https://github.com/llvm/llvm-project/blob/e26e9ba288ce156b851504ebbb7d8a775572957c/clang-tools-extra/clangd/FormattedString.cpp#L368).
I think this is semantically wrong because in natural language aswell as markup 
languages like md, html, ... a paragraph should be followed by a blank line.
But since a lot of code relies on that single white space paragraph, this 
should be addressed in a different patch.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D76094/new/

https://reviews.llvm.org/D76094

Files:
  clang-tools-extra/clangd/Hover.cpp
  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
@@ -1883,6 +1883,118 @@
   }
 }
 
+TEST(Hover, LineBreakConversionDocs) {
+  struct Case {
+const std::function Builder;
+llvm::StringRef ExpectedRenderMarkdown;
+llvm::StringRef ExpectedRenderPlainText;
+  } Cases[] = {{
+   [](HoverInfo &HI) { HI.Documentation = "foo\n\n\nbar"; },
+   "foo  \nbar",
+   "foo\nbar",
+   },
+   {
+   [](HoverInfo &HI) { HI.Documentation = "foo\n\n\n\tbar"; },
+   "foo  \nbar",
+   "foo\nbar",
+   },
+   {
+   [](HoverInfo &HI) { HI.Documentation = "foo\n\n\n bar"; },
+   "foo  \nbar",
+   "foo\nbar",
+   },
+   {
+   [](HoverInfo &HI) { HI.Documentation = "foo.\nbar"; },
+   "foo.  \nbar",
+   "foo.\nbar",
+   },
+   {
+   [](HoverInfo &HI) { HI.Documentation = "foo:\nbar"; },
+   "foo:  \nbar",
+   "foo:\nbar",
+   },
+   {
+   [](HoverInfo &HI) { HI.Documentation = "foo,\nbar"; },
+   "foo,  \nbar",
+   "foo,\nbar",
+   },
+   {
+   [](HoverInfo &HI) { HI.Documentation = "foo;\nbar"; },
+   "foo;  \nbar",
+   "foo;\nbar",
+   },
+   {
+   [](HoverInfo &HI) { HI.Documentation = "foo!\nbar"; },
+   "foo!  \nbar",
+   "foo!\nbar",
+   },
+   {
+   [](HoverInfo &HI) { HI.Documentation = "foo?\nbar"; },
+   "foo?  \nbar",
+   "foo?\nbar",
+   },
+   {
+   [](HoverInfo &HI) { HI.Documentation = "foo\n-bar"; },
+   "foo  \n-bar",
+   "foo\n-bar",
+   },
+   {
+   [](HoverInfo &HI) { HI.Documentation = "foo\n*bar"; },
+   // TODO `*` should probably not be escaped after line break
+   "foo  \n\\*bar",
+   "foo\n*bar",
+   },
+   {
+   [](HoverInfo &HI) { HI.Documentation = "foo\n@bar"; },
+   "foo  \n@bar",
+   "foo\n@bar",
+   },
+   {
+   [](HoverInfo &HI) { HI.Documentation = "foo\n\\bar"; },
+   // TODO `\` should probably not be escaped after line break
+   "foo  \nbar",
+   "foo\n\\bar",
+   },
+   {
+   [](HoverInfo &HI) { HI.Documentation = "foo\n>bar"; },
+   // TODO `>` should probably not be escaped after line break
+   "foo  \n\\>bar",
+   "foo\n>bar",
+   },
+   {
+   [](HoverInfo &HI) { HI.Documentation = "foo\n#bar"; },
+   "foo  \n#bar",
+   "foo\n#bar",
+   },
+   {
+   [](HoverInfo &HI) { HI.Documentation = "foo  \nbar"; },
+   "foo  \nbar",
+   "foo\nbar",
+   },
+   {
+   [](HoverInfo &HI) { HI.Documentation = "foo\nbar"; },
+   "foo  \nbar",
+   "foo\nbar",
+   },
+   {
+   [](HoverInfo &HI) { HI.Documentation = "foo\\\nbar"; },
+   "foo  \nbar",
+   "foo\nbar",
+   },
+   {
+   [](HoverInfo &HI) { HI.Documentati