From 0c0917048c8ff442db334b1cabf1a8b750de76c1 Mon Sep 17 00:00:00 2001 From: Alex Kocharin Date: Thu, 25 Dec 2014 10:56:43 +0300 Subject: [PATCH] Allow sequence of 4+ markers in pairs --- lib/rules_inline/del.js | 110 +++++++++++++++++------------ lib/rules_inline/ins.js | 110 +++++++++++++++++------------ lib/rules_inline/mark.js | 110 +++++++++++++++++------------ test/fixtures/markdown-it/del.txt | 24 +++++++ test/fixtures/markdown-it/ins.txt | 25 +++++-- test/fixtures/markdown-it/mark.txt | 24 +++++-- 6 files changed, 251 insertions(+), 152 deletions(-) diff --git a/lib/rules_inline/del.js b/lib/rules_inline/del.js index 0af767c..b91b34c 100644 --- a/lib/rules_inline/del.js +++ b/lib/rules_inline/del.js @@ -1,62 +1,80 @@ -// Process ~~deleted text~~ - 'use strict'; -module.exports = function del(state, silent) { - var found, - pos, + +// parse sequence of markers, +// "start" should point at a valid marker +function scanDelims(state, start) { + var pos = start, lastChar, nextChar, count, + can_open = true, + can_close = true, + 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++; } + if (pos >= max) { can_open = false; } + count = pos - start; + + nextChar = pos < max ? state.src.charCodeAt(pos) : -1; + + // check whitespace conditions + if (nextChar === 0x20 || nextChar === 0x0A) { can_open = false; } + if (lastChar === 0x20 || lastChar === 0x0A) { can_close = false; } + + return { + can_open: can_open, + can_close: can_close, + delims: count + }; +} + +module.exports = function(state, silent) { + var startCount, + count, + tagCount, + found, stack, + res, max = state.posMax, start = state.pos, - lastChar, - nextChar; + marker = state.src.charCodeAt(start); - if (state.src.charCodeAt(start) !== 0x7E/* ~ */) { return false; } + if (marker !== 0x7E/* ~ */) { return false; } if (silent) { return false; } // don't run any pairs in validation mode - if (start + 4 >= max) { return false; } - if (state.src.charCodeAt(start + 1) !== 0x7E/* ~ */) { return false; } - if (state.level >= state.options.maxNesting) { return false; } - lastChar = start > 0 ? state.src.charCodeAt(start - 1) : -1; - nextChar = state.src.charCodeAt(start + 2); - - if (lastChar === 0x7E/* ~ */) { return false; } - if (nextChar === 0x7E/* ~ */) { return false; } - if (nextChar === 0x20 || nextChar === 0x0A) { return false; } - - pos = start + 2; - while (pos < max && state.src.charCodeAt(pos) === 0x7E/* ~ */) { pos++; } - if (pos > start + 3) { - // sequence of 4+ markers taking as literal, same as in a emphasis - state.pos += pos - start; - if (!silent) { state.pending += state.src.slice(start, pos); } + res = scanDelims(state, start); + startCount = res.delims; + if (!res.can_open) { + state.pos += startCount; + if (!silent) { state.pending += state.src.slice(start, state.pos); } return true; } - state.pos = start + 2; - stack = 1; - - while (state.pos + 1 < max) { - if (state.src.charCodeAt(state.pos) === 0x7E/* ~ */) { - if (state.src.charCodeAt(state.pos + 1) === 0x7E/* ~ */) { - lastChar = state.src.charCodeAt(state.pos - 1); - nextChar = state.pos + 2 < max ? state.src.charCodeAt(state.pos + 2) : -1; - if (nextChar !== 0x7E/* ~ */ && lastChar !== 0x7E/* ~ */) { - if (lastChar !== 0x20 && lastChar !== 0x0A) { - // closing '~~' - stack--; - } else if (nextChar !== 0x20 && nextChar !== 0x0A) { - // opening '~~' - stack++; - } // else { - // // standalone ' ~~ ' indented with spaces - // } - if (stack <= 0) { - found = true; - break; - } + if (state.level >= state.options.maxNesting) { return false; } + stack = Math.floor(startCount / 2); + if (stack <= 0) { return false; } + state.pos = start + startCount; + + while (state.pos < max) { + if (state.src.charCodeAt(state.pos) === marker) { + res = scanDelims(state, state.pos); + count = res.delims; + tagCount = Math.floor(count / 2); + if (res.can_close) { + if (tagCount >= stack) { + state.pos += count - 2; + found = true; + break; } + stack -= tagCount; + state.pos += count; + continue; } + + if (res.can_open) { stack += tagCount; } + state.pos += count; + continue; } state.parser.skipToken(state); diff --git a/lib/rules_inline/ins.js b/lib/rules_inline/ins.js index 2a55981..2924ea7 100644 --- a/lib/rules_inline/ins.js +++ b/lib/rules_inline/ins.js @@ -1,62 +1,80 @@ -// Process ++inserted text++ - 'use strict'; -module.exports = function ins(state, silent) { - var found, - pos, + +// parse sequence of markers, +// "start" should point at a valid marker +function scanDelims(state, start) { + var pos = start, lastChar, nextChar, count, + can_open = true, + can_close = true, + 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++; } + if (pos >= max) { can_open = false; } + count = pos - start; + + nextChar = pos < max ? state.src.charCodeAt(pos) : -1; + + // check whitespace conditions + if (nextChar === 0x20 || nextChar === 0x0A) { can_open = false; } + if (lastChar === 0x20 || lastChar === 0x0A) { can_close = false; } + + return { + can_open: can_open, + can_close: can_close, + delims: count + }; +} + +module.exports = function(state, silent) { + var startCount, + count, + tagCount, + found, stack, + res, max = state.posMax, start = state.pos, - lastChar, - nextChar; + marker = state.src.charCodeAt(start); - if (state.src.charCodeAt(start) !== 0x2B/* + */) { return false; } + if (marker !== 0x2B/* + */) { return false; } if (silent) { return false; } // don't run any pairs in validation mode - if (start + 4 >= max) { return false; } - if (state.src.charCodeAt(start + 1) !== 0x2B/* + */) { return false; } - if (state.level >= state.options.maxNesting) { return false; } - lastChar = start > 0 ? state.src.charCodeAt(start - 1) : -1; - nextChar = state.src.charCodeAt(start + 2); - - if (lastChar === 0x2B/* + */) { return false; } - if (nextChar === 0x2B/* + */) { return false; } - if (nextChar === 0x20 || nextChar === 0x0A) { return false; } - - pos = start + 2; - while (pos < max && state.src.charCodeAt(pos) === 0x2B/* + */) { pos++; } - if (pos !== start + 2) { - // sequence of 3+ markers taking as literal, same as in a emphasis - state.pos += pos - start; - if (!silent) { state.pending += state.src.slice(start, pos); } + res = scanDelims(state, start); + startCount = res.delims; + if (!res.can_open) { + state.pos += startCount; + if (!silent) { state.pending += state.src.slice(start, state.pos); } return true; } - state.pos = start + 2; - stack = 1; - - while (state.pos + 1 < max) { - if (state.src.charCodeAt(state.pos) === 0x2B/* + */) { - if (state.src.charCodeAt(state.pos + 1) === 0x2B/* + */) { - lastChar = state.src.charCodeAt(state.pos - 1); - nextChar = state.pos + 2 < max ? state.src.charCodeAt(state.pos + 2) : -1; - if (nextChar !== 0x2B/* + */ && lastChar !== 0x2B/* + */) { - if (lastChar !== 0x20 && lastChar !== 0x0A) { - // closing '++' - stack--; - } else if (nextChar !== 0x20 && nextChar !== 0x0A) { - // opening '++' - stack++; - } // else { - // // standalone ' ++ ' indented with spaces - // } - if (stack <= 0) { - found = true; - break; - } + if (state.level >= state.options.maxNesting) { return false; } + stack = Math.floor(startCount / 2); + if (stack <= 0) { return false; } + state.pos = start + startCount; + + while (state.pos < max) { + if (state.src.charCodeAt(state.pos) === marker) { + res = scanDelims(state, state.pos); + count = res.delims; + tagCount = Math.floor(count / 2); + if (res.can_close) { + if (tagCount >= stack) { + state.pos += count - 2; + found = true; + break; } + stack -= tagCount; + state.pos += count; + continue; } + + if (res.can_open) { stack += tagCount; } + state.pos += count; + continue; } state.parser.skipToken(state); diff --git a/lib/rules_inline/mark.js b/lib/rules_inline/mark.js index d154ba4..e2c88dd 100644 --- a/lib/rules_inline/mark.js +++ b/lib/rules_inline/mark.js @@ -1,62 +1,80 @@ -// Process ==highlighted text== - 'use strict'; -module.exports = function del(state, silent) { - var found, - pos, + +// parse sequence of markers, +// "start" should point at a valid marker +function scanDelims(state, start) { + var pos = start, lastChar, nextChar, count, + can_open = true, + can_close = true, + 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++; } + if (pos >= max) { can_open = false; } + count = pos - start; + + nextChar = pos < max ? state.src.charCodeAt(pos) : -1; + + // check whitespace conditions + if (nextChar === 0x20 || nextChar === 0x0A) { can_open = false; } + if (lastChar === 0x20 || lastChar === 0x0A) { can_close = false; } + + return { + can_open: can_open, + can_close: can_close, + delims: count + }; +} + +module.exports = function(state, silent) { + var startCount, + count, + tagCount, + found, stack, + res, max = state.posMax, start = state.pos, - lastChar, - nextChar; + marker = state.src.charCodeAt(start); - if (state.src.charCodeAt(start) !== 0x3D/* = */) { return false; } + if (marker !== 0x3D/* = */) { return false; } if (silent) { return false; } // don't run any pairs in validation mode - if (start + 4 >= max) { return false; } - if (state.src.charCodeAt(start + 1) !== 0x3D/* = */) { return false; } - if (state.level >= state.options.maxNesting) { return false; } - lastChar = start > 0 ? state.src.charCodeAt(start - 1) : -1; - nextChar = state.src.charCodeAt(start + 2); - - if (lastChar === 0x3D/* = */) { return false; } - if (nextChar === 0x3D/* = */) { return false; } - if (nextChar === 0x20 || nextChar === 0x0A) { return false; } - - pos = start + 2; - while (pos < max && state.src.charCodeAt(pos) === 0x3D/* = */) { pos++; } - if (pos !== start + 2) { - // sequence of 3+ markers taking as literal, same as in a emphasis - state.pos += pos - start; - if (!silent) { state.pending += state.src.slice(start, pos); } + res = scanDelims(state, start); + startCount = res.delims; + if (!res.can_open) { + state.pos += startCount; + if (!silent) { state.pending += state.src.slice(start, state.pos); } return true; } - state.pos = start + 2; - stack = 1; - - while (state.pos + 1 < max) { - if (state.src.charCodeAt(state.pos) === 0x3D/* = */) { - if (state.src.charCodeAt(state.pos + 1) === 0x3D/* = */) { - lastChar = state.src.charCodeAt(state.pos - 1); - nextChar = state.pos + 2 < max ? state.src.charCodeAt(state.pos + 2) : -1; - if (nextChar !== 0x3D/* = */ && lastChar !== 0x3D/* = */) { - if (lastChar !== 0x20 && lastChar !== 0x0A) { - // closing '==' - stack--; - } else if (nextChar !== 0x20 && nextChar !== 0x0A) { - // opening '==' - stack++; - } // else { - // // standalone ' == ' indented with spaces - // } - if (stack <= 0) { - found = true; - break; - } + if (state.level >= state.options.maxNesting) { return false; } + stack = Math.floor(startCount / 2); + if (stack <= 0) { return false; } + state.pos = start + startCount; + + while (state.pos < max) { + if (state.src.charCodeAt(state.pos) === marker) { + res = scanDelims(state, state.pos); + count = res.delims; + tagCount = Math.floor(count / 2); + if (res.can_close) { + if (tagCount >= stack) { + state.pos += count - 2; + found = true; + break; } + stack -= tagCount; + state.pos += count; + continue; } + + if (res.can_open) { stack += tagCount; } + state.pos += count; + continue; } state.parser.skipToken(state); diff --git a/test/fixtures/markdown-it/del.txt b/test/fixtures/markdown-it/del.txt index f69c27f..5e2035f 100644 --- a/test/fixtures/markdown-it/del.txt +++ b/test/fixtures/markdown-it/del.txt @@ -4,6 +4,30 @@

Strikeout

. +. +x ~~~~foo~~ bar~~ +. +

x foo bar

+. + +. +x ~~foo ~~bar~~~~ +. +

x foo bar

+. + +. +x ~~~~foo~~~~ +. +

x foo

+. + +. +x ~~~foo~~~ +. +

x foo

+. + Strikeouts have the same priority as emphases: . diff --git a/test/fixtures/markdown-it/ins.txt b/test/fixtures/markdown-it/ins.txt index e08600e..5f3e067 100644 --- a/test/fixtures/markdown-it/ins.txt +++ b/test/fixtures/markdown-it/ins.txt @@ -5,17 +5,28 @@ . -These are not inserts, you have to use exactly two "++": . -x +++foo+++ +x ++++foo++ bar++ +. +

x foo bar

+. + +. +x ++foo ++bar++++ +. +

x foo bar

+. -x ++foo+++ +. +x ++++foo++++ +. +

x foo

+. -x +++foo++ . -

x +++foo+++

-

x ++foo+++

-

x +++foo++

+x +++foo+++ +. +

x +foo+

. Inserts have the same priority as emphases: diff --git a/test/fixtures/markdown-it/mark.txt b/test/fixtures/markdown-it/mark.txt index 5e12916..26cd70a 100644 --- a/test/fixtures/markdown-it/mark.txt +++ b/test/fixtures/markdown-it/mark.txt @@ -4,18 +4,28 @@

Mark

. +. +x ====foo== bar== +. +

x foo bar

+. -These are not marks, you have to use exactly two "==": . -x ===foo=== +x ==foo ==bar==== +. +

x foo bar

+. -x ==foo=== +. +x ====foo==== +. +

x foo

+. -x ===foo== . -

x ===foo===

-

x ==foo===

-

x ===foo==

+x ===foo=== +. +

x =foo=

. Marks have the same priority as emphases: