clang-format如何在超出列宽限制时自动拆分逻辑条件到新行
问题描述
我设置的代码行长度限制为120字符,通常代码仅在超出120字符时才会换行,但多数时候我希望将逻辑条件拆分到不同新行。
我有如下表达式:
if ((long_function_name_12345().get_some_status1() != status1) || (long_function_name_12345().get_some_status2() != status2) || force || some_other_logical_bool) { }
使用clang-format格式化后得到的结果不符合预期:
if ((long_function_name_12345().get_some_status1() != status1) || (long_function_name_12345().get_some_status2() != status2) || force || some_other_logical_bool) { }
我期望的行为是一旦代码超出列限制(本例为120)就在任意逻辑块处换行。目前我找到的强制换行方法是加注释标记:
if ((long_function_name_12345().get_some_status1() != status1) || (long_function_name_12345().get_some_status2() != status2) || // break force || // break some_other_logical_bool) { }
该方法不够优雅但确实可用,比用// clang-format off/on标签包裹代码要美观一些。
我使用的clang-format版本为10.0.0-4ubuntu1~18.04.2,当前.clang-format配置如下:
--- Language: Cpp # BasedOnStyle: WebKit AccessModifierOffset: -4 AlignAfterOpenBracket: AlwaysBreak AlignConsecutiveMacros: false AlignConsecutiveAssignments: false AlignConsecutiveDeclarations: false AlignEscapedNewlines: Right AlignOperands: false AlignTrailingComments: false AllowAllArgumentsOnNextLine: false AllowAllConstructorInitializersOnNextLine: false AllowAllParametersOfDeclarationOnNextLine: false AllowShortBlocksOnASingleLine: false AllowShortCaseLabelsOnASingleLine: false # Don't allow function on one line, e.g. 'int f() {return 1;}' - unless its inline AllowShortFunctionsOnASingleLine: InlineOnly AllowShortLambdasOnASingleLine: All AllowShortIfStatementsOnASingleLine: Never AllowShortLoopsOnASingleLine: false AlwaysBreakAfterDefinitionReturnType: None AlwaysBreakAfterReturnType: None AlwaysBreakBeforeMultilineStrings: false AlwaysBreakTemplateDeclarations: MultiLine BinPackArguments: false BinPackParameters: false BraceWrapping: AfterCaseLabel: true AfterClass: true AfterControlStatement: true AfterEnum: true AfterFunction: true AfterNamespace: true AfterObjCDeclaration: true AfterStruct: true AfterUnion: true AfterExternBlock: true BeforeCatch: true BeforeElse: true IndentBraces: true SplitEmptyFunction: true SplitEmptyRecord: true SplitEmptyNamespace: true # have line breaks after operators: 'if (a ||' instead of on the next line ' || b' BreakBeforeBinaryOperators: None BreakBeforeBraces: Allman BreakBeforeInheritanceComma: false BreakInheritanceList: BeforeColon BreakBeforeTernaryOperators: true BreakConstructorInitializersBeforeComma: false BreakConstructorInitializers: BeforeComma BreakAfterJavaFieldAnnotations: false BreakStringLiterals: true # effectively the line length limit before clang-format starts to use multiple lines ColumnLimit: 120 CommentPragmas: '^ IWYU pragma:' CompactNamespaces: false ConstructorInitializerAllOnOneLineOrOnePerLine: false ConstructorInitializerIndentWidth: 4 ContinuationIndentWidth: 4 # format braces lists (uniform init) like a function param list Cpp11BracedListStyle: true DeriveLineEnding: true DerivePointerAlignment: false DisableFormat: false ExperimentalAutoDetectBinPacking: false FixNamespaceComments: false ForEachMacros: - foreach - Q_FOREACH - BOOST_FOREACH IncludeBlocks: Preserve IncludeCategories: - Regex: '^"(llvm|llvm-c|clang|clang-c)/' Priority: 2 SortPriority: 0 - Regex: '^(<|"(gtest|gmock|isl|json)/)' Priority: 3 SortPriority: 0 - Regex: '.*' Priority: 1 SortPriority: 0 IncludeIsMainRegex: '(Test)?$' IncludeIsMainSourceRegex: '' IndentCaseLabels: true IndentGotoLabels: true IndentPPDirectives: None IndentWidth: 4 IndentWrappedFunctionNames: false JavaScriptQuotes: Leave JavaScriptWrapImports: true KeepEmptyLinesAtTheStartOfBlocks: true MacroBlockBegin: '' MacroBlockEnd: '' MaxEmptyLinesToKeep: 1 NamespaceIndentation: Inner ObjCBinPackProtocolList: Auto ObjCBlockIndentWidth: 4 ObjCSpaceAfterProperty: true ObjCSpaceBeforeProtocolList: true PenaltyBreakAssignment: 2 PenaltyBreakBeforeFirstCallParameter: 19 PenaltyBreakComment: 300 PenaltyBreakFirstLessLess: 120 PenaltyBreakString: 1000 PenaltyBreakTemplateDeclaration: 10 PenaltyExcessCharacter: 1000000 PenaltyReturnTypeOnItsOwnLine: 60 # 'int *p' instead of 'int* p;' PointerAlignment: Right ReflowComments: true SortIncludes: true SortUsingDeclarations: true SpaceAfterCStyleCast: false SpaceAfterLogicalNot: false SpaceAfterTemplateKeyword: true SpaceBeforeAssignmentOperators: true SpaceBeforeCpp11BracedList: true SpaceBeforeCtorInitializerColon: true SpaceBeforeInheritanceColon: true SpaceBeforeParens: ControlStatements SpaceBeforeRangeBasedForLoopColon: true SpaceInEmptyBlock: true SpaceInEmptyParentheses: false SpacesBeforeTrailingComments: 1 SpacesInAngles: false SpacesInConditionalStatement: false SpacesInContainerLiterals: true SpacesInCStyleCastParentheses: false SpacesInParentheses: false SpacesInSquareBrackets: false SpaceBeforeSquareBrackets: false Standard: Latest StatementMacros: - Q_UNUSED - QT_REQUIRE_VERSION TabWidth: 8 UseCRLF: false UseTab: Never ...
解决方案
- 你使用的clang-format 10版本没有直接支持该需求的配置项,逻辑表达式换行的细粒度控制是在clang-format 13及以上版本才新增的功能。
- 如果可以升级clang-format版本,升级到13+之后修改以下两个配置即可实现需求:
你原有配置中# 开启后只要逻辑运算符所在行超过列限制,就会每个操作数单独换行 BreakBeforeBinaryOperators: All # 降低在二元运算符处换行的惩罚值,让clang-format更倾向于在逻辑运算符位置换行 PenaltyBreakBeforeBinaryOperator: 10BreakBeforeBinaryOperators为None,也就是不在运算符前换行,修改后会自动把每个逻辑条件拆到单独行,不需要额外加注释标记。 - 如果不能升级clang-format版本,除了你当前使用的加
// break注释的方案外,还有两种更易维护的方案:- 把逻辑条件提前赋值给单独的bool变量,不仅满足换行要求,还能提高代码可读性:
bool status_invalid = (long_function_name_12345().get_some_status1() != status1) || (long_function_name_12345().get_some_status2() != status2); if (status_invalid || force || some_other_logical_bool) { }- 适当降低
PenaltyExcessCharacter的数值,你当前配置为1000000,调低后clang-format会更愿意换行而不是把短的逻辑条件挤到同一行,不过该调整是全局的,可能影响其他代码的格式化效果,需要根据项目整体风格测试后调整。
内容的提问来源于stack exchange,提问作者code_fodder
相关产品推荐
相关产品推荐

