Skip to content

Commit 2a5de07

Browse files
committed
Added "unbreakable punctuation" & "hellip" base typographic rules.
1 parent d62aebb commit 2a5de07

File tree

5 files changed

+128
-8
lines changed

5 files changed

+128
-8
lines changed

src/ServiceProvider.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
namespace Whitecube\Strings;
44

55
use Illuminate\Support\ServiceProvider as Provider;
6+
use Illuminate\Support\Str;
7+
use Illuminate\Support\Stringable;
68

79
class ServiceProvider extends Provider
810
{
@@ -11,12 +13,28 @@ class ServiceProvider extends Provider
1113
*/
1214
public function register()
1315
{
16+
Typography::rule(
17+
key: 'unbreakable-punctuation',
18+
regex: '/(?:(?: )|\s)+([:!?;])/',
19+
callback: fn(array $matches) => ' '.$matches[1],
20+
);
21+
Typography::rule(
22+
key: 'hellip',
23+
regex: '/(?:(?:\&\#8230\;)|(?:\&\#x2026\;)|(?:\.\.\.)|\…)/',
24+
callback: fn(array $matches) => '…',
25+
);
1426
}
1527

1628
/**
1729
* Bootstrap the application services.
1830
*/
1931
public function boot()
2032
{
33+
Str::macro('typography', function (string $value) {
34+
return (new Typography($value))->handle();
35+
});
36+
Stringable::macro('typography', function () {
37+
return new static(Str::typography($this->value));
38+
});
2139
}
2240
}

src/Typography.php

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
<?php
2+
3+
namespace Whitecube\Strings;
4+
5+
use Closure;
6+
7+
class Typography
8+
{
9+
/**
10+
* The provided string to transform.
11+
*/
12+
protected string $value;
13+
14+
/**
15+
* The provided string to transform.
16+
*/
17+
protected static array $handlers = [];
18+
19+
/**
20+
* Add a typographic rule handler.
21+
*/
22+
static public function rule(string $key, string $regex, Closure $callback): void
23+
{
24+
static::$handlers[$key] = [$regex, $callback];
25+
}
26+
27+
/**
28+
* Remove a typographic rule handler.
29+
*/
30+
static public function remove(string $key): void
31+
{
32+
if(isset(static::$handlers[$key])) {
33+
unset(static::$handlers[$key]);
34+
}
35+
}
36+
37+
/**
38+
* Remove a typographic rule handler.
39+
*/
40+
static protected function getHandlers(): array
41+
{
42+
return array_reduce(static::$handlers, function($all, $handler) {
43+
[$regex, $callback] = $handler;
44+
$all[$regex] = $callback;
45+
return $all;
46+
}, []);
47+
}
48+
49+
/**
50+
* Create a new Typography transformer instance
51+
*/
52+
public function __construct(string $value)
53+
{
54+
$this->value = $value;
55+
}
56+
57+
/**
58+
* Run the value transformations.
59+
*/
60+
public function handle(): string
61+
{
62+
return preg_replace_callback_array(
63+
static::getHandlers(),
64+
$this->value
65+
);
66+
}
67+
}

tests/Feature/TypographyTest.php

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php
2+
3+
use Whitecube\Strings\ServiceProvider;
4+
5+
it('can load ServiceProvider correctly', function() {
6+
expect(app()->providerIsLoaded(ServiceProvider::class))->toBeTrue();
7+
});
8+
9+
it('can replace breakable spaces in front of punctuation marks', function() {
10+
$cases = [
11+
'<p>This string contains a breakable exclamation ! And an unbreakable one too! Even a weird one &nbsp; ! Foo.</p>',
12+
'<p>This string contains a breakable question ? And an unbreakable one too? Even a weird one &nbsp; ? Foo.</p>',
13+
'<p>This string contains a breakable colon : And an unbreakable one too: Even a weird one &nbsp; : Foo.</p>',
14+
'<p>This string contains a breakable semicolon ; And an unbreakable one too; Even a weird one &nbsp; ; Foo.</p>',
15+
];
16+
17+
$expectations = [
18+
'<p>This string contains a breakable exclamation&nbsp;! And an unbreakable one too! Even a weird one&nbsp;! Foo.</p>',
19+
'<p>This string contains a breakable question&nbsp;? And an unbreakable one too? Even a weird one&nbsp;? Foo.</p>',
20+
'<p>This string contains a breakable colon&nbsp;: And an unbreakable one too: Even a weird one&nbsp;: Foo.</p>',
21+
'<p>This string contains a breakable semicolon&nbsp;; And an unbreakable one too; Even a weird one&nbsp;; Foo.</p>',
22+
];
23+
24+
foreach ($cases as $i => $value) {
25+
expect((string) str($value)->typography())->toBe($expectations[$i]);
26+
}
27+
});
28+
29+
it('can replace horizontal ellipsises with according HTML entity', function() {
30+
$cases = [
31+
'<p>This string&#8230; contains an &#8230; ellipsis &#8230;at multiple locations&#8230;</p>',
32+
'<p>This string&#x2026; contains an &#x2026; ellipsis &#x2026;at multiple locations&#x2026;</p>',
33+
'<p>This string... contains an ... ellipsis ...at multiple locations...</p>',
34+
'<p>This string… contains an … ellipsis …at multiple locations…</p>',
35+
];
36+
37+
$expectation = '<p>This string&hellip; contains an &hellip; ellipsis &hellip;at multiple locations&hellip;</p>';
38+
39+
foreach ($cases as $value) {
40+
expect((string) str($value)->typography())->toBe($expectation);
41+
}
42+
});

tests/Pest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
|
1212
*/
1313

14-
uses(\Whitecube\Strings\Tests\TestCase::class)->in('Unit');
14+
uses(\Whitecube\Strings\Tests\TestCase::class)->in('Feature');
1515

1616
/*
1717
|--------------------------------------------------------------------------

tests/Unit/ExampleTest.php

Lines changed: 0 additions & 7 deletions
This file was deleted.

0 commit comments

Comments
 (0)