motion-markdown-it 0.4.0.3.0

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 (64) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +243 -0
  3. data/lib/motion-markdown-it.rb +71 -0
  4. data/lib/motion-markdown-it/common/entities.rb +1084 -0
  5. data/lib/motion-markdown-it/common/html_blocks.rb +60 -0
  6. data/lib/motion-markdown-it/common/html_re.rb +28 -0
  7. data/lib/motion-markdown-it/common/string.rb +14 -0
  8. data/lib/motion-markdown-it/common/url_schemas.rb +173 -0
  9. data/lib/motion-markdown-it/common/utils.rb +216 -0
  10. data/lib/motion-markdown-it/helpers/parse_link_destination.rb +75 -0
  11. data/lib/motion-markdown-it/helpers/parse_link_label.rb +51 -0
  12. data/lib/motion-markdown-it/helpers/parse_link_title.rb +48 -0
  13. data/lib/motion-markdown-it/index.rb +507 -0
  14. data/lib/motion-markdown-it/parser_block.rb +113 -0
  15. data/lib/motion-markdown-it/parser_core.rb +46 -0
  16. data/lib/motion-markdown-it/parser_inline.rb +121 -0
  17. data/lib/motion-markdown-it/presets/commonmark.rb +76 -0
  18. data/lib/motion-markdown-it/presets/default.rb +42 -0
  19. data/lib/motion-markdown-it/presets/zero.rb +59 -0
  20. data/lib/motion-markdown-it/renderer.rb +286 -0
  21. data/lib/motion-markdown-it/ruler.rb +327 -0
  22. data/lib/motion-markdown-it/rules_block/blockquote.rb +138 -0
  23. data/lib/motion-markdown-it/rules_block/code.rb +35 -0
  24. data/lib/motion-markdown-it/rules_block/fence.rb +94 -0
  25. data/lib/motion-markdown-it/rules_block/heading.rb +56 -0
  26. data/lib/motion-markdown-it/rules_block/hr.rb +45 -0
  27. data/lib/motion-markdown-it/rules_block/html_block.rb +73 -0
  28. data/lib/motion-markdown-it/rules_block/lheading.rb +54 -0
  29. data/lib/motion-markdown-it/rules_block/list.rb +242 -0
  30. data/lib/motion-markdown-it/rules_block/paragraph.rb +51 -0
  31. data/lib/motion-markdown-it/rules_block/reference.rb +161 -0
  32. data/lib/motion-markdown-it/rules_block/state_block.rb +184 -0
  33. data/lib/motion-markdown-it/rules_block/table.rb +161 -0
  34. data/lib/motion-markdown-it/rules_core/block.rb +20 -0
  35. data/lib/motion-markdown-it/rules_core/inline.rb +20 -0
  36. data/lib/motion-markdown-it/rules_core/linkify.rb +138 -0
  37. data/lib/motion-markdown-it/rules_core/normalize.rb +44 -0
  38. data/lib/motion-markdown-it/rules_core/replacements.rb +90 -0
  39. data/lib/motion-markdown-it/rules_core/smartquotes.rb +158 -0
  40. data/lib/motion-markdown-it/rules_core/state_core.rb +20 -0
  41. data/lib/motion-markdown-it/rules_inline/autolink.rb +74 -0
  42. data/lib/motion-markdown-it/rules_inline/backticks.rb +51 -0
  43. data/lib/motion-markdown-it/rules_inline/emphasis.rb +172 -0
  44. data/lib/motion-markdown-it/rules_inline/entity.rb +51 -0
  45. data/lib/motion-markdown-it/rules_inline/escape.rb +55 -0
  46. data/lib/motion-markdown-it/rules_inline/html_inline.rb +49 -0
  47. data/lib/motion-markdown-it/rules_inline/image.rb +158 -0
  48. data/lib/motion-markdown-it/rules_inline/link.rb +153 -0
  49. data/lib/motion-markdown-it/rules_inline/newline.rb +47 -0
  50. data/lib/motion-markdown-it/rules_inline/state_inline.rb +57 -0
  51. data/lib/motion-markdown-it/rules_inline/strikethrough.rb +130 -0
  52. data/lib/motion-markdown-it/rules_inline/text.rb +94 -0
  53. data/lib/motion-markdown-it/token.rb +134 -0
  54. data/lib/motion-markdown-it/version.rb +5 -0
  55. data/spec/motion-markdown-it/bench_mark_spec.rb +44 -0
  56. data/spec/motion-markdown-it/commonmark_spec.rb +16 -0
  57. data/spec/motion-markdown-it/markdown_it_spec.rb +18 -0
  58. data/spec/motion-markdown-it/misc_spec.rb +277 -0
  59. data/spec/motion-markdown-it/ruler_spec.rb +153 -0
  60. data/spec/motion-markdown-it/testgen_helper.rb +68 -0
  61. data/spec/motion-markdown-it/token_spec.rb +17 -0
  62. data/spec/motion-markdown-it/utils_spec.rb +82 -0
  63. data/spec/spec_helper.rb +6 -0
  64. metadata +158 -0
@@ -0,0 +1,47 @@
1
+ # Proceess '\n'
2
+ #------------------------------------------------------------------------------
3
+ module MarkdownIt
4
+ module RulesInline
5
+ class Newline
6
+
7
+ #------------------------------------------------------------------------------
8
+ def self.newline(state, silent)
9
+ pos = state.pos
10
+ return false if state.src.charCodeAt(pos) != 0x0A # \n
11
+
12
+ pmax = state.pending.length - 1
13
+ max = state.posMax
14
+
15
+ # ' \n' -> hardbreak
16
+ # Lookup in pending chars is bad practice! Don't copy to other rules!
17
+ # Pending string is stored in concat mode, indexed lookups will cause
18
+ # convertion to flat mode.
19
+ if !silent
20
+ if pmax >= 0 && state.pending.charCodeAt(pmax) == 0x20
21
+ if pmax >= 1 && state.pending.charCodeAt(pmax - 1) == 0x20
22
+ state.pending = state.pending.sub(/ +$/, '')
23
+ state.push('hardbreak', 'br', 0)
24
+ else
25
+ state.pending = state.pending.slice(0...-1)
26
+ state.push('softbreak', 'br', 0)
27
+ end
28
+
29
+ else
30
+ state.push('softbreak', 'br', 0)
31
+ end
32
+ end
33
+
34
+ pos += 1
35
+
36
+ # skip heading spaces for next line
37
+ while pos < max && state.src.charCodeAt(pos) == 0x20
38
+ pos += 1
39
+ end
40
+
41
+ state.pos = pos
42
+ return true
43
+ end
44
+
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,57 @@
1
+ # Inline parser state
2
+ #------------------------------------------------------------------------------
3
+ module MarkdownIt
4
+ module RulesInline
5
+ class StateInline
6
+
7
+ attr_accessor :src, :env, :md, :tokens, :pos, :posMax, :level
8
+ attr_accessor :pending, :pendingLevel, :cache
9
+
10
+ #------------------------------------------------------------------------------
11
+ def initialize(src, md, env, outTokens)
12
+ @src = src
13
+ @env = env
14
+ @md = md
15
+ @tokens = outTokens
16
+
17
+ @pos = 0
18
+ @posMax = @src.length
19
+ @level = 0
20
+ @pending = ''
21
+ @pendingLevel = 0
22
+
23
+ @cache = {} # Stores { start: end } pairs. Useful for backtrack
24
+ # optimization of pairs parse (emphasis, strikes).
25
+ end
26
+
27
+
28
+ # Flush pending text
29
+ #------------------------------------------------------------------------------
30
+ def pushPending
31
+ token = Token.new('text', '', 0)
32
+ token.content = @pending
33
+ token.level = @pendingLevel
34
+ @tokens.push(token)
35
+ @pending = ''
36
+ return token
37
+ end
38
+
39
+ # Push new token to "stream".
40
+ # If pending text exists - flush it as text token
41
+ #------------------------------------------------------------------------------
42
+ def push(type, tag, nesting)
43
+ pushPending unless @pending.empty?
44
+
45
+ token = Token.new(type, tag, nesting);
46
+ @level -= 1 if nesting < 0
47
+ token.level = @level
48
+ @level += 1 if nesting > 0
49
+
50
+ @pendingLevel = @level
51
+ @tokens.push(token)
52
+ return token
53
+ end
54
+
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,130 @@
1
+ # ~~strike through~~
2
+ #------------------------------------------------------------------------------
3
+ module MarkdownIt
4
+ module RulesInline
5
+ class Strikethrough
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
+
24
+ if (pos >= max)
25
+ can_open = false
26
+ end
27
+
28
+ count = pos - start
29
+
30
+ # treat end of the line as a whitespace
31
+ nextChar = pos < max ? state.src.charCodeAt(pos) : 0x20
32
+
33
+ isLastPunctChar = isMdAsciiPunct(lastChar) || isPunctChar(lastChar.chr)
34
+ isNextPunctChar = isMdAsciiPunct(nextChar) || isPunctChar(nextChar.chr)
35
+
36
+ isLastWhiteSpace = isWhiteSpace(lastChar)
37
+ isNextWhiteSpace = isWhiteSpace(nextChar)
38
+
39
+ if (isNextWhiteSpace)
40
+ can_open = false
41
+ elsif (isNextPunctChar)
42
+ if (!(isLastWhiteSpace || isLastPunctChar))
43
+ can_open = false
44
+ end
45
+ end
46
+
47
+ if (isLastWhiteSpace)
48
+ can_close = false
49
+ elsif (isLastPunctChar)
50
+ if (!(isNextWhiteSpace || isNextPunctChar))
51
+ can_close = false
52
+ end
53
+ end
54
+
55
+ return { can_open: can_open, can_close: can_close, delims: count }
56
+ end
57
+
58
+ #------------------------------------------------------------------------------
59
+ def self.strikethrough(state, silent)
60
+ max = state.posMax
61
+ start = state.pos
62
+ marker = state.src.charCodeAt(start)
63
+
64
+ return false if (marker != 0x7E) # ~
65
+ return false if (silent) # don't run any pairs in validation mode
66
+
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
75
+
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
100
+
101
+ state.md.inline.skipToken(state)
102
+ end
103
+
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
109
+
110
+ # found!
111
+ state.posMax = state.pos
112
+ state.pos = start + 2
113
+
114
+ # Earlier we checked !silent, but this implementation does not need it
115
+ token = state.push('s_open', 's', 1)
116
+ token.markup = '~~'
117
+
118
+ state.md.inline.tokenize(state)
119
+
120
+ token = state.push('s_close', 's', -1)
121
+ token.markup = '~~'
122
+
123
+ state.pos = state.posMax + 2
124
+ state.posMax = max
125
+ return true
126
+ end
127
+
128
+ end
129
+ end
130
+ end
@@ -0,0 +1,94 @@
1
+ # Skip text characters for text token, place those to pending buffer
2
+ # and increment current pos
3
+ #------------------------------------------------------------------------------
4
+ module MarkdownIt
5
+ module RulesInline
6
+ class Text
7
+
8
+ # Rule to skip pure text
9
+ # '{}$%@~+=:' reserved for extentions
10
+
11
+ # !, ", #, $, %, &, ', (, ), *, +, ,, -, ., /, :, ;, <, =, >, ?, @, [, \, ], ^, _, `, {, |, }, or ~
12
+
13
+ # !!!! Don't confuse with "Markdown ASCII Punctuation" chars
14
+ # http://spec.commonmark.org/0.15/#ascii-punctuation-character
15
+ #------------------------------------------------------------------------------
16
+ def self.isTerminatorChar(ch)
17
+ case ch
18
+ when 0x0A, # \n
19
+ 0x21, # !
20
+ 0x23, # #
21
+ 0x24, # $
22
+ 0x25, # %
23
+ 0x26, # &
24
+ 0x2A, # *
25
+ 0x2B, # +
26
+ 0x2D, # -
27
+ 0x3A, # :
28
+ 0x3C, # <
29
+ 0x3D, # =
30
+ 0x3E, # >
31
+ 0x40, # @
32
+ 0x5B, # [
33
+ 0x5C, # \
34
+ 0x5D, # ]
35
+ 0x5E, # ^
36
+ 0x5F, # _
37
+ 0x60, # `
38
+ 0x7B, # {
39
+ 0x7D, # }
40
+ 0x7E # ~
41
+ return true
42
+ else
43
+ return false
44
+ end
45
+ end
46
+
47
+ #------------------------------------------------------------------------------
48
+ def self.text(state, silent)
49
+ pos = state.pos
50
+
51
+ while pos < state.posMax && !self.isTerminatorChar(state.src.charCodeAt(pos))
52
+ pos += 1
53
+ end
54
+
55
+ return false if pos == state.pos
56
+
57
+ state.pending += state.src.slice(state.pos...pos) if !silent
58
+ state.pos = pos
59
+ return true
60
+ end
61
+
62
+ # // Alternative implementation, for memory.
63
+ # //
64
+ # // It costs 10% of performance, but allows extend terminators list, if place it
65
+ # // to `ParcerInline` property. Probably, will switch to it sometime, such
66
+ # // flexibility required.
67
+ #
68
+ # /*
69
+ # var TERMINATOR_RE = /[\n!#$%&*+\-:<=>@[\\\]^_`{}~]/;
70
+ #
71
+ # module.exports = function text(state, silent) {
72
+ # var pos = state.pos,
73
+ # idx = state.src.slice(pos).search(TERMINATOR_RE);
74
+ #
75
+ # // first char is terminator -> empty text
76
+ # if (idx === 0) { return false; }
77
+ #
78
+ # // no terminator -> text till end of string
79
+ # if (idx < 0) {
80
+ # if (!silent) { state.pending += state.src.slice(pos); }
81
+ # state.pos = state.src.length;
82
+ # return true;
83
+ # }
84
+ #
85
+ # if (!silent) { state.pending += state.src.slice(pos, pos + idx); }
86
+ #
87
+ # state.pos += idx;
88
+ #
89
+ # return true;
90
+ # };*/
91
+
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,134 @@
1
+ # Token class
2
+ #------------------------------------------------------------------------------
3
+ module MarkdownIt
4
+ class Token
5
+
6
+ attr_accessor :type, :tag, :attrs, :map, :nesting, :level, :children
7
+ attr_accessor :content, :markup, :info, :meta, :block, :hidden
8
+
9
+ # new Token(type, tag, nesting)
10
+ #
11
+ # Create new token and fill passed properties.
12
+ #------------------------------------------------------------------------------
13
+ def initialize(type, tag, nesting)
14
+ # * Token#type -> String
15
+ # *
16
+ # * Type of the token (string, e.g. "paragraph_open")
17
+ @type = type
18
+
19
+ # * Token#tag -> String
20
+ # *
21
+ # * html tag name, e.g. "p"
22
+ @tag = tag
23
+
24
+ # * Token#attrs -> Array
25
+ # *
26
+ # * Html attributes. Format: `[ [ name1, value1 ], [ name2, value2 ] ]`
27
+ @attrs = nil
28
+
29
+ # * Token#map -> Array
30
+ # *
31
+ # * Source map info. Format: `[ line_begin, line_end ]`
32
+ @map = nil
33
+
34
+ # * Token#nesting -> Number
35
+ # *
36
+ # * Level change (number in {-1, 0, 1} set), where:
37
+ # *
38
+ # * - `1` means the tag is opening
39
+ # * - `0` means the tag is self-closing
40
+ # * - `-1` means the tag is closing
41
+ @nesting = nesting
42
+
43
+ # * Token#level -> Number
44
+ # *
45
+ # * nesting level, the same as `state.level`
46
+ @level = 0
47
+
48
+ # * Token#children -> Array
49
+ # *
50
+ # * An array of child nodes (inline and img tokens)
51
+ @children = nil
52
+
53
+ # * Token#content -> String
54
+ # *
55
+ # * In a case of self-closing tag (code, html, fence, etc.),
56
+ # * it has contents of this tag.
57
+ @content = ''
58
+
59
+ # * Token#markup -> String
60
+ # *
61
+ # * '*' or '_' for emphasis, fence string for fence, etc.
62
+ @markup = ''
63
+
64
+ # * Token#info -> String
65
+ # *
66
+ # * fence infostring
67
+ @info = ''
68
+
69
+ # * Token#meta -> Object
70
+ # *
71
+ # * A place for plugins to store an arbitrary data
72
+ @meta = nil
73
+
74
+ # * Token#block -> Boolean
75
+ # *
76
+ # * True for block-level tokens, false for inline tokens.
77
+ # * Used in renderer to calculate line breaks
78
+ @block = false
79
+
80
+ # * Token#hidden -> Boolean
81
+ # *
82
+ # * If it's true, ignore this element when rendering. Used for tight lists
83
+ # * to hide paragraphs.
84
+ @hidden = false
85
+ end
86
+
87
+
88
+ # * Token.attrIndex(name) -> Number
89
+ # *
90
+ # * Search attribute index by name.
91
+ #------------------------------------------------------------------------------
92
+ def attrIndex(name)
93
+ return -1 if !@attrs
94
+
95
+ attrs = @attrs
96
+
97
+ attrs.each_with_index do |attr_, index|
98
+ return index if attr_[0] == name
99
+ end
100
+ return -1
101
+ end
102
+
103
+ # * Token.attrPush(attrData)
104
+ # *
105
+ # * Add `[ name, value ]` attribute to list. Init attrs if necessary
106
+ #------------------------------------------------------------------------------
107
+ def attrPush(attrData)
108
+ if @attrs
109
+ @attrs.push(attrData)
110
+ else
111
+ @attrs = [ attrData ]
112
+ end
113
+ end
114
+
115
+ #------------------------------------------------------------------------------
116
+ def to_json
117
+ {
118
+ type: @type,
119
+ tag: @tag,
120
+ attrs: @attrs,
121
+ map: @map,
122
+ nesting: @nesting,
123
+ level: @level,
124
+ children: @children.nil? ? nil : @children.each {|t| t.to_json},
125
+ content: @content,
126
+ markup: @markup,
127
+ info: @info,
128
+ meta: @meta,
129
+ block: @block,
130
+ hidden: @hidden
131
+ }
132
+ end
133
+ end
134
+ end