sammccall created this revision. sammccall added a reviewer: kadircet. Herald added subscribers: cfe-commits, usaxena95, arphaman, jkorous, MaskRay, ilya-biryukov. Herald added a project: clang. sammccall added a comment.
Some saturday morning procrastination. But I'm wondering how good an idea this is for plaintext: "Returns 'foo' on failure" is better than "Returns foo on failure", right? (With backticks instead of single quotes, phab is eating my formatting) Maybe we should have an Emphasis flag or something on plaintext to preserve the quotes? (We don't want to include them e.g. in the return type chunks) Also, realized our model for whitespace around paragraph chunks isn't ideal after all :-( Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D77456 Files: 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
Index: clang-tools-extra/clangd/unittests/HoverTests.cpp =================================================================== --- clang-tools-extra/clangd/unittests/HoverTests.cpp +++ clang-tools-extra/clangd/unittests/HoverTests.cpp @@ -1935,7 +1935,7 @@ } } -TEST(Hover, DocCommentLineBreakConversion) { +TEST(Hover, ParseDocumentation) { struct Case { llvm::StringRef Documentation; llvm::StringRef ExpectedRenderMarkdown; @@ -1994,6 +1994,22 @@ "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) { Index: clang-tools-extra/clangd/Hover.cpp =================================================================== --- clang-tools-extra/clangd/Hover.cpp +++ clang-tools-extra/clangd/Hover.cpp @@ -747,7 +747,7 @@ // 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); @@ -806,6 +806,53 @@ 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(Input), 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 = [&] { @@ -813,7 +860,7 @@ return; auto &P = Output.addParagraph(); for (llvm::StringRef Line : ParagraphLines) - P.appendText(Line.str()); + parseDocumentationLine(Line, P); ParagraphLines.clear(); }; Index: clang-tools-extra/clangd/FormattedString.h =================================================================== --- clang-tools-extra/clangd/FormattedString.h +++ clang-tools-extra/clangd/FormattedString.h @@ -46,10 +46,10 @@ 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 { Index: clang-tools-extra/clangd/FormattedString.cpp =================================================================== --- clang-tools-extra/clangd/FormattedString.cpp +++ clang-tools-extra/clangd/FormattedString.cpp @@ -216,23 +216,10 @@ } // 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 @@ } } -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; }
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits