Completed
Branch wip/litedown (377511)
by Josh
03:42
created

Emphasis::execute()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 5
ccs 0
cts 4
cp 0
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 3
nc 1
nop 0
crap 2
1
<?php
2
3
/**
4
* @package   s9e\TextFormatter
5
* @copyright Copyright (c) 2010-2017 The s9e Authors
6
* @license   http://www.opensource.org/licenses/mit-license.php The MIT License
7
*/
8
namespace s9e\TextFormatter\Plugins\Litedown\Parser;
9
10
class Emphasis extends AbstractParser
11
{
12
	/**
13
	* {@inheritdoc}
14
	*/
15
	protected function execute()
16
	{
17
		$this->matchEmphasisByCharacter('*', '/\\*+/');
18
		$this->matchEmphasisByCharacter('_', '/_+/');
19
	}
20
21
	/**
22
	* Match emphasis and strong applied using given character
23
	*
24
	* @param  string $character Markup character, either * or _
25
	* @param  string $regexp    Regexp used to match the series of emphasis character
26
	* @return void
27
	*/
28
	protected function matchEmphasisByCharacter($character, $regexp)
29
	{
30
		$pos = strpos($this->text, $character);
31
		if ($pos === false)
32
		{
33
			return;
34
		}
35
36
		foreach ($this->getEmphasisByBlock($regexp, $pos) as $block)
37
		{
38
			$this->processEmphasisBlock($block);
39
		}
40
	}
41
42
	/**
43
	* Get emphasis markup split by block
44
	*
45
	* @param  string  $regexp Regexp used to match emphasis
46
	* @param  integer $pos    Position in the text of the first emphasis character
47
	* @return array[]         Each array contains a list of [matchPos, matchLen] pairs
48
	*/
49
	protected function getEmphasisByBlock($regexp, $pos)
50
	{
51
		$block    = [];
52
		$blocks   = [];
53
		$breakPos = strpos($this->text, "\x17", $pos);
54
55
		preg_match_all($regexp, $this->text, $matches, PREG_OFFSET_CAPTURE, $pos);
56
		foreach ($matches[0] as $m)
57
		{
58
			$matchPos = $m[1];
59
			$matchLen = strlen($m[0]);
60
61
			// Test whether we've just passed the limits of a block
62
			if ($matchPos > $breakPos)
63
			{
64
				$blocks[] = $block;
65
				$block    = [];
66
				$breakPos = strpos($this->text, "\x17", $matchPos);
67
			}
68
69
			// Test whether we should ignore this markup
70
			if (!$this->ignoreEmphasis($matchPos, $matchLen))
71
			{
72
				$block[] = [$matchPos, $matchLen];
73
			}
74
		}
75
		$blocks[] = $block;
76
77
		return $blocks;
78
	}
79
80
	/**
81
	* Test whether emphasis should be ignored at the given position in the text
82
	*
83
	* @param  integer $matchPos Position of the emphasis in the text
84
	* @param  integer $matchLen Length of the emphasis
85
	* @return bool
86
	*/
87
	protected function ignoreEmphasis($matchPos, $matchLen)
88
	{
89
		// Ignore single underscores between alphanumeric characters
90
		return ($this->text[$matchPos] === '_' && $matchLen === 1 && $this->isSurroundedByAlnum($matchPos, $matchLen));
91
	}
92
93
	/**
94
	* Process a list of emphasis markup strings
95
	*
96
	* @param  array[] $block List of [matchPos, matchLen] pairs
97
	* @return void
98
	*/
99
	protected function processEmphasisBlock(array $block)
100
	{
101
		$emPos     = null;
102
		$strongPos = null;
103
		foreach ($block as list($matchPos, $matchLen))
104
		{
105
			$canOpen      = !$this->isBeforeWhitespace($matchPos + $matchLen - 1);
106
			$canClose     = !$this->isAfterWhitespace($matchPos);
107
			$closeLen     = ($canClose) ? min($matchLen, 3) : 0;
108
			$closeEm      = ($closeLen & 1) && isset($emPos);
109
			$closeStrong  = ($closeLen & 2) && isset($strongPos);
110
			$emEndPos     = $matchPos;
111
			$strongEndPos = $matchPos;
112
			$remaining    = $matchLen;
113
114
			if (isset($emPos) && $emPos === $strongPos)
115
			{
116
				if ($closeEm)
117
				{
118
					$emPos += 2;
119
				}
120
				else
121
				{
122
					++$strongPos;
123
				}
124
			}
125
126
			if ($closeEm && $closeStrong)
127
			{
128
				if ($emPos < $strongPos)
129
				{
130
					$emEndPos += 2;
131
				}
132
				else
133
				{
134
					++$strongEndPos;
135
				}
136
			}
137
138
			if ($closeEm)
139
			{
140
				--$remaining;
141
				$this->parser->addTagPair('EM', $emPos, 1, $emEndPos, 1);
142
				$emPos = null;
143
			}
144
			if ($closeStrong)
145
			{
146
				$remaining -= 2;
147
				$this->parser->addTagPair('STRONG', $strongPos, 2, $strongEndPos, 2);
148
				$strongPos = null;
149
			}
150
151
			if ($canOpen)
152
			{
153
				$remaining = min($remaining, 3);
154
				if ($remaining & 1)
155
				{
156
					$emPos     = $matchPos + $matchLen - $remaining;
157
				}
158
				if ($remaining & 2)
159
				{
160
					$strongPos = $matchPos + $matchLen - $remaining;
161
				}
162
			}
163
		}
164
	}
165
}