-
-
Notifications
You must be signed in to change notification settings - Fork 7
/
CommentLengthFixer.php
199 lines (154 loc) · 5.55 KB
/
CommentLengthFixer.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
<?php
declare(strict_types=1);
/*
* This file is part of Contao.
*
* (c) Leo Feyer
*
* @license LGPL-3.0-or-later
*/
namespace Contao\EasyCodingStandard\Fixer;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\DocBlock\DocBlock;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
final class CommentLengthFixer extends AbstractFixer
{
use IndentationFixerTrait;
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Comments should be between 74 and 86 characters long per line.',
[
new CodeSample(
<<<'EOT'
<?php
// This comment exceeds the maximum line length of 80 characters. It should be
// distributed accross two lines.
if (true) {
}
EOT,
),
],
);
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isAnyTokenKindsFound([T_COMMENT, T_DOC_COMMENT]);
}
/**
* Must run after InlinePhpdocCommentFixer.
*/
public function getPriority(): int
{
return 25;
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
for ($index = 1, $count = \count($tokens); $index < $count; ++$index) {
if ($tokens[$index]->isGivenKind(T_COMMENT)) {
$index = $this->handleComment($tokens, $index);
} elseif ($tokens[$index]->isGivenKind(T_DOC_COMMENT)) {
$index = $this->handleDocComment($tokens, $index);
}
}
}
private function handleComment(Tokens $tokens, int $index): int
{
$content = $tokens[$index]->getContent();
if (!str_starts_with($content, '// ')) {
return $index + 1;
}
// Ignore comments that are on the same line as the code
if (!str_contains($tokens[$index - 1]->getContent(), "\n")) {
return $index + 1;
}
$end = $index;
$comment = substr($content, 3);
while (true) {
$next = $tokens->getNextNonWhitespace($end);
if (null === $next || !$tokens[$next]->isGivenKind(T_COMMENT)) {
break;
}
$content = $tokens[$next]->getContent();
// Preserve lines that contain URLs or lists
if (preg_match('#^// (https:|- |\d+\. )#', $content)) {
return $next + 1;
}
$comment .= ' '.substr($content, 3);
$end = $next;
}
$lines = $this->getLines($comment, 80, '//');
if (substr_count((string) end($lines), ' ') < 2) {
$lines = $this->getLines($comment, 86, '//');
if (substr_count((string) end($lines), ' ') < 2) {
$lines = $this->getLines($comment, 74, '//');
}
}
$new = [];
$indent = $this->getIndent($tokens, $index);
for ($i = 0, $c = \count($lines); $i < $c; ++$i) {
if ($i > 0) {
$new[] = new Token([T_WHITESPACE, $indent]);
}
$new[] = new Token([T_COMMENT, $lines[$i]]);
}
$tokens->clearRange($index, $end);
$tokens->insertAt($index, $new);
return $end + 1;
}
private function handleDocComment(Tokens $tokens, int $index): int
{
$text = null;
$newLines = [];
$docBlock = new DocBlock($tokens[$index]->getContent());
$lines = $docBlock->getLines();
$content = end($lines)->getContent();
$indent = substr($content, 0, strpos($content, '*'));
foreach ($lines as $line) {
$content = $line->getContent();
// Preserve lines that contain URLs, lists or indented content
if ($line->containsATag() || !$line->containsUsefulContent() || preg_match('#^ *\* ( |https:|- |\d+\. )#', $content)) {
if ($text) {
$comment = rtrim($text);
$lns = $this->getLines($comment, 80, '*');
if (substr_count((string) end($lns), ' ') < 2) {
$lns = $this->getLines($comment, 86, '*');
if (substr_count((string) end($lns), ' ') < 2) {
$lns = $this->getLines($comment, 74, '*');
}
}
foreach ($lns as $ln) {
$newLines[] = "$indent$ln\n";
}
$text = null;
}
$newLines[] = $content;
continue;
}
$text .= rtrim(substr($content, \strlen($indent) + 2)).' ';
}
$tokens->offsetSet($index, new Token([T_DOC_COMMENT, implode('', $newLines)]));
return $index + 1;
}
private function getLines(string $comment, int $length, string $prefix = ''): array
{
$lines = [];
$i = 0;
$chunks = explode(' ', $comment);
while ([] !== $chunks) {
$word = array_shift($chunks);
if (!isset($lines[$i])) {
$lines[$i] = $prefix;
}
if ($prefix !== $lines[$i] && \strlen($lines[$i]) + \strlen($word) > $length) {
$lines[++$i] = $prefix;
}
$lines[$i] .= " $word";
}
return $lines;
}
}