//===-- FormatTests.cpp - Automatic code formatting tests -----------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "Format.h"
#include "Annotations.h"
#include "SourceCode.h"
#include "TestFS.h"
#include "clang/Format/Format.h"
#include "clang/Tooling/Core/Replacement.h"
#include "llvm/Support/Error.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"

namespace clang {
namespace clangd {
namespace {

std::string afterTyped(llvm::StringRef CodeWithCursor,
                           llvm::StringRef Typed) {
  Annotations Code(CodeWithCursor);
  unsigned Cursor = llvm::cantFail(positionToOffset(Code.code(), Code.point()));
  auto Changes =
      formatIncremental(Code.code(), Cursor, Typed,
                        format::getGoogleStyle(format::FormatStyle::LK_Cpp));
  tooling::Replacements Merged;
  for (const auto& R : Changes)
    if (llvm::Error E = Merged.add(R))
      ADD_FAILURE() << llvm::toString(std::move(E));
  auto NewCode = tooling::applyAllReplacements(Code.code(), Merged);
  EXPECT_TRUE(bool(NewCode))
      << "Bad replacements: " << llvm::toString(NewCode.takeError());
  NewCode->insert(transformCursorPosition(Cursor, Changes), "^");
  return *NewCode;
}

// We can't pass raw strings directly to EXPECT_EQ because of gcc bugs.
void expectAfterNewline(const char *Before, const char *After) {
  EXPECT_EQ(After, afterTyped(Before, "\n")) << Before;
}
void expectAfter(const char *Typed, const char *Before, const char *After) {
  EXPECT_EQ(After, afterTyped(Before, Typed)) << Before;
}

TEST(FormatIncremental, SplitComment) {
  expectAfterNewline(R"cpp(
// this comment was
^split
)cpp",
   R"cpp(
// this comment was
// ^split
)cpp");

  expectAfterNewline(R"cpp(
// trailing whitespace is not a split
^   
)cpp",
   R"cpp(
// trailing whitespace is not a split
^
)cpp");

  expectAfterNewline(R"cpp(
// splitting a
^
// multiline comment
)cpp",
                     R"cpp(
// splitting a
// ^
// multiline comment
)cpp");

  expectAfterNewline(R"cpp(
// extra   
    ^     whitespace
)cpp",
   R"cpp(
// extra
// ^whitespace
)cpp");

  expectAfterNewline(R"cpp(
/// triple
^slash
)cpp",
   R"cpp(
/// triple
/// ^slash
)cpp");

  expectAfterNewline(R"cpp(
/// editor continuation
//^
)cpp",
   R"cpp(
/// editor continuation
/// ^
)cpp");

  expectAfterNewline(R"cpp(
// break before
^ // slashes
)cpp",
   R"cpp(
// break before
^// slashes
)cpp");


  expectAfterNewline(R"cpp(
int x;  // aligned
^comment
)cpp",
   R"cpp(
int x;  // aligned
        // ^comment
)cpp");

  // Fixed bug: the second line of the aligned comment shouldn't be "attached"
  // to the cursor and outdented.
  expectAfterNewline(R"cpp(
void foo() {
  if (x)
    return; // All spelled tokens are accounted for.
            // that takes two lines
            ^
}
)cpp",
   R"cpp(
void foo() {
  if (x)
    return;  // All spelled tokens are accounted for.
             // that takes two lines
  ^
}
)cpp");
}

TEST(FormatIncremental, Indentation) {
  expectAfterNewline(R"cpp(
void foo() {
  if (bar)
^
)cpp",
   R"cpp(
void foo() {
  if (bar)
    ^
)cpp");

  expectAfterNewline(R"cpp(
void foo() {
  bar(baz(
^
)cpp",
   R"cpp(
void foo() {
  bar(baz(
      ^
)cpp");

  expectAfterNewline(R"cpp(
void foo() {
^}
)cpp",
   R"cpp(
void foo() {
  ^
}
)cpp");

  expectAfterNewline(R"cpp(
class X {
protected:
^
)cpp",
   R"cpp(
class X {
 protected:
  ^
)cpp");

// Mismatched brackets (1)
  expectAfterNewline(R"cpp(
void foo() {
  foo{bar(
^}
}
)cpp",
   R"cpp(
void foo() {
  foo {
    bar(
        ^}
}
)cpp");
// Mismatched brackets (2)
  expectAfterNewline(R"cpp(
void foo() {
  foo{bar(
^text}
}
)cpp",
   R"cpp(
void foo() {
  foo {
    bar(
        ^text}
}
)cpp");
// Matched brackets
  expectAfterNewline(R"cpp(
void foo() {
  foo{bar(
^)
}
)cpp",
   R"cpp(
void foo() {
  foo {
    bar(
        ^)
}
)cpp");
}

TEST(FormatIncremental, FormatPreviousLine) {
  expectAfterNewline(R"cpp(
void foo() {
   untouched( );
int x=2;
^
)cpp",
                     R"cpp(
void foo() {
   untouched( );
   int x = 2;
   ^
)cpp");

  expectAfterNewline(R"cpp(
int x=untouched( );
auto L = []{return;return;};
^
)cpp",
   R"cpp(
int x=untouched( );
auto L = [] {
  return;
  return;
};
^
)cpp");
}

TEST(FormatIncremental, Annoyances) {
  // Don't remove newlines the user typed!
  expectAfterNewline(R"cpp(
int x(){


^
}
)cpp",
   R"cpp(
int x(){


  ^
}
)cpp");
  // FIXME: we should not remove newlines here, either.
  expectAfterNewline(R"cpp(
class x{
 public:

^
}
)cpp",
   R"cpp(
class x{
 public:
  ^
}
)cpp");
}

TEST(FormatIncremental, FormatBrace) {
  expectAfter("}", R"cpp(
vector<int> x= {
  1,
  2,
  3}^
)cpp",
              R"cpp(
vector<int> x = {1, 2, 3}^
)cpp");
}

} // namespace
} // namespace clangd
} // namespace clang
