Ensure we have the correct rule on the stack.
Change-Id: Ie814df7b759a2381be0b815eeefdb5d1f7adcde0
$search = $searchBase;
if ( $stack->top === false ) {
$currentClosing = '';
- } elseif (
- $stack->top->close === '}-' &&
- $stack->top->count > 2
- ) {
- # adjust closing for -{{{...{{
- $currentClosing = '}';
- $search .= $currentClosing;
} else {
$currentClosing = $stack->top->close;
$search .= $currentClosing;
strspn( $text, $curChar[$curLen - 1], $i + 1 ) + 1 :
strspn( $text, $curChar, $i );
+ $savedPrefix = '';
+ $lineStart = ( $i > 0 && $text[$i - 1] == "\n" );
+
+ if ( $curChar === "-{" && $count > $curLen ) {
+ // -{ => {{ transition because rightmost wins
+ $savedPrefix = '-';
+ $i++;
+ $curChar = '{';
+ $count--;
+ $rule = $this->rules[$curChar];
+ }
+
# we need to add to stack only if opening brace count is enough for one of the rules
if ( $count >= $rule['min'] ) {
# Add it to the stack
$piece = [
'open' => $curChar,
'close' => $rule['end'],
+ 'savedPrefix' => $savedPrefix,
'count' => $count,
- 'lineStart' => ( $i > 0 && $text[$i - 1] == "\n" ),
+ 'lineStart' => $lineStart,
];
$stack->push( $piece );
}
} else {
# Add literal brace(s)
- $accum .= htmlspecialchars( str_repeat( $curChar, $count ) );
+ $accum .= htmlspecialchars( $savedPrefix . str_repeat( $curChar, $count ) );
}
$i += $count;
} elseif ( $found == 'close' ) {
# check for maximum matching characters (if there are 5 closing
# characters, we will probably need only 3 - depending on the rules)
$rule = $this->rules[$piece->open];
- if ( $piece->close === '}-' && $piece->count > 2 ) {
- # tweak for -{..{{ }}..}-
- $rule = $this->rules['{'];
- }
if ( $count > $rule['max'] ) {
# The specified maximum exists in the callback array, unless the caller
# has made an error
# The invocation is at the start of the line if lineStart is set in
# the stack, and all opening brackets are used up.
- if ( $maxCount == $matchingCount && !empty( $piece->lineStart ) ) {
+ if ( $maxCount == $matchingCount &&
+ !empty( $piece->lineStart ) &&
+ strlen( $piece->savedPrefix ) == 0 ) {
$attr = ' lineStart="1"';
} else {
$attr = '';
if ( $piece->count >= $min ) {
$stack->push( $piece );
$accum =& $stack->getAccum();
+ } elseif ( $piece->count == 1 && $piece->open === '{' && $piece->savedPrefix === '-' ) {
+ $piece->savedPrefix = '';
+ $piece->open = '-{';
+ $piece->count = 2;
+ $piece->close = $this->rules[$piece->open]['end'];
+ $stack->push( $piece );
+ $accum =& $stack->getAccum();
} else {
$s = substr( $piece->open, 0, -1 );
$s .= str_repeat(
substr( $piece->open, -1 ),
$piece->count - strlen( $s )
);
- $accum .= $s;
+ $accum .= $piece->savedPrefix . $s;
}
+ } elseif ( $piece->savedPrefix !== '' ) {
+ $accum .= $piece->savedPrefix;
}
+
$stackFlags = $stack->getFlags();
if ( isset( $stackFlags['findEquals'] ) ) {
$findEquals = $stackFlags['findEquals'];
*/
public $close;
+ /**
+ * @var string Saved prefix that may affect later processing,
+ * e.g. to differentiate `-{{{{` and `{{{{` after later seeing `}}}`.
+ */
+ public $savedPrefix = '';
+
/**
* @var int Number of opening characters found (number of "=" for heading)
*/
*/
public function breakSyntax( $openingCount = false ) {
if ( $this->open == "\n" ) {
- $s = $this->parts[0]->out;
+ $s = $this->savedPrefix . $this->parts[0]->out;
} else {
if ( $openingCount === false ) {
$openingCount = $this->count;
substr( $this->open, -1 ),
$openingCount - strlen( $s )
);
+ $s = $this->savedPrefix . $s;
$first = true;
foreach ( $this->parts as $part ) {
if ( $first ) {
$search = $searchBase;
if ( $stack->top === false ) {
$currentClosing = '';
- } elseif (
- $stack->top->close === '}-' &&
- $stack->top->count > 2
- ) {
- # adjust closing for -{{{...{{
- $currentClosing = '}';
- $search .= $currentClosing;
} else {
$currentClosing = $stack->top->close;
$search .= $currentClosing;
strspn( $text, $curChar[$curLen - 1], $i + 1 ) + 1 :
strspn( $text, $curChar, $i );
+ $savedPrefix = '';
+ $lineStart = ( $i > 0 && $text[$i - 1] == "\n" );
+
+ if ( $curChar === "-{" && $count > $curLen ) {
+ // -{ => {{ transition because rightmost wins
+ $savedPrefix = '-';
+ $i++;
+ $curChar = '{';
+ $count--;
+ $rule = $this->rules[$curChar];
+ }
+
# we need to add to stack only if opening brace count is enough for one of the rules
if ( $count >= $rule['min'] ) {
# Add it to the stack
$piece = [
'open' => $curChar,
'close' => $rule['end'],
+ 'savedPrefix' => $savedPrefix,
'count' => $count,
- 'lineStart' => ( $i > 0 && $text[$i - 1] == "\n" ),
+ 'lineStart' => $lineStart,
];
$stack->push( $piece );
}
} else {
# Add literal brace(s)
- self::addLiteral( $accum, str_repeat( $curChar, $count ) );
+ self::addLiteral( $accum, $savedPrefix . str_repeat( $curChar, $count ) );
}
$i += $count;
} elseif ( $found == 'close' ) {
# check for maximum matching characters (if there are 5 closing
# characters, we will probably need only 3 - depending on the rules)
$rule = $this->rules[$piece->open];
- if ( $piece->close === '}-' && $piece->count > 2 ) {
- # tweak for -{..{{ }}..}-
- $rule = $this->rules['{'];
- }
if ( $count > $rule['max'] ) {
# The specified maximum exists in the callback array, unless the caller
# has made an error
# The invocation is at the start of the line if lineStart is set in
# the stack, and all opening brackets are used up.
- if ( $maxCount == $matchingCount && !empty( $piece->lineStart ) ) {
+ if ( $maxCount == $matchingCount &&
+ !empty( $piece->lineStart ) &&
+ strlen( $piece->savedPrefix ) == 0 ) {
$children[] = [ '@lineStart', [ 1 ] ];
}
$titleNode = [ 'title', $titleAccum ];
if ( $piece->count >= $min ) {
$stack->push( $piece );
$accum =& $stack->getAccum();
+ } elseif ( $piece->count == 1 && $piece->open === '{' && $piece->savedPrefix === '-' ) {
+ $piece->savedPrefix = '';
+ $piece->open = '-{';
+ $piece->count = 2;
+ $piece->close = $this->rules[$piece->open]['end'];
+ $stack->push( $piece );
+ $accum =& $stack->getAccum();
} else {
$s = substr( $piece->open, 0, -1 );
$s .= str_repeat(
substr( $piece->open, -1 ),
$piece->count - strlen( $s )
);
- self::addLiteral( $accum, $s );
+ self::addLiteral( $accum, $piece->savedPrefix . $s );
}
+ } elseif ( $piece->savedPrefix !== '' ) {
+ self::addLiteral( $accum, $piece->savedPrefix );
}
$stackFlags = $stack->getFlags();
*/
public function breakSyntax( $openingCount = false ) {
if ( $this->open == "\n" ) {
- $accum = $this->parts[0]->out;
+ $accum = array_merge( [ $this->savedPrefix ], $this->parts[0]->out );
} else {
if ( $openingCount === false ) {
$openingCount = $this->count;
substr( $this->open, -1 ),
$openingCount - strlen( $s )
);
- $accum = [ $s ];
+ $accum = [ $this->savedPrefix . $s ];
$lastIndex = 0;
$first = true;
foreach ( $this->parts as $part ) {
<p about="#mwt1" typeof="mw:Transclusion" data-mw='{"parts":[{"template":{"target":{"wt":"echo","href":"./Template:Echo"},"params":{"1":{"wt":"hi {{}}"}},"i":0}}]}'>hi {{}}</p>
!! end
+!! test
+Preprocessor precedence 18: another rightmost wins scenario
+!! options
+parsoid=wt2html
+!! wikitext
+{{ -{{{{1|tplarg}}} }} }-
+!! html/php
+<p>{{ -{tplarg }} }-
+</p>
+!! html/parsoid
+<p>{{ -{<span about="#mwt1" typeof="mw:Param" data-mw='{"parts":[{"templatearg":{"target":{"wt":"1"},"params":{"1":{"wt":"tplarg"}},"i":0}}]}'>tplarg</span> }} }-</p>
+!! end
+
+!! test
+Preprocessor precedence 19: break syntax
+!! options
+parsoid=wt2html
+!! wikitext
+-{{
+!! html/php
+<p>-{{
+</p>
+!! html/parsoid
+<p>-{{</p>
+!! end
+
###
### Token Stream Patcher tests
###