Primo Committ

This commit is contained in:
paoloar77
2024-05-07 12:17:25 +02:00
commit e73d0e5113
7204 changed files with 884387 additions and 0 deletions

View File

@@ -0,0 +1,152 @@
<?php
/*
* This file is part of the league/commonmark package.
*
* (c) Colin O'Dell <colinodell@gmail.com>
*
* Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
* - (c) John MacFarlane
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace League\CommonMark\Delimiter;
use League\CommonMark\Inline\Element\AbstractStringContainer;
final class Delimiter implements DelimiterInterface
{
/** @var string */
private $char;
/** @var int */
private $length;
/** @var int */
private $originalLength;
/** @var AbstractStringContainer */
private $inlineNode;
/** @var DelimiterInterface|null */
private $previous;
/** @var DelimiterInterface|null */
private $next;
/** @var bool */
private $canOpen;
/** @var bool */
private $canClose;
/** @var bool */
private $active;
/** @var int|null */
private $index;
/**
* @param string $char
* @param int $numDelims
* @param AbstractStringContainer $node
* @param bool $canOpen
* @param bool $canClose
* @param int|null $index
*/
public function __construct(string $char, int $numDelims, AbstractStringContainer $node, bool $canOpen, bool $canClose, ?int $index = null)
{
$this->char = $char;
$this->length = $numDelims;
$this->originalLength = $numDelims;
$this->inlineNode = $node;
$this->canOpen = $canOpen;
$this->canClose = $canClose;
$this->active = true;
$this->index = $index;
}
public function canClose(): bool
{
return $this->canClose;
}
/**
* @param bool $canClose
*
* @return void
*/
public function setCanClose(bool $canClose)
{
$this->canClose = $canClose;
}
public function canOpen(): bool
{
return $this->canOpen;
}
public function isActive(): bool
{
return $this->active;
}
public function setActive(bool $active)
{
$this->active = $active;
}
public function getChar(): string
{
return $this->char;
}
public function getIndex(): ?int
{
return $this->index;
}
public function getNext(): ?DelimiterInterface
{
return $this->next;
}
public function setNext(?DelimiterInterface $next)
{
$this->next = $next;
}
public function getLength(): int
{
return $this->length;
}
public function setLength(int $length)
{
$this->length = $length;
}
public function getOriginalLength(): int
{
return $this->originalLength;
}
public function getInlineNode(): AbstractStringContainer
{
return $this->inlineNode;
}
public function getPrevious(): ?DelimiterInterface
{
return $this->previous;
}
public function setPrevious(?DelimiterInterface $previous): DelimiterInterface
{
$this->previous = $previous;
return $this;
}
}

View File

@@ -0,0 +1,71 @@
<?php
/*
* This file is part of the league/commonmark package.
*
* (c) Colin O'Dell <colinodell@gmail.com>
*
* Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
* - (c) John MacFarlane
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace League\CommonMark\Delimiter;
use League\CommonMark\Inline\Element\AbstractStringContainer;
interface DelimiterInterface
{
public function canClose(): bool;
public function canOpen(): bool;
public function isActive(): bool;
/**
* @param bool $active
*
* @return void
*/
public function setActive(bool $active);
/**
* @return string
*/
public function getChar(): string;
public function getIndex(): ?int;
public function getNext(): ?DelimiterInterface;
/**
* @param DelimiterInterface|null $next
*
* @return void
*/
public function setNext(?DelimiterInterface $next);
public function getLength(): int;
/**
* @param int $length
*
* @return void
*/
public function setLength(int $length);
public function getOriginalLength(): int;
public function getInlineNode(): AbstractStringContainer;
public function getPrevious(): ?DelimiterInterface;
/**
* @param DelimiterInterface|null $previous
*
* @return mixed|void
*/
public function setPrevious(?DelimiterInterface $previous);
}

View File

@@ -0,0 +1,234 @@
<?php
/*
* This file is part of the league/commonmark package.
*
* (c) Colin O'Dell <colinodell@gmail.com>
*
* Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
* - (c) John MacFarlane
*
* Additional emphasis processing code based on commonmark-java (https://github.com/atlassian/commonmark-java)
* - (c) Atlassian Pty Ltd
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace League\CommonMark\Delimiter;
use League\CommonMark\Delimiter\Processor\DelimiterProcessorCollection;
use League\CommonMark\Inline\AdjacentTextMerger;
final class DelimiterStack
{
/**
* @var DelimiterInterface|null
*/
private $top;
/**
* @param DelimiterInterface $newDelimiter
*
* @return void
*/
public function push(DelimiterInterface $newDelimiter)
{
$newDelimiter->setPrevious($this->top);
if ($this->top !== null) {
$this->top->setNext($newDelimiter);
}
$this->top = $newDelimiter;
}
private function findEarliest(DelimiterInterface $stackBottom = null): ?DelimiterInterface
{
$delimiter = $this->top;
while ($delimiter !== null && $delimiter->getPrevious() !== $stackBottom) {
$delimiter = $delimiter->getPrevious();
}
return $delimiter;
}
/**
* @param DelimiterInterface $delimiter
*
* @return void
*/
public function removeDelimiter(DelimiterInterface $delimiter)
{
if ($delimiter->getPrevious() !== null) {
$delimiter->getPrevious()->setNext($delimiter->getNext());
}
if ($delimiter->getNext() === null) {
// top of stack
$this->top = $delimiter->getPrevious();
} else {
$delimiter->getNext()->setPrevious($delimiter->getPrevious());
}
}
private function removeDelimiterAndNode(DelimiterInterface $delimiter): void
{
$delimiter->getInlineNode()->detach();
$this->removeDelimiter($delimiter);
}
private function removeDelimitersBetween(DelimiterInterface $opener, DelimiterInterface $closer): void
{
$delimiter = $closer->getPrevious();
while ($delimiter !== null && $delimiter !== $opener) {
$previous = $delimiter->getPrevious();
$this->removeDelimiter($delimiter);
$delimiter = $previous;
}
}
/**
* @param DelimiterInterface|null $stackBottom
*
* @return void
*/
public function removeAll(DelimiterInterface $stackBottom = null)
{
while ($this->top && $this->top !== $stackBottom) {
$this->removeDelimiter($this->top);
}
}
/**
* @param string $character
*
* @return void
*/
public function removeEarlierMatches(string $character)
{
$opener = $this->top;
while ($opener !== null) {
if ($opener->getChar() === $character) {
$opener->setActive(false);
}
$opener = $opener->getPrevious();
}
}
/**
* @param string|string[] $characters
*
* @return DelimiterInterface|null
*/
public function searchByCharacter($characters): ?DelimiterInterface
{
if (!\is_array($characters)) {
$characters = [$characters];
}
$opener = $this->top;
while ($opener !== null) {
if (\in_array($opener->getChar(), $characters)) {
break;
}
$opener = $opener->getPrevious();
}
return $opener;
}
/**
* @param DelimiterInterface|null $stackBottom
* @param DelimiterProcessorCollection $processors
*
* @return void
*/
public function processDelimiters(?DelimiterInterface $stackBottom, DelimiterProcessorCollection $processors)
{
$openersBottom = [];
// Find first closer above stackBottom
$closer = $this->findEarliest($stackBottom);
// Move forward, looking for closers, and handling each
while ($closer !== null) {
$delimiterChar = $closer->getChar();
$delimiterProcessor = $processors->getDelimiterProcessor($delimiterChar);
if (!$closer->canClose() || $delimiterProcessor === null) {
$closer = $closer->getNext();
continue;
}
$openingDelimiterChar = $delimiterProcessor->getOpeningCharacter();
$useDelims = 0;
$openerFound = false;
$potentialOpenerFound = false;
$opener = $closer->getPrevious();
while ($opener !== null && $opener !== $stackBottom && $opener !== ($openersBottom[$delimiterChar] ?? null)) {
if ($opener->canOpen() && $opener->getChar() === $openingDelimiterChar) {
$potentialOpenerFound = true;
$useDelims = $delimiterProcessor->getDelimiterUse($opener, $closer);
if ($useDelims > 0) {
$openerFound = true;
break;
}
}
$opener = $opener->getPrevious();
}
if (!$openerFound) {
if (!$potentialOpenerFound) {
// Only do this when we didn't even have a potential
// opener (one that matches the character and can open).
// If an opener was rejected because of the number of
// delimiters (e.g. because of the "multiple of 3"
// Set lower bound for future searches for openersrule),
// we want to consider it next time because the number
// of delimiters can change as we continue processing.
$openersBottom[$delimiterChar] = $closer->getPrevious();
if (!$closer->canOpen()) {
// We can remove a closer that can't be an opener,
// once we've seen there's no matching opener.
$this->removeDelimiter($closer);
}
}
$closer = $closer->getNext();
continue;
}
$openerNode = $opener->getInlineNode();
$closerNode = $closer->getInlineNode();
// Remove number of used delimiters from stack and inline nodes.
$opener->setLength($opener->getLength() - $useDelims);
$closer->setLength($closer->getLength() - $useDelims);
$openerNode->setContent(\substr($openerNode->getContent(), 0, -$useDelims));
$closerNode->setContent(\substr($closerNode->getContent(), 0, -$useDelims));
$this->removeDelimitersBetween($opener, $closer);
// The delimiter processor can re-parent the nodes between opener and closer,
// so make sure they're contiguous already. Exclusive because we want to keep opener/closer themselves.
AdjacentTextMerger::mergeTextNodesBetweenExclusive($openerNode, $closerNode);
$delimiterProcessor->process($openerNode, $closerNode, $useDelims);
// No delimiter characters left to process, so we can remove delimiter and the now empty node.
if ($opener->getLength() === 0) {
$this->removeDelimiterAndNode($opener);
}
if ($closer->getLength() === 0) {
$next = $closer->getNext();
$this->removeDelimiterAndNode($closer);
$closer = $next;
}
}
// Remove all delimiters
$this->removeAll($stackBottom);
}
}

View File

@@ -0,0 +1,73 @@
<?php
/*
* This file is part of the league/commonmark package.
*
* (c) Colin O'Dell <colinodell@gmail.com>
*
* Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
* - (c) John MacFarlane
*
* Additional emphasis processing code based on commonmark-java (https://github.com/atlassian/commonmark-java)
* - (c) Atlassian Pty Ltd
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace League\CommonMark\Delimiter\Processor;
final class DelimiterProcessorCollection implements DelimiterProcessorCollectionInterface
{
/** @var array<string,DelimiterProcessorInterface>|DelimiterProcessorInterface[] */
private $processorsByChar = [];
public function add(DelimiterProcessorInterface $processor)
{
$opening = $processor->getOpeningCharacter();
$closing = $processor->getClosingCharacter();
if ($opening === $closing) {
$old = $this->processorsByChar[$opening] ?? null;
if ($old !== null && $old->getOpeningCharacter() === $old->getClosingCharacter()) {
$this->addStaggeredDelimiterProcessorForChar($opening, $old, $processor);
} else {
$this->addDelimiterProcessorForChar($opening, $processor);
}
} else {
$this->addDelimiterProcessorForChar($opening, $processor);
$this->addDelimiterProcessorForChar($closing, $processor);
}
}
public function getDelimiterProcessor(string $char): ?DelimiterProcessorInterface
{
return $this->processorsByChar[$char] ?? null;
}
public function getDelimiterCharacters(): array
{
return \array_keys($this->processorsByChar);
}
private function addDelimiterProcessorForChar(string $delimiterChar, DelimiterProcessorInterface $processor): void
{
if (isset($this->processorsByChar[$delimiterChar])) {
throw new \InvalidArgumentException(\sprintf('Delim processor for character "%s" already exists', $processor->getOpeningCharacter()));
}
$this->processorsByChar[$delimiterChar] = $processor;
}
private function addStaggeredDelimiterProcessorForChar(string $opening, DelimiterProcessorInterface $old, DelimiterProcessorInterface $new): void
{
if ($old instanceof StaggeredDelimiterProcessor) {
$s = $old;
} else {
$s = new StaggeredDelimiterProcessor($opening, $old);
}
$s->add($new);
$this->processorsByChar[$opening] = $s;
}
}

View File

@@ -0,0 +1,48 @@
<?php
/*
* This file is part of the league/commonmark package.
*
* (c) Colin O'Dell <colinodell@gmail.com>
*
* Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
* - (c) John MacFarlane
*
* Additional emphasis processing code based on commonmark-java (https://github.com/atlassian/commonmark-java)
* - (c) Atlassian Pty Ltd
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace League\CommonMark\Delimiter\Processor;
interface DelimiterProcessorCollectionInterface
{
/**
* Add the given delim processor to the collection
*
* @param DelimiterProcessorInterface $processor The delim processor to add
*
* @throws \InvalidArgumentException Exception will be thrown if attempting to add multiple processors for the same character
*
* @return void
*/
public function add(DelimiterProcessorInterface $processor);
/**
* Returns the delim processor which handles the given character if one exists
*
* @param string $char
*
* @return DelimiterProcessorInterface|null
*/
public function getDelimiterProcessor(string $char): ?DelimiterProcessorInterface;
/**
* Returns an array of delimiter characters who have associated processors
*
* @return string[]
*/
public function getDelimiterCharacters(): array;
}

View File

@@ -0,0 +1,86 @@
<?php
/*
* This file is part of the league/commonmark package.
*
* (c) Colin O'Dell <colinodell@gmail.com>
*
* Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
* - (c) John MacFarlane
*
* Additional emphasis processing code based on commonmark-java (https://github.com/atlassian/commonmark-java)
* - (c) Atlassian Pty Ltd
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace League\CommonMark\Delimiter\Processor;
use League\CommonMark\Delimiter\DelimiterInterface;
use League\CommonMark\Inline\Element\AbstractStringContainer;
/**
* Interface for a delimiter processor
*/
interface DelimiterProcessorInterface
{
/**
* Returns the character that marks the beginning of a delimited node.
*
* This must not clash with any other processors being added to the environment.
*
* @return string
*/
public function getOpeningCharacter(): string;
/**
* Returns the character that marks the ending of a delimited node.
*
* This must not clash with any other processors being added to the environment.
*
* Note that for a symmetric delimiter such as "*", this is the same as the opening.
*
* @return string
*/
public function getClosingCharacter(): string;
/**
* Minimum number of delimiter characters that are needed to active this.
*
* Must be at least 1.
*
* @return int
*/
public function getMinLength(): int;
/**
* Determine how many (if any) of the delimiter characters should be used.
*
* This allows implementations to decide how many characters to be used
* based on the properties of the delimiter runs. An implementation can also
* return 0 when it doesn't want to allow this particular combination of
* delimiter runs.
*
* @param DelimiterInterface $opener The opening delimiter run
* @param DelimiterInterface $closer The closing delimiter run
*
* @return int
*/
public function getDelimiterUse(DelimiterInterface $opener, DelimiterInterface $closer): int;
/**
* Process the matched delimiters, e.g. by wrapping the nodes between opener
* and closer in a new node, or appending a new node after the opener.
*
* Note that removal of the delimiter from the delimiter nodes and detaching
* them is done by the caller.
*
* @param AbstractStringContainer $opener The node that contained the opening delimiter
* @param AbstractStringContainer $closer The node that contained the closing delimiter
* @param int $delimiterUse The number of delimiters that were used
*
* @return void
*/
public function process(AbstractStringContainer $opener, AbstractStringContainer $closer, int $delimiterUse);
}

View File

@@ -0,0 +1,137 @@
<?php
/*
* This file is part of the league/commonmark package.
*
* (c) Colin O'Dell <colinodell@gmail.com>
*
* Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
* - (c) John MacFarlane
*
* Additional emphasis processing code based on commonmark-java (https://github.com/atlassian/commonmark-java)
* - (c) Atlassian Pty Ltd
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace League\CommonMark\Delimiter\Processor;
use League\CommonMark\Delimiter\DelimiterInterface;
use League\CommonMark\Inline\Element\AbstractStringContainer;
use League\CommonMark\Inline\Element\Emphasis;
use League\CommonMark\Inline\Element\Strong;
use League\CommonMark\Util\ConfigurationAwareInterface;
use League\CommonMark\Util\ConfigurationInterface;
final class EmphasisDelimiterProcessor implements DelimiterProcessorInterface, ConfigurationAwareInterface
{
/** @var string */
private $char;
/** @var ConfigurationInterface|null */
private $config;
/**
* @param string $char The emphasis character to use (typically '*' or '_')
*/
public function __construct(string $char)
{
$this->char = $char;
}
public function getOpeningCharacter(): string
{
return $this->char;
}
public function getClosingCharacter(): string
{
return $this->char;
}
public function getMinLength(): int
{
return 1;
}
public function getDelimiterUse(DelimiterInterface $opener, DelimiterInterface $closer): int
{
// "Multiple of 3" rule for internal delimiter runs
if (($opener->canClose() || $closer->canOpen()) && $closer->getOriginalLength() % 3 !== 0 && ($opener->getOriginalLength() + $closer->getOriginalLength()) % 3 === 0) {
return 0;
}
// Calculate actual number of delimiters used from this closer
if ($opener->getLength() >= 2 && $closer->getLength() >= 2) {
if ($this->enableStrong()) {
return 2;
}
return 0;
}
if ($this->enableEm()) {
return 1;
}
return 0;
}
public function process(AbstractStringContainer $opener, AbstractStringContainer $closer, int $delimiterUse)
{
if ($delimiterUse === 1) {
$emphasis = new Emphasis();
} elseif ($delimiterUse === 2) {
$emphasis = new Strong();
} else {
return;
}
$next = $opener->next();
while ($next !== null && $next !== $closer) {
$tmp = $next->next();
$emphasis->appendChild($next);
$next = $tmp;
}
$opener->insertAfter($emphasis);
}
public function setConfiguration(ConfigurationInterface $configuration)
{
$this->config = $configuration;
}
private function enableStrong(): bool
{
if ($this->config === null) {
return false;
}
$deprecatedEnableStrong = $this->config->get('enable_strong', ConfigurationInterface::MISSING);
if ($deprecatedEnableStrong !== ConfigurationInterface::MISSING) {
@\trigger_error('The "enable_strong" configuration option is deprecated in league/commonmark 1.6 and will be replaced with "commonmark > enable_strong" in 2.0', \E_USER_DEPRECATED);
} else {
$deprecatedEnableStrong = true;
}
return $this->config->get('commonmark/enable_strong', $deprecatedEnableStrong);
}
private function enableEm(): bool
{
if ($this->config === null) {
return false;
}
$deprecatedEnableEm = $this->config->get('enable_em', ConfigurationInterface::MISSING);
if ($deprecatedEnableEm !== ConfigurationInterface::MISSING) {
@\trigger_error('The "enable_em" configuration option is deprecated in league/commonmark 1.6 and will be replaced with "commonmark > enable_em" in 2.0', \E_USER_DEPRECATED);
} else {
$deprecatedEnableEm = true;
}
return $this->config->get('commonmark/enable_em', $deprecatedEnableEm);
}
}

View File

@@ -0,0 +1,106 @@
<?php
/*
* This file is part of the league/commonmark package.
*
* (c) Colin O'Dell <colinodell@gmail.com>
*
* Additional emphasis processing code based on commonmark-java (https://github.com/atlassian/commonmark-java)
* - (c) Atlassian Pty Ltd
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace League\CommonMark\Delimiter\Processor;
use League\CommonMark\Delimiter\DelimiterInterface;
use League\CommonMark\Inline\Element\AbstractStringContainer;
/**
* An implementation of DelimiterProcessorInterface that dispatches all calls to two or more other DelimiterProcessors
* depending on the length of the delimiter run. All child DelimiterProcessors must have different minimum
* lengths. A given delimiter run is dispatched to the child with the largest acceptable minimum length. If no
* child is applicable, the one with the largest minimum length is chosen.
*
* @internal
*/
final class StaggeredDelimiterProcessor implements DelimiterProcessorInterface
{
/** @var string */
private $delimiterChar;
/** @var int */
private $minLength = 0;
/** @var array<int, DelimiterProcessorInterface>|DelimiterProcessorInterface[] */
private $processors = []; // keyed by minLength in reverse order
public function __construct(string $char, DelimiterProcessorInterface $processor)
{
$this->delimiterChar = $char;
$this->add($processor);
}
public function getOpeningCharacter(): string
{
return $this->delimiterChar;
}
public function getClosingCharacter(): string
{
return $this->delimiterChar;
}
public function getMinLength(): int
{
return $this->minLength;
}
/**
* Adds the given processor to this staggered delimiter processor
*
* @param DelimiterProcessorInterface $processor
*
* @return void
*/
public function add(DelimiterProcessorInterface $processor)
{
$len = $processor->getMinLength();
if (isset($this->processors[$len])) {
throw new \InvalidArgumentException(\sprintf('Cannot add two delimiter processors for char "%s" and minimum length %d', $this->delimiterChar, $len));
}
$this->processors[$len] = $processor;
\krsort($this->processors);
$this->minLength = \min($this->minLength, $len);
}
public function getDelimiterUse(DelimiterInterface $opener, DelimiterInterface $closer): int
{
return $this->findProcessor($opener->getLength())->getDelimiterUse($opener, $closer);
}
public function process(AbstractStringContainer $opener, AbstractStringContainer $closer, int $delimiterUse)
{
$this->findProcessor($delimiterUse)->process($opener, $closer, $delimiterUse);
}
private function findProcessor(int $len): DelimiterProcessorInterface
{
// Find the "longest" processor which can handle this length
foreach ($this->processors as $processor) {
if ($processor->getMinLength() <= $len) {
return $processor;
}
}
// Just use the first one in our list
/** @var DelimiterProcessorInterface $first */
$first = \reset($this->processors);
return $first;
}
}