parser_block.js 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. /** internal
  2. * class ParserBlock
  3. *
  4. * Block-level tokenizer.
  5. **/
  6. 'use strict';
  7. var Ruler = require('./ruler');
  8. var _rules = [
  9. // First 2 params - rule name & source. Secondary array - list of rules,
  10. // which can be terminated by this one.
  11. [ 'table', require('./rules_block/table'), [ 'paragraph', 'reference' ] ],
  12. [ 'code', require('./rules_block/code') ],
  13. [ 'fence', require('./rules_block/fence'), [ 'paragraph', 'reference', 'blockquote', 'list' ] ],
  14. [ 'blockquote', require('./rules_block/blockquote'), [ 'paragraph', 'reference', 'blockquote', 'list' ] ],
  15. [ 'hr', require('./rules_block/hr'), [ 'paragraph', 'reference', 'blockquote', 'list' ] ],
  16. [ 'list', require('./rules_block/list'), [ 'paragraph', 'reference', 'blockquote' ] ],
  17. [ 'reference', require('./rules_block/reference') ],
  18. [ 'html_block', require('./rules_block/html_block'), [ 'paragraph', 'reference', 'blockquote' ] ],
  19. [ 'heading', require('./rules_block/heading'), [ 'paragraph', 'reference', 'blockquote' ] ],
  20. [ 'lheading', require('./rules_block/lheading') ],
  21. [ 'paragraph', require('./rules_block/paragraph') ]
  22. ];
  23. /**
  24. * new ParserBlock()
  25. **/
  26. function ParserBlock() {
  27. /**
  28. * ParserBlock#ruler -> Ruler
  29. *
  30. * [[Ruler]] instance. Keep configuration of block rules.
  31. **/
  32. this.ruler = new Ruler();
  33. for (var i = 0; i < _rules.length; i++) {
  34. this.ruler.push(_rules[i][0], _rules[i][1], { alt: (_rules[i][2] || []).slice() });
  35. }
  36. }
  37. // Generate tokens for input range
  38. //
  39. ParserBlock.prototype.tokenize = function (state, startLine, endLine) {
  40. var ok, i,
  41. rules = this.ruler.getRules(''),
  42. len = rules.length,
  43. line = startLine,
  44. hasEmptyLines = false,
  45. maxNesting = state.md.options.maxNesting;
  46. while (line < endLine) {
  47. state.line = line = state.skipEmptyLines(line);
  48. if (line >= endLine) { break; }
  49. // Termination condition for nested calls.
  50. // Nested calls currently used for blockquotes & lists
  51. if (state.sCount[line] < state.blkIndent) { break; }
  52. // If nesting level exceeded - skip tail to the end. That's not ordinary
  53. // situation and we should not care about content.
  54. if (state.level >= maxNesting) {
  55. state.line = endLine;
  56. break;
  57. }
  58. // Try all possible rules.
  59. // On success, rule should:
  60. //
  61. // - update `state.line`
  62. // - update `state.tokens`
  63. // - return true
  64. for (i = 0; i < len; i++) {
  65. ok = rules[i](state, line, endLine, false);
  66. if (ok) { break; }
  67. }
  68. // set state.tight if we had an empty line before current tag
  69. // i.e. latest empty line should not count
  70. state.tight = !hasEmptyLines;
  71. // paragraph might "eat" one newline after it in nested lists
  72. if (state.isEmpty(state.line - 1)) {
  73. hasEmptyLines = true;
  74. }
  75. line = state.line;
  76. if (line < endLine && state.isEmpty(line)) {
  77. hasEmptyLines = true;
  78. line++;
  79. state.line = line;
  80. }
  81. }
  82. };
  83. /**
  84. * ParserBlock.parse(str, md, env, outTokens)
  85. *
  86. * Process input string and push block tokens into `outTokens`
  87. **/
  88. ParserBlock.prototype.parse = function (src, md, env, outTokens) {
  89. var state;
  90. if (!src) { return; }
  91. state = new this.State(src, md, env, outTokens);
  92. this.tokenize(state, state.line, state.lineMax);
  93. };
  94. ParserBlock.prototype.State = require('./rules_block/state_block');
  95. module.exports = ParserBlock;