/** internal * class ParserInline * * Tokenizes paragraph content. **/ import Ruler from './ruler.mjs'; import StateInline from './rules_inline/state_inline.mjs'; import r_text from './rules_inline/text.mjs'; import r_linkify from './rules_inline/linkify.mjs'; import r_newline from './rules_inline/newline.mjs'; import r_escape from './rules_inline/escape.mjs'; import r_backticks from './rules_inline/backticks.mjs'; import r_strikethrough from './rules_inline/strikethrough.mjs'; import r_emphasis from './rules_inline/emphasis.mjs'; import r_link from './rules_inline/link.mjs'; import r_image from './rules_inline/image.mjs'; import r_autolink from './rules_inline/autolink.mjs'; import r_html_inline from './rules_inline/html_inline.mjs'; import r_entity from './rules_inline/entity.mjs'; import r_balance_pairs from './rules_inline/balance_pairs.mjs'; import r_fragments_join from './rules_inline/fragments_join.mjs'; //////////////////////////////////////////////////////////////////////////////// // Parser rules const _rules = [ [ 'text', r_text ], [ 'linkify', r_linkify ], [ 'newline', r_newline ], [ 'escape', r_escape ], [ 'backticks', r_backticks ], [ 'strikethrough', r_strikethrough.tokenize ], [ 'emphasis', r_emphasis.tokenize ], [ 'link', r_link ], [ 'image', r_image ], [ 'autolink', r_autolink ], [ 'html_inline', r_html_inline ], [ 'entity', r_entity ] ]; // `rule2` ruleset was created specifically for emphasis/strikethrough // post-processing and may be changed in the future. // // Don't use this for anything except pairs (plugins working with `balance_pairs`). // const _rules2 = [ [ 'balance_pairs', r_balance_pairs ], [ 'strikethrough', r_strikethrough.postProcess ], [ 'emphasis', r_emphasis.postProcess ], // rules for pairs separate '**' into its own text tokens, which may be left unused, // rule below merges unused segments back with the rest of the text [ 'fragments_join', r_fragments_join ] ]; /** * new ParserInline() **/ function ParserInline() { /** * ParserInline#ruler -> Ruler * * [[Ruler]] instance. Keep configuration of inline rules. **/ this.ruler = new Ruler(); for (let i = 0; i < _rules.length; i++) { this.ruler.push(_rules[i][0], _rules[i][1]); } /** * ParserInline#ruler2 -> Ruler * * [[Ruler]] instance. Second ruler used for post-processing * (e.g. in emphasis-like rules). **/ this.ruler2 = new Ruler(); for (let i = 0; i < _rules2.length; i++) { this.ruler2.push(_rules2[i][0], _rules2[i][1]); } } // Skip single token by running all rules in validation mode; // returns `true` if any rule reported success // ParserInline.prototype.skipToken = function (state) { const pos = state.pos; const rules = this.ruler.getRules(''); const len = rules.length; const maxNesting = state.md.options.maxNesting; const cache = state.cache; if (typeof cache[pos] !== 'undefined') { state.pos = cache[pos]; return; } let ok = false; if (state.level < maxNesting) { for (let i = 0; i < len; i++) { // Increment state.level and decrement it later to limit recursion. // It's harmless to do here, because no tokens are created. But ideally, // we'd need a separate private state variable for this purpose. // state.level++; ok = rules[i](state, true); state.level--; if (ok) { if (pos >= state.pos) { throw new Error("inline rule didn't increment state.pos"); } break; } } } else { // Too much nesting, just skip until the end of the paragraph. // // NOTE: this will cause links to behave incorrectly in the following case, // when an amount of `[` is exactly equal to `maxNesting + 1`: // // [[[[[[[[[[[[[[[[[[[[[foo]() // // TODO: remove this workaround when CM standard will allow nested links // (we can replace it by preventing links from being parsed in // validation mode) // state.pos = state.posMax; } if (!ok) { state.pos++; } cache[pos] = state.pos; }; // Generate tokens for input range // ParserInline.prototype.tokenize = function (state) { const rules = this.ruler.getRules(''); const len = rules.length; const end = state.posMax; const maxNesting = state.md.options.maxNesting; while (state.pos < end) { // Try all possible rules. // On success, rule should: // // - update `state.pos` // - update `state.tokens` // - return true const prevPos = state.pos; let ok = false; if (state.level < maxNesting) { for (let i = 0; i < len; i++) { ok = rules[i](state, false); if (ok) { if (prevPos >= state.pos) { throw new Error("inline rule didn't increment state.pos"); } break; } } } if (ok) { if (state.pos >= end) { break; } continue; } state.pending += state.src[state.pos++]; } if (state.pending) { state.pushPending(); } }; /** * ParserInline.parse(str, md, env, outTokens) * * Process input string and push inline tokens into `outTokens` **/ ParserInline.prototype.parse = function (str, md, env, outTokens) { const state = new this.State(str, md, env, outTokens); this.tokenize(state); const rules = this.ruler2.getRules(''); const len = rules.length; for (let i = 0; i < len; i++) { rules[i](state); } }; ParserInline.prototype.State = StateInline; export default ParserInline;