From b7e69eff062736b6ba4f669f79a5ed844ab836df Mon Sep 17 00:00:00 2001 From: per1234 Date: Mon, 1 Aug 2022 14:59:36 -0700 Subject: [PATCH 1/3] Correctly escape escaped content in formatter configuration 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. --- arduino-ide-extension/src/node/clang-formatter.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/arduino-ide-extension/src/node/clang-formatter.ts b/arduino-ide-extension/src/node/clang-formatter.ts index b34b034ac..d7bdeb9e2 100644 --- a/arduino-ide-extension/src/node/clang-formatter.ts +++ b/arduino-ide-extension/src/node/clang-formatter.ts @@ -123,7 +123,10 @@ function toClangOptions( // See: https://releases.llvm.org/11.0.1/tools/clang/docs/ClangFormatStyleOptions.html export function style({ TabWidth, UseTab }: ClangFormatOptions): string { - return JSON.stringify(styleJson({ TabWidth, UseTab })).replace(/\"/g, '\\"'); + return JSON.stringify(styleJson({ TabWidth, UseTab })).replace( + /[\\"]/g, + '\\$&' + ); } function styleJson({ From bc3ab7f22ed9228520b670f2c7635baa6051a1a0 Mon Sep 17 00:00:00 2001 From: per1234 Date: Tue, 2 Aug 2022 21:42:20 -0700 Subject: [PATCH 2/3] Escape special characters in formatter configuration for Windows 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 --- .../src/node/clang-formatter.ts | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/arduino-ide-extension/src/node/clang-formatter.ts b/arduino-ide-extension/src/node/clang-formatter.ts index d7bdeb9e2..48375836c 100644 --- a/arduino-ide-extension/src/node/clang-formatter.ts +++ b/arduino-ide-extension/src/node/clang-formatter.ts @@ -1,3 +1,4 @@ +import * as os from 'os'; import { EnvVariablesServer } from '@theia/core/lib/common/env-variables'; import { MaybePromise } from '@theia/core/lib/common/types'; import { FileUri } from '@theia/core/lib/node/file-uri'; @@ -123,10 +124,23 @@ function toClangOptions( // See: https://releases.llvm.org/11.0.1/tools/clang/docs/ClangFormatStyleOptions.html export function style({ TabWidth, UseTab }: ClangFormatOptions): string { - return JSON.stringify(styleJson({ TabWidth, UseTab })).replace( + let styleArgument = JSON.stringify(styleJson({ TabWidth, UseTab })).replace( /[\\"]/g, '\\$&' ); + if (os.platform() === 'win32') { + // Windows command interpreter does not use backslash escapes. This causes the argument to have alternate quoted and + // unquoted sections. + // Special characters in the unquoted sections must be caret escaped. + const styleArgumentSplit = styleArgument.split('"'); + for (let i = 1; i < styleArgumentSplit.length; i += 2) { + styleArgumentSplit[i] = styleArgumentSplit[i].replace(/[<>^|]/g, '^$&'); + } + + styleArgument = styleArgumentSplit.join('"'); + } + + return styleArgument; } function styleJson({ From 32bf182aef71ab98bcb0ef36a8118f5e4177e55a Mon Sep 17 00:00:00 2001 From: per1234 Date: Mon, 1 Aug 2022 07:03:06 -0700 Subject: [PATCH 3/3] Sync sketch formatter configuration from source 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 --- .../src/node/clang-formatter.ts | 127 ++++++++++++------ 1 file changed, 83 insertions(+), 44 deletions(-) diff --git a/arduino-ide-extension/src/node/clang-formatter.ts b/arduino-ide-extension/src/node/clang-formatter.ts index 48375836c..2431d96d6 100644 --- a/arduino-ide-extension/src/node/clang-formatter.ts +++ b/arduino-ide-extension/src/node/clang-formatter.ts @@ -122,7 +122,6 @@ function toClangOptions( return { UseTab: 'Never', TabWidth: 2 }; } -// See: https://releases.llvm.org/11.0.1/tools/clang/docs/ClangFormatStyleOptions.html export function style({ TabWidth, UseTab }: ClangFormatOptions): string { let styleArgument = JSON.stringify(styleJson({ TabWidth, UseTab })).replace( /[\\"]/g, @@ -147,16 +146,15 @@ function styleJson({ TabWidth, UseTab, }: ClangFormatOptions): Record { + // Source: https://github.com/arduino/tooling-project-assets/tree/main/other/clang-format-configuration return { - Language: 'Cpp', - // # LLVM is the default style setting, used when a configuration option is not set here - BasedOnStyle: 'LLVM', AccessModifierOffset: -2, AlignAfterOpenBracket: 'Align', - AlignConsecutiveAssignments: false, - AlignConsecutiveBitFields: false, - AlignConsecutiveDeclarations: false, - AlignConsecutiveMacros: false, + AlignArrayOfStructures: 'None', + AlignConsecutiveAssignments: 'None', + AlignConsecutiveBitFields: 'None', + AlignConsecutiveDeclarations: 'None', + AlignConsecutiveMacros: 'None', AlignEscapedNewlines: 'DontAlign', AlignOperands: 'Align', AlignTrailingComments: true, @@ -167,16 +165,18 @@ function styleJson({ AllowShortCaseLabelsOnASingleLine: true, AllowShortEnumsOnASingleLine: true, AllowShortFunctionsOnASingleLine: 'Empty', - AllowShortIfStatementsOnASingleLine: 'Always', + AllowShortIfStatementsOnASingleLine: 'AllIfsAndElse', AllowShortLambdasOnASingleLine: 'Empty', AllowShortLoopsOnASingleLine: true, AlwaysBreakAfterDefinitionReturnType: 'None', AlwaysBreakAfterReturnType: 'None', AlwaysBreakBeforeMultilineStrings: false, AlwaysBreakTemplateDeclarations: 'No', + AttributeMacros: ['__capability'], + BasedOnStyle: 'LLVM', BinPackArguments: true, BinPackParameters: true, - // # Only used when "BreakBeforeBraces" set to "Custom" + BitFieldColonSpacing: 'Both', BraceWrapping: { AfterCaseLabel: false, AfterClass: false, @@ -184,7 +184,7 @@ function styleJson({ AfterEnum: false, AfterFunction: false, AfterNamespace: false, - // #AfterObjCDeclaration: + AfterObjCDeclaration: false, AfterStruct: false, AfterUnion: false, AfterExternBlock: false, @@ -193,104 +193,143 @@ function styleJson({ BeforeLambdaBody: false, BeforeWhile: false, IndentBraces: false, - SplitEmptyFunction: false, - SplitEmptyRecord: false, - SplitEmptyNamespace: false, + SplitEmptyFunction: true, + SplitEmptyRecord: true, + SplitEmptyNamespace: true, }, - // # Java-specific - // #BreakAfterJavaFieldAnnotations: + BreakAfterJavaFieldAnnotations: false, BreakBeforeBinaryOperators: 'NonAssignment', BreakBeforeBraces: 'Attach', + BreakBeforeConceptDeclarations: false, + BreakBeforeInheritanceComma: false, BreakBeforeTernaryOperators: true, BreakConstructorInitializers: 'BeforeColon', + BreakConstructorInitializersBeforeComma: false, BreakInheritanceList: 'BeforeColon', BreakStringLiterals: false, ColumnLimit: 0, - // # "" matches none CommentPragmas: '', CompactNamespaces: false, - ConstructorInitializerAllOnOneLineOrOnePerLine: true, + ConstructorInitializerAllOnOneLineOrOnePerLine: false, ConstructorInitializerIndentWidth: 2, ContinuationIndentWidth: 2, Cpp11BracedListStyle: false, DeriveLineEnding: true, DerivePointerAlignment: true, DisableFormat: false, - // # Docs say "Do not use this in config files". The default (LLVM 11.0.1) is "false". - // #ExperimentalAutoDetectBinPacking: + EmptyLineAfterAccessModifier: 'Leave', + EmptyLineBeforeAccessModifier: 'Leave', + ExperimentalAutoDetectBinPacking: false, FixNamespaceComments: false, - ForEachMacros: [], + ForEachMacros: ['foreach', 'Q_FOREACH', 'BOOST_FOREACH'], + IfMacros: ['KJ_IF_MAYBE'], IncludeBlocks: 'Preserve', - IncludeCategories: [], - // # "" matches none + 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', - // # Java-specific - // #JavaImportGroups: - // # JavaScript-specific - // #JavaScriptQuotes: - // #JavaScriptWrapImports + JavaScriptQuotes: 'Leave', + JavaScriptWrapImports: true, KeepEmptyLinesAtTheStartOfBlocks: true, + LambdaBodyIndentation: 'Signature', + Language: 'Cpp', MacroBlockBegin: '', MacroBlockEnd: '', - // # Set to a large number to effectively disable MaxEmptyLinesToKeep: 100000, NamespaceIndentation: 'None', - NamespaceMacros: [], - // # Objective C-specific - // #ObjCBinPackProtocolList: - // #ObjCBlockIndentWidth: - // #ObjCBreakBeforeNestedBlockParam: - // #ObjCSpaceAfterProperty: - // #ObjCSpaceBeforeProtocolList + 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, - // # Used as a fallback if alignment style can't be detected from code (DerivePointerAlignment: true) PointerAlignment: 'Right', - RawStringFormats: [], + QualifierAlignment: 'Leave', + ReferenceAlignment: 'Pointer', ReflowComments: false, - SortIncludes: 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: false, + SpacesInAngles: 'Leave', SpacesInCStyleCastParentheses: false, SpacesInConditionalStatement: false, SpacesInContainerLiterals: false, + SpacesInLineCommentPrefix: { Minimum: 0, Maximum: -1 }, SpacesInParentheses: false, SpacesInSquareBrackets: false, Standard: 'Auto', - StatementMacros: [], + StatementAttributeLikeMacros: ['Q_EMIT'], + StatementMacros: ['Q_UNUSED', 'QT_REQUIRE_VERSION'], TabWidth, - TypenameMacros: [], - // # Default to LF if line endings can't be detected from the content (DeriveLineEnding). UseCRLF: false, UseTab, - WhitespaceSensitiveMacros: [], + WhitespaceSensitiveMacros: [ + 'STRINGIZE', + 'PP_STRINGIZE', + 'BOOST_PP_STRINGIZE', + 'NS_SWIFT_NAME', + 'CF_SWIFT_NAME', + ], }; }