Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/2.4'
Browse files Browse the repository at this point in the history
  • Loading branch information
colinodell committed Jul 22, 2024
2 parents 5de7972 + b81cc1d commit 139af38
Show file tree
Hide file tree
Showing 23 changed files with 184 additions and 96 deletions.
35 changes: 34 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,37 @@ Updates should follow the [Keep a CHANGELOG](https://keepachangelog.com/) princi
- `autolink/allowed_protocols` - an array of protocols to allow autolinking for
- `autolink/default_protocol` - the default protocol to use when none is specified

### Changed

- Made compatible with CommonMark spec 0.31.0, including:
- Allow closing fence to be followed by tabs
- Remove restrictive limitation on inline comments
- Unicode symbols now treated like punctuation (for purposes of flankingness)
- Trailing tabs on the last line of indented code blocks will be excluded
- Improved HTML comment matching
- `Paragraph`s only containing link reference definitions will be kept in the AST until the `Document` is finalized
- (These were previously removed immediately after parsing the `Paragraph`)

### Fixed

- Fixed list tightness not being determined properly in some edge cases
- Fixed incorrect ending line numbers for several block types in various scenarios
- Fixed lowercase inline HTML declarations not being accepted

## [2.4.4] - 2024-07-22

### Fixed

- Fixed SmartPunct extension changing already-formatted quotation marks (#1030)

## [2.4.3] - 2024-07-22

### Fixed

- Fixed the Attributes extension not supporting CSS level 3 selectors (#1013)
- Fixed `UrlAutolinkParser` incorrectly parsing text containing `www` anywhere before an autolink (#1025)


## [2.4.2] - 2024-02-02

### Fixed
Expand Down Expand Up @@ -574,7 +605,9 @@ No changes were introduced since the previous release.
- Alternative 1: Use `CommonMarkConverter` or `GithubFlavoredMarkdownConverter` if you don't need to customize the environment
- Alternative 2: Instantiate a new `Environment` and add the necessary extensions yourself

[unreleased]: https://github.com/thephpleague/commonmark/compare/2.4.2...main
[unreleased]: https://github.com/thephpleague/commonmark/compare/2.4.4...main
[2.4.4]: https://github.com/thephpleague/commonmark/compare/2.4.3...2.4.4
[2.4.3]: https://github.com/thephpleague/commonmark/compare/2.4.2...2.4.3
[2.4.2]: https://github.com/thephpleague/commonmark/compare/2.4.1...2.4.2
[2.4.1]: https://github.com/thephpleague/commonmark/compare/2.4.0...2.4.1
[2.4.0]: https://github.com/thephpleague/commonmark/compare/2.3.9...2.4.0
Expand Down
12 changes: 6 additions & 6 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@
"require-dev": {
"ext-json": "*",
"cebe/markdown": "^1.0",
"commonmark/cmark": "0.30.3",
"commonmark/commonmark.js": "0.30.0",
"commonmark/cmark": "0.31.0",
"commonmark/commonmark.js": "0.31.0",
"composer/package-versions-deprecated": "^1.8",
"embed/embed": "^4.4",
"erusev/parsedown": "^1.0",
Expand All @@ -56,9 +56,9 @@
"type": "package",
"package": {
"name": "commonmark/commonmark.js",
"version": "0.30.0",
"version": "0.31.0",
"dist": {
"url": "https://github.com/commonmark/commonmark.js/archive/0.30.0.zip",
"url": "https://github.com/commonmark/commonmark.js/archive/0.31.0.zip",
"type": "zip"
}
}
Expand All @@ -67,9 +67,9 @@
"type": "package",
"package": {
"name": "commonmark/cmark",
"version": "0.30.3",
"version": "0.31.0",
"dist": {
"url": "https://github.com/commonmark/cmark/archive/0.30.3.zip",
"url": "https://github.com/commonmark/cmark/archive/0.31.0.zip",
"type": "zip"
}
}
Expand Down
2 changes: 1 addition & 1 deletion docs/2.4/extensions/embed.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ We do provide an adapter for the popular [`embed/embed`](https://github.com/osca
because it supports fetching multiple URLs in parallel, which is ideal for performance, and it supports a wide range
of embeddable content.

To use that library, you'll need to `composer install embed/embed` and then pass `new OscaroteroEmbedAdapter()` as the `adapter`
To use that library, you'll need to `composer require embed/embed` and then pass `new OscaroteroEmbedAdapter()` as the `adapter`
configuration option, as shown in the [**Usage**](#usage) section above.

Need to customize the maximum width/height of the embedded content? You can do that by instantiating the service provided by
Expand Down
2 changes: 1 addition & 1 deletion src/Extension/Attributes/Util/AttributesHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
*/
final class AttributesHelper
{
private const SINGLE_ATTRIBUTE = '\s*([.#][_a-z0-9-]+|' . RegexHelper::PARTIAL_ATTRIBUTENAME . RegexHelper::PARTIAL_ATTRIBUTEVALUESPEC . '?)\s*';
private const SINGLE_ATTRIBUTE = '\s*([.]-?[_a-z]\S*|[#]\S+|' . RegexHelper::PARTIAL_ATTRIBUTENAME . RegexHelper::PARTIAL_ATTRIBUTEVALUESPEC . '?)\s*';
private const ATTRIBUTE_LIST = '/^{:?(' . self::SINGLE_ATTRIBUTE . ')+}/i';

/**
Expand Down
2 changes: 1 addition & 1 deletion src/Extension/Autolink/UrlAutolinkParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ final class UrlAutolinkParser implements InlineParserInterface
*
* @psalm-readonly
*/
private array $prefixes = ['www'];
private array $prefixes = ['www.'];

/**
* @psalm-var non-empty-string
Expand Down
2 changes: 1 addition & 1 deletion src/Extension/CommonMark/Node/Block/ListBlock.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class ListBlock extends AbstractBlock implements TightBlockInterface
public const DELIM_PERIOD = 'period';
public const DELIM_PAREN = 'paren';

protected bool $tight = false;
protected bool $tight = false; // TODO Make lists tight by default in v3

/** @psalm-readonly */
protected ListData $listData;
Expand Down
2 changes: 1 addition & 1 deletion src/Extension/CommonMark/Parser/Block/FencedCodeParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public function tryContinue(Cursor $cursor, BlockContinueParserInterface $active
{
// Check for closing code fence
if (! $cursor->isIndented() && $cursor->getNextNonSpaceCharacter() === $this->block->getChar()) {
$match = RegexHelper::matchFirst('/^(?:`{3,}|~{3,})(?= *$)/', $cursor->getLine(), $cursor->getNextNonSpacePosition());
$match = RegexHelper::matchFirst('/^(?:`{3,}|~{3,})(?=[ \t]*$)/', $cursor->getLine(), $cursor->getNextNonSpacePosition());
if ($match !== null && \strlen($match[0]) >= $this->block->getLength()) {
// closing fence - we're at end of line, so we can finalize now
return BlockContinue::finished();
Expand Down
19 changes: 6 additions & 13 deletions src/Extension/CommonMark/Parser/Block/IndentedCodeParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,21 +63,14 @@ public function addLine(string $line): void

public function closeBlock(): void
{
$reversed = \array_reverse($this->strings->toArray(), true);
foreach ($reversed as $index => $line) {
if ($line !== '' && $line !== "\n" && ! \preg_match('/^(\n *)$/', $line)) {
break;
}
$lines = $this->strings->toArray();

unset($reversed[$index]);
// Note that indented code block cannot be empty, so $lines will always have at least one non-empty element
while (\preg_match('/^[ \t]*$/', \end($lines))) { // @phpstan-ignore-line
\array_pop($lines);
}

$fixed = \array_reverse($reversed);
$tmp = \implode("\n", $fixed);
if (\substr($tmp, -1) !== "\n") {
$tmp .= "\n";
}

$this->block->setLiteral($tmp);
$this->block->setLiteral(\implode("\n", $lines) . "\n");
$this->block->setEndLine($this->block->getStartLine() + \count($lines) - 1);
}
}
62 changes: 38 additions & 24 deletions src/Extension/CommonMark/Parser/Block/ListBlockParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,6 @@ final class ListBlockParser extends AbstractBlockContinueParser
/** @psalm-readonly */
private ListBlock $block;

private bool $hadBlankLine = false;

private int $linesAfterBlank = 0;

public function __construct(ListData $listData)
{
$this->block = new ListBlock($listData);
Expand All @@ -48,32 +44,50 @@ public function isContainer(): bool

public function canContain(AbstractBlock $childBlock): bool
{
if (! $childBlock instanceof ListItem) {
return false;
}

// Another list item is being added to this list block.
// If the previous line was blank, that means this list
// block is "loose" (not tight).
if ($this->hadBlankLine && $this->linesAfterBlank === 1) {
$this->block->setTight(false);
$this->hadBlankLine = false;
}

return true;
return $childBlock instanceof ListItem;
}

public function tryContinue(Cursor $cursor, BlockContinueParserInterface $activeBlockParser): ?BlockContinue
{
if ($cursor->isBlank()) {
$this->hadBlankLine = true;
$this->linesAfterBlank = 0;
} elseif ($this->hadBlankLine) {
$this->linesAfterBlank++;
}

// List blocks themselves don't have any markers, only list items. So try to stay in the list.
// If there is a block start other than list item, canContain makes sure that this list is closed.
return BlockContinue::at($cursor);
}

public function closeBlock(): void
{
$item = $this->block->firstChild();
while ($item instanceof AbstractBlock) {
// check for non-final list item ending with blank line:
if ($item->next() !== null && self::endsWithBlankLine($item)) {
$this->block->setTight(false);
break;
}

// recurse into children of list item, to see if there are spaces between any of them
$subitem = $item->firstChild();
while ($subitem instanceof AbstractBlock) {
if ($subitem->next() && self::endsWithBlankLine($subitem)) {
$this->block->setTight(false);
break 2;
}

$subitem = $subitem->next();
}

$item = $item->next();
}

$lastChild = $this->block->lastChild();
if ($lastChild instanceof AbstractBlock) {
$this->block->setEndLine($lastChild->getEndLine());
}
}

private static function endsWithBlankLine(AbstractBlock $block): bool
{
$next = $block->next();

return $next instanceof AbstractBlock && $block->getEndLine() !== $next->getStartLine() - 1;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ public function tryStart(Cursor $cursor, MarkdownParserStateInterface $parserSta
if (! ($matched instanceof ListBlockParser) || ! $listData->equals($matched->getBlock()->getListData())) {
$listBlockParser = new ListBlockParser($listData);
// We start out with assuming a list is tight. If we find a blank line, we set it to loose later.
// TODO for 3.0: Just make them tight by default in the block so we can remove this call
$listBlockParser->getBlock()->setTight(true);

return BlockStart::of($listBlockParser, $listItemParser)->at($cursor);
Expand Down
30 changes: 11 additions & 19 deletions src/Extension/CommonMark/Parser/Block/ListItemParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,9 @@

namespace League\CommonMark\Extension\CommonMark\Parser\Block;

use League\CommonMark\Extension\CommonMark\Node\Block\ListBlock;
use League\CommonMark\Extension\CommonMark\Node\Block\ListData;
use League\CommonMark\Extension\CommonMark\Node\Block\ListItem;
use League\CommonMark\Node\Block\AbstractBlock;
use League\CommonMark\Node\Block\Paragraph;
use League\CommonMark\Parser\Block\AbstractBlockContinueParser;
use League\CommonMark\Parser\Block\BlockContinue;
use League\CommonMark\Parser\Block\BlockContinueParserInterface;
Expand All @@ -28,8 +26,6 @@ final class ListItemParser extends AbstractBlockContinueParser
/** @psalm-readonly */
private ListItem $block;

private bool $hadBlankLine = false;

public function __construct(ListData $listData)
{
$this->block = new ListItem($listData);
Expand All @@ -47,18 +43,7 @@ public function isContainer(): bool

public function canContain(AbstractBlock $childBlock): bool
{
if ($this->hadBlankLine) {
// We saw a blank line in this list item, that means the list block is loose.
//
// spec: if any of its constituent list items directly contain two block-level elements with a blank line
// between them
$parent = $this->block->parent();
if ($parent instanceof ListBlock) {
$parent->setTight(false);
}
}

return true;
return ! $childBlock instanceof ListItem;
}

public function tryContinue(Cursor $cursor, BlockContinueParserInterface $activeBlockParser): ?BlockContinue
Expand All @@ -69,9 +54,6 @@ public function tryContinue(Cursor $cursor, BlockContinueParserInterface $active
return BlockContinue::none();
}

$activeBlock = $activeBlockParser->getBlock();
// If the active block is a code block, blank lines in it should not affect if the list is tight.
$this->hadBlankLine = $activeBlock instanceof Paragraph || $activeBlock instanceof ListItem;
$cursor->advanceToNextNonSpaceOrTab();

return BlockContinue::at($cursor);
Expand All @@ -87,4 +69,14 @@ public function tryContinue(Cursor $cursor, BlockContinueParserInterface $active
// Note: We'll hit this case for lazy continuation lines, they will get added later.
return BlockContinue::none();
}

public function closeBlock(): void
{
if (($lastChild = $this->block->lastChild()) instanceof AbstractBlock) {
$this->block->setEndLine($lastChild->getEndLine());
} else {
// Empty list item
$this->block->setEndLine($this->block->getStartLine());
}
}
}
28 changes: 10 additions & 18 deletions src/Extension/SmartPunct/QuoteParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,19 @@

final class QuoteParser implements InlineParserInterface
{
/**
* @deprecated This constant is no longer used and will be removed in a future major release
*/
public const DOUBLE_QUOTES = [Quote::DOUBLE_QUOTE, Quote::DOUBLE_QUOTE_OPENER, Quote::DOUBLE_QUOTE_CLOSER];

/**
* @deprecated This constant is no longer used and will be removed in a future major release
*/
public const SINGLE_QUOTES = [Quote::SINGLE_QUOTE, Quote::SINGLE_QUOTE_OPENER, Quote::SINGLE_QUOTE_CLOSER];

public function getMatchDefinition(): InlineParserMatch
{
return InlineParserMatch::oneOf(...[...self::DOUBLE_QUOTES, ...self::SINGLE_QUOTES]);
return InlineParserMatch::oneOf(Quote::SINGLE_QUOTE, Quote::DOUBLE_QUOTE);
}

/**
Expand All @@ -40,8 +47,6 @@ public function parse(InlineParserContext $inlineContext): bool
$char = $inlineContext->getFullMatch();
$cursor = $inlineContext->getCursor();

$normalizedCharacter = $this->getNormalizedQuoteCharacter($char);

$charBefore = $cursor->peek(-1);
if ($charBefore === null) {
$charBefore = "\n";
Expand All @@ -58,28 +63,15 @@ public function parse(InlineParserContext $inlineContext): bool
$canOpen = $leftFlanking && ! $rightFlanking;
$canClose = $rightFlanking;

$node = new Quote($normalizedCharacter, ['delim' => true]);
$node = new Quote($char, ['delim' => true]);
$inlineContext->getContainer()->appendChild($node);

// Add entry to stack to this opener
$inlineContext->getDelimiterStack()->push(new Delimiter($normalizedCharacter, 1, $node, $canOpen, $canClose));
$inlineContext->getDelimiterStack()->push(new Delimiter($char, 1, $node, $canOpen, $canClose));

return true;
}

private function getNormalizedQuoteCharacter(string $character): string
{
if (\in_array($character, self::DOUBLE_QUOTES, true)) {
return Quote::DOUBLE_QUOTE;
}

if (\in_array($character, self::SINGLE_QUOTES, true)) {
return Quote::SINGLE_QUOTE;
}

return $character;
}

/**
* @return bool[]
*/
Expand Down
2 changes: 2 additions & 0 deletions src/Node/Block/Paragraph.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,6 @@

class Paragraph extends AbstractBlock
{
/** @internal */
public bool $onlyContainsLinkReferenceDefinitions = false;
}
Loading

0 comments on commit 139af38

Please sign in to comment.