123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130 |
- // For each opening emphasis-like marker find a matching closing one
- //
- 'use strict';
- function processDelimiters(state, delimiters) {
- var closerIdx, openerIdx, closer, opener, minOpenerIdx, newMinOpenerIdx,
- isOddMatch, lastJump,
- openersBottom = {},
- max = delimiters.length;
- if (!max) return;
- // headerIdx is the first delimiter of the current (where closer is) delimiter run
- var headerIdx = 0;
- var lastTokenIdx = -2; // needs any value lower than -1
- var jumps = [];
- for (closerIdx = 0; closerIdx < max; closerIdx++) {
- closer = delimiters[closerIdx];
- jumps.push(0);
- // markers belong to same delimiter run if:
- // - they have adjacent tokens
- // - AND markers are the same
- //
- if (delimiters[headerIdx].marker !== closer.marker || lastTokenIdx !== closer.token - 1) {
- headerIdx = closerIdx;
- }
- lastTokenIdx = closer.token;
- // 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;
- // Previously calculated lower bounds (previous fails)
- // for each marker, each delimiter length modulo 3,
- // and for whether this closer can be an opener;
- // https://github.com/commonmark/cmark/commit/34250e12ccebdc6372b8b49c44fab57c72443460
- if (!openersBottom.hasOwnProperty(closer.marker)) {
- openersBottom[closer.marker] = [ -1, -1, -1, -1, -1, -1 ];
- }
- minOpenerIdx = openersBottom[closer.marker][(closer.open ? 3 : 0) + (closer.length % 3)];
- openerIdx = headerIdx - jumps[headerIdx] - 1;
- newMinOpenerIdx = openerIdx;
- for (; openerIdx > minOpenerIdx; openerIdx -= jumps[openerIdx] + 1) {
- opener = delimiters[openerIdx];
- if (opener.marker !== closer.marker) continue;
- if (opener.open && opener.end < 0) {
- isOddMatch = false;
- // from spec:
- //
- // 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 (!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 ?
- jumps[openerIdx - 1] + 1 :
- 0;
- jumps[closerIdx] = closerIdx - openerIdx + lastJump;
- jumps[openerIdx] = lastJump;
- closer.open = false;
- opener.end = closerIdx;
- opener.close = false;
- newMinOpenerIdx = -1;
- // treat next token as start of run,
- // it optimizes skips in **<...>**a**<...>** pathological case
- lastTokenIdx = -2;
- 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.open ? 3 : 0) + ((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);
- for (curr = 0; curr < max; curr++) {
- if (tokens_meta[curr] && tokens_meta[curr].delimiters) {
- processDelimiters(state, tokens_meta[curr].delimiters);
- }
- }
- };
|