parser_inline.js 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. /** internal
  2. * class ParserInline
  3. *
  4. * Tokenizes paragraph content.
  5. **/
  6. 'use strict';
  7. var Ruler = require('./ruler');
  8. ////////////////////////////////////////////////////////////////////////////////
  9. // Parser rules
  10. var _rules = [
  11. [ 'text', require('./rules_inline/text') ],
  12. [ 'linkify', require('./rules_inline/linkify') ],
  13. [ 'newline', require('./rules_inline/newline') ],
  14. [ 'escape', require('./rules_inline/escape') ],
  15. [ 'backticks', require('./rules_inline/backticks') ],
  16. [ 'strikethrough', require('./rules_inline/strikethrough').tokenize ],
  17. [ 'emphasis', require('./rules_inline/emphasis').tokenize ],
  18. [ 'link', require('./rules_inline/link') ],
  19. [ 'image', require('./rules_inline/image') ],
  20. [ 'autolink', require('./rules_inline/autolink') ],
  21. [ 'html_inline', require('./rules_inline/html_inline') ],
  22. [ 'entity', require('./rules_inline/entity') ]
  23. ];
  24. // `rule2` ruleset was created specifically for emphasis/strikethrough
  25. // post-processing and may be changed in the future.
  26. //
  27. // Don't use this for anything except pairs (plugins working with `balance_pairs`).
  28. //
  29. var _rules2 = [
  30. [ 'balance_pairs', require('./rules_inline/balance_pairs') ],
  31. [ 'strikethrough', require('./rules_inline/strikethrough').postProcess ],
  32. [ 'emphasis', require('./rules_inline/emphasis').postProcess ],
  33. // rules for pairs separate '**' into its own text tokens, which may be left unused,
  34. // rule below merges unused segments back with the rest of the text
  35. [ 'fragments_join', require('./rules_inline/fragments_join') ]
  36. ];
  37. /**
  38. * new ParserInline()
  39. **/
  40. function ParserInline() {
  41. var i;
  42. /**
  43. * ParserInline#ruler -> Ruler
  44. *
  45. * [[Ruler]] instance. Keep configuration of inline rules.
  46. **/
  47. this.ruler = new Ruler();
  48. for (i = 0; i < _rules.length; i++) {
  49. this.ruler.push(_rules[i][0], _rules[i][1]);
  50. }
  51. /**
  52. * ParserInline#ruler2 -> Ruler
  53. *
  54. * [[Ruler]] instance. Second ruler used for post-processing
  55. * (e.g. in emphasis-like rules).
  56. **/
  57. this.ruler2 = new Ruler();
  58. for (i = 0; i < _rules2.length; i++) {
  59. this.ruler2.push(_rules2[i][0], _rules2[i][1]);
  60. }
  61. }
  62. // Skip single token by running all rules in validation mode;
  63. // returns `true` if any rule reported success
  64. //
  65. ParserInline.prototype.skipToken = function (state) {
  66. var ok, i, pos = state.pos,
  67. rules = this.ruler.getRules(''),
  68. len = rules.length,
  69. maxNesting = state.md.options.maxNesting,
  70. cache = state.cache;
  71. if (typeof cache[pos] !== 'undefined') {
  72. state.pos = cache[pos];
  73. return;
  74. }
  75. if (state.level < maxNesting) {
  76. for (i = 0; i < len; i++) {
  77. // Increment state.level and decrement it later to limit recursion.
  78. // It's harmless to do here, because no tokens are created. But ideally,
  79. // we'd need a separate private state variable for this purpose.
  80. //
  81. state.level++;
  82. ok = rules[i](state, true);
  83. state.level--;
  84. if (ok) { break; }
  85. }
  86. } else {
  87. // Too much nesting, just skip until the end of the paragraph.
  88. //
  89. // NOTE: this will cause links to behave incorrectly in the following case,
  90. // when an amount of `[` is exactly equal to `maxNesting + 1`:
  91. //
  92. // [[[[[[[[[[[[[[[[[[[[[foo]()
  93. //
  94. // TODO: remove this workaround when CM standard will allow nested links
  95. // (we can replace it by preventing links from being parsed in
  96. // validation mode)
  97. //
  98. state.pos = state.posMax;
  99. }
  100. if (!ok) { state.pos++; }
  101. cache[pos] = state.pos;
  102. };
  103. // Generate tokens for input range
  104. //
  105. ParserInline.prototype.tokenize = function (state) {
  106. var ok, i,
  107. rules = this.ruler.getRules(''),
  108. len = rules.length,
  109. end = state.posMax,
  110. maxNesting = state.md.options.maxNesting;
  111. while (state.pos < end) {
  112. // Try all possible rules.
  113. // On success, rule should:
  114. //
  115. // - update `state.pos`
  116. // - update `state.tokens`
  117. // - return true
  118. if (state.level < maxNesting) {
  119. for (i = 0; i < len; i++) {
  120. ok = rules[i](state, false);
  121. if (ok) { break; }
  122. }
  123. }
  124. if (ok) {
  125. if (state.pos >= end) { break; }
  126. continue;
  127. }
  128. state.pending += state.src[state.pos++];
  129. }
  130. if (state.pending) {
  131. state.pushPending();
  132. }
  133. };
  134. /**
  135. * ParserInline.parse(str, md, env, outTokens)
  136. *
  137. * Process input string and push inline tokens into `outTokens`
  138. **/
  139. ParserInline.prototype.parse = function (str, md, env, outTokens) {
  140. var i, rules, len;
  141. var state = new this.State(str, md, env, outTokens);
  142. this.tokenize(state);
  143. rules = this.ruler2.getRules('');
  144. len = rules.length;
  145. for (i = 0; i < len; i++) {
  146. rules[i](state);
  147. }
  148. };
  149. ParserInline.prototype.State = require('./rules_inline/state_inline');
  150. module.exports = ParserInline;