Clang setup; where is .clang-format

I did switch off my PC for the evening will see if I can find time tomorrow to find something. I barely use classes so it will be new territory for me.

By the way, it's not advisable to use pinMode in the constructor if that constructor is used at global scope.

I am aware of that. This little exercise was mostly theoretical, just messing around. Here is the full sketch.

/* An exercise in using classes and operator overloading
 * September 2022
 */

// General class to simplify the syntax to write to a pin
class PinOut {
private:
  uint8_t m_pin;

public:
  // Single constructor for PinOut
  PinOut(uint8_t pin): m_pin(pin) {
    pinMode(pin, OUTPUT);
  }

  // Assignment operators are normally expected to return by reference
  // allowing, for example, something like a = b = c
  PinOut& operator=(uint8_t state) {
    digitalWrite(m_pin, state ? HIGH : LOW);
    return *this;
  }
};

// Example usage: create "PinOut" objects
PinOut pin1(LED_BUILTIN);  // Digital pin 13
PinOut pin2(12);           // second pin with different timing

enum PinStatus : uint8_t { off, on };

void setup() {
  // put your setup code here, to run once:
}

void loop() {
  // put your main code here, to run repeatedly:
  static uint32_t prev1secMillis = 0;
  static uint32_t prev10msMillis = 0;
  static uint8_t pin1Status = off;
  static uint8_t pin2Status = off;

  if (millis() - prev1secMillis >= 500) {
    pin1Status = (pin1Status == off) ? on : off;
    pin1 = pin1Status;
    prev1secMillis += 500;
  }
  if (millis() - prev10msMillis >= 100) {
    pin2Status = (pin2Status == off) ? on : off;
    pin2 = pin2Status;
    prev10msMillis += 100;
  }
}

It was partly learning about operator overload, in this case =. For example, why the *this pointer return when overloading =? Keeps the mind occupied.

By the way, the operator overload part is not mangled by auto format.

Would appreciate your input a for possible solution to the post #20.

Indeed. Thanks for the investigation and report! I can reproduce it and also identified the general cause. I submitted a formal bug report to the developers on your behalf here:

@ptillisch thanks for reporting.

I did some experimenting and didn't find anything. It seems they give some options about how it should be broken, but not about whether it should be broken.

I believe this feature request would provide the style you want:

I didn't manage to find any option that even affects this so I'm stumped.

But don't you think this style is more readable?:

enum PinStatus : uint8_t {
  off,
  on
};

@ptillisch thanks for investigating.

IDE 1 does not change the basic format with auto format, I like the way it does it. IDE 2 takes a much more "hard core" approach "enforcing" what is somewhat based on recommended formats. I do not entirely agree with that, individuals (at least us older ones) should have a lot more say in what they prefer. Just look at the longtime programmer's request here.

I started programming in Fortran in the early 60's, the other option was Cobol but that was for the commerce boys. It was on the varsity IBM 1620 model 1, later on the model 2 and eventually the 360. We entered code by punching cards, stacks of them. The puncher used a control card which basically hard coded the program format which needed to be perfect or the compiler crashed.

I went through basic (when the original IBM PC became available) to Borland Turbo Pascal, then C up to C+ (short lived) and eventually C++, with a healthy dose of assembler in between.

Very early on I learned the value of developing a programming style, even though early editors were rather crude, to enable reading your code fast and reliably. That style did change over time to suit compiler development. As time went by it did become more difficult to get used to style changes.

So the readability of style is pretty ingrained and, for me, the above is not more readable. Yes, I can change and adapt but would prefer not to at this stage.

With all that said, I hardly ever, if ever, use auto format for my own code. I enter it first time to follow my style (we will leave out programming errors here). I use it on code from other when collaborating on projects (the complete lack of any reasonable formatting by some is quite unbelievable - making an auto format option almost mandatory).

So, it would be nice to have the ability to change auto format in a way to match my style (hence this exercise) but is certainly not the end, I can live with things as they are.

This is purely a result of the change from the Artistic Style formatter tool to ClangFormat. I went to extraordinary lengths attempting to make the ClangFormat configuration produce an "Auto Format" behavior that matched Arduino IDE 1.x. However, in some cases this simply was not possible due to differences between the two tools. Artistic Style can be configured to simply ignore aspects of code style. ClangFormat generally does not allow such a thing, as discussed here:

We gave careful consideration to abandoning ClangFormat and switching back to Artistic Style once this situation was identified. However, Artistic Style is no longer being actively maintained. This has been the case for years and there is no sign that the situation will ever change. ClangFormat is very actively developed and maintained as part of the LLVM project and very popular. Arduino IDE 2.x is already using another LLVM component, clangd, as the C++ language server. So this seems like a much more "future proof" formatter tool. The best opportunity to make such a significant change was the 2.0.0 release.

The good news is this active status means that a deficiency encountered today might well be resolved tomorrow. I have seen some very significant advancements made between the ClangFormat 11.1.0 I started working with to the 14.0.0 currently in use by Arduino IDE 2.x. And that occurred over the course of only a year! 15.0.0 is now available and there are again some significant advancements in the tool. I found that these advancements increase the configurability of the tool, but also allow the formatting style produced by the previous version to be maintained.

Although it seems you have found some unfortunate exceptions, I found that ClangFormat typically allows a lot of control over the specific formatting style. In cases where Artistic Style was configured for a "leave alone" behavior and the same was not available from ClangFormat, this configurability allowed me to base the decision on the established Arduino code style, as determined by extensive surveys of the existing official code bases.

The perspective I have gained over the years is that the specific code style used is not really so important. What is important is using whatever code style consistently.

@ptillisch . Thanks for the reply and explanations.

I do accept that things change and progress (it was quite shock getting back to electronics after a long time away) and what we would like today will most likely be available in the not too distant future.

I was not at all aware of an even newer version 15.

Messing with the ClangFormat configuration file, I am sure it is the one reflecting your hard work (many thanks for that), and using the " CLANG-FORMAT STYLE OPTIONS" document I noticed things like:

**AllowAllConstructorInitializersOnNextLine** (`Boolean`) clang-format 9
This option is **deprecated**. See `NextLine` of `PackConstructorInitializers`.

The "PackConstructorInitializers" option does not appear in that configuration file but "AllowAllConstructorInitializersOnNextLine" does.

So as things change it becomes a task in itself to stay up to date. I will keep and eye on this. Problem is, that by the time the options allowing my original requirement become available I might have adopted the new way :face_with_raised_eyebrow:.

Thanks for all your effort and input.

I know all about this from experience. I ended up having to update the configuration file from the version developed for ClangFormat 11.1.0 all the way up to 14.0.0 in one go. It was a lot of work! The good news is that the ClangFormat developers do a good job at ensuring the changes are done in a backwards compatible manner. So we actually could have continued using the original configuration file with 14.0.0. But since this configuration file is likely to be used in many other projects I think it is important for it to be kept up to date with the configuration interface provided by the target ClangFormat version.

II set up an extensive automated validation system to facilitate the ongoing maintenance of Arduino's configuration file. This could also be reused by the community for developing and maintaining their custom configurations:

https://github.com/arduino/tooling-project-assets/tree/main/other/clang-format-configuration#readme

One of the validations is to run a clang-format --dump-config --style=file command to get the effective configuration, then diff that against the current configuration file contents. This will show any changes to the configuration file data structure resulting from an update to a new version of ClangFormat. You can see the diff for the 15.0.0 bump here:

https://github.com/arduino/tooling-project-assets/actions/runs/2999560739/jobs/4813411184#step:9:14

Unfortunately I don't see anything that looks relevant to the configuration questions you had.

The other significant validation is to format a huge collection of test data, then compare the output against "golden masters" that have the expected formatting. The diff shows any changes in the formatting style that would result from a ClangFormat update or a change to the configuration file. You can see the diff here:

https://github.com/arduino/tooling-project-assets/actions/runs/2999560739/jobs/4813411605#step:7:56

The fact there is a diff at all belies my claim that the updates are backwards compatible. However, it seems mostly to be an improved handling of the unfortunate use of CR EOL in the "CTC GO MOTIONS" library examples you can see here:

https://github.com/arduino-libraries/CTC-Go-Motions-Expansion/blob/master/examples/Projects/Wiggler/Wiggler_Stage5/Wiggler_Stage5.ino

The other diff is related to the template concepts and requires language features added in C++20. In this case, it is probably more a situation of ClangFormat never handling this case at all so there wasn't a previously established style to use as default. In the case of Arduino sketches, a change in this area is likely to have a minimal impact because the language features are not even available for most platforms (C++11 is still the prevalent standard) and probably not much used in Arduino sketches even when it is available.

I will of course evaluate these results much more carefully when the time comes for us to update to ClangFormat 15.0.0 and minimize any impacts.

Thanks for the follow up. Also thanks for all the careful research and effort you obviously put into this.

Hi All,

I have recently been using IDE 2.0 and have been liking it a lot but I don't no why my clang-format file is being ignored? I have set BreakBeforeBraces to Allman style but it just doesn't work no matter where I put it?

Where exactly is the .clang-format file located ?

I have put it in the sketch root folder as well as both user directories.
i.e. C:\Users<username>.arduinoIDE\ and C:\Users<username>\AppData\Local\Arduino15
I was used to using AStyle in both Arduino 1 and Atmel Studio 7 (Microchip Studio) but this is proving to be a difficulty.
Any help would be much appreciated.

Thanks,
Chris.

My IDE 2.0 .clag-format file, which I know works because I can make changes and see them work immediately when Ctrl-T is used in the IDE is in C:\Users\Bob2.arduinoIDE where Bob2 is my Windows username. Note the full stop before the folder name

My notes indicate that I copied the original from #https://docs.arduino.cc/software/ide-v2/tutorials/ide-v2-customize-auto-formatter

Are you missing a backslash in the path?

The backslash between the 2 and the full stop is "swallowed" by the forum software but using 2 backslashes fixes what it looks like

The path should, of course, be C:\Users\Bob2\.arduinoIDE

Is that the directory where the ino file also lives?

C:\Users\yourUsername\.arduinoIDE is the global one; the one in the sketch directory overrides the global one.

If you make mistakes in the file, it will silently ignore the file.

I realised that; just made the same mistake :frowning:

So I (briefly) saw !

I have it in all 3 locations but here is my clang file, if I've made a mistake somewhere please let me know.

# Source: https://github.com/arduino/tooling-project-assets/tree/main/other/clang-format-configuration
---
AccessModifierOffset: -2
AlignAfterOpenBracket: Align
AlignArrayOfStructures: None
AlignConsecutiveAssignments: None
AlignConsecutiveBitFields: None
AlignConsecutiveDeclarations: None
AlignConsecutiveMacros: None
AlignEscapedNewlines: DontAlign
AlignOperands: Align
AlignTrailingComments: true
AllowAllArgumentsOnNextLine: true
AllowAllConstructorInitializersOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: Always
AllowShortCaseLabelsOnASingleLine: true
AllowShortEnumsOnASingleLine: true
AllowShortFunctionsOnASingleLine: Empty
AllowShortIfStatementsOnASingleLine: AllIfsAndElse
AllowShortLambdasOnASingleLine: Empty
AllowShortLoopsOnASingleLine: true
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: No
AttributeMacros:
  - __capability
BasedOnStyle: LLVM
BinPackArguments: true
BinPackParameters: true
BitFieldColonSpacing: Both
BraceWrapping:
  AfterCaseLabel: false
  AfterClass: false
  AfterControlStatement: Never
  AfterEnum: false
  AfterFunction: false
  AfterNamespace: false
  AfterObjCDeclaration: false
  AfterStruct: false
  AfterUnion: false
  AfterExternBlock: false
  BeforeCatch: false
  BeforeElse: false
  BeforeLambdaBody: false
  BeforeWhile: false
  IndentBraces: false
  SplitEmptyFunction: true
  SplitEmptyRecord: true
  SplitEmptyNamespace: true
BreakAfterJavaFieldAnnotations: false
BreakBeforeBinaryOperators: NonAssignment
BreakBeforeBraces: Allman
BreakBeforeConceptDeclarations: false
BreakBeforeInheritanceComma: false
BreakBeforeTernaryOperators: true
BreakConstructorInitializers: BeforeColon
BreakConstructorInitializersBeforeComma: false
BreakInheritanceList: BeforeColon
BreakStringLiterals: false
ColumnLimit: 0
CommentPragmas: ''
CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ConstructorInitializerIndentWidth: 2
ContinuationIndentWidth: 2
Cpp11BracedListStyle: false
DeriveLineEnding: true
DerivePointerAlignment: true
DisableFormat: false
EmptyLineAfterAccessModifier: Leave
EmptyLineBeforeAccessModifier: Leave
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: false
ForEachMacros:
  - foreach
  - Q_FOREACH
  - BOOST_FOREACH
IfMacros:
  - KJ_IF_MAYBE
IncludeBlocks: Preserve
IncludeCategories:
  - Regex: '^"(llvm|llvm-c|clang|clang-c)/'
    Priority: 2
    SortPriority: 0
    CaseSensitive: false
  - Regex: '^(<|"(gtest|gmock|isl|json)/)'
    Priority: 3
    SortPriority: 0
    CaseSensitive: false
  - Regex: '.*'
    Priority: 1
    SortPriority: 0
    CaseSensitive: false
IncludeIsMainRegex: ''
IncludeIsMainSourceRegex: ''
IndentAccessModifiers: false
IndentCaseBlocks: true
IndentCaseLabels: true
IndentExternBlock: Indent
IndentGotoLabels: false
IndentPPDirectives: None
IndentRequires: true
IndentWidth: 4
IndentWrappedFunctionNames: false
InsertTrailingCommas: None
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: true
LambdaBodyIndentation: Signature
Language: Cpp
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 100000
NamespaceIndentation: None
ObjCBinPackProtocolList: Auto
ObjCBlockIndentWidth: 2
ObjCBreakBeforeNestedBlockParam: true
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PPIndentWidth: -1
PackConstructorInitializers: BinPack
PenaltyBreakAssignment: 1
PenaltyBreakBeforeFirstCallParameter: 1
PenaltyBreakComment: 1
PenaltyBreakFirstLessLess: 1
PenaltyBreakOpenParenthesis: 1
PenaltyBreakString: 1
PenaltyBreakTemplateDeclaration: 1
PenaltyExcessCharacter: 1
PenaltyIndentedWhitespace: 1
PenaltyReturnTypeOnItsOwnLine: 1
PointerAlignment: Right
QualifierAlignment: Leave
ReferenceAlignment: Pointer
ReflowComments: false
RemoveBracesLLVM: false
SeparateDefinitionBlocks: Leave
ShortNamespaceLines: 0
SortIncludes: Never
SortJavaStaticImport: Before
SortUsingDeclarations: false
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: false
SpaceAroundPointerQualifiers: Default
SpaceBeforeAssignmentOperators: true
SpaceBeforeCaseColon: false
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
SpaceBeforeParensOptions:
  AfterControlStatements: true
  AfterForeachMacros: true
  AfterFunctionDefinitionName: false
  AfterFunctionDeclarationName: false
  AfterIfMacros: true
  AfterOverloadedOperator: false
  BeforeNonEmptyParentheses: false
SpaceBeforeRangeBasedForLoopColon: true
SpaceBeforeSquareBrackets: false
SpaceInEmptyBlock: false
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 2
SpacesInAngles: Leave
SpacesInCStyleCastParentheses: false
SpacesInConditionalStatement: false
SpacesInContainerLiterals: false
SpacesInLineCommentPrefix:
  Minimum: 0
  Maximum: -1
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Auto
StatementAttributeLikeMacros:
  - Q_EMIT
StatementMacros:
  - Q_UNUSED
  - QT_REQUIRE_VERSION
TabWidth: 2
UseCRLF: false
UseTab: Never
WhitespaceSensitiveMacros:
  - STRINGIZE
  - PP_STRINGIZE
  - BOOST_PP_STRINGIZE
  - NS_SWIFT_NAME
  - CF_SWIFT_NAME

Thanks,
Chris.