diff --git a/src/Search/Comb/Comb.php b/src/Search/Comb/Comb.php index d51c652f3b7..e716ec3a227 100644 --- a/src/Search/Comb/Comb.php +++ b/src/Search/Comb/Comb.php @@ -1077,13 +1077,13 @@ private function extractSnippets($value, $chunks) [, $before, $chunk, $after] = $match; $before = $surplus.$before; $surplus = ''; - $half = floor(($length - Str::length($chunk)) / 2); + $half = max(0, floor(($length - Str::length($chunk)) / 2)); if (Str::length($after) < $half) { $snippet = $chunk.$after; - $snippet = Str::safeTruncateReverse($before, $length - Str::length($snippet)).$snippet; + $snippet = Str::safeTruncateReverse($before, max(0, $length - Str::length($snippet))).$snippet; } else { $snippet = Str::safeTruncateReverse($before, $half).$chunk; - $trimmed = Str::safeTruncate($after, $length - Str::length($snippet)); + $trimmed = Str::safeTruncate($after, max(0, $length - Str::length($snippet))); $surplus = Str::substr($after, Str::length($trimmed)); $snippet = $snippet.$trimmed; } diff --git a/tests/Search/CombTest.php b/tests/Search/CombTest.php index 44584920740..076cf241b70 100644 --- a/tests/Search/CombTest.php +++ b/tests/Search/CombTest.php @@ -202,6 +202,36 @@ public function it_extracts_snippets_from_a_bard_field() $this->assertEquals($expected, collect($results['data'] ?? [])->pluck('snippets.content')->all()); } + #[Test] + public function it_extracts_snippets_when_the_term_is_longer_than_the_snippet_length() + { + // https://github.com/statamic/cms/issues/12951 + $content = <<<'EOT' + We know, it was a long wait, but now we finally have it, support for OpenID + Connect front and back-channel logout. The backchannel_logout_session_required + flag can be set on a client. See backchannel_logout_uri too. The + frontchannel_logout_session_required flag is the front-channel equivalent, and + backchannel_logout_session_required appears once more right here. + EOT; + + $comb = new Comb([ + ['content' => $content], + ], ['snippet_length' => 30]); + + try { + $results = $comb->lookUp('backchannel_logout_session_required'); + } catch (NoResultsFound $e) { + $results = []; + } + + $expected = [[ + 'backchannel_logout_session_required', + 'backchannel_logout_session_required', + ]]; + + $this->assertEquals($expected, collect($results['data'] ?? [])->pluck('snippets.content')->all()); + } + #[Test] public function it_can_search_for_plus_signs() {