Skip to content

Commit

Permalink
Add SlugRegister so IDs are not duplicated
Browse files Browse the repository at this point in the history
  • Loading branch information
aidantwoods committed May 5, 2020
1 parent b3580ef commit f84ea1b
Show file tree
Hide file tree
Showing 7 changed files with 121 additions and 13 deletions.
4 changes: 3 additions & 1 deletion src/Components/Blocks/Header.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use Erusev\Parsedown\AST\StateRenderable;
use Erusev\Parsedown\Components\Block;
use Erusev\Parsedown\Configurables\HeaderSlug;
use Erusev\Parsedown\Configurables\SlugRegister;
use Erusev\Parsedown\Configurables\StrictMode;
use Erusev\Parsedown\Html\Renderables\Element;
use Erusev\Parsedown\Parsedown;
Expand Down Expand Up @@ -98,9 +99,10 @@ public function stateRenderable()
/** @return Element */
function (State $State) {
$HeaderSlug = $State->get(HeaderSlug::class);
$Register = $State->get(SlugRegister::class);
$attributes = (
$HeaderSlug->isEnabled()
? ['id' => $HeaderSlug->transform($this->text())]
? ['id' => $HeaderSlug->transform($Register, $this->text())]
: []
);

Expand Down
4 changes: 3 additions & 1 deletion src/Components/Blocks/SetextHeader.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Erusev\Parsedown\Components\AcquisitioningBlock;
use Erusev\Parsedown\Components\Block;
use Erusev\Parsedown\Configurables\HeaderSlug;
use Erusev\Parsedown\Configurables\SlugRegister;
use Erusev\Parsedown\Html\Renderables\Element;
use Erusev\Parsedown\Parsedown;
use Erusev\Parsedown\Parsing\Context;
Expand Down Expand Up @@ -90,9 +91,10 @@ public function stateRenderable()
/** @return Element */
function (State $State) {
$HeaderSlug = $State->get(HeaderSlug::class);
$Register = $State->get(SlugRegister::class);
$attributes = (
$HeaderSlug->isEnabled()
? ['id' => $HeaderSlug->transform($this->text())]
? ['id' => $HeaderSlug->transform($Register, $this->text())]
: []
);

Expand Down
43 changes: 39 additions & 4 deletions src/Configurables/HeaderSlug.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,19 @@ final class HeaderSlug implements Configurable
/** @var \Closure(string):string */
private $slugCallback;

/** @var \Closure(string,int):string */
private $duplicationCallback;

/**
* @param bool $enabled
* @param (\Closure(string):string)|null $slugCallback
* @param (\Closure(string, int):string)|null $duplicationCallback
*/
public function __construct($enabled, $slugCallback = null)
{
public function __construct(
$enabled,
$slugCallback = null,
$duplicationCallback = null
) {
$this->enabled = $enabled;

if (! isset($slugCallback)) {
Expand All @@ -32,6 +39,14 @@ public function __construct($enabled, $slugCallback = null)
} else {
$this->slugCallback = $slugCallback;
}

if (! isset($duplicationCallback)) {
$this->duplicationCallback = function (string $slug, int $duplicateNumber): string {
return $slug . '-' . \strval($duplicateNumber-1);
};
} else {
$this->duplicationCallback = $duplicationCallback;
}
}

/** @return bool */
Expand All @@ -40,9 +55,23 @@ public function isEnabled()
return $this->enabled;
}

public function transform(string $text): string
public function transform(SlugRegister $SlugRegister, string $text): string
{
return ($this->slugCallback)($text);
$slug = ($this->slugCallback)($text);

if ($SlugRegister->slugCount($slug) > 0) {
$newSlug = ($this->duplicationCallback)($slug, $SlugRegister->mutatingIncrement($slug));

while ($SlugRegister->slugCount($newSlug) > 0) {
$newSlug = ($this->duplicationCallback)($slug, $SlugRegister->mutatingIncrement($slug));
}

return $newSlug;
}

$SlugRegister->mutatingIncrement($slug);

return $slug;
}

/** @param \Closure(string):string $slugCallback */
Expand All @@ -51,6 +80,12 @@ public static function withCallback($slugCallback): self
return new self(true, $slugCallback);
}

/** @param \Closure(string,int):string $duplicationCallback */
public static function withDuplicationCallback($duplicationCallback): self
{
return new self(true, null, $duplicationCallback);
}

/** @return self */
public static function enabled()
{
Expand Down
44 changes: 44 additions & 0 deletions src/Configurables/SlugRegister.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php

namespace Erusev\Parsedown\Configurables;

use Erusev\Parsedown\MutableConfigurable;

final class SlugRegister implements MutableConfigurable
{
/** @var array<string, int> */
private $register;

/**
* @param array<string, int> $register
*/
public function __construct(array $register = [])
{
$this->register = $register;
}

/** @return self */
public static function initial()
{
return new self;
}

public function mutatingIncrement(string $slug): int
{
if (! isset($this->register[$slug])) {
$this->register[$slug] = 0;
}

return ++$this->register[$slug];
}

public function slugCount(string $slug): int
{
return $this->register[$slug] ?? 0;
}

public function isolatedCopy(): self
{
return new self($this->register);
}
}
13 changes: 7 additions & 6 deletions tests/data/slug_heading.html
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
<h1 id="foo">foo</h1>
<h1 id="foo-bar">foo bar</h1>
<h1 id="foobar">foo_bar</h1>
<h1 id="foobar">foo+bar</h1>
<h1 id="foobar-1">foo+bar-1</h1>
<h1 id="foobar-2">foo+bar</h1>
<h1 id="2rer0ගම්මැද්ද-v-force-ඉනොවේශන්-නේෂන්-සඳහා-එවූ-නි">2rer*(0👍ගම්මැද්ද V FORCE ඉනොවේශන් නේෂන් සඳහා එවූ නි</h1>
<h2 id="foo">foo</h2>
<h2 id="foo-bar">foo bar</h2>
<h2 id="foobar">foo_bar</h2>
<h2 id="foobar">foo+bar</h2>
<h2 id="2rer0ගම්මැද්ද-v-force-ඉනොවේශන්-නේෂන්-සඳහා-එවූ-නි">2rer*(0👍ගම්මැද්ද V FORCE ඉනොවේශන් නේෂන් සඳහා එවූ නි</h2>
<h2 id="foo-1">foo</h2>
<h2 id="foo-bar-1">foo bar</h2>
<h2 id="foobar-3">foo_bar</h2>
<h2 id="foobar-4">foo+bar</h2>
<h2 id="2rer0ගම්මැද්ද-v-force-ඉනොවේශන්-නේෂන්-සඳහා-එවූ-නි-1">2rer*(0👍ගම්මැද්ද V FORCE ඉනොවේශන් නේෂන් සඳහා එවූ නි</h2>
2 changes: 2 additions & 0 deletions tests/data/slug_heading.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

# foo_bar

# foo+bar-1

# foo+bar

# 2rer*(0👍ගම්මැද්ද V FORCE ඉනොවේශන් නේෂන් සඳහා එවූ නි
Expand Down
24 changes: 23 additions & 1 deletion tests/src/Configurables/HeaderSlugTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Erusev\Parsedown\Tests\Configurables;

use Erusev\Parsedown\Configurables\HeaderSlug;
use Erusev\Parsedown\Configurables\SlugRegister;
use Erusev\Parsedown\State;
use PHPUnit\Framework\TestCase;

Expand All @@ -19,6 +20,7 @@ public function testNamedConstructor()

$this->assertSame(true, $State->get(HeaderSlug::class)->isEnabled());
}

/**
* @return void
* @throws \PHPUnit\Framework\ExpectationFailedException
Expand All @@ -32,7 +34,27 @@ public function testCustomCallback()

$this->assertSame(
'foo_bar',
$HeaderSlug->transform('foo bar')
$HeaderSlug->transform(SlugRegister::initial(), 'foo bar')
);
}

/**
* @return void
* @throws \PHPUnit\Framework\ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public function testCustomDuplicationCallback()
{
$HeaderSlug = HeaderSlug::withDuplicationCallback(function (string $t, int $n): string {
return $t . '_' . \strval($n-1);
});

$SlugRegister = new SlugRegister;
$HeaderSlug->transform($SlugRegister, 'foo bar');

$this->assertSame(
'foo-bar_1',
$HeaderSlug->transform($SlugRegister, 'foo bar')
);
}
}

0 comments on commit f84ea1b

Please sign in to comment.