IDE's Auto Format isn't wrapping lines

I'm using Arduino IDE 2.3.2 under Linux Mint. Its 'Auto Format' feature appears twice in the menus (under the Edit and Tools menus), but when I select it, it doesn't wrap lines. In fact, in some cases, it unwraps lines. So I'm wondering whether I have something set up incorrectly.

As a professional software engineer, I'm in the habit of using autoformatting tools, and embracing whatever standards they format to. I might not particularly like the standard, but it helps avoid pointless style arguments, and it means code changes in the repo are more likely substance than style, which makes comparing different versions of code more helpful. Still, I don't think many devs like scrolling horizontally to read code, so wrapping lines to some reasonable length seems a pretty basic autoformatting feature.

Here's an example. I've wrapped the arguments in the function declaration to 80 characters, and also the ternary expression in the last return statement. But some other lines are much longer:

unsigned long doSomethingUseful(MyMeaningfulStructName maryTheStruct, int foo,
                                int bar) {

  // I'll put the curly brace on a separate line for fun. Oh, and this comment is long.
  if (foo > bar)
  {
    return maryTheStruct.someLongThing;
  }

  while (unreasonablyLongCriterium && anotherUnreasonablyLongCriterium && yetAnotherUnreasonablyLongCriterium && stillAnotherUnreasonablyLongCriterium && byCrikeyHowManyCriteriaCanAWhileLoopHave) {
    Serial.println(F("Surely 197 characters is long enough to wrap it?!"));
  }

  return globalThingo > CRAZY_CONST ?
      maryTheStruct.anotherLongThing * bar - foo : somethingTheCatDraggedIn;
}

Then I pressed Ctrl+T, and the code got changed to this:

unsigned long doSomethingUseful(MyMeaningfulStructName maryTheStruct, int foo,
                                int bar) {

  // I'll put the curly brace on a separate line for fun. Oh, and this comment is long.
  if (foo > bar) {
    return maryTheStruct.someLongThing;
  }

  while (unreasonablyLongCriterium && anotherUnreasonablyLongCriterium && yetAnotherUnreasonablyLongCriterium && stillAnotherUnreasonablyLongCriterium && byCrikeyHowManyCriteriaCanAWhileLoopHave) {
    Serial.println(F("Surely 197 characters is long enough to wrap it?!"));
  }

  return globalThingo > CRAZY_CONST ? maryTheStruct.anotherLongThing * bar - foo : somethingTheCatDraggedIn;
}

It's left the function declaration alone and moved the curly brace, but it's made the return statement a single long line, and it hasn't wrapped anything.

I've seen a lot of posts whingeing about the 80-character line length, so it seems line wrapping does happen for some people, but perhaps only while they're typing it. (I do my coding in a separate editor, and I've been using the IDE just for compiling and autoformatting.)

There don't seem to be a lot of settings in the IDE, but is there one for wrapping or line length that I'm missing?

Thanks,
Mik.

Hi @nomadMik. You can learn how to configure the Arduino IDE 2.x Auto Format feature from this tutorial:

https://docs.arduino.cc/software/ide-v2/tutorials/ide-v2-customize-auto-formatter

The ColumnLimit configuration key is used to configure the wrapping. If you want to set it to 80, add this configuration (the official Arduino configuration, with the single change of setting ColumnLimit to 80 instead of to 0) to a file named .clang-format under the ~/.arduinoIDE folder:

# 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: 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
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

Although I haven't played much with it, you can plat with the .clang-format settings using http://clang-format-configurator.site/

That brought up what looks like a list of IDE functions, and shows some of the shortcuts. That's useful, but they're not settings; when I type 'line', I see dozens of functions like 'copy line down' and 'add line comment', which are all things I know how to do in vim. Is this what you expected? Thanks.

Thanks! It looks like it just uses the clang formatter, which I've used before, so that's helpful.

But my question was really about coding standards. Is it intended for the Arduino IDE to ship with it set so functions have unlimited line lengths? I'm happy to run with whatever, but it seems a strange standard—I've never heard a sighted software engineer say they don't mind scrolling horizontally.

Cheers,
Mik.

Yeah, you can configure the IDE to wrap the lines as displayed in the editor. This doesn't actually affect the contents of the file as the Auto Format feature does, only how it is displayed.

You can cycle through the editor wrapping settings by pressing the Alt+Z keyboard shortcut (note that there are multiple settings rather than a simple enable/disable toggle.). But since you say you are using a separate editor, this feature probably wouldn't be interesting to you (the editor you are using likely has an equivalent setting).

That is correct. It is currently using ClangFormat version 14.0.0. You can use the provided .clang-format configuration file when working with ClangFormat directly from the command line and you'll get the same formatting results.

Essentially, yes.

During the initial planning for the complete rewrite of Arduino IDE for the 2.x version series, the decision was made to use a new code formatting tool. The reason for the decision was that the Artistic Style formatter tool used by Arduino IDE 1.x was no longer maintained. The open source LLVM project of which ClangFormat is a part is very well maintained and popular so it seemed like a good tool. The goal was for the default configuration of the Arduino IDE 2.x Auto Format feature to match as closely as possible with the output from the default configuration of the Arduino IDE 1.x Auto Format. The default Artistic Style configuration did not impose a line width limit, so neither does the ClangFormat configuration.

These are differences between the behavior of the Arduino IDE 1.x and 2.x Auto Format. ClangFormat has a very "opinionated" behavior. There is a lot of configuration options for the code style, but it is rarely possible to configure it to just leave the code however it happens to be styled already as Artistic Style allows. This meant that, despite great efforts being made to create a configuration that matched the 1.x behavior, there were some unavoidable differences.

In the case of the brace placement, the choice of which of the available ClangFormat configuration options to use (lacking a "don't care" option) was based on the predominant style, as determined by a comprehensive survey of official Arduino code.

In the case of unwrapping the line, you can break up the line even with the Arduino IDE 2.x default ClangFormat configuration by breaking before the operator instead of after as you did:

  return globalThingo > CRAZY_CONST
           ? maryTheStruct.anotherLongThing * bar - foo
           : somethingTheCatDraggedIn;

You'll note that the formatter now forces a break before the : operator as well, which seems reasonable.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.