|
|
@ -3,53 +3,106 @@ |
|
|
|
'use strict'; |
|
|
|
|
|
|
|
|
|
|
|
module.exports = function link_pairs(state) { |
|
|
|
var i, j, lastDelim, currDelim, |
|
|
|
delimiters = state.delimiters, |
|
|
|
max = state.delimiters.length; |
|
|
|
function processDelimiters(state, delimiters) { |
|
|
|
var closerIdx, openerIdx, closer, opener, minOpenerIdx, newMinOpenerIdx, |
|
|
|
isOddMatch, lastJump, |
|
|
|
openersBottom = {}, |
|
|
|
max = delimiters.length; |
|
|
|
|
|
|
|
for (closerIdx = 0; closerIdx < max; closerIdx++) { |
|
|
|
closer = delimiters[closerIdx]; |
|
|
|
|
|
|
|
// Length is only used for emphasis-specific "rule of 3",
|
|
|
|
// if it's not defined (in strikethrough or 3rd party plugins),
|
|
|
|
// we can default it to 0 to disable those checks.
|
|
|
|
//
|
|
|
|
closer.length = closer.length || 0; |
|
|
|
|
|
|
|
if (!closer.close) continue; |
|
|
|
|
|
|
|
for (i = 0; i < max; i++) { |
|
|
|
lastDelim = delimiters[i]; |
|
|
|
// Previously calculated lower bounds (previous fails)
|
|
|
|
// for each marker and each delimiter length modulo 3.
|
|
|
|
if (!openersBottom.hasOwnProperty(closer.marker)) { |
|
|
|
openersBottom[closer.marker] = [ -1, -1, -1 ]; |
|
|
|
} |
|
|
|
|
|
|
|
minOpenerIdx = openersBottom[closer.marker][closer.length % 3]; |
|
|
|
newMinOpenerIdx = -1; |
|
|
|
|
|
|
|
if (!lastDelim.close) { continue; } |
|
|
|
openerIdx = closerIdx - closer.jump - 1; |
|
|
|
|
|
|
|
j = i - lastDelim.jump - 1; |
|
|
|
for (; openerIdx > minOpenerIdx; openerIdx -= opener.jump + 1) { |
|
|
|
opener = delimiters[openerIdx]; |
|
|
|
|
|
|
|
while (j >= 0) { |
|
|
|
currDelim = delimiters[j]; |
|
|
|
if (opener.marker !== closer.marker) continue; |
|
|
|
|
|
|
|
if (currDelim.open && |
|
|
|
currDelim.marker === lastDelim.marker && |
|
|
|
currDelim.end < 0 && |
|
|
|
currDelim.level === lastDelim.level) { |
|
|
|
if (newMinOpenerIdx === -1) newMinOpenerIdx = openerIdx; |
|
|
|
|
|
|
|
var odd_match = false; |
|
|
|
if (opener.open && |
|
|
|
opener.end < 0 && |
|
|
|
opener.level === closer.level) { |
|
|
|
|
|
|
|
// typeofs are for backward compatibility with plugins
|
|
|
|
if ((currDelim.close || lastDelim.open) && |
|
|
|
typeof currDelim.length !== 'undefined' && |
|
|
|
typeof lastDelim.length !== 'undefined') { |
|
|
|
isOddMatch = false; |
|
|
|
|
|
|
|
// from spec:
|
|
|
|
// sum of the lengths [...] must not be a multiple of 3
|
|
|
|
// unless both lengths are multiples of 3
|
|
|
|
if ((currDelim.length + lastDelim.length) % 3 === 0) { |
|
|
|
if (currDelim.length % 3 !== 0 || lastDelim.length % 3 !== 0) { |
|
|
|
odd_match = true; |
|
|
|
//
|
|
|
|
// If one of the delimiters can both open and close emphasis, then the
|
|
|
|
// sum of the lengths of the delimiter runs containing the opening and
|
|
|
|
// closing delimiters must not be a multiple of 3 unless both lengths
|
|
|
|
// are multiples of 3.
|
|
|
|
//
|
|
|
|
if (opener.close || closer.open) { |
|
|
|
if ((opener.length + closer.length) % 3 === 0) { |
|
|
|
if (opener.length % 3 !== 0 || closer.length % 3 !== 0) { |
|
|
|
isOddMatch = true; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if (!odd_match) { |
|
|
|
lastDelim.jump = i - j; |
|
|
|
lastDelim.open = false; |
|
|
|
currDelim.end = i; |
|
|
|
currDelim.jump = 0; |
|
|
|
if (!isOddMatch) { |
|
|
|
// If previous delimiter cannot be an opener, we can safely skip
|
|
|
|
// the entire sequence in future checks. This is required to make
|
|
|
|
// sure algorithm has linear complexity (see *_*_*_*_*_... case).
|
|
|
|
//
|
|
|
|
lastJump = openerIdx > 0 && !delimiters[openerIdx - 1].open ? |
|
|
|
delimiters[openerIdx - 1].jump + 1 : |
|
|
|
0; |
|
|
|
|
|
|
|
closer.jump = closerIdx - openerIdx + lastJump; |
|
|
|
closer.open = false; |
|
|
|
opener.end = closerIdx; |
|
|
|
opener.jump = lastJump; |
|
|
|
opener.close = false; |
|
|
|
newMinOpenerIdx = -1; |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if (newMinOpenerIdx !== -1) { |
|
|
|
// If match for this delimiter run failed, we want to set lower bound for
|
|
|
|
// future lookups. This is required to make sure algorithm has linear
|
|
|
|
// complexity.
|
|
|
|
//
|
|
|
|
// See details here:
|
|
|
|
// https://github.com/commonmark/cmark/issues/178#issuecomment-270417442
|
|
|
|
//
|
|
|
|
openersBottom[closer.marker][(closer.length || 0) % 3] = newMinOpenerIdx; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
module.exports = function link_pairs(state) { |
|
|
|
var curr, |
|
|
|
tokens_meta = state.tokens_meta, |
|
|
|
max = state.tokens_meta.length; |
|
|
|
|
|
|
|
processDelimiters(state, state.delimiters); |
|
|
|
|
|
|
|
j -= currDelim.jump + 1; |
|
|
|
for (curr = 0; curr < max; curr++) { |
|
|
|
if (tokens_meta[curr] && tokens_meta[curr].delimiters) { |
|
|
|
processDelimiters(state, tokens_meta[curr].delimiters); |
|
|
|
} |
|
|
|
} |
|
|
|
}; |
|
|
|