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