|
@ -9,97 +9,54 @@ function isAlphaNum(code) { |
|
|
(code >= 0x61 /* a */ && code <= 0x7A /* z */); |
|
|
(code >= 0x61 /* a */ && code <= 0x7A /* z */); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// returns the amount of markers (1, 2, 3, 4+), or -1 on failure;
|
|
|
// parse sequence of emphasis markers,
|
|
|
// "start" should point at a valid marker
|
|
|
// "start" should point at a valid marker
|
|
|
//
|
|
|
function scanDelims(state, start) { |
|
|
// note: in case if 4+ markers it is still not a valid emphasis,
|
|
|
|
|
|
// should be treated as a special case
|
|
|
|
|
|
function parseStart(state, start) { |
|
|
|
|
|
var pos = start, lastChar, nextChar, count, |
|
|
var pos = start, lastChar, nextChar, count, |
|
|
|
|
|
can_open = true, |
|
|
|
|
|
can_close = true, |
|
|
max = state.posMax, |
|
|
max = state.posMax, |
|
|
marker = state.src.charCodeAt(start); |
|
|
marker = state.src.charCodeAt(start); |
|
|
|
|
|
|
|
|
lastChar = start > 0 ? state.src.charCodeAt(start - 1) : -1; |
|
|
lastChar = start > 0 ? state.src.charCodeAt(start - 1) : -1; |
|
|
|
|
|
|
|
|
while (pos < max && state.src.charCodeAt(pos) === marker) { pos++; } |
|
|
while (pos < max && state.src.charCodeAt(pos) === marker) { pos++; } |
|
|
if (pos >= max) { return -1; } |
|
|
if (pos >= max) { can_open = false; } |
|
|
count = pos - start; |
|
|
count = pos - start; |
|
|
|
|
|
|
|
|
// Quoting spec:
|
|
|
|
|
|
//
|
|
|
|
|
|
// Character can open emphasis iff
|
|
|
|
|
|
// 1. it is not part of a sequence of four or more unescaped markers,
|
|
|
|
|
|
// 2. it is not followed by whitespace,
|
|
|
|
|
|
// 3. it is "_" and it is not preceded by an ASCII alphanumeric character, and
|
|
|
|
|
|
// 4. either it is not followed by a marker or it is followed immediately by strong emphasis.
|
|
|
|
|
|
|
|
|
|
|
|
if (count >= 4) { |
|
|
if (count >= 4) { |
|
|
// check condition 1
|
|
|
// sequence of four or more unescaped markers can't start/end an emphasis
|
|
|
// sequence of four or more unescaped markers can't start an emphasis
|
|
|
can_open = can_close = false; |
|
|
return count; |
|
|
} else { |
|
|
} |
|
|
nextChar = pos < max ? state.src.charCodeAt(pos) : -1; |
|
|
|
|
|
|
|
|
// check condition 2, marker followed by whitespace
|
|
|
// check whitespace conditions
|
|
|
nextChar = state.src.charCodeAt(pos); |
|
|
if (nextChar === 0x20 || nextChar === 0x0A) { can_open = false; } |
|
|
if (nextChar === 0x20 || nextChar === 0x0A) { return -1; } |
|
|
if (lastChar === 0x20 || lastChar === 0x0A) { can_close = false; } |
|
|
|
|
|
|
|
|
if (marker === 0x5F /* _ */) { |
|
|
if (marker === 0x5F /* _ */) { |
|
|
// check condition 3, if it's the beginning of the word
|
|
|
// check if we aren't inside the word
|
|
|
// we need to look back for this
|
|
|
if (isAlphaNum(lastChar)) { can_open = false; } |
|
|
if (isAlphaNum(lastChar)) { return -1; } |
|
|
if (isAlphaNum(nextChar)) { can_close = false; } |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
return count; |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// returns the amount of markers (1, 2, 3, 4+), or -1 on failure;
|
|
|
return { |
|
|
// "start" should point at a valid marker
|
|
|
can_open: can_open, |
|
|
//
|
|
|
can_close: can_close, |
|
|
// note: in case if 4+ markers it is still not a valid emphasis,
|
|
|
delims: count |
|
|
// should be treated as a special case
|
|
|
}; |
|
|
function parseEnd(state, start) { |
|
|
|
|
|
var pos = start, lastChar, count, |
|
|
|
|
|
max = state.posMax, |
|
|
|
|
|
marker = state.src.charCodeAt(start); |
|
|
|
|
|
|
|
|
|
|
|
lastChar = start > 0 ? state.src.charCodeAt(start - 1) : -1; |
|
|
|
|
|
|
|
|
|
|
|
while (pos < max && state.src.charCodeAt(pos) === marker) { pos++; } |
|
|
|
|
|
count = pos - start; |
|
|
|
|
|
|
|
|
|
|
|
// Quoting spec:
|
|
|
|
|
|
//
|
|
|
|
|
|
// Character can close emphasis iff
|
|
|
|
|
|
// 1. it is not part of a sequence of four or more unescaped markers,
|
|
|
|
|
|
// 2. it is not preceded by whitespace,
|
|
|
|
|
|
// 3. it is not "_" or it is not followed by an ASCII alphanumeric character
|
|
|
|
|
|
|
|
|
|
|
|
if (count >= 4) { |
|
|
|
|
|
// check condition 1
|
|
|
|
|
|
// sequence of four or more unescaped markers can't start an emphasis
|
|
|
|
|
|
return count; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// check condition 2, marker preceded by whitespace
|
|
|
|
|
|
if (lastChar === 0x20 || lastChar === 0x0A) { return -1; } |
|
|
|
|
|
|
|
|
|
|
|
if (marker === 0x5F) { |
|
|
|
|
|
// check condition 3, if it's the end of the word
|
|
|
|
|
|
if (pos < max && isAlphaNum(state.src.charCodeAt(pos))) { return -1; } |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return count; |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
module.exports = function emphasis(state, silent) { |
|
|
module.exports = function emphasis(state, silent) { |
|
|
var startCount, |
|
|
var startCount, |
|
|
count, |
|
|
count, |
|
|
oldFlag, |
|
|
|
|
|
found, |
|
|
found, |
|
|
ok, |
|
|
ok, |
|
|
oldCount, |
|
|
oldCount, |
|
|
newCount, |
|
|
newCount, |
|
|
stack, |
|
|
stack, |
|
|
|
|
|
res, |
|
|
max = state.posMax, |
|
|
max = state.posMax, |
|
|
start = state.pos, |
|
|
start = state.pos, |
|
|
haveLiteralAsterisk, |
|
|
haveLiteralAsterisk, |
|
@ -110,28 +67,26 @@ module.exports = function emphasis(state, silent) { |
|
|
// skip emphasis in links because it has lower priority, compare:
|
|
|
// skip emphasis in links because it has lower priority, compare:
|
|
|
// [foo *bar]()*
|
|
|
// [foo *bar]()*
|
|
|
// [foo `bar]()`
|
|
|
// [foo `bar]()`
|
|
|
if (state.validateInsideEm || state.validateInsideLink) { return false; } |
|
|
if (state.validateInsideLink) { return false; } |
|
|
|
|
|
|
|
|
startCount = parseStart(state, start); |
|
|
res = scanDelims(state, start); |
|
|
if (startCount < 0) { return false; } |
|
|
startCount = res.delims; |
|
|
if (startCount >= 4) { |
|
|
if (!res.can_open) { |
|
|
state.pos += startCount; |
|
|
state.pos += startCount; |
|
|
state.pending += state.src.slice(start, startCount); |
|
|
if (!silent) { state.pending += state.src.slice(start, state.pos); } |
|
|
return true; |
|
|
return true; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (state.level >= state.options.maxNesting) { return false; } |
|
|
if (state.level >= state.options.maxNesting) { return false; } |
|
|
|
|
|
|
|
|
oldFlag = state.validateInsideEm; |
|
|
|
|
|
|
|
|
|
|
|
state.pos = start + startCount; |
|
|
state.pos = start + startCount; |
|
|
stack = [ startCount ]; |
|
|
stack = [ startCount ]; |
|
|
state.validateInsideEm = true; |
|
|
|
|
|
|
|
|
|
|
|
while (state.pos < max) { |
|
|
while (state.pos < max) { |
|
|
if (state.src.charCodeAt(state.pos) === marker && !haveLiteralAsterisk) { |
|
|
if (state.src.charCodeAt(state.pos) === marker && !haveLiteralAsterisk) { |
|
|
count = parseEnd(state, state.pos); |
|
|
res = scanDelims(state, state.pos); |
|
|
if (count >= 1 && count < 4) { |
|
|
count = res.delims; |
|
|
|
|
|
if (res.can_close) { |
|
|
oldCount = stack.pop(); |
|
|
oldCount = stack.pop(); |
|
|
newCount = count; |
|
|
newCount = count; |
|
|
|
|
|
|
|
@ -158,8 +113,7 @@ module.exports = function emphasis(state, silent) { |
|
|
continue; |
|
|
continue; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
count = parseStart(state, state.pos); |
|
|
if (res.can_open) { |
|
|
if (count >= 1 && count < 4) { |
|
|
|
|
|
stack.push(count); |
|
|
stack.push(count); |
|
|
state.pos += count; |
|
|
state.pos += count; |
|
|
continue; |
|
|
continue; |
|
@ -176,9 +130,6 @@ module.exports = function emphasis(state, silent) { |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// restore old state
|
|
|
|
|
|
state.validateInsideEm = oldFlag; |
|
|
|
|
|
|
|
|
|
|
|
if (!found) { |
|
|
if (!found) { |
|
|
// parser failed to find ending tag, so it's not valid emphasis
|
|
|
// parser failed to find ending tag, so it's not valid emphasis
|
|
|
state.pos = start; |
|
|
state.pos = start; |
|
@ -186,10 +137,10 @@ module.exports = function emphasis(state, silent) { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// found!
|
|
|
// found!
|
|
|
if (!silent) { |
|
|
|
|
|
state.posMax = state.pos; |
|
|
state.posMax = state.pos; |
|
|
state.pos = start + startCount; |
|
|
state.pos = start + startCount; |
|
|
|
|
|
|
|
|
|
|
|
if (!silent) { |
|
|
if (startCount === 2 || startCount === 3) { |
|
|
if (startCount === 2 || startCount === 3) { |
|
|
state.push({ type: 'strong_open', level: state.level++ }); |
|
|
state.push({ type: 'strong_open', level: state.level++ }); |
|
|
} |
|
|
} |
|
|