// Copyright (C) 2019 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0

#include "clangformat-test.h"

#include "../clangformatbaseindenter.h"

#include <texteditor/tabsettings.h>
#include <utils/fileutils.h>

#include <QTest>
#include <QTextCursor>
#include <QTextDocument>

#include <optional>

namespace ClangFormat::Internal {

class ClangFormatTestIndenter : public ClangFormatBaseIndenter
{
public:
    ClangFormatTestIndenter(QTextDocument *doc) : ClangFormatBaseIndenter(doc) {}

private:
    std::optional<TextEditor::TabSettingsData> tabSettings() const override { return {}; }
};

class ClangFormatExtendedTestIndenter : public ClangFormatTestIndenter
{
public:
    ClangFormatExtendedTestIndenter(QTextDocument *doc) : ClangFormatTestIndenter(doc) {}

private:
    bool formatCodeInsteadOfIndent() const override { return true; }
    bool formatWhileTyping() const override { return true; }
};

class ClangFormatTest final : public QObject
{
    Q_OBJECT

public:
    ClangFormatTest();
    ~ClangFormatTest();

private slots:
    void testIndentBasicFile();
    void testIndentEmptyLine();
    void testIndentLambda();
    void testIndentNestedIfElse();
    void testIndentInitializerListInArguments();
    void testIndentLambdaWithReturnType();
    void testIndentFunctionArgumentLambdaWithNextLineScope();
    void testIndentScopeAsFunctionArgument();
    void testIndentInsideStructuredBinding();
    void testIndentMacrosWithoutSemicolon();
    void testIndentAfterSquareBracesInsideBraceInitialization();
    void testIndentStringLiteralContinuation();
    void testIndentTemplateparameters();
    void testNoExtraIndentAfterStatementInsideSquareBraces();
    void testNoExtraIndentAfterBraceInitialization();
    void testIndentMultipleEmptyLines();
    void testIndentEmptyLineAndKeepPreviousEmptyLines();
    void testIndentOnElectricCharacterButNotRemoveEmptyLinesBefore();
    void testIndentAfterExtraSpaceInpreviousLine();
    void testIndentEmptyLineInsideParentheses();
    void testIndentInsideIf();
    void testIndentInsideIf2();
    void testIndentInsideIf3();
    void testEmptyLineInInitializerList();
    void testIndentClosingBraceAfterComma();
    void testDoNotIndentClosingBraceAfterSemicolon();
    void testIndentAfterIf();
    void testIndentAfterElse();
    void testSameIndentAfterSecondNewLineAfterIf();
    void testIndentAfterNewLineInsideIfWithFunctionCall();
    void testSameIndentAfterSecondNewLineInsideIfWithFunctionCall();
    void testSameIndentAfterSecondNonEmptyNewLineInsideIfWithFunctionCall();
    void testSameIndentsOnNewLinesAfterComments();
    void testIndentAfterEmptyLineAfterAngledIncludeDirective();
    void testIndentAfterEmptyLineAfterQuotedIncludeDirective();
    void testIndentAfterLineComment();
    void testIndentAfterBlockComment();
    void testIndentAfterIfdef();
    void testIndentAfterEmptyLineInTheFileBeginning();
    void testIndentFunctionBodyButNotFormatBeforeIt();
    void testIndentAfterFunctionBodyAndNotFormatBefore();
    void testReformatToEmptyFunction();
    void testReformatToNonEmptyFunction();
    void testIndentClosingScopeAndFormatBeforeIt();
    void testDoNotFormatAfterTheFirstColon();
    void testOnlyIndentIncompleteStatementOnElectricalCharacter();
    void testIndentAndFormatCompleteStatementOnSemicolon();
    void testIndentAndFormatCompleteStatementOnClosingScope();
    void testOnlyIndentClosingParenthesis();
    void testEquallyIndentInsideParenthesis();
    void testFormatBasicFile();
    void testFormatEmptyLine();
    void testFormatLambda();
    void testIndentTwoLambdas();
    void testFormatInitializerListInArguments();
    void testFormatFunctionArgumentLambdaWithScope();
    void testFormatScopeAsFunctionArgument();
    void testFormatStructuredBinding();
    void testFormatStringLiteralContinuation();
    void testFormatTemplateparameters();
    void testSortIncludes();
    void testChainedMemberFunctionCalls();
    void testCommentBlock();
    void testClassIndentStructure();
    void testIndentInitializeVector();
    void testIndentFunctionArgumentOnNewLine();
    void testIndentCommentOnNewLine();
    void testUtf8SymbolLine();
    void testFunctionCallClosingParenthesis();
    void testFunctionCallClosingParenthesisEmptyLine();
    void testNoIndentationInMiddleOfLine();
    void testIndentationInMiddleOfLine();
    void testIndentationInTheBegginingOfLine();
    void testIndentationReturnAfterIf();
    void testIndentationReturnAfterIfSomthingFunction();
    void testReformatQualifier();

private:
    void insertLines(const std::vector<QString> &lines);
    std::vector<QString> documentLines() const;

    QTextDocument * const m_doc;
    QTextCursor * const m_cursor;
    ClangFormatBaseIndenter * const m_indenter;
    ClangFormatBaseIndenter * const m_extendedIndenter;
};

ClangFormatTest::ClangFormatTest()
    : m_doc(new QTextDocument(this)),
      m_cursor(new QTextCursor(m_doc)),
      m_indenter(new ClangFormatTestIndenter(m_doc)),
      m_extendedIndenter(new ClangFormatExtendedTestIndenter(m_doc))
{
    m_indenter->setFileName(Utils::FilePath::fromString(TESTDATA_DIR "/test.cpp"));
    m_extendedIndenter->setFileName(Utils::FilePath::fromString(TESTDATA_DIR "/test.cpp"));
}

ClangFormatTest::~ClangFormatTest()
{
    delete m_extendedIndenter;
    delete m_indenter;
    delete m_cursor;
}

void ClangFormatTest::insertLines(const std::vector<QString> &lines)
{
    m_doc->clear();
    m_cursor->setPosition(0);
    for (std::size_t lineNumber = 1; lineNumber <= lines.size(); ++lineNumber) {
        if (lineNumber > 1)
            m_cursor->insertBlock();
        m_cursor->insertText(lines[lineNumber - 1]);
    }
    m_cursor->setPosition(0);
    m_cursor->movePosition(QTextCursor::End, QTextCursor::KeepAnchor);

}

std::vector<QString> ClangFormatTest::documentLines() const
{
    std::vector<QString> result;
    const int lines = m_doc->blockCount();
    result.reserve(static_cast<size_t>(lines));
    for (int line = 0; line < lines; ++line)
        result.push_back(m_doc->findBlockByNumber(line).text());
    return result;
}

void ClangFormatTest::testIndentBasicFile()
{
    insertLines({"int main()", "{", "int a;", "}"});
    m_indenter->indent(*m_cursor, QChar::Null, TextEditor::TabSettingsData());
    QCOMPARE(documentLines(), (std::vector<QString>{"int main()", "{", "    int a;", "}"}));
}

void ClangFormatTest::testIndentEmptyLine()
{
    insertLines({"int main()", "{", "", "}"});
    m_indenter->indent(*m_cursor, QChar::Null, TextEditor::TabSettingsData());
    QCOMPARE(documentLines(), (std::vector<QString>{"int main()", "{", "    ", "}"}));
}

void ClangFormatTest::testIndentLambda()
{
    insertLines({"int b = foo([](){", "", "});"});
    m_indenter->indent(*m_cursor, QChar::Null, TextEditor::TabSettingsData());
    QCOMPARE(documentLines(), (std::vector<QString>{"int b = foo([](){", "    ", "});"}));
}

void ClangFormatTest::testIndentNestedIfElse()
{
    insertLines({"if (a)",
                 "if (b)",
                 "foo();",
                 "else",
                 "bar();",
                 "else",
                 "baz();"});
    m_indenter->indent(*m_cursor, QChar::Null, TextEditor::TabSettingsData());
    QCOMPARE(documentLines(), (std::vector<QString>{
                 "if (a)",
                 "    if (b)",
                 "        foo();",
                 "    else",
                 "        bar();",
                 "else",
                 "    baz();"}));
}

void ClangFormatTest::testIndentInitializerListInArguments()
{
    insertLines({"foo(arg1,", "args,", "{1, 2});"});
    m_indenter->indent(*m_cursor, QChar::Null, TextEditor::TabSettingsData());
    QCOMPARE(documentLines(), (std::vector<QString>{"foo(arg1,", "    args,", "    {1, 2});"}));
}

void ClangFormatTest::testIndentLambdaWithReturnType()
{
    insertLines({"{", "auto lambda = []() -> bool {", "", "};", "}"});
    m_indenter->indent(*m_cursor, QChar::Null, TextEditor::TabSettingsData());
    QCOMPARE(documentLines(), (std::vector<QString>{"{", "    auto lambda = []() -> bool {", "        ", "    };", "}"}));
}

void ClangFormatTest::testIndentFunctionArgumentLambdaWithNextLineScope()
{
    insertLines({"foo([]()", "{", "", "});"});
    m_indenter->indent(*m_cursor, QChar::Null, TextEditor::TabSettingsData());
    QCOMPARE(documentLines(), (std::vector<QString>{"foo([]()", "    {", "        ", "    });"}));
}

void ClangFormatTest::testIndentScopeAsFunctionArgument()
{
    insertLines({"foo(", "{", "", "});"});
    m_indenter->indent(*m_cursor, QChar::Null, TextEditor::TabSettingsData());
    QCOMPARE(documentLines(), (std::vector<QString>{"foo(", "    {", "        ", "    });"}));
}

void ClangFormatTest::testIndentInsideStructuredBinding()
{
    insertLines({"auto [a,", "b] = c;"});
    m_indenter->indent(*m_cursor, QChar::Null, TextEditor::TabSettingsData());
    QCOMPARE(documentLines(), (std::vector<QString>{"auto [a,", "      b] = c;"}));
}

void ClangFormatTest::testIndentMacrosWithoutSemicolon()
{
    insertLines({"void test()",
                 "{",
                 "ASSERT(1);",
                 "ASSERT(2)",
                 "ASSERT(3)",
                 "ASSERT(4);",
                 "ASSERT(5)",
                 "}"});
    m_indenter->indent(*m_cursor, QChar::Null, TextEditor::TabSettingsData());
    QCOMPARE(documentLines(), (std::vector<QString>{
                 "void test()",
                 "{",
                 "    ASSERT(1);",
                 "    ASSERT(2)",
                 "    ASSERT(3)",
                 "    ASSERT(4);",
                 "    ASSERT(5)",
                 "}"}));
}

void ClangFormatTest::testIndentAfterSquareBracesInsideBraceInitialization()
{
    insertLines({"int foo() {", "char a = char{b[0]};", "int c;", "}"});
    m_indenter->indent(*m_cursor, QChar::Null, TextEditor::TabSettingsData());
    QCOMPARE(documentLines(), (std::vector<QString>{"int foo() {", "    char a = char{b[0]};", "    int c;", "}"}));
}

void ClangFormatTest::testIndentStringLiteralContinuation()
{
    insertLines({"foo(bar, \"foo\"", "\"bar\");"});
    m_indenter->indent(*m_cursor, QChar::Null, TextEditor::TabSettingsData());
    QCOMPARE(documentLines(), (std::vector<QString>{"foo(bar, \"foo\"", "         \"bar\");"}));
}

void ClangFormatTest::testIndentTemplateparameters()
{
    insertLines({"using Alias = Template<A,", "B,", "C>"});
    m_indenter->indent(*m_cursor, QChar::Null, TextEditor::TabSettingsData());
    QCOMPARE(documentLines(), (std::vector<QString>{
                     "using Alias = Template<A,",
                     "                       B,",
                     "                       C>"}));
}

void ClangFormatTest::testNoExtraIndentAfterStatementInsideSquareBraces()
{
    insertLines({"{", "    x[y=z];", "    int a;", "}"});
    m_indenter->indent(*m_cursor, QChar::Null, TextEditor::TabSettingsData());
    QCOMPARE(documentLines(), (std::vector<QString>{"{", "    x[y=z];", "    int a;", "}"}));
}

void ClangFormatTest::testNoExtraIndentAfterBraceInitialization()
{
    insertLines({"int j{i?5:10};", "return 0;"});
    m_indenter->indent(*m_cursor, QChar::Null, TextEditor::TabSettingsData());
    QCOMPARE(documentLines(), (std::vector<QString>{"int j{i?5:10};", "return 0;"}));
}

void ClangFormatTest::testIndentMultipleEmptyLines()
{
    insertLines({"{", "", "", "", "}"});
    m_indenter->indent(*m_cursor, QChar::Null, TextEditor::TabSettingsData());
    QCOMPARE(documentLines(), (std::vector<QString>{"{", "    ", "    ", "    ", "}"}));
}

void ClangFormatTest::testIndentEmptyLineAndKeepPreviousEmptyLines()
{
    insertLines({"{", "    ", "    ", "", "}"});
    m_indenter->indentBlock(m_doc->findBlockByNumber(3), QChar::Null, TextEditor::TabSettingsData());
    QCOMPARE(documentLines(), (std::vector<QString>{"{", "    ", "    ", "    ", "}"}));
}

void ClangFormatTest::testIndentOnElectricCharacterButNotRemoveEmptyLinesBefore()
{
    insertLines({"{", "    ", "    ", "if ()", "}"});
    m_indenter->indentBlock(m_doc->findBlockByNumber(3), '(', TextEditor::TabSettingsData());
    QCOMPARE(documentLines(), (std::vector<QString>{"{", "    ", "    ", "    if ()", "}"}));
}

void ClangFormatTest::testIndentAfterExtraSpaceInpreviousLine()
{
    insertLines({"if (a ", "&& b)"});
    m_indenter->indentBlock(m_doc->findBlockByNumber(1), QChar::Null, TextEditor::TabSettingsData());
    QCOMPARE(documentLines(), (std::vector<QString>{"if (a", "    && b)"}));
}

void ClangFormatTest::testIndentEmptyLineInsideParentheses()
{
    insertLines({"if (a ", "", "    && b)"});
    m_indenter->indentBlock(m_doc->findBlockByNumber(1), QChar::Null, TextEditor::TabSettingsData());
    QCOMPARE(documentLines(), (std::vector<QString>{"if (a", "    ", "    && b)"}));
}

void ClangFormatTest::testIndentInsideIf()
{
    insertLines({"if (a && b", ")"});
    m_indenter->indentBlock(m_doc->findBlockByNumber(1), QChar::Null, TextEditor::TabSettingsData());
    QCOMPARE(documentLines(), (std::vector<QString>{"if (a && b", "    )"}));
}

void ClangFormatTest::testIndentInsideIf2()
{
    insertLines({"if (a && b &&", ")"});
    m_indenter->indentBlock(m_doc->findBlockByNumber(1), QChar::Null, TextEditor::TabSettingsData());
    QCOMPARE(documentLines(), (std::vector<QString>{"if (a && b &&", "    )"}));
}

void ClangFormatTest::testIndentInsideIf3()
{
    insertLines({"if (a || b", ")"});
    m_indenter->indentBlock(m_doc->findBlockByNumber(1), QChar::Null, TextEditor::TabSettingsData());
    QCOMPARE(documentLines(), (std::vector<QString>{"if (a || b", "    )"}));
}

void ClangFormatTest::testEmptyLineInInitializerList()
{
    insertLines({"Bar foo{a,", "", "};"});
    m_indenter->indentBlock(m_doc->findBlockByNumber(1), QChar::Null, TextEditor::TabSettingsData());
    QCOMPARE(documentLines(), (std::vector<QString>{"Bar foo{a,", "        ", "};"}));
}

void ClangFormatTest::testIndentClosingBraceAfterComma()
{
    insertLines({"Bar foo{a,", "}"});
    m_indenter->indentBlock(m_doc->findBlockByNumber(1), QChar::Null, TextEditor::TabSettingsData());
    QCOMPARE(documentLines(), (std::vector<QString>{"Bar foo{a,", "        }"}));
}

void ClangFormatTest::testDoNotIndentClosingBraceAfterSemicolon()
{
    insertLines({"{", "    a;" "}"});
    m_indenter->indentBlock(m_doc->findBlockByNumber(2), QChar::Null, TextEditor::TabSettingsData());
    QCOMPARE(documentLines(), (std::vector<QString>{"{", "    a;" "}"}));
}

void ClangFormatTest::testIndentAfterIf()
{
    insertLines({"if (a)", ""});
    m_indenter->indentBlock(m_doc->findBlockByNumber(1), QChar::Null, TextEditor::TabSettingsData());
    QCOMPARE(documentLines(), (std::vector<QString>{"if (a)", "    "}));
}

void ClangFormatTest::testIndentAfterElse()
{
    insertLines({"if (a)", "    foo();", "else", ""});
    m_indenter->indentBlock(m_doc->findBlockByNumber(3), QChar::Null, TextEditor::TabSettingsData());
    QCOMPARE(documentLines(), (std::vector<QString>{"if (a)", "    foo();", "else", "    "}));
}

void ClangFormatTest::testSameIndentAfterSecondNewLineAfterIf()
{
    insertLines({"if (a)", "    ", ""});
    m_indenter->indentBlock(m_doc->findBlockByNumber(2), QChar::Null, TextEditor::TabSettingsData());
    QCOMPARE(documentLines(), (std::vector<QString>{"if (a)", "    ", "    "}));
}

void ClangFormatTest::testIndentAfterNewLineInsideIfWithFunctionCall()
{
    insertLines({"if (foo()", ")"});
    m_indenter->indentBlock(m_doc->findBlockByNumber(1), QChar::Null, TextEditor::TabSettingsData());
    QCOMPARE(documentLines(), (std::vector<QString>{"if (foo()", "    )"}));
}

void ClangFormatTest::testSameIndentAfterSecondNewLineInsideIfWithFunctionCall()
{
    insertLines({"if (foo()", "    ", ")"});
    m_indenter->indentBlock(m_doc->findBlockByNumber(2), QChar::Null, TextEditor::TabSettingsData());
    QCOMPARE(documentLines(), (std::vector<QString>{"if (foo()", "    ", "    )"}));
}

void ClangFormatTest::testSameIndentAfterSecondNonEmptyNewLineInsideIfWithFunctionCall()
{
    insertLines({"if (foo()", "    ", "bar)"});
    m_indenter->indentBlock(m_doc->findBlockByNumber(2), QChar::Null, TextEditor::TabSettingsData());
    QCOMPARE(documentLines(), (std::vector<QString>{"if (foo()", "    ", "    bar)"}));
}

void ClangFormatTest::testSameIndentsOnNewLinesAfterComments()
{
    insertLines({"namespace {} //comment", "", ""});
    m_indenter->indentBlock(m_doc->findBlockByNumber(2), QChar::Null, TextEditor::TabSettingsData());
    QCOMPARE(documentLines(), (std::vector<QString>{"namespace {} //comment", "", ""}));
}

void ClangFormatTest::testIndentAfterEmptyLineAfterAngledIncludeDirective()
{
    insertLines({"#include <string>", "", "using namespace std;"});
    m_indenter->indentBlock(m_doc->findBlockByNumber(2), QChar::Null, TextEditor::TabSettingsData());
    QCOMPARE(documentLines(), (std::vector<QString>{"#include <string>", "", "using namespace std;"}));
}

void ClangFormatTest::testIndentAfterEmptyLineAfterQuotedIncludeDirective()
{
    insertLines({"#include \"foo.h\"", "", "using namespace std;"});
    m_indenter->indentBlock(m_doc->findBlockByNumber(2), QChar::Null, TextEditor::TabSettingsData());
    QCOMPARE(documentLines(), (std::vector<QString>{"#include \"foo.h\"", "", "using namespace std;"}));
}

void ClangFormatTest::testIndentAfterLineComment()
{
    insertLines({"int foo()",
                 "{",
                 "    // Comment",
                 "    ",
                 "    if (",
                 "}"});
    m_indenter->indentBlock(m_doc->findBlockByNumber(4), '(', TextEditor::TabSettingsData());
    QCOMPARE(documentLines(), (std::vector<QString>{
                 "int foo()",
                 "{",
                 "    // Comment",
                 "    ",
                 "    if (",
                 "}"}));
}

void ClangFormatTest::testIndentAfterBlockComment()
{
    insertLines({"int foo()",
                 "{",
                 "    bar(); /* Comment */",
                 "    ",
                 "    if (",
                 "}"});
    m_indenter->indentBlock(m_doc->findBlockByNumber(4), '(', TextEditor::TabSettingsData());
    QCOMPARE(documentLines(), (std::vector<QString>{
                 "int foo()",
                 "{",
                 "    bar(); /* Comment */",
                 "    ",
                 "    if (",
                 "}"}));
}

void ClangFormatTest::testIndentAfterIfdef()
{
    insertLines({"int foo()",
                 "{",
                 "#ifdef FOO",
                 "#endif",
                 "    ",
                 "    if (",
                 "}"});
    m_indenter->indentBlock(m_doc->findBlockByNumber(5), '(', TextEditor::TabSettingsData());
    QCOMPARE(documentLines(), (std::vector<QString>{
                 "int foo()",
                 "{",
                 "#ifdef FOO",
                 "#endif",
                 "    ",
                 "    if (",
                 "}"}));
}

void ClangFormatTest::testIndentAfterEmptyLineInTheFileBeginning()
{
    insertLines({"", "void foo()"});
    m_indenter->indentBlock(m_doc->findBlockByNumber(1), ')', TextEditor::TabSettingsData());
    QCOMPARE(documentLines(), (std::vector<QString>{"", "void foo()"}));
}

void ClangFormatTest::testIndentFunctionBodyButNotFormatBeforeIt()
{
    insertLines({"int foo(int a, int b,",
                 "        int c, int d",
                 "        ) {",
                 "",
                 "}"});
    m_extendedIndenter->indentBlock(m_doc->findBlockByNumber(3), QChar::Null, TextEditor::TabSettingsData());
    QCOMPARE(documentLines(), (std::vector<QString>{
                 "int foo(int a, int b,",
                 "        int c, int d",
                 "        ) {",
                 "    ",
                 "}"}));
}

void ClangFormatTest::testIndentAfterFunctionBodyAndNotFormatBefore()
{
    insertLines({"int foo(int a, int b, int c, int d)",
                 "{",
                 "    ",
                 "}"});
    m_extendedIndenter->indentBlock(m_doc->findBlockByNumber(3),
                                 QChar::Null,
                                 TextEditor::TabSettingsData(),
                                 m_doc->characterCount() - 3);
    QCOMPARE(documentLines(), (std::vector<QString>{
                 "int foo(int a, int b, int c, int d)",
                 "{",
                 "    ",
                 "}"}));
}

void ClangFormatTest::testReformatToEmptyFunction()
{
    insertLines({"int foo(int a, int b, int c, int d)", "{", "    ", "}"});
    m_extendedIndenter->indentBlock(m_doc->findBlockByNumber(3), '}', TextEditor::TabSettingsData());
    QCOMPARE(documentLines(), (std::vector<QString>{"int foo(int a, int b, int c, int d) {}"}));
}

void ClangFormatTest::testReformatToNonEmptyFunction()
{
    insertLines({"int foo(int a, int b) {", "", "}"});
    m_extendedIndenter->indentBlock(m_doc->findBlockByNumber(1), QChar::Null,
                                    TextEditor::TabSettingsData());
    QCOMPARE(documentLines(), (std::vector<QString>{"int foo(int a, int b) {", "    ", "}"}));
}

void ClangFormatTest::testIndentClosingScopeAndFormatBeforeIt()
{
    insertLines({"if(a && b", "   &&c && d", "   ) {", "", "}"});
    m_extendedIndenter->indentBlock(m_doc->findBlockByNumber(4), '}', TextEditor::TabSettingsData());
    QCOMPARE(documentLines(), (std::vector<QString>{"if (a && b && c && d) {", "}"}));
}

void ClangFormatTest::testDoNotFormatAfterTheFirstColon()
{
    insertLines({"{", "    Qt:", "}"});
    m_extendedIndenter->indentBlock(m_doc->findBlockByNumber(1), ':', TextEditor::TabSettingsData(), 9);
    QCOMPARE(documentLines(), (std::vector<QString>{"{", "    Qt:", "}"}));
}

void ClangFormatTest::testOnlyIndentIncompleteStatementOnElectricalCharacter()
{
    insertLines({"{bar();", "foo()", "}"});
    m_extendedIndenter->indentBlock(m_doc->findBlockByNumber(1), '(', TextEditor::TabSettingsData(), 12);
    QCOMPARE(documentLines(), (std::vector<QString>{"{bar();", "    foo()", "}"}));
}

void ClangFormatTest::testIndentAndFormatCompleteStatementOnSemicolon()
{
    insertLines({"{bar();", "foo();", "}"});
    m_extendedIndenter->indentBlock(m_doc->findBlockByNumber(1), ';', TextEditor::TabSettingsData(), 14);
    QCOMPARE(documentLines(), (std::vector<QString>{"{", "    bar();", "    foo();", "}"}));
}

void ClangFormatTest::testIndentAndFormatCompleteStatementOnClosingScope()
{
    insertLines({"{bar();", "foo();", "}"});
    m_extendedIndenter->indentBlock(m_doc->findBlockByNumber(1), '}', TextEditor::TabSettingsData(), 16);
    QCOMPARE(documentLines(), (std::vector<QString>{"{", "    bar();", "    foo();", "}"}));
}

void ClangFormatTest::testOnlyIndentClosingParenthesis()
{
    insertLines({"foo(a,", "    ", ")"});
    m_extendedIndenter->indentBlock(m_doc->findBlockByNumber(2), QChar::Null, TextEditor::TabSettingsData());
    QCOMPARE(documentLines(), (std::vector<QString>{"foo(a,", "    ", "    )"}));
}

void ClangFormatTest::testEquallyIndentInsideParenthesis()
{
    insertLines({"if (a", ")"});
    m_extendedIndenter->indentBlock(m_doc->findBlockByNumber(1), QChar::Null,
                                    TextEditor::TabSettingsData());
    auto linesAfterFirstLineBreak = documentLines();
    insertLines({"if (a", "    ", ")"});
    m_extendedIndenter->indentBlock(m_doc->findBlockByNumber(2),
                                    QChar::Null, TextEditor::TabSettingsData());
    QCOMPARE(linesAfterFirstLineBreak, (std::vector<QString>{"if (a", "    )"}));
    QCOMPARE(documentLines(), (std::vector<QString>{"if (a", "    ", "    )"}));
}

void ClangFormatTest::testFormatBasicFile()
{
    insertLines({"int main()", "{", "int a;", "}"});
    m_indenter->format({{1, 4}});
    QCOMPARE(documentLines(), (std::vector<QString>{"int main()", "{", "    int a;", "}"}));
}

void ClangFormatTest::testFormatEmptyLine()
{
    insertLines({"int main()", "{", "", "}"});
    m_indenter->format({{1, 4}});
    QCOMPARE(documentLines(), (std::vector<QString>{"int main() {}"}));
}

void ClangFormatTest::testFormatLambda()
{
    insertLines({"int b = foo([](){", "", "});"});
    m_indenter->format({{1, 3}});
    QCOMPARE(documentLines(), (std::vector<QString>{"int b = foo([]() {", "", "});"}));
}

void ClangFormatTest::testIndentTwoLambdas()
{
    insertLines({
        "void SomeTool::reloadModels()",
        "{",
        "m_client->listModels([this](QStringList newModels) {",
        "newModels.sort();",
        "setSupportedModels(std::move(newModels));",
        "}, [this](QString error) {",
        "reportListModelsError(error);",
        "});",
        "}"});
    m_indenter->indent(*m_cursor, {}, {});
    QEXPECT_FAIL(nullptr, "QTCREATORBUG-34031", Abort);
    QCOMPARE(documentLines(), (std::vector<QString>{
        "void SomeTool::reloadModels()",
        "{",
        "    m_client->listModels([this](QStringList newModels) {",
        "            newModels.sort();",
        "            setSupportedModels(std::move(newModels));",
        "        }, [this](QString error) {",
        "            reportListModelsError(error);",
        "        });",
        "}"}));
}

void ClangFormatTest::testFormatInitializerListInArguments()
{
    insertLines({"foo(arg1,", "args,", "{1, 2});"});
    m_indenter->format({{1, 3}});
    QCOMPARE(documentLines(), (std::vector<QString>{"foo(arg1, args, {1, 2});"}));
}

void ClangFormatTest::testFormatFunctionArgumentLambdaWithScope()
{
    insertLines({"foo([]()", "{", "", "});"});
    m_indenter->format({{1, 4}});
    QCOMPARE(documentLines(), (std::vector<QString>{"foo([]() {", "", "});"}));
}

void ClangFormatTest::testFormatScopeAsFunctionArgument()
{
    insertLines({"foo(", "{", "", "});"});
    m_indenter->format({{1, 4}});
    QCOMPARE(documentLines(), (std::vector<QString>{"foo({", "", "});"}));
}

void ClangFormatTest::testFormatStructuredBinding()
{
    insertLines({"auto [a,", "b] = c;"});
    m_indenter->format({{1, 2}});
    QCOMPARE(documentLines(), (std::vector<QString>{"auto [a, b] = c;"}));
}

void ClangFormatTest::testFormatStringLiteralContinuation()
{
    insertLines({"foo(bar, \"foo\"", "\"bar\");"});
    m_indenter->format({{1, 2}});
    QCOMPARE(documentLines(), (std::vector<QString>{"foo(bar,", "    \"foo\"", "    \"bar\");"}));
}

void ClangFormatTest::testFormatTemplateparameters()
{
    insertLines({"using Alias = Template<A,", "B,", "C>"});
    m_indenter->format({{1, 3}});
    QCOMPARE(documentLines(), (std::vector<QString>{"using Alias = Template<A, B, C>"}));
}

void ClangFormatTest::testSortIncludes()
{
    insertLines({"#include \"b.h\"",
                 "#include \"a.h\"",
                 "",
                 "#include <bb.h>",
                 "#include <aa.h>"});
    m_indenter->format({{1, 5}});
    QCOMPARE(documentLines(), (std::vector<QString>{
                 "#include \"a.h\"",
                 "#include \"b.h\"",
                 "",
                 "#include <aa.h>",
                 "#include <bb.h>"}));
}

void ClangFormatTest::testChainedMemberFunctionCalls()
{
    insertLines({"S().func().func()", ".func();"});
    m_indenter->indent(*m_cursor, QChar::Null, TextEditor::TabSettingsData());
    QCOMPARE(documentLines(), (std::vector<QString>{"S().func().func()", "    .func();"}));
}

void ClangFormatTest::testCommentBlock()
{
    insertLines({"/****************************************************************************",
                 "**",
                 "** Copyright (C) 2021 The Qt Company Ltd.",
                 "** Contact: https://www.qt.io/licensing/",
                 "**",
                 "** This file is part of Qt Creator.",
                 "**",
                 "****************************************************************************/"});
    m_indenter->indent(*m_cursor, QChar::Null, TextEditor::TabSettingsData());
    QCOMPARE(documentLines(), (std::vector<QString>{
                 "/****************************************************************************",
                 "**",
                 "** Copyright (C) 2021 The Qt Company Ltd.",
                 "** Contact: https://www.qt.io/licensing/",
                 "**",
                 "** This file is part of Qt Creator.",
                 "**",
                    "****************************************************************************/"}));
}

void ClangFormatTest::testClassIndentStructure()
{
    insertLines(
        {"class test {", "    Q_OBJECT", "    QML_ELEMENT", "    QML_SINGLETON", "    public:", "};"});
    m_indenter->indent(*m_cursor, QChar::Null, TextEditor::TabSettingsData());
    QCOMPARE(documentLines(),
             (std::vector<QString>{"class test {",
                                   "    Q_OBJECT",
                                   "    QML_ELEMENT",
                                   "    QML_SINGLETON",
                                   "public:",
                                   "};"}));
}

void ClangFormatTest::testIndentInitializeVector()
{
    insertLines({
        "class Test {",
        "public:",
        "    Test();",
        "};",
        "",
        "Test::Test()",
        "{",
        "    QList<int> list = {",
        "        1,",
        "        2,",
        "        3,",
        "    };",
        "    QList<int> list_2 = {",
        "        1,",
        "        2,",
        "        3,",
        "    };",
        "}",
        "",
        "int main()",
        "{",
        "}"
    });
    m_indenter->indent(*m_cursor, QChar::Null, TextEditor::TabSettingsData());
    QCOMPARE(documentLines(),
             (std::vector<QString>{
                 "class Test {",
                 "public:",
                 "    Test();",
                 "};",
                 "",
                 "Test::Test()",
                 "{",
                 "    QList<int> list = {",
                 "        1,",
                 "        2,",
                 "        3,",
                 "    };",
                 "    QList<int> list_2 = {",
                 "        1,",
                 "        2,",
                 "        3,",
                 "    };",
                 "}",
                 "",
                 "int main()",
                 "{",
                 "}"
             }));
}

void ClangFormatTest::testIndentFunctionArgumentOnNewLine()
{
    insertLines(
        {"Bar foo(",
         "a,",
         ")"
        });
    m_indenter->indentBlock(m_doc->findBlockByNumber(1), QChar::Null, TextEditor::TabSettingsData());
    QCOMPARE(documentLines(),
             (std::vector<QString>{
                "Bar foo(",
                "    a,",
                ")"
              }));
}

void ClangFormatTest::testIndentCommentOnNewLine()
{
    insertLines(
        {"/*!",
         "    \\qmlproperty double Type::property",
         " ",
         "    \\brief The property of Type.",
         "*/"
        });
    m_indenter->indent(*m_cursor, QChar::Null, TextEditor::TabSettingsData());
    QCOMPARE(documentLines(),
             (std::vector<QString>{
                 "/*!",
                 "    \\qmlproperty double Type::property",
                 " ",
                 "    \\brief The property of Type.",
                 "*/"
             }));
}

void ClangFormatTest::testUtf8SymbolLine()
{
    insertLines({"int main()",
                 "{",
                 "    cout << \"ä\" << endl;",
                 "    return 0;",
                 "}"});
    m_indenter->indent(*m_cursor, QChar::Null, TextEditor::TabSettingsData());
    QCOMPARE(documentLines(),
             (std::vector<QString>{"int main()",
                                   "{",
                                   "    cout << \"ä\" << endl;",
                                   "    return 0;",
                                   "}"}));
}

void ClangFormatTest::testFunctionCallClosingParenthesis()
{
    insertLines(
        {"class X {",
         "public:",
         "    QString add() const;",
         "    QString sub() const;",
         "};",
         "",
         "QString X::add() const",
         "{",
         "    return QString()",
         "}",
         "",
         "QString X::sub() const",
         "{}"});
    m_indenter->indentBlock(m_doc->findBlockByNumber(8), ')', TextEditor::TabSettingsData());
    QCOMPARE(
        documentLines(),
        (std::vector<QString>{
            "class X {",
            "public:",
            "    QString add() const;",
            "    QString sub() const;",
            "};",
            "",
            "QString X::add() const",
            "{",
            "    return QString()",
            "}",
            "",
            "QString X::sub() const",
            "{}",
        }));
}

void ClangFormatTest::testFunctionCallClosingParenthesisEmptyLine()
{
    insertLines(
        {"class X {",
         "public:",
         "    QString add() const;",
         "    QString sub() const;",
         "};",
         "",
         "QString X::add() const",
         "{",
         "",
         "    return QString()",
         "}",
         "",
         "QString X::sub() const",
         "{}"});
    m_indenter->indentBlock(m_doc->findBlockByNumber(8), QChar::Null, TextEditor::TabSettingsData());
    QCOMPARE(
        documentLines(),
        (std::vector<QString>{
                              "class X {",
                              "public:",
                              "    QString add() const;",
                              "    QString sub() const;",
                              "};",
                              "",
                              "QString X::add() const",
                              "{",
                              "    ",
                              "    return QString()",
                              "}",
                              "",
                              "QString X::sub() const",
                              "{}",
                              }));
}

void ClangFormatTest::testNoIndentationInMiddleOfLine()
{
    insertLines({"int main()",
                 "{",
                 "    S s = {.i = 1}, .l = 2, .f = 3, .d = 4};",
                 "    return 0;",
                 "}"});
    m_cursor->setPosition(30);
    m_extendedIndenter->indent(*m_cursor, '}', TextEditor::TabSettingsData(), 30);
    QCOMPARE(documentLines(),
             (std::vector<QString>{"int main()",
                                   "{",
                                   "    S s = {.i = 1}, .l = 2, .f = 3, .d = 4};",
                                   "    return 0;",
                                   "}"}));
}

void ClangFormatTest::testIndentationInMiddleOfLine()
{
    insertLines({"int main()",
                 "{",
                 "    S s = {.i = 1,",
                 ".l = 2, .f = 3, .d = 4};",
                 "    return 0;",
                 "}"});
    m_cursor->setPosition(32);
    m_extendedIndenter->indent(*m_cursor, QChar::Null, TextEditor::TabSettingsData(), 32);
    QCOMPARE(documentLines(),
             (std::vector<QString>{"int main()",
                                   "{",
                                   "    S s = {.i = 1,",
                                   "           .l = 2, .f = 3, .d = 4};",
                                   "    return 0;",
                                   "}"}));
}

void ClangFormatTest::testIndentationInTheBegginingOfLine()
{
    insertLines({"int main()",
                 "{",
                 "    if () {",
                 "          } else",
                 "}"});
    m_cursor->setPosition(35);
    m_extendedIndenter->indent(*m_cursor, '}', TextEditor::TabSettingsData(), 35);
    QCOMPARE(documentLines(),
             (std::vector<QString>{"int main()",
                                   "{",
                                   "    if () {",
                                   "    } else",
                                   "}"}));
}

void ClangFormatTest::testIndentationReturnAfterIf()
{
    insertLines({"int main()",
                 "{",
                 "    if (true)",
                 "    return 0;",
                 "}"});
    m_indenter->indent(*m_cursor, QChar::Null, TextEditor::TabSettingsData());
    QCOMPARE(documentLines(),
             (std::vector<QString>{"int main()",
                                   "{",
                                   "    if (true)",
                                   "        return 0;",
                                   "}"}));
}

void ClangFormatTest::testIndentationReturnAfterIfSomthingFunction()
{
    insertLines({"int main()",
                 "{",
                 "    if_somthing()",
                 "    return 0;",
                 "}"});
    m_indenter->indent(*m_cursor, QChar::Null, TextEditor::TabSettingsData());
    QCOMPARE(documentLines(),
             (std::vector<QString>{"int main()",
                                   "{",
                                   "    if_somthing()",
                                   "    return 0;",
                                   "}"}));
}

void ClangFormatTest::testReformatQualifier()
{
    insertLines({
        "struct S",
        "{",
        "    S &operator=(S const &s);",
        "};",
        "S &S::operator=(const S &s) {}"
    });
    m_extendedIndenter->autoIndent(*m_cursor, TextEditor::TabSettingsData());
    const std::vector<QString> expected{
        "struct S",
        "{",
        "    S &operator=(S const &s);",
        "};",
        "S &S::operator=(S const &s) {}"};
    QCOMPARE(documentLines(), expected);
}

QObject *createClangFormatTest()
{
    return new ClangFormatTest;
}

} // namespace ClangFormat::Internal

#include "clangformat-test.moc"
