diff --git a/UPGRADE.md b/UPGRADE.md new file mode 100644 index 00000000..e30fa088 --- /dev/null +++ b/UPGRADE.md @@ -0,0 +1,19 @@ +# Upgrade to 0.3 + +## `DefinitionListTerm::$definitions` is a list of `Node`'s instead of `SpanNode`'s + +If you define a custom `definition-list.html.twig`, no longer wrap the value in +a `

` element (the `.first` and `.last` classes are automatically added): + +```diff +

+ {% for definitionListTerm in definitionList.terms %} +
{{ definitionListTerm.term.render()|raw }}
+
+ {% for definition in definitionListTerm.definitions %} +-

{{ definition.render()|raw }}

++ {{ definition.render()|raw }} + {% endfor %} +
+
+``` diff --git a/lib/Parser.php b/lib/Parser.php index 99df39bd..34044443 100644 --- a/lib/Parser.php +++ b/lib/Parser.php @@ -152,6 +152,9 @@ public function createSpanNode($span): SpanNode return $this->getNodeFactory()->createSpanNode($this, $span); } + /** + * Parses the given contents as a new file. + */ public function parse(string $contents): DocumentNode { $this->getEnvironment()->reset(); @@ -159,6 +162,15 @@ public function parse(string $contents): DocumentNode return $this->parseLocal($contents); } + /** + * Parses the given contents in a new document node. + * + * CAUTION: This modifies the state of the Parser, do not use this method + * on the main parser (use `Parser::getSubParser()->parseLocal(...)` instead). + * + * Use this method to parse contents of an other node. Nodes created by + * this new parser are not added to the main DocumentNode. + */ public function parseLocal(string $contents): DocumentNode { $this->documentParser = $this->createDocumentParser(); diff --git a/lib/Parser/DefinitionListTerm.php b/lib/Parser/DefinitionListTerm.php index e8b821d1..3b11b63a 100644 --- a/lib/Parser/DefinitionListTerm.php +++ b/lib/Parser/DefinitionListTerm.php @@ -4,6 +4,7 @@ namespace Doctrine\RST\Parser; +use Doctrine\RST\Nodes\Node; use Doctrine\RST\Nodes\SpanNode; use RuntimeException; @@ -15,12 +16,12 @@ class DefinitionListTerm /** @var SpanNode[] */ private $classifiers = []; - /** @var SpanNode[] */ + /** @var Node[] */ private $definitions = []; /** * @param SpanNode[] $classifiers - * @param SpanNode[] $definitions + * @param Node[] $definitions */ public function __construct(SpanNode $term, array $classifiers, array $definitions) { @@ -43,14 +44,14 @@ public function getClassifiers(): array } /** - * @return SpanNode[] + * @return Node[] */ public function getDefinitions(): array { return $this->definitions; } - public function getFirstDefinition(): SpanNode + public function getFirstDefinition(): Node { if (! isset($this->definitions[0])) { throw new RuntimeException('No definitions found.'); diff --git a/lib/Parser/LineDataParser.php b/lib/Parser/LineDataParser.php index 624d41ba..81df4964 100644 --- a/lib/Parser/LineDataParser.php +++ b/lib/Parser/LineDataParser.php @@ -6,6 +6,7 @@ use Doctrine\Common\EventManager; use Doctrine\RST\Event\OnLinkParsedEvent; +use Doctrine\RST\Nodes\ParagraphNode; use Doctrine\RST\Nodes\SpanNode; use Doctrine\RST\Parser; @@ -147,27 +148,42 @@ public function parseListLine(string $line): ?ListLine */ public function parseDefinitionList(array $lines): DefinitionList { - $definitionList = []; + /** @var array{term: SpanNode, classifiers: list, definition: string}|null $definitionListTerm */ $definitionListTerm = null; - $currentDefinition = null; + $definitionList = []; + + $createDefinitionTerm = function (array $definitionListTerm): DefinitionListTerm { + // parse any markup in the definition (e.g. lists, directives) + $definitionNodes = $this->parser->getSubParser()->parseLocal($definitionListTerm['definition'])->getNodes(); + if (count($definitionNodes) === 1 && $definitionNodes[0] instanceof ParagraphNode) { + // if there is only one paragraph node, the value is put directly in the
element + $definitionNodes = [$definitionNodes[0]->getValue()]; + } else { + // otherwise, .first and .last are added to the first and last nodes of the definition + $definitionNodes[0]->setClasses($definitionNodes[0]->getClasses() + ['first']); + $definitionNodes[count($definitionNodes) - 1]->setClasses($definitionNodes[count($definitionNodes) - 1]->getClasses() + ['last']); + } + + return new DefinitionListTerm( + $definitionListTerm['term'], + $definitionListTerm['classifiers'], + $definitionNodes + ); + }; foreach ($lines as $key => $line) { - // term definition line - if ($definitionListTerm !== null && substr($line, 0, 4) === ' ') { - $definition = trim($line); + // indent or empty line = term definition line + if ($definitionListTerm !== null && (substr($line, 0, 4) === ' ' || trim($line) === '')) { + $definition = substr($line, 4); - $currentDefinition .= $definition . ' '; + $definitionListTerm['definition'] .= $definition . "\n"; - // non empty string + // non empty string at the start of the line = definition term } elseif (trim($line) !== '') { // we are starting a new term so if we have an existing // term with definitions, add it to the definition list if ($definitionListTerm !== null) { - $definitionList[] = new DefinitionListTerm( - $definitionListTerm['term'], - $definitionListTerm['classifiers'], - $definitionListTerm['definitions'] - ); + $definitionList[] = $createDefinitionTerm($definitionListTerm); } $parts = explode(':', trim($line)); @@ -182,31 +198,16 @@ public function parseDefinitionList(array $lines): DefinitionList $definitionListTerm = [ 'term' => $this->parser->createSpanNode($term), 'classifiers' => $classifiers, - 'definitions' => [], + 'definition' => '', ]; - - // last line - } elseif ($definitionListTerm !== null && count($lines) - 1 === $key) { - if ($currentDefinition !== null) { - $definitionListTerm['definitions'][] = $this->parser->createSpanNode($currentDefinition); - - $currentDefinition = null; - } - - $definitionList[] = new DefinitionListTerm( - $definitionListTerm['term'], - $definitionListTerm['classifiers'], - $definitionListTerm['definitions'] - ); - - // empty line, start of a new definition for the current term - } elseif ($currentDefinition !== null && $definitionListTerm !== null) { - $definitionListTerm['definitions'][] = $this->parser->createSpanNode($currentDefinition); - - $currentDefinition = null; } } + // append the last definition of the list + if ($definitionListTerm !== null) { + $definitionList[] = $createDefinitionTerm($definitionListTerm); + } + return new DefinitionList($definitionList); } } diff --git a/lib/Templates/default/html/definition-list.html.twig b/lib/Templates/default/html/definition-list.html.twig index bea3bcb7..668f799a 100644 --- a/lib/Templates/default/html/definition-list.html.twig +++ b/lib/Templates/default/html/definition-list.html.twig @@ -14,21 +14,11 @@ {% endif %} - {% if definitionListTerm.definitions|length > 1 %} -
- {% for definition in definitionListTerm.definitions %} - {% if loop.first %} -

{{ definition.render()|raw }}

- {% elseif loop.last %} -

{{ definition.render()|raw }}

- {% else %} -

{{ definition.render()|raw }}

- {% endif %} - {% endfor %} -
- {% elseif definitionListTerm.definitions|length == 1 %} -
{{ definitionListTerm.firstDefinition.render()|raw }}
- {% endif %} +
+ {% for definition in definitionListTerm.definitions %} + {{ definition.render()|raw }} + {% endfor %} +
{% endfor %} {% endapply %} diff --git a/tests/Functional/tests/class-directive/class-directive.html b/tests/Functional/tests/class-directive/class-directive.html index 37ec2b10..f324a102 100644 --- a/tests/Functional/tests/class-directive/class-directive.html +++ b/tests/Functional/tests/class-directive/class-directive.html @@ -17,7 +17,7 @@
term 1
-
Definition 1
+
Definition 1
diff --git a/tests/Functional/tests/definition-list/definition-list.html b/tests/Functional/tests/definition-list/definition-list.html index 4e5606d2..b0338b41 100644 --- a/tests/Functional/tests/definition-list/definition-list.html +++ b/tests/Functional/tests/definition-list/definition-list.html @@ -8,33 +8,35 @@

Text around the definition list.

term 1
-
Definition 1
+
Definition 1
term 2
-

Definition 1

-

Definition 2

-

Definition 3

+

Definition 1

+

Definition 2

+

Definition 3

term 3 :classifier
-
Definition 1
+
Definition 1
term 4 :classifier one:classifier two
-
Definition 1
+
Definition 1
term with & :classifier with &
-
Definition 1 with &
+
Definition 1 with &
term with & :classifier with &:classifier with &
-

Definition 1 with &

-

Definition 2 with &

+

Definition 1 with &

+

Definition 2 with &

term 5: classifier
-
Definition 1
+
Definition 1
multi-line definition term
-

Definition 1 line 1 Definition 1 line 2

-

Definition 2 line 1 Definition 2 line 2

+

Definition 1 line 1 +Definition 1 line 2

+

Definition 2 line 1 +Definition 2 line 2

@@ -47,13 +49,15 @@

term 1
-

Definition 1 line 1 Definition 1 line 2

-

Definition 2

+

Definition 1 line 1 +Definition 1 line 2

+

Definition 2

term 2
-

Definition 1 line 1 Definition 1 line 2

-

Definition 2

+

Definition 1 line 1 +Definition 1 line 2

+

Definition 2

@@ -65,11 +69,26 @@

Paragraph 1

term 1
-
definition 1 definition 2
+
definition 1 +definition 2

Paragraph 2

term 2
-
definition 1 definition 2
+
definition 1 +definition 2
+
+ +
+

+ Directive in definition list +

+
+
term 1
+
+
+

directive in definition list

+
+
diff --git a/tests/Functional/tests/definition-list/definition-list.rst b/tests/Functional/tests/definition-list/definition-list.rst index 37b243dd..c270e426 100644 --- a/tests/Functional/tests/definition-list/definition-list.rst +++ b/tests/Functional/tests/definition-list/definition-list.rst @@ -73,3 +73,11 @@ Paragraph 2 term 2 definition 1 definition 2 + +Directive in definition list +============================ + +term 1 + .. note:: + + directive in definition list diff --git a/tests/Functional/tests/standalone-hyperlinks/standalone-hyperlinks.html b/tests/Functional/tests/standalone-hyperlinks/standalone-hyperlinks.html index 71652f8a..c867ff85 100644 --- a/tests/Functional/tests/standalone-hyperlinks/standalone-hyperlinks.html +++ b/tests/Functional/tests/standalone-hyperlinks/standalone-hyperlinks.html @@ -20,10 +20,31 @@ mailto:name@example.comhttp://www.asianewsphoto.com/(S(neugxif4twuizg551ywh3f55))/Web_ENG/View_DetailPhoto.aspx?PicId=752http://www.asianewsphoto.com/(S(neugxif4twuizg551ywh3f55))http://lcweb2.loc.gov/cgi-bin/query/h?pp/horyd:@field(NUMBER+@band(thc+5a46634))http://www.example.com/wpstyle/?p=364https://www.example.com/foo/?bar=baz&inga=42&quuxhttp://userid:password@example.com:8080http://userid:password@example.com:8080/http://userid@example.comhttp://userid@example.com/http://userid@example.com:8080http://userid@example.com:8080/http://userid:password@example.comhttp://userid:password@example.com/http://142.42.1.1/http://142.42.1.1:8080/http://⌘.wshttp://⌘.ws/http://☺.damowmow.com/http://code.google.com/events/#&product=browserhttp://j.mpftp://foo.bar/bazhttp://foo.bar/?q=Test%20URL-encoded%20stuffhttp://例子.测试http://उदाहरण.परीक्षाhttp://-._!$&'()*+,;=:%40:80%2f::::::@example.comhttp://1337.nethttp://a.b-c.dehttp://223.255.255.254mailto:jane.doe@example.comchrome://chromeirc://irc.freenode.net:6667/freenodemicrosoft.windows.camera:foobarcoaps+ws://foobar How about multiple http://example.com/uris on the mailto:same-line@example.com

-
-
Should fail against :
-
6:00p filename.txt www.c.ws/䨹 Just a www.example.com link. bit.ly/foo “is.gd/foo/” WWW.EXAMPLE.COM http:// http://. http://.. http://? http://?? // //a /// foo.com h://test http:// shouldfail.com :// should fail ✪df.ws/1234 example.com example.com/
-
+

Should fail against:

+
+

6:00p +filename.txt +www.c.ws/䨹 +Just a www.example.com link. +bit.ly/foo +“is.gd/foo/” +WWW.EXAMPLE.COM +http:// +http://. +http://.. +http://? +http://?? +// +//a +/// +foo.com +h://test +http:// shouldfail.com +:// should fail +✪df.ws/1234 +example.com +example.com/

+

These are currently problematic and fail, since the parser appears to see named links appearing within these URLs (i.e. "blah_") and attempts to replace these with tokens. These should match the standalone hyperlink pattern, but they do diff --git a/tests/Functional/tests/standalone-hyperlinks/standalone-hyperlinks.rst b/tests/Functional/tests/standalone-hyperlinks/standalone-hyperlinks.rst index d55aa397..406e9b96 100644 --- a/tests/Functional/tests/standalone-hyperlinks/standalone-hyperlinks.rst +++ b/tests/Functional/tests/standalone-hyperlinks/standalone-hyperlinks.rst @@ -64,6 +64,7 @@ Matches the right thing in the following lines: How about multiple http://example.com/uris on the mailto:same-line@example.com Should fail against: + 6:00p filename.txt www.c.ws/䨹