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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 94704fbe5fa8a967bf999b985367ab32dba0486e
4
- data.tar.gz: f839bf41bb8c37eb15c4ac327e9e607172d75333
3
+ metadata.gz: 90ee7c9cfe812b3c3a61238f5b26e77ca7675968
4
+ data.tar.gz: d34cc85f6c5b9c11cd7d00d252cfaacca693b071
5
5
  SHA512:
6
- metadata.gz: ee8f65ccb731bde283c02f26432607e83c65b6bef7cf64b4f4f3599a81b57931ff5cc4103b7e7db38b3f339cad733ba21f69adb60650b8e2d1adcfc53fe7515e
7
- data.tar.gz: 20dc882df2b9cc935df9ee03d4f1f4d10e4197a48f39ed7f183fa8b817aa5e8f290890da967f2d3377fb1efe6a5dc253d0999d13b4a1e12205647ea29ec62e85
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
  [![Gem Version](https://badge.fury.io/rb/motion-markdown-it-plugins.svg)](http://badge.fury.io/rb/motion-markdown-it-plugins)
4
+ [![Build Status](https://travis-ci.org/digitalmoksha/motion-markdown-it-plugins.svg?branch=master)](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
- PUNCT_CHARS = ' \n()[]\'".,!?-'
10
- PUNCT_CAHRS_ESCAPED = PUNCT_CHARS.chars.map {|c| escapeRE(c)}.join
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('inline', 'abbr_replace', lambda { |state| Abbr.abbr_replace(state) })
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 < 0 || state.src.charCodeAt(labelEnd + 1) != 0x3A) # ':'
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 (title.length == 0)
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
- state.env[:abbrRegExp] = Regexp.new(regText)
71
- end
72
- reg = state.env[:abbrRegExp]
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 (lastIndex > pos)
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
- pos = lastIndex - m[3].length
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.tShift[nextLine] < state.blkIndent)
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.tShift[nextLine] - state.blkIndent >= 4)
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 pos
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 (state.isEmpty(nextLine))
66
+ return false if nextLine >= endLine
67
+
68
+ if state.isEmpty(nextLine)
67
69
  nextLine += 1
68
- return false if (nextLine > endLine)
70
+ return false if nextLine >= endLine
69
71
  end
70
72
 
71
- return false if (state.tShift[nextLine] < state.blkIndent)
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
- #--- Iterate list items
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.tShift[ddLine] + 2
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.tShift[nextLine] < state.blkIndent)
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.tShift[dtLine] < state.blkIndent)
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.tShift[ddLine] < state.blkIndent)
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
- # Finilize list
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', 'ins', lambda { |state, silent| Ins.insert(state, silent) })
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
- # parse sequence of markers,
14
- # "start" should point at a valid marker
15
- #------------------------------------------------------------------------------
16
- def self.scanDelims(state, start)
17
- pos = start
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
- count = pos - start
20
+ return false if silent
35
21
 
36
- # treat end of the line as a whitespace
37
- nextChar = pos < max ? state.src.charCodeAt(pos) : 0x20
22
+ return false if marker != 0x2B # +
38
23
 
39
- isLastPunctChar = isMdAsciiPunct(lastChar) || isPunctChar(lastChar.chr(Encoding::UTF_8))
40
- isNextPunctChar = isMdAsciiPunct(nextChar) || isPunctChar(nextChar.chr(Encoding::UTF_8))
24
+ scanned = state.scanDelims(state.pos, true)
25
+ len = scanned[:length]
26
+ ch = fromCharCode(marker)
41
27
 
42
- isLastWhiteSpace = isWhiteSpace(lastChar)
43
- isNextWhiteSpace = isWhiteSpace(nextChar)
28
+ return false if len < 2
44
29
 
45
- if (isNextWhiteSpace)
46
- can_open = false
47
- elsif (isNextPunctChar)
48
- if (!(isLastWhiteSpace || isLastPunctChar))
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
- if (isLastWhiteSpace)
54
- can_close = false
55
- elsif (isLastPunctChar)
56
- if (!(isNextWhiteSpace || isNextPunctChar))
57
- can_close = false
58
- end
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
- return {can_open: can_open, can_close: can_close, delims: count}
55
+ state.pos += scanned[:length]
56
+
57
+ return true
62
58
  end
63
59
 
64
- #------------------------------------------------------------------------------
65
- def self.insert(state, silent)
66
- max = state.posMax
67
- start = state.pos
68
- marker = state.src.charCodeAt(start)
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
- return false if (marker != 0x2B) # '+'
71
- return false if (silent) # don't run any pairs in validation mode
67
+ i = 0
68
+ while i < max
69
+ startDelim = delimiters[i]
72
70
 
73
- res = scanDelims(state, start)
74
- startCount = res[:delims]
71
+ (i += 1) and next if startDelim[:marker] != 0x2B # +
75
72
 
76
- if (!res[:can_open])
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
- stack = (startCount / 2).floor
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.md.inline.skipToken(state)
109
- end
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
- if (!found)
112
- # parser failed to find ending tag, so it's not valid emphasis
113
- state.pos = start
114
- return false
115
- end
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
- # found!
118
- state.posMax = state.pos
119
- state.pos = start + 2
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
- # Earlier we checked !silent, but this implementation does not need it
122
- token = state.push('ins_open', 'ins', 1)
123
- token.markup = marker.chr * 2
96
+ i += 1
97
+ end
124
98
 
125
- state.md.inline.tokenize(state);
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
- token = state.push('ins_close', 'ins', -1);
128
- token.markup = marker.chr * 2
113
+ j -= 1
129
114
 
130
- state.pos = state.posMax + 2
131
- state.posMax = max
132
- return true
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.mark(state, silent) })
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
- # parse sequence of markers,
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.scanDelims(state, start)
17
- pos = start
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
16
+ def self.tokenize(state, silent)
17
+ start = state.pos
18
+ marker = state.src.charCodeAt(start)
33
19
 
34
- count = pos - start
20
+ return false if silent
35
21
 
36
- # treat end of the line as a whitespace
37
- nextChar = pos < max ? state.src.charCodeAt(pos) : 0x20
22
+ return false if marker != 0x3D # =
38
23
 
39
- isLastPunctChar = isMdAsciiPunct(lastChar) || isPunctChar(lastChar.chr(Encoding::UTF_8))
40
- isNextPunctChar = isMdAsciiPunct(nextChar) || isPunctChar(nextChar.chr(Encoding::UTF_8))
24
+ scanned = state.scanDelims(state.pos, true)
25
+ len = scanned[:length]
26
+ ch = fromCharCode(marker)
41
27
 
42
- isLastWhiteSpace = isWhiteSpace(lastChar)
43
- isNextWhiteSpace = isWhiteSpace(nextChar)
28
+ return false if len < 2
44
29
 
45
- if (isNextWhiteSpace)
46
- can_open = false
47
- elsif (isNextPunctChar)
48
- if (!(isLastWhiteSpace || isLastPunctChar))
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
- if (isLastWhiteSpace)
54
- can_close = false
55
- elsif (isLastPunctChar)
56
- if (!(isNextWhiteSpace || isNextPunctChar))
57
- can_close = false
58
- end
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
- return {can_open: can_open, can_close: can_close, delims: count}
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.mark(state, silent)
66
- max = state.posMax
67
- start = state.pos
68
- marker = state.src.charCodeAt(start)
69
-
70
- return false if (marker != 0x3D) # '='
71
- return false if (silent) # don't run any pairs in validation mode
72
-
73
- res = scanDelims(state, start)
74
- startCount = res[:delims]
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
- if (!res[:can_open])
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
- stack = (startCount / 2).floor
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
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
- state.md.inline.skipToken(state)
109
- end
113
+ j -= 1
110
114
 
111
- if (!found)
112
- # parser failed to find ending tag, so it's not valid emphasis
113
- state.pos = start
114
- return false
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
@@ -1,5 +1,5 @@
1
1
  module MotionMarkdownItPlugins
2
2
 
3
- VERSION = '1.0.10'
3
+ VERSION = '8.4.1'
4
4
 
5
5
  end
@@ -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
@@ -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
- expect(count).to eq 3
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 {|params| expect(params).to eq ' name '; return 1 } })
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
@@ -1,6 +1,5 @@
1
1
  # make sure to have `--require spec_helper` in `.rspec` to have the
2
2
  # spec_helper.rb included automatically in spec files
3
- require 'byebug'
4
- require 'benchmark'
3
+ require 'pry-byebug'
5
4
  require 'motion-markdown-it-plugins'
6
5
  require 'testgen_helper'
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: 1.0.10
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-02-04 00:00:00.000000000 Z
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: '4.0'
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: '4.0'
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: []