motion-markdown-it-plugins 1.0.10 → 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 +1 -0
- data/lib/motion-markdown-it-plugins/abbr/abbr.rb +33 -16
- data/lib/motion-markdown-it-plugins/container/container.rb +5 -4
- data/lib/motion-markdown-it-plugins/deflist/deflist.rb +43 -13
- data/lib/motion-markdown-it-plugins/ins/ins.rb +88 -101
- data/lib/motion-markdown-it-plugins/mark/mark.rb +90 -103
- data/lib/motion-markdown-it-plugins/version.rb +1 -1
- data/spec/abbr/abbr_spec.rb +1 -1
- data/spec/container/api_spec.rb +12 -5
- data/spec/spec_helper.rb +1 -2
- data/spec/sub/sub_spec.rb +5 -0
- metadata +18 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 90ee7c9cfe812b3c3a61238f5b26e77ca7675968
|
4
|
+
data.tar.gz: d34cc85f6c5b9c11cd7d00d252cfaacca693b071
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 702e8cf07a3a85b8a01ae2ca222041c4ba84165755017c21cb290137e38ca4bab00e9c6c9e9c88dfcc836c855d2d78486bfaae7bdb40da707263355b23dd83a6
|
7
|
+
data.tar.gz: 5eaa10dbde3ec0b172d3c357ecd2e1db17f87f2af1deb9e0b08c63393e2a73ab3b943d0de5e8d9b151c62c2da48113252b3676057df58958197853e611401c0f
|
data/README.md
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# motion-markdown-it-plugins
|
2
2
|
|
3
3
|
[](http://badge.fury.io/rb/motion-markdown-it-plugins)
|
4
|
+
[](https://travis-ci.org/digitalmoksha/motion-markdown-it-plugins)
|
4
5
|
|
5
6
|
Various useful plugins for use with `motion-markdown-it`. Works with Ruby and RubyMotion.
|
6
7
|
|
@@ -6,15 +6,21 @@ include MarkdownIt::Common::Utils
|
|
6
6
|
|
7
7
|
module MotionMarkdownItPlugins
|
8
8
|
class Abbr
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
# ASCII characters in Cc, Sc, Sm, Sk categories we should terminate on;
|
10
|
+
# you can check character classes here:
|
11
|
+
# http://www.unicode.org/Public/UNIDATA/UnicodeData.txt
|
12
|
+
OTHER_CHARS = ' \r\n$+<=>^`|~'
|
13
|
+
OTHER_CHARS_ESCAPED = OTHER_CHARS.chars.map {|c| escapeRE(c)}.join
|
14
|
+
|
15
|
+
UNICODE_PUNCT_RE = UCMicro::Categories::P::REGEX
|
16
|
+
UNICODE_SPACE_RE = UCMicro::Categories::Z::REGEX
|
17
|
+
|
12
18
|
#------------------------------------------------------------------------------
|
13
19
|
def self.init_plugin(md)
|
14
|
-
md.block.ruler.before('reference', 'abbr_def',
|
20
|
+
md.block.ruler.before('reference', 'abbr_def',
|
15
21
|
lambda { |state, startLine, endLine, silent| Abbr.abbr_def(state, startLine, endLine, silent) },
|
16
22
|
{alt: ['', 'paragraph', 'reference']})
|
17
|
-
md.core.ruler.after('
|
23
|
+
md.core.ruler.after('linkify', 'abbr_replace', lambda { |state| Abbr.abbr_replace(state) })
|
18
24
|
end
|
19
25
|
|
20
26
|
#------------------------------------------------------------------------------
|
@@ -43,12 +49,13 @@ module MotionMarkdownItPlugins
|
|
43
49
|
pos += 1
|
44
50
|
end
|
45
51
|
|
46
|
-
return false if (labelEnd
|
52
|
+
return false if (labelEnd.nil? || state.src.charCodeAt(labelEnd + 1) != 0x3A) # ':'
|
47
53
|
return true if (silent)
|
48
54
|
|
49
55
|
label = state.src.slice(labelStart...labelEnd).gsub(/\\(.)/, '\1')
|
50
56
|
title = state.src.slice((labelEnd + 2)...max).strip
|
51
|
-
return false if
|
57
|
+
return false if label.length == 0
|
58
|
+
return false if title.length == 0
|
52
59
|
|
53
60
|
state.env[:abbreviations] = {} if (!state.env[:abbreviations])
|
54
61
|
state.env[:abbreviations][label] = title if state.env[:abbreviations][label].nil?
|
@@ -62,14 +69,19 @@ module MotionMarkdownItPlugins
|
|
62
69
|
blockTokens = state.tokens
|
63
70
|
|
64
71
|
return if (!state.env[:abbreviations])
|
65
|
-
if (!state.env[:abbrRegExp])
|
66
|
-
regText = "(^|[#{PUNCT_CAHRS_ESCAPED}])("
|
67
|
-
regText << state.env[:abbreviations].keys.sort {|a, b| b.length <=> a.length}.map {|x| escapeRE(x)}.join('|')
|
68
|
-
regText << ")($|[#{PUNCT_CAHRS_ESCAPED}])"
|
69
72
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
+
regSimpleText = '(?:'
|
74
|
+
regSimpleText << state.env[:abbreviations].keys.sort {|a, b| b.length <=> a.length}.map {|x| escapeRE(x)}.join('|')
|
75
|
+
regSimpleText << ')'
|
76
|
+
regSimple = Regexp.new(regSimpleText)
|
77
|
+
|
78
|
+
regText = "(^|#{UNICODE_PUNCT_RE}|#{UNICODE_SPACE_RE}" +
|
79
|
+
"|[#{OTHER_CHARS_ESCAPED}])"
|
80
|
+
regText << '(' + state.env[:abbreviations].keys.sort {|a, b| b.length <=> a.length}.map {|x| escapeRE(x)}.join('|') + ')'
|
81
|
+
regText << "($|#{UNICODE_PUNCT_RE}|#{UNICODE_SPACE_RE}" +
|
82
|
+
"|[#{OTHER_CHARS_ESCAPED}])"
|
83
|
+
|
84
|
+
reg = Regexp.new(regText)
|
73
85
|
|
74
86
|
j = 0
|
75
87
|
l = blockTokens.length
|
@@ -88,9 +100,13 @@ module MotionMarkdownItPlugins
|
|
88
100
|
lastIndex = 0
|
89
101
|
nodes = []
|
90
102
|
|
103
|
+
# fast regexp run to determine whether there are any abbreviated words
|
104
|
+
# in the current token
|
105
|
+
i -= 1 and next if !(regSimple =~ text)
|
106
|
+
|
91
107
|
while ((m = reg.match(text, lastIndex)))
|
92
108
|
lastIndex = m.end(0)
|
93
|
-
if (
|
109
|
+
if m.begin(0) > 0 || m[1]
|
94
110
|
token = MarkdownIt::Token.new('text', '', 0)
|
95
111
|
token.content = text.slice(pos...(m.begin(0) + m[1].length))
|
96
112
|
nodes.push(token)
|
@@ -107,7 +123,8 @@ module MotionMarkdownItPlugins
|
|
107
123
|
token = MarkdownIt::Token.new('abbr_close', 'abbr', -1)
|
108
124
|
nodes.push(token)
|
109
125
|
|
110
|
-
|
126
|
+
lastIndex -= m[3].length
|
127
|
+
pos = lastIndex
|
111
128
|
end
|
112
129
|
|
113
130
|
i -= 1 and next if nodes.empty?
|
@@ -8,11 +8,11 @@ module MotionMarkdownItPlugins
|
|
8
8
|
extend MarkdownIt::Common::Utils
|
9
9
|
|
10
10
|
attr_accessor :render
|
11
|
-
|
11
|
+
|
12
12
|
#------------------------------------------------------------------------------
|
13
13
|
def self.init_plugin(md, name, options = {})
|
14
14
|
container_obj = Container.new(md, name, options)
|
15
|
-
md.block.ruler.before('fence', "container_#{name}",
|
15
|
+
md.block.ruler.before('fence', "container_#{name}",
|
16
16
|
lambda { |state, startLine, endLine, silent| container_obj.container(state, startLine, endLine, silent) },
|
17
17
|
{alt: [ '', 'paragraph', 'reference', 'blockquote', 'list' ]})
|
18
18
|
|
@@ -71,6 +71,7 @@ module MotionMarkdownItPlugins
|
|
71
71
|
|
72
72
|
markup = state.src.slice(start...pos)
|
73
73
|
params = state.src.slice(pos...max)
|
74
|
+
|
74
75
|
return false if (!@validate.call(params))
|
75
76
|
|
76
77
|
# Since start is found, we can report success here in validation mode
|
@@ -90,7 +91,7 @@ module MotionMarkdownItPlugins
|
|
90
91
|
start = state.bMarks[nextLine] + state.tShift[nextLine]
|
91
92
|
max = state.eMarks[nextLine]
|
92
93
|
|
93
|
-
if (start < max && state.
|
94
|
+
if (start < max && state.sCount[nextLine] < state.blkIndent)
|
94
95
|
# non-empty line with negative indent should stop the list:
|
95
96
|
# - ```
|
96
97
|
# test
|
@@ -99,7 +100,7 @@ module MotionMarkdownItPlugins
|
|
99
100
|
|
100
101
|
next if (@marker_char != state.src.charCodeAt(start))
|
101
102
|
|
102
|
-
if (state.
|
103
|
+
if (state.sCount[nextLine] - state.blkIndent >= 4)
|
103
104
|
# closing fence should be indented less than 4 spaces
|
104
105
|
next
|
105
106
|
end
|
@@ -9,7 +9,7 @@ module MotionMarkdownItPlugins
|
|
9
9
|
|
10
10
|
#------------------------------------------------------------------------------
|
11
11
|
def self.init_plugin(md)
|
12
|
-
md.block.ruler.before('paragraph', 'deflist',
|
12
|
+
md.block.ruler.before('paragraph', 'deflist',
|
13
13
|
lambda { |state, startLine, endLine, silent| Deflist.deflist(state, startLine, endLine, silent) },
|
14
14
|
{alt: ['', 'paragraph', 'reference']})
|
15
15
|
end
|
@@ -36,7 +36,7 @@ module MotionMarkdownItPlugins
|
|
36
36
|
# no empty definitions, e.g. " : "
|
37
37
|
return -1 if (pos >= max)
|
38
38
|
|
39
|
-
return
|
39
|
+
return start
|
40
40
|
end
|
41
41
|
|
42
42
|
#------------------------------------------------------------------------------
|
@@ -63,21 +63,27 @@ module MotionMarkdownItPlugins
|
|
63
63
|
end
|
64
64
|
|
65
65
|
nextLine = startLine + 1
|
66
|
-
if
|
66
|
+
return false if nextLine >= endLine
|
67
|
+
|
68
|
+
if state.isEmpty(nextLine)
|
67
69
|
nextLine += 1
|
68
|
-
return false if
|
70
|
+
return false if nextLine >= endLine
|
69
71
|
end
|
70
72
|
|
71
|
-
return false if (state.
|
73
|
+
return false if (state.sCount[nextLine] < state.blkIndent)
|
72
74
|
contentStart = skipMarker(state, nextLine)
|
73
75
|
return false if (contentStart < 0)
|
74
76
|
|
75
77
|
# Start list
|
76
78
|
listTokIdx = state.tokens.length
|
79
|
+
tight = true
|
80
|
+
|
77
81
|
token = state.push('dl_open', 'dl', 1)
|
78
82
|
token.map = listLines = [ startLine, 0 ]
|
79
83
|
|
80
|
-
|
84
|
+
#
|
85
|
+
# Iterate list items
|
86
|
+
#
|
81
87
|
|
82
88
|
dtLine = startLine
|
83
89
|
ddLine = nextLine
|
@@ -90,7 +96,6 @@ module MotionMarkdownItPlugins
|
|
90
96
|
# OUTER:
|
91
97
|
while true
|
92
98
|
break_outer = false
|
93
|
-
tight = true
|
94
99
|
prevEmptyEnd = false
|
95
100
|
|
96
101
|
token = state.push('dt_open', 'dt', 1)
|
@@ -107,13 +112,37 @@ module MotionMarkdownItPlugins
|
|
107
112
|
token = state.push('dd_open', 'dd', 1)
|
108
113
|
token.map = itemLines = [ nextLine, 0 ]
|
109
114
|
|
115
|
+
pos = contentStart
|
116
|
+
max = state.eMarks[ddLine]
|
117
|
+
offset = state.sCount[ddLine] + contentStart - (state.bMarks[ddLine] + state.tShift[ddLine])
|
118
|
+
|
119
|
+
while pos < max
|
120
|
+
ch = state.src.charCodeAt(pos)
|
121
|
+
|
122
|
+
if isSpace(ch)
|
123
|
+
if ch == 0x09
|
124
|
+
offset += 4 - offset % 4
|
125
|
+
else
|
126
|
+
offset += 1
|
127
|
+
end
|
128
|
+
else
|
129
|
+
break
|
130
|
+
end
|
131
|
+
|
132
|
+
pos += 1
|
133
|
+
end
|
134
|
+
|
135
|
+
contentStart = pos
|
136
|
+
|
110
137
|
oldTight = state.tight
|
111
138
|
oldDDIndent = state.ddIndent
|
112
139
|
oldIndent = state.blkIndent
|
113
140
|
oldTShift = state.tShift[ddLine]
|
141
|
+
oldSCount = state.sCount[ddLine]
|
114
142
|
oldParentType = state.parentType
|
115
|
-
state.blkIndent = state.ddIndent = state.
|
143
|
+
state.blkIndent = state.ddIndent = state.sCount[ddLine] + 2
|
116
144
|
state.tShift[ddLine] = contentStart - state.bMarks[ddLine]
|
145
|
+
state.sCount[ddLine] = offset
|
117
146
|
state.tight = true
|
118
147
|
state.parentType = 'deflist'
|
119
148
|
|
@@ -128,6 +157,7 @@ module MotionMarkdownItPlugins
|
|
128
157
|
prevEmptyEnd = (state.line - ddLine) > 1 && state.isEmpty(state.line - 1)
|
129
158
|
|
130
159
|
state.tShift[ddLine] = oldTShift
|
160
|
+
state.sCount[ddLine] = oldSCount
|
131
161
|
state.tight = oldTight
|
132
162
|
state.parentType = oldParentType
|
133
163
|
state.blkIndent = oldIndent
|
@@ -138,7 +168,7 @@ module MotionMarkdownItPlugins
|
|
138
168
|
itemLines[1] = nextLine = state.line
|
139
169
|
|
140
170
|
break_outer = true and break if (nextLine >= endLine)
|
141
|
-
break_outer = true and break if (state.
|
171
|
+
break_outer = true and break if (state.sCount[nextLine] < state.blkIndent)
|
142
172
|
contentStart = skipMarker(state, nextLine)
|
143
173
|
break if (contentStart < 0)
|
144
174
|
|
@@ -148,19 +178,19 @@ module MotionMarkdownItPlugins
|
|
148
178
|
# insert DD tag and repeat checking
|
149
179
|
end
|
150
180
|
break if break_outer
|
151
|
-
|
181
|
+
|
152
182
|
break if (nextLine >= endLine)
|
153
183
|
dtLine = nextLine
|
154
184
|
|
155
185
|
break if (state.isEmpty(dtLine))
|
156
|
-
break if (state.
|
186
|
+
break if (state.sCount[dtLine] < state.blkIndent)
|
157
187
|
|
158
188
|
ddLine = dtLine + 1
|
159
189
|
break if (ddLine >= endLine)
|
160
190
|
ddLine += 1 if (state.isEmpty(ddLine))
|
161
191
|
break if (ddLine >= endLine)
|
162
192
|
|
163
|
-
break if (state.
|
193
|
+
break if (state.sCount[ddLine] < state.blkIndent)
|
164
194
|
contentStart = skipMarker(state, ddLine)
|
165
195
|
break if (contentStart < 0)
|
166
196
|
|
@@ -168,7 +198,7 @@ module MotionMarkdownItPlugins
|
|
168
198
|
# insert DT and DD tags and repeat checking
|
169
199
|
end
|
170
200
|
|
171
|
-
#
|
201
|
+
# Finalize list
|
172
202
|
token = state.push('dl_close', 'dl', -1)
|
173
203
|
listLines[1] = nextLine
|
174
204
|
state.line = nextLine
|
@@ -7,130 +7,117 @@ module MotionMarkdownItPlugins
|
|
7
7
|
|
8
8
|
#------------------------------------------------------------------------------
|
9
9
|
def self.init_plugin(md)
|
10
|
-
md.inline.ruler.before('emphasis',
|
10
|
+
md.inline.ruler.before('emphasis', 'ins', lambda { |state, silent| Ins.tokenize(state, silent) })
|
11
|
+
md.inline.ruler2.before('emphasis', 'ins', lambda { |state| Ins.postProcess(state) })
|
11
12
|
end
|
12
13
|
|
13
|
-
#
|
14
|
-
#
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
can_open = true
|
19
|
-
can_close = true
|
20
|
-
max = state.posMax
|
21
|
-
marker = state.src.charCodeAt(start)
|
22
|
-
|
23
|
-
# treat beginning of the line as a whitespace
|
24
|
-
lastChar = start > 0 ? state.src.charCodeAt(start - 1) : 0x20
|
25
|
-
|
26
|
-
while (pos < max && state.src.charCodeAt(pos) == marker)
|
27
|
-
pos += 1
|
28
|
-
end
|
29
|
-
|
30
|
-
if (pos >= max)
|
31
|
-
can_open = false
|
32
|
-
end
|
14
|
+
# Insert each marker as a separate text token, and add it to delimiter list
|
15
|
+
#
|
16
|
+
def self.tokenize(state, silent)
|
17
|
+
start = state.pos
|
18
|
+
marker = state.src.charCodeAt(start)
|
33
19
|
|
34
|
-
|
20
|
+
return false if silent
|
35
21
|
|
36
|
-
|
37
|
-
nextChar = pos < max ? state.src.charCodeAt(pos) : 0x20
|
22
|
+
return false if marker != 0x2B # +
|
38
23
|
|
39
|
-
|
40
|
-
|
24
|
+
scanned = state.scanDelims(state.pos, true)
|
25
|
+
len = scanned[:length]
|
26
|
+
ch = fromCharCode(marker)
|
41
27
|
|
42
|
-
|
43
|
-
isNextWhiteSpace = isWhiteSpace(nextChar)
|
28
|
+
return false if len < 2
|
44
29
|
|
45
|
-
if
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
can_open = false
|
50
|
-
end
|
30
|
+
if len % 2 > 0
|
31
|
+
token = state.push('text', '', 0)
|
32
|
+
token.content = ch
|
33
|
+
len -= 1
|
51
34
|
end
|
52
35
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
36
|
+
i = 0
|
37
|
+
while i < len
|
38
|
+
token = state.push('text', '', 0)
|
39
|
+
token.content = ch + ch
|
40
|
+
|
41
|
+
state.delimiters.push({
|
42
|
+
marker: marker,
|
43
|
+
length: scanned[:length],
|
44
|
+
jump: i,
|
45
|
+
token: state.tokens.length - 1,
|
46
|
+
level: state.level,
|
47
|
+
end: -1,
|
48
|
+
open: scanned[:can_open],
|
49
|
+
close: scanned[:can_close]
|
50
|
+
})
|
51
|
+
|
52
|
+
i += 2
|
59
53
|
end
|
60
54
|
|
61
|
-
|
55
|
+
state.pos += scanned[:length]
|
56
|
+
|
57
|
+
return true
|
62
58
|
end
|
63
59
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
60
|
+
# Walk through delimiter list and replace text tokens with tags
|
61
|
+
#
|
62
|
+
def self.postProcess(state)
|
63
|
+
loneMarkers = []
|
64
|
+
delimiters = state.delimiters
|
65
|
+
max = state.delimiters.length
|
69
66
|
|
70
|
-
|
71
|
-
|
67
|
+
i = 0
|
68
|
+
while i < max
|
69
|
+
startDelim = delimiters[i]
|
72
70
|
|
73
|
-
|
74
|
-
startCount = res[:delims]
|
71
|
+
(i += 1) and next if startDelim[:marker] != 0x2B # +
|
75
72
|
|
76
|
-
|
77
|
-
state.pos += startCount
|
78
|
-
# Earlier we checked !silent, but this implementation does not need it
|
79
|
-
state.pending += state.src.slice(start...state.pos)
|
80
|
-
return true
|
81
|
-
end
|
73
|
+
(i += 1) and next if startDelim[:end] == -1
|
82
74
|
|
83
|
-
|
84
|
-
return false if (stack <= 0)
|
85
|
-
state.pos = start + startCount
|
86
|
-
|
87
|
-
while (state.pos < max)
|
88
|
-
if (state.src.charCodeAt(state.pos) == marker)
|
89
|
-
res = scanDelims(state, state.pos)
|
90
|
-
count = res[:delims]
|
91
|
-
tagCount = (count / 2).floor
|
92
|
-
if (res[:can_close])
|
93
|
-
if (tagCount >= stack)
|
94
|
-
state.pos += count - 2
|
95
|
-
found = true
|
96
|
-
break
|
97
|
-
end
|
98
|
-
stack -= tagCount
|
99
|
-
state.pos += count
|
100
|
-
next
|
101
|
-
end
|
102
|
-
|
103
|
-
stack += tagCount if (res[:can_open])
|
104
|
-
state.pos += count
|
105
|
-
next
|
106
|
-
end
|
75
|
+
endDelim = delimiters[startDelim[:end]]
|
107
76
|
|
108
|
-
state.
|
109
|
-
|
77
|
+
token = state.tokens[startDelim[:token]]
|
78
|
+
token.type = 'ins_open'
|
79
|
+
token.tag = 'ins'
|
80
|
+
token.nesting = 1
|
81
|
+
token.markup = '++'
|
82
|
+
token.content = ''
|
110
83
|
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
84
|
+
token = state.tokens[endDelim[:token]]
|
85
|
+
token.type = 'ins_close'
|
86
|
+
token.tag = 'ins'
|
87
|
+
token.nesting = -1
|
88
|
+
token.markup = '++'
|
89
|
+
token.content = ''
|
116
90
|
|
117
|
-
|
118
|
-
|
119
|
-
|
91
|
+
if (state.tokens[endDelim[:token] - 1].type == 'text' &&
|
92
|
+
state.tokens[endDelim[:token] - 1].content == '+')
|
93
|
+
loneMarkers.push(endDelim[:token] - 1)
|
94
|
+
end
|
120
95
|
|
121
|
-
|
122
|
-
|
123
|
-
token.markup = marker.chr * 2
|
96
|
+
i += 1
|
97
|
+
end
|
124
98
|
|
125
|
-
|
99
|
+
# If a marker sequence has an odd number of characters, it's splitted
|
100
|
+
# like this: `~~~~~` -> `~` + `~~` + `~~`, leaving one marker at the
|
101
|
+
# start of the sequence.
|
102
|
+
#
|
103
|
+
# So, we have to move all those markers after subsequent s_close tags.
|
104
|
+
#
|
105
|
+
while loneMarkers.length > 0
|
106
|
+
i = loneMarkers.pop
|
107
|
+
j = i + 1
|
108
|
+
|
109
|
+
while j < state.tokens.length && state.tokens[j].type == 'ins_close'
|
110
|
+
j += 1
|
111
|
+
end
|
126
112
|
|
127
|
-
|
128
|
-
token.markup = marker.chr * 2
|
113
|
+
j -= 1
|
129
114
|
|
130
|
-
|
131
|
-
|
132
|
-
|
115
|
+
if i != j
|
116
|
+
token = state.tokens[j]
|
117
|
+
state.tokens[j] = state.tokens[i]
|
118
|
+
state.tokens[i] = token
|
119
|
+
end
|
120
|
+
end
|
133
121
|
end
|
134
|
-
|
135
122
|
end
|
136
|
-
end
|
123
|
+
end
|
@@ -7,130 +7,117 @@ module MotionMarkdownItPlugins
|
|
7
7
|
|
8
8
|
#------------------------------------------------------------------------------
|
9
9
|
def self.init_plugin(md)
|
10
|
-
md.inline.ruler.before('emphasis', 'mark', lambda { |state, silent| Mark.
|
10
|
+
md.inline.ruler.before('emphasis', 'mark', lambda { |state, silent| Mark.tokenize(state, silent) })
|
11
|
+
md.inline.ruler2.before('emphasis', 'mark', lambda { |state| Mark.postProcess(state) })
|
11
12
|
end
|
12
13
|
|
13
|
-
#
|
14
|
-
# "start" should point at a valid marker
|
14
|
+
# Insert each marker as a separate text token, and add it to delimiter list
|
15
15
|
#------------------------------------------------------------------------------
|
16
|
-
def self.
|
17
|
-
|
18
|
-
|
19
|
-
can_close = true
|
20
|
-
max = state.posMax
|
21
|
-
marker = state.src.charCodeAt(start)
|
22
|
-
|
23
|
-
# treat beginning of the line as a whitespace
|
24
|
-
lastChar = start > 0 ? state.src.charCodeAt(start - 1) : 0x20
|
25
|
-
|
26
|
-
while (pos < max && state.src.charCodeAt(pos) == marker)
|
27
|
-
pos += 1
|
28
|
-
end
|
29
|
-
|
30
|
-
if (pos >= max)
|
31
|
-
can_open = false
|
32
|
-
end
|
16
|
+
def self.tokenize(state, silent)
|
17
|
+
start = state.pos
|
18
|
+
marker = state.src.charCodeAt(start)
|
33
19
|
|
34
|
-
|
20
|
+
return false if silent
|
35
21
|
|
36
|
-
|
37
|
-
nextChar = pos < max ? state.src.charCodeAt(pos) : 0x20
|
22
|
+
return false if marker != 0x3D # =
|
38
23
|
|
39
|
-
|
40
|
-
|
24
|
+
scanned = state.scanDelims(state.pos, true)
|
25
|
+
len = scanned[:length]
|
26
|
+
ch = fromCharCode(marker)
|
41
27
|
|
42
|
-
|
43
|
-
isNextWhiteSpace = isWhiteSpace(nextChar)
|
28
|
+
return false if len < 2
|
44
29
|
|
45
|
-
if
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
can_open = false
|
50
|
-
end
|
30
|
+
if len % 2 > 0
|
31
|
+
token = state.push('text', '', 0)
|
32
|
+
token.content = ch
|
33
|
+
len -= 1
|
51
34
|
end
|
52
35
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
36
|
+
i = 0
|
37
|
+
while i < len
|
38
|
+
token = state.push('text', '', 0)
|
39
|
+
token.content = ch + ch
|
40
|
+
|
41
|
+
state.delimiters.push({
|
42
|
+
marker: marker,
|
43
|
+
length: scanned[:length],
|
44
|
+
jump: i,
|
45
|
+
token: state.tokens.length - 1,
|
46
|
+
level: state.level,
|
47
|
+
end: -1,
|
48
|
+
open: scanned[:can_open],
|
49
|
+
close: scanned[:can_close]
|
50
|
+
})
|
51
|
+
|
52
|
+
i += 2
|
59
53
|
end
|
60
54
|
|
61
|
-
|
55
|
+
state.pos += scanned[:length]
|
56
|
+
|
57
|
+
return true
|
62
58
|
end
|
63
59
|
|
60
|
+
# Walk through delimiter list and replace text tokens with tags
|
64
61
|
#------------------------------------------------------------------------------
|
65
|
-
def self.
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
62
|
+
def self.postProcess(state)
|
63
|
+
loneMarkers = []
|
64
|
+
delimiters = state.delimiters
|
65
|
+
max = state.delimiters.length
|
66
|
+
|
67
|
+
i = 0
|
68
|
+
while i < max
|
69
|
+
startDelim = delimiters[i]
|
70
|
+
|
71
|
+
(i += 1) and next if startDelim[:marker] != 0x3D # =
|
72
|
+
|
73
|
+
(i += 1) and next if startDelim[:end] == -1
|
74
|
+
|
75
|
+
endDelim = delimiters[startDelim[:end]]
|
76
|
+
|
77
|
+
token = state.tokens[startDelim[:token]]
|
78
|
+
token.type = 'mark_open'
|
79
|
+
token.tag = 'mark'
|
80
|
+
token.nesting = 1
|
81
|
+
token.markup = '=='
|
82
|
+
token.content = ''
|
83
|
+
|
84
|
+
token = state.tokens[endDelim[:token]]
|
85
|
+
token.type = 'mark_close'
|
86
|
+
token.tag = 'mark'
|
87
|
+
token.nesting = -1
|
88
|
+
token.markup = '=='
|
89
|
+
token.content = ''
|
90
|
+
|
91
|
+
if (state.tokens[endDelim[:token] - 1].type == 'text' &&
|
92
|
+
state.tokens[endDelim[:token] - 1].content == '=')
|
93
|
+
loneMarkers.push(endDelim[:token] - 1)
|
94
|
+
end
|
75
95
|
|
76
|
-
|
77
|
-
state.pos += startCount
|
78
|
-
# Earlier we checked !silent, but this implementation does not need it
|
79
|
-
state.pending += state.src.slice(start...state.pos)
|
80
|
-
return true
|
96
|
+
i += 1
|
81
97
|
end
|
82
98
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
found = true
|
96
|
-
break
|
97
|
-
end
|
98
|
-
stack -= tagCount
|
99
|
-
state.pos += count
|
100
|
-
next
|
101
|
-
end
|
102
|
-
|
103
|
-
stack += tagCount if (res[:can_open])
|
104
|
-
state.pos += count
|
105
|
-
next
|
99
|
+
# If a marker sequence has an odd number of characters, it's splitted
|
100
|
+
# like this: `~~~~~` -> `~` + `~~` + `~~`, leaving one marker at the
|
101
|
+
# start of the sequence.
|
102
|
+
#
|
103
|
+
# So, we have to move all those markers after subsequent s_close tags.
|
104
|
+
#
|
105
|
+
while loneMarkers.length > 0
|
106
|
+
i = loneMarkers.pop
|
107
|
+
j = i + 1
|
108
|
+
|
109
|
+
while j < state.tokens.length && state.tokens[j].type === 'mark_close'
|
110
|
+
j += 1
|
106
111
|
end
|
107
112
|
|
108
|
-
|
109
|
-
end
|
113
|
+
j -= 1
|
110
114
|
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
+
if i != j
|
116
|
+
token = state.tokens[j]
|
117
|
+
state.tokens[j] = state.tokens[i]
|
118
|
+
state.tokens[i] = token
|
119
|
+
end
|
115
120
|
end
|
116
|
-
|
117
|
-
# found!
|
118
|
-
state.posMax = state.pos
|
119
|
-
state.pos = start + 2
|
120
|
-
|
121
|
-
# Earlier we checked !silent, but this implementation does not need it
|
122
|
-
token = state.push('mark_open', 'mark', 1)
|
123
|
-
token.markup = marker.chr * 2
|
124
|
-
|
125
|
-
state.md.inline.tokenize(state)
|
126
|
-
|
127
|
-
token = state.push('mark_close', 'mark', -1)
|
128
|
-
token.markup = marker.chr * 2
|
129
|
-
|
130
|
-
state.pos = state.posMax + 2
|
131
|
-
state.posMax = max
|
132
|
-
return true
|
133
121
|
end
|
134
|
-
|
135
122
|
end
|
136
|
-
end
|
123
|
+
end
|
data/spec/abbr/abbr_spec.rb
CHANGED
@@ -2,7 +2,7 @@ fixture_dir = fixture_path('abbr/fixtures')
|
|
2
2
|
|
3
3
|
#------------------------------------------------------------------------------
|
4
4
|
describe 'markdown-it-abbr' do
|
5
|
-
md = MarkdownIt::Parser.new
|
5
|
+
md = MarkdownIt::Parser.new(linkify: true)
|
6
6
|
md.use(MotionMarkdownItPlugins::Abbr)
|
7
7
|
generate(File.join(fixture_dir, 'abbr.txt'), md)
|
8
8
|
end
|
data/spec/container/api_spec.rb
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
#------------------------------------------------------------------------------
|
2
2
|
describe 'api' do
|
3
|
-
|
3
|
+
|
4
4
|
#------------------------------------------------------------------------------
|
5
5
|
it 'renderer' do
|
6
6
|
md = MarkdownIt::Parser.new
|
7
7
|
md.use(MotionMarkdownItPlugins::Container, 'spoiler',
|
8
|
-
{ render: lambda {|tokens, idx, _options, env, renderer|
|
8
|
+
{ render: lambda {|tokens, idx, _options, env, renderer|
|
9
9
|
tokens[idx].nesting == 1 ? "<details><summary>click me</summary>\n" : "</details>\n" }})
|
10
10
|
res = md.render("::: spoiler\n*content*\n:::\n")
|
11
11
|
expect(res).to eq "<details><summary>click me</summary>\n<p><em>content</em></p>\n</details>\n"
|
@@ -61,14 +61,21 @@ describe 'api' do
|
|
61
61
|
md = MarkdownIt::Parser.new
|
62
62
|
md.use(MotionMarkdownItPlugins::Container, 'name', {validate: lambda {|params| count += 1} })
|
63
63
|
md.parse(":\n::\n:::\n::::\n:::::\n", {})
|
64
|
-
|
64
|
+
|
65
|
+
# called by paragraph and lheading 3 times each
|
66
|
+
expect(count).to be > 0
|
67
|
+
expect(count % 3).to eq 0
|
65
68
|
end
|
66
69
|
|
67
70
|
#------------------------------------------------------------------------------
|
68
71
|
it 'should not trim params' do
|
69
72
|
md = MarkdownIt::Parser.new
|
70
|
-
md.use(MotionMarkdownItPlugins::Container, 'name',
|
71
|
-
{validate: lambda
|
73
|
+
md.use(MotionMarkdownItPlugins::Container, 'name',
|
74
|
+
{validate: lambda do |params|
|
75
|
+
expect(params).to eq " \tname "
|
76
|
+
return 1
|
77
|
+
end
|
78
|
+
})
|
72
79
|
md.parse("::: \tname \ncontent\n:::\n", {})
|
73
80
|
end
|
74
81
|
end
|
data/spec/spec_helper.rb
CHANGED
data/spec/sub/sub_spec.rb
CHANGED
@@ -1,5 +1,10 @@
|
|
1
1
|
fixture_dir = fixture_path('sub/fixtures')
|
2
2
|
|
3
|
+
# TODO: these are parsed incorrectly:
|
4
|
+
#
|
5
|
+
# ~~~foo~~~
|
6
|
+
# ~~~foo~ bar~~
|
7
|
+
|
3
8
|
#------------------------------------------------------------------------------
|
4
9
|
describe 'markdown-it-sub' do
|
5
10
|
md = MarkdownIt::Parser.new
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: motion-markdown-it-plugins
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 8.4.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brett Walker
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-04-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: motion-markdown-it
|
@@ -16,14 +16,28 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '8.0'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
26
|
+
version: '8.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bacon-expect
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.0'
|
27
41
|
description: Plugins for use with motion-markdown-it
|
28
42
|
email: github@digitalmoksha.com
|
29
43
|
executables: []
|