motion-markdown-it-plugins 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +18 -0
- data/lib/motion-markdown-it-plugins.rb +24 -0
- data/lib/motion-markdown-it-plugins/abbr/abbr.rb +130 -0
- data/lib/motion-markdown-it-plugins/checkbox_replace/checkbox_replace.rb +103 -0
- data/lib/motion-markdown-it-plugins/container/container.rb +154 -0
- data/lib/motion-markdown-it-plugins/deflist/deflist.rb +185 -0
- data/lib/motion-markdown-it-plugins/ins/ins.rb +136 -0
- data/lib/motion-markdown-it-plugins/mark/mark.rb +136 -0
- data/lib/motion-markdown-it-plugins/sub/sub.rb +70 -0
- data/lib/motion-markdown-it-plugins/sup/sup.rb +71 -0
- data/lib/motion-markdown-it-plugins/version.rb +5 -0
- data/spec/abbr/abbr_spec.rb +8 -0
- data/spec/checkbox_replace/checkbox_replace_spec.rb +64 -0
- data/spec/container/api_spec.rb +76 -0
- data/spec/container/default_spec.rb +8 -0
- data/spec/container/misc_spec.rb +17 -0
- data/spec/deflist/deflist_spec.rb +9 -0
- data/spec/ins/ins_spec.rb +9 -0
- data/spec/mark/mark_spec.rb +9 -0
- data/spec/spec_helper.rb +6 -0
- data/spec/sub/sub_spec.rb +9 -0
- data/spec/sup/sup_spec.rb +9 -0
- data/spec/testgen_helper.rb +80 -0
- metadata +92 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 932bce733bfd0e1070663a7b560a5e255817c85c
|
4
|
+
data.tar.gz: 54572d459acb95df6d00fd0736ee71f5ecc3691d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 1bf53209e1279e1467415f9c9b8b91f59e57ec526edac36cad5b287993a8ea6b7409202490e84017b9487d98915e359a4012f59f232aaf568335546c5f9632b3
|
7
|
+
data.tar.gz: 9b67f602c172395eb74d7d9c2a114a6315df287eb3ae404c821eeb253c11fe0cd828e705bf2f2c17b59827ea47279d277a6882aa8cda0a6753b8d2d9769cbc91
|
data/README.md
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# motion-markdown-it-plugins
|
2
|
+
|
3
|
+
[![Gem Version](https://badge.fury.io/rb/motion-markdown-it-plugins.svg)](http://badge.fury.io/rb/motion-markdown-it-plugins)
|
4
|
+
|
5
|
+
Various useful plugins for use with `motion-markdown-it`. Works with Ruby and RubyMotion.
|
6
|
+
|
7
|
+
## Plugins
|
8
|
+
|
9
|
+
Each one has a README with details
|
10
|
+
|
11
|
+
* [Abbreviations](https://github.com/digitalmoksha/motion-markdown-it-plugins/tree/master/lib/motion-markdown-it-plugins/abbr)
|
12
|
+
* [Checkbox/Tasklists](https://github.com/digitalmoksha/motion-markdown-it-plugins/tree/master/lib/motion-markdown-it-plugins/checkbox_replace)
|
13
|
+
* [Containers](https://github.com/digitalmoksha/motion-markdown-it-plugins/tree/master/lib/motion-markdown-it-plugins/container)
|
14
|
+
* [Definition Lists](https://github.com/digitalmoksha/motion-markdown-it-plugins/tree/master/lib/motion-markdown-it-plugins/deflist)
|
15
|
+
* [Insert](https://github.com/digitalmoksha/motion-markdown-it-plugins/tree/master/lib/motion-markdown-it-plugins/ins)
|
16
|
+
* [Mark](https://github.com/digitalmoksha/motion-markdown-it-plugins/tree/master/lib/motion-markdown-it-plugins/mark)
|
17
|
+
* [Subscript](https://github.com/digitalmoksha/motion-markdown-it-plugins/tree/master/lib/motion-markdown-it-plugins/sub)
|
18
|
+
* [Superscript](https://github.com/digitalmoksha/motion-markdown-it-plugins/tree/master/lib/motion-markdown-it-plugins/sup)
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
if defined?(Motion::Project::Config)
|
4
|
+
|
5
|
+
lib_dir_path = File.dirname(File.expand_path(__FILE__))
|
6
|
+
Motion::Project::App.setup do |app|
|
7
|
+
app.files.unshift(Dir.glob(File.join(lib_dir_path, "motion-markdown-it-plugins/**/*.rb")))
|
8
|
+
end
|
9
|
+
|
10
|
+
require 'motion-markdown-it'
|
11
|
+
|
12
|
+
else
|
13
|
+
|
14
|
+
require 'motion-markdown-it'
|
15
|
+
require 'motion-markdown-it-plugins/checkbox_replace/checkbox_replace'
|
16
|
+
require 'motion-markdown-it-plugins/deflist/deflist'
|
17
|
+
require 'motion-markdown-it-plugins/abbr/abbr'
|
18
|
+
require 'motion-markdown-it-plugins/ins/ins'
|
19
|
+
require 'motion-markdown-it-plugins/mark/mark'
|
20
|
+
require 'motion-markdown-it-plugins/sub/sub'
|
21
|
+
require 'motion-markdown-it-plugins/sup/sup'
|
22
|
+
require 'motion-markdown-it-plugins/container/container'
|
23
|
+
|
24
|
+
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
# Enclose abbreviations in <abbr> tags
|
2
|
+
#
|
3
|
+
# Based on Javascript version: https://github.com/markdown-it/markdown-it-abbr
|
4
|
+
#------------------------------------------------------------------------------
|
5
|
+
include MarkdownIt::Common::Utils
|
6
|
+
|
7
|
+
module MotionMarkdownItPlugins
|
8
|
+
class Abbr
|
9
|
+
PUNCT_CHARS = ' \n()[]\'".,!?-'
|
10
|
+
PUNCT_CAHRS_ESCAPED = PUNCT_CHARS.chars.map {|c| escapeRE(c)}.join
|
11
|
+
|
12
|
+
#------------------------------------------------------------------------------
|
13
|
+
def self.init_plugin(md)
|
14
|
+
md.block.ruler.before('reference', 'abbr_def',
|
15
|
+
lambda { |state, startLine, endLine, silent| Abbr.abbr_def(state, startLine, endLine, silent) },
|
16
|
+
{alt: ['', 'paragraph', 'reference']})
|
17
|
+
md.core.ruler.after('inline', 'abbr_replace', lambda { |state| Abbr.abbr_replace(state) })
|
18
|
+
end
|
19
|
+
|
20
|
+
#------------------------------------------------------------------------------
|
21
|
+
def self.abbr_def(state, startLine, endLine, silent)
|
22
|
+
pos = state.bMarks[startLine] + state.tShift[startLine]
|
23
|
+
max = state.eMarks[startLine]
|
24
|
+
|
25
|
+
return false if (pos + 2 >= max)
|
26
|
+
return false if (state.src.charCodeAt(pos) != 0x2A) # '*'
|
27
|
+
pos += 1
|
28
|
+
return false if (state.src.charCodeAt(pos) != 0x5B) # '['
|
29
|
+
pos += 1
|
30
|
+
|
31
|
+
labelStart = pos
|
32
|
+
|
33
|
+
while pos < max
|
34
|
+
ch = state.src.charCodeAt(pos)
|
35
|
+
if (ch == 0x5B) # '['
|
36
|
+
return false
|
37
|
+
elsif (ch == 0x5D) # ']'
|
38
|
+
labelEnd = pos
|
39
|
+
break
|
40
|
+
elsif (ch == 0x5C) # '\'
|
41
|
+
pos += 1
|
42
|
+
end
|
43
|
+
pos += 1
|
44
|
+
end
|
45
|
+
|
46
|
+
return false if (labelEnd < 0 || state.src.charCodeAt(labelEnd + 1) != 0x3A) # ':'
|
47
|
+
return true if (silent)
|
48
|
+
|
49
|
+
label = state.src.slice(labelStart...labelEnd).gsub(/\\(.)/, '\1')
|
50
|
+
title = state.src.slice((labelEnd + 2)...max).strip
|
51
|
+
return false if (title.length == 0)
|
52
|
+
|
53
|
+
state.env[:abbreviations] = {} if (!state.env[:abbreviations])
|
54
|
+
state.env[:abbreviations][label] = title if state.env[:abbreviations][label].nil?
|
55
|
+
|
56
|
+
state.line = startLine + 1
|
57
|
+
return true
|
58
|
+
end
|
59
|
+
|
60
|
+
#------------------------------------------------------------------------------
|
61
|
+
def self.abbr_replace(state)
|
62
|
+
blockTokens = state.tokens
|
63
|
+
|
64
|
+
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
|
+
|
70
|
+
state.env[:abbrRegExp] = Regexp.new(regText)
|
71
|
+
end
|
72
|
+
reg = state.env[:abbrRegExp]
|
73
|
+
|
74
|
+
j = 0
|
75
|
+
l = blockTokens.length
|
76
|
+
while j < l
|
77
|
+
j += 1 and next if (blockTokens[j].type != 'inline')
|
78
|
+
tokens = blockTokens[j].children
|
79
|
+
|
80
|
+
# We scan from the end, to keep position when new tags added.
|
81
|
+
i = tokens.length - 1
|
82
|
+
while i >= 0
|
83
|
+
currentToken = tokens[i]
|
84
|
+
i -= 1 and next if (currentToken.type != 'text')
|
85
|
+
|
86
|
+
pos = 0
|
87
|
+
text = currentToken.content
|
88
|
+
lastIndex = 0
|
89
|
+
nodes = []
|
90
|
+
|
91
|
+
while ((m = reg.match(text, lastIndex)))
|
92
|
+
lastIndex = m.end(0)
|
93
|
+
if (lastIndex > pos)
|
94
|
+
token = MarkdownIt::Token.new('text', '', 0)
|
95
|
+
token.content = text.slice(pos...(m.begin(0) + m[1].length))
|
96
|
+
nodes.push(token)
|
97
|
+
end
|
98
|
+
|
99
|
+
token = MarkdownIt::Token.new('abbr_open', 'abbr', 1)
|
100
|
+
token.attrs = [ [ 'title', state.env[:abbreviations][m[2]] ] ]
|
101
|
+
nodes.push(token)
|
102
|
+
|
103
|
+
token = MarkdownIt::Token.new('text', '', 0)
|
104
|
+
token.content = m[2]
|
105
|
+
nodes.push(token)
|
106
|
+
|
107
|
+
token = MarkdownIt::Token.new('abbr_close', 'abbr', -1)
|
108
|
+
nodes.push(token)
|
109
|
+
|
110
|
+
pos = lastIndex - m[3].length
|
111
|
+
end
|
112
|
+
|
113
|
+
i -= 1 and next if nodes.empty?
|
114
|
+
|
115
|
+
if (pos < text.length)
|
116
|
+
token = MarkdownIt::Token.new('text', '', 0)
|
117
|
+
token.content = text.slice(pos..-1)
|
118
|
+
nodes.push(token)
|
119
|
+
end
|
120
|
+
|
121
|
+
# replace current node
|
122
|
+
blockTokens[j].children = tokens = arrayReplaceAt(tokens, i, nodes)
|
123
|
+
i -= 1
|
124
|
+
end
|
125
|
+
j += 1
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
end
|
130
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
# Based on Javascript version: https://github.com/mcecot/markdown-it-checkbox
|
2
|
+
#------------------------------------------------------------------------------
|
3
|
+
include MarkdownIt::Common::Utils
|
4
|
+
|
5
|
+
module MotionMarkdownItPlugins
|
6
|
+
class CheckboxReplace
|
7
|
+
PATTERN = /\[(X|\s|\_|\-)\]\s(.*)/i
|
8
|
+
|
9
|
+
#------------------------------------------------------------------------------
|
10
|
+
def self.init_plugin(md, options)
|
11
|
+
check = CheckboxReplace.new(md, options)
|
12
|
+
md.core.ruler.push('checkbox', lambda { |state| check.checkbox(state) } )
|
13
|
+
end
|
14
|
+
|
15
|
+
#------------------------------------------------------------------------------
|
16
|
+
def initialize(md, options)
|
17
|
+
@lastId = 0
|
18
|
+
defaults = {divWrap: false, divClass: 'checkbox', idPrefix: 'checkbox'}
|
19
|
+
@options = defaults.merge(options)
|
20
|
+
end
|
21
|
+
|
22
|
+
#------------------------------------------------------------------------------
|
23
|
+
def createTokens(checked, label)
|
24
|
+
nodes = []
|
25
|
+
|
26
|
+
# <div class="checkbox">
|
27
|
+
if @options[:divWrap]
|
28
|
+
token = MarkdownIt::Token.new("checkbox_open", "div", 1)
|
29
|
+
token.attrs = [["class", @options[:divClass]]]
|
30
|
+
nodes.push token
|
31
|
+
end
|
32
|
+
|
33
|
+
# <input type="checkbox" id="checkbox{n}" checked="true">
|
34
|
+
id = "#{@options[:idPrefix]}#{@lastId}"
|
35
|
+
@lastId += 1
|
36
|
+
token = MarkdownIt::Token.new("checkbox_input", "input", 0)
|
37
|
+
token.attrs = [["type","checkbox"],["id",id]]
|
38
|
+
if (checked == true)
|
39
|
+
token.attrs.push ["checked","true"]
|
40
|
+
end
|
41
|
+
nodes.push token
|
42
|
+
|
43
|
+
# <label for="checkbox{n}">
|
44
|
+
token = MarkdownIt::Token.new("label_open", "label", 1)
|
45
|
+
token.attrs = [["for",id]]
|
46
|
+
nodes.push token
|
47
|
+
|
48
|
+
# content of label tag
|
49
|
+
token = MarkdownIt::Token.new("text", "", 0)
|
50
|
+
token.content = label
|
51
|
+
nodes.push token
|
52
|
+
|
53
|
+
# closing tags
|
54
|
+
nodes.push MarkdownIt::Token.new("label_close", "label", -1)
|
55
|
+
if @options[:divWrap]
|
56
|
+
nodes.push MarkdownIt::Token.new("checkbox_close", "div", -1)
|
57
|
+
end
|
58
|
+
|
59
|
+
return nodes
|
60
|
+
end
|
61
|
+
|
62
|
+
#------------------------------------------------------------------------------
|
63
|
+
def splitTextToken(original)
|
64
|
+
|
65
|
+
text = original.content
|
66
|
+
matches = text.match(PATTERN)
|
67
|
+
|
68
|
+
return original if matches == nil
|
69
|
+
|
70
|
+
value = matches[1]
|
71
|
+
label = matches[2]
|
72
|
+
checked = false
|
73
|
+
checked = true if (value == "X" || value == "x")
|
74
|
+
|
75
|
+
return createTokens(checked, label)
|
76
|
+
end
|
77
|
+
|
78
|
+
#------------------------------------------------------------------------------
|
79
|
+
def checkbox(state)
|
80
|
+
blockTokens = state.tokens
|
81
|
+
j = 0
|
82
|
+
l = blockTokens.length
|
83
|
+
while j < l
|
84
|
+
if blockTokens[j].type != "inline"
|
85
|
+
j += 1
|
86
|
+
next
|
87
|
+
end
|
88
|
+
tokens = blockTokens[j].children
|
89
|
+
|
90
|
+
# We scan from the end, to keep position when new tags added.
|
91
|
+
# Use reversed logic in links start/end match
|
92
|
+
i = tokens.length - 1
|
93
|
+
while i >= 0
|
94
|
+
token = tokens[i]
|
95
|
+
blockTokens[j].children = tokens = arrayReplaceAt(tokens, i, splitTextToken(token))
|
96
|
+
i -= 1
|
97
|
+
end
|
98
|
+
j += 1
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
@@ -0,0 +1,154 @@
|
|
1
|
+
# Process block-level custom containers
|
2
|
+
#
|
3
|
+
# Based on Javascript version: https://github.com/markdown-it/markdown-it-container
|
4
|
+
#------------------------------------------------------------------------------
|
5
|
+
|
6
|
+
module MotionMarkdownItPlugins
|
7
|
+
class Container
|
8
|
+
extend MarkdownIt::Common::Utils
|
9
|
+
|
10
|
+
attr_accessor :render
|
11
|
+
|
12
|
+
#------------------------------------------------------------------------------
|
13
|
+
def self.init_plugin(md, name, options = {})
|
14
|
+
container_obj = Container.new(md, name, options)
|
15
|
+
md.block.ruler.before('fence', "container_#{name}",
|
16
|
+
lambda { |state, startLine, endLine, silent| container_obj.container(state, startLine, endLine, silent) },
|
17
|
+
{alt: [ '', 'paragraph', 'reference', 'blockquote', 'list' ]})
|
18
|
+
|
19
|
+
md.renderer.rules["container_#{name}_open"] = container_obj.render
|
20
|
+
md.renderer.rules["container_#{name}_close"] = container_obj.render
|
21
|
+
end
|
22
|
+
|
23
|
+
#------------------------------------------------------------------------------
|
24
|
+
def initialize(md, name, options)
|
25
|
+
@options = options || {}
|
26
|
+
@name = name
|
27
|
+
@min_markers = 3
|
28
|
+
@marker_str = @options[:marker] || ':'
|
29
|
+
@marker_char = @marker_str.charCodeAt(0)
|
30
|
+
@marker_len = @marker_str.length
|
31
|
+
@validate = @options[:validate] || lambda {|params| validateDefault(params) }
|
32
|
+
@render = @options[:render] || lambda {|tokens, idx, _options, env, renderer| renderDefault(tokens, idx, _options, env, renderer) }
|
33
|
+
end
|
34
|
+
|
35
|
+
#------------------------------------------------------------------------------
|
36
|
+
def validateDefault(params)
|
37
|
+
return params.strip.split(' ', 2)[0] == @name
|
38
|
+
end
|
39
|
+
|
40
|
+
#------------------------------------------------------------------------------
|
41
|
+
def renderDefault(tokens, idx, _options, env, renderer)
|
42
|
+
|
43
|
+
# add a class to the opening tag
|
44
|
+
if (tokens[idx].nesting == 1)
|
45
|
+
tokens[idx].attrPush([ 'class', @name ])
|
46
|
+
end
|
47
|
+
|
48
|
+
return renderer.renderToken(tokens, idx, _options, env, renderer)
|
49
|
+
end
|
50
|
+
|
51
|
+
#------------------------------------------------------------------------------
|
52
|
+
def container(state, startLine, endLine, silent)
|
53
|
+
auto_closed = false
|
54
|
+
start = state.bMarks[startLine] + state.tShift[startLine]
|
55
|
+
max = state.eMarks[startLine]
|
56
|
+
|
57
|
+
# Check out the first character quickly,
|
58
|
+
# this should filter out most of non-containers
|
59
|
+
return false if (@marker_char != state.src.charCodeAt(start))
|
60
|
+
|
61
|
+
# Check out the rest of the marker string
|
62
|
+
pos = start + 1
|
63
|
+
while pos <= max
|
64
|
+
break if (@marker_str[(pos - start) % @marker_len] != state.src[pos])
|
65
|
+
pos += 1
|
66
|
+
end
|
67
|
+
|
68
|
+
marker_count = ((pos - start) / @marker_len).floor
|
69
|
+
return false if (marker_count < @min_markers)
|
70
|
+
pos -= (pos - start) % @marker_len
|
71
|
+
|
72
|
+
markup = state.src.slice(start...pos)
|
73
|
+
params = state.src.slice(pos...max)
|
74
|
+
return false if (!@validate.call(params))
|
75
|
+
|
76
|
+
# Since start is found, we can report success here in validation mode
|
77
|
+
return true if (silent)
|
78
|
+
|
79
|
+
# Search for the end of the block
|
80
|
+
nextLine = startLine
|
81
|
+
|
82
|
+
while true
|
83
|
+
nextLine += 1
|
84
|
+
if (nextLine >= endLine)
|
85
|
+
# unclosed block should be autoclosed by end of document.
|
86
|
+
# also block seems to be autoclosed by end of parent
|
87
|
+
break
|
88
|
+
end
|
89
|
+
|
90
|
+
start = state.bMarks[nextLine] + state.tShift[nextLine]
|
91
|
+
max = state.eMarks[nextLine]
|
92
|
+
|
93
|
+
if (start < max && state.tShift[nextLine] < state.blkIndent)
|
94
|
+
# non-empty line with negative indent should stop the list:
|
95
|
+
# - ```
|
96
|
+
# test
|
97
|
+
break
|
98
|
+
end
|
99
|
+
|
100
|
+
next if (@marker_char != state.src.charCodeAt(start))
|
101
|
+
|
102
|
+
if (state.tShift[nextLine] - state.blkIndent >= 4)
|
103
|
+
# closing fence should be indented less than 4 spaces
|
104
|
+
next
|
105
|
+
end
|
106
|
+
|
107
|
+
pos = start + 1
|
108
|
+
while pos <= max
|
109
|
+
break if (@marker_str[(pos - start) % @marker_len] != state.src[pos])
|
110
|
+
pos += 1
|
111
|
+
end
|
112
|
+
|
113
|
+
# closing code fence must be at least as long as the opening one
|
114
|
+
next if (((pos - start).floor / @marker_len) < marker_count)
|
115
|
+
|
116
|
+
# make sure tail has spaces only
|
117
|
+
pos -= (pos - start) % @marker_len
|
118
|
+
pos = state.skipSpaces(pos)
|
119
|
+
|
120
|
+
next if (pos < max)
|
121
|
+
|
122
|
+
# found!
|
123
|
+
auto_closed = true
|
124
|
+
break
|
125
|
+
end
|
126
|
+
|
127
|
+
old_parent = state.parentType
|
128
|
+
old_line_max = state.lineMax
|
129
|
+
state.parentType = 'container'
|
130
|
+
|
131
|
+
# this will prevent lazy continuations from ever going past our end marker
|
132
|
+
state.lineMax = nextLine
|
133
|
+
|
134
|
+
token = state.push("container_#{@name}_open", 'div', 1)
|
135
|
+
token.markup = markup
|
136
|
+
token.block = true
|
137
|
+
token.info = params
|
138
|
+
token.map = [ startLine, nextLine ]
|
139
|
+
|
140
|
+
state.md.block.tokenize(state, startLine + 1, nextLine)
|
141
|
+
|
142
|
+
token = state.push("container_#{@name}_close", 'div', -1)
|
143
|
+
token.markup = state.src.slice(start...pos)
|
144
|
+
token.block = true
|
145
|
+
|
146
|
+
state.parentType = old_parent
|
147
|
+
state.lineMax = old_line_max
|
148
|
+
state.line = nextLine + (auto_closed ? 1 : 0)
|
149
|
+
|
150
|
+
return true
|
151
|
+
end
|
152
|
+
|
153
|
+
end
|
154
|
+
end
|
@@ -0,0 +1,185 @@
|
|
1
|
+
# Process definition lists
|
2
|
+
#
|
3
|
+
# Based on Javascript version: https://github.com/markdown-it/markdown-it-deflist
|
4
|
+
#------------------------------------------------------------------------------
|
5
|
+
include MarkdownIt::Common::Utils
|
6
|
+
|
7
|
+
module MotionMarkdownItPlugins
|
8
|
+
class Deflist
|
9
|
+
|
10
|
+
#------------------------------------------------------------------------------
|
11
|
+
def self.init_plugin(md)
|
12
|
+
md.block.ruler.before('paragraph', 'deflist',
|
13
|
+
lambda { |state, startLine, endLine, silent| Deflist.deflist(state, startLine, endLine, silent) },
|
14
|
+
{alt: ['', 'paragraph', 'reference']})
|
15
|
+
end
|
16
|
+
|
17
|
+
# Search `[:~][\n ]`, returns next pos after marker on success
|
18
|
+
# or -1 on fail.
|
19
|
+
#------------------------------------------------------------------------------
|
20
|
+
def self.skipMarker(state, line)
|
21
|
+
start = state.bMarks[line] + state.tShift[line]
|
22
|
+
max = state.eMarks[line]
|
23
|
+
|
24
|
+
return -1 if (start >= max)
|
25
|
+
|
26
|
+
# Check bullet
|
27
|
+
marker = state.src.charCodeAt(start)
|
28
|
+
start += 1
|
29
|
+
return -1 if (marker != 0x7E && marker != 0x3A) # '~' ':'
|
30
|
+
|
31
|
+
pos = state.skipSpaces(start)
|
32
|
+
|
33
|
+
# require space after ":"
|
34
|
+
return -1 if (start == pos)
|
35
|
+
|
36
|
+
# no empty definitions, e.g. " : "
|
37
|
+
return -1 if (pos >= max)
|
38
|
+
|
39
|
+
return pos
|
40
|
+
end
|
41
|
+
|
42
|
+
#------------------------------------------------------------------------------
|
43
|
+
def self.markTightParagraphs(state, idx)
|
44
|
+
level = state.level + 2
|
45
|
+
i = idx + 2
|
46
|
+
l = state.tokens.length - 2
|
47
|
+
while i < l
|
48
|
+
if (state.tokens[i].level == level && state.tokens[i].type == 'paragraph_open')
|
49
|
+
state.tokens[i + 2].hidden = true
|
50
|
+
state.tokens[i].hidden = true
|
51
|
+
i += 2
|
52
|
+
end
|
53
|
+
i += 1
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
#------------------------------------------------------------------------------
|
58
|
+
def self.deflist(state, startLine, endLine, silent)
|
59
|
+
if silent
|
60
|
+
# quirk: validation mode validates a dd block only, not a whole deflist
|
61
|
+
return false if (state.ddIndent < 0)
|
62
|
+
return skipMarker(state, startLine) >= 0
|
63
|
+
end
|
64
|
+
|
65
|
+
nextLine = startLine + 1
|
66
|
+
if (state.isEmpty(nextLine))
|
67
|
+
nextLine += 1
|
68
|
+
return false if (nextLine > endLine)
|
69
|
+
end
|
70
|
+
|
71
|
+
return false if (state.tShift[nextLine] < state.blkIndent)
|
72
|
+
contentStart = skipMarker(state, nextLine)
|
73
|
+
return false if (contentStart < 0)
|
74
|
+
|
75
|
+
# Start list
|
76
|
+
listTokIdx = state.tokens.length
|
77
|
+
token = state.push('dl_open', 'dl', 1)
|
78
|
+
token.map = listLines = [ startLine, 0 ]
|
79
|
+
|
80
|
+
#--- Iterate list items
|
81
|
+
|
82
|
+
dtLine = startLine
|
83
|
+
ddLine = nextLine
|
84
|
+
|
85
|
+
# One definition list can contain multiple DTs,
|
86
|
+
# and one DT can be followed by multiple DDs.
|
87
|
+
#
|
88
|
+
# Thus, there is two loops here, and label is
|
89
|
+
# needed to break out of the second one
|
90
|
+
# OUTER:
|
91
|
+
while true
|
92
|
+
break_outer = false
|
93
|
+
tight = true
|
94
|
+
prevEmptyEnd = false
|
95
|
+
|
96
|
+
token = state.push('dt_open', 'dt', 1)
|
97
|
+
token.map = [ dtLine, dtLine ]
|
98
|
+
|
99
|
+
token = state.push('inline', '', 0)
|
100
|
+
token.map = [ dtLine, dtLine ]
|
101
|
+
token.content = state.getLines(dtLine, dtLine + 1, state.blkIndent, false).strip
|
102
|
+
token.children = []
|
103
|
+
|
104
|
+
token = state.push('dt_close', 'dt', -1)
|
105
|
+
|
106
|
+
while true
|
107
|
+
token = state.push('dd_open', 'dd', 1)
|
108
|
+
token.map = itemLines = [ nextLine, 0 ]
|
109
|
+
|
110
|
+
oldTight = state.tight
|
111
|
+
oldDDIndent = state.ddIndent
|
112
|
+
oldIndent = state.blkIndent
|
113
|
+
oldTShift = state.tShift[ddLine]
|
114
|
+
oldParentType = state.parentType
|
115
|
+
state.blkIndent = state.ddIndent = state.tShift[ddLine] + 2
|
116
|
+
state.tShift[ddLine] = contentStart - state.bMarks[ddLine]
|
117
|
+
state.tight = true
|
118
|
+
state.parentType = 'deflist'
|
119
|
+
|
120
|
+
state.md.block.tokenize(state, ddLine, endLine, true)
|
121
|
+
|
122
|
+
# If any of list item is tight, mark list as tight
|
123
|
+
if (!state.tight || prevEmptyEnd)
|
124
|
+
tight = false
|
125
|
+
end
|
126
|
+
# Item become loose if finish with empty line,
|
127
|
+
# but we should filter last element, because it means list finish
|
128
|
+
prevEmptyEnd = (state.line - ddLine) > 1 && state.isEmpty(state.line - 1)
|
129
|
+
|
130
|
+
state.tShift[ddLine] = oldTShift
|
131
|
+
state.tight = oldTight
|
132
|
+
state.parentType = oldParentType
|
133
|
+
state.blkIndent = oldIndent
|
134
|
+
state.ddIndent = oldDDIndent
|
135
|
+
|
136
|
+
token = state.push('dd_close', 'dd', -1)
|
137
|
+
|
138
|
+
itemLines[1] = nextLine = state.line
|
139
|
+
|
140
|
+
break_outer = true and break if (nextLine >= endLine)
|
141
|
+
break_outer = true and break if (state.tShift[nextLine] < state.blkIndent)
|
142
|
+
contentStart = skipMarker(state, nextLine)
|
143
|
+
break if (contentStart < 0)
|
144
|
+
|
145
|
+
ddLine = nextLine
|
146
|
+
|
147
|
+
# go to the next loop iteration:
|
148
|
+
# insert DD tag and repeat checking
|
149
|
+
end
|
150
|
+
break if break_outer
|
151
|
+
|
152
|
+
break if (nextLine >= endLine)
|
153
|
+
dtLine = nextLine
|
154
|
+
|
155
|
+
break if (state.isEmpty(dtLine))
|
156
|
+
break if (state.tShift[dtLine] < state.blkIndent)
|
157
|
+
|
158
|
+
ddLine = dtLine + 1
|
159
|
+
break if (ddLine >= endLine)
|
160
|
+
ddLine += 1 if (state.isEmpty(ddLine))
|
161
|
+
break if (ddLine >= endLine)
|
162
|
+
|
163
|
+
break if (state.tShift[ddLine] < state.blkIndent)
|
164
|
+
contentStart = skipMarker(state, ddLine)
|
165
|
+
break if (contentStart < 0)
|
166
|
+
|
167
|
+
# go to the next loop iteration:
|
168
|
+
# insert DT and DD tags and repeat checking
|
169
|
+
end
|
170
|
+
|
171
|
+
# Finilize list
|
172
|
+
token = state.push('dl_close', 'dl', -1)
|
173
|
+
listLines[1] = nextLine
|
174
|
+
state.line = nextLine
|
175
|
+
|
176
|
+
# mark paragraphs tight if needed
|
177
|
+
if (tight)
|
178
|
+
markTightParagraphs(state, listTokIdx)
|
179
|
+
end
|
180
|
+
|
181
|
+
return true
|
182
|
+
end
|
183
|
+
|
184
|
+
end
|
185
|
+
end
|