motion-markdown-it 0.4.0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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,138 @@
1
+ # Block quotes
2
+ #------------------------------------------------------------------------------
3
+ module MarkdownIt
4
+ module RulesBlock
5
+ class Blockquote
6
+
7
+ #------------------------------------------------------------------------------
8
+ def self.blockquote(state, startLine, endLine, silent)
9
+ pos = state.bMarks[startLine] + state.tShift[startLine]
10
+ max = state.eMarks[startLine]
11
+
12
+ # check the block quote marker
13
+ return false if state.src.charCodeAt(pos) != 0x3E # >
14
+ pos += 1
15
+
16
+ # we know that it's going to be a valid blockquote,
17
+ # so no point trying to find the end of it in silent mode
18
+ return true if silent
19
+
20
+ # skip one optional space after '>'
21
+ pos += 1 if state.src.charCodeAt(pos) == 0x20
22
+
23
+ oldIndent = state.blkIndent
24
+ state.blkIndent = 0
25
+
26
+ oldBMarks = [ state.bMarks[startLine] ]
27
+ state.bMarks[startLine] = pos
28
+
29
+ # check if we have an empty blockquote
30
+ pos = pos < max ? state.skipSpaces(pos) : pos
31
+ lastLineEmpty = pos >= max
32
+
33
+ oldTShift = [ state.tShift[startLine] ]
34
+ state.tShift[startLine] = pos - state.bMarks[startLine]
35
+
36
+ terminatorRules = state.md.block.ruler.getRules('blockquote')
37
+
38
+ # Search the end of the block
39
+ #
40
+ # Block ends with either:
41
+ # 1. an empty line outside:
42
+ # ```
43
+ # > test
44
+ #
45
+ # ```
46
+ # 2. an empty line inside:
47
+ # ```
48
+ # >
49
+ # test
50
+ # ```
51
+ # 3. another tag
52
+ # ```
53
+ # > test
54
+ # - - -
55
+ # ```
56
+ nextLine = startLine + 1
57
+ while nextLine < endLine
58
+ pos = state.bMarks[nextLine] + state.tShift[nextLine]
59
+ max = state.eMarks[nextLine]
60
+
61
+ if pos >= max
62
+ # Case 1: line is not inside the blockquote, and this line is empty.
63
+ break
64
+ end
65
+
66
+ if state.src.charCodeAt(pos) == 0x3E # >
67
+ pos += 1
68
+ # This line is inside the blockquote.
69
+
70
+ # skip one optional space after '>'
71
+ pos += 1 if state.src.charCodeAt(pos) == 0x20
72
+
73
+ oldBMarks.push(state.bMarks[nextLine])
74
+ state.bMarks[nextLine] = pos
75
+
76
+ pos = pos < max ? state.skipSpaces(pos) : pos
77
+ lastLineEmpty = pos >= max
78
+
79
+ oldTShift.push(state.tShift[nextLine])
80
+ state.tShift[nextLine] = pos - state.bMarks[nextLine]
81
+ nextLine += 1
82
+ next
83
+ else
84
+ pos += 1
85
+ end
86
+
87
+ # Case 2: line is not inside the blockquote, and the last line was empty.
88
+ break if lastLineEmpty
89
+
90
+ # Case 3: another tag found.
91
+ terminate = false
92
+ (0...terminatorRules.length).each do |i|
93
+ if terminatorRules[i].call(state, nextLine, endLine, true)
94
+ terminate = true
95
+ break
96
+ end
97
+ end
98
+ break if terminate
99
+
100
+ oldBMarks.push(state.bMarks[nextLine])
101
+ oldTShift.push(state.tShift[nextLine])
102
+
103
+ # A negative number means that this is a paragraph continuation
104
+ #
105
+ # Any negative number will do the job here, but it's better for it
106
+ # to be large enough to make any bugs obvious.
107
+ state.tShift[nextLine] = -1337
108
+ nextLine += 1
109
+ end
110
+
111
+ oldParentType = state.parentType
112
+ state.parentType = 'blockquote'
113
+
114
+ token = state.push('blockquote_open', 'blockquote', 1)
115
+ token.markup = '>'
116
+ token.map = lines = [ startLine, 0 ]
117
+
118
+ state.md.block.tokenize(state, startLine, nextLine)
119
+
120
+ token = state.push('blockquote_close', 'blockquote', -1)
121
+ token.markup = '>'
122
+
123
+ state.parentType = oldParentType
124
+ lines[1] = state.line
125
+
126
+ # Restore original tShift; this might not be necessary since the parser
127
+ # has already been here, but just to make sure we can do that.
128
+ (0...oldTShift.length).each do |i|
129
+ state.bMarks[i + startLine] = oldBMarks[i]
130
+ state.tShift[i + startLine] = oldTShift[i]
131
+ end
132
+ state.blkIndent = oldIndent
133
+ return true
134
+ end
135
+
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,35 @@
1
+ # Code block (4 spaces padded)
2
+ #------------------------------------------------------------------------------
3
+ module MarkdownIt
4
+ module RulesBlock
5
+ class Code
6
+
7
+ #------------------------------------------------------------------------------
8
+ def self.code(state, startLine, endLine, silent = true)
9
+ return false if (state.tShift[startLine] - state.blkIndent < 4)
10
+
11
+ last = nextLine = startLine + 1
12
+ while nextLine < endLine
13
+ if state.isEmpty(nextLine)
14
+ nextLine += 1
15
+ next
16
+ end
17
+ if (state.tShift[nextLine] - state.blkIndent >= 4)
18
+ nextLine += 1
19
+ last = nextLine
20
+ next
21
+ end
22
+ break
23
+ end
24
+
25
+ state.line = nextLine
26
+
27
+ token = state.push('code_block', 'code', 0)
28
+ token.content = state.getLines(startLine, last, 4 + state.blkIndent, true)
29
+ token.map = [ startLine, state.line ]
30
+ return true
31
+ end
32
+
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,94 @@
1
+ # fences (``` lang, ~~~ lang)
2
+ #------------------------------------------------------------------------------
3
+ module MarkdownIt
4
+ module RulesBlock
5
+ class Fence
6
+
7
+ #------------------------------------------------------------------------------
8
+ def self.fence(state, startLine, endLine, silent)
9
+ haveEndMarker = false
10
+ pos = state.bMarks[startLine] + state.tShift[startLine]
11
+ max = state.eMarks[startLine]
12
+
13
+ return false if pos + 3 > max
14
+
15
+ marker = state.src.charCodeAt(pos)
16
+
17
+ if marker != 0x7E && marker != 0x60 # != ~ && != `
18
+ return false
19
+ end
20
+
21
+ # scan marker length
22
+ mem = pos;
23
+ pos = state.skipChars(pos, marker)
24
+ len = pos - mem
25
+
26
+ return false if len < 3
27
+
28
+ markup = state.src.slice(mem...pos)
29
+ params = state.src.slice(pos...max)
30
+
31
+ return false if params.include?('`')
32
+
33
+ # Since start is found, we can report success here in validation mode
34
+ return true if silent
35
+
36
+ # search end of block
37
+ nextLine = startLine
38
+
39
+ while true
40
+ nextLine += 1
41
+ if nextLine >= endLine
42
+ # unclosed block should be autoclosed by end of document.
43
+ # also block seems to be autoclosed by end of parent
44
+ break
45
+ end
46
+
47
+ pos = mem = state.bMarks[nextLine] + state.tShift[nextLine]
48
+ max = state.eMarks[nextLine];
49
+
50
+ if pos < max && state.tShift[nextLine] < state.blkIndent
51
+ # non-empty line with negative indent should stop the list:
52
+ # - ```
53
+ # test
54
+ break
55
+ end
56
+
57
+ next if state.src.charCodeAt(pos) != marker
58
+
59
+ if state.tShift[nextLine] - state.blkIndent >= 4
60
+ # closing fence should be indented less than 4 spaces
61
+ next
62
+ end
63
+
64
+ pos = state.skipChars(pos, marker)
65
+
66
+ # closing code fence must be at least as long as the opening one
67
+ next if pos - mem < len
68
+
69
+ # make sure tail has spaces only
70
+ pos = state.skipSpaces(pos)
71
+
72
+ next if pos < max
73
+
74
+ haveEndMarker = true
75
+ # found!
76
+ break
77
+ end
78
+
79
+ # If a fence has heading spaces, they should be removed from its inner block
80
+ len = state.tShift[startLine]
81
+ state.line = nextLine + (haveEndMarker ? 1 : 0)
82
+
83
+ token = state.push('fence', 'code', 0)
84
+ token.info = params
85
+ token.content = state.getLines(startLine + 1, nextLine, len, true)
86
+ token.markup = markup
87
+ token.map = [ startLine, state.line ]
88
+
89
+ return true
90
+ end
91
+
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,56 @@
1
+ # heading (#, ##, ...)
2
+ #------------------------------------------------------------------------------
3
+ module MarkdownIt
4
+ module RulesBlock
5
+ class Heading
6
+
7
+ #------------------------------------------------------------------------------
8
+ def self.heading(state, startLine, endLine, silent)
9
+ pos = state.bMarks[startLine] + state.tShift[startLine]
10
+ max = state.eMarks[startLine]
11
+ ch = state.src.charCodeAt(pos)
12
+
13
+ return false if (ch != 0x23 || pos >= max)
14
+
15
+ # count heading level
16
+ level = 1
17
+ pos += 1
18
+ ch = state.src.charCodeAt(pos)
19
+ while (ch == 0x23 && pos < max && level <= 6) # '#'
20
+ level += 1
21
+ pos += 1
22
+ ch = state.src.charCodeAt(pos)
23
+ end
24
+
25
+ return false if (level > 6 || (pos < max && ch != 0x20)) # space
26
+
27
+ return true if (silent)
28
+
29
+ # Let's cut tails like ' ### ' from the end of string
30
+
31
+ max = state.skipCharsBack(max, 0x20, pos) # space
32
+ tmp = state.skipCharsBack(max, 0x23, pos) # '#'
33
+ if (tmp > pos && state.src.charCodeAt(tmp - 1) == 0x20) # space
34
+ max = tmp
35
+ end
36
+
37
+ state.line = startLine + 1
38
+
39
+ token = state.push('heading_open', "h#{level.to_s}", 1)
40
+ token.markup = '########'.slice(0...level)
41
+ token.map = [ startLine, state.line ]
42
+
43
+ token = state.push('inline', '', 0)
44
+ token.content = state.src.slice(pos...max).strip
45
+ token.map = [ startLine, state.line ]
46
+ token.children = []
47
+
48
+ token = state.push('heading_close', "h#{level.to_s}", -1)
49
+ token.markup = '########'.slice(0...level)
50
+
51
+ return true
52
+ end
53
+
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,45 @@
1
+ # Horizontal rule
2
+ #------------------------------------------------------------------------------
3
+ module MarkdownIt
4
+ module RulesBlock
5
+ class Hr
6
+
7
+ #------------------------------------------------------------------------------
8
+ def self.hr(state, startLine, endLine, silent)
9
+ pos = state.bMarks[startLine] + state.tShift[startLine]
10
+ max = state.eMarks[startLine]
11
+ marker = state.src.charCodeAt(pos)
12
+ pos += 1
13
+
14
+ # Check hr marker
15
+ if (marker != 0x2A && # *
16
+ marker != 0x2D && # -
17
+ marker != 0x5F) # _
18
+ return false
19
+ end
20
+
21
+ # markers can be mixed with spaces, but there should be at least 3 one
22
+
23
+ cnt = 1
24
+ while (pos < max)
25
+ ch = state.src.charCodeAt(pos)
26
+ pos += 1
27
+ return false if (ch != marker && ch != 0x20) # space
28
+ cnt += 1 if (ch == marker)
29
+ end
30
+
31
+ return false if (cnt < 3)
32
+ return true if (silent)
33
+
34
+ state.line = startLine + 1
35
+
36
+ token = state.push('hr', 'hr', 0)
37
+ token.map = [ startLine, state.line ]
38
+ token.markup = marker.chr * (cnt + 1)
39
+
40
+ return true
41
+ end
42
+
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,73 @@
1
+ # HTML block
2
+ #------------------------------------------------------------------------------
3
+ module MarkdownIt
4
+ module RulesBlock
5
+ class HtmlBlock
6
+
7
+ HTML_TAG_OPEN_RE = /^<([a-zA-Z][a-zA-Z0-9]{0,14})[\s\/>]/
8
+ HTML_TAG_CLOSE_RE = /^<\/([a-zA-Z][a-zA-Z0-9]{0,14})[\s>]/
9
+
10
+ #------------------------------------------------------------------------------
11
+ def self.isLetter(ch)
12
+ lc = ch | 0x20; # to lower case
13
+ return (lc >= 0x61) && (lc <= 0x7a) # >= a and <= z
14
+ end
15
+
16
+ #------------------------------------------------------------------------------
17
+ def self.html_block(state, startLine, endLine, silent)
18
+ pos = state.bMarks[startLine]
19
+ max = state.eMarks[startLine]
20
+ shift = state.tShift[startLine]
21
+
22
+ pos += shift
23
+
24
+ return false if !state.md.options[:html]
25
+ return false if shift > 3 || (pos + 2) >= max
26
+ return false if state.src.charCodeAt(pos) != 0x3C # <
27
+
28
+ ch = state.src.charCodeAt(pos + 1)
29
+
30
+ if ch == 0x21 || ch == 0x3F # ! or ?
31
+ # Directive start / comment start / processing instruction start
32
+ return true if silent
33
+
34
+ elsif ch == 0x2F || isLetter(ch) # /
35
+
36
+ # Probably start or end of tag
37
+ if ch == 0x2F # \
38
+ # closing tag
39
+ match = state.src.slice(pos...max).match(HTML_TAG_CLOSE_RE)
40
+ return false if (!match)
41
+ else
42
+ # opening tag
43
+ match = state.src.slice(pos...max).match(HTML_TAG_OPEN_RE)
44
+ return false if !match
45
+ end
46
+
47
+ # Make sure tag name is valid
48
+ return false if HTML_BLOCKS[match[1].downcase] != true
49
+ return true if silent
50
+
51
+ else
52
+ return false
53
+ end
54
+
55
+ # If we are here - we detected HTML block.
56
+ # Let's roll down till empty line (block end).
57
+ nextLine = startLine + 1
58
+ while nextLine < state.lineMax && !state.isEmpty(nextLine)
59
+ nextLine += 1
60
+ end
61
+
62
+ state.line = nextLine
63
+
64
+ token = state.push('html_block', '', 0)
65
+ token.map = [ startLine, state.line ]
66
+ token.content = state.getLines(startLine, nextLine, 0, true)
67
+
68
+ return true
69
+ end
70
+
71
+ end
72
+ end
73
+ end