forked from OSchip/llvm-project
[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
This commit is contained in:
parent
1ccde53342
commit
30d17d8852
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -46,10 +46,10 @@ public:
|
|||
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 {
|
||||
|
|
|
@ -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();
|
||||
};
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
Loading…
Reference in New Issue