From 710a62f1d5d5ae44f4674345c61220573cceeafa Mon Sep 17 00:00:00 2001 From: Vitaly Puzrin Date: Sat, 25 Oct 2014 12:20:59 +0400 Subject: [PATCH] Added support --- demo/sample.md | 2 + lib/configs/full.js | 43 +++++++++++++++ lib/parser_inline.js | 3 +- lib/renderer.js | 8 +++ lib/rules_inline/del.js | 4 +- lib/rules_inline/ins.js | 78 +++++++++++++++++++++++++++ test/fixtures/remarkable/ins.txt | 93 ++++++++++++++++++++++++++++++++ 7 files changed, 228 insertions(+), 3 deletions(-) create mode 100644 lib/configs/full.js create mode 100644 lib/rules_inline/ins.js create mode 100644 test/fixtures/remarkable/ins.txt diff --git a/demo/sample.md b/demo/sample.md index 141b57f..cebf727 100644 --- a/demo/sample.md +++ b/demo/sample.md @@ -56,6 +56,8 @@ _This is italic text_ ~~Deleted text~~ +++Inserted text++ + ## Blockquotes diff --git a/lib/configs/full.js b/lib/configs/full.js new file mode 100644 index 0000000..c7a59cd --- /dev/null +++ b/lib/configs/full.js @@ -0,0 +1,43 @@ +// Remarkable default options + +'use strict'; + + +module.exports = { + options: { + html: false, // Enable html tags in source + xhtmlOut: false, // Use '/' to close single tags (
) + breaks: false, // Convert '\n' in paragraphs into
+ langPrefix: 'language-', // CSS language prefix for fenced blocks + linkify: false, // autoconvert url-like texts to links + typographer: false, // Enable smartypants and other sweet transforms + + // Highlighter function. Should return escaped html, + // or '' if input not changed + highlight: function (/*str, , lang*/) { return ''; }, + + maxNesting: 20 // Internal protection, recursion limit + }, + + components: { + + // Don't restrict block/inline rules + block: {}, + inline: {}, + + typographer: { + options: { + singleQuotes: '‘’', // set empty to disable + doubleQuotes: '“”', // set '«»' for russian, '„“' for deutch, empty to disable + copyright: true, // (c) (C) → © + trademark: true, // (tm) (TM) → ™ + registered: true, // (r) (R) → ® + plusminus: true, // +- → ± + paragraph: true, // (p) (P) → § + ellipsis: true, // ... → … + dupes: true, // ???????? → ???, !!!!! → !!!, `,,` → `,` + dashes: true // -- → — + } + } + } +}; diff --git a/lib/parser_inline.js b/lib/parser_inline.js index 62a12e0..d327acf 100644 --- a/lib/parser_inline.js +++ b/lib/parser_inline.js @@ -18,6 +18,7 @@ rules.push(require('./rules_inline/newline')); rules.push(require('./rules_inline/escape')); rules.push(require('./rules_inline/backticks')); rules.push(require('./rules_inline/del')); +rules.push(require('./rules_inline/ins')); rules.push(require('./rules_inline/emphasis')); rules.push(require('./rules_inline/links')); rules.push(require('./rules_inline/autolink')); @@ -50,7 +51,7 @@ function ParserInline() { // Rule to skip pure text // - '{$%@}' reserved for extentions // - '<>"' added for internal html escaping - this.textMatch = /^[^\n\\`*_\[\]!&{}$%@<>"~]+/; + this.textMatch = /^[^\n\\`*_\[\]!&{}$%@<>"~+]+/; // By default CommonMark allows too much in links // If you need to restrict it - override this with your validator. diff --git a/lib/renderer.js b/lib/renderer.js index c4b66c8..b0b6c9f 100644 --- a/lib/renderer.js +++ b/lib/renderer.js @@ -200,6 +200,14 @@ rules.del_close = function(/*tokens, idx, options*/) { }; +rules.ins_open = function(/*tokens, idx, options*/) { + return ''; +}; +rules.ins_close = function(/*tokens, idx, options*/) { + return ''; +}; + + rules.hardbreak = function (tokens, idx, options) { return options.xhtmlOut ? '
\n' : '
\n'; }; diff --git a/lib/rules_inline/del.js b/lib/rules_inline/del.js index 0bcb2eb..0ad84ba 100644 --- a/lib/rules_inline/del.js +++ b/lib/rules_inline/del.js @@ -1,4 +1,4 @@ -// Process ~~strike through~~ +// Process ~~deleted text~~ 'use strict'; @@ -24,7 +24,7 @@ module.exports = function del(state, silent) { nextChar = state.src.charCodeAt(start + 2); if (lastChar === 0x7E/* ~ */) { return false; } - if (nextChar === 0x7E/* */) { return false; } + if (nextChar === 0x7E/* ~ */) { return false; } if (nextChar === 0x20 || nextChar === 0x0A) { return false; } pos = start + 2; diff --git a/lib/rules_inline/ins.js b/lib/rules_inline/ins.js new file mode 100644 index 0000000..291f625 --- /dev/null +++ b/lib/rules_inline/ins.js @@ -0,0 +1,78 @@ +// Process ++inserted text++ + +'use strict'; + +module.exports = function ins(state, silent) { + var found, + pos, + max = state.posMax, + start = state.pos, + lastChar, + nextChar; + + if (state.src.charCodeAt(start) !== 0x2B/* + */) { return false; } + if (start + 4 >= max) { return false; } + if (state.src.charCodeAt(start + 1) !== 0x2B/* + */) { return false; } + + // make ins lower a priority tag with respect to links, same as ; + // this code also prevents recursion + if (silent && state.isInLabel) { 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); } + return true; + } + + state.pos = start + 2; + + 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 '++' + found = true; + break; + } + } + } + } + + state.parser.skipToken(state); + } + + if (!found) { + // parser failed to find ending tag, so it's not valid emphasis + state.pos = start; + return false; + } + + // found! + state.posMax = state.pos; + state.pos = start + 2; + + if (!silent) { + state.push({ type: 'ins_open', level: state.level++ }); + state.parser.tokenize(state); + state.push({ type: 'ins_close', level: --state.level }); + } + + state.pos = state.posMax + 2; + state.posMax = max; + return true; +}; diff --git a/test/fixtures/remarkable/ins.txt b/test/fixtures/remarkable/ins.txt new file mode 100644 index 0000000..33fa0ae --- /dev/null +++ b/test/fixtures/remarkable/ins.txt @@ -0,0 +1,93 @@ +. +++Insert++ +. +

Insert

+. + + +These are not inserts, you have to use exactly two "++": +. +x +++foo+++ + +x ++foo+++ + +x +++foo++ +. +

x +++foo+++

+

x ++foo+++

+

x +++foo++

+. + +Inserts have the same priority as emphases: + +. +**++test**++ + +++**test++** +. +

**test**

+

++test++

+. + +Inserts have the same priority as emphases with respect to links: +. +[++link]()++ + +++[link++]() +. +

++link++

+

++link++

+. + +Inserts have the same priority as emphases with respect to backticks: +. +++`code++` + +`++code`++ +. +

++code++

+

++code++

+. + +Nested inserts: +. +++foo ++bar++ baz++ +. +

foo bar baz

+. + +. +++f **o ++o b++ a** r++ +. +

f o o b a r

+. + +Should not have a whitespace between text and "++": +. +foo ++ bar ++ baz +. +

foo ++ bar ++ baz

+. + + +Newline should be considered a whitespace: + +. +++test +++ + +++ +test++ + +++ +test +++ +. +

++test +++

+

++ +test++

+

++ +test +++

+.