Skip to content

Commit fd58389

Browse files
committed
FunctionLengthSniff ignore blank lines and comments
1 parent 6a06085 commit fd58389

File tree

1 file changed

+120
-21
lines changed

1 file changed

+120
-21
lines changed

Inpsyde/Sniffs/CodeQuality/FunctionLengthSniff.php

Lines changed: 120 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,16 @@ final class FunctionLengthSniff implements Sniff
3434
*/
3535
public $ignoreDocBlocks = true;
3636

37+
/**
38+
* @var true
39+
*/
40+
public $ignoreSingleLineComments = true;
41+
42+
/**
43+
* @var true
44+
*/
45+
public $ignoreWhiteLines = true;
46+
3747
/**
3848
* @return int[]
3949
*/
@@ -49,16 +59,30 @@ public function register(): array
4959
public function process(File $file, $position)
5060
{
5161
$length = $this->getStructureLengthInLines($file, $position);
62+
if ($length <= $this->maxLength) {
63+
return;
64+
}
5265

53-
if ($length > $this->maxLength) {
54-
$error = sprintf(
55-
'Your function is too long. Currently using %d lines. Can be up to %d lines.',
56-
$length,
57-
$this->maxLength
58-
);
59-
60-
$file->addError($error, $position, 'TooLong');
66+
$ignored = [];
67+
$suffix = '';
68+
$this->ignoreWhiteLines and $ignored[] = 'white lines';
69+
$this->ignoreSingleLineComments and $ignored[] = 'single line comments';
70+
$this->ignoreDocBlocks and $ignored[] = 'doc blocks';
71+
if ($ignored) {
72+
$suffix = ' (ignoring ';
73+
$last = array_pop($ignored);
74+
$others = implode(', ', $ignored);
75+
$suffix .= $others ? "{$others} and {$last})" : "{$last})";
6176
}
77+
78+
$error = sprintf(
79+
'Your function is too long. Currently using %d lines%s, max is %d.',
80+
$length,
81+
$suffix,
82+
$this->maxLength
83+
);
84+
85+
$file->addError($error, $position, 'TooLong');
6286
}
6387

6488
/**
@@ -77,25 +101,100 @@ public function getStructureLengthInLines(File $file, int $position): int
77101
return 0;
78102
}
79103

80-
$opener = $token['scope_opener'];
81-
$closer = $token['scope_closer'];
82-
$length = $tokens[$closer]['line'] - $tokens[$opener]['line'];
104+
$start = $token['scope_opener'];
105+
$end = $token['scope_closer'];
106+
$length = $tokens[$end]['line'] - $tokens[$start]['line'];
83107

84-
if (!$this->ignoreDocBlocks) {
108+
if ($length < $this->maxLength) {
85109
return $length;
86110
}
87111

88-
$decrease = 0;
89-
for ($i = $opener + 1; $i < $closer; $i++) {
90-
if ($tokens[$i]['code'] === T_DOC_COMMENT_OPEN_TAG) {
91-
$openerLine = (int)$tokens[$i]['line'];
92-
$closer = $tokens[$i]['comment_closer'] ?? null;
93-
$decrease += is_numeric($closer)
94-
? (int)$tokens[$closer]['line'] - ($openerLine - 1)
95-
: 1;
112+
return $length - $this->collectLinesToExclude($start, $end, $tokens);
113+
}
114+
115+
/**
116+
* @param int $start
117+
* @param int $end
118+
* @param array $tokens
119+
* @return int
120+
*/
121+
private function collectLinesToExclude(
122+
int $start,
123+
int $end,
124+
array $tokens
125+
): int {
126+
127+
$linesData = $docblocks = [];
128+
129+
$skipLines = [$tokens[$start + 1]['line'], $tokens[$end - 1]['line']];
130+
for ($i = $start + 1; $i < $end - 1; $i++) {
131+
if (in_array($tokens[$i]['line'], $skipLines, true)) {
132+
continue;
96133
}
134+
135+
$docblocks = $this->docBlocksData($tokens, $i, $docblocks);
136+
$linesData = $this->ignoredLinesData($tokens[$i], $linesData);
137+
}
138+
139+
$empty = array_filter(array_column($linesData, 'empty'));
140+
$onlyComment = array_filter(array_column($linesData, 'only-comment'));
141+
142+
$toExcludeCount = array_sum($docblocks);
143+
if ($this->ignoreWhiteLines) {
144+
$toExcludeCount += count($empty);
145+
}
146+
if ($this->ignoreSingleLineComments) {
147+
$toExcludeCount += count($onlyComment) - count($empty);
148+
}
149+
150+
return $toExcludeCount;
151+
}
152+
153+
/**
154+
* @param array $token
155+
* @param array $lines
156+
* @return array
157+
*/
158+
private function ignoredLinesData(array $token, array $lines): array
159+
{
160+
$line = $token['line'];
161+
if (!array_key_exists($line, $lines)) {
162+
$lines[$line] = ['empty' => true, 'only-comment' => true];
163+
}
164+
165+
if (!in_array($token['code'], [T_COMMENT, T_WHITESPACE], true)) {
166+
$lines[$line]['only-comment'] = false;
97167
}
98168

99-
return max(0, $length - $decrease);
169+
if ($token['code'] !== T_WHITESPACE) {
170+
$lines[$line]['empty'] = false;
171+
}
172+
173+
return $lines;
174+
}
175+
176+
/**
177+
* @param array $tokens
178+
* @param int $position
179+
* @param array $docBlocks
180+
* @return array
181+
*/
182+
private function docBlocksData(
183+
array $tokens,
184+
int $position,
185+
array $docBlocks
186+
): array {
187+
if (!$this->ignoreDocBlocks
188+
|| $tokens[$position]['code'] !== T_DOC_COMMENT_OPEN_TAG
189+
) {
190+
return $docBlocks;
191+
}
192+
193+
$closer = $tokens[$position]['comment_closer'] ?? null;
194+
$docBlocks[] = is_numeric($closer)
195+
? 1 + ($tokens[$closer]['line'] - $tokens[$position]['line'])
196+
: 1;
197+
198+
return $docBlocks;
100199
}
101200
}

0 commit comments

Comments
 (0)