motion-markdown-it 4.4.0 → 8.4.1

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