motion-markdown-it 4.4.0 → 8.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +69 -16
- data/lib/motion-markdown-it.rb +7 -5
- data/lib/motion-markdown-it/common/html_blocks.rb +6 -2
- data/lib/motion-markdown-it/common/utils.rb +19 -4
- data/lib/motion-markdown-it/helpers/helper_wrapper.rb +9 -0
- data/lib/motion-markdown-it/helpers/parse_link_destination.rb +8 -7
- data/lib/motion-markdown-it/index.rb +60 -18
- data/lib/motion-markdown-it/parser_block.rb +7 -10
- data/lib/motion-markdown-it/parser_inline.rb +50 -14
- data/lib/motion-markdown-it/presets/commonmark.rb +7 -1
- data/lib/motion-markdown-it/presets/default.rb +4 -3
- data/lib/motion-markdown-it/presets/zero.rb +6 -1
- data/lib/motion-markdown-it/renderer.rb +46 -14
- data/lib/motion-markdown-it/rules_block/blockquote.rb +167 -31
- data/lib/motion-markdown-it/rules_block/code.rb +4 -3
- data/lib/motion-markdown-it/rules_block/fence.rb +9 -4
- data/lib/motion-markdown-it/rules_block/heading.rb +8 -3
- data/lib/motion-markdown-it/rules_block/hr.rb +10 -5
- data/lib/motion-markdown-it/rules_block/html_block.rb +6 -3
- data/lib/motion-markdown-it/rules_block/lheading.rb +64 -26
- data/lib/motion-markdown-it/rules_block/list.rb +91 -22
- data/lib/motion-markdown-it/rules_block/paragraph.rb +14 -9
- data/lib/motion-markdown-it/rules_block/reference.rb +24 -14
- data/lib/motion-markdown-it/rules_block/state_block.rb +79 -24
- data/lib/motion-markdown-it/rules_block/table.rb +52 -26
- data/lib/motion-markdown-it/rules_core/normalize.rb +1 -23
- data/lib/motion-markdown-it/rules_core/replacements.rb +22 -2
- data/lib/motion-markdown-it/rules_core/smartquotes.rb +41 -12
- data/lib/motion-markdown-it/rules_inline/autolink.rb +5 -4
- data/lib/motion-markdown-it/rules_inline/balance_pairs.rb +48 -0
- data/lib/motion-markdown-it/rules_inline/emphasis.rb +104 -149
- data/lib/motion-markdown-it/rules_inline/entity.rb +2 -2
- data/lib/motion-markdown-it/rules_inline/escape.rb +5 -3
- data/lib/motion-markdown-it/rules_inline/image.rb +12 -23
- data/lib/motion-markdown-it/rules_inline/link.rb +20 -25
- data/lib/motion-markdown-it/rules_inline/newline.rb +2 -1
- data/lib/motion-markdown-it/rules_inline/state_inline.rb +60 -1
- data/lib/motion-markdown-it/rules_inline/strikethrough.rb +81 -97
- data/lib/motion-markdown-it/rules_inline/text_collapse.rb +40 -0
- data/lib/motion-markdown-it/token.rb +46 -1
- data/lib/motion-markdown-it/version.rb +1 -1
- data/spec/motion-markdown-it/markdown_it_spec.rb +2 -2
- data/spec/motion-markdown-it/misc_spec.rb +90 -14
- data/spec/motion-markdown-it/testgen_helper.rb +1 -1
- data/spec/spec_helper.rb +2 -3
- metadata +13 -13
- data/lib/motion-markdown-it/common/url_schemas.rb +0 -173
- data/spec/motion-markdown-it/bench_mark_spec.rb +0 -44
@@ -7,20 +7,20 @@ module MarkdownIt
|
|
7
7
|
class ParserBlock
|
8
8
|
|
9
9
|
attr_accessor :ruler
|
10
|
-
|
10
|
+
|
11
11
|
RULES = [
|
12
12
|
# First 2 params - rule name & source. Secondary array - list of rules,
|
13
13
|
# which can be terminated by this one.
|
14
|
+
[ 'table', lambda { |state, startLine, endLine, silent| RulesBlock::Table.table(state, startLine, endLine, silent) }, [ 'paragraph', 'reference' ] ],
|
14
15
|
[ 'code', lambda { |state, startLine, endLine, silent| RulesBlock::Code.code(state, startLine, endLine, silent) } ],
|
15
16
|
[ 'fence', lambda { |state, startLine, endLine, silent| RulesBlock::Fence.fence(state, startLine, endLine, silent) }, [ 'paragraph', 'reference', 'blockquote', 'list' ] ],
|
16
|
-
[ 'blockquote', lambda { |state, startLine, endLine, silent| RulesBlock::Blockquote.blockquote(state, startLine, endLine, silent) }, [ 'paragraph', 'reference', 'list' ] ],
|
17
|
+
[ 'blockquote', lambda { |state, startLine, endLine, silent| RulesBlock::Blockquote.blockquote(state, startLine, endLine, silent) }, [ 'paragraph', 'reference', 'blockquote', 'list' ] ],
|
17
18
|
[ 'hr', lambda { |state, startLine, endLine, silent| RulesBlock::Hr.hr(state, startLine, endLine, silent) }, [ 'paragraph', 'reference', 'blockquote', 'list' ] ],
|
18
19
|
[ 'list', lambda { |state, startLine, endLine, silent| RulesBlock::List.list(state, startLine, endLine, silent) }, [ 'paragraph', 'reference', 'blockquote' ] ],
|
19
20
|
[ 'reference', lambda { |state, startLine, endLine, silent| RulesBlock::Reference.reference(state, startLine, endLine, silent) } ],
|
20
21
|
[ 'heading', lambda { |state, startLine, endLine, silent| RulesBlock::Heading.heading(state, startLine, endLine, silent) }, [ 'paragraph', 'reference', 'blockquote' ] ],
|
21
22
|
[ 'lheading', lambda { |state, startLine, endLine, silent| RulesBlock::Lheading.lheading(state, startLine, endLine, silent) } ],
|
22
23
|
[ 'html_block', lambda { |state, startLine, endLine, silent| RulesBlock::HtmlBlock.html_block(state, startLine, endLine, silent) }, [ 'paragraph', 'reference', 'blockquote' ] ],
|
23
|
-
[ 'table', lambda { |state, startLine, endLine, silent| RulesBlock::Table.table(state, startLine, endLine, silent) }, [ 'paragraph', 'reference' ] ],
|
24
24
|
[ 'paragraph', lambda { |state, startLine, endLine, silent| RulesBlock::Paragraph.paragraph(state, startLine) } ]
|
25
25
|
]
|
26
26
|
|
@@ -47,14 +47,14 @@ module MarkdownIt
|
|
47
47
|
line = startLine
|
48
48
|
hasEmptyLines = false
|
49
49
|
maxNesting = state.md.options[:maxNesting]
|
50
|
-
|
50
|
+
|
51
51
|
while line < endLine
|
52
52
|
state.line = line = state.skipEmptyLines(line)
|
53
53
|
break if line >= endLine
|
54
54
|
|
55
55
|
# Termination condition for nested calls.
|
56
56
|
# Nested calls currently used for blockquotes & lists
|
57
|
-
break if state.
|
57
|
+
break if state.sCount[line] < state.blkIndent
|
58
58
|
|
59
59
|
# If nesting level exceeded - skip tail to the end. That's not ordinary
|
60
60
|
# situation and we should not care about content.
|
@@ -74,7 +74,7 @@ module MarkdownIt
|
|
74
74
|
break if ok
|
75
75
|
end
|
76
76
|
|
77
|
-
# set state.tight
|
77
|
+
# set state.tight if we had an empty line before current tag
|
78
78
|
# i.e. latest empty line should not count
|
79
79
|
state.tight = !hasEmptyLines
|
80
80
|
|
@@ -88,9 +88,6 @@ module MarkdownIt
|
|
88
88
|
if line < endLine && state.isEmpty(line)
|
89
89
|
hasEmptyLines = true
|
90
90
|
line += 1
|
91
|
-
|
92
|
-
# two empty lines should stop the parser in list mode
|
93
|
-
break if line < endLine && state.parentType == 'list' && state.isEmpty(line)
|
94
91
|
state.line = line
|
95
92
|
end
|
96
93
|
end
|
@@ -102,7 +99,7 @@ module MarkdownIt
|
|
102
99
|
#------------------------------------------------------------------------------
|
103
100
|
def parse(src, md, env, outTokens)
|
104
101
|
|
105
|
-
|
102
|
+
return if !src
|
106
103
|
|
107
104
|
state = RulesBlock::StateBlock.new(src, md, env, outTokens)
|
108
105
|
|
@@ -5,9 +5,9 @@
|
|
5
5
|
#------------------------------------------------------------------------------
|
6
6
|
module MarkdownIt
|
7
7
|
class ParserInline
|
8
|
-
|
9
|
-
attr_accessor :ruler
|
10
|
-
|
8
|
+
|
9
|
+
attr_accessor :ruler, :ruler2
|
10
|
+
|
11
11
|
#------------------------------------------------------------------------------
|
12
12
|
# Parser rules
|
13
13
|
|
@@ -16,8 +16,8 @@ module MarkdownIt
|
|
16
16
|
[ 'newline', lambda { |state, startLine| RulesInline::Newline.newline(state, startLine) } ],
|
17
17
|
[ 'escape', lambda { |state, startLine| RulesInline::Escape.escape(state, startLine) } ],
|
18
18
|
[ 'backticks', lambda { |state, startLine| RulesInline::Backticks.backtick(state, startLine) } ],
|
19
|
-
[ 'strikethrough', lambda { |state,
|
20
|
-
[ 'emphasis', lambda { |state,
|
19
|
+
[ 'strikethrough', lambda { |state, silent| RulesInline::Strikethrough.tokenize(state, silent) } ],
|
20
|
+
[ 'emphasis', lambda { |state, silent| RulesInline::Emphasis.tokenize(state, silent) } ],
|
21
21
|
[ 'link', lambda { |state, startLine| RulesInline::Link.link(state, startLine) } ],
|
22
22
|
[ 'image', lambda { |state, startLine| RulesInline::Image.image(state, startLine) } ],
|
23
23
|
[ 'autolink', lambda { |state, startLine| RulesInline::Autolink.autolink(state, startLine) } ],
|
@@ -25,6 +25,12 @@ module MarkdownIt
|
|
25
25
|
[ 'entity', lambda { |state, startLine| RulesInline::Entity.entity(state, startLine) } ],
|
26
26
|
]
|
27
27
|
|
28
|
+
RULES2 = [
|
29
|
+
[ 'balance_pairs', lambda { |state| RulesInline::BalancePairs.link_pairs(state) } ],
|
30
|
+
[ 'strikethrough', lambda { |state| RulesInline::Strikethrough.postProcess(state) } ],
|
31
|
+
[ 'emphasis', lambda { |state| RulesInline::Emphasis.postProcess(state) } ],
|
32
|
+
[ 'text_collapse', lambda { |state| RulesInline::TextCollapse.text_collapse(state) } ]
|
33
|
+
];
|
28
34
|
|
29
35
|
#------------------------------------------------------------------------------
|
30
36
|
def initialize
|
@@ -36,6 +42,16 @@ module MarkdownIt
|
|
36
42
|
RULES.each do |rule|
|
37
43
|
@ruler.push(rule[0], rule[1])
|
38
44
|
end
|
45
|
+
|
46
|
+
# ParserInline#ruler2 -> Ruler
|
47
|
+
#
|
48
|
+
# [[Ruler]] instance. Second ruler used for post-processing
|
49
|
+
# (e.g. in emphasis-like rules).
|
50
|
+
@ruler2 = Ruler.new
|
51
|
+
|
52
|
+
RULES2.each do |rule|
|
53
|
+
@ruler2.push(rule[0], rule[1])
|
54
|
+
end
|
39
55
|
end
|
40
56
|
|
41
57
|
# Skip single token by running all rules in validation mode;
|
@@ -47,28 +63,42 @@ module MarkdownIt
|
|
47
63
|
len = rules.length
|
48
64
|
maxNesting = state.md.options[:maxNesting]
|
49
65
|
cache = state.cache
|
50
|
-
|
66
|
+
ok = false
|
51
67
|
|
52
68
|
if cache[pos] != nil
|
53
69
|
state.pos = cache[pos]
|
54
70
|
return
|
55
71
|
end
|
56
72
|
|
57
|
-
# istanbul ignore else
|
58
73
|
if state.level < maxNesting
|
59
74
|
0.upto(len -1) do |i|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
75
|
+
# Increment state.level and decrement it later to limit recursion.
|
76
|
+
# It's harmless to do here, because no tokens are created. But ideally,
|
77
|
+
# we'd need a separate private state variable for this purpose.
|
78
|
+
state.level += 1
|
79
|
+
ok = rules[i].call(state, true)
|
80
|
+
state.level -= 1
|
81
|
+
|
82
|
+
break if ok
|
64
83
|
end
|
84
|
+
else
|
85
|
+
# Too much nesting, just skip until the end of the paragraph.
|
86
|
+
#
|
87
|
+
# NOTE: this will cause links to behave incorrectly in the following case,
|
88
|
+
# when an amount of `[` is exactly equal to `maxNesting + 1`:
|
89
|
+
#
|
90
|
+
# [[[[[[[[[[[[[[[[[[[[[foo]()
|
91
|
+
#
|
92
|
+
# TODO: remove this workaround when CM standard will allow nested links
|
93
|
+
# (we can replace it by preventing links from being parsed in
|
94
|
+
# validation mode)
|
95
|
+
state.pos = state.posMax
|
65
96
|
end
|
66
97
|
|
67
|
-
state.pos += 1
|
98
|
+
state.pos += 1 if !ok
|
68
99
|
cache[pos] = state.pos
|
69
100
|
end
|
70
101
|
|
71
|
-
|
72
102
|
# Generate tokens for input range
|
73
103
|
#------------------------------------------------------------------------------
|
74
104
|
def tokenize(state)
|
@@ -115,7 +145,13 @@ module MarkdownIt
|
|
115
145
|
state = RulesInline::StateInline.new(str, md, env, outTokens)
|
116
146
|
|
117
147
|
tokenize(state)
|
118
|
-
end
|
119
148
|
|
149
|
+
rules = @ruler2.getRules('')
|
150
|
+
len = rules.length
|
151
|
+
|
152
|
+
0.upto(len - 1) do |i|
|
153
|
+
rules[i].call(state)
|
154
|
+
end
|
155
|
+
end
|
120
156
|
end
|
121
157
|
end
|
@@ -23,7 +23,8 @@ module MarkdownIt
|
|
23
23
|
quotes: "\u201c\u201d\u2018\u2019", # “”‘’
|
24
24
|
|
25
25
|
# Highlighter function. Should return escaped HTML,
|
26
|
-
# or '' if
|
26
|
+
# or '' if the source string is not changed and should be escaped externaly.
|
27
|
+
# If result starts with <pre... internal wrapper is skipped.
|
27
28
|
#
|
28
29
|
# function (/*str, lang*/) { return ''; }
|
29
30
|
#
|
@@ -69,6 +70,11 @@ module MarkdownIt
|
|
69
70
|
'link',
|
70
71
|
'newline',
|
71
72
|
'text'
|
73
|
+
],
|
74
|
+
rules2: [
|
75
|
+
'balance_pairs',
|
76
|
+
'emphasis',
|
77
|
+
'text_collapse'
|
72
78
|
]
|
73
79
|
}
|
74
80
|
}
|
@@ -20,16 +20,17 @@ module MarkdownIt
|
|
20
20
|
#
|
21
21
|
# For example, you can use '«»„“' for Russian, '„“‚‘' for German,
|
22
22
|
# and ['«\xA0', '\xA0»', '‹\xA0', '\xA0›'] for French (including nbsp).
|
23
|
-
quotes: "\u201c\u201d\u2018\u2019", # “”‘’
|
23
|
+
quotes: "\u201c\u201d\u2018\u2019", # “”‘’
|
24
24
|
|
25
25
|
# Highlighter function. Should return escaped HTML,
|
26
|
-
# or '' if
|
26
|
+
# or '' if the source string is not changed and should be escaped externaly.
|
27
|
+
# If result starts with <pre... internal wrapper is skipped.
|
27
28
|
#
|
28
29
|
# function (/*str, lang*/) { return ''; }
|
29
30
|
#
|
30
31
|
highlight: nil,
|
31
32
|
|
32
|
-
maxNesting:
|
33
|
+
maxNesting: 100 # Internal protection, recursion limit
|
33
34
|
},
|
34
35
|
|
35
36
|
components: {
|
@@ -24,7 +24,8 @@ module MarkdownIt
|
|
24
24
|
quotes: "\u201c\u201d\u2018\u2019", # “”‘’
|
25
25
|
|
26
26
|
# Highlighter function. Should return escaped HTML,
|
27
|
-
# or '' if
|
27
|
+
# or '' if the source string is not changed and should be escaped externaly.
|
28
|
+
# If result starts with <pre... internal wrapper is skipped.
|
28
29
|
#
|
29
30
|
# function (/*str, lang*/) { return ''; }
|
30
31
|
#
|
@@ -52,6 +53,10 @@ module MarkdownIt
|
|
52
53
|
inline: {
|
53
54
|
rules: [
|
54
55
|
'text'
|
56
|
+
],
|
57
|
+
rules2: [
|
58
|
+
'balance_pairs',
|
59
|
+
'text_collapse'
|
55
60
|
]
|
56
61
|
}
|
57
62
|
}
|
@@ -10,16 +10,24 @@ module MarkdownIt
|
|
10
10
|
extend MarkdownIt::Common::Utils
|
11
11
|
|
12
12
|
attr_accessor :rules
|
13
|
-
|
13
|
+
|
14
14
|
# Default Rules
|
15
15
|
#------------------------------------------------------------------------------
|
16
|
-
def self.code_inline(tokens, idx)
|
17
|
-
|
16
|
+
def self.code_inline(tokens, idx, options, env, renderer)
|
17
|
+
token = tokens[idx]
|
18
|
+
|
19
|
+
return '<code' + renderer.renderAttrs(token) + '>' +
|
20
|
+
escapeHtml(tokens[idx].content) +
|
21
|
+
'</code>'
|
18
22
|
end
|
19
23
|
|
20
24
|
#------------------------------------------------------------------------------
|
21
|
-
def self.code_block(tokens, idx)
|
22
|
-
|
25
|
+
def self.code_block(tokens, idx, options, env, renderer)
|
26
|
+
token = tokens[idx]
|
27
|
+
|
28
|
+
return '<pre' + renderer.renderAttrs(token) + '><code>' +
|
29
|
+
escapeHtml(tokens[idx].content) +
|
30
|
+
"</code></pre>\n"
|
23
31
|
end
|
24
32
|
|
25
33
|
#------------------------------------------------------------------------------
|
@@ -30,7 +38,6 @@ module MarkdownIt
|
|
30
38
|
|
31
39
|
if !info.empty?
|
32
40
|
langName = info.split(/\s+/)[0]
|
33
|
-
token.attrPush([ 'class', options[:langPrefix] + langName ])
|
34
41
|
end
|
35
42
|
|
36
43
|
if options[:highlight]
|
@@ -39,7 +46,33 @@ module MarkdownIt
|
|
39
46
|
highlighted = escapeHtml(token.content)
|
40
47
|
end
|
41
48
|
|
42
|
-
|
49
|
+
if highlighted.start_with?('<pre')
|
50
|
+
return highlighted + "\n"
|
51
|
+
end
|
52
|
+
|
53
|
+
# If language exists, inject class gently, without modifying original token.
|
54
|
+
# May be, one day we will add .clone() for token and simplify this part, but
|
55
|
+
# now we prefer to keep things local.
|
56
|
+
if !info.empty?
|
57
|
+
i = token.attrIndex('class')
|
58
|
+
tmpAttrs = token.attrs ? token.attrs.dup : []
|
59
|
+
|
60
|
+
if i < 0
|
61
|
+
tmpAttrs.push([ 'class', options[:langPrefix] + langName ])
|
62
|
+
else
|
63
|
+
tmpAttrs[i][1] += ' ' + options[:langPrefix] + langName
|
64
|
+
end
|
65
|
+
|
66
|
+
# Fake token just to render attributes
|
67
|
+
tmpToken = Token.new(nil, nil, nil)
|
68
|
+
tmpToken.attrs = tmpAttrs
|
69
|
+
|
70
|
+
return '<pre><code' + renderer.renderAttrs(tmpToken) + '>' +
|
71
|
+
highlighted +
|
72
|
+
"</code></pre>\n"
|
73
|
+
end
|
74
|
+
|
75
|
+
return '<pre><code' + renderer.renderAttrs(token) + '>' + highlighted + "</code></pre>\n"
|
43
76
|
end
|
44
77
|
|
45
78
|
#------------------------------------------------------------------------------
|
@@ -84,8 +117,8 @@ module MarkdownIt
|
|
84
117
|
#------------------------------------------------------------------------------
|
85
118
|
def initialize
|
86
119
|
@default_rules = {
|
87
|
-
'code_inline' => lambda {|tokens, idx, options, env, renderer| Renderer.code_inline(tokens, idx)},
|
88
|
-
'code_block' => lambda {|tokens, idx, options, env, renderer| Renderer.code_block(tokens, idx)},
|
120
|
+
'code_inline' => lambda {|tokens, idx, options, env, renderer| Renderer.code_inline(tokens, idx, options, env, renderer)},
|
121
|
+
'code_block' => lambda {|tokens, idx, options, env, renderer| Renderer.code_block(tokens, idx, options, env, renderer)},
|
89
122
|
'fence' => lambda {|tokens, idx, options, env, renderer| Renderer.fence(tokens, idx, options, env, renderer)},
|
90
123
|
'image' => lambda {|tokens, idx, options, env, renderer| Renderer.image(tokens, idx, options, env, renderer)},
|
91
124
|
'hardbreak' => lambda {|tokens, idx, options, env, renderer| Renderer.hardbreak(tokens, idx, options)},
|
@@ -94,7 +127,7 @@ module MarkdownIt
|
|
94
127
|
'html_block' => lambda {|tokens, idx, options, env, renderer| Renderer.html_block(tokens, idx)},
|
95
128
|
'html_inline' => lambda {|tokens, idx, options, env, renderer| Renderer.html_inline(tokens, idx)}
|
96
129
|
}
|
97
|
-
|
130
|
+
|
98
131
|
# Renderer#rules -> Object
|
99
132
|
#
|
100
133
|
# Contains render rules for tokens. Can be updated and extended.
|
@@ -110,7 +143,7 @@ module MarkdownIt
|
|
110
143
|
# var result = md.renderInline(...);
|
111
144
|
# ```
|
112
145
|
#
|
113
|
-
# Each rule is called as
|
146
|
+
# Each rule is called as independet static function with fixed signature:
|
114
147
|
#
|
115
148
|
# ```javascript
|
116
149
|
# function my_token_render(tokens, idx, options, env, renderer) {
|
@@ -220,7 +253,7 @@ module MarkdownIt
|
|
220
253
|
|
221
254
|
0.upto(tokens.length - 1) do |i|
|
222
255
|
type = tokens[i].type
|
223
|
-
|
256
|
+
|
224
257
|
if rules[type] != nil
|
225
258
|
result += rules[type].call(tokens, i, options, env, self)
|
226
259
|
else
|
@@ -244,11 +277,10 @@ module MarkdownIt
|
|
244
277
|
#------------------------------------------------------------------------------
|
245
278
|
def renderInlineAsText(tokens, options, env)
|
246
279
|
result = ''
|
247
|
-
rules = @rules
|
248
280
|
|
249
281
|
0.upto(tokens.length - 1) do |i|
|
250
282
|
if tokens[i].type == 'text'
|
251
|
-
result +=
|
283
|
+
result += tokens[i].content
|
252
284
|
elsif tokens[i].type == 'image'
|
253
285
|
result += renderInlineAsText(tokens[i].children, options, env)
|
254
286
|
end
|
@@ -3,38 +3,93 @@
|
|
3
3
|
module MarkdownIt
|
4
4
|
module RulesBlock
|
5
5
|
class Blockquote
|
6
|
+
extend Common::Utils
|
6
7
|
|
7
8
|
#------------------------------------------------------------------------------
|
8
9
|
def self.blockquote(state, startLine, endLine, silent)
|
9
|
-
|
10
|
-
|
10
|
+
oldLineMax = state.lineMax
|
11
|
+
pos = state.bMarks[startLine] + state.tShift[startLine]
|
12
|
+
max = state.eMarks[startLine]
|
13
|
+
|
14
|
+
# if it's indented more than 3 spaces, it should be a code block
|
15
|
+
return false if (state.sCount[startLine] - state.blkIndent >= 4)
|
11
16
|
|
12
17
|
# check the block quote marker
|
13
18
|
return false if state.src.charCodeAt(pos) != 0x3E # >
|
14
19
|
pos += 1
|
15
|
-
|
20
|
+
|
16
21
|
# we know that it's going to be a valid blockquote,
|
17
22
|
# so no point trying to find the end of it in silent mode
|
18
23
|
return true if silent
|
19
24
|
|
25
|
+
# skip spaces after ">" and re-calculate offset
|
26
|
+
initial = offset = state.sCount[startLine] + pos - (state.bMarks[startLine] + state.tShift[startLine])
|
27
|
+
|
20
28
|
# skip one optional space after '>'
|
21
|
-
|
29
|
+
if state.src.charCodeAt(pos) == 0x20 # space
|
30
|
+
# ' > test '
|
31
|
+
# ^ -- position start of line here:
|
32
|
+
pos += 1
|
33
|
+
initial += 1
|
34
|
+
offset +=1
|
35
|
+
adjustTab = false
|
36
|
+
spaceAfterMarker = true
|
37
|
+
elsif state.src.charCodeAt(pos) == 0x09 # tab
|
38
|
+
spaceAfterMarker = true
|
22
39
|
|
23
|
-
|
24
|
-
|
40
|
+
if ((state.bsCount[startLine] + offset) % 4 == 3)
|
41
|
+
# ' >\t test '
|
42
|
+
# ^ -- position start of line here (tab has width===1)
|
43
|
+
pos += 1
|
44
|
+
initial += 1
|
45
|
+
offset += 1
|
46
|
+
adjustTab = false
|
47
|
+
else
|
48
|
+
# ' >\t test '
|
49
|
+
# ^ -- position start of line here + shift bsCount slightly
|
50
|
+
# to make extra space appear
|
51
|
+
adjustTab = true
|
52
|
+
end
|
53
|
+
else
|
54
|
+
spaceAfterMarker = false
|
55
|
+
end
|
25
56
|
|
26
57
|
oldBMarks = [ state.bMarks[startLine] ]
|
27
58
|
state.bMarks[startLine] = pos
|
28
59
|
|
29
|
-
|
30
|
-
|
31
|
-
|
60
|
+
while pos < max
|
61
|
+
ch = state.src.charCodeAt(pos)
|
62
|
+
|
63
|
+
if isSpace(ch)
|
64
|
+
if ch == 0x09
|
65
|
+
offset += 4 - (offset + state.bsCount[startLine] + (adjustTab ? 1 : 0)) % 4
|
66
|
+
else
|
67
|
+
offset += 1
|
68
|
+
end
|
69
|
+
else
|
70
|
+
break
|
71
|
+
end
|
72
|
+
|
73
|
+
pos += 1
|
74
|
+
end
|
75
|
+
|
76
|
+
oldBSCount = [ state.bsCount[startLine] ]
|
77
|
+
state.bsCount[startLine] = state.sCount[startLine] + 1 + (spaceAfterMarker ? 1 : 0)
|
78
|
+
|
79
|
+
lastLineEmpty = pos >= max
|
80
|
+
|
81
|
+
oldSCount = [ state.sCount[startLine] ]
|
82
|
+
state.sCount[startLine] = offset - initial
|
32
83
|
|
33
84
|
oldTShift = [ state.tShift[startLine] ]
|
34
85
|
state.tShift[startLine] = pos - state.bMarks[startLine]
|
35
86
|
|
36
87
|
terminatorRules = state.md.block.ruler.getRules('blockquote')
|
37
88
|
|
89
|
+
oldParentType = state.parentType
|
90
|
+
state.parentType = 'blockquote'
|
91
|
+
wasOutdented = false
|
92
|
+
|
38
93
|
# Search the end of the block
|
39
94
|
#
|
40
95
|
# Block ends with either:
|
@@ -48,15 +103,23 @@ module MarkdownIt
|
|
48
103
|
# >
|
49
104
|
# test
|
50
105
|
# ```
|
51
|
-
# 3. another tag
|
106
|
+
# 3. another tag:
|
52
107
|
# ```
|
53
108
|
# > test
|
54
109
|
# - - -
|
55
110
|
# ```
|
56
111
|
nextLine = startLine + 1
|
57
112
|
while nextLine < endLine
|
58
|
-
|
59
|
-
|
113
|
+
# check if it's outdented, i.e. it's inside list item and indented
|
114
|
+
# less than said list item:
|
115
|
+
#
|
116
|
+
# ```
|
117
|
+
# 1. anything
|
118
|
+
# > current blockquote
|
119
|
+
# 2. checking this line
|
120
|
+
# ```
|
121
|
+
wasOutdented = true if (state.sCount[nextLine] < state.blkIndent)
|
122
|
+
|
60
123
|
pos = state.bMarks[nextLine] + state.tShift[nextLine]
|
61
124
|
max = state.eMarks[nextLine]
|
62
125
|
|
@@ -65,19 +128,69 @@ module MarkdownIt
|
|
65
128
|
break
|
66
129
|
end
|
67
130
|
|
68
|
-
if state.src.charCodeAt(pos) == 0x3E
|
131
|
+
if state.src.charCodeAt(pos) == 0x3E && !wasOutdented # >
|
69
132
|
pos += 1
|
70
133
|
# This line is inside the blockquote.
|
71
134
|
|
135
|
+
# skip spaces after ">" and re-calculate offset
|
136
|
+
initial = offset = state.sCount[nextLine] + pos - (state.bMarks[nextLine] + state.tShift[nextLine])
|
137
|
+
|
72
138
|
# skip one optional space after '>'
|
73
|
-
|
139
|
+
if state.src.charCodeAt(pos) == 0x20 # space
|
140
|
+
# ' > test '
|
141
|
+
# ^ -- position start of line here:
|
142
|
+
pos += 1
|
143
|
+
initial += 1
|
144
|
+
offset += 1
|
145
|
+
adjustTab = false
|
146
|
+
spaceAfterMarker = true
|
147
|
+
elsif state.src.charCodeAt(pos) == 0x09 # tab
|
148
|
+
spaceAfterMarker = true
|
149
|
+
|
150
|
+
if ((state.bsCount[nextLine] + offset) % 4 == 3)
|
151
|
+
# ' >\t test '
|
152
|
+
# ^ -- position start of line here (tab has width===1)
|
153
|
+
pos += 1
|
154
|
+
initial += 1
|
155
|
+
offset += 1
|
156
|
+
adjustTab = false
|
157
|
+
else
|
158
|
+
# ' >\t test '
|
159
|
+
# ^ -- position start of line here + shift bsCount slightly
|
160
|
+
# to make extra space appear
|
161
|
+
adjustTab = true
|
162
|
+
end
|
163
|
+
else
|
164
|
+
spaceAfterMarker = false
|
165
|
+
end
|
74
166
|
|
75
167
|
oldBMarks.push(state.bMarks[nextLine])
|
76
168
|
state.bMarks[nextLine] = pos
|
77
169
|
|
78
|
-
|
170
|
+
while pos < max
|
171
|
+
ch = state.src.charCodeAt(pos)
|
172
|
+
|
173
|
+
if isSpace(ch)
|
174
|
+
if ch == 0x09
|
175
|
+
offset += 4 - (offset + state.bsCount[nextLine] + (adjustTab ? 1 : 0)) % 4
|
176
|
+
else
|
177
|
+
offset += 1
|
178
|
+
end
|
179
|
+
else
|
180
|
+
break
|
181
|
+
end
|
182
|
+
|
183
|
+
pos += 1
|
184
|
+
end
|
185
|
+
|
79
186
|
lastLineEmpty = pos >= max
|
80
187
|
|
188
|
+
oldBSCount.push(state.bsCount[nextLine])
|
189
|
+
state.bsCount[nextLine] = state.sCount[nextLine] + 1 + (spaceAfterMarker ? 1 : 0)
|
190
|
+
|
191
|
+
oldSCount.push(state.sCount[nextLine])
|
192
|
+
state.sCount[nextLine] = offset - initial\
|
193
|
+
|
81
194
|
oldTShift.push(state.tShift[nextLine])
|
82
195
|
state.tShift[nextLine] = pos - state.bMarks[nextLine]
|
83
196
|
nextLine += 1
|
@@ -97,39 +210,62 @@ module MarkdownIt
|
|
97
210
|
break
|
98
211
|
end
|
99
212
|
end
|
100
|
-
|
213
|
+
|
214
|
+
if terminate
|
215
|
+
# Quirk to enforce "hard termination mode" for paragraphs;
|
216
|
+
# normally if you call `tokenize(state, startLine, nextLine)`,
|
217
|
+
# paragraphs will look below nextLine for paragraph continuation,
|
218
|
+
# but if blockquote is terminated by another tag, they shouldn't
|
219
|
+
state.lineMax = nextLine
|
220
|
+
|
221
|
+
if state.blkIndent != 0
|
222
|
+
# state.blkIndent was non-zero, we now set it to zero,
|
223
|
+
# so we need to re-calculate all offsets to appear as
|
224
|
+
# if indent wasn't changed
|
225
|
+
oldBMarks.push(state.bMarks[nextLine])
|
226
|
+
oldBSCount.push(state.bsCount[nextLine])
|
227
|
+
oldTShift.push(state.tShift[nextLine])
|
228
|
+
oldSCount.push(state.sCount[nextLine])
|
229
|
+
state.sCount[nextLine] -= state.blkIndent
|
230
|
+
end
|
231
|
+
|
232
|
+
break
|
233
|
+
end
|
101
234
|
|
102
235
|
oldBMarks.push(state.bMarks[nextLine])
|
236
|
+
oldBSCount.push(state.bsCount[nextLine])
|
103
237
|
oldTShift.push(state.tShift[nextLine])
|
238
|
+
oldSCount.push(state.sCount[nextLine])
|
104
239
|
|
105
|
-
# A negative
|
240
|
+
# A negative indentation means that this is a paragraph continuation
|
106
241
|
#
|
107
|
-
|
108
|
-
# to be large enough to make any bugs obvious.
|
109
|
-
state.tShift[nextLine] = -1
|
242
|
+
state.sCount[nextLine] = -1
|
110
243
|
nextLine += 1
|
111
244
|
end
|
112
245
|
|
113
|
-
|
114
|
-
state.
|
246
|
+
oldIndent = state.blkIndent
|
247
|
+
state.blkIndent = 0
|
115
248
|
|
116
|
-
token
|
117
|
-
token.markup
|
118
|
-
token.map
|
249
|
+
token = state.push('blockquote_open', 'blockquote', 1)
|
250
|
+
token.markup = '>'
|
251
|
+
token.map = lines = [ startLine, 0 ]
|
119
252
|
|
120
253
|
state.md.block.tokenize(state, startLine, nextLine)
|
121
254
|
|
122
|
-
token
|
123
|
-
token.markup
|
255
|
+
token = state.push('blockquote_close', 'blockquote', -1)
|
256
|
+
token.markup = '>'
|
124
257
|
|
125
|
-
state.
|
126
|
-
|
258
|
+
state.lineMax = oldLineMax;
|
259
|
+
state.parentType = oldParentType
|
260
|
+
lines[1] = state.line
|
127
261
|
|
128
262
|
# Restore original tShift; this might not be necessary since the parser
|
129
263
|
# has already been here, but just to make sure we can do that.
|
130
264
|
(0...oldTShift.length).each do |i|
|
131
|
-
state.bMarks[i + startLine]
|
132
|
-
state.tShift[i + startLine]
|
265
|
+
state.bMarks[i + startLine] = oldBMarks[i]
|
266
|
+
state.tShift[i + startLine] = oldTShift[i]
|
267
|
+
state.sCount[i + startLine] = oldSCount[i]
|
268
|
+
state.bsCount[i + startLine] = oldBSCount[i]
|
133
269
|
end
|
134
270
|
state.blkIndent = oldIndent
|
135
271
|
return true
|