在clang-format中,惩罚项是用来做什么的?

70

clang-format 样式选项文档 包含一些名为 PenaltyXXX 的选项。该文档没有解释如何使用这些惩罚值。能否描述一下如何使用这些惩罚值及其实现的效果(可以举例说明)?


5
有一个由Daniel Jasper制作的视频,在大约10:40的时候,他简要解释了惩罚机制。这似乎在决定“最佳”格式时起到了作用。 - user3920237
2个回答

46

当你的一行代码超过了长度限制时,clang-format需要在某处插入一个或多个换行符。你可以把惩罚看作是一种阻止某些换行行为的方式。例如,假设你有以下代码:

Namespaces::Are::Pervasive::SomeReallyVerySuperDuperLongFunctionName(args);
// and the column limit is here:                                        ^

Clang-format 很可能会格式化得看起来有些奇怪:

Namespaces::Are::Pervasive::SomeReallyVerySuperDuperLongFunctionName(
    args);

对于这类情况,你可能会决定容忍违反行长度一两个字符,因此您可以通过将PenaltyExcessCharacter设置为一个较低的数字,而将PenaltyBreakBeforeFirstCallParameter设置为一个较高的数字来控制。

就我个人而言,我真的不喜欢返回类型单独占一行,所以我将PenaltyReturnTypeOnItsOwnLine设置为非常大的数字。

顺带一提,这个系统是从Latex继承下来的,它允许您指定各种用于换行、分页和断字等的惩罚。


那么较低的惩罚意味着它将首先被选择?惩罚值实际上映射到什么?我怎么知道3会被优先于5?3和5代表什么? - void.pointer
2
@void.pointer:数字值不能以绝对比例解释,它们只有相对含义。此外,它们不是在单一上下文中使用的,而是根据正在格式化的源代码逐步累加。我一直在阅读源代码,发现到处都是硬编码的魔法惩罚,范围从1到至少5000... - Cameron
@Cameron 我仍然不确定它们是如何工作的。就我而言,我只看到没有任何目的的数字。此时,我不知道应该为哪些选项分配哪些数字。这个系统不太可预测。 - void.pointer
1
罚款似乎影响了断行算法,该算法试图选择最佳路径(其中最佳路径是将换行符放置在一行中的方式,以使总惩罚最小化)。但是到处都有许多特殊情况。而且很多选项相互作用,有时不太清楚。肯定有许多边角案例会产生奇怪的结果。 - Cameron

10
你能描述如何使用这些惩罚值以及它们所达到的效果吗?可以举个例子吗?在这个Git 2.15(2017年第四季度)的clang-format中,您可以看到一个关于C语言的Git项目的示例:
请参阅提交42efde4(由Johannes Schindelin (dscho)于2017年9月29日提交)。
(由Johannes Schindelin -- dscho --合并于提交42efde4,2017年10月1日) 您可以在这里看到旧值和新值:

clang

为了说明这些价值观:

clang-format: 调整换行惩罚

我们非常、非常、非常希望将每行的列限制在80个以下:Git邮件列表上为数不多的一致风格建议是,每行不应超过80列(即使79列更合理,因为代码经常被视为差异,并且比较会添加一个额外字符)。

然而,如Brandon Williams所指出的那样,超额字符的惩罚5太低,无法保证这一点。

(请参见此线程)

从现有的clang-format示例和文档中可以看出,100是被认为适用于Stuff You Really Don't Want的惩罚分数,因此让我们将其分配为"excess characters"的惩罚分数,即过长的行。 在此同时,进一步调整惩罚分数:实际上我们并不太关心在注释或字符串文字内部防止换行,所以100分的惩罚分数似乎过高。 同样地,我们并不是非常坚持在赋值运算符之前保持换行(Git的很多代码都会在=字符后立即断开,以保持80列/行的限制)。 我们对函数的返回类型单独占据一行的程度比0分的惩罚分数更加不满意,因此也进行了调整。 最后,我们并不特别喜欢在调用的第一个参数之前断开,但如果这使得行长度小于80列/行,那么我们就这样做,因此降低在调用的第一个参数之前断开的惩罚分数,但不要像在注释中引入新的换行符那样降低太多。

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