From fdeadcd147ffb21f347262494d144651a9492fc8 Mon Sep 17 00:00:00 2001 From: Sven Luijten Date: Sat, 29 Jul 2023 21:27:18 +0200 Subject: [PATCH 1/5] Allow adding attributes without value --- src/Extension/Attributes/Util/AttributesHelper.php | 4 ++++ src/Util/RegexHelper.php | 2 +- tests/unit/Extension/Attributes/Util/AttributesHelperTest.php | 2 ++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Extension/Attributes/Util/AttributesHelper.php b/src/Extension/Attributes/Util/AttributesHelper.php index de5c111e1d..cf096cc25f 100644 --- a/src/Extension/Attributes/Util/AttributesHelper.php +++ b/src/Extension/Attributes/Util/AttributesHelper.php @@ -72,6 +72,10 @@ public static function parseAttributes(Cursor $cursor): array continue; } + if (\strpos($attribute, '=') === false) { + $attribute .= '=""'; + } + /** @psalm-suppress PossiblyUndefinedArrayOffset */ [$name, $value] = \explode('=', $attribute, 2); diff --git a/src/Util/RegexHelper.php b/src/Util/RegexHelper.php index 8ead731e2b..18ee38be7d 100644 --- a/src/Util/RegexHelper.php +++ b/src/Util/RegexHelper.php @@ -47,7 +47,7 @@ final class RegexHelper public const PARTIAL_SINGLEQUOTEDVALUE = '\'[^\']*\''; public const PARTIAL_DOUBLEQUOTEDVALUE = '"[^"]*"'; public const PARTIAL_ATTRIBUTEVALUE = '(?:' . self::PARTIAL_UNQUOTEDVALUE . '|' . self::PARTIAL_SINGLEQUOTEDVALUE . '|' . self::PARTIAL_DOUBLEQUOTEDVALUE . ')'; - public const PARTIAL_ATTRIBUTEVALUESPEC = '(?:' . '\s*=' . '\s*' . self::PARTIAL_ATTRIBUTEVALUE . ')'; + public const PARTIAL_ATTRIBUTEVALUESPEC = '(?:' . '\s*=?' . '\s*' . self::PARTIAL_ATTRIBUTEVALUE . '?)'; public const PARTIAL_ATTRIBUTE = '(?:' . '\s+' . self::PARTIAL_ATTRIBUTENAME . self::PARTIAL_ATTRIBUTEVALUESPEC . '?)'; public const PARTIAL_OPENTAG = '<' . self::PARTIAL_TAGNAME . self::PARTIAL_ATTRIBUTE . '*' . '\s*\/?>'; public const PARTIAL_CLOSETAG = '<\/' . self::PARTIAL_TAGNAME . '\s*[>]'; diff --git a/tests/unit/Extension/Attributes/Util/AttributesHelperTest.php b/tests/unit/Extension/Attributes/Util/AttributesHelperTest.php index 17a465348a..21ebac2d7a 100644 --- a/tests/unit/Extension/Attributes/Util/AttributesHelperTest.php +++ b/tests/unit/Extension/Attributes/Util/AttributesHelperTest.php @@ -58,6 +58,7 @@ public function dataForTestParseAttributes(): iterable yield [new Cursor('{: target=_blank}'), ['target' => '_blank']]; yield [new Cursor('{: target=_blank }'), ['target' => '_blank']]; yield [new Cursor('{: target=_blank }'), ['target' => '_blank']]; + yield [new Cursor('{: disabled}'), ['disabled' => '']]; // Examples without colons yield [new Cursor('{title="My Title"}'), ['title' => 'My Title']]; @@ -73,6 +74,7 @@ public function dataForTestParseAttributes(): iterable yield [new Cursor('{ target=_blank}'), ['target' => '_blank']]; yield [new Cursor('{target=_blank }'), ['target' => '_blank']]; yield [new Cursor('{ target=_blank }'), ['target' => '_blank']]; + yield [new Cursor('{disabled}'), ['disabled' => '']]; // Stuff at the beginning yield [new Cursor(' {: #custom-id }'), ['id' => 'custom-id']]; From cdf4ffda3b5232201fbfe50dc2c82467668086a6 Mon Sep 17 00:00:00 2001 From: Sven Luijten Date: Mon, 31 Jul 2023 16:38:35 +0200 Subject: [PATCH 2/5] Leave base regex alone, move optionality into attributes --- src/Extension/Attributes/Util/AttributesHelper.php | 2 +- src/Util/RegexHelper.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Extension/Attributes/Util/AttributesHelper.php b/src/Extension/Attributes/Util/AttributesHelper.php index cf096cc25f..01a9a40f0d 100644 --- a/src/Extension/Attributes/Util/AttributesHelper.php +++ b/src/Extension/Attributes/Util/AttributesHelper.php @@ -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-z0-9-]+|' . RegexHelper::PARTIAL_ATTRIBUTENAME . RegexHelper::PARTIAL_ATTRIBUTEVALUESPEC . '?)\s*'; private const ATTRIBUTE_LIST = '/^{:?(' . self::SINGLE_ATTRIBUTE . ')+}/i'; /** diff --git a/src/Util/RegexHelper.php b/src/Util/RegexHelper.php index 18ee38be7d..8ead731e2b 100644 --- a/src/Util/RegexHelper.php +++ b/src/Util/RegexHelper.php @@ -47,7 +47,7 @@ final class RegexHelper public const PARTIAL_SINGLEQUOTEDVALUE = '\'[^\']*\''; public const PARTIAL_DOUBLEQUOTEDVALUE = '"[^"]*"'; public const PARTIAL_ATTRIBUTEVALUE = '(?:' . self::PARTIAL_UNQUOTEDVALUE . '|' . self::PARTIAL_SINGLEQUOTEDVALUE . '|' . self::PARTIAL_DOUBLEQUOTEDVALUE . ')'; - public const PARTIAL_ATTRIBUTEVALUESPEC = '(?:' . '\s*=?' . '\s*' . self::PARTIAL_ATTRIBUTEVALUE . '?)'; + public const PARTIAL_ATTRIBUTEVALUESPEC = '(?:' . '\s*=' . '\s*' . self::PARTIAL_ATTRIBUTEVALUE . ')'; public const PARTIAL_ATTRIBUTE = '(?:' . '\s+' . self::PARTIAL_ATTRIBUTENAME . self::PARTIAL_ATTRIBUTEVALUESPEC . '?)'; public const PARTIAL_OPENTAG = '<' . self::PARTIAL_TAGNAME . self::PARTIAL_ATTRIBUTE . '*' . '\s*\/?>'; public const PARTIAL_CLOSETAG = '<\/' . self::PARTIAL_TAGNAME . '\s*[>]'; From d6c5d781b3e488b73db54d1557b19ced4d27f209 Mon Sep 17 00:00:00 2001 From: Sven Luijten Date: Mon, 31 Jul 2023 16:39:10 +0200 Subject: [PATCH 3/5] Set attribute to true if it has no value --- src/Extension/Attributes/Util/AttributesHelper.php | 8 +++++--- .../Extension/Attributes/Util/AttributesHelperTest.php | 8 ++++---- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/Extension/Attributes/Util/AttributesHelper.php b/src/Extension/Attributes/Util/AttributesHelper.php index 01a9a40f0d..8f094fddbd 100644 --- a/src/Extension/Attributes/Util/AttributesHelper.php +++ b/src/Extension/Attributes/Util/AttributesHelper.php @@ -72,12 +72,14 @@ public static function parseAttributes(Cursor $cursor): array continue; } - if (\strpos($attribute, '=') === false) { - $attribute .= '=""'; + $parts = \explode('=', $attribute, 2); + if (\count($parts) === 1) { + $attributes[$attribute] = true; + continue; } /** @psalm-suppress PossiblyUndefinedArrayOffset */ - [$name, $value] = \explode('=', $attribute, 2); + [$name, $value] = $parts; $first = $value[0]; $last = \substr($value, -1); diff --git a/tests/unit/Extension/Attributes/Util/AttributesHelperTest.php b/tests/unit/Extension/Attributes/Util/AttributesHelperTest.php index 21ebac2d7a..93da345906 100644 --- a/tests/unit/Extension/Attributes/Util/AttributesHelperTest.php +++ b/tests/unit/Extension/Attributes/Util/AttributesHelperTest.php @@ -53,12 +53,12 @@ public function dataForTestParseAttributes(): iterable yield [new Cursor('{: #custom-id }'), ['id' => 'custom-id']]; yield [new Cursor('{: #custom-id #another-id }'), ['id' => 'another-id']]; yield [new Cursor('{: .class1 .class2 }'), ['class' => 'class1 class2']]; - yield [new Cursor('{: #custom-id .class1 .class2 title="My Title" }'), ['id' => 'custom-id', 'class' => 'class1 class2', 'title' => 'My Title']]; + yield [new Cursor('{: #custom-id .class1 .class2 title="My Title" disabled }'), ['id' => 'custom-id', 'class' => 'class1 class2', 'title' => 'My Title', 'disabled' => true]]; yield [new Cursor('{:target=_blank}'), ['target' => '_blank']]; yield [new Cursor('{: target=_blank}'), ['target' => '_blank']]; yield [new Cursor('{: target=_blank }'), ['target' => '_blank']]; yield [new Cursor('{: target=_blank }'), ['target' => '_blank']]; - yield [new Cursor('{: disabled}'), ['disabled' => '']]; + yield [new Cursor('{: disabled}'), ['disabled' => true]]; // Examples without colons yield [new Cursor('{title="My Title"}'), ['title' => 'My Title']]; @@ -69,12 +69,12 @@ public function dataForTestParseAttributes(): iterable yield [new Cursor('{ #custom-id }'), ['id' => 'custom-id']]; yield [new Cursor('{ #custom-id #another-id }'), ['id' => 'another-id']]; yield [new Cursor('{ .class1 .class2 }'), ['class' => 'class1 class2']]; - yield [new Cursor('{ #custom-id .class1 .class2 title="My Title" }'), ['id' => 'custom-id', 'class' => 'class1 class2', 'title' => 'My Title']]; + yield [new Cursor('{ #custom-id .class1 .class2 title="My Title" disabled }'), ['id' => 'custom-id', 'class' => 'class1 class2', 'title' => 'My Title', 'disabled' => true]]; yield [new Cursor('{target=_blank}'), ['target' => '_blank']]; yield [new Cursor('{ target=_blank}'), ['target' => '_blank']]; yield [new Cursor('{target=_blank }'), ['target' => '_blank']]; yield [new Cursor('{ target=_blank }'), ['target' => '_blank']]; - yield [new Cursor('{disabled}'), ['disabled' => '']]; + yield [new Cursor('{disabled}'), ['disabled' => true]]; // Stuff at the beginning yield [new Cursor(' {: #custom-id }'), ['id' => 'custom-id']]; From 86c2d06e8b1c879ea66363341bd2cdc380961a23 Mon Sep 17 00:00:00 2001 From: Colin O'Dell Date: Mon, 31 Jul 2023 13:24:45 -0400 Subject: [PATCH 4/5] Add end-to-end test for attributes without values --- .../Extension/Attributes/data/special_attributes.html | 1 + .../functional/Extension/Attributes/data/special_attributes.md | 2 ++ 2 files changed, 3 insertions(+) diff --git a/tests/functional/Extension/Attributes/data/special_attributes.html b/tests/functional/Extension/Attributes/data/special_attributes.html index 7826cb4553..670299c6fe 100644 --- a/tests/functional/Extension/Attributes/data/special_attributes.html +++ b/tests/functional/Extension/Attributes/data/special_attributes.html @@ -12,3 +12,4 @@

The Site

Attributes without quote and non-whitespace char link

Attributes without quote and non-whitespace char and a dot link.

Multiple attributes without quote and non-whitespace char and a dot link.

+

image

diff --git a/tests/functional/Extension/Attributes/data/special_attributes.md b/tests/functional/Extension/Attributes/data/special_attributes.md index f66089ced1..3e73c1c7f2 100644 --- a/tests/functional/Extension/Attributes/data/special_attributes.md +++ b/tests/functional/Extension/Attributes/data/special_attributes.md @@ -28,3 +28,5 @@ Attributes without quote and non-whitespace char [link](http://url.com){target=_ Attributes without quote and non-whitespace char and a dot [link](http://url.com){target=_blank}. Multiple attributes without quote and non-whitespace char and a dot [link](http://url.com){#id .class target=_blank}. + +![image](/assets/image.jpg){valueless-attribute} From a8076d813a864071ef7415499f6d9d8ca80232cb Mon Sep 17 00:00:00 2001 From: Colin O'Dell Date: Mon, 31 Jul 2023 13:24:09 -0400 Subject: [PATCH 5/5] Update CHANGELOG --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c43dbf0a9..53920b982a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ Updates should follow the [Keep a CHANGELOG](https://keepachangelog.com/) princi ## [Unreleased][unreleased] +### Added + +- The `AttributesExtension` now supports attributes without values (#985, #986) + ## [2.4.0] - 2023-03-24 ### Added