Skip to content

Commit

Permalink
[clangd] Tweak parseDocumentation loop to use raw lines. NFC
Browse files Browse the repository at this point in the history
This clears the way for the raw lines themselves to be parsed easily.

(Okay, one functional change: fix punctuation linebreaks with trailing WS)
  • Loading branch information
sam-mccall committed Apr 4, 2020
1 parent a975fde commit ebd522a
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 57 deletions.
97 changes: 40 additions & 57 deletions clang-tools-extra/clangd/Hover.cpp
Expand Up @@ -616,46 +616,39 @@ llvm::Optional<HoverInfo> getHoverContents(const Expr *E, ParsedAST &AST) {
return llvm::None;
}

bool isParagraphLineBreak(llvm::StringRef Str, size_t LineBreakIndex) {
return Str.substr(LineBreakIndex + 1)
.drop_while([](auto C) { return C == ' ' || C == '\t'; })
.startswith("\n");
bool isParagraphBreak(llvm::StringRef Rest) {
return Rest.ltrim(" \t").startswith("\n");
}

bool isPunctuationLineBreak(llvm::StringRef Str, size_t LineBreakIndex) {
bool punctuationIndicatesLineBreak(llvm::StringRef Line) {
constexpr llvm::StringLiteral Punctuation = R"txt(.:,;!?)txt";

return LineBreakIndex > 0 && Punctuation.contains(Str[LineBreakIndex - 1]);
Line = Line.rtrim();
return !Line.empty() && Punctuation.contains(Line.back());
}

bool isFollowedByHardLineBreakIndicator(llvm::StringRef Str,
size_t LineBreakIndex) {
bool isHardLineBreakIndicator(llvm::StringRef Rest) {
// '-'/'*' md list, '@'/'\' documentation command, '>' md blockquote,
// '#' headings, '`' code blocks
constexpr llvm::StringLiteral LinbreakIdenticators = R"txt(-*@\>#`)txt";
constexpr llvm::StringLiteral LinebreakIndicators = R"txt(-*@\>#`)txt";

auto NextNonSpaceCharIndex = Str.find_first_not_of(' ', LineBreakIndex + 1);

if (NextNonSpaceCharIndex == llvm::StringRef::npos) {
Rest = Rest.ltrim(" \t");
if (Rest.empty())
return false;
}

auto FollowedBySingleCharIndicator =
LinbreakIdenticators.find(Str[NextNonSpaceCharIndex]) !=
llvm::StringRef::npos;

auto FollowedByNumberedListIndicator =
llvm::isDigit(Str[NextNonSpaceCharIndex]) &&
NextNonSpaceCharIndex + 1 < Str.size() &&
(Str[NextNonSpaceCharIndex + 1] == '.' ||
Str[NextNonSpaceCharIndex + 1] == ')');
if (LinebreakIndicators.contains(Rest.front()))
return true;

return FollowedBySingleCharIndicator || FollowedByNumberedListIndicator;
if (llvm::isDigit(Rest.front())) {
llvm::StringRef AfterDigit = Rest.drop_while(llvm::isDigit);
if (AfterDigit.startswith(".") || AfterDigit.startswith(")"))
return true;
}
return false;
}

bool isHardLineBreak(llvm::StringRef Str, size_t LineBreakIndex) {
return isPunctuationLineBreak(Str, LineBreakIndex) ||
isFollowedByHardLineBreakIndicator(Str, LineBreakIndex);
bool isHardLineBreakAfter(llvm::StringRef Line, llvm::StringRef Rest) {
return punctuationIndicatesLineBreak(Line) || isHardLineBreakIndicator(Rest);
}

} // namespace
Expand Down Expand Up @@ -814,42 +807,32 @@ markup::Document HoverInfo::present() const {
}

void parseDocumentation(llvm::StringRef Input, markup::Document &Output) {
std::vector<llvm::StringRef> ParagraphLines;
auto FlushParagraph = [&] {
if (ParagraphLines.empty())
return;
auto &P = Output.addParagraph();
for (llvm::StringRef Line : ParagraphLines)
P.appendText(Line.str());
ParagraphLines.clear();
};

constexpr auto WhiteSpaceChars = "\t\n\v\f\r ";

auto TrimmedInput = Input.trim();

std::string CurrentLine;

for (size_t CharIndex = 0; CharIndex < TrimmedInput.size();) {
if (TrimmedInput[CharIndex] == '\n') {
// Trim whitespace infront of linebreak
const auto LastNonSpaceCharIndex =
CurrentLine.find_last_not_of(WhiteSpaceChars) + 1;
CurrentLine.erase(LastNonSpaceCharIndex);
llvm::StringRef Line, Rest;
for (std::tie(Line, Rest) = Input.split('\n');
!(Line.empty() && Rest.empty());
std::tie(Line, Rest) = Rest.split('\n')) {

if (isParagraphLineBreak(TrimmedInput, CharIndex) ||
isHardLineBreak(TrimmedInput, CharIndex)) {
// FIXME: maybe distinguish between line breaks and paragraphs
Output.addParagraph().appendText(CurrentLine);
CurrentLine = "";
} else {
// Ommit linebreak
CurrentLine += ' ';
}
// After a linebreak remove spaces to avoid 4 space markdown code blocks.
// FIXME: make FlushParagraph handle this.
Line = Line.ltrim();
if (!Line.empty())
ParagraphLines.push_back(Line);

CharIndex++;
// After a linebreak always remove spaces to avoid 4 space markdown code
// blocks, also skip all additional linebreaks since they have no effect
CharIndex = TrimmedInput.find_first_not_of(WhiteSpaceChars, CharIndex);
} else {
CurrentLine += TrimmedInput[CharIndex];
CharIndex++;
if (isParagraphBreak(Rest) || isHardLineBreakAfter(Line, Rest)) {
FlushParagraph();
}
}
if (!CurrentLine.empty()) {
Output.addParagraph().appendText(CurrentLine);
}
FlushParagraph();
}

llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
Expand Down
5 changes: 5 additions & 0 deletions clang-tools-extra/clangd/unittests/HoverTests.cpp
Expand Up @@ -1980,6 +1980,11 @@ TEST(Hover, DocCommentLineBreakConversion) {
"foo. \nbar",
"foo.\nbar",
},
{
"foo. \nbar",
"foo. \nbar",
"foo.\nbar",
},
{
"foo\n*bar",
"foo \n\\*bar",
Expand Down

0 comments on commit ebd522a

Please sign in to comment.