Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Line length ignore for docs #128

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion moodle/Sniffs/Files/LineLengthSniff.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@

namespace MoodleHQ\MoodleCS\moodle\Sniffs\Files;

use MoodleHQ\MoodleCS\moodle\Util\Docblocks;
use PHP_CodeSniffer\Standards\Generic\Sniffs\Files\LineLengthSniff as GenericLineLengthSniff;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;

class LineLengthSniff extends GenericLineLengthSniff
Expand All @@ -40,6 +40,17 @@ public function process(File $file, $stackptr) {
if (strpos($file->getFilename(), DIRECTORY_SEPARATOR . 'lang' . DIRECTORY_SEPARATOR) !== false) {
return;
}

parent::process($file, $stackptr);
}

protected function checkLineLength($phpcsFile, $tokens, $stackPtr) {
// Ignore lines that are part of a docblock.
// We may extend this to only ignore certain tags in the future.
if (Docblocks::getStartOfCurrentDocblock($phpcsFile, $stackPtr) !== null) {
return;
}

parent::checkLineLength($phpcsFile, $tokens, $stackPtr);
}
}
75 changes: 75 additions & 0 deletions moodle/Tests/Sniffs/Files/LineLengthSniffTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<?php

// This file is part of Moodle - https://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <https://www.gnu.org/licenses/>.

namespace MoodleHQ\MoodleCS\moodle\Tests\Sniffs\Commenting;

use MoodleHQ\MoodleCS\moodle\Tests\MoodleCSBaseTestCase;

/**
* Test the MissingDocblockSniff sniff.
*
* @copyright 2024 onwards Andrew Lyons <[email protected]>
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*
* @covers \MoodleHQ\MoodleCS\moodle\Sniffs\Files\LineLengthSniff
*/
class LineLengthSniffTest extends MoodleCSBaseTestCase
{
/**
* @dataProvider fixtureProvider
*/
public function testSniffWithFixtures(
string $fixture,
?string $fixturePath,
array $errors,
array $warnings
): void {
// xdebug_break();
$this->setStandard('moodle');
$this->setSniff('moodle.Files.LineLength');
$this->setFixture(
sprintf("%s/fixtures/LineLength/%s.php", __DIR__, $fixture),
$fixturePath,
);
$this->setErrors($errors);
$this->setWarnings($warnings);

$this->verifyCsResults();
}

public static function fixtureProvider(): array {
$cases = [
[
'fixture' => 'langfile',
'fixturePath' => '/lang/en/assignfeedback_editpdf.php',
'errors' => [],
'warnings' => [],
],
[
'fixture' => 'standard',
'fixturePath' => null,
'errors' => [
13 => 'Line exceeds maximum limit of 180 characters; contains 182 characters',
],
'warnings' => [

],
],
];
return $cases;
}
}
3 changes: 3 additions & 0 deletions moodle/Tests/Sniffs/Files/fixtures/LineLength/langfile.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<?php

$string['example_of_a_very_long_string_name'] = 'This is an example of a very long string with a vey long name which combined will exceed the maximum lenth of your typical Moodle coding style';
16 changes: 16 additions & 0 deletions moodle/Tests/Sniffs/Files/fixtures/LineLength/standard.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

class Example
{
/**
* This is an example of a very long string within the docblock of a class.
*
* Checks that all actiities in the specified section are hidden. You need to be in the course page. It can be used being logged as a student and as a teacher on editing mode.
*
* @Given /^I change the name of the "(?P<activity_name_string>(?:[^"]|\\")*)" activity name to "(?P<new_name_string>(?:[^"]|\\")*)"$/
*/
public function i_change_names(): void {
// This is also a really stupidly long comment but this one is not allowed to be over long. The reason we accept long docblock strings but not long comments string is because
// docblocks are used as code.
}
}
59 changes: 45 additions & 14 deletions moodle/Util/Docblocks.php
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,15 @@ abstract class Docblocks
// Commented out: 'uses' => ['#.*/tests/.*_test.php#'], can also be out from tests (Coding style dixit).
];

/** @var int[]|string[] A list of tokens which are within a docblock */
protected static array $midDocBlockTokens = [
T_DOC_COMMENT,
T_DOC_COMMENT_STAR,
T_DOC_COMMENT_WHITESPACE,
T_DOC_COMMENT_TAG,
T_DOC_COMMENT_STRING,
];

/**
* Get the docblock for a file, class, interface, trait, or method.
*
Expand All @@ -164,38 +173,60 @@ public static function getDocBlock(
}

/**
* Get the docblock pointer for a file, class, interface, trait, or method.
* Get the start pointer for a token which is in a docblock.
*
* @param File $phpcsFile
* @param int $stackPtr
* @return null|int
* @param int $stackPtr The token to check
* @return null|int The docblock start token if the stackPtr is in a docblock, null otherwise.
*/
public static function getDocBlockPointer(
public static function getStartOfCurrentDocblock(
File $phpcsFile,
int $stackPtr
): ?int {
$tokens = $phpcsFile->getTokens();
if (!array_key_exists($stackPtr, $tokens)) {
// This _should_ not happen unless it is called incorrectly, like in the LineLength sniff.
return null; // @codeCoverageIgnore
}
$token = $tokens[$stackPtr];

// Check if the passed pointer was for a doc.
$midDocBlockTokens = [
T_DOC_COMMENT,
T_DOC_COMMENT_STAR,
T_DOC_COMMENT_WHITESPACE,
T_DOC_COMMENT_TAG,
T_DOC_COMMENT_STRING,
];
if ($token['code'] === T_DOC_COMMENT_OPEN_TAG) {
return $stackPtr;
} elseif ($token['code'] === T_DOC_COMMENT_CLOSE_TAG) {
}

if ($token['code'] === T_DOC_COMMENT_CLOSE_TAG) {
// The pointer was for a close tag. Fetch the corresponding open tag.
return $token['comment_opener'];
} elseif (in_array($token['code'], $midDocBlockTokens)) {
}

if (in_array($token['code'], self::$midDocBlockTokens)) {
// The pointer was for a token inside the docblock. Fetch the corresponding open tag.
$commentStart = $phpcsFile->findPrevious(T_DOC_COMMENT_OPEN_TAG, $stackPtr);
return $commentStart ?: null;
}

return null;
}

/**
* Get the docblock pointer for a file, class, interface, trait, or method.
*
* @param File $phpcsFile
* @param int $stackPtr
* @return null|int
*/
public static function getDocBlockPointer(
File $phpcsFile,
int $stackPtr
): ?int {
$tokens = $phpcsFile->getTokens();
$token = $tokens[$stackPtr];

// Check if the passed pointer was for a doc.
if ($docblockPtr = self::getStartOfCurrentDocblock($phpcsFile, $stackPtr)) {
return $docblockPtr;
}

// If the pointer was for a file, fetch the doc tag from the open tag.
if ($tokens[$stackPtr]['code'] === T_OPEN_TAG) {
return self::getDocTagFromOpenTag($phpcsFile, $stackPtr);
Expand Down