diff --git a/src/Console/Commands/CheckCommand.php b/src/Console/Commands/CheckCommand.php index 6461b0d..e11efba 100644 --- a/src/Console/Commands/CheckCommand.php +++ b/src/Console/Commands/CheckCommand.php @@ -198,6 +198,8 @@ private function renderLineIssue(Issue $issue): void $issue, ); + $suggestionsHelperText = ($suggestions > '') ? 'Did you mean:' : 'No suggestions found.'; + render(<<
@@ -208,7 +210,7 @@ private function renderLineIssue(Issue $issue): void
- Did you mean: + {$suggestionsHelperText} {$suggestions}
@@ -272,6 +274,8 @@ private function renderLineLessIssue(Issue $issue): void $issue, ); + $suggestionsHelperText = ($suggestions > '') ? 'Did you mean:' : 'No suggestions found.'; + render(<<
@@ -282,7 +286,7 @@ private function renderLineLessIssue(Issue $issue): void
- Did you mean: + {$suggestionsHelperText} {$suggestions}
diff --git a/src/Services/Spellcheckers/Aspell.php b/src/Services/Spellcheckers/Aspell.php index f517a6d..f436ece 100644 --- a/src/Services/Spellcheckers/Aspell.php +++ b/src/Services/Spellcheckers/Aspell.php @@ -64,7 +64,7 @@ public function check(string $text): array */ private function getMisspellings(string $text): array { - $misspellings = iterator_to_array($this->run($text)); + $misspellings = $this->run($text); $this->cache->set($text, $misspellings); @@ -101,14 +101,45 @@ private function run(string $text): array $output = $process->getOutput(); - return array_values(array_map(function (string $line): Misspelling { - [$wordMetadataAsString, $suggestionsAsString] = explode(':', trim($line)); + $lines = $this->getAspellLinesFromOutput($output); + + return $this->getMisspellingsFromOutputLines($lines); + } + + /** + * Gets Aspell output lines as an array + * + * @return array + */ + private function getAspellLinesFromOutput(string $output): array + { + return array_values( + array_filter( + explode(PHP_EOL, trim($output)), fn (string $line): bool => ! str_starts_with($line, '@(#)') && $line !== '*' + ) + ); + } - $word = explode(' ', $wordMetadataAsString)[1]; - $suggestions = explode(', ', trim($suggestionsAsString)); + /** + * Maps output lines to an array of Misspellings if suggestions exist, null if no suggestions or an empty array if spelling is correct + * + * @param array $lines + * @return array + */ + private function getMisspellingsFromOutputLines(array $lines): array + { + return array_values(array_map(function (string $line): Misspelling { + if (preg_match('/#\\s(\\w*)\\sd*/', $line, $matches)) { + $word = $matches[1]; + $suggestions = []; + } else { + [$wordMetadataAsString, $suggestionsAsString] = explode(':', trim($line)); + $word = explode(' ', $wordMetadataAsString)[1]; + $suggestions = explode(', ', trim($suggestionsAsString)); + } return new Misspelling($word, $this->takeSuggestions($suggestions)); - }, array_filter(explode(PHP_EOL, $output), fn (string $line): bool => str_starts_with($line, '&')))); + }, $lines)); } /** diff --git a/tests/.pest/snapshots/Console/OutputTest/it_may_fail.snap b/tests/.pest/snapshots/Console/OutputTest/it_may_fail.snap index c8d0969..3800555 100644 --- a/tests/.pest/snapshots/Console/OutputTest/it_may_fail.snap +++ b/tests/.pest/snapshots/Console/OutputTest/it_may_fail.snap @@ -1,4 +1,4 @@ - .............⨯⨯⨯.⨯....⨯⨯.⨯⨯⨯⨯⨯⨯....⨯⨯ + .............⨯⨯⨯.⨯......⨯⨯.⨯⨯⨯⨯⨯⨯....⨯⨯⨯ Misspelling ./tests/Fixtures/FolderWithTypoos: 'typoos' ./tests/Fixtures/FolderWithTypoos @@ -185,7 +185,12 @@ ---------------------^ Did you mean: constant, constants, consonant, consent - FAIL 37 misspelling(s) found in your project. + Misspelling ./tests/Fixtures/TypoNoSuggestions/ClassWithTypoErrors.php:16:48: 'xxxxxxxxxxxxxxxxxxxx' + 16▕  public int $propertyWithTypoAndNoSuggestionsXxxxxxxxxxxxxxxxxxxx = 1; + ------------------------------------------------^ + No suggestions found. + + FAIL 38 misspelling(s) found in your project. Duration: 0.00s Hint: You may correct the misspellings individually, ignore them one by one, or ignore all of them using the peck --ignore-all option. diff --git a/tests/Fixtures/TypoNoSuggestions/ClassWithTypoErrors.php b/tests/Fixtures/TypoNoSuggestions/ClassWithTypoErrors.php new file mode 100644 index 0000000..e350200 --- /dev/null +++ b/tests/Fixtures/TypoNoSuggestions/ClassWithTypoErrors.php @@ -0,0 +1,17 @@ +toBeEmpty(); }); +it('detects issue and returns as fail when there are no suggestions', function (): void { + $checker = new SourceCodeChecker( + Config::instance(), + Aspell::default(), + ); + + $issues = $checker->check([ + 'directory' => __DIR__.'/../../Fixtures/TypoNoSuggestions', + 'onSuccess' => fn (): null => null, + 'onFailure' => fn (): null => null, + ]); + + expect($issues)->toHaveCount(1) + ->and($issues[0]->file)->toEndWith('tests/Fixtures/TypoNoSuggestions/ClassWithTypoErrors.php') + ->and($issues[0]->line)->toBe(16) + ->and($issues[0]->misspelling->word)->toBe('xxxxxxxxxxxxxxxxxxxx') + ->and($issues[0]->misspelling->suggestions)->toBe([]); + +}); + it('detects issues in the given directory of classes', function (): void { $checker = new SourceCodeChecker( Config::instance(), diff --git a/tests/Unit/KernelTest.php b/tests/Unit/KernelTest.php index f3e10d9..0981a76 100644 --- a/tests/Unit/KernelTest.php +++ b/tests/Unit/KernelTest.php @@ -13,5 +13,5 @@ 'onFailure' => fn (): null => null, ]); - expect($issues)->toHaveCount(37); + expect($issues)->toHaveCount(38); }); diff --git a/tests/Unit/Services/AspellTest.php b/tests/Unit/Services/AspellTest.php index 9560871..598c6c1 100644 --- a/tests/Unit/Services/AspellTest.php +++ b/tests/Unit/Services/AspellTest.php @@ -5,6 +5,7 @@ use Peck\Config; use Peck\Plugins\Cache; use Peck\Services\Spellcheckers\Aspell; +use Peck\ValueObjects\Misspelling; it('does not detect issues', function (): void { $spellchecker = Aspell::default(); @@ -91,3 +92,16 @@ expect($issues)->not->toBeEmpty(); }); + +it('reports a bad spelling even when there are no suggestions', function (): void { + Cache::default()->flush(); + $spellchecker = Aspell::default(); + + $issues = $spellchecker->check('ppppppppppppppooihihihihih'); + + expect($issues)->toBeArray() + ->and($issues[0]->word)->toBe('ppppppppppppppooihihihihih') + ->and($issues[0]->suggestions)->toBe([]) + ->and($issues[0])->toBeInstanceOf(Misspelling::class); + +});