Index: lib/Format/BreakableToken.h =================================================================== --- lib/Format/BreakableToken.h +++ lib/Format/BreakableToken.h @@ -161,8 +161,8 @@ /// /// A result having offset == StringRef::npos means that no reformat is /// necessary. - virtual Split getSplitAfterLastLine(unsigned TailOffset, unsigned ColumnLimit, - llvm::Regex &CommentPragmasRegex) const { + virtual Split getSplitAfterLastLine(unsigned TailOffset, + unsigned ColumnLimit) const { return Split(StringRef::npos, 0); } @@ -347,8 +347,8 @@ void replaceWhitespaceBefore(unsigned LineIndex, unsigned PreviousEndColumn, unsigned ColumnLimit, Split SplitBefore, WhitespaceManager &Whitespaces) override; - Split getSplitAfterLastLine(unsigned TailOffset, unsigned ColumnLimit, - llvm::Regex &CommentPragmasRegex) const override; + Split getSplitAfterLastLine(unsigned TailOffset, + unsigned ColumnLimit) const override; bool mayReflow(unsigned LineIndex, llvm::Regex &CommentPragmasRegex) const override; Index: lib/Format/BreakableToken.cpp =================================================================== --- lib/Format/BreakableToken.cpp +++ lib/Format/BreakableToken.cpp @@ -22,6 +22,11 @@ #include #define DEBUG_TYPE "format-token-breaker" +#define DBG(A) \ + DEBUG({ \ + llvm::dbgs() << __func__ << ":" << __LINE__ << ":" << #A << " = '" << A \ + << "'\n"; \ + }); namespace clang { namespace format { @@ -681,12 +686,18 @@ InPPDirective, /*Newlines=*/1, ContentColumn[LineIndex] - Prefix.size()); } -BreakableToken::Split BreakableBlockComment::getSplitAfterLastLine( - unsigned TailOffset, unsigned ColumnLimit, - llvm::Regex &CommentPragmasRegex) const { - if (DelimitersOnNewline) - return getSplit(Lines.size() - 1, TailOffset, ColumnLimit, - CommentPragmasRegex); +BreakableToken::Split +BreakableBlockComment::getSplitAfterLastLine(unsigned TailOffset, + unsigned ColumnLimit) const { + if (DelimitersOnNewline) { + // Replace the trailing whitespace of the last line with a newline. + // In case the last line is empty, the ending '*/' is already on its own + // line. + StringRef Line = Content.back().substr(TailOffset); + StringRef TrimmedLine = Line.rtrim(Blanks); + if (!TrimmedLine.empty()) + return Split(TrimmedLine.size(), Line.size() - TrimmedLine.size()); + } return Split(StringRef::npos, 0); } Index: lib/Format/ContinuationIndenter.cpp =================================================================== --- lib/Format/ContinuationIndenter.cpp +++ lib/Format/ContinuationIndenter.cpp @@ -1383,8 +1383,8 @@ } } - BreakableToken::Split SplitAfterLastLine = Token->getSplitAfterLastLine( - TailOffset, ColumnLimit, CommentPragmasRegex); + BreakableToken::Split SplitAfterLastLine = + Token->getSplitAfterLastLine(TailOffset, ColumnLimit); if (SplitAfterLastLine.first != StringRef::npos) { if (!DryRun) Token->replaceWhitespaceAfterLastLine(TailOffset, SplitAfterLastLine, Index: unittests/Format/FormatTestJS.cpp =================================================================== --- unittests/Format/FormatTestJS.cpp +++ unittests/Format/FormatTestJS.cpp @@ -158,6 +158,53 @@ "var x = 1;\n" "}", getGoogleJSStyleWithColumns(20))); + + // Don't break the first line of a single line short jsdoc comment pragma. + EXPECT_EQ("/** @returns j */", + format("/** @returns j */", + getGoogleJSStyleWithColumns(20))); + + // Break a single line long jsdoc comment pragma. + EXPECT_EQ("/**\n" + " * @returns {string} jsdoc line 12\n" + " */", + format("/** @returns {string} jsdoc line 12 */", + getGoogleJSStyleWithColumns(20))); + + EXPECT_EQ("/**\n" + " * @returns {string} jsdoc line 12\n" + " */", + format("/** @returns {string} jsdoc line 12 */", + getGoogleJSStyleWithColumns(20))); + + EXPECT_EQ("/**\n" + " * @returns {string} jsdoc line 12\n" + " */", + format("/** @returns {string} jsdoc line 12*/", + getGoogleJSStyleWithColumns(20))); + + // Fix a multiline jsdoc comment ending in a comment pragma. + EXPECT_EQ("/**\n" + " * line 1\n" + " * line 2\n" + " * @returns {string} jsdoc line 12\n" + " */", + format("/** line 1\n" + " * line 2\n" + " * @returns {string} jsdoc line 12 */", + getGoogleJSStyleWithColumns(20))); + + EXPECT_EQ("/**\n" + " * line 1\n" + " * line 2\n" + " *\n" + " * @returns j\n" + " */", + format("/** line 1\n" + " * line 2\n" + " *\n" + " * @returns j */", + getGoogleJSStyleWithColumns(20))); } TEST_F(FormatTestJS, UnderstandsJavaScriptOperators) {