Skip to content

Commit bfcc2a0

Browse files
committed
DisallowSpaceIndent: Fix the case of the moving space
Given this code: ```php wp_die( $api ); // Tab - Space - Tab - Tab ``` with tab width set to `4`, `wp_die()` would start in column 13. The sniff was currently fixing this to: ```php wp_die( $api ); // Tab - Tab - Tab - Space ``` which changed the start column to 14 and changed the "space hidden before a tab" to precision alignment. This commit fixes that and is a further iteration building onto the improvements in 1404. The above code will now be fixed as: ```php wp_die( $api ); // Tab - Tab - Tab ``` Notes: * As the tabs in whitespace at the start of `T_INLINE_HTML` and `T_COMMENT` tokens is not replaced by spaces in the `content` by the tokenizer, this has to be done within the sniff to determine what the correct length of the whitespace should be. * Basing the correction of the space-based length of the whitespace allows for fixing with higher precision. * Incidentally, this also fixes one of the metrics being recorded incorrectly. For in-depth details of the effect on the metrics of this fix, please see: https://gist.github.com/jrfnl/5e2d75894c8e60a8f314b9fcb0ad3f62 * The `tabWidth` is now set in the unit test file.
1 parent 1ce9081 commit bfcc2a0

File tree

3 files changed

+38
-17
lines changed

3 files changed

+38
-17
lines changed

src/Standards/Generic/Sniffs/WhiteSpace/DisallowSpaceIndentSniff.php

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,8 @@ public function process(File $phpcsFile, $stackPtr)
8989
$content = $tokens[$i]['content'];
9090
}
9191

92+
$expectedIndentSize = $tokens[$i]['length'];
93+
9294
// If this is an inline HTML token or a subsequent line of a multi-line comment,
9395
// split the content into indentation whitespace and the actual HTML/text.
9496
$nonWhitespace = '';
@@ -98,22 +100,26 @@ public function process(File $phpcsFile, $stackPtr)
98100
) {
99101
if (isset($matches[1]) === true) {
100102
$content = $matches[1];
103+
104+
// Tabs are not replaced in content, so the "length" is wrong.
105+
$matches[1] = str_replace("\t", str_repeat(' ', $this->tabWidth), $matches[1]);
106+
$expectedIndentSize = strlen($matches[1]);
101107
}
102108

103109
if (isset($matches[2]) === true) {
104110
$nonWhitespace = $matches[2];
105111
}
106112
}
107113

108-
$hasSpaces = strpos($content, ' ');
109-
$hasTabs = strpos($content, "\t");
114+
$foundSpaces = substr_count($content, ' ');
115+
$foundTabs = substr_count($content, "\t");
110116

111-
if ($hasSpaces === false && $hasTabs === false) {
117+
if ($foundSpaces === 0 && $foundTabs === 0) {
112118
// Empty line.
113119
continue;
114120
}
115121

116-
if ($hasSpaces === false && $hasTabs !== false) {
122+
if ($foundSpaces === 0 && $foundTabs > 0) {
117123
// All ok, nothing to do.
118124
$phpcsFile->recordMetric($i, 'Line indent', 'tabs');
119125
continue;
@@ -131,20 +137,21 @@ public function process(File $phpcsFile, $stackPtr)
131137
// We just don't know yet whether they need to be replaced or
132138
// are precision indentation, nor whether they are correctly
133139
// placed at the end of the whitespace.
134-
$trimmed = str_replace(' ', '', $content);
135-
$numSpaces = (strlen($content) - strlen($trimmed));
136-
$numTabs = (int) floor($numSpaces / $this->tabWidth);
137-
$tabAfterSpaces = strpos($content, "\t", $hasSpaces);
140+
$tabAfterSpaces = strpos($content, "\t", strpos($content, ' '));
141+
142+
// Calculate the expected tabs and spaces.
143+
$expectedTabs = (int) floor($expectedIndentSize / $this->tabWidth);
144+
$expectedSpaces = ($expectedIndentSize % $this->tabWidth);
138145

139-
if ($hasTabs === false) {
146+
if ($foundTabs === 0) {
140147
$phpcsFile->recordMetric($i, 'Line indent', 'spaces');
141148

142-
if ($numTabs === 0) {
149+
if ($foundTabs === $expectedTabs && $foundSpaces === $expectedSpaces) {
143150
// Ignore: precision indentation.
144151
continue;
145152
}
146153
} else {
147-
if ($numTabs === 0) {
154+
if ($foundTabs === $expectedTabs && $foundSpaces === $expectedSpaces) {
148155
// Precision indentation.
149156
$phpcsFile->recordMetric($i, 'Line indent', 'tabs');
150157

@@ -161,10 +168,9 @@ public function process(File $phpcsFile, $stackPtr)
161168
$error = 'Tabs must be used to indent lines; spaces are not allowed';
162169
$fix = $phpcsFile->addFixableError($error, $i, 'SpacesUsed');
163170
if ($fix === true) {
164-
$remaining = ($numSpaces % $this->tabWidth);
165-
$padding = str_repeat("\t", $numTabs);
166-
$padding .= str_repeat(' ', $remaining);
167-
$phpcsFile->fixer->replaceToken($i, $trimmed.$padding.$nonWhitespace);
171+
$padding = str_repeat("\t", $expectedTabs);
172+
$padding .= str_repeat(' ', $expectedSpaces);
173+
$phpcsFile->fixer->replaceToken($i, $padding.$nonWhitespace);
168174
}
169175
}//end for
170176

src/Standards/Generic/Tests/WhiteSpace/DisallowSpaceIndentUnitTest.inc.fixed

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@ EOF;
1919
$world = '';
2020
// here the indention is mixed with tabs and spaces
2121
// [tab][space][space][space][tab]return "<$name$xmlns$type_str $atts xsi:nil=\"true\"/>";
22-
return "<$name$xmlns$type_str $atts xsi:nil=\"true\"/>";
22+
return "<$name$xmlns$type_str $atts xsi:nil=\"true\"/>";
2323
// [space][space][space][tab]return "<$name$xmlns$type_str $atts xsi:nil=\"true\"/>";
24-
return "<$name$xmlns$type_str $atts xsi:nil=\"true\"/>";
24+
return "<$name$xmlns$type_str $atts xsi:nil=\"true\"/>";
2525
// Doc comments are indent with tabs and one space
2626
//[tab]/**
2727
//[tab][space]*

src/Standards/Generic/Tests/WhiteSpace/DisallowSpaceIndentUnitTest.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,21 @@ class DisallowSpaceIndentUnitTest extends AbstractSniffUnitTest
1515
{
1616

1717

18+
/**
19+
* Get a list of CLI values to set before the file is tested.
20+
*
21+
* @param string $testFile The name of the file being tested.
22+
* @param \PHP_CodeSniffer\Config $config The config data for the test run.
23+
*
24+
* @return void
25+
*/
26+
public function setCliValues($testFile, $config)
27+
{
28+
$config->tabWidth = 4;
29+
30+
}//end setCliValues()
31+
32+
1833
/**
1934
* Returns the lines where errors should occur.
2035
*

0 commit comments

Comments
 (0)