summaryrefslogtreecommitdiff
path: root/packages/markdown-it-14.1.0/lib/parser_block.mjs
blob: 486d68fee3e91ed2c200d9ae0ea43256572c686d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
/** internal
 * class ParserBlock
 *
 * Block-level tokenizer.
 **/

import Ruler from './ruler.mjs'
import StateBlock from './rules_block/state_block.mjs'

import r_table from './rules_block/table.mjs'
import r_code from './rules_block/code.mjs'
import r_fence from './rules_block/fence.mjs'
import r_blockquote from './rules_block/blockquote.mjs'
import r_hr from './rules_block/hr.mjs'
import r_list from './rules_block/list.mjs'
import r_reference from './rules_block/reference.mjs'
import r_html_block from './rules_block/html_block.mjs'
import r_heading from './rules_block/heading.mjs'
import r_lheading from './rules_block/lheading.mjs'
import r_paragraph from './rules_block/paragraph.mjs'

const _rules = [
  // First 2 params - rule name & source. Secondary array - list of rules,
  // which can be terminated by this one.
  ['table',      r_table,      ['paragraph', 'reference']],
  ['code',       r_code],
  ['fence',      r_fence,      ['paragraph', 'reference', 'blockquote', 'list']],
  ['blockquote', r_blockquote, ['paragraph', 'reference', 'blockquote', 'list']],
  ['hr',         r_hr,         ['paragraph', 'reference', 'blockquote', 'list']],
  ['list',       r_list,       ['paragraph', 'reference', 'blockquote']],
  ['reference',  r_reference],
  ['html_block', r_html_block, ['paragraph', 'reference', 'blockquote']],
  ['heading',    r_heading,    ['paragraph', 'reference', 'blockquote']],
  ['lheading',   r_lheading],
  ['paragraph',  r_paragraph]
]

/**
 * new ParserBlock()
 **/
function ParserBlock () {
  /**
   * ParserBlock#ruler -> Ruler
   *
   * [[Ruler]] instance. Keep configuration of block rules.
   **/
  this.ruler = new Ruler()

  for (let i = 0; i < _rules.length; i++) {
    this.ruler.push(_rules[i][0], _rules[i][1], { alt: (_rules[i][2] || []).slice() })
  }
}

// Generate tokens for input range
//
ParserBlock.prototype.tokenize = function (state, startLine, endLine) {
  const rules = this.ruler.getRules('')
  const len = rules.length
  const maxNesting = state.md.options.maxNesting
  let line = startLine
  let hasEmptyLines = false

  while (line < endLine) {
    state.line = line = state.skipEmptyLines(line)
    if (line >= endLine) { break }

    // Termination condition for nested calls.
    // Nested calls currently used for blockquotes & lists
    if (state.sCount[line] < state.blkIndent) { break }

    // If nesting level exceeded - skip tail to the end. That's not ordinary
    // situation and we should not care about content.
    if (state.level >= maxNesting) {
      state.line = endLine
      break
    }

    // Try all possible rules.
    // On success, rule should:
    //
    // - update `state.line`
    // - update `state.tokens`
    // - return true
    const prevLine = state.line
    let ok = false

    for (let i = 0; i < len; i++) {
      ok = rules[i](state, line, endLine, false)
      if (ok) {
        if (prevLine >= state.line) {
          throw new Error("block rule didn't increment state.line")
        }
        break
      }
    }

    // this can only happen if user disables paragraph rule
    if (!ok) throw new Error('none of the block rules matched')

    // set state.tight if we had an empty line before current tag
    // i.e. latest empty line should not count
    state.tight = !hasEmptyLines

    // paragraph might "eat" one newline after it in nested lists
    if (state.isEmpty(state.line - 1)) {
      hasEmptyLines = true
    }

    line = state.line

    if (line < endLine && state.isEmpty(line)) {
      hasEmptyLines = true
      line++
      state.line = line
    }
  }
}

/**
 * ParserBlock.parse(str, md, env, outTokens)
 *
 * Process input string and push block tokens into `outTokens`
 **/
ParserBlock.prototype.parse = function (src, md, env, outTokens) {
  if (!src) { return }

  const state = new this.State(src, md, env, outTokens)

  this.tokenize(state, state.line, state.lineMax)
}

ParserBlock.prototype.State = StateBlock

export default ParserBlock