Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions UPGRADE.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ The library has been updated to use "link target" instead of "link":
* Renamed `LineDataParser::parseLink()` and `LineDataParser::createLink()` to
`LineDataParser::parseLinkTarget()` and `LineDataParser::createLinkTarget()`.

## Refactored Directive Options

* Removed `DirectiveOption` in favor of `FieldOption` (with a different signature)
* Removed `LineDataParser::parseDirectiveOption()` in favor of `LineDataParser::parseFieldOption()`

# Upgrade to 0.5

## Property visibility changed from protected to private
Expand Down
36 changes: 0 additions & 36 deletions lib/Parser/DirectiveOption.php

This file was deleted.

57 changes: 31 additions & 26 deletions lib/Parser/DocumentParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,9 @@ final class DocumentParser
/** @var string|null */
private $listMarker = null;

/** @var FieldOption|null */
private $fieldOption = null;

/**
* @param Directive[] $directives
*/
Expand Down Expand Up @@ -454,20 +457,34 @@ private function parseLine(string $line): bool
break;

case State::DIRECTIVE:
if (! $this->isDirectiveOption($line)) {
if (! $this->lineChecker->isDirective($line)) {
$directive = $this->getCurrentDirective();
$this->isCode = $directive !== null ? $directive->wantCode() : false;
$this->setState(State::BEGIN);
if ($this->lineChecker->isDirective($line) && $this->directive === null) {
$this->flush();
$this->initDirective($line);

return false;
break;
}

if ($this->fieldOption !== null && $this->lineChecker->isBlockLine($line, $this->fieldOption->getOffset())) {
$this->fieldOption->appendLine($line);

break;
}

if ($this->lineChecker->isFieldOption($line)) {
if ($this->fieldOption !== null) {
$this->directive->setOption($this->fieldOption->getName(), $this->fieldOption->getBody());
}

$this->flush();
$this->initDirective($line);
$this->fieldOption = $this->lineDataParser->parseFieldOption($line);

break;
}

break;
$directive = $this->getCurrentDirective();
$this->isCode = $directive !== null ? $directive->wantCode() : false;
$this->setState(State::BEGIN);

return false;

default:
$this->environment->getErrorManager()->error('Parser ended in an unexcepted state');
Expand Down Expand Up @@ -599,6 +616,11 @@ private function flush(): void
if ($this->directive !== null) {
$currentDirective = $this->getCurrentDirective();

if ($this->fieldOption !== null) {
$this->directive->setOption($this->fieldOption->getName(), $this->fieldOption->getBody());
$this->fieldOption = null;
}

if ($currentDirective !== null) {
try {
$currentDirective->process(
Expand Down Expand Up @@ -646,23 +668,6 @@ private function getCurrentDirective(): ?Directive
return $this->directives[$name];
}

private function isDirectiveOption(string $line): bool
{
if ($this->directive === null) {
return false;
}

$directiveOption = $this->lineDataParser->parseDirectiveOption($line);

if ($directiveOption === null) {
return false;
}

$this->directive->setOption($directiveOption->getName(), $directiveOption->getValue());

return true;
}

private function initDirective(string $line): bool
{
$parserDirective = $this->lineDataParser->parseDirective($line);
Expand Down
64 changes: 64 additions & 0 deletions lib/Parser/FieldOption.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<?php

declare(strict_types=1);

namespace Doctrine\RST\Parser;

use function ltrim;
use function str_replace;
use function strlen;
use function trim;

final class FieldOption
{
/** @var string */
private $name;

/** @var int */
private $offset;

/** @var string */
private $body;

/** @var int */
private $lineCount = 0;

public function __construct(string $name, int $offset, string $body)
{
$this->name = str_replace('\: ', ': ', $name);
$this->offset = $offset + 1;
$this->body = $body;
}

public function appendLine(string $line): void
{
$trimmedLine = ltrim($line);
if (strlen($trimmedLine) === 0) {
return;
}

if (++$this->lineCount === 1) {
$this->offset = strlen($line) - strlen($trimmedLine);
}

$this->body .= ' ' . $trimmedLine;
}

public function getName(): string
{
return $this->name;
}

public function getOffset(): int
{
return $this->offset;
}

/**
* @return true|string
*/
public function getBody()
{
return trim($this->body) === '' ? true : $this->body;
}
}
5 changes: 5 additions & 0 deletions lib/Parser/LineChecker.php
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,11 @@ public function isDirective(string $line): bool
return preg_match('/^\.\. (\|(.+)\| |)([^\s]+)::( (.*)|)$/mUsi', $line) > 0;
}

public function isFieldOption(string $line, int $offset = 0): bool
{
return preg_match('/^\s*:(?:\\\\:\s|[^:]|:\S)+:(?: .+|$)/', $line) > 0;
}

/**
* Check if line is an indented one.
*
Expand Down
24 changes: 9 additions & 15 deletions lib/Parser/LineDataParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -80,21 +80,6 @@ private function createLinkTarget(string $name, string $url, string $type): Link
return new Link($name, $url, $type);
}

public function parseDirectiveOption(string $line): ?DirectiveOption
{
if (preg_match('/^(\s+):(.+): (.*)$/mUsi', $line, $match) > 0) {
return new DirectiveOption($match[2], trim($match[3]));
}

if (preg_match('/^(\s+):(.+):(\s*)$/mUsi', $line, $match) > 0) {
$value = trim($match[3]);

return new DirectiveOption($match[2], true);
}

return null;
}

public function parseDirective(string $line): ?Directive
{
if (preg_match('/^\.\. (\|(.+)\| |)([^\s]+)::( (.*)|)$/mUsi', $line, $match) > 0) {
Expand Down Expand Up @@ -233,4 +218,13 @@ public function parseDefinitionList(array $lines): DefinitionList

return new DefinitionList($definitionList);
}

public function parseFieldOption(string $line): ?FieldOption
{
if (preg_match('/^(?P<offset>\s*):(?P<name>(?:\\\\:\s|[^:]|:\S)+):(?: +(?P<body>.+)|)$/', $line, $match) > 0) {
return new FieldOption($match['name'], strlen($match['offset']), $match['body'] ?? '');
}

return null;
}
}
2 changes: 1 addition & 1 deletion tests/Functional/tests/figure/figure.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<figure>
<img src="foo.jpg" width="100" />
<img src="foo.jpg" width="100" alt="Field options might use more than one line" />
<figcaption>
<p>This is a foo!</p>
</figcaption>
Expand Down
2 changes: 2 additions & 0 deletions tests/Functional/tests/figure/figure.rst
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
.. figure:: foo.jpg
:width: 100
:alt: Field options might use
more than one line

This is a foo!
12 changes: 12 additions & 0 deletions tests/Parser/LineCheckerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -91,4 +91,16 @@ public function testIsDefinitionListEnded(): void
self::assertFalse($this->lineChecker->isDefinitionListEnded('Term', ' Definition'));
self::assertFalse($this->lineChecker->isDefinitionListEnded('', ' Definition'));
}

public function testIsFieldList(): void
{
self::assertTrue($this->lineChecker->isFieldOption(':glob:'));
self::assertTrue($this->lineChecker->isFieldOption(':Date: 2001-08-16'));
self::assertTrue($this->lineChecker->isFieldOption(' :Date: 2001-08-16'));
self::assertTrue($this->lineChecker->isFieldOption(':Date published: 2001-08-16'));
self::assertTrue($this->lineChecker->isFieldOption(':Date:published: 2001-08-16'));
self::assertTrue($this->lineChecker->isFieldOption(':Date\: published: 2001-08-16'));
self::assertTrue($this->lineChecker->isFieldOption(':Date: published: 2001-08-16'));
self::assertFalse($this->lineChecker->isFieldOption('Date: 2001-08-16'));
}
}
31 changes: 31 additions & 0 deletions tests/Parser/LineDataParserTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,37 @@ public function getTestLinks(): array
];
}

/**
* @param true|string $body
*
* @dataProvider getTestFieldOptions
*/
public function testParseFieldOptions(string $line, ?string $name, $body = null): void
{
$actual = $this->lineDataParser->parseFieldOption($line);
if ($name === null) {
self::assertNull($actual);
} else {
self::assertNotNull($actual);
self::assertSame($name, $actual->getName(), 'Incorrect field option name');
self::assertSame($body, $actual->getBody(), 'Incorrect field option body');
}
}

/**
* @return array{string, string|null, true|string}[]
*/
public function getTestFieldOptions(): array
{
return [
[':glob:', 'glob', true],
[':alt: Some text', 'alt', 'Some text'],
[':date:published: 2022-09-20', 'date:published', '2022-09-20'],
[':date\: published: 2022-09-20', 'date: published', '2022-09-20'],
[':date: published: 2022-09-20', 'date', 'published: 2022-09-20'],
];
}

protected function setUp(): void
{
$this->parser = $this->createMock(Parser::class);
Expand Down