state_inline.mjs 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. // Inline parser state
  2. import Token from '../token.mjs'
  3. import { isWhiteSpace, isPunctChar, isMdAsciiPunct } from '../common/utils.mjs'
  4. function StateInline (src, md, env, outTokens) {
  5. this.src = src
  6. this.env = env
  7. this.md = md
  8. this.tokens = outTokens
  9. this.tokens_meta = Array(outTokens.length)
  10. this.pos = 0
  11. this.posMax = this.src.length
  12. this.level = 0
  13. this.pending = ''
  14. this.pendingLevel = 0
  15. // Stores { start: end } pairs. Useful for backtrack
  16. // optimization of pairs parse (emphasis, strikes).
  17. this.cache = {}
  18. // List of emphasis-like delimiters for current tag
  19. this.delimiters = []
  20. // Stack of delimiter lists for upper level tags
  21. this._prev_delimiters = []
  22. // backtick length => last seen position
  23. this.backticks = {}
  24. this.backticksScanned = false
  25. // Counter used to disable inline linkify-it execution
  26. // inside <a> and markdown links
  27. this.linkLevel = 0
  28. }
  29. // Flush pending text
  30. //
  31. StateInline.prototype.pushPending = function () {
  32. const token = new Token('text', '', 0)
  33. token.content = this.pending
  34. token.level = this.pendingLevel
  35. this.tokens.push(token)
  36. this.pending = ''
  37. return token
  38. }
  39. // Push new token to "stream".
  40. // If pending text exists - flush it as text token
  41. //
  42. StateInline.prototype.push = function (type, tag, nesting) {
  43. if (this.pending) {
  44. this.pushPending()
  45. }
  46. const token = new Token(type, tag, nesting)
  47. let token_meta = null
  48. if (nesting < 0) {
  49. // closing tag
  50. this.level--
  51. this.delimiters = this._prev_delimiters.pop()
  52. }
  53. token.level = this.level
  54. if (nesting > 0) {
  55. // opening tag
  56. this.level++
  57. this._prev_delimiters.push(this.delimiters)
  58. this.delimiters = []
  59. token_meta = { delimiters: this.delimiters }
  60. }
  61. this.pendingLevel = this.level
  62. this.tokens.push(token)
  63. this.tokens_meta.push(token_meta)
  64. return token
  65. }
  66. // Scan a sequence of emphasis-like markers, and determine whether
  67. // it can start an emphasis sequence or end an emphasis sequence.
  68. //
  69. // - start - position to scan from (it should point at a valid marker);
  70. // - canSplitWord - determine if these markers can be found inside a word
  71. //
  72. StateInline.prototype.scanDelims = function (start, canSplitWord) {
  73. const max = this.posMax
  74. const marker = this.src.charCodeAt(start)
  75. // treat beginning of the line as a whitespace
  76. const lastChar = start > 0 ? this.src.charCodeAt(start - 1) : 0x20
  77. let pos = start
  78. while (pos < max && this.src.charCodeAt(pos) === marker) { pos++ }
  79. const count = pos - start
  80. // treat end of the line as a whitespace
  81. const nextChar = pos < max ? this.src.charCodeAt(pos) : 0x20
  82. const isLastPunctChar = isMdAsciiPunct(lastChar) || isPunctChar(String.fromCharCode(lastChar))
  83. const isNextPunctChar = isMdAsciiPunct(nextChar) || isPunctChar(String.fromCharCode(nextChar))
  84. const isLastWhiteSpace = isWhiteSpace(lastChar)
  85. const isNextWhiteSpace = isWhiteSpace(nextChar)
  86. const left_flanking =
  87. !isNextWhiteSpace && (!isNextPunctChar || isLastWhiteSpace || isLastPunctChar)
  88. const right_flanking =
  89. !isLastWhiteSpace && (!isLastPunctChar || isNextWhiteSpace || isNextPunctChar)
  90. const can_open = left_flanking && (canSplitWord || !right_flanking || isLastPunctChar)
  91. const can_close = right_flanking && (canSplitWord || !left_flanking || isNextPunctChar)
  92. return { can_open, can_close, length: count }
  93. }
  94. // re-export Token class to use in block rules
  95. StateInline.prototype.Token = Token
  96. export default StateInline