Chris@0
|
1 <?php
|
Chris@0
|
2 /**
|
Chris@17
|
3 * \Drupal\Sniffs\Commenting\InlineCommentSniff.
|
Chris@0
|
4 *
|
Chris@0
|
5 * @category PHP
|
Chris@0
|
6 * @package PHP_CodeSniffer
|
Chris@0
|
7 * @link http://pear.php.net/package/PHP_CodeSniffer
|
Chris@0
|
8 */
|
Chris@0
|
9
|
Chris@17
|
10 namespace Drupal\Sniffs\Commenting;
|
Chris@17
|
11
|
Chris@17
|
12 use PHP_CodeSniffer\Files\File;
|
Chris@17
|
13 use PHP_CodeSniffer\Sniffs\Sniff;
|
Chris@17
|
14 use PHP_CodeSniffer\Util\Tokens;
|
Chris@17
|
15
|
Chris@0
|
16 /**
|
Chris@17
|
17 * \Drupal\Sniffs\Commenting\InlineCommentSniff.
|
Chris@0
|
18 *
|
Chris@0
|
19 * Checks that no perl-style comments are used. Checks that inline comments ("//")
|
Chris@0
|
20 * have a space after //, start capitalized and end with proper punctuation.
|
Chris@17
|
21 * Largely copied from
|
Chris@17
|
22 * \PHP_CodeSniffer\Standards\Squiz\Sniffs\Commenting\InlineCommentSniff.
|
Chris@0
|
23 *
|
Chris@0
|
24 * @category PHP
|
Chris@0
|
25 * @package PHP_CodeSniffer
|
Chris@0
|
26 * @link http://pear.php.net/package/PHP_CodeSniffer
|
Chris@0
|
27 */
|
Chris@17
|
28 class InlineCommentSniff implements Sniff
|
Chris@0
|
29 {
|
Chris@0
|
30
|
Chris@0
|
31 /**
|
Chris@0
|
32 * A list of tokenizers this sniff supports.
|
Chris@0
|
33 *
|
Chris@0
|
34 * @var array
|
Chris@0
|
35 */
|
Chris@0
|
36 public $supportedTokenizers = array(
|
Chris@0
|
37 'PHP',
|
Chris@0
|
38 'JS',
|
Chris@0
|
39 );
|
Chris@0
|
40
|
Chris@0
|
41
|
Chris@0
|
42 /**
|
Chris@0
|
43 * Returns an array of tokens this test wants to listen for.
|
Chris@0
|
44 *
|
Chris@0
|
45 * @return array
|
Chris@0
|
46 */
|
Chris@0
|
47 public function register()
|
Chris@0
|
48 {
|
Chris@0
|
49 return array(
|
Chris@0
|
50 T_COMMENT,
|
Chris@0
|
51 T_DOC_COMMENT_OPEN_TAG,
|
Chris@0
|
52 );
|
Chris@0
|
53
|
Chris@0
|
54 }//end register()
|
Chris@0
|
55
|
Chris@0
|
56
|
Chris@0
|
57 /**
|
Chris@0
|
58 * Processes this test, when one of its tokens is encountered.
|
Chris@0
|
59 *
|
Chris@17
|
60 * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
|
Chris@17
|
61 * @param int $stackPtr The position of the current token in the
|
Chris@17
|
62 * stack passed in $tokens.
|
Chris@0
|
63 *
|
Chris@0
|
64 * @return void
|
Chris@0
|
65 */
|
Chris@17
|
66 public function process(File $phpcsFile, $stackPtr)
|
Chris@0
|
67 {
|
Chris@0
|
68 $tokens = $phpcsFile->getTokens();
|
Chris@0
|
69
|
Chris@0
|
70 // If this is a function/class/interface doc block comment, skip it.
|
Chris@0
|
71 // We are only interested in inline doc block comments, which are
|
Chris@0
|
72 // not allowed.
|
Chris@0
|
73 if ($tokens[$stackPtr]['code'] === T_DOC_COMMENT_OPEN_TAG) {
|
Chris@0
|
74 $nextToken = $phpcsFile->findNext(
|
Chris@17
|
75 Tokens::$emptyTokens,
|
Chris@0
|
76 ($stackPtr + 1),
|
Chris@0
|
77 null,
|
Chris@0
|
78 true
|
Chris@0
|
79 );
|
Chris@0
|
80
|
Chris@0
|
81 $ignore = array(
|
Chris@0
|
82 T_CLASS,
|
Chris@0
|
83 T_INTERFACE,
|
Chris@0
|
84 T_TRAIT,
|
Chris@0
|
85 T_FUNCTION,
|
Chris@0
|
86 T_CLOSURE,
|
Chris@0
|
87 T_PUBLIC,
|
Chris@0
|
88 T_PRIVATE,
|
Chris@0
|
89 T_PROTECTED,
|
Chris@0
|
90 T_FINAL,
|
Chris@0
|
91 T_STATIC,
|
Chris@0
|
92 T_ABSTRACT,
|
Chris@0
|
93 T_CONST,
|
Chris@0
|
94 T_PROPERTY,
|
Chris@0
|
95 T_VAR,
|
Chris@0
|
96 );
|
Chris@0
|
97
|
Chris@0
|
98 // Also ignore all doc blocks defined in the outer scope (no scope
|
Chris@0
|
99 // conditions are set).
|
Chris@0
|
100 if (in_array($tokens[$nextToken]['code'], $ignore) === true
|
Chris@0
|
101 || empty($tokens[$stackPtr]['conditions']) === true
|
Chris@0
|
102 ) {
|
Chris@0
|
103 return;
|
Chris@0
|
104 }
|
Chris@0
|
105
|
Chris@0
|
106 if ($phpcsFile->tokenizerType === 'JS') {
|
Chris@0
|
107 // We allow block comments if a function or object
|
Chris@0
|
108 // is being assigned to a variable.
|
Chris@17
|
109 $ignore = Tokens::$emptyTokens;
|
Chris@0
|
110 $ignore[] = T_EQUAL;
|
Chris@0
|
111 $ignore[] = T_STRING;
|
Chris@0
|
112 $ignore[] = T_OBJECT_OPERATOR;
|
Chris@0
|
113 $nextToken = $phpcsFile->findNext($ignore, ($nextToken + 1), null, true);
|
Chris@0
|
114 if ($tokens[$nextToken]['code'] === T_FUNCTION
|
Chris@0
|
115 || $tokens[$nextToken]['code'] === T_CLOSURE
|
Chris@0
|
116 || $tokens[$nextToken]['code'] === T_OBJECT
|
Chris@0
|
117 || $tokens[$nextToken]['code'] === T_PROTOTYPE
|
Chris@0
|
118 ) {
|
Chris@0
|
119 return;
|
Chris@0
|
120 }
|
Chris@0
|
121 }
|
Chris@0
|
122
|
Chris@0
|
123 $prevToken = $phpcsFile->findPrevious(
|
Chris@17
|
124 Tokens::$emptyTokens,
|
Chris@0
|
125 ($stackPtr - 1),
|
Chris@0
|
126 null,
|
Chris@0
|
127 true
|
Chris@0
|
128 );
|
Chris@0
|
129
|
Chris@0
|
130 if ($tokens[$prevToken]['code'] === T_OPEN_TAG) {
|
Chris@0
|
131 return;
|
Chris@0
|
132 }
|
Chris@0
|
133
|
Chris@0
|
134 // Inline doc blocks are allowed in JSDoc.
|
Chris@0
|
135 if ($tokens[$stackPtr]['content'] === '/**' && $phpcsFile->tokenizerType !== 'JS') {
|
Chris@0
|
136 // The only exception to inline doc blocks is the /** @var */
|
Chris@0
|
137 // declaration.
|
Chris@0
|
138 $content = $phpcsFile->getTokensAsString($stackPtr, ($tokens[$stackPtr]['comment_closer'] - $stackPtr + 1));
|
Chris@0
|
139 if (preg_match('#^/\*\* @var [a-zA-Z0-9_\\\\\[\]|]+ \$[a-zA-Z0-9_]+ \*/$#', $content) !== 1) {
|
Chris@0
|
140 $error = 'Inline doc block comments are not allowed; use "/* Comment */" or "// Comment" instead';
|
Chris@0
|
141 $phpcsFile->addError($error, $stackPtr, 'DocBlock');
|
Chris@0
|
142 }
|
Chris@0
|
143 }
|
Chris@0
|
144 }//end if
|
Chris@0
|
145
|
Chris@0
|
146 if ($tokens[$stackPtr]['content']{0} === '#') {
|
Chris@0
|
147 $error = 'Perl-style comments are not allowed; use "// Comment" instead';
|
Chris@0
|
148 $fix = $phpcsFile->addFixableError($error, $stackPtr, 'WrongStyle');
|
Chris@0
|
149 if ($fix === true) {
|
Chris@0
|
150 $comment = ltrim($tokens[$stackPtr]['content'], "# \t");
|
Chris@0
|
151 $phpcsFile->fixer->replaceToken($stackPtr, "// $comment");
|
Chris@0
|
152 }
|
Chris@0
|
153 }
|
Chris@0
|
154
|
Chris@0
|
155 // We don't want end of block comments. If the last comment is a closing
|
Chris@0
|
156 // curly brace.
|
Chris@0
|
157 $previousContent = $phpcsFile->findPrevious(T_WHITESPACE, ($stackPtr - 1), null, true);
|
Chris@0
|
158 if ($tokens[$previousContent]['line'] === $tokens[$stackPtr]['line']) {
|
Chris@0
|
159 if ($tokens[$previousContent]['code'] === T_CLOSE_CURLY_BRACKET) {
|
Chris@0
|
160 return;
|
Chris@0
|
161 }
|
Chris@0
|
162
|
Chris@0
|
163 // Special case for JS files.
|
Chris@0
|
164 if ($tokens[$previousContent]['code'] === T_COMMA
|
Chris@0
|
165 || $tokens[$previousContent]['code'] === T_SEMICOLON
|
Chris@0
|
166 ) {
|
Chris@0
|
167 $lastContent = $phpcsFile->findPrevious(T_WHITESPACE, ($previousContent - 1), null, true);
|
Chris@0
|
168 if ($tokens[$lastContent]['code'] === T_CLOSE_CURLY_BRACKET) {
|
Chris@0
|
169 return;
|
Chris@0
|
170 }
|
Chris@0
|
171 }
|
Chris@0
|
172 }
|
Chris@0
|
173
|
Chris@0
|
174 $comment = rtrim($tokens[$stackPtr]['content']);
|
Chris@0
|
175
|
Chris@0
|
176 // Only want inline comments.
|
Chris@0
|
177 if (substr($comment, 0, 2) !== '//') {
|
Chris@0
|
178 return;
|
Chris@0
|
179 }
|
Chris@0
|
180
|
Chris@0
|
181 // Ignore code example lines.
|
Chris@0
|
182 if ($this->isInCodeExample($phpcsFile, $stackPtr) === true) {
|
Chris@0
|
183 return;
|
Chris@0
|
184 }
|
Chris@0
|
185
|
Chris@0
|
186 // Verify the indentation of this comment line.
|
Chris@0
|
187 $this->processIndentation($phpcsFile, $stackPtr);
|
Chris@0
|
188
|
Chris@0
|
189 // If the current line starts with a tag such as "@see" then the trailing dot
|
Chris@0
|
190 // rules and upper case start rules don't apply.
|
Chris@0
|
191 if (strpos(trim(substr($tokens[$stackPtr]['content'], 2)), '@') === 0) {
|
Chris@0
|
192 return;
|
Chris@0
|
193 }
|
Chris@0
|
194
|
Chris@0
|
195 // The below section determines if a comment block is correctly capitalised,
|
Chris@0
|
196 // and ends in a full-stop. It will find the last comment in a block, and
|
Chris@0
|
197 // work its way up.
|
Chris@0
|
198 $nextComment = $phpcsFile->findNext(array(T_COMMENT), ($stackPtr + 1), null, false);
|
Chris@0
|
199 if (($nextComment !== false)
|
Chris@0
|
200 && (($tokens[$nextComment]['line']) === ($tokens[$stackPtr]['line'] + 1))
|
Chris@0
|
201 // A tag such as @todo means a separate comment block.
|
Chris@0
|
202 && strpos(trim(substr($tokens[$nextComment]['content'], 2)), '@') !== 0
|
Chris@0
|
203 ) {
|
Chris@0
|
204 return;
|
Chris@0
|
205 }
|
Chris@0
|
206
|
Chris@0
|
207 $topComment = $stackPtr;
|
Chris@0
|
208 $lastComment = $stackPtr;
|
Chris@0
|
209 while (($topComment = $phpcsFile->findPrevious(array(T_COMMENT), ($lastComment - 1), null, false)) !== false) {
|
Chris@0
|
210 if ($tokens[$topComment]['line'] !== ($tokens[$lastComment]['line'] - 1)) {
|
Chris@0
|
211 break;
|
Chris@0
|
212 }
|
Chris@0
|
213
|
Chris@0
|
214 $lastComment = $topComment;
|
Chris@0
|
215 }
|
Chris@0
|
216
|
Chris@0
|
217 $topComment = $lastComment;
|
Chris@0
|
218 $commentText = '';
|
Chris@0
|
219
|
Chris@0
|
220 for ($i = $topComment; $i <= $stackPtr; $i++) {
|
Chris@0
|
221 if ($tokens[$i]['code'] === T_COMMENT) {
|
Chris@0
|
222 $commentText .= trim(substr($tokens[$i]['content'], 2));
|
Chris@0
|
223 }
|
Chris@0
|
224 }
|
Chris@0
|
225
|
Chris@0
|
226 if ($commentText === '') {
|
Chris@0
|
227 $error = 'Blank comments are not allowed';
|
Chris@0
|
228 $fix = $phpcsFile->addFixableError($error, $stackPtr, 'Empty');
|
Chris@0
|
229 if ($fix === true) {
|
Chris@0
|
230 $phpcsFile->fixer->replaceToken($stackPtr, '');
|
Chris@0
|
231 }
|
Chris@0
|
232
|
Chris@0
|
233 return;
|
Chris@0
|
234 }
|
Chris@0
|
235
|
Chris@0
|
236 $words = preg_split('/\s+/', $commentText);
|
Chris@0
|
237 if (preg_match('|\p{Lu}|u', $commentText[0]) === 0 && $commentText[0] !== '@') {
|
Chris@0
|
238 // Allow special lower cased words that contain non-alpha characters
|
Chris@0
|
239 // (function references, machine names with underscores etc.).
|
Chris@0
|
240 $matches = array();
|
Chris@0
|
241 preg_match('/[a-z]+/', $words[0], $matches);
|
Chris@0
|
242 if (isset($matches[0]) === true && $matches[0] === $words[0]) {
|
Chris@0
|
243 $error = 'Inline comments must start with a capital letter';
|
Chris@0
|
244 $fix = $phpcsFile->addFixableError($error, $topComment, 'NotCapital');
|
Chris@0
|
245 if ($fix === true) {
|
Chris@0
|
246 $newComment = preg_replace("/$words[0]/", ucfirst($words[0]), $tokens[$topComment]['content'], 1);
|
Chris@0
|
247 $phpcsFile->fixer->replaceToken($topComment, $newComment);
|
Chris@0
|
248 }
|
Chris@0
|
249 }
|
Chris@0
|
250 }
|
Chris@0
|
251
|
Chris@0
|
252 $commentCloser = $commentText[(strlen($commentText) - 1)];
|
Chris@0
|
253 $acceptedClosers = array(
|
Chris@0
|
254 'full-stops' => '.',
|
Chris@0
|
255 'exclamation marks' => '!',
|
Chris@0
|
256 'colons' => ':',
|
Chris@0
|
257 'question marks' => '?',
|
Chris@0
|
258 'or closing parentheses' => ')',
|
Chris@0
|
259 );
|
Chris@0
|
260
|
Chris@0
|
261 // Allow @tag style comments without punctuation.
|
Chris@0
|
262 if (in_array($commentCloser, $acceptedClosers) === false && $commentText[0] !== '@') {
|
Chris@0
|
263 // Allow special last words like URLs or function references
|
Chris@0
|
264 // without punctuation.
|
Chris@0
|
265 $lastWord = $words[(count($words) - 1)];
|
Chris@0
|
266 $matches = array();
|
Chris@0
|
267 preg_match('/https?:\/\/.+/', $lastWord, $matches);
|
Chris@0
|
268 $isUrl = isset($matches[0]) === true;
|
Chris@0
|
269 preg_match('/[$a-zA-Z_]+\([$a-zA-Z_]*\)/', $lastWord, $matches);
|
Chris@0
|
270 $isFunction = isset($matches[0]) === true;
|
Chris@0
|
271
|
Chris@0
|
272 // Also allow closing tags like @endlink or @endcode.
|
Chris@0
|
273 $isEndTag = $lastWord[0] === '@';
|
Chris@0
|
274
|
Chris@0
|
275 if ($isUrl === false && $isFunction === false && $isEndTag === false) {
|
Chris@0
|
276 $error = 'Inline comments must end in %s';
|
Chris@0
|
277 $ender = '';
|
Chris@0
|
278 foreach ($acceptedClosers as $closerName => $symbol) {
|
Chris@0
|
279 $ender .= ' '.$closerName.',';
|
Chris@0
|
280 }
|
Chris@0
|
281
|
Chris@0
|
282 $ender = trim($ender, ' ,');
|
Chris@0
|
283 $data = array($ender);
|
Chris@0
|
284 $fix = $phpcsFile->addFixableError($error, $stackPtr, 'InvalidEndChar', $data);
|
Chris@0
|
285 if ($fix === true) {
|
Chris@0
|
286 $newContent = preg_replace('/(\s+)$/', '.$1', $tokens[$stackPtr]['content']);
|
Chris@0
|
287 $phpcsFile->fixer->replaceToken($stackPtr, $newContent);
|
Chris@0
|
288 }
|
Chris@0
|
289 }
|
Chris@0
|
290 }//end if
|
Chris@0
|
291
|
Chris@0
|
292 // Finally, the line below the last comment cannot be empty if this inline
|
Chris@0
|
293 // comment is on a line by itself.
|
Chris@0
|
294 if ($tokens[$previousContent]['line'] < $tokens[$stackPtr]['line'] && ($stackPtr + 1) < $phpcsFile->numTokens) {
|
Chris@0
|
295 for ($i = ($stackPtr + 1); $i < $phpcsFile->numTokens; $i++) {
|
Chris@0
|
296 if ($tokens[$i]['line'] === ($tokens[$stackPtr]['line'] + 1)) {
|
Chris@0
|
297 if ($tokens[$i]['code'] !== T_WHITESPACE || $i === ($phpcsFile->numTokens - 1)) {
|
Chris@0
|
298 return;
|
Chris@0
|
299 }
|
Chris@0
|
300 } else if ($tokens[$i]['line'] > ($tokens[$stackPtr]['line'] + 1)) {
|
Chris@0
|
301 break;
|
Chris@0
|
302 }
|
Chris@0
|
303 }
|
Chris@0
|
304
|
Chris@0
|
305 $warning = 'There must be no blank line following an inline comment';
|
Chris@0
|
306 $fix = $phpcsFile->addFixableWarning($warning, $stackPtr, 'SpacingAfter');
|
Chris@0
|
307 if ($fix === true) {
|
Chris@0
|
308 $next = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), null, true);
|
Chris@0
|
309 if ($next === ($phpcsFile->numTokens - 1)) {
|
Chris@0
|
310 return;
|
Chris@0
|
311 }
|
Chris@0
|
312
|
Chris@0
|
313 $phpcsFile->fixer->beginChangeset();
|
Chris@0
|
314 for ($i = ($stackPtr + 1); $i < $next; $i++) {
|
Chris@0
|
315 if ($tokens[$i]['line'] === $tokens[$next]['line']) {
|
Chris@0
|
316 break;
|
Chris@0
|
317 }
|
Chris@0
|
318
|
Chris@0
|
319 $phpcsFile->fixer->replaceToken($i, '');
|
Chris@0
|
320 }
|
Chris@0
|
321
|
Chris@0
|
322 $phpcsFile->fixer->endChangeset();
|
Chris@0
|
323 }
|
Chris@0
|
324 }//end if
|
Chris@0
|
325
|
Chris@0
|
326 }//end process()
|
Chris@0
|
327
|
Chris@0
|
328
|
Chris@0
|
329 /**
|
Chris@0
|
330 * Determines if a comment line is part of an @code/@endcode example.
|
Chris@0
|
331 *
|
Chris@17
|
332 * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
|
Chris@17
|
333 * @param int $stackPtr The position of the current token
|
Chris@17
|
334 * in the stack passed in $tokens.
|
Chris@0
|
335 *
|
Chris@0
|
336 * @return boolean Returns true if the comment line is within a @code block,
|
Chris@0
|
337 * false otherwise.
|
Chris@0
|
338 */
|
Chris@17
|
339 protected function isInCodeExample(File $phpcsFile, $stackPtr)
|
Chris@0
|
340 {
|
Chris@0
|
341 $tokens = $phpcsFile->getTokens();
|
Chris@0
|
342 $prevComment = $stackPtr;
|
Chris@0
|
343 $lastComment = $stackPtr;
|
Chris@0
|
344 while (($prevComment = $phpcsFile->findPrevious(array(T_COMMENT), ($lastComment - 1), null, false)) !== false) {
|
Chris@0
|
345 if ($tokens[$prevComment]['line'] !== ($tokens[$lastComment]['line'] - 1)) {
|
Chris@0
|
346 return false;
|
Chris@0
|
347 }
|
Chris@0
|
348
|
Chris@0
|
349 if ($tokens[$prevComment]['content'] === '// @code'.$phpcsFile->eolChar) {
|
Chris@0
|
350 return true;
|
Chris@0
|
351 }
|
Chris@0
|
352
|
Chris@0
|
353 if ($tokens[$prevComment]['content'] === '// @endcode'.$phpcsFile->eolChar) {
|
Chris@0
|
354 return false;
|
Chris@0
|
355 }
|
Chris@0
|
356
|
Chris@0
|
357 $lastComment = $prevComment;
|
Chris@0
|
358 }
|
Chris@0
|
359
|
Chris@0
|
360 return false;
|
Chris@0
|
361
|
Chris@0
|
362 }//end isInCodeExample()
|
Chris@0
|
363
|
Chris@0
|
364
|
Chris@0
|
365 /**
|
Chris@0
|
366 * Checks the indentation level of the comment contents.
|
Chris@0
|
367 *
|
Chris@17
|
368 * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
|
Chris@17
|
369 * @param int $stackPtr The position of the current token
|
Chris@17
|
370 * in the stack passed in $tokens.
|
Chris@0
|
371 *
|
Chris@0
|
372 * @return void
|
Chris@0
|
373 */
|
Chris@17
|
374 protected function processIndentation(File $phpcsFile, $stackPtr)
|
Chris@0
|
375 {
|
Chris@0
|
376 $tokens = $phpcsFile->getTokens();
|
Chris@0
|
377 $comment = rtrim($tokens[$stackPtr]['content']);
|
Chris@0
|
378 $spaceCount = 0;
|
Chris@0
|
379 $tabFound = false;
|
Chris@0
|
380
|
Chris@0
|
381 $commentLength = strlen($comment);
|
Chris@0
|
382 for ($i = 2; $i < $commentLength; $i++) {
|
Chris@0
|
383 if ($comment[$i] === "\t") {
|
Chris@0
|
384 $tabFound = true;
|
Chris@0
|
385 break;
|
Chris@0
|
386 }
|
Chris@0
|
387
|
Chris@0
|
388 if ($comment[$i] !== ' ') {
|
Chris@0
|
389 break;
|
Chris@0
|
390 }
|
Chris@0
|
391
|
Chris@0
|
392 $spaceCount++;
|
Chris@0
|
393 }
|
Chris@0
|
394
|
Chris@0
|
395 $fix = false;
|
Chris@0
|
396 if ($tabFound === true) {
|
Chris@0
|
397 $error = 'Tab found before comment text; expected "// %s" but found "%s"';
|
Chris@0
|
398 $data = array(
|
Chris@0
|
399 ltrim(substr($comment, 2)),
|
Chris@0
|
400 $comment,
|
Chris@0
|
401 );
|
Chris@0
|
402 $fix = $phpcsFile->addFixableError($error, $stackPtr, 'TabBefore', $data);
|
Chris@0
|
403 } else if ($spaceCount === 0 && strlen($comment) > 2) {
|
Chris@0
|
404 $error = 'No space found before comment text; expected "// %s" but found "%s"';
|
Chris@0
|
405 $data = array(
|
Chris@0
|
406 substr($comment, 2),
|
Chris@0
|
407 $comment,
|
Chris@0
|
408 );
|
Chris@0
|
409 $fix = $phpcsFile->addFixableError($error, $stackPtr, 'NoSpaceBefore', $data);
|
Chris@0
|
410 }//end if
|
Chris@0
|
411
|
Chris@0
|
412 if ($fix === true) {
|
Chris@0
|
413 $newComment = '// '.ltrim($tokens[$stackPtr]['content'], "/\t ");
|
Chris@0
|
414 $phpcsFile->fixer->replaceToken($stackPtr, $newComment);
|
Chris@0
|
415 }
|
Chris@0
|
416
|
Chris@0
|
417 if ($spaceCount > 1) {
|
Chris@0
|
418 // Check if there is a comment on the previous line that justifies the
|
Chris@0
|
419 // indentation.
|
Chris@0
|
420 $prevComment = $phpcsFile->findPrevious(array(T_COMMENT), ($stackPtr - 1), null, false);
|
Chris@0
|
421 if (($prevComment !== false) && (($tokens[$prevComment]['line']) === ($tokens[$stackPtr]['line'] - 1))) {
|
Chris@0
|
422 $prevCommentText = rtrim($tokens[$prevComment]['content']);
|
Chris@0
|
423 $prevSpaceCount = 0;
|
Chris@0
|
424 for ($i = 2; $i < strlen($prevCommentText); $i++) {
|
Chris@0
|
425 if ($prevCommentText[$i] !== ' ') {
|
Chris@0
|
426 break;
|
Chris@0
|
427 }
|
Chris@0
|
428
|
Chris@0
|
429 $prevSpaceCount++;
|
Chris@0
|
430 }
|
Chris@0
|
431
|
Chris@0
|
432 if ($spaceCount > $prevSpaceCount && $prevSpaceCount > 0) {
|
Chris@0
|
433 // A previous comment could be a list item or @todo.
|
Chris@0
|
434 $indentationStarters = array(
|
Chris@0
|
435 '-',
|
Chris@0
|
436 '@todo',
|
Chris@0
|
437 );
|
Chris@0
|
438 $words = preg_split('/\s+/', $prevCommentText);
|
Chris@0
|
439 $numberedList = (bool) preg_match('/^[0-9]+\./', $words[1]);
|
Chris@0
|
440 if (in_array($words[1], $indentationStarters) === true) {
|
Chris@0
|
441 if ($spaceCount !== ($prevSpaceCount + 2)) {
|
Chris@0
|
442 $error = 'Comment indentation error after %s element, expected %s spaces';
|
Chris@0
|
443 $fix = $phpcsFile->addFixableError($error, $stackPtr, 'SpacingBefore', array($words[1], $prevSpaceCount + 2));
|
Chris@0
|
444 if ($fix === true) {
|
Chris@0
|
445 $newComment = '//'.str_repeat(' ', ($prevSpaceCount + 2)).ltrim($tokens[$stackPtr]['content'], "/\t ");
|
Chris@0
|
446 $phpcsFile->fixer->replaceToken($stackPtr, $newComment);
|
Chris@0
|
447 }
|
Chris@0
|
448 }
|
Chris@0
|
449 } else if ($numberedList === true) {
|
Chris@0
|
450 $expectedSpaceCount = ($prevSpaceCount + strlen($words[1]) + 1);
|
Chris@0
|
451 if ($spaceCount !== $expectedSpaceCount) {
|
Chris@0
|
452 $error = 'Comment indentation error, expected %s spaces';
|
Chris@0
|
453 $fix = $phpcsFile->addFixableError($error, $stackPtr, 'SpacingBefore', array($expectedSpaceCount));
|
Chris@0
|
454 if ($fix === true) {
|
Chris@0
|
455 $newComment = '//'.str_repeat(' ', $expectedSpaceCount).ltrim($tokens[$stackPtr]['content'], "/\t ");
|
Chris@0
|
456 $phpcsFile->fixer->replaceToken($stackPtr, $newComment);
|
Chris@0
|
457 }
|
Chris@0
|
458 }
|
Chris@0
|
459 } else {
|
Chris@0
|
460 $error = 'Comment indentation error, expected only %s spaces';
|
Chris@0
|
461 $phpcsFile->addError($error, $stackPtr, 'SpacingBefore', array($prevSpaceCount));
|
Chris@0
|
462 }//end if
|
Chris@0
|
463 }//end if
|
Chris@0
|
464 } else {
|
Chris@0
|
465 $error = '%s spaces found before inline comment; expected "// %s" but found "%s"';
|
Chris@0
|
466 $data = array(
|
Chris@0
|
467 $spaceCount,
|
Chris@0
|
468 substr($comment, (2 + $spaceCount)),
|
Chris@0
|
469 $comment,
|
Chris@0
|
470 );
|
Chris@0
|
471 $fix = $phpcsFile->addFixableError($error, $stackPtr, 'SpacingBefore', $data);
|
Chris@0
|
472 if ($fix === true) {
|
Chris@0
|
473 $phpcsFile->fixer->replaceToken($stackPtr, '// '.substr($comment, (2 + $spaceCount)).$phpcsFile->eolChar);
|
Chris@0
|
474 }
|
Chris@0
|
475 }//end if
|
Chris@0
|
476 }//end if
|
Chris@0
|
477
|
Chris@0
|
478 }//end processIndentation()
|
Chris@0
|
479
|
Chris@0
|
480
|
Chris@0
|
481 }//end class
|