diff --git a/lib/renderer.js b/lib/renderer.js index 3e742fe..35a49e7 100644 --- a/lib/renderer.js +++ b/lib/renderer.js @@ -240,17 +240,11 @@ rules.mark_close = function(/*tokens, idx, options*/) { }; -rules.sub_open = function(/*tokens, idx, options*/) { - return ''; +rules.sub = function(tokens, idx/*, options*/) { + return '' + escapeHtml(tokens[idx].content) + ''; }; -rules.sub_close = function(/*tokens, idx, options*/) { - return ''; -}; -rules.sup_open = function(/*tokens, idx, options*/) { - return ''; -}; -rules.sup_close = function(/*tokens, idx, options*/) { - return ''; +rules.sup = function(tokens, idx/*, options*/) { + return '' + escapeHtml(tokens[idx].content) + ''; }; diff --git a/lib/rules_inline/del.js b/lib/rules_inline/del.js index c34e4dd..1258e56 100644 --- a/lib/rules_inline/del.js +++ b/lib/rules_inline/del.js @@ -26,8 +26,8 @@ module.exports = function del(state, silent) { pos = start + 2; while (pos < max && state.src.charCodeAt(pos) === 0x7E/* ~ */) { pos++; } - if (pos !== start + 2) { - // sequence of 3+ markers taking as literal, same as in a emphasis + 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); } return true; diff --git a/lib/rules_inline/sub.js b/lib/rules_inline/sub.js index dd5d8fd..a126e9a 100644 --- a/lib/rules_inline/sub.js +++ b/lib/rules_inline/sub.js @@ -2,53 +2,40 @@ 'use strict'; +// same as UNESCAPE_MD_RE plus a space +var UNESCAPE_RE = /\\([ \\!"#$%&'()*+,.\/:;<=>?@[\]^_`{|}~-])/g; + module.exports = function sub(state, silent) { var found, - stack, + content, max = state.posMax, - start = state.pos, - lastChar, - nextChar; + start = state.pos; if (state.src.charCodeAt(start) !== 0x7E/* ~ */) { return false; } if (silent) { return false; } // don't run any pairs in validation mode if (start + 2 >= max) { 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 (nextChar === 0x20 || nextChar === 0x0A) { return false; } - state.pos = start + 1; - stack = 1; while (state.pos < max) { if (state.src.charCodeAt(state.pos) === 0x7E/* ~ */) { - lastChar = state.src.charCodeAt(state.pos - 1); - nextChar = state.pos + 1 < max ? state.src.charCodeAt(state.pos + 1) : -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; - } - } + found = true; + break; } state.parser.skipToken(state); } - if (!found) { - // parser failed to find ending tag, so it's not valid emphasis + if (!found || start + 1 === state.pos) { + state.pos = start; + return false; + } + + content = state.src.slice(start + 1, state.pos); + + // don't allow unescaped spaces/newlines inside + if (content.match(/(^|[^\\])(\\\\)*\s/)) { state.pos = start; return false; } @@ -58,9 +45,11 @@ module.exports = function sub(state, silent) { state.pos = start + 1; if (!silent) { - state.push({ type: 'sub_open', level: state.level++ }); - state.parser.tokenize(state); - state.push({ type: 'sub_close', level: --state.level }); + state.push({ + type: 'sub', + level: state.level, + content: content.replace(UNESCAPE_RE, '$1') + }); } state.pos = state.posMax + 1; diff --git a/lib/rules_inline/sup.js b/lib/rules_inline/sup.js index 79287ae..07bf357 100644 --- a/lib/rules_inline/sup.js +++ b/lib/rules_inline/sup.js @@ -1,54 +1,41 @@ -// Process ~superscript~ +// Process ^superscript^ 'use strict'; +// same as UNESCAPE_MD_RE plus a space +var UNESCAPE_RE = /\\([ \\!"#$%&'()*+,.\/:;<=>?@[\]^_`{|}~-])/g; + module.exports = function sup(state, silent) { var found, - stack, + content, max = state.posMax, - start = state.pos, - lastChar, - nextChar; + start = state.pos; if (state.src.charCodeAt(start) !== 0x5E/* ^ */) { return false; } if (silent) { return false; } // don't run any pairs in validation mode if (start + 2 >= max) { 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 (nextChar === 0x20 || nextChar === 0x0A) { return false; } - state.pos = start + 1; - stack = 1; while (state.pos < max) { if (state.src.charCodeAt(state.pos) === 0x5E/* ^ */) { - lastChar = state.src.charCodeAt(state.pos - 1); - nextChar = state.pos + 1 < max ? state.src.charCodeAt(state.pos + 1) : -1; - if (nextChar !== 0x5E/* ^ */ && lastChar !== 0x5E/* ^ */) { - 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; - } - } + found = true; + break; } state.parser.skipToken(state); } - if (!found) { - // parser failed to find ending tag, so it's not valid emphasis + if (!found || start + 1 === state.pos) { + state.pos = start; + return false; + } + + content = state.src.slice(start + 1, state.pos); + + // don't allow unescaped spaces/newlines inside + if (content.match(/(^|[^\\])(\\\\)*\s/)) { state.pos = start; return false; } @@ -58,9 +45,11 @@ module.exports = function sup(state, silent) { state.pos = start + 1; if (!silent) { - state.push({ type: 'sup_open', level: state.level++ }); - state.parser.tokenize(state); - state.push({ type: 'sup_close', level: --state.level }); + state.push({ + type: 'sup', + level: state.level, + content: content.replace(UNESCAPE_RE, '$1') + }); } state.pos = state.posMax + 1; diff --git a/test/fixtures/remarkable/del.txt b/test/fixtures/remarkable/del.txt index 62792e2..f69c27f 100644 --- a/test/fixtures/remarkable/del.txt +++ b/test/fixtures/remarkable/del.txt @@ -4,20 +4,6 @@

Strikeout

. - -These are not strikeouts, you have to use exactly two "~~": -. -x ~~~foo~~~ - -x ~~foo~~~ - -x ~~~foo~~ -. -

x ~~~foo~~~

-

x ~~foo~~~

-

x ~~~foo~~

-. - Strikeouts have the same priority as emphases: . diff --git a/test/fixtures/remarkable/sub.txt b/test/fixtures/remarkable/sub.txt index 53dfbdf..f4c7804 100644 --- a/test/fixtures/remarkable/sub.txt +++ b/test/fixtures/remarkable/sub.txt @@ -1,12 +1,36 @@ . -~foo ~bar~ baz~ +~foo\~ . -

foo bar baz

+

~foo~

. . -this ~ is ~ not ~ subscript +~foo bar~ . -

this ~ is ~ not ~ subscript

+

~foo bar~

+. + +. +~foo\ bar\ baz~ +. +

foo bar baz

+. + +. +~\ foo\ ~ +. +

foo

+. + +. +~foo\\\\\\\ bar~ +. +

foo\\\ bar

+. + +. +~foo\\\\\\ bar~ +. +

~foo\\\ bar~

. diff --git a/test/fixtures/remarkable/sup.txt b/test/fixtures/remarkable/sup.txt index 1984017..1db493f 100644 --- a/test/fixtures/remarkable/sup.txt +++ b/test/fixtures/remarkable/sup.txt @@ -1,18 +1,42 @@ . -^foo ^bar^ baz^ +^test^ . -

foo bar baz

+

test

. . -^foo ~bar~ baz^ +^foo\^ . -

foo bar baz

+

^foo^

. . -this ^ is ^ not ^ superscript +2^4 + 3^5 . -

this ^ is ^ not ^ superscript

+

2^4 + 3^5

+. + +. +^foo~bar^baz^bar~foo^ +. +

foo~barbazbar~foo

+. + +. +^\ foo\ ^ +. +

foo

+. + +. +^foo\\\\\\\ bar^ +. +

foo\\\ bar

+. + +. +^foo\\\\\\ bar^ +. +

^foo\\\ bar^

.