state_inline.js 3.4 KB

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