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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 17 additions & 2 deletions src/Converter/EmphasisConverter.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,27 @@ public function convert(ElementInterface $element): string
* the start or end $style, respectively. This prevents <em>foo</em><em>bar</em> from
* being converted to *foo**bar* which is incorrect. We want *foobar* instead.
*/
$preStyle = $this->getNormTag($element->getPreviousSibling()) === $tag ? '' : $style;
$postStyle = $this->getNormTag($element->getNextSibling()) === $tag ? '' : $style;
$preStyle = $this->isMergeableSibling($element->getPreviousSibling(), $tag) ? '' : $style;
$postStyle = $this->isMergeableSibling($element->getNextSibling(), $tag) ? '' : $style;

return $prefix . $preStyle . \trim($value) . $postStyle . $suffix;
}

/**
* A same-type sibling only emits emphasis markers when its trimmed content is
* truthy; a sibling whose trimmed value is falsy (whitespace-only, or "0")
* returns its bare value (see the early return in convert()) and emits none.
* Suppressing our marker is only correct in the former case, otherwise the
* output is left unbalanced (issue #252). The truthiness check mirrors
* convert()'s `! \trim($value)` guard so the two stay consistent.
*/
private function isMergeableSibling(?ElementInterface $sibling, string $tag): bool
{
return $sibling !== null
&& $this->getNormTag($sibling) === $tag
&& (bool) \trim($sibling->getValue());
}

/**
* @return string[]
*/
Expand Down
10 changes: 10 additions & 0 deletions tests/HtmlConverterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,16 @@ public function testConsecutiveSpans(): void
$this->assertHtmlGivesMarkdown('<em>Foo</em> <em>Bar</em>', '*Foo* *Bar*');
$this->assertHtmlGivesMarkdown('<strong>Foo</strong> <strong>Bar</strong>', '**Foo** **Bar**');
$this->assertHtmlGivesMarkdown('<strong>Foo</strong><b>Bar</b><em>Foo</em>', '**FooBar***Foo*');
// A whitespace-only neighbour of the same type emits no markers, so the
// adjacent span must keep its own instead of merging (issue #252).
$this->assertHtmlGivesMarkdown('<strong> </strong><strong>hello</strong>', ' **hello**');
$this->assertHtmlGivesMarkdown('<em> </em><em>hello</em>', ' *hello*');
$this->assertHtmlGivesMarkdown('<strong>hello</strong><strong> </strong>', '**hello** ');
$this->assertHtmlGivesMarkdown('<em>hello</em><em> </em>', '*hello* ');
// A same-type neighbour whose trimmed content is falsy (e.g. "0") also
// emits no markers, so the adjacent span must keep its own (issue #252).
$this->assertHtmlGivesMarkdown('<em>0</em><em>hello</em>', '0*hello*');
$this->assertHtmlGivesMarkdown('<em>hello</em><em>0</em>', '*hello*0');
}

public function testNesting(): void
Expand Down