ClangFormat: Fix unit-tests

Amends dcf763c7ee.
Change the logic in empty lines modification to properly
indent all empty lines.

Change-Id: Id945cf66915dfd192216660543594a7905426761
Reviewed-by: Marco Bubke <marco.bubke@qt.io>
This commit is contained in:
Ivan Donchevskii
2019-02-19 12:46:38 +01:00
parent 08e1fbbbfa
commit be65a57935
3 changed files with 119 additions and 74 deletions

View File

@@ -39,7 +39,7 @@ namespace ClangFormat {
static void adjustFormatStyleForLineBreak(clang::format::FormatStyle &style, static void adjustFormatStyleForLineBreak(clang::format::FormatStyle &style,
ReplacementsToKeep replacementsToKeep) ReplacementsToKeep replacementsToKeep)
{ {
style.MaxEmptyLinesToKeep = 2; style.MaxEmptyLinesToKeep = 100;
style.SortIncludes = false; style.SortIncludes = false;
style.SortUsingDeclarations = false; style.SortUsingDeclarations = false;
@@ -68,7 +68,6 @@ static clang::tooling::Replacements filteredReplacements(
const clang::tooling::Replacements &replacements, const clang::tooling::Replacements &replacements,
int utf8Offset, int utf8Offset,
int utf8Length, int utf8Length,
int extraEmptySpaceOffset,
ReplacementsToKeep replacementsToKeep) ReplacementsToKeep replacementsToKeep)
{ {
clang::tooling::Replacements filtered; clang::tooling::Replacements filtered;
@@ -81,9 +80,6 @@ static clang::tooling::Replacements filteredReplacements(
if (replacementDoesNotMatchRestriction) if (replacementDoesNotMatchRestriction)
continue; continue;
if (replacementOffset >= utf8Offset - 1)
replacementOffset += extraEmptySpaceOffset;
llvm::StringRef text = replacementsToKeep == ReplacementsToKeep::OnlyIndent llvm::StringRef text = replacementsToKeep == ReplacementsToKeep::OnlyIndent
? clearExtraNewline(replacement.getReplacementText()) ? clearExtraNewline(replacement.getReplacementText())
: replacement.getReplacementText(); : replacement.getReplacementText();
@@ -147,18 +143,23 @@ static int previousEmptyLinesLength(const QTextBlock &currentBlock)
return length; return length;
} }
static void modifyToIndentEmptyLines( static int modifyToIndentEmptyLines(QByteArray &buffer, const QTextBlock &block, bool secondTry)
QByteArray &buffer, int utf8Offset, const QTextBlock &block, bool secondTry)
{ {
const QString blockText = block.text(); const QString blockText = block.text();
int firstNonWhitespace = Utils::indexOf(blockText, int firstNonWhitespace = Utils::indexOf(blockText,
[](const QChar &ch) { return !ch.isSpace(); }); [](const QChar &ch) { return !ch.isSpace(); });
int utf8Offset = Utils::Text::utf8NthLineOffset(block.document(),
buffer,
block.blockNumber() + 1);
if (firstNonWhitespace > 0) if (firstNonWhitespace > 0)
utf8Offset += firstNonWhitespace; utf8Offset += firstNonWhitespace;
else
utf8Offset += blockText.length();
const bool closingParenBlock = firstNonWhitespace >= 0 const bool closingParenBlock = firstNonWhitespace >= 0
&& blockText.at(firstNonWhitespace) == ')'; && blockText.at(firstNonWhitespace) == ')';
int extraLength = 0;
if (firstNonWhitespace < 0 || closingParenBlock) { if (firstNonWhitespace < 0 || closingParenBlock) {
//This extra text works for the most cases. //This extra text works for the most cases.
QByteArray dummyText("a;a;"); QByteArray dummyText("a;a;");
@@ -174,6 +175,7 @@ static void modifyToIndentEmptyLines(
dummyText = "&& a"; dummyText = "&& a";
buffer.insert(utf8Offset, dummyText); buffer.insert(utf8Offset, dummyText);
extraLength += dummyText.length();
} }
if (secondTry) { if (secondTry) {
@@ -186,52 +188,61 @@ static void modifyToIndentEmptyLines(
// unclosed parentheses. // unclosed parentheses.
// TODO: Does it help to add different endings depending on the context? // TODO: Does it help to add different endings depending on the context?
buffer.insert(nextLinePos, ')'); buffer.insert(nextLinePos, ')');
extraLength += 1;
} }
} }
return extraLength;
} }
static Utils::LineColumn utf16LineColumn(const QTextBlock &block, static Utils::LineColumn utf16LineColumn(const QByteArray &utf8Buffer, int utf8Offset)
int blockOffsetUtf8,
const QByteArray &utf8Buffer,
int utf8Offset)
{ {
// If lastIndexOf('\n') returns -1 then we are fine to add 1 and get 0 offset. Utils::LineColumn lineColumn;
const int lineStartUtf8Offset = utf8Offset == 0 lineColumn.line = std::count(utf8Buffer.begin(), utf8Buffer.begin() + utf8Offset, '\n') + 1;
? 0 lineColumn.column = utf8Offset - utf8Buffer.lastIndexOf('\n', utf8Offset - 1);
: utf8Buffer.lastIndexOf('\n', utf8Offset - 1) + 1; return lineColumn;
int line = block.blockNumber() + 1; // Init with the line corresponding the block.
if (utf8Offset < blockOffsetUtf8) {
line -= static_cast<int>(std::count(utf8Buffer.begin() + lineStartUtf8Offset,
utf8Buffer.begin() + blockOffsetUtf8,
'\n'));
} else {
line += static_cast<int>(std::count(utf8Buffer.begin() + blockOffsetUtf8,
utf8Buffer.begin() + lineStartUtf8Offset,
'\n'));
}
const QByteArray lineText = utf8Buffer.mid(lineStartUtf8Offset,
utf8Offset - lineStartUtf8Offset);
return Utils::LineColumn(line, QString::fromUtf8(lineText).size() + 1);
} }
static TextEditor::Replacements utf16Replacements(const QTextBlock &block, static QString utf16LineLengthInUtf8Buffer(const QByteArray &utf8Buffer, int currentUtf8Offset)
int blockOffsetUtf8, {
const int lineStartUtf8Offset = utf8Buffer.lastIndexOf('\n', currentUtf8Offset - 1) + 1;
const int lineEndUtf8Offset = utf8Buffer.indexOf('\n', currentUtf8Offset);
return QString::fromUtf8(
utf8Buffer.mid(lineStartUtf8Offset, lineEndUtf8Offset - lineStartUtf8Offset));
}
static bool isInsideModifiedLine(const QString &originalLine,
const QString &modifiedLine,
int column)
{
// Track the cases when we have inserted extra text into the line to get the indentation.
return originalLine.length() < modifiedLine.length() && column != modifiedLine.length() + 1
&& (column > originalLine.length() || originalLine.trimmed().isEmpty()
|| !modifiedLine.startsWith(originalLine));
}
static TextEditor::Replacements utf16Replacements(const QTextDocument *doc,
const QByteArray &utf8Buffer, const QByteArray &utf8Buffer,
const clang::tooling::Replacements &replacements) const clang::tooling::Replacements &replacements)
{ {
TextEditor::Replacements convertedReplacements; TextEditor::Replacements convertedReplacements;
convertedReplacements.reserve(replacements.size()); convertedReplacements.reserve(replacements.size());
for (const clang::tooling::Replacement &replacement : replacements) { for (const clang::tooling::Replacement &replacement : replacements) {
const Utils::LineColumn lineColUtf16 = utf16LineColumn(block, Utils::LineColumn lineColUtf16 = utf16LineColumn(utf8Buffer,
blockOffsetUtf8, static_cast<int>(replacement.getOffset()));
utf8Buffer,
static_cast<int>(
replacement.getOffset()));
if (!lineColUtf16.isValid()) if (!lineColUtf16.isValid())
continue; continue;
const int utf16Offset = Utils::Text::positionInText(block.document(),
const QString lineText = doc->findBlockByNumber(lineColUtf16.line - 1).text();
const QString bufferLineText = utf16LineLengthInUtf8Buffer(utf8Buffer,
replacement.getOffset());
if (isInsideModifiedLine(lineText, bufferLineText, lineColUtf16.column))
continue;
lineColUtf16.column = std::min(lineColUtf16.column, lineText.length() + 1);
const int utf16Offset = Utils::Text::positionInText(doc,
lineColUtf16.line, lineColUtf16.line,
lineColUtf16.column); lineColUtf16.column);
const int utf16Length = QString::fromUtf8( const int utf16Length = QString::fromUtf8(
@@ -270,14 +281,11 @@ static QString selectedLines(QTextDocument *doc,
const QTextBlock &startBlock, const QTextBlock &startBlock,
const QTextBlock &endBlock) const QTextBlock &endBlock)
{ {
QString text = Utils::Text::textAt(QTextCursor(doc), return Utils::Text::textAt(QTextCursor(doc),
startBlock.position(), startBlock.position(),
std::max(0, std::max(0,
endBlock.position() + endBlock.length() endBlock.position() + endBlock.length()
- startBlock.position() - 1)); - startBlock.position() - 1));
while (!text.isEmpty() && text.rbegin()->isSpace())
text.chop(1);
return text;
} }
ClangFormatBaseIndenter::ClangFormatBaseIndenter(QTextDocument *doc) ClangFormatBaseIndenter::ClangFormatBaseIndenter(QTextDocument *doc)
@@ -358,10 +366,7 @@ TextEditor::Replacements ClangFormatBaseIndenter::format(
clang::format::FormattingAttemptStatus status; clang::format::FormattingAttemptStatus status;
const clang::tooling::Replacements clangReplacements const clang::tooling::Replacements clangReplacements
= reformat(style, buffer.data(), ranges, m_fileName.toString().toStdString(), &status); = reformat(style, buffer.data(), ranges, m_fileName.toString().toStdString(), &status);
const TextEditor::Replacements toReplace = utf16Replacements(block, const TextEditor::Replacements toReplace = utf16Replacements(m_doc, buffer, clangReplacements);
utf8Offset,
buffer,
clangReplacements);
applyReplacements(m_doc, toReplace); applyReplacements(m_doc, toReplace);
return toReplace; return toReplace;
@@ -392,7 +397,7 @@ static bool doNotIndentInContext(QTextDocument *doc, int pos)
return false; return false;
} }
void ClangFormatBaseIndenter::indentBlocks(const QTextBlock &startBlock, void ClangFormatBaseIndenter::indentBlocks(QTextBlock startBlock,
const QTextBlock &endBlock, const QTextBlock &endBlock,
const QChar &typedChar, const QChar &typedChar,
int cursorPositionInEditor) int cursorPositionInEditor)
@@ -403,6 +408,15 @@ void ClangFormatBaseIndenter::indentBlocks(const QTextBlock &startBlock,
return; return;
} }
if (startBlock.position() > 0) {
startBlock = startBlock.previous();
while (startBlock.position() > 0 && startBlock.text().trimmed().isEmpty())
startBlock = startBlock.previous();
if (!startBlock.text().trimmed().isEmpty()) {
startBlock = startBlock.next();
}
}
const int startBlockPosition = startBlock.position(); const int startBlockPosition = startBlock.position();
trimFirstNonEmptyBlock(startBlock); trimFirstNonEmptyBlock(startBlock);
if (cursorPositionInEditor >= 0) if (cursorPositionInEditor >= 0)
@@ -564,22 +578,20 @@ TextEditor::Replacements ClangFormatBaseIndenter::replacements(QByteArray buffer
if (replacementsToKeep == ReplacementsToKeep::IndentAndBefore) if (replacementsToKeep == ReplacementsToKeep::IndentAndBefore)
rangeStart = formattingRangeStart(startBlock, buffer, lastSaveRevision()); rangeStart = formattingRangeStart(startBlock, buffer, lastSaveRevision());
int extraEmptySpaceOffset = previousEmptyLinesLength(startBlock);
utf8Offset -= extraEmptySpaceOffset;
buffer.remove(utf8Offset, extraEmptySpaceOffset);
adjustFormatStyleForLineBreak(style, replacementsToKeep);
if (typedChar == QChar::Null && startBlock == endBlock) {
modifyToIndentEmptyLines(buffer, utf8Offset, startBlock, secondTry);
utf8Length = 0;
}
if (replacementsToKeep == ReplacementsToKeep::IndentAndBefore) { if (replacementsToKeep == ReplacementsToKeep::IndentAndBefore) {
buffer.insert(utf8Offset - 1, " //"); buffer.insert(utf8Offset - 1, " //");
extraEmptySpaceOffset -= 3;
utf8Offset += 3; utf8Offset += 3;
} }
adjustFormatStyleForLineBreak(style, replacementsToKeep);
if (typedChar == QChar::Null) {
for (int index = startBlock.blockNumber(); index <= endBlock.blockNumber(); ++index) {
utf8Length += modifyToIndentEmptyLines(buffer,
m_doc->findBlockByNumber(index),
secondTry);
}
}
if (replacementsToKeep != ReplacementsToKeep::IndentAndBefore || utf8Offset < rangeStart) if (replacementsToKeep != ReplacementsToKeep::IndentAndBefore || utf8Offset < rangeStart)
rangeStart = utf8Offset; rangeStart = utf8Offset;
@@ -601,7 +613,6 @@ TextEditor::Replacements ClangFormatBaseIndenter::replacements(QByteArray buffer
clangReplacements, clangReplacements,
utf8Offset, utf8Offset,
utf8Length, utf8Length,
extraEmptySpaceOffset,
replacementsToKeep); replacementsToKeep);
} }
const bool canTryAgain = replacementsToKeep == ReplacementsToKeep::OnlyIndent const bool canTryAgain = replacementsToKeep == ReplacementsToKeep::OnlyIndent
@@ -617,7 +628,7 @@ TextEditor::Replacements ClangFormatBaseIndenter::replacements(QByteArray buffer
true); true);
} }
return utf16Replacements(startBlock, originalOffsetUtf8, originalBuffer, filtered); return utf16Replacements(m_doc, buffer, filtered);
} }
} // namespace ClangFormat } // namespace ClangFormat

View File

@@ -75,7 +75,7 @@ protected:
private: private:
void indent(const QTextCursor &cursor, const QChar &typedChar, int cursorPositionInEditor); void indent(const QTextCursor &cursor, const QChar &typedChar, int cursorPositionInEditor);
void indentBlocks(const QTextBlock &startBlock, void indentBlocks(QTextBlock startBlock,
const QTextBlock &endBlock, const QTextBlock &endBlock,
const QChar &typedChar, const QChar &typedChar,
int cursorPositionInEditor); int cursorPositionInEditor);

View File

@@ -331,6 +331,40 @@ TEST_F(ClangFormat, NoExtraIndentAfterBraceInitialization)
"return 0;")); "return 0;"));
} }
TEST_F(ClangFormat, IndentMultipleEmptyLines)
{
insertLines({"{",
"",
"",
"",
"}"});
indenter.indent(cursor, QChar::Null, TextEditor::TabSettings());
ASSERT_THAT(documentLines(), ElementsAre("{",
" ",
" ",
" ",
"}"));
}
TEST_F(ClangFormat, IndentEmptyLineAndKeepPreviousEmptyLines)
{
insertLines({"{",
" ",
" ",
"",
"}"});
indenter.indentBlock(doc.findBlockByNumber(3), QChar::Null, TextEditor::TabSettings());
ASSERT_THAT(documentLines(), ElementsAre("{",
" ",
" ",
" ",
"}"));
}
TEST_F(ClangFormat, IndentFunctionBodyAndFormatBeforeIt) TEST_F(ClangFormat, IndentFunctionBodyAndFormatBeforeIt)
{ {
insertLines({"int foo(int a, int b,", insertLines({"int foo(int a, int b,",
@@ -469,7 +503,7 @@ TEST_F(ClangFormat, FormatBasicFile)
"int a;", "int a;",
"}"}); "}"});
indenter.format(cursor); indenter.format({{1, 4}});
ASSERT_THAT(documentLines(), ElementsAre("int main()", ASSERT_THAT(documentLines(), ElementsAre("int main()",
"{", "{",
@@ -484,7 +518,7 @@ TEST_F(ClangFormat, FormatEmptyLine)
"", "",
"}"}); "}"});
indenter.format(cursor); indenter.format({{1, 4}});
ASSERT_THAT(documentLines(), ElementsAre("int main() {}")); ASSERT_THAT(documentLines(), ElementsAre("int main() {}"));
} }
@@ -495,7 +529,7 @@ TEST_F(ClangFormat, FormatLambda)
"", "",
"});"}); "});"});
indenter.format(cursor); indenter.format({{1, 3}});
ASSERT_THAT(documentLines(), ElementsAre("int b = foo([]() {", ASSERT_THAT(documentLines(), ElementsAre("int b = foo([]() {",
"", "",
@@ -508,7 +542,7 @@ TEST_F(ClangFormat, FormatInitializerListInArguments)
"args,", "args,",
"{1, 2});"}); "{1, 2});"});
indenter.format(cursor); indenter.format({{1, 3}});
ASSERT_THAT(documentLines(), ElementsAre("foo(arg1, args, {1, 2});")); ASSERT_THAT(documentLines(), ElementsAre("foo(arg1, args, {1, 2});"));
} }
@@ -520,7 +554,7 @@ TEST_F(ClangFormat, FormatFunctionArgumentLambdaWithScope)
"", "",
"});"}); "});"});
indenter.format(cursor); indenter.format({{1, 4}});
ASSERT_THAT(documentLines(), ASSERT_THAT(documentLines(),
ElementsAre("foo([]() {", ElementsAre("foo([]() {",
@@ -535,7 +569,7 @@ TEST_F(ClangFormat, FormatScopeAsFunctionArgument)
"", "",
"});"}); "});"});
indenter.format(cursor); indenter.format({{1, 4}});
ASSERT_THAT(documentLines(), ASSERT_THAT(documentLines(),
ElementsAre("foo({", ElementsAre("foo({",
@@ -548,7 +582,7 @@ TEST_F(ClangFormat, FormatStructuredBinding)
insertLines({"auto [a,", insertLines({"auto [a,",
"b] = c;"}); "b] = c;"});
indenter.format(cursor); indenter.format({{1, 2}});
ASSERT_THAT(documentLines(), ElementsAre("auto [a, b] = c;")); ASSERT_THAT(documentLines(), ElementsAre("auto [a, b] = c;"));
} }
@@ -558,7 +592,7 @@ TEST_F(ClangFormat, FormatStringLiteralContinuation)
insertLines({"foo(bar, \"foo\"", insertLines({"foo(bar, \"foo\"",
"\"bar\");"}); "\"bar\");"});
indenter.format(cursor); indenter.format({{1, 2}});
ASSERT_THAT(documentLines(), ElementsAre("foo(bar,", ASSERT_THAT(documentLines(), ElementsAre("foo(bar,",
" \"foo\"", " \"foo\"",
@@ -571,7 +605,7 @@ TEST_F(ClangFormat, FormatTemplateparameters)
"B,", "B,",
"C>"}); "C>"});
indenter.format(cursor); indenter.format({{1, 3}});
ASSERT_THAT(documentLines(), ElementsAre("using Alias = Template<A, B, C>")); ASSERT_THAT(documentLines(), ElementsAre("using Alias = Template<A, B, C>"));
} }