Skip to content

Commit

Permalink
Merge pull request #411 from thephpleague/feature/unordered-list-markers
Browse files Browse the repository at this point in the history
Allow the unordered list markers to be configurable
  • Loading branch information
colinodell committed Feb 8, 2020
2 parents a13ee46 + 50106ef commit ec2d74d
Show file tree
Hide file tree
Showing 4 changed files with 206 additions and 3 deletions.
2 changes: 1 addition & 1 deletion .phpstorm.meta.php
Expand Up @@ -28,7 +28,7 @@
expectedArguments(\League\CommonMark\Inline\Element\Newline::__construct(), 0, argumentsSet('league_commonmark_newline_types'));
expectedReturnValues(\League\CommonMark\Inline\Element\Newline::getType(), argumentsSet('league_commonmark_newline_types'));

registerArgumentsSet('league_commonmark_options', 'renderer', 'enable_em', 'enable_strong', 'use_asterisk', 'use_underscore', 'html_input', 'allow_unsafe_links', 'max_nesting_level');
registerArgumentsSet('league_commonmark_options', 'renderer', 'enable_em', 'enable_strong', 'use_asterisk', 'use_underscore', 'unordered_list_markers', 'html_input', 'allow_unsafe_links', 'max_nesting_level');
expectedArguments(\League\CommonMark\EnvironmentInterface::getConfig(), 0, argumentsSet('league_commonmark_options'));
expectedArguments(\League\CommonMark\Util\ConfigurationInterface::get(), 0, argumentsSet('league_commonmark_options'));
expectedArguments(\League\CommonMark\Util\ConfigurationInterface::set(), 0, argumentsSet('league_commonmark_options'));
Expand Down
2 changes: 2 additions & 0 deletions docs/1.0/configuration.md
Expand Up @@ -24,6 +24,7 @@ $converter = new CommonMarkConverter([
'enable_strong' => true,
'use_asterisk' => true,
'use_underscore' => true,
'unordered_list_markers' => ['-', '*', '+'],
'html_input' => 'escape',
'allow_unsafe_links' => false,
'max_nesting_level' => INF,
Expand All @@ -40,6 +41,7 @@ Here's a list of currently-supported options:
* `enable_strong` - Disable `<strong>` parsing by setting to `false`; enable with `true` (default: `true`)
* `use_asterisk` - Disable parsing of `*` for emphasis by setting to `false`; enable with `true` (default: `true`)
* `use_underscore` - Disable parsing of `_` for emphasis by setting to `false`; enable with `true` (default: `true`)
* `unordered_list_markers` - Array of characters that can be used to indicated a bulleted list (default: `["-", "*", "+"]`)
* `html_input` - How to handle HTML input. Set this option to one of the following strings:
- `strip` - Strip all HTML (equivalent to `'safe' => true`)
- `allow` - Allow all HTML input as-is (default value; equivalent to `'safe' => false)
Expand Down
36 changes: 34 additions & 2 deletions src/Block/Parser/ListParser.php
Expand Up @@ -20,10 +20,26 @@
use League\CommonMark\Block\Element\Paragraph;
use League\CommonMark\ContextInterface;
use League\CommonMark\Cursor;
use League\CommonMark\Util\ConfigurationAwareInterface;
use League\CommonMark\Util\ConfigurationInterface;
use League\CommonMark\Util\RegexHelper;

final class ListParser implements BlockParserInterface
final class ListParser implements BlockParserInterface, ConfigurationAwareInterface
{
/** @var ConfigurationInterface|null */
private $config;

/** @var string|null */
private $listMarkerRegex;

/**
* {@inheritdoc}
*/
public function setConfiguration(ConfigurationInterface $configuration)
{
$this->config = $configuration;
}

/**
* @param ContextInterface $context
* @param Cursor $cursor
Expand All @@ -45,7 +61,7 @@ public function parse(ContextInterface $context, Cursor $cursor): bool
$tmpCursor->advanceToNextNonSpaceOrTab();
$rest = $tmpCursor->getRemainder();

if (\preg_match('/^[*+-]/', $rest) === 1) {
if (\preg_match($this->listMarkerRegex ?? $this->generateListMarkerRegex(), $rest) === 1) {
$data = new ListData();
$data->markerOffset = $indent;
$data->type = ListBlock::TYPE_UNORDERED;
Expand Down Expand Up @@ -121,4 +137,20 @@ private function calculateListMarkerPadding(Cursor $cursor, int $markerLength):

return $markerLength + $spacesAfterMarker;
}

private function generateListMarkerRegex(): string
{
// No configuration given - use the defaults
if ($this->config === null) {
return $this->listMarkerRegex = '/^[*+-]/';
}

$markers = $this->config->get('unordered_list_markers', ['*', '+', '-']);

if (!\is_array($markers)) {
throw new \RuntimeException('Invalid configuration option "unordered_list_markers": value must be an array of strings');
}

return $this->listMarkerRegex = '/^[' . \preg_quote(\implode('', $markers), '/') . ']/';
}
}
169 changes: 169 additions & 0 deletions tests/unit/Block/Parser/ListParserTest.php
@@ -0,0 +1,169 @@
<?php

/*
* This file is part of the league/commonmark package.
*
* (c) Colin O'Dell <colinodell@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace League\CommonMark\Tests\Unit\Block\Parser;

use League\CommonMark\Block\Element\Document;
use League\CommonMark\Block\Element\ListBlock;
use League\CommonMark\Block\Element\ListItem;
use League\CommonMark\Block\Parser\ListParser;
use League\CommonMark\Context;
use League\CommonMark\Cursor;
use League\CommonMark\Environment;
use League\CommonMark\Util\Configuration;
use PHPUnit\Framework\TestCase;

final class ListParserTest extends TestCase
{
public function testOrderedListStartingAtOne()
{
$input = '1. Foo';

$context = new Context(new Document(), new Environment());
$context->setNextLine($input);
$cursor = new Cursor($input);

$parser = new ListParser();
$this->assertTrue($parser->parse($context, $cursor));

$container = $context->getContainer();

$this->assertTrue($container instanceof ListItem);
/** @var ListItem $container */
$this->assertSame(ListBlock::TYPE_ORDERED, $container->getListData()->type);
$this->assertSame(1, $container->getListData()->start);
}

public function testOrderedListStartingAtTwo()
{
$input = '2. Foo';

$context = new Context(new Document(), new Environment());
$context->setNextLine($input);
$cursor = new Cursor($input);

$parser = new ListParser();
$this->assertTrue($parser->parse($context, $cursor));

$container = $context->getContainer();

$this->assertTrue($container instanceof ListItem);
/** @var ListItem $container */
$this->assertSame(ListBlock::TYPE_ORDERED, $container->getListData()->type);
$this->assertSame(2, $container->getListData()->start);
}

public function testUnorderedListWithDashMarker()
{
$input = '- Foo';

$context = new Context(new Document(), new Environment());
$context->setNextLine($input);
$cursor = new Cursor($input);

$parser = new ListParser();
$this->assertTrue($parser->parse($context, $cursor));

$container = $context->getContainer();

$this->assertTrue($container instanceof ListItem);
/** @var ListItem $container */
$this->assertSame(ListBlock::TYPE_UNORDERED, $container->getListData()->type);
$this->assertSame('-', $container->getListData()->bulletChar);
}

public function testUnorderedListWithAsteriskMarker()
{
$input = '* Foo';

$context = new Context(new Document(), new Environment());
$context->setNextLine($input);
$cursor = new Cursor($input);

$parser = new ListParser();
$this->assertTrue($parser->parse($context, $cursor));

$container = $context->getContainer();

$this->assertTrue($container instanceof ListItem);
/** @var ListItem $container */
$this->assertSame(ListBlock::TYPE_UNORDERED, $container->getListData()->type);
$this->assertSame('*', $container->getListData()->bulletChar);
}

public function testUnorderedListWithPlusMarker()
{
$input = '+ Foo';

$context = new Context(new Document(), new Environment());
$context->setNextLine($input);
$cursor = new Cursor($input);

$parser = new ListParser();
$this->assertTrue($parser->parse($context, $cursor));

$container = $context->getContainer();

$this->assertTrue($container instanceof ListItem);
/** @var ListItem $container */
$this->assertSame(ListBlock::TYPE_UNORDERED, $container->getListData()->type);
$this->assertSame('+', $container->getListData()->bulletChar);
}

public function testUnorderedListWithCustomMarker()
{
$input = '^ Foo';

$context = new Context(new Document(), new Environment());
$context->setNextLine($input);
$cursor = new Cursor($input);

$parser = new ListParser();
$parser->setConfiguration(new Configuration(['unordered_list_markers' => ['^']]));
$this->assertTrue($parser->parse($context, $cursor));

$container = $context->getContainer();

$this->assertTrue($container instanceof ListItem);
/** @var ListItem $container */
$this->assertSame(ListBlock::TYPE_UNORDERED, $container->getListData()->type);
$this->assertSame('^', $container->getListData()->bulletChar);
}

public function testUnorderedListWithDisabledMarker()
{
$input = '+ Foo';

$context = new Context(new Document(), new Environment());
$context->setNextLine($input);
$cursor = new Cursor($input);

$parser = new ListParser();
$parser->setConfiguration(new Configuration(['unordered_list_markers' => ['-', '*']]));
$this->assertFalse($parser->parse($context, $cursor));
}

public function testInvalidListMarkerConfiguration()
{
$this->expectException(\RuntimeException::class);
$this->expectExceptionMessage('Invalid configuration option "unordered_list_markers": value must be an array of strings');
$input = '+ Foo';

$context = new Context(new Document(), new Environment());
$context->setNextLine($input);
$cursor = new Cursor($input);

$parser = new ListParser();
$parser->setConfiguration(new Configuration(['unordered_list_markers' => '-']));

$parser->parse($context, $cursor);
}
}

0 comments on commit ec2d74d

Please sign in to comment.