-
-
Notifications
You must be signed in to change notification settings - Fork 436
Sync sketch formatter configuration from source #1285
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
The sketch code formatter configuration is passed to the ClangFormat tool as a string representing a JSON object via a command line argument. The quotes in the JSON syntax are escaped in order to make them compatible with this usage. Previously, consideration was not given to escaping of the content. For example, with the previous escaping code, this content: `\"` would be converted to `\\"`, whereas the correct escaping would look like `\\\"`. That did not result in problems only because the configuration didn't contain escaped content. This good fortune will not persist through updates to the configuration so the command must be properly processed. The content of the configuration will now be escaped in addition to the quotes of the JSON data format.
The sketch code formatter configuration is passed to the ClangFormat tool as a string representing a JSON object via a command line argument. Previously, the contents of this string were not given any special treatment to ensure compatibility with the command interpreter used on Windows machines. That did not result in problems only because the configuration didn't contain problematic combinations of characters. This good fortune will not persist through updates to the configuration, so the command must be properly processed. The Windows command interpreter does not use the POSIX style backslash escaping. For this reason, escaped quotes in the argument are recognized as normal quotes, meaning that the string alternates between quoted and unquoted states at random. When a character with special significance to the Windows command interpreter happens to occur outside a quoted section, an error results. The solution is to use the Windows command interpreter's caret escaping on these characters. Since such an escaping system is not recognized by POSIX shells, this is only done when the application is running on a Windows machine. References: - https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/echo#remarks - https://en.wikipedia.org/wiki/Escape_character#Windows_Command_Prompt
The Arduino IDE's "Auto Format" feature is configured to produce the standard Arduino sketch formatting style, as established by the Arduino IDE 1.x formatter. The configuration is consumed by several other projects which require the configuration in a YAML file. In order to provide all the consumers with a single canonical source and to locate the infrastructure and activity related to the maintenance of the file in a more appropriate repository, it is now hosted in a permanent location in the `arduino/tooling-project-assets` repository. The following changes have been made to the source configuration: - Move documentation comments to a dedicated file in the upstream repository - Make additional non-functional changes to the configuration format to facilitate maintenance - Update to use the configuration API of ClangFormat 14.0.0 This last item did result in some functional changes to the configuration which will result in minor differences in the formatter output. These are actually reversions of unwanted differences from the Arduino IDE 1.x formatter output, which were unavoidable when using the 11.0.1 version of ClangFormat in use at the time of the configuration's creation. These changes will provide greater consistency during the migration from Arduino IDE 1.x to 2.x. The default output of the Arduino IDE 1.x formatter will continue to be considered the "gold standard" until Arduino IDE 2.x graduates from "pre-release" status. The Arduino IDE 2.x formatter configuration is fully customizable according to the preferences of each user. Those already using custom configurations will not be affected in any way (though they are encouraged to sync their configuration files from the source to bring them into compliance with the configuration API of the ClangFormat version currently in use by Arduino IDE 2.x). See the documentation and commit history for the source file for details on the configuration changes: https://github.com/arduino/tooling-project-assets/tree/main/other/clang-format-configuration
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for the great work and the detailed change description, Per!
Is there a concrete example you want me to verify? One thing that came to my mind; checking the escaping when starting the IDE2 on Windows from CMD.exe
, PowerShell
, and GitBash
.
There is a huge collection of test data in the repository where the configuration is maintained, but it doesn't contain one specific sketch intended to serve for this sort of purpose. So I concatenated all the "targeted" test data files into the code below. That test data is designed to provide 100% coverage of the ClangFormat 14.0.0 configuration options (which the official Arduino sketch "samples" test data is not guaranteed to do). Due to the nature of the content, and the concatenation, this is not a valid program in this form, but it is something ClangFormat can process. If you put this code into a sketch and then run an "Auto Format" on it using the IDE build from this PR, you should see no diff when comparing that sketch to the file formatted by running The command for directly formatting would look something like this:
You will see a diff if you compare the Auto Format output from a previous version of the IDE with the one from this PR. That is the result of the unavoidable or intentional configuration changes made in this PR (which are more thoroughly documented in the commit messages here). class Foo {
public:
protected:
private:
};
void foo(int arg1, int arg2) {
(void)arg1;
(void)arg2;
}
void bar() {
foo(42,
1234);
foo(42,
1234);
}
struct Foo_t {
int bar;
int baz;
};
Foo_t pippo[] = {
{1, 11},
{ 11, 1 },
};
Foo_t pluto[] = {
{ 1, 11},
{11, 1},
};
Foo_t paperino[] = {
{ 1, 11 },
{ 11, 1 },
};
int a = 42;
int bb = 42;
int ccc = 42;
int dddd = 42;
struct Foo_t {
int a : 1;
int bb : 16;
int ccc : 1;
int dddd : 16;
};
int a = 42;
long b = 42;
int c = 42;
long d = 42;
#define FOO_A 42
#define FOO_BB 42
#define FOO_CCC 42
#define FOO_DDDD 42
#define FOO \
int a; \
int bb; \
int ccc; \
int dddd; \
int e;
#define BAR \
int a; \
int bb; \
int ccc; \
int dddd; \
int e;
#define BAZ \
int a; \
int bb; \
int ccc; \
int dddd; \
int e;
int foo = 42 +
1234;
int bar = 42 +
1234;
int baz = 42
+ 1234;
int qux = 42
+ 1234;
int a; // foo
int b = 42; // foo
int c; // foo
int d = 42; // foo
void foo(int arg1, int arg2) {
(void)arg1;
(void)arg2;
}
void bar() {
foo(
42, 1234);
foo(42,
1234);
}
class Foo {
Foo();
Foo(int);
int bar;
int baz;
};
Foo::Foo() :
bar(42), baz(1234) {}
Foo::Foo(int qux) :
bar(qux),
baz(1234) {}
void aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(
int foo, int bar, int baz);
void aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(
int foo, int bar, int baz) {
(void)foo;
(void)bar;
(void)baz;
}
void bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb(int foo,
int bar,
int baz);
void bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb(int foo,
int bar,
int baz) {
(void)foo;
(void)bar;
(void)baz;
}
void foo() {
while (true) {
}
while (true) {}
while (true) {
continue;
}
}
void foo() {
switch (1) {
case 2: break;
case 3:
break;
}
}
enum Foo_t { A, B };
enum Bar_t {
C,
D
};
void foo() {}
void bar() {
}
int baz() { return 42; }
int qux() {
return 42;
}
class Pippo {
void foo() {}
void bar() {
}
int baz() { return 42; }
int qux() {
return 42;
}
};
void foo() {
bool a = true;
if (a)
return;
if (a)
return;
else if (a)
return;
else
return;
if (a)
return;
else if (a) {
return;
} else {
return;
}
if (a) return;
if (a)
return;
else if (a)
return;
else
return;
if (a)
return;
else if (a) {
return;
} else {
return;
}
if (a) return;
if (a) return;
else if (a)
return;
else
return;
if (a) return;
else if (a) {
return;
} else {
return;
}
if (a) return;
if (a) return;
else if (a) return;
else return;
if (a) return;
else if (a) {
return;
} else {
return;
}
}
auto foo = []() {};
auto bar = []() {
};
auto baz = []() {
return 42;
};
void qux(void (*)()) {}
void pippo() {
qux([]() {});
}
void foo() {
while (true) continue;
while (true)
continue;
while (true) {
continue;
}
}
int foo();
int foo() { return 42; }
class Bar {
int foo() { return 42; }
};
int
baz();
int
baz() {
return 42;
}
class Qux {
int
baz() {
return 42;
}
};
void foo() {
char bar[] =
"asdf"
"zxcv";
char baz[] = "asdf"
"zxcv";
(void)bar;
(void)baz;
}
template<typename T> T foo() {}
template<typename T>
T bar() {}
template<typename T> T baz(int a,
int b) {
(void)a;
(void)b;
}
template<typename T>
T qux(int a,
int b) {
(void)a;
(void)b;
}
void foo(int arg1, int arg2, int arg3) {
(void)arg1;
(void)arg2;
(void)arg3;
}
void bar() {
int aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa = 42;
foo(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa);
foo(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa);
}
void foo(int aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, int bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb,
int cccccccccccccccccccccccccccccccccccccccccccccccccc) {
(void)aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa;
(void)bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb;
(void)cccccccccccccccccccccccccccccccccccccccccccccccccc;
}
void bar(int aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,
int bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb,
int cccccccccccccccccccccccccccccccccccccccccccccccccc) {
(void)aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa;
(void)bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb;
(void)cccccccccccccccccccccccccccccccccccccccccccccccccc;
}
struct Foo_t {
int foo : 2;
int bar:2;
int baz :2;
int qux: 2;
};
void foo() {
switch (1) {
case 2:
{
break;
}
case 3: {
break;
}
}
}
class Foo
{
public:
};
class Bar {
public:
};
void foo() {
if (true)
{
return;
}
if (true) {
return;
}
if (true &&
true)
{
return;
}
if (true &&
true) {
return;
}
}
enum Foo_t
{
A,
B
};
enum Bar_t {
C,
D
};
extern "C"
{
int foo();
}
extern "C" {
int bar();
}
void foo()
{
return;
}
void bar() {
return;
}
namespace
{
__attribute__ ((unused))int foo;
}
namespace {
__attribute__ ((unused))int bar;
}
struct Foo_t
{
int bar;
};
struct Baz_t {
int qux;
};
union Foo_t
{
int bar;
};
union Baz_t {
int qux;
};
void foo() {}
void bar() {
try {
foo();
}
catch (int e) {
(void)e;
}
try {
foo();
} catch (int e) {
(void)e;
}
}
void foo() {
if (true) {
return;
}
else {
return;
}
if (true) {
return;
} else {
return;
}
}
void foo(void (*)()) {}
void bar() {
foo([]()
{
return;
});
foo([]() {
return;
});
}
void foo() {
do {
continue;
}
while (true);
do {
continue;
} while (true);
}
void foo()
{}
void bar()
{}
void foo() {
}
void bar() {}
void baz()
{
}
void qux() {}
namespace {
}
namespace
{
}
namespace {}
namespace
{}
class Foo {
};
class Bar
{
};
class Baz {};
class Qux
{};
int aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa = 42;
int bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa;
int ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc =
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa;
int ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd =
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb;
int eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee =
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+ bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb;
int fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
= aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+ bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb;
extern "C"
{
void foo();
}
void foo()
{
switch (1)
{
case 2:
{
break;
}
}
if (true)
{
return;
}
do {
continue;
} while (true);
}
void bar()
{
try
{
foo();
}
catch (int e)
{
(void)e;
}
try
{
foo();
}
catch (int e)
{
(void)e;
}
}
class Foo
{
public:
};
enum Foo_t
{
A,
B
};
namespace
{
const int foo = 42;
}
struct Bar_t
{
int foo;
};
union Baz_t
{
int foo;
};
extern "C" {
void foo();
}
void foo() {
switch (1) {
case 2: {
break;
}
}
if (true) {
return;
}
do {
continue;
} while (true);
}
void bar() {
try {
foo();
} catch (int e) {
(void)e;
}
try {
foo();
} catch (int e) {
(void)e;
}
}
class Foo {
public:
};
enum Foo_t {
A,
B
};
namespace {
const int foo = 42;
}
struct Bar_t {
int foo;
};
union Baz_t {
int foo;
};
extern "C"
{
void foo();
}
void foo()
{
switch (1)
{
case 2:
{
break;
}
}
if (true)
{
return;
}
do {
continue;
} while (true);
}
void bar()
{
try
{
foo();
}
catch (int e)
{
(void)e;
}
try
{
foo();
}
catch (int e)
{
(void)e;
}
}
class Foo
{
public:
};
enum Foo_t
{
A,
B
};
namespace
{
const int foo = 42;
}
struct Bar_t
{
int foo;
};
union Baz_t {
int foo;
};
template<typename T>
concept foo = requires(T bar) {
(void)bar;
};
template<typename T> concept baz = requires(T bar) {
(void)bar;
};
void foo() {
bool aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa = true;
bool bar = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
? aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa;
bar = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ?
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa :
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa;
(void)bar;
}
class Foo {
Foo();
Foo(int);
Foo(int, int);
int bar;
int baz;
};
Foo::Foo()
: bar(42),
baz(42) {}
Foo::Foo(int qux)
: bar(qux)
, baz(42) {}
Foo::Foo(int qux, int pippo) :
bar(qux),
baz(pippo) {}
class Foo {};
class Bar {};
class Baz
: Foo,
Bar {};
class Qux
: Foo
, Bar {};
class Pippo :
Foo,
Bar {};
char foo[] = "veryVeryVeryVery"
"VeryVeryVeryVeryVeryVery"
"VeryVeryVeryVeryVeryVeryVery"
"VeryVeryVeryVeryVeryVeryVeryVery"
"VeryVeryVeryVeryVeryVeryVery"
"VeryVeryVeryVeryVeryVeryVeryVeryVeryLongString";
char bar[] = "veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongString";
// VeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLong veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLong veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLong veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLong veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLong veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLong veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLong
namespace foo { namespace bar {
}}
namespace baz {
namespace qux {
}
}
class Foo {
Foo();
Foo(int);
int aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa;
int bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb;
int ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc;
};
Foo::Foo()
: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(42), bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb(42), ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc(42) {}
Foo::Foo(int bar)
: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(bar),
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb(42),
ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc(42) {}
class Foo {
Foo();
Foo(int);
Foo(int, int);
int pippo;
int pluto;
int paperino;
};
class Bar {};
class Baz {};
class Qux
: Foo,
Bar,
Baz {};
Foo::Foo()
: pippo(42),
pluto(42),
paperino(42) {}
int foo =
42;
int foo[] = {42, 1234};
int bar[] = { 42, 1234 };
// this file intentionally has CRLF line endings
int* foo;
int* bar;
int * baz;
int *qux;
void pippo(int& foo) {
(void)foo;
}
void pluto(int & foo) {
(void)foo;
}
void paperino(int &foo) {
(void)foo;
}
class Foo {
public:
int foo;
};
class Bar {
public:
int foo;
private:
int bar;
};
class Baz {
public:
int foo;
};
class Foo {
public:
private:
int foo;
protected:
};
class Bar {
public:
private:
int foo;
protected:
};
class Baz {
public:
private:
int foo;
protected:
};
class Qux {
public:
private:
int foo;
protected:
};
namespace foo {
}
namespace bar {
} // namespace baz
#include "include/b.h"
#include <bb.h>
#include "include/a.h"
#include <aa.h>
#include "include/d.h"
#include <dd.h>
#include "include/c.h"
#include <cc.h>
class Foo {
int bar;
public:
int baz;
};
class Pippo {
int pluto;
public:
int paperino;
};
void foo() {
switch (1) {
case 2:
{
break;
}
case 3: {
break;
}
}
}
void foo() {
switch (1) {
case 2:
break;
case 3:
break;
}
}
extern "C" {
void foo();
void bar();
}
void foo() {
bar:
baz:
goto bar;
goto baz;
}
#if defined(FOO)
#define BAR
#elif defined(BAZ)
# define QUX
#else
#define PIPPO
#endif
template<typename T>
requires(true)
T foo() {}
template<typename T>
requires(true)
T bar() {}
void foo() {
int bar;
(void)bar;
int baz;
(void)baz;
}
void
foo() {}
void
bar() {}
int foo[] = {
42,
1234
};
int bar[] = {
42,
1234,
};
void foo() {
if (true) {
return;
}
if (true) {
return;
}
}
void foo(void (*)()) {}
void bar() {
foo(
[]() {
// baz
});
foo(
[]() {
// baz
});
}
int foo;
int bar;
namespace {
const int foo = 42;
const int bar = 42;
namespace baz {
const int qux = 42;
}
namespace pippo {
const int pluto = 42;
}
}
#if defined(FOO)
#define BAR
#elif defined(BAZ)
#define QUX
#else
#define PIPPO
#endif
class Foo {
Foo();
Foo(int);
Foo(int, int);
Foo(int, int, int);
Foo(int, int, int, int);
int a;
int b;
int c;
int dddddddddddddddddddddddddddddddddddddddddddddddddd;
int eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee;
int ffffffffffffffffffffffffffffffffffffffffffffffffff;
int d;
int e;
int f;
int ggggggggggggggggggggggggg;
int hhhhhhhhhhhhhhhhhhhhhhhhh;
int iiiiiiiiiiiiiiiiiiiiiiiii;
};
Foo::Foo()
: a(42),
b(1234),
c(11) {}
Foo::Foo(int bar)
: dddddddddddddddddddddddddddddddddddddddddddddddddd(bar), eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee(1234),
ffffffffffffffffffffffffffffffffffffffffffffffffff(11) {}
Foo::Foo(int bar, int baz) : a(bar), b(baz), c(11) {}
Foo::Foo(int bar, int baz, int qux)
: dddddddddddddddddddddddddddddddddddddddddddddddddd(bar),
eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee(baz),
ffffffffffffffffffffffffffffffffffffffffffffffffff(qux) {}
Foo::Foo(int bar, int baz, int qux, int pippo)
: ggggggggggggggggggggggggg(bar), hhhhhhhhhhhhhhhhhhhhhhhhh(baz), iiiiiiiiiiiiiiiiiiiiiiiii(qux) {
(void)pippo;
}
int* foo;
int *bar;
int * baz;
void pippo(int& foo) {
(void)foo;
}
void pluto(int &foo) {
(void)foo;
}
void paperino(int & foo) {
(void)foo;
}
const int foo = 42;
int const bar = 42;
void pippo(int& foo) {
(void)foo;
}
void pluto(int &foo) {
(void)foo;
}
void paperino(int & foo) {
(void)foo;
}
// foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo
// foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo
// foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo
/* bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar
* bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar
* bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar */
// foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo
/* bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar */
void foo() {
if (true)
return;
if (true) {
return;
}
}
int foo;
void bar() {}
void baz() {}
void qux() {}
void pippo() {}
void pluto() {}
void paperino() {}
namespace {
}
namespace {
__attribute__((unused)) int foo;
}
namespace {
__attribute__((unused)) int bar;
__attribute__((unused)) int baz;
}
namespace {
__attribute__((unused)) int pippo;
__attribute__((unused)) int pluto;
__attribute__((unused)) int paperino;
}
#include "include/b.h"
#include "include/a.h"
#include <bb.h>
#include <aa.h>
namespace a {
int aa;
int ab;
}
namespace b {
int ba;
int bb;
}
using b::bb;
using b::ba;
using a::ab;
using a::aa;
int foo = (int) 42;
int bar = (int)42;
bool foo() {
return ! true;
return !true;
}
template <typename T> T foo() {}
template<typename T> T bar() {}
void* const* foo = nullptr;
void* const * bar = nullptr;
void foo() {
int bar = 42;
bar += 1;
int baz= 42;
baz+= 1;
}
void foo() {
switch (1) {
case 2 : break;
case 3: break;
}
}
int foo {};
int bar{};
class Foo {
Foo();
Foo(int);
int bar;
};
Foo::Foo() : bar(42) {}
Foo::Foo(int baz): bar(baz) {}
class Foo {};
class Bar : Foo {};
class Baz: Foo {};
void foo() {
if(true) {
foo();
}
}
void bar () {
if (true) {
foo ();
}
}
void foo() {
if (true) {}
if(true) {}
}
void bar (int);
void baz(int);
void bar (int qux) {
(void)qux;
}
void baz(int qux) {
(void)qux;
}
#if (true)
#endif
#if(true)
#endif
class Foo {
public:
Foo(){};
Foo(int baz)
: bar(baz) {}
int bar;
Foo operator++ (int qux) {
(void)qux;
return Foo(42);
}
Foo operator--(int qux) {
(void)qux;
return Foo(42);
}
};
void pippo() {}
void pluto () {}
void foo() {
for (char bar : "baz") {
(void) bar;
}
for (char bar: "baz") {
(void) bar;
}
}
int foo [42];
int bar[42];
void foo() { }
void bar() {}
void foo( ) {
foo( );
}
void bar() {
bar();
}
//foo
// bar
// baz
template<int foo> void bar() {}
void baz() {
bar< 42 >();
bar<42>();
}
int foo = ( int )42;
int bar = (int)42;
void foo() {
if ( true ) {}
if (true) {}
}
//foo
// bar
// baz
void foo( int bar ) {
(void)bar;
}
void baz(int qux) {
foo( qux );
foo(qux);
}
int foo[ 42 ];
int bar[42];
#include <set>
#include <vector>
std::vector<std::set<int> > foo;
std::vector<std::set<int>> bar;
// This test data must have multiple indentation levels in order to provide sufficient coverage of the unintuitive
// interaction between IndentWidth and TabWidth. See the TabWidth option notes for details.
void foo() {
int bar =
42;
(void)bar;
{
int baz =
42;
(void)baz;
{
int qux =
42;
(void)qux;
}
}
}
// this file intentionally has CRLF line endings
// This test data must have multiple indentation levels in order to provide sufficient coverage of how UseTab is
// affected by the unintuitive interaction between IndentWidth and TabWidth. See the TabWidth option notes for details.
int a; // foo
int bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb; // foo
void bar() {
int baz =
42;
(void)baz;
{
int qux =
42;
(void)qux;
{
int pippo =
42;
(void)pippo;
}
}
} |
I did a crude test of this by just checking for a diff of the formatted output of the sketch above when the was IDE started from each and there was no diff. However, it is possible that there could be differences in the interpretation of the The better way to verify this would be to do a diff on the output produced by the IDE running the command:
I did a bit of fumbling around with the The caret escaping used for Windows in this PR is cmd-specific. PowerShell uses |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I verified it on Windows starting the IDE2 from CMD.EXE
, Git Bash
, and PowerShell
, and on macOS. The instructions were great. Excellent work, Per 👍
The Arduino IDE's "Auto Format" feature is configured to produce the standard Arduino sketch formatting style, as established by the Arduino IDE 1.x formatter.
The configuration is consumed by several other projects which require the configuration in a YAML file. In order to provide all the consumers with a single canonical source and to locate the infrastructure and activity related to the maintenance of the file in a more appropriate repository, it is now hosted in a permanent location in the
arduino/tooling-project-assets
repository:https://github.com/arduino/tooling-project-assets/tree/main/other/clang-format-configuration
The following changes have been made to the source configuration:
This last item did result in some functional changes to the configuration which will result in minor differences in the formatter output.
These are actually reversions of unwanted differences from the Arduino IDE 1.x formatter output, which were unavoidable when using the 11.0.1 version of ClangFormat in use at the time of the configuration's creation. These changes will provide greater consistency during the migration from Arduino IDE 1.x to 2.x. The default output of the Arduino IDE 1.x formatter will continue to be considered the "gold standard" until Arduino IDE 2.x graduates from "pre-release" status.
The Arduino IDE 2.x formatter configuration is fully customizable according to the preferences of each user:
https://docs.arduino.cc/software/ide-v2/tutorials/ide-v2-customize-auto-formatter
Those already using custom configurations will not be affected in any way (though they are encouraged to sync their configuration files from the source to bring them into compliance with the configuration API of the ClangFormat version currently in use by Arduino IDE 2.x).
See the documentation and commit history for the source file for details on the configuration changes:
https://github.com/arduino/tooling-project-assets/tree/main/other/clang-format-configuration
Resolves #42
Required updates to configuration escaping code
The formatter configuration is passed to the ClangFormat tool as a string representation of a JSON object via a
--style=<configuration>
command line argument. Since it passes through the command line, this argument must be correctly quoted and escaped.I discovered that, although it happened to work for the content of the previous configuration, the code that processed the argument string was insufficient to handle any arbitrary valid configuration content, and produced an invalid command from the updated configuration content.
This more minimal configuration effectively demonstrates the problem:
(This causes
#include
directives matching the regular expression^(<|")ba(r|z)
to be sorted after others)POSIX escaping
Quotes in the argument content must be backslash escaped in order to cause them to be treated as normal characters by POSIX shell command interpreters. The previous escaping code used by the Arduino IDE assumed that all quotes were from the JSON syntax. We see from the example configuration above that this is not a safe assumption. The configuration content itself might contain quote characters. With the previous escaping code, these would be converted to
\\"
, whereas the correct escaping would look like\\\"
.The previous escaping would produce the following command and error when ran on Linux and macOS:
A small modification to the
replace
method arguments provides the correct handling of quotes and any other escaped characters in the configuration content, producing this command:Escaping for Windows command interpreter
The command produced at the end of the previous section is perfectly valid for use with the POSIX shell command interpreters of Linux or macOS. Unfortunately this is not the case for the Windows command interpreter:
|
is a special character in the Windows command interpreter. This is not a problem when it is quoted. At first glance, it would appear that it is quoted in this command, but it is not. The reason is that the Windows command interpreter does not use the POSIX style backslash escaping. For this reason, backslash escaped quotes (\"
) in the argument are recognized as normal quotes. This means that, as far as the Windows command interpreter is concerned, the argument is alternating between quoted and unquoted states at random:(Green highlighted characters are quoted, yellow highlighted characters are unquoted)
If any characters with special significance to the Windows command interpreter (e.g.,
<
,>
,^
,|
) happen to occur in the unquoted segments, the command produces an error.The caret character (
^
) is used for escaping by the Windows command interpreter. A naive approach would be to simply caret escape all special characters in the argument, producing this command:However, these added carets are only normal characters in the quoted segments. The problem can be seen in the effective configuration dumped by
ClangFormat
:(Note that an unexpected
^
was introduced to the regular expression)So the caret escaping code must only treat the unquoted segments, which occur after every other quote in the string. This finally produces a valid command for use on Windows machines:
POSIX shell command interpreters don't know anything about Microsoft's caret escaping nonsense, so this must only be applied when running the formatting command on a Windows machine.
Reviewer checklist