Clang-format换行

79
我正在寻找一个clang-format的设置,以防止该工具删除换行符。

例如,我将我的ColumnLimit设置为120,在重新格式化一些示例代码时会发生以下情况。

之前:

#include <vector>
#include <string>

std::vector<std::string> get_vec()
{
   return std::vector<std::string> {
      "this is a test",
      "some of the lines are longer",
      "than other, but I would like",
      "to keep them on separate lines"
   };
}

int main()
{
   auto vec = get_vec();
}

之后:

#include <vector>
#include <string>

std::vector<std::string> get_vec()
{
   return std::vector<std::string>{"this is a test", "some of the lines are longer", "than other, but I would like",
         "to keep them on separate lines"};
}

int main()
{
   auto vec = get_vec();
}

我希望的是工具可以在文本行超过120个字符时进行换行,但不会因为一行文本小于120个字符就将其与其他行合并。

是否有这样的选项? 我在文档中没有看到相关内容。


1
对于您的特定示例,设置 AllowShortFunctionsOnASingleLine: None 将起作用。 - dejvuth
这样做可以防止main被解包,但我更关心向量初始化的一般情况。如果在另一个(更长的)函数中以这种方式初始化向量,则仍将被解包。 - zmb
我在想调整各种“惩罚”选项是否有帮助,但它们似乎都是与断行相关的惩罚,而不是“不断行”的惩罚。 - zmb
你能举个例子说明上述设置未按预期工作的情况吗?该工具不再缩短您的函数,并应遵守列限制。向量初始化也应该正常工作。 - dejvuth
我在问题中更新了示例。使用AllowShortFunctionsOnASingleLine: Nonemain不再被拆分成多行,但是你可以看到向量初始化仍然出现问题。 - zmb
显示剩余2条评论
5个回答

68

因此,在混乱的clang格式代码中进行了一些修改并制作了一些补丁,这是我的两分钱意见:

  • Clang格式是基于使用libclang解析AST来消除所有空格,将令牌序列分解为“未包装行”的,这类似于“逻辑”代码行,然后应用规则/配置信息,有时将“未包装行”分解成较小的单位,并在新的空格/缩进下重新输出。

    第一次解析代码时很难使其遵守原始的白噪声,因为那会被抛弃掉。

  • 您可以通过以下方式轻松地控制换行的位置:

    • 设置列限制
    • 使用“bin pack参数”选项
    • 为各种断点设置惩罚-函数返回类型后换行、第一个调用参数前换行、字符串文字、注释...
    • 在行尾放置注释(clang格式无法删除注释,因此必须拆分该行)
    • 使用clang-format off/on指令

这里是你可以尝试的一件事:

std::vector<std::string> get_vec()
{
   return std::vector<std::string> {   //
      "this is a test",                //
      "some of the lines are longer",  //
      "than other, but I would like",  //
      "to keep them on separate lines" //
   };
}

与使用// clang-format off相比的优点是,如果您稍后更改了制表符宽度或其他某些选项,这些代码行仍将获得这些格式更改,因此您不需要手动进入// clang-format off 区域进行修复。但它仍然有点像黑客技巧,情况因人而异。

最终,clang-format很大程度上是关于对整个代码库施加统一的格式,确保在程序中的所有字符串文字都以相同的样式进行格式化。如果您想对换行决策进行微观级别的控制,那就不是该工具的精神所在,您将不得不禁用它。

这有时可能会令人沮丧,尤其当您想对数组进行操作并使列对齐或者其他类似的事情时——例如,下面是来自lua C api的一些自然代码:

static luaL_Reg const methods[] = {
    {"matches",               &dispatch::intf_match_unit},
    {"to_recall",             &dispatch::intf_put_recall_unit},
    {"to_map",                &dispatch::intf_put_unit},
    {"erase",                 &dispatch::intf_erase_unit},
    {"clone",                 intf_copy_unit},
    {"extract",               &dispatch::intf_extract_unit},
    {"advance",               intf_advance_unit},
};

当clang-format运行时,通常不会使右列对齐,而是将其放置在逗号后的固定空格数上,据我所知,你无能为力。

或者,如果您有用于OpenGL的4 x 4矩阵:

      constexpr float shadow_skew_hardcoded[16] =
        { 1.0f, 0.0f, 0.0f, 0.0f,
          0.5f, 0.5f, 0.0f, 0.0f,
          0.0f, 0.0f, 1.0f, 0.0f,
          0.0f, 0.0f, 0.0f, 1.0f };
如果您让clang-format运行这样的内容,它只会使它们混乱,据我所知,没有简单的方法使其格式化得好看,因此您只能采用“许多微不足道的注释”或在遇到这种情况时使用clang-format off。 这些只是该工具的固有限制。 如果您不希望经常做这样的事情,那么这可能不是适合您的工具。

很棒的答案。您的建议似乎比// clang-format off更好,而且为什么它会这样行为的解释非常有道理。 - zmb
1
ColumnLimit 为0时仍保留换行符。如何解释这个问题? - KindDragon
2
我不得不调整你的技巧,因为我正在使用多行#define宏。关键是在每行末尾加上/**/\\ - qbolec
@KindDragon 如果你也想将最大行长度保持在120个字符以内呢? - scruel

19

我不确定你是否可以通过clang-format做到完全符合你的要求,但是可以告诉clang-format保留代码的某些部分。我会在正是你所谈论的场景下使用这个功能,即当一个特定的格式使代码更易读时,就会保留代码块的格式。

std::vector<std::string> get_vec()
{
   // clang-format off
   return std::vector<std::string> {
      "this is a test",
      "some of the lines are longer",
      "than other, but I would like",
      "to keep them on separate lines"
   };
   // clang-format on
}

请参考: http://clang.llvm.org/docs/ClangFormatStyleOptions.html#disabling-formatting-on-a-piece-of-code


2
我并不是说这个答案不好,但这绝对是一个可怕的问题解决方案。用注释来使自动格式化工作,会让你的代码变得混乱。 - Martin G
也许吧,但实际上我很少使用它来打破格式规则。我通常很感激有这样的选择,但不喜欢它被滥用而“弄乱你的代码”。滥用工具通常是用户的问题。 - MattG

18
在最后一个字符串后添加逗号。这将告诉clang-format将其垂直格式化。例如: https://godbolt.org/z/bZxr__ 右键单击 > 格式化文本
#include <string>
#include <vector>

std::vector<std::string> get_vec() {
  return std::vector<std::string>{
      "this is a test",
      "some of the lines are longer",
      "than other, but I would like",
      "to keep them on separate lines", // comma here after last element
  };
}

int main() { auto vec = get_vec(); }

4
我很喜欢它,但是对我来说这很不明显。为什么它有效?是否有一个样式选项可以控制它? - Eric Backus
1
枚举中的尾随逗号也可以实现相同的效果。 - Alex Weissnicht
很遗憾,这对函数调用不起作用,因为你还不能有一个悬挂逗号。 - majinnaibu

11

我在文档中目前没有找到任何允许您这样做的选项。

将ColumnLimit设置为0仍会保留文字换行。

clang-format-mp-3.4 test.c -style="{ ColumnLimit: 0 }"

#include <vector>
#include <memory>
#include <string>

int main() {
  std::vector<std::string> vec = {
    "this is a test",
    "with some strings",
    "that I want on separate lines"
  };
}

1
他还想将“ColumnLimit”设置为120? - Danh
2
没错。这样可以保留我的换行,但无法解决过长的行(在我看来,这是该工具的主要优势之一)。 - zmb
1
这是一个折中的方案,但对我来说是毫无疑问的选择。如果clang-format不会在我的数组定义、更复杂的std::cout表达式、枚举、参数列表等方面搞乱换行,我很愿意让程序员手动将代码限制在100列。 - Gauthier

1
使用这些规则在 .clang-format 中。
BasedOnStyle: LLVM
AlignAfterOpenBracket: AlwaysBreak
AllowShortBlocksOnASingleLine: Empty
BreakConstructorInitializers: AfterColon
BinPackArguments: false  // Important for this case
BinPackParameters: false  // Important for this case
AlignEscapedNewlines: DontAlign
SpacesBeforeTrailingComments: 2
AllowShortBlocksOnASingleLine: Never
AllowShortFunctionsOnASingleLine: None
ContinuationIndentWidth: 2
IndentWidth: 2
Standard: c++17
UseTab: Never

我得到了接近预期结果的格式

#include <string>
#include <vector>

std::vector<std::string> get_vec() {
  return std::vector<std::string>{
    "this is a test",
    "some of the lines are longer",
    "than other, but I would like",
    "to keep them on separate lines"};
}

int main() {
  auto vec = get_vec();
}

然而,如果您想将 ColumnLimit 设置为非零值,则此方法不起作用:

  ValidateVisitor(clang::CompilerInstance *Compiler) :
      Compiler(Compiler), Context(&Compiler->getASTContext()),
      SM(&Context->getSourceManager()) {
  }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接