如何用clang-format将预处理指令(#)与代码块对齐?
如何让clang-format对齐代码块内的预处理指令
我在用clang-format格式化C++代码,希望把带#的预处理指令和周围代码块对齐,但clang-format的默认规则让这些指令的缩进和函数、循环、条件语句不匹配。
当前格式化效果
#pragma region WIFI { #ifdef __CONFIG_WIFI__ void wifi_handler() { switch(wifi.connectionMode) { case WIFI_STA: { #ifdef __CONFIG_WEBSOCKET__ if(WiFi.status() == WL_CONNECTED && webSocketClient.isConnected()) webSocketClient.loop(); #endif break; } case WIFI_AP: { #ifdef __CONFIG_FTP__ ftpSrv.handleFTP(); #endif #ifdef __CONFIG_WEB__ webServer.handleClient(); #endif #ifdef __CONFIG_WEBSOCKET__ webSocketServer.loop(); #endif break; } } } #endif #pragma endregion }
期望效果
#pragma region WIFI { #ifdef __CONFIG_WIFI__ void wifi_handler() { switch(wifi.connectionMode) { case WIFI_STA: { #ifdef __CONFIG_WEBSOCKET__ if(WiFi.status() == WL_CONNECTED && webSocketClient.isConnected()) webSocketClient.loop(); #endif break; } case WIFI_AP: { #ifdef __CONFIG_FTP__ ftpSrv.handleFTP(); #endif #ifdef __CONFIG_WEB__ webServer.handleClient(); #endif #ifdef __CONFIG_WEBSOCKET__ webSocketServer.loop(); #endif break; } } } #endif #pragma endregion }
我的.clang-format配置
--- BasedOnStyle: LLVM AccessModifierOffset: -2 AlignAfterOpenBracket: Align AlignArrayOfStructures: Right AlignConsecutiveAssignments: Enabled: true AcrossEmptyLines: false AcrossComments: false AlignCompound: true PadOperators: true AlignConsecutiveBitFields: Enabled: true AcrossEmptyLines: false AcrossComments: false AlignCompound: true PadOperators: true AlignConsecutiveDeclarations: Enabled: true AcrossEmptyLines: false AcrossComments: false AlignCompound: true PadOperators: true AlignConsecutiveMacros: Enabled: true AcrossEmptyLines: false AcrossComments: false AlignCompound: true PadOperators: true AlignConsecutiveShortCaseStatements: Enabled: true AcrossEmptyLines: false AcrossComments: true AlignCaseColons: true AlignEscapedNewlines: Left AlignOperands: AlignAfterOperator AlignTrailingComments: Kind: Always OverEmptyLines: 0 AllowAllArgumentsOnNextLine: false AllowAllParametersOfDeclarationOnNextLine: false AllowShortBlocksOnASingleLine: Always AllowShortCaseLabelsOnASingleLine: false AllowShortEnumsOnASingleLine: true AllowShortFunctionsOnASingleLine: All AllowShortIfStatementsOnASingleLine: AllIfsAndElse AllowShortLambdasOnASingleLine: All AllowShortLoopsOnASingleLine: true AlwaysBreakAfterDefinitionReturnType: None AlwaysBreakAfterReturnType: None AlwaysBreakBeforeMultilineStrings: false AlwaysBreakTemplateDeclarations: MultiLine AttributeMacros: - __capability BinPackArguments: false BinPackParameters: false BitFieldColonSpacing: Both BraceWrapping: AfterCaseLabel: true AfterClass: true AfterControlStatement: Always AfterEnum: true AfterFunction: true AfterNamespace: true AfterObjCDeclaration: false AfterStruct: true AfterUnion: true AfterExternBlock: true BeforeCatch: true BeforeElse: true BeforeLambdaBody: true BeforeWhile: true IndentBraces: false SplitEmptyFunction: true SplitEmptyRecord: true SplitEmptyNamespace: true BreakAfterAttributes: Never BreakAfterJavaFieldAnnotations: false BreakArrays: true BreakBeforeBinaryOperators: None BreakBeforeBraces: Custom BreakBeforeConceptDeclarations: Allowed BreakBeforeInlineASMColon: OnlyMultiline BreakBeforeTernaryOperators: false BreakConstructorInitializers: AfterColon BreakInheritanceList: AfterComma BreakStringLiterals: false ColumnLimit: 180 CommentPragmas: "^ IWYU pragma:" CompactNamespaces: false ConstructorInitializerIndentWidth: 2 ContinuationIndentWidth: 2 Cpp11BracedListStyle: true DerivePointerAlignment: false DisableFormat: false EmptyLineAfterAccessModifier: Always EmptyLineBeforeAccessModifier: LogicalBlock ExperimentalAutoDetectBinPacking: false FixNamespaceComments: true 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: (Test)?$ IncludeIsMainSourceRegex: "" IndentAccessModifiers: false IndentCaseBlocks: false IndentCaseLabels: true IndentExternBlock: AfterExternBlock IndentGotoLabels: true IndentPPDirectives: BeforeHash IndentRequiresClause: true IndentWidth: 2 IndentWrappedFunctionNames: true InsertBraces: false InsertNewlineAtEOF: true InsertTrailingCommas: None IntegerLiteralSeparator: Binary: 0 BinaryMinDigits: 0 Decimal: 0 DecimalMinDigits: 0 Hex: 0 HexMinDigits: 0 JavaScriptQuotes: Leave JavaScriptWrapImports: true KeepEmptyLinesAtEOF: true KeepEmptyLinesAtTheStartOfBlocks: true LambdaBodyIndentation: Signature Language: Cpp LineEnding: DeriveLF MacroBlockBegin: "" MacroBlockEnd: "" MaxEmptyLinesToKeep: 1 NamespaceIndentation: Inner ObjCBinPackProtocolList: Auto ObjCBlockIndentWidth: 2 ObjCBreakBeforeNestedBlockParam: true ObjCSpaceAfterProperty: false ObjCSpaceBeforeProtocolList: true PPIndentWidth: -1 PackConstructorInitializers: NextLine PenaltyBreakAssignment: 2 PenaltyBreakBeforeFirstCallParameter: 19 PenaltyBreakComment: 300 PenaltyBreakFirstLessLess: 120 PenaltyBreakOpenParenthesis: 0 PenaltyBreakString: 1000 PenaltyBreakTemplateDeclaration: 10 PenaltyExcessCharacter: 1000000 PenaltyIndentedWhitespace: 0 PenaltyReturnTypeOnItsOwnLine: 60 PointerAlignment: Right QualifierAlignment: Leave ReferenceAlignment: Pointer ReflowComments: false RemoveBracesLLVM: false RemoveParentheses: Leave RemoveSemicolon: false RequiresClausePosition: SingleLine RequiresExpressionIndentation: OuterScope SeparateDefinitionBlocks: Leave ShortNamespaceLines: 1 SortIncludes: Never SortJavaStaticImport: Before SortUsingDeclarations: LexicographicNumeric SpaceAfterCStyleCast: false SpaceAfterLogicalNot: false SpaceAfterTemplateKeyword: true SpaceAroundPointerQualifiers: Default SpaceBeforeAssignmentOperators: true SpaceBeforeCaseColon: false SpaceBeforeCpp11BracedList: false SpaceBeforeCtorInitializerColon: true SpaceBeforeInheritanceColon: true SpaceBeforeJsonColon: false SpaceBeforeParens: Custom SpaceBeforeParensOptions: AfterControlStatements: false AfterForeachMacros: true AfterFunctionDeclarationName: false AfterFunctionDefinitionName: false AfterIfMacros: true AfterOverloadedOperator: false AfterRequiresInClause: true AfterRequiresInExpression: true BeforeNonEmptyParentheses: false SpaceBeforeRangeBasedForLoopColon: false SpaceBeforeSquareBrackets: false SpaceInEmptyBlock: false SpacesBeforeTrailingComments: 1 SpacesInAngles: Never SpacesInContainerLiterals: false SpacesInLineCommentPrefix: Minimum: 1 Maximum: -1 SpacesInParens: Never SpacesInParensOptions: InConditionalStatements: false InCStyleCasts: false InEmptyParentheses: false Other: false SpacesInSquareBrackets: false Standard: Latest StatementAttributeLikeMacros: - Q_EMIT StatementMacros: - Q_UNUSED - QT_REQUIRE_VERSION TabWidth: 8 UseTab: Never VerilogBreakBetweenInstancePorts: true WhitespaceSensitiveMacros: - BOOST_PP_STRINGIZE - CF_SWIFT_NAME - NS_SWIFT_NAME - PP_STRINGIZE - STRINGIZE BracedInitializerIndentWidth: 2
能不能通过clang-format配置实现?
不能。clang-format对预处理指令的缩进逻辑是独立的,没有配置项能让#ifdef/#endif这类指令自动跟随所在代码块的缩进级别。现有的IndentPPDirectives选项只有三个可选值:
None:预处理指令无缩进AfterHash:#号无缩进,指令内容缩进BeforeHash:#号前添加缩进,指令内容和#对齐
这些选项都无法让预处理指令和周围的代码块(比如case块内的代码)保持一致的缩进。
替代方案:用脚本调整缩进
可以用Python脚本遍历代码行,跟踪当前的缩进级别,然后调整预处理指令的缩进。以下是一个示例脚本:
import re def adjust_pp_directive_indent(file_path, indent_width=2): indent_level = 0 indent_char = ' ' * indent_width modified_lines = [] with open(file_path, 'r') as f: lines = f.readlines() for line in lines: stripped_line = line.strip() # 处理增加缩进的情况:遇到{且不是预处理指令的一部分 if stripped_line.endswith('{') and not stripped_line.startswith('#'): modified_lines.append(line) indent_level += 1 continue # 处理减少缩进的情况:遇到}且不是预处理指令的一部分 if stripped_line.startswith('}') and not stripped_line.startswith('#'): indent_level = max(0, indent_level - 1) modified_lines.append(line) continue # 处理预处理指令(跳过region相关pragma) if stripped_line.startswith('#') and not stripped_line.startswith('#pragma region') and not stripped_line.startswith('#pragma endregion'): # 去掉原有缩进 raw_pp_line = re.sub(r'^\s+', '', line) # 添加当前级别缩进 indented_pp_line = indent_char * indent_level + raw_pp_line modified_lines.append(indented_pp_line) continue # 其他行保持原样 modified_lines.append(line) with open(file_path, 'w') as f: f.writelines(modified_lines) # 使用示例:替换为你的代码文件路径 adjust_pp_directive_indent('your_code.cpp', indent_width=2)
脚本说明
- 通过代码中的
{和}跟踪当前缩进级别 - 跳过
#pragma region和#pragma endregion(可根据需求修改过滤规则) - 对目标预处理指令,清除原有缩进后添加当前级别对应的空格
- 其他代码行保持不变
使用步骤
- 先运行clang-format完成基础格式化
- 再运行该脚本调整预处理指令的缩进
这样就能让预处理指令和周围代码块的缩进对齐了。
内容的提问来源于stack exchange,提问作者Mahdi Ghasemi
相关产品推荐
相关产品推荐

