3

For an Arduino LED animation project I have been working on an editor tool using HTML and javascript. After setting up various things, the tool enables generating C++ code to paste into an Arduino sketch.

It's all been working fine, the generated code compiles and runs on the board but there is an annoying 'auto-formatting' quirk in the Arduino IDE.

The code generator outputs a block of code like this:

const Scene scene_library[] PROGMEM = {
  { 7, 1, 40, 5, 320, 0xFF,
  {  { 0, ANIM_ROW_CHASE, 1, 3, 0, 0, 0, 0 },
  { 1, ANIM_ROW_CHASE, 2, 3, 0, 0, 0, 0 },
  { 2, ANIM_ROW_CHASE, 3, 3, 0, 0, 0, 0 },
  { 3, ANIM_ROW_CHASE, 4, 3, 0, 0, 0, 0 },
  { 4, ANIM_ROW_CHASE, 5, 3, 0, 0, 0, 0 },
  { 5, ANIM_ROW_CHASE, 6, 3, 0, 0, 0, 0 },
  { 6, ANIM_ROW_CHASE, 7, 3, 0, 0, 0, 0 } }, 7 }
};

But if I use the 'auto-formatting' feature (cmd-T on MacOS) it unwraps the data and puts it on a single line. It still compiles and runs but it means the page scrolls horizontally a ridiculous amount.

What causes this and what I can do this fix it?

const Scene scene_library[] PROGMEM = {
  { 7, 1, 40, 5, 320, 0xFF, { { 0, ANIM_ROW_CHASE, 1, 3, 0, 0, 0, 0 }, { 1, ANIM_ROW_CHASE, 2, 3, 0, 0, 0, 0 }, { 2, ANIM_ROW_CHASE, 3, 3, 0, 0, 0, 0 }, { 3, ANIM_ROW_CHASE, 4, 3, 0, 0, 0, 0 }, { 4, ANIM_ROW_CHASE, 5, 3, 0, 0, 0, 0 }, { 5, ANIM_ROW_CHASE, 6, 3, 0, 0, 0, 0 }, { 6, ANIM_ROW_CHASE, 7, 3, 0, 0, 0, 0 } }, 7 }
};

I generate other data blocks which format fine:

const AnimConfig ambient_configs[] PROGMEM = {
  { 7, ANIM_STATIC_ROW, 0, 40, 0, 0, 255, 0 },
  { 7, ANIM_STATIC_ROW, 0, 25, 0, 0, 255, 0 },
  { 7, ANIM_STATIC_ROW, 0, 15, 0, 0, 255, 0 },
  { 7, ANIM_STATIC_ROW, 0, 8, 0, 0, 255, 0 },
  { 7, ANIM_STATIC_ROW, 0, 5, 8, 0, 255, 0 }
};
New contributor
spring is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.
1
  • upvote ... this is a really good subject Commented yesterday

1 Answer 1

6

Fix

You can create a file named .clang-format in your sketch directory with the following content and the problem should go away. These are derived from IDE v2.3.6. For future versions it may be better to use original sources mentioned below.

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: Attach
BreakBeforeConceptDeclarations: false
BreakBeforeInheritanceComma: false
BreakBeforeTernaryOperators: true
BreakConstructorInitializers: BeforeColon
BreakConstructorInitializersBeforeComma: false
BreakInheritanceList: BeforeColon
BreakStringLiterals: false
ColumnLimit: 80 #<---------------
CommentPragmas: ''
CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ConstructorInitializerIndentWidth: 2
ContinuationIndentWidth: 2
Cpp11BracedListStyle: false
#BreakAfterOpenBracketBracedList: true
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: 2
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

The above is based options from the arduino-ide source repo. However, Edgar Bonet mentioned in a comment below:

Note that clang-format has decent default settings, which include ColumnLimit: 80. Thus, an empty style file is enough to avoid the overlong line. A style file with the single line PenaltyIndentedWhitespace: 1 would already give a decent formatting.

Cause

Bad default formatting options. Or at least ones not good for you.

I tend to just manually format things, so I'm not overly familiar with the formatting options. The active ingredient is this BreakAfterOpenBracketBracedList: true. Initially I adjusted ColumnLimit to at least clamp the code to the screen. I set that back to 0 (no limit) when BreakAfterOpenBracketBracedList seemed to do the job. I noticed that broke formatting altogether in the IDE doing this, so I've temporarily changed the configuration to use ColumnLimit: 80. Apparently I was testing outside the IDE as well with a newer version of clang-format. If I find a better option, I'll update to that.

You may want to play around with the other options to see if you can find a better way of going about it. Arduino IDE 2.3.6 bundles clang-format version 14.0.0.

You don't need to reload the IDE or anything. You can modify that file and hit CTRL+T in the IDE until it does what you want, or at least until it does something you can stand. There do seem to exist web based tools for generating/editing clang format configurations that will give you a preview.

Sources

In you're wondering, what I'd done is look at IDE source code and found this comment:

   * Calculates the `-style` flag for the formatter. Uses a `.clang-format` file if exists.
   * Otherwise, falls back to the default config.
   *
   * Style precedence:
   *  1. in the sketch folder,
   *  1. `~/.arduinoIDE/.clang-format`,
   *  1. `directories#data/.clang-format`, and
   *  1. default style flag as a string.

You can see below that in the case that no custom .clang-format file is found it produces a style based on this file modifying its TabWidth and UseTab setting with whatever were in the IDE preferences.

All I did was convert that file to yaml, and add the BreakAfterOpenBracketBracedListColumnLimit: 80 to it.

5
  • If you find a better configuration change before I update, you can put it in your own answer or let me know in a comment and I may incorporate it into this one. Commented yesterday
  • 3
    Good find! Note that clang-format has decent default settings, which include ColumnLimit: 80. Thus, an empty style file is enough to avoid the overlong line. A style file with the single line PenaltyIndentedWhitespace: 1 would already give a decent formatting. Commented yesterday
  • 2
    super useful answer ... upvote ... please add the filename in the first sentence of your answer Commented yesterday
  • @EdgarBonet I didn't even think to try an empty file. I haven't been back to try things, but I wanted to incorporate your information. Sorry if it looks a bit shoehorned into the answer, but I thought it would be better to get it in front of a reader sooner rather than later. Commented yesterday
  • @jsotola Good catch. I managed to leave the clang-format version number out also, somehow. Commented yesterday

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.