teak-llvm/clang-tools-extra/clangd/unittests/FormatTests.cpp
Sam McCall 25c6257ba0 [clangd] Revamp textDocument/onTypeFormatting.
Summary:
The existing implementation (which triggers on }) is fairly simple and
has flaws:
 - doesn't trigger frequently/regularly enough (particularly in editors that type the }
 for you)
 - often reformats too much code around the edit
 - has jarring cases that I don't have clear ideas for fixing

This implementation is designed to trigger on newline, which feels to me more
intuitive than } or ;.
It does have allow for reformatting after other characters - it has a
basic behavior and a model for adding specialized behavior for
particular characters. But at least initially I'd stick to advertising
\n in the capabilities.

This also handles comment splitting: when you insert a line break inside
a line comment, it will make the new line into an aligned line comment.

Working on tests, but want people to patch it in and try it - it's hard to
see if "feel" is right purely by looking at a test.

Reviewers: ilya-biryukov, hokein

Subscribers: mgorny, ioeric, MaskRay, jkorous, arphaman, kadircet, cfe-commits

Tags: #clang

Differential Revision: https://reviews.llvm.org/D60605

llvm-svn: 362939
2019-06-10 14:26:21 +00:00

309 lines
4.9 KiB
C++

//===-- 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