// Inline parser state import Token from '../token.mjs' import { isWhiteSpace, isPunctChar, isMdAsciiPunct } from '../common/utils.mjs' function StateInline (src, md, env, outTokens) { this.src = src this.env = env this.md = md this.tokens = outTokens this.tokens_meta = Array(outTokens.length) this.pos = 0 this.posMax = this.src.length this.level = 0 this.pending = '' this.pendingLevel = 0 // Stores { start: end } pairs. Useful for backtrack // optimization of pairs parse (emphasis, strikes). this.cache = {} // List of emphasis-like delimiters for current tag this.delimiters = [] // Stack of delimiter lists for upper level tags this._prev_delimiters = [] // backtick length => last seen position this.backticks = {} this.backticksScanned = false // Counter used to disable inline linkify-it execution // inside and markdown links this.linkLevel = 0 } // Flush pending text // StateInline.prototype.pushPending = function () { const token = new Token('text', '', 0) token.content = this.pending token.level = this.pendingLevel this.tokens.push(token) this.pending = '' return token } // Push new token to "stream". // If pending text exists - flush it as text token // StateInline.prototype.push = function (type, tag, nesting) { if (this.pending) { this.pushPending() } const token = new Token(type, tag, nesting) let token_meta = null if (nesting < 0) { // closing tag this.level-- this.delimiters = this._prev_delimiters.pop() } token.level = this.level if (nesting > 0) { // opening tag this.level++ this._prev_delimiters.push(this.delimiters) this.delimiters = [] token_meta = { delimiters: this.delimiters } } this.pendingLevel = this.level this.tokens.push(token) this.tokens_meta.push(token_meta) return token } // Scan a sequence of emphasis-like markers, and determine whether // it can start an emphasis sequence or end an emphasis sequence. // // - start - position to scan from (it should point at a valid marker); // - canSplitWord - determine if these markers can be found inside a word // StateInline.prototype.scanDelims = function (start, canSplitWord) { let can_open, can_close let left_flanking = true let right_flanking = true const max = this.posMax const marker = this.src.charCodeAt(start) // treat beginning of the line as a whitespace const lastChar = start > 0 ? this.src.charCodeAt(start - 1) : 0x20 let pos = start while (pos < max && this.src.charCodeAt(pos) === marker) { pos++ } const count = pos - start // treat end of the line as a whitespace const nextChar = pos < max ? this.src.charCodeAt(pos) : 0x20 const isLastPunctChar = isMdAsciiPunct(lastChar) || isPunctChar(String.fromCharCode(lastChar)) const isNextPunctChar = isMdAsciiPunct(nextChar) || isPunctChar(String.fromCharCode(nextChar)) const isLastWhiteSpace = isWhiteSpace(lastChar) const isNextWhiteSpace = isWhiteSpace(nextChar) if (isNextWhiteSpace) { left_flanking = false } else if (isNextPunctChar) { if (!(isLastWhiteSpace || isLastPunctChar)) { left_flanking = false } } if (isLastWhiteSpace) { right_flanking = false } else if (isLastPunctChar) { if (!(isNextWhiteSpace || isNextPunctChar)) { right_flanking = false } } if (!canSplitWord) { can_open = left_flanking && (!right_flanking || isLastPunctChar) can_close = right_flanking && (!left_flanking || isNextPunctChar) } else { can_open = left_flanking can_close = right_flanking } return { can_open, can_close, length: count } } // re-export Token class to use in block rules StateInline.prototype.Token = Token export default StateInline