Skip to content

Commit 5f63628

Browse files
committed
Add MultipleEmptyLinesSniff
1 parent f50b15e commit 5f63628

File tree

7 files changed

+270
-3
lines changed

7 files changed

+270
-3
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
55

66
## [8.0.0] -
77
### Added
8-
- Add MO4.WhiteSpace.ConstantSpacing
8+
- Add `MO4.WhiteSpace.ConstantSpacing`
9+
- Add `MO4.WhiteSpace.MultipleEmptyLinesSniff`
910
### Changed
1011
- refactored tests
1112

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
<?php
2+
3+
/**
4+
*
5+
* Check multiple consecutive newlines in a file.
6+
* Source: MediaWiki. I didn't want to add dependency to whole package (because of only one sniff).
7+
*
8+
* @link https://github.com/wikimedia/mediawiki-tools-codesniffer/blob/272835d/MediaWiki/Sniffs/WhiteSpace/MultipleEmptyLinesSniff.php
9+
*/
10+
11+
namespace MO4\Sniffs\WhiteSpace;
12+
13+
use PHP_CodeSniffer\Files\File;
14+
use PHP_CodeSniffer\Sniffs\Sniff;
15+
16+
class MultipleEmptyLinesSniff implements Sniff
17+
{
18+
/**
19+
* Registers the tokens that this sniff wants to listen for.
20+
*
21+
* @return array<int, int>
22+
*
23+
* @see Tokens.php
24+
*/
25+
public function register(): array
26+
{
27+
return [
28+
// Assume most comments end with a newline
29+
T_COMMENT,
30+
// Assume all <?php open tags end with a newline
31+
T_OPEN_TAG,
32+
T_WHITESPACE,
33+
];
34+
}
35+
36+
/**
37+
* @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint
38+
*
39+
* @param File $phpcsFile
40+
* @param int $stackPtr The current token index.
41+
*
42+
* @return void|int
43+
*/
44+
public function process(File $phpcsFile, $stackPtr)
45+
{
46+
$tokens = $phpcsFile->getTokens();
47+
48+
// This sniff intentionally doesn't care about whitespace at the end of the file
49+
if (!isset($tokens[$stackPtr + 3])
50+
|| $tokens[$stackPtr + 2]['line'] === $tokens[$stackPtr + 3]['line']
51+
) {
52+
return $stackPtr + 3;
53+
}
54+
55+
if ($tokens[$stackPtr + 1]['line'] === $tokens[$stackPtr + 2]['line']) {
56+
return $stackPtr + 2;
57+
}
58+
59+
// Finally, check the assumption the current token is or ends with a newline
60+
if ($tokens[$stackPtr]['line'] === $tokens[$stackPtr + 1]['line']) {
61+
return;
62+
}
63+
64+
// Search for the next non-newline token
65+
$next = $stackPtr + 1;
66+
67+
while (isset($tokens[$next + 1]) &&
68+
$tokens[$next]['code'] === T_WHITESPACE &&
69+
$tokens[$next]['line'] !== $tokens[$next + 1]['line']
70+
) {
71+
$next++;
72+
}
73+
74+
$count = $next - $stackPtr - 1;
75+
76+
if ($count > 1
77+
&& $phpcsFile->addFixableError(
78+
'Multiple empty lines should not exist in a row; found %s consecutive empty lines',
79+
$stackPtr + 1,
80+
'MultipleEmptyLines',
81+
[$count]
82+
)
83+
) {
84+
$phpcsFile->fixer->beginChangeset();
85+
86+
// Remove all newlines except the first two, i.e. keep one empty line
87+
for ($i = $stackPtr + 2; $i < $next; $i++) {
88+
$phpcsFile->fixer->replaceToken($i, '');
89+
}
90+
91+
$phpcsFile->fixer->endChangeset();
92+
}
93+
94+
// Don't check the current sequence a second time
95+
return $next;
96+
}
97+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?php
2+
3+
4+
/**
5+
* license header etc.
6+
*/
7+
8+
/**
9+
* Failed examples.
10+
* @return void
11+
*/
12+
function wfFailedExamples() {
13+
$a = 1;
14+
15+
16+
17+
18+
19+
20+
$b = 2;
21+
22+
23+
// Comment
24+
25+
26+
27+
28+
// Comment 2
29+
30+
31+
// Comment 3
32+
33+
/* Comment 4 */
34+
35+
# Comment 5
36+
37+
$c = 3;
38+
}
39+
40+
/**
41+
* Passed examples.
42+
* @return void
43+
*/
44+
function wfPassedExamples() {
45+
$a = 1;
46+
47+
$b = 2;
48+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
3+
/**
4+
* license header etc.
5+
*/
6+
7+
/**
8+
* Failed examples.
9+
* @return void
10+
*/
11+
function wfFailedExamples() {
12+
$a = 1;
13+
14+
$b = 2;
15+
16+
// Comment
17+
18+
// Comment 2
19+
20+
// Comment 3
21+
22+
/* Comment 4 */
23+
24+
# Comment 5
25+
26+
$c = 3;
27+
}
28+
29+
/**
30+
* Passed examples.
31+
* @return void
32+
*/
33+
function wfPassedExamples() {
34+
$a = 1;
35+
36+
$b = 2;
37+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
3+
/**
4+
* license header etc.
5+
*/
6+
7+
/**
8+
* Failed examples.
9+
* @return void
10+
*/
11+
function wfFailedExamples() {
12+
$a = 1;
13+
14+
$b = 2;
15+
16+
// Comment
17+
18+
// Comment 2
19+
20+
// Comment 3
21+
22+
/* Comment 4 */
23+
24+
# Comment 5
25+
26+
$c = 3;
27+
}
28+
29+
/**
30+
* Passed examples.
31+
* @return void
32+
*/
33+
function wfPassedExamples() {
34+
$a = 1;
35+
36+
$b = 2;
37+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?php
2+
3+
/**
4+
* This file is part of the mo4-coding-standard (phpcs standard)
5+
*
6+
* @author Xaver Loppenstedt <[email protected]>
7+
*
8+
* @license http://spdx.org/licenses/MIT MIT License
9+
*
10+
* @link https://github.com/mayflower/mo4-coding-standard
11+
*/
12+
13+
declare(strict_types=1);
14+
15+
namespace MO4\Tests\WhiteSpace;
16+
17+
use MO4\Tests\AbstractMo4SniffUnitTest;
18+
19+
/**
20+
* Unit test class for the MultipleEmptyLines sniff.
21+
*
22+
* A sniff unit test checks a .inc file for expected violations of a single
23+
* coding standard. Expected errors and warnings are stored in this class.
24+
*
25+
* @author Xaver Loppenstedt <[email protected]>
26+
*
27+
* @copyright 2013 Xaver Loppenstedt, some rights reserved.
28+
*
29+
* @license http://spdx.org/licenses/MIT MIT License
30+
*
31+
* @link https://github.com/mayflower/mo4-coding-standard
32+
*/
33+
class MultipleEmptyLinesUnitTest extends AbstractMo4SniffUnitTest
34+
{
35+
protected $expectedErrorList = [
36+
'MultipleEmptyLinesUnitTest.pass.inc' => [],
37+
'MultipleEmptyLinesUnitTest.fail.inc' => [
38+
2 => 1,
39+
14 => 1,
40+
21 => 1,
41+
24 => 1,
42+
29 => 1,
43+
],
44+
];
45+
}

README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,10 +77,12 @@ To change the sorting order for your project, add this snippet to your custom `r
7777
### MO4.Strings.VariableInDoubleQuotedString
7878
* Interpolated variables in double quoted strings must be surrounded by `{ }`, e.g. `{$VAR}` instead of `$VAR`.
7979

80-
## MO4.WhiteSpace.ConstantSpacing
81-
80+
### MO4.WhiteSpace.ConstantSpacing
8281
* const must be followed by a single space.
8382

83+
### MO4.WhiteSpace.MultipleEmptyLines
84+
* No more than one empty consecutive line is allowed. Taken from [mediawiki/mediawiki-codesniffer](https://github.com/wikimedia/mediawiki-tools-codesniffer).
85+
8486
### Further rules (imported from other standards)
8587
* See `MO4/ruleset.xml`, which has each imported rule commented.
8688

0 commit comments

Comments
 (0)