mirror of
https://github.com/erusev/parsedown.git
synced 2023-08-10 21:13:06 +03:00
Add SlugRegister so IDs are not duplicated
This commit is contained in:
parent
4e99e29d28
commit
8764512c23
@ -6,6 +6,7 @@ use Erusev\Parsedown\AST\Handler;
|
|||||||
use Erusev\Parsedown\AST\StateRenderable;
|
use Erusev\Parsedown\AST\StateRenderable;
|
||||||
use Erusev\Parsedown\Components\Block;
|
use Erusev\Parsedown\Components\Block;
|
||||||
use Erusev\Parsedown\Configurables\HeaderSlug;
|
use Erusev\Parsedown\Configurables\HeaderSlug;
|
||||||
|
use Erusev\Parsedown\Configurables\SlugRegister;
|
||||||
use Erusev\Parsedown\Configurables\StrictMode;
|
use Erusev\Parsedown\Configurables\StrictMode;
|
||||||
use Erusev\Parsedown\Html\Renderables\Element;
|
use Erusev\Parsedown\Html\Renderables\Element;
|
||||||
use Erusev\Parsedown\Parsedown;
|
use Erusev\Parsedown\Parsedown;
|
||||||
@ -98,9 +99,10 @@ final class Header implements Block
|
|||||||
/** @return Element */
|
/** @return Element */
|
||||||
function (State $State) {
|
function (State $State) {
|
||||||
$HeaderSlug = $State->get(HeaderSlug::class);
|
$HeaderSlug = $State->get(HeaderSlug::class);
|
||||||
|
$Register = $State->get(SlugRegister::class);
|
||||||
$attributes = (
|
$attributes = (
|
||||||
$HeaderSlug->isEnabled()
|
$HeaderSlug->isEnabled()
|
||||||
? ['id' => $HeaderSlug->transform($this->text())]
|
? ['id' => $HeaderSlug->transform($Register, $this->text())]
|
||||||
: []
|
: []
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ use Erusev\Parsedown\AST\StateRenderable;
|
|||||||
use Erusev\Parsedown\Components\AcquisitioningBlock;
|
use Erusev\Parsedown\Components\AcquisitioningBlock;
|
||||||
use Erusev\Parsedown\Components\Block;
|
use Erusev\Parsedown\Components\Block;
|
||||||
use Erusev\Parsedown\Configurables\HeaderSlug;
|
use Erusev\Parsedown\Configurables\HeaderSlug;
|
||||||
|
use Erusev\Parsedown\Configurables\SlugRegister;
|
||||||
use Erusev\Parsedown\Html\Renderables\Element;
|
use Erusev\Parsedown\Html\Renderables\Element;
|
||||||
use Erusev\Parsedown\Parsedown;
|
use Erusev\Parsedown\Parsedown;
|
||||||
use Erusev\Parsedown\Parsing\Context;
|
use Erusev\Parsedown\Parsing\Context;
|
||||||
@ -90,9 +91,10 @@ final class SetextHeader implements AcquisitioningBlock
|
|||||||
/** @return Element */
|
/** @return Element */
|
||||||
function (State $State) {
|
function (State $State) {
|
||||||
$HeaderSlug = $State->get(HeaderSlug::class);
|
$HeaderSlug = $State->get(HeaderSlug::class);
|
||||||
|
$Register = $State->get(SlugRegister::class);
|
||||||
$attributes = (
|
$attributes = (
|
||||||
$HeaderSlug->isEnabled()
|
$HeaderSlug->isEnabled()
|
||||||
? ['id' => $HeaderSlug->transform($this->text())]
|
? ['id' => $HeaderSlug->transform($Register, $this->text())]
|
||||||
: []
|
: []
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -12,12 +12,19 @@ final class HeaderSlug implements Configurable
|
|||||||
/** @var \Closure(string):string */
|
/** @var \Closure(string):string */
|
||||||
private $slugCallback;
|
private $slugCallback;
|
||||||
|
|
||||||
|
/** @var \Closure(string,int):string */
|
||||||
|
private $duplicationCallback;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param bool $enabled
|
* @param bool $enabled
|
||||||
* @param (\Closure(string):string)|null $slugCallback
|
* @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;
|
$this->enabled = $enabled;
|
||||||
|
|
||||||
if (! isset($slugCallback)) {
|
if (! isset($slugCallback)) {
|
||||||
@ -32,6 +39,14 @@ final class HeaderSlug implements Configurable
|
|||||||
} else {
|
} else {
|
||||||
$this->slugCallback = $slugCallback;
|
$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 */
|
/** @return bool */
|
||||||
@ -40,9 +55,23 @@ final class HeaderSlug implements Configurable
|
|||||||
return $this->enabled;
|
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 */
|
/** @param \Closure(string):string $slugCallback */
|
||||||
@ -51,6 +80,12 @@ final class HeaderSlug implements Configurable
|
|||||||
return new self(true, $slugCallback);
|
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 */
|
/** @return self */
|
||||||
public static function enabled()
|
public static function enabled()
|
||||||
{
|
{
|
||||||
|
44
src/Configurables/SlugRegister.php
Normal file
44
src/Configurables/SlugRegister.php
Normal file
@ -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);
|
||||||
|
}
|
||||||
|
}
|
@ -1,10 +1,11 @@
|
|||||||
<h1 id="foo">foo</h1>
|
<h1 id="foo">foo</h1>
|
||||||
<h1 id="foo-bar">foo bar</h1>
|
<h1 id="foo-bar">foo bar</h1>
|
||||||
<h1 id="foobar">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>
|
<h1 id="2rer0ගම්මැද්ද-v-force-ඉනොවේශන්-නේෂන්-සඳහා-එවූ-නි">2rer*(0👍ගම්මැද්ද V FORCE ඉනොවේශන් නේෂන් සඳහා එවූ නි</h1>
|
||||||
<h2 id="foo">foo</h2>
|
<h2 id="foo-1">foo</h2>
|
||||||
<h2 id="foo-bar">foo bar</h2>
|
<h2 id="foo-bar-1">foo bar</h2>
|
||||||
<h2 id="foobar">foo_bar</h2>
|
<h2 id="foobar-3">foo_bar</h2>
|
||||||
<h2 id="foobar">foo+bar</h2>
|
<h2 id="foobar-4">foo+bar</h2>
|
||||||
<h2 id="2rer0ගම්මැද්ද-v-force-ඉනොවේශන්-නේෂන්-සඳහා-එවූ-නි">2rer*(0👍ගම්මැද්ද V FORCE ඉනොවේශන් නේෂන් සඳහා එවූ නි</h2>
|
<h2 id="2rer0ගම්මැද්ද-v-force-ඉනොවේශන්-නේෂන්-සඳහා-එවූ-නි-1">2rer*(0👍ගම්මැද්ද V FORCE ඉනොවේශන් නේෂන් සඳහා එවූ නි</h2>
|
@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
# foo_bar
|
# foo_bar
|
||||||
|
|
||||||
|
# foo+bar-1
|
||||||
|
|
||||||
# foo+bar
|
# foo+bar
|
||||||
|
|
||||||
# 2rer*(0👍ගම්මැද්ද V FORCE ඉනොවේශන් නේෂන් සඳහා එවූ නි
|
# 2rer*(0👍ගම්මැද්ද V FORCE ඉනොවේශන් නේෂන් සඳහා එවූ නි
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
namespace Erusev\Parsedown\Tests\Configurables;
|
namespace Erusev\Parsedown\Tests\Configurables;
|
||||||
|
|
||||||
use Erusev\Parsedown\Configurables\HeaderSlug;
|
use Erusev\Parsedown\Configurables\HeaderSlug;
|
||||||
|
use Erusev\Parsedown\Configurables\SlugRegister;
|
||||||
use Erusev\Parsedown\State;
|
use Erusev\Parsedown\State;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
@ -19,6 +20,7 @@ final class HeaderSlugTest extends TestCase
|
|||||||
|
|
||||||
$this->assertSame(true, $State->get(HeaderSlug::class)->isEnabled());
|
$this->assertSame(true, $State->get(HeaderSlug::class)->isEnabled());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return void
|
* @return void
|
||||||
* @throws \PHPUnit\Framework\ExpectationFailedException
|
* @throws \PHPUnit\Framework\ExpectationFailedException
|
||||||
@ -32,7 +34,27 @@ final class HeaderSlugTest extends TestCase
|
|||||||
|
|
||||||
$this->assertSame(
|
$this->assertSame(
|
||||||
'foo_bar',
|
'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')
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user