@@ -127,28 +127,59 @@ public function iterateByCharacters($characters, $callback, Delimiter $stackBott
127127 $ characters = [$ characters ];
128128 }
129129
130+ $ openersBottom = array_fill_keys ($ characters , $ stackBottom );
131+
130132 // Find first closer above stackBottom
131133 $ closer = $ this ->findEarliest ($ stackBottom );
132134
133135 while ($ closer !== null ) {
134- if ($ closer ->canClose () && (in_array ($ closer ->getChar (), $ characters ))) {
136+ $ closerChar = $ closer ->getChar ();
137+ if ($ closer ->canClose () && (in_array ($ closerChar , $ characters ))) {
135138 // Found emphasis closer. Now look back for first matching opener:
136- $ opener = $ closer ->getPrevious ();
137- while ($ opener !== null && $ opener !== $ stackBottom ) {
138- if ($ opener ->getChar () === $ closer ->getChar () && $ opener ->canOpen ()) {
139- break ;
140- }
141- $ opener = $ opener ->getPrevious ();
142- }
139+ $ opener = $ this ->findFirstMatchingOpener ($ closer , $ openersBottom , $ stackBottom );
143140
144- if ($ opener !== null && $ opener !== $ stackBottom ) {
141+ $ oldCloser = $ closer ;
142+
143+ if ($ opener !== null ) {
145144 $ closer = $ callback ($ opener , $ closer , $ this );
146145 } else {
147146 $ closer = $ closer ->getNext ();
147+ // Set lower bound for future searches for openers:
148+ $ openersBottom [$ closerChar ] = $ oldCloser ->getPrevious ();
149+ if (!$ oldCloser ->canOpen ()) {
150+ // We can remove a closer that can't be an opener,
151+ // once we've seen there's no matching opener:
152+ $ this ->removeDelimiter ($ oldCloser );
153+ }
148154 }
149155 } else {
150156 $ closer = $ closer ->getNext ();
151157 }
152158 }
153159 }
160+
161+ /**
162+ * @param Delimiter $closer
163+ * @param array $openersBottom
164+ * @param Delimiter|null $stackBottom
165+ *
166+ * @return Delimiter|null
167+ */
168+ protected function findFirstMatchingOpener (Delimiter $ closer , $ openersBottom , Delimiter $ stackBottom = null )
169+ {
170+ $ closerChar = $ closer ->getChar ();
171+ $ opener = $ closer ->getPrevious ();
172+
173+ while ($ opener !== null && $ opener !== $ stackBottom && $ opener !== $ openersBottom [$ closerChar ]) {
174+ if ($ opener ->getChar () === $ closerChar && $ opener ->canOpen ()) {
175+ return $ opener ;
176+ }
177+
178+ $ opener = $ opener ->getPrevious ();
179+ }
180+
181+ return null ;
182+ }
183+
184+
154185}
0 commit comments