motion-markdown-it-plugins 1.0.10 → 8.4.1

Sign up to get free protection for your applications and to get access to all the features.
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: []