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.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +69 -16
  3. data/lib/motion-markdown-it.rb +7 -5
  4. data/lib/motion-markdown-it/common/html_blocks.rb +6 -2
  5. data/lib/motion-markdown-it/common/utils.rb +19 -4
  6. data/lib/motion-markdown-it/helpers/helper_wrapper.rb +9 -0
  7. data/lib/motion-markdown-it/helpers/parse_link_destination.rb +8 -7
  8. data/lib/motion-markdown-it/index.rb +60 -18
  9. data/lib/motion-markdown-it/parser_block.rb +7 -10
  10. data/lib/motion-markdown-it/parser_inline.rb +50 -14
  11. data/lib/motion-markdown-it/presets/commonmark.rb +7 -1
  12. data/lib/motion-markdown-it/presets/default.rb +4 -3
  13. data/lib/motion-markdown-it/presets/zero.rb +6 -1
  14. data/lib/motion-markdown-it/renderer.rb +46 -14
  15. data/lib/motion-markdown-it/rules_block/blockquote.rb +167 -31
  16. data/lib/motion-markdown-it/rules_block/code.rb +4 -3
  17. data/lib/motion-markdown-it/rules_block/fence.rb +9 -4
  18. data/lib/motion-markdown-it/rules_block/heading.rb +8 -3
  19. data/lib/motion-markdown-it/rules_block/hr.rb +10 -5
  20. data/lib/motion-markdown-it/rules_block/html_block.rb +6 -3
  21. data/lib/motion-markdown-it/rules_block/lheading.rb +64 -26
  22. data/lib/motion-markdown-it/rules_block/list.rb +91 -22
  23. data/lib/motion-markdown-it/rules_block/paragraph.rb +14 -9
  24. data/lib/motion-markdown-it/rules_block/reference.rb +24 -14
  25. data/lib/motion-markdown-it/rules_block/state_block.rb +79 -24
  26. data/lib/motion-markdown-it/rules_block/table.rb +52 -26
  27. data/lib/motion-markdown-it/rules_core/normalize.rb +1 -23
  28. data/lib/motion-markdown-it/rules_core/replacements.rb +22 -2
  29. data/lib/motion-markdown-it/rules_core/smartquotes.rb +41 -12
  30. data/lib/motion-markdown-it/rules_inline/autolink.rb +5 -4
  31. data/lib/motion-markdown-it/rules_inline/balance_pairs.rb +48 -0
  32. data/lib/motion-markdown-it/rules_inline/emphasis.rb +104 -149
  33. data/lib/motion-markdown-it/rules_inline/entity.rb +2 -2
  34. data/lib/motion-markdown-it/rules_inline/escape.rb +5 -3
  35. data/lib/motion-markdown-it/rules_inline/image.rb +12 -23
  36. data/lib/motion-markdown-it/rules_inline/link.rb +20 -25
  37. data/lib/motion-markdown-it/rules_inline/newline.rb +2 -1
  38. data/lib/motion-markdown-it/rules_inline/state_inline.rb +60 -1
  39. data/lib/motion-markdown-it/rules_inline/strikethrough.rb +81 -97
  40. data/lib/motion-markdown-it/rules_inline/text_collapse.rb +40 -0
  41. data/lib/motion-markdown-it/token.rb +46 -1
  42. data/lib/motion-markdown-it/version.rb +1 -1
  43. data/spec/motion-markdown-it/markdown_it_spec.rb +2 -2
  44. data/spec/motion-markdown-it/misc_spec.rb +90 -14
  45. data/spec/motion-markdown-it/testgen_helper.rb +1 -1
  46. data/spec/spec_helper.rb +2 -3
  47. metadata +13 -13
  48. data/lib/motion-markdown-it/common/url_schemas.rb +0 -173
  49. data/spec/motion-markdown-it/bench_mark_spec.rb +0 -44
@@ -4,7 +4,7 @@ module MarkdownIt
4
4
  module RulesInline
5
5
  class Entity
6
6
  extend Common::Utils
7
-
7
+
8
8
  DIGITAL_RE = /^&#((?:x[a-f0-9]{1,8}|[0-9]{1,8}));/i
9
9
  NAMED_RE = /^&([a-z][a-z0-9]{1,31});/i
10
10
 
@@ -33,7 +33,7 @@ module MarkdownIt
33
33
  match = state.src.slice_to_end(pos).match(NAMED_RE)
34
34
  if match
35
35
  if HTMLEntities::MAPPINGS[match[1]]
36
- state.pending += HTMLEntities::MAPPINGS[match[1]].chr(Encoding::UTF_8) if !silent
36
+ state.pending += fromCodePoint(HTMLEntities::MAPPINGS[match[1]]) if !silent
37
37
  state.pos += match[0].length
38
38
  return true
39
39
  end
@@ -1,8 +1,9 @@
1
- # Proceess escaped chars and hardbreaks
1
+ # Process escaped chars and hardbreaks
2
2
  #------------------------------------------------------------------------------
3
3
  module MarkdownIt
4
4
  module RulesInline
5
5
  class Escape
6
+ extend Common::Utils
6
7
 
7
8
  ESCAPED = []
8
9
 
@@ -36,7 +37,9 @@ module MarkdownIt
36
37
 
37
38
  pos += 1
38
39
  # skip leading whitespaces from next line
39
- while (pos < max && state.src.charCodeAt(pos) == 0x20)
40
+ while pos < max
41
+ ch = state.src.charCodeAt(pos)
42
+ break if !isSpace(ch)
40
43
  pos += 1
41
44
  end
42
45
 
@@ -49,7 +52,6 @@ module MarkdownIt
49
52
  state.pos += 1
50
53
  return true
51
54
  end
52
-
53
55
  end
54
56
  end
55
57
  end
@@ -3,9 +3,6 @@
3
3
  module MarkdownIt
4
4
  module RulesInline
5
5
  class Image
6
- extend Helpers::ParseLinkDestination
7
- extend Helpers::ParseLinkLabel
8
- extend Helpers::ParseLinkTitle
9
6
  extend Common::Utils
10
7
 
11
8
  #------------------------------------------------------------------------------
@@ -18,7 +15,7 @@ module MarkdownIt
18
15
  return false if (state.src.charCodeAt(state.pos + 1) != 0x5B) # [
19
16
 
20
17
  labelStart = state.pos + 2
21
- labelEnd = parseLinkLabel(state, state.pos + 1, false)
18
+ labelEnd = state.md.helpers.parseLinkLabel(state, state.pos + 1, false)
22
19
 
23
20
  # parser failed to find ']', so it's not a valid link
24
21
  return false if (labelEnd < 0)
@@ -34,7 +31,7 @@ module MarkdownIt
34
31
  pos += 1
35
32
  while pos < max
36
33
  code = state.src.charCodeAt(pos)
37
- break if (code != 0x20 && code != 0x0A)
34
+ break if (!isSpace(code) && code != 0x0A)
38
35
  pos += 1
39
36
  end
40
37
  return false if (pos >= max)
@@ -42,7 +39,7 @@ module MarkdownIt
42
39
  # [link]( <href> "title" )
43
40
  # ^^^^^^ parsing link destination
44
41
  start = pos
45
- res = parseLinkDestination(state.src, pos, state.posMax)
42
+ res = state.md.helpers.parseLinkDestination(state.src, pos, state.posMax)
46
43
  if (res[:ok])
47
44
  href = state.md.normalizeLink.call(res[:str])
48
45
  if (state.md.validateLink.call(href))
@@ -57,13 +54,13 @@ module MarkdownIt
57
54
  start = pos
58
55
  while pos < max
59
56
  code = state.src.charCodeAt(pos)
60
- break if (code != 0x20 && code != 0x0A)
57
+ break if (!isSpace(code) && code != 0x0A)
61
58
  pos += 1
62
59
  end
63
60
 
64
61
  # [link]( <href> "title" )
65
62
  # ^^^^^^^ parsing link title
66
- res = parseLinkTitle(state.src, pos, state.posMax)
63
+ res = state.md.helpers.parseLinkTitle(state.src, pos, state.posMax)
67
64
  if (pos < max && start != pos && res[:ok])
68
65
  title = res[:str]
69
66
  pos = res[:pos]
@@ -72,7 +69,7 @@ module MarkdownIt
72
69
  # ^^ skipping these spaces
73
70
  while pos < max
74
71
  code = state.src.charCodeAt(pos);
75
- break if (code != 0x20 && code != 0x0A)
72
+ break if (!isSpace(code) && code != 0x0A)
76
73
  pos += 1
77
74
  end
78
75
  else
@@ -90,17 +87,9 @@ module MarkdownIt
90
87
  #
91
88
  return false if state.env[:references].nil?
92
89
 
93
- # [foo] [bar]
94
- # ^^ optional whitespace (can include newlines)
95
- while pos < max
96
- code = state.src.charCodeAt(pos)
97
- break if (code != 0x20 && code != 0x0A)
98
- pos += 1
99
- end
100
-
101
90
  if (pos < max && state.src.charCodeAt(pos) == 0x5B) # [
102
91
  start = pos + 1
103
- pos = parseLinkLabel(state, pos)
92
+ pos = state.md.helpers.parseLinkLabel(state, pos)
104
93
  if (pos >= 0)
105
94
  label = state.src.slice(start...pos)
106
95
  pos += 1
@@ -129,20 +118,20 @@ module MarkdownIt
129
118
  # so all that's left to do is to call tokenizer.
130
119
  #
131
120
  if (!silent)
132
- state.pos = labelStart
133
- state.posMax = labelEnd
121
+ content = state.src.slice(labelStart...labelEnd)
134
122
 
135
- newState = RulesInline::StateInline.new(
136
- state.src.slice(labelStart...labelEnd),
123
+ state.md.inline.parse(
124
+ content,
137
125
  state.md,
138
126
  state.env,
139
127
  tokens = []
140
128
  )
141
- newState.md.inline.tokenize(newState)
142
129
 
143
130
  token = state.push('image', 'img', 0)
144
131
  token.attrs = attrs = [ [ 'src', href ], [ 'alt', '' ] ]
145
132
  token.children = tokens
133
+ token.content = content;
134
+
146
135
  unless (title.nil? || title.empty?)
147
136
  attrs.push([ 'title', title ])
148
137
  end
@@ -3,22 +3,20 @@
3
3
  module MarkdownIt
4
4
  module RulesInline
5
5
  class Link
6
- extend Helpers::ParseLinkDestination
7
- extend Helpers::ParseLinkLabel
8
- extend Helpers::ParseLinkTitle
9
6
  extend Common::Utils
10
7
 
11
8
  #------------------------------------------------------------------------------
12
9
  def self.link(state, silent)
13
- href = ''
14
- oldPos = state.pos
15
- max = state.posMax
16
- start = state.pos
10
+ href = ''
11
+ oldPos = state.pos
12
+ max = state.posMax
13
+ start = state.pos
14
+ parseReference = true
17
15
 
18
16
  return false if (state.src.charCodeAt(state.pos) != 0x5B) # [
19
17
 
20
18
  labelStart = state.pos + 1
21
- labelEnd = parseLinkLabel(state, state.pos, true)
19
+ labelEnd = state.md.helpers.parseLinkLabel(state, state.pos, true)
22
20
 
23
21
  # parser failed to find ']', so it's not a valid link
24
22
  return false if (labelEnd < 0)
@@ -29,12 +27,15 @@ module MarkdownIt
29
27
  # Inline link
30
28
  #
31
29
 
30
+ # might have found a valid shortcut link, disable reference parsing
31
+ parseReference = false
32
+
32
33
  # [link]( <href> "title" )
33
34
  # ^^ skipping these spaces
34
35
  pos += 1
35
36
  while pos < max
36
37
  code = state.src.charCodeAt(pos)
37
- break if (code != 0x20 && code != 0x0A)
38
+ break if (!isSpace(code) && code != 0x0A)
38
39
  pos += 1
39
40
  end
40
41
  return false if (pos >= max)
@@ -42,7 +43,7 @@ module MarkdownIt
42
43
  # [link]( <href> "title" )
43
44
  # ^^^^^^ parsing link destination
44
45
  start = pos
45
- res = parseLinkDestination(state.src, pos, state.posMax)
46
+ res = state.md.helpers.parseLinkDestination(state.src, pos, state.posMax)
46
47
  if (res[:ok])
47
48
  href = state.md.normalizeLink.call(res[:str])
48
49
  if (state.md.validateLink.call(href))
@@ -57,13 +58,13 @@ module MarkdownIt
57
58
  start = pos
58
59
  while pos < max
59
60
  code = state.src.charCodeAt(pos)
60
- break if (code != 0x20 && code != 0x0A)
61
+ break if (!isSpace(code) && code != 0x0A)
61
62
  pos += 1
62
63
  end
63
64
 
64
65
  # [link]( <href> "title" )
65
66
  # ^^^^^^^ parsing link title
66
- res = parseLinkTitle(state.src, pos, state.posMax)
67
+ res = state.md.helpers.parseLinkTitle(state.src, pos, state.posMax)
67
68
  if (pos < max && start != pos && res[:ok])
68
69
  title = res[:str]
69
70
  pos = res[:pos]
@@ -72,7 +73,7 @@ module MarkdownIt
72
73
  # ^^ skipping these spaces
73
74
  while pos < max
74
75
  code = state.src.charCodeAt(pos)
75
- break if (code != 0x20 && code != 0x0A)
76
+ break if (!isSpace(code) && code != 0x0A)
76
77
  pos += 1
77
78
  end
78
79
  else
@@ -80,27 +81,21 @@ module MarkdownIt
80
81
  end
81
82
 
82
83
  if (pos >= max || state.src.charCodeAt(pos) != 0x29) # )
83
- state.pos = oldPos
84
- return false
84
+ # parsing a valid shortcut link failed, fallback to reference
85
+ parseReference = true
85
86
  end
86
87
  pos += 1
87
- else
88
+ end
89
+
90
+ if parseReference
88
91
  #
89
92
  # Link reference
90
93
  #
91
94
  return false if state.env[:references].nil?
92
95
 
93
- # [foo] [bar]
94
- # ^^ optional whitespace (can include newlines)
95
- while pos < max
96
- code = state.src.charCodeAt(pos);
97
- break if (code != 0x20 && code != 0x0A)
98
- pos += 1
99
- end
100
-
101
96
  if (pos < max && state.src.charCodeAt(pos) == 0x5B) # [
102
97
  start = pos + 1
103
- pos = parseLinkLabel(state, pos)
98
+ pos = state.md.helpers.parseLinkLabel(state, pos)
104
99
  if (pos >= 0)
105
100
  label = state.src.slice(start...pos)
106
101
  pos += 1
@@ -3,6 +3,7 @@
3
3
  module MarkdownIt
4
4
  module RulesInline
5
5
  class Newline
6
+ extend Common::Utils
6
7
 
7
8
  #------------------------------------------------------------------------------
8
9
  def self.newline(state, silent)
@@ -34,7 +35,7 @@ module MarkdownIt
34
35
  pos += 1
35
36
 
36
37
  # skip heading spaces for next line
37
- while pos < max && state.src.charCodeAt(pos) == 0x20
38
+ while pos < max && isSpace(state.src.charCodeAt(pos))
38
39
  pos += 1
39
40
  end
40
41
 
@@ -3,9 +3,10 @@
3
3
  module MarkdownIt
4
4
  module RulesInline
5
5
  class StateInline
6
+ include MarkdownIt::Common::Utils
6
7
 
7
8
  attr_accessor :src, :env, :md, :tokens, :pos, :posMax, :level
8
- attr_accessor :pending, :pendingLevel, :cache
9
+ attr_accessor :pending, :pendingLevel, :cache, :delimiters
9
10
 
10
11
  #------------------------------------------------------------------------------
11
12
  def initialize(src, md, env, outTokens)
@@ -22,6 +23,7 @@ module MarkdownIt
22
23
 
23
24
  @cache = {} # Stores { start: end } pairs. Useful for backtrack
24
25
  # optimization of pairs parse (emphasis, strikes).
26
+ @delimiters = []
25
27
  end
26
28
 
27
29
 
@@ -52,6 +54,63 @@ module MarkdownIt
52
54
  return token
53
55
  end
54
56
 
57
+ # Scan a sequence of emphasis-like markers, and determine whether
58
+ # it can start an emphasis sequence or end an emphasis sequence.
59
+ #
60
+ # - start - position to scan from (it should point at a valid marker);
61
+ # - canSplitWord - determine if these markers can be found inside a word
62
+ #------------------------------------------------------------------------------
63
+ def scanDelims(start, canSplitWord)
64
+ pos = start
65
+ left_flanking = true
66
+ right_flanking = true
67
+ max = @posMax
68
+ marker = @src.charCodeAt(start)
69
+
70
+ # treat beginning of the line as a whitespace
71
+ lastChar = start > 0 ? @src.charCodeAt(start - 1) : 0x20
72
+
73
+ while (pos < max && @src.charCodeAt(pos) == marker)
74
+ pos += 1
75
+ end
76
+
77
+ count = pos - start
78
+
79
+ # treat end of the line as a whitespace
80
+ nextChar = pos < max ? @src.charCodeAt(pos) : 0x20
81
+
82
+ isLastPunctChar = isMdAsciiPunct(lastChar) || isPunctChar(fromCodePoint(lastChar))
83
+ isNextPunctChar = isMdAsciiPunct(nextChar) || isPunctChar(fromCodePoint(nextChar))
84
+
85
+ isLastWhiteSpace = isWhiteSpace(lastChar)
86
+ isNextWhiteSpace = isWhiteSpace(nextChar)
87
+
88
+ if (isNextWhiteSpace)
89
+ left_flanking = false
90
+ elsif (isNextPunctChar)
91
+ if (!(isLastWhiteSpace || isLastPunctChar))
92
+ left_flanking = false
93
+ end
94
+ end
95
+
96
+ if isLastWhiteSpace
97
+ right_flanking = false
98
+ elsif isLastPunctChar
99
+ if !(isNextWhiteSpace || isNextPunctChar)
100
+ right_flanking = false
101
+ end
102
+ end
103
+
104
+ if !canSplitWord
105
+ can_open = left_flanking && (!right_flanking || isLastPunctChar)
106
+ can_close = right_flanking && (!left_flanking || isNextPunctChar)
107
+ else
108
+ can_open = left_flanking
109
+ can_close = right_flanking
110
+ end
111
+
112
+ return { can_open: can_open, can_close: can_close, length: count }
113
+ end
55
114
  end
56
115
  end
57
116
  end
@@ -4,127 +4,111 @@ module MarkdownIt
4
4
  module RulesInline
5
5
  class Strikethrough
6
6
  extend Common::Utils
7
-
8
- # parse sequence of markers,
9
- # "start" should point at a valid marker
10
- def self.scanDelims(state, start)
11
- pos = start
12
- can_open = true
13
- can_close = true
14
- max = state.posMax
15
- marker = state.src.charCodeAt(start)
16
-
17
- # treat beginning of the line as a whitespace
18
- lastChar = start > 0 ? state.src.charCodeAt(start - 1) : 0x20
19
-
20
- while (pos < max && state.src.charCodeAt(pos) == marker)
21
- pos += 1
22
- end
23
7
 
24
- if (pos >= max)
25
- can_open = false
26
- end
8
+ # Insert each marker as a separate text token, and add it to delimiter list
9
+ #------------------------------------------------------------------------------
10
+ def self.tokenize(state, silent)
11
+ start = state.pos
12
+ marker = state.src.charCodeAt(start)
27
13
 
28
- count = pos - start
14
+ return false if silent
29
15
 
30
- # treat end of the line as a whitespace
31
- nextChar = pos < max ? state.src.charCodeAt(pos) : 0x20
16
+ return false if marker != 0x7E # ~
32
17
 
33
- isLastPunctChar = isMdAsciiPunct(lastChar) || isPunctChar(lastChar.chr(Encoding::UTF_8))
34
- isNextPunctChar = isMdAsciiPunct(nextChar) || isPunctChar(nextChar.chr(Encoding::UTF_8))
18
+ scanned = state.scanDelims(state.pos, true)
19
+ len = scanned[:length]
20
+ ch = fromCodePoint(marker)
35
21
 
36
- isLastWhiteSpace = isWhiteSpace(lastChar)
37
- isNextWhiteSpace = isWhiteSpace(nextChar)
22
+ return false if len < 2
38
23
 
39
- if (isNextWhiteSpace)
40
- can_open = false
41
- elsif (isNextPunctChar)
42
- if (!(isLastWhiteSpace || isLastPunctChar))
43
- can_open = false
44
- end
24
+ if len % 2 > 0
25
+ token = state.push('text', '', 0)
26
+ token.content = ch
27
+ len -= 1
45
28
  end
46
29
 
47
- if (isLastWhiteSpace)
48
- can_close = false
49
- elsif (isLastPunctChar)
50
- if (!(isNextWhiteSpace || isNextPunctChar))
51
- can_close = false
52
- end
30
+ i = 0
31
+ while i < len
32
+ token = state.push('text', '', 0)
33
+ token.content = ch + ch
34
+
35
+ state.delimiters.push({
36
+ marker: marker,
37
+ length: scanned[:length],
38
+ jump: i,
39
+ token: state.tokens.length - 1,
40
+ level: state.level,
41
+ end: -1,
42
+ open: scanned[:can_open],
43
+ close: scanned[:can_close]
44
+ })
45
+ i += 2
53
46
  end
54
47
 
55
- return { can_open: can_open, can_close: can_close, delims: count }
48
+ state.pos += scanned[:length]
49
+
50
+ return true
56
51
  end
57
52
 
53
+ # Walk through delimiter list and replace text tokens with tags
58
54
  #------------------------------------------------------------------------------
59
- def self.strikethrough(state, silent)
60
- max = state.posMax
61
- start = state.pos
62
- marker = state.src.charCodeAt(start)
55
+ def self.postProcess(state)
56
+ loneMarkers = []
57
+ delimiters = state.delimiters
58
+ max = state.delimiters.length
63
59
 
64
- return false if (marker != 0x7E) # ~
65
- return false if (silent) # don't run any pairs in validation mode
60
+ 0.upto(max - 1) do |i|
61
+ startDelim = delimiters[i]
66
62
 
67
- res = scanDelims(state, start)
68
- startCount = res[:delims]
69
- if (!res[:can_open])
70
- state.pos += startCount
71
- # Earlier we checked !silent, but this implementation does not need it
72
- state.pending += state.src.slice(start...state.pos)
73
- return true
74
- end
63
+ next if startDelim[:marker] != 0x7E # ~
75
64
 
76
- stack = (startCount / 2).floor
77
- return false if (stack <= 0)
78
- state.pos = start + startCount
79
-
80
- while (state.pos < max)
81
- if (state.src.charCodeAt(state.pos) == marker)
82
- res = scanDelims(state, state.pos)
83
- count = res[:delims]
84
- tagCount = (count / 2).floor
85
- if (res[:can_close])
86
- if (tagCount >= stack)
87
- state.pos += count - 2
88
- found = true
89
- break
90
- end
91
- stack -= tagCount
92
- state.pos += count
93
- next
94
- end
95
-
96
- stack += tagCount if (res[:can_open])
97
- state.pos += count
98
- next
99
- end
65
+ next if startDelim[:end] == -1
100
66
 
101
- state.md.inline.skipToken(state)
102
- end
67
+ endDelim = delimiters[startDelim[:end]]
103
68
 
104
- if (!found)
105
- # parser failed to find ending tag, so it's not valid emphasis
106
- state.pos = start
107
- return false
108
- end
69
+ token = state.tokens[startDelim[:token]]
70
+ token.type = 's_open'
71
+ token.tag = 's'
72
+ token.nesting = 1
73
+ token.markup = '~~'
74
+ token.content = ''
109
75
 
110
- # found!
111
- state.posMax = state.pos
112
- state.pos = start + 2
76
+ token = state.tokens[endDelim[:token]]
77
+ token.type = 's_close'
78
+ token.tag = 's'
79
+ token.nesting = -1
80
+ token.markup = '~~'
81
+ token.content = ''
113
82
 
114
- # Earlier we checked !silent, but this implementation does not need it
115
- token = state.push('s_open', 's', 1)
116
- token.markup = '~~'
83
+ if (state.tokens[endDelim[:token] - 1].type == 'text' &&
84
+ state.tokens[endDelim[:token] - 1].content == '~')
85
+ loneMarkers.push(endDelim[:token] - 1)
86
+ end
87
+ end
117
88
 
118
- state.md.inline.tokenize(state)
89
+ # If a marker sequence has an odd number of characters, it's splitted
90
+ # like this: `~~~~~` -> `~` + `~~` + `~~`, leaving one marker at the
91
+ # start of the sequence.
92
+ #
93
+ # So, we have to move all those markers after subsequent s_close tags.
94
+ #
95
+ while loneMarkers.length > 0
96
+ i = loneMarkers.pop
97
+ j = i + 1
98
+
99
+ while j < state.tokens.length && state.tokens[j].type == 's_close'
100
+ j += 1
101
+ end
119
102
 
120
- token = state.push('s_close', 's', -1)
121
- token.markup = '~~'
103
+ j -= 1
122
104
 
123
- state.pos = state.posMax + 2
124
- state.posMax = max
125
- return true
105
+ if i != j
106
+ token = state.tokens[j]
107
+ state.tokens[j] = state.tokens[i]
108
+ state.tokens[i] = token
109
+ end
110
+ end
126
111
  end
127
-
128
112
  end
129
113
  end
130
114
  end