diff --git a/CHANGELOG.md b/CHANGELOG.md index 7fa8c4633..e7f0dbfd0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,9 @@ Changelog ========= +Bug fixes: + - Fixing include and exclude patterns #2593 @mamazu + ## 2024-03-09 Features: @@ -51,7 +54,7 @@ Bug fixes: Documentation: - - Added Helix LSP instructions #2581 @lens0021 + - Added Helix LSP instructions #2581 @lens0021 - Fix typos in Behat #2534 @vuon9 - Fix broken external links #2500 @einenlum diff --git a/lib/Filesystem/Domain/FileList.php b/lib/Filesystem/Domain/FileList.php index b2053473d..8d2b7a76f 100644 --- a/lib/Filesystem/Domain/FileList.php +++ b/lib/Filesystem/Domain/FileList.php @@ -104,7 +104,27 @@ public function includeAndExclude(array $includePatterns = [], array $excludePat } // Sort map by keys so that more specific paths are getting matched first - krsort($inclusionMap); + uksort($inclusionMap, function (string $x, string $y) { + $partsX = explode(DIRECTORY_SEPARATOR, $x); + $partsY = explode(DIRECTORY_SEPARATOR, $y); + // Longer paths should come first + $countDiff = -(count($partsX) <=> count($partsY)); + if ($countDiff !== 0) { + return $countDiff; + } + + foreach ($partsX as $i => $pathPartX) { + if ($pathPartX === '**' || $pathPartX === '*') { + return 1; + } + + $compare = strcmp($pathPartX, $partsY[$i]); + if($compare !== 0) { + return $compare; + } + } + return 0; + }); return $this->filter(function (SplFileInfo $info) use ($inclusionMap): bool { foreach($inclusionMap as $glob => $isIncluded) { diff --git a/lib/Filesystem/Tests/Unit/Domain/FileListTest.php b/lib/Filesystem/Tests/Unit/Domain/FileListTest.php index 22fc6890b..0f2667a91 100644 --- a/lib/Filesystem/Tests/Unit/Domain/FileListTest.php +++ b/lib/Filesystem/Tests/Unit/Domain/FileListTest.php @@ -2,6 +2,7 @@ namespace Phpactor\Filesystem\Tests\Unit\Domain; +use Generator; use Phpactor\Filesystem\Domain\FileList; use Phpactor\Filesystem\Domain\FilePath; use Phpactor\Filesystem\Tests\IntegrationTestCase; @@ -197,22 +198,52 @@ public function testIncludesExcludePatterns(): void ); } - public function testExcludesWithShortFolderName(): void - { - $list = FileList::fromFilePaths([ - FilePath::fromString('/src/package/test.php'), - FilePath::fromString('/src/a/test.php'), - ])->includeAndExclude( - includePatterns: [ '/src/**/*'], - excludePatterns: [ '/src/a/*' ], + /** +@param array $fileList +@param array $includePatterns +@param array $excludePatterns +@param array $expected +* @dataProvider provideExcludesWithShortFolderName() +*/ + public function testExcludesWithShortFolderName( + array $fileList, + array $includePatterns, + array $excludePatterns, + array $expected, + ): void { + $list = FileList::fromFilePaths($fileList)->includeAndExclude( + includePatterns:$includePatterns, + excludePatterns: $excludePatterns ); - self::assertEquals( + self::assertEquals($expected, array_map(fn (FilePath $x) => (string) $x, iterator_to_array($list))); + } + + public function provideExcludesWithShortFolderName(): Generator + { + yield 'ascii file name' => [ [ FilePath::fromString('/src/package/test.php'), + FilePath::fromString('/src/a/test.php'), ], - iterator_to_array($list) - ); + [ '/src/**/*'], + [ '/src/a/*' ], + [ + '/src/package/test.php', + ], + ]; + + yield 'unicode file name' => [ + [ + FilePath::fromString('/src/package/test.php'), + FilePath::fromString('/src/ü/test.php'), + ], + [ '/src/**/*'], + [ '/src/ü/*' ], + [ + '/src/package/test.php', + ], + ]; } public function testContainingString(): void