[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