Skip to content
Open
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
2 changes: 1 addition & 1 deletion src/Checkers/FileSystemChecker.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public function check(array $parameters): array
$misspelling,
$fileOrDirectory->getRealPath(),
0,
), $this->spellchecker->check($name),
), $this->spellchecker->check($name, $fileOrDirectory->getRealPath()),
);

$issues = [
Expand Down
2 changes: 1 addition & 1 deletion src/Checkers/SourceCodeChecker.php
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ private function getIssuesFromSourceFile(SplFileInfo $file): array
$misspelling,
$file->getRealPath(),
$this->getErrorLine($file, $name),
), $this->spellchecker->check(SpellcheckFormatter::format($name))),
), $this->spellchecker->check(SpellcheckFormatter::format($name), $file->getRealPath())),
];
}

Expand Down
45 changes: 40 additions & 5 deletions src/Config.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,20 @@ final class Config
*
* @param array<int, string> $whitelistedWords
* @param array<int, string> $whitelistedPaths
* @param array<string, array<int, string>> $fileSpecificIgnores
*/
public function __construct(
public array $whitelistedWords = [],
public array $whitelistedPaths = [],
public array $fileSpecificIgnores = [],
public ?string $preset = null,
public ?string $language = null,
) {
$this->whitelistedWords = array_map(strtolower(...), $whitelistedWords);
$this->fileSpecificIgnores = array_map(
fn (array $words): array => array_map(strtolower(...), $words),
$fileSpecificIgnores
);
}

/**
Expand Down Expand Up @@ -96,7 +102,8 @@ public static function instance(): self
* language?: string,
* ignore?: array{
* words?: array<int, string>,
* paths?: array<int, string>
* paths?: array<int, string>,
* files?: array<string, array<int, string>>
* }
* } $jsonAsArray
*/
Expand All @@ -105,6 +112,7 @@ public static function instance(): self
return self::$instance = new self(
$jsonAsArray['ignore']['words'] ?? [],
$jsonAsArray['ignore']['paths'] ?? [],
$jsonAsArray['ignore']['files'] ?? [],
$jsonAsArray['preset'] ?? null,
$jsonAsArray['language'] ?? null,
);
Expand Down Expand Up @@ -152,14 +160,40 @@ public function ignoreWords(array $words): void
}

/**
* Checks if the word is ignored.
* Checks if the word is ignored globally or for a specific file.
*/
public function isWordIgnored(string $word): bool
public function isWordIgnored(string $word, ?string $filePath = null): bool
{
return in_array(strtolower($word), [
$word = strtolower($word);

// Check global ignores
$globalIgnores = [
...$this->whitelistedWords,
...array_map(strtolower(...), PresetProvider::whitelistedWords($this->preset)),
]);
];

if (in_array($word, $globalIgnores)) {
return true;
}

// Check file-specific ignores
if ($filePath !== null) {
$projectPath = ProjectPath::get();

// Normalize the file path to be relative to project root
$normalizedFilePath = $filePath;
if (str_starts_with($filePath, $projectPath.'/')) {
$normalizedFilePath = substr($filePath, strlen($projectPath) + 1);
}

foreach ($this->fileSpecificIgnores as $path => $words) {
if ($normalizedFilePath === $path && in_array($word, $words)) {
return true;
}
}
}

return false;
}

/**
Expand All @@ -183,6 +217,7 @@ private function persist(): void
'ignore' => [
'words' => $this->whitelistedWords,
'paths' => $this->whitelistedPaths,
'files' => $this->fileSpecificIgnores,
],
], JSON_PRETTY_PRINT));
}
Expand Down
2 changes: 1 addition & 1 deletion src/Contracts/Services/Spellchecker.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@ interface Spellchecker
*
* @return array<int, Misspelling>
*/
public function check(string $text): array;
public function check(string $text, ?string $filePath = null): array;
}
4 changes: 2 additions & 2 deletions src/Services/Spellcheckers/Aspell.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public static function default(): self
*
* @return array<int, Misspelling>
*/
public function check(string $text): array
public function check(string $text, ?string $filePath = null): array
{
/** @var array<int, Misspelling>|null $misspellings */
$misspellings = $this->cache->has($text) ? $this->cache->get($text) : $this->getMisspellings($text);
Expand All @@ -54,7 +54,7 @@ public function check(string $text): array

return array_filter(
$misspellings,
fn (Misspelling $misspelling): bool => ! $this->config->isWordIgnored($misspelling->word),
fn (Misspelling $misspelling): bool => ! $this->config->isWordIgnored($misspelling->word, $filePath),
);
}

Expand Down
7 changes: 4 additions & 3 deletions tests/Unit/ConfigTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
'php',
])->and($config->whitelistedPaths)->toBe([
'tests',
]);
])->and($config->fileSpecificIgnores)->toBe([]);
});

it('should to be a singleton', function (): void {
Expand All @@ -30,7 +30,8 @@
$config = Config::instance();

expect($config->whitelistedWords)->toBe([])
->and($config->whitelistedPaths)->toBe([]);
->and($config->whitelistedPaths)->toBe([])
->and($config->fileSpecificIgnores)->toBe([]);
});

it('should be able to create a peck.json config file', function (): void {
Expand Down Expand Up @@ -58,7 +59,7 @@
])
->and($config->whitelistedPaths)->toBe([
'tests',
]);
])->and($config->fileSpecificIgnores)->toBe([]);
});

describe('language', function (): void {
Expand Down
50 changes: 50 additions & 0 deletions tests/Unit/Services/AspellTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -128,3 +128,53 @@
->and($misspellings[0]->word)->toBe('Xxxxxxxxxxxxxxxxxx')
->and($misspellings[0]->suggestions)->toBeEmpty();
});

it('ignores words for specific files', function (): void {
$config = new Config([], [], [
'src/Test.php' => ['testword'],
'tests/SomeTest.php' => ['unittest'],
]);
$cache = new Cache(__DIR__.'/../../.peck-test.cache');
$spellchecker = new Aspell($config, $cache);

// Should ignore 'testword' in src/Test.php
$issues = $spellchecker->check('testword', 'src/Test.php');
expect($issues)->toBeEmpty();

// Should not ignore 'testword' in other files
$issues = $spellchecker->check('testword', 'src/Other.php');
expect($issues)->toHaveCount(1)
->and($issues[0]->word)->toBe('testword');

// Should ignore 'unittest' in the specific test file
$issues = $spellchecker->check('unittest', 'tests/SomeTest.php');
expect($issues)->toBeEmpty();

// Should not ignore 'unittest' in non-test files
$issues = $spellchecker->check('unittest', 'src/Service.php');
expect($issues)->toHaveCount(1)
->and($issues[0]->word)->toBe('unittest');
});

it('combines global and file-specific ignores', function (): void {
$config = new Config(['globalword'], [], [
'src/Test.php' => ['specificword'],
]);
$cache = new Cache(__DIR__.'/../../.peck-test.cache');
$spellchecker = new Aspell($config, $cache);

// Global word should be ignored everywhere
$issues = $spellchecker->check('globalword', 'src/Test.php');
expect($issues)->toBeEmpty();

$issues = $spellchecker->check('globalword', 'src/Other.php');
expect($issues)->toBeEmpty();

// File-specific word should only be ignored in that file
$issues = $spellchecker->check('specificword', 'src/Test.php');
expect($issues)->toBeEmpty();

$issues = $spellchecker->check('specificword', 'src/Other.php');
expect($issues)->toHaveCount(1)
->and($issues[0]->word)->toBe('specificword');
});