markly 0.7.0 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/conduct.md +133 -0
- data/ext/markly/arena.c +9 -8
- data/ext/markly/autolink.c +217 -134
- data/ext/markly/blocks.c +27 -2
- data/ext/markly/cmark-gfm-core-extensions.h +11 -11
- data/ext/markly/cmark-gfm-extension_api.h +1 -0
- data/ext/markly/cmark-gfm.h +18 -2
- data/ext/markly/cmark.c +3 -3
- data/ext/markly/commonmark.c +19 -34
- data/ext/markly/extconf.rb +8 -1
- data/ext/markly/html.c +22 -6
- data/ext/markly/inlines.c +148 -51
- data/ext/markly/latex.c +6 -4
- data/ext/markly/man.c +7 -11
- data/ext/markly/map.c +11 -4
- data/ext/markly/map.h +5 -2
- data/ext/markly/markly.c +582 -586
- data/ext/markly/markly.h +1 -1
- data/ext/markly/node.c +76 -10
- data/ext/markly/node.h +42 -1
- data/ext/markly/parser.h +1 -0
- data/ext/markly/plaintext.c +12 -29
- data/ext/markly/references.c +1 -0
- data/ext/markly/render.c +15 -7
- data/ext/markly/scanners.c +13916 -10380
- data/ext/markly/scanners.h +8 -0
- data/ext/markly/scanners.re +47 -8
- data/ext/markly/strikethrough.c +1 -1
- data/ext/markly/table.c +81 -31
- data/ext/markly/xml.c +2 -1
- data/lib/markly/flags.rb +16 -0
- data/lib/markly/node/inspect.rb +59 -53
- data/lib/markly/node.rb +125 -58
- data/lib/markly/renderer/generic.rb +129 -124
- data/lib/markly/renderer/html.rb +294 -275
- data/lib/markly/version.rb +7 -1
- data/lib/markly.rb +36 -30
- data/license.md +39 -0
- data/readme.md +36 -0
- data.tar.gz.sig +0 -0
- metadata +61 -29
- metadata.gz.sig +0 -0
- data/bin/markly +0 -94
- data/lib/markly/markly.bundle +0 -0
data/lib/markly/node.rb
CHANGED
@@ -1,72 +1,139 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# Released under the MIT License.
|
4
|
+
# Copyright, 2016-2019, by Garen Torikian.
|
5
|
+
# Copyright, 2016-2017, by Yuki Izumi.
|
6
|
+
# Copyright, 2017, by Goro Fuji.
|
7
|
+
# Copyright, 2018, by Jerry van Leeuwen.
|
8
|
+
# Copyright, 2020-2023, by Samuel Williams.
|
9
|
+
|
3
10
|
require_relative 'node/inspect'
|
4
11
|
|
5
12
|
module Markly
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
# Public: An iterator that "walks the tree," descending into children recursively.
|
11
|
-
#
|
12
|
-
# blk - A {Proc} representing the action to take for each child
|
13
|
-
def walk(&block)
|
14
|
-
return enum_for(:walk) unless block_given?
|
13
|
+
class Node
|
14
|
+
include Enumerable
|
15
|
+
include Inspect
|
15
16
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
17
|
+
# Public: An iterator that "walks the tree," descending into children recursively.
|
18
|
+
#
|
19
|
+
# blk - A {Proc} representing the action to take for each child
|
20
|
+
def walk(&block)
|
21
|
+
return enum_for(:walk) unless block_given?
|
21
22
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
# Returns a {String}.
|
28
|
-
def to_html(flags: DEFAULT, extensions: [])
|
29
|
-
_render_html(flags, extensions).force_encoding('utf-8')
|
30
|
-
end
|
23
|
+
yield self
|
24
|
+
each do |child|
|
25
|
+
child.walk(&block)
|
26
|
+
end
|
27
|
+
end
|
31
28
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
29
|
+
# Public: Convert the node to an HTML string.
|
30
|
+
#
|
31
|
+
# options - A {Symbol} or {Array of Symbol}s indicating the render options
|
32
|
+
# extensions - An {Array of Symbol}s indicating the extensions to use
|
33
|
+
#
|
34
|
+
# Returns a {String}.
|
35
|
+
def to_html(flags: DEFAULT, extensions: [])
|
36
|
+
_render_html(flags, extensions).force_encoding('utf-8')
|
37
|
+
end
|
41
38
|
|
42
|
-
|
39
|
+
# Public: Convert the node to a CommonMark string.
|
40
|
+
#
|
41
|
+
# options - A {Symbol} or {Array of Symbol}s indicating the render options
|
42
|
+
# width - Column to wrap the output at
|
43
|
+
#
|
44
|
+
# Returns a {String}.
|
45
|
+
def to_commonmark(flags: DEFAULT, width: 120)
|
46
|
+
_render_commonmark(flags, width).force_encoding('utf-8')
|
47
|
+
end
|
43
48
|
|
44
|
-
|
45
|
-
#
|
46
|
-
# options - A {Symbol} or {Array of Symbol}s indicating the render options
|
47
|
-
# width - Column to wrap the output at
|
48
|
-
#
|
49
|
-
# Returns a {String}.
|
50
|
-
def to_plaintext(flags: DEFAULT, width: 120)
|
51
|
-
_render_plaintext(flags, width).force_encoding('utf-8')
|
52
|
-
end
|
49
|
+
alias to_markdown to_commonmark
|
53
50
|
|
54
|
-
|
55
|
-
|
56
|
-
|
51
|
+
# Public: Convert the node to a plain text string.
|
52
|
+
#
|
53
|
+
# options - A {Symbol} or {Array of Symbol}s indicating the render options
|
54
|
+
# width - Column to wrap the output at
|
55
|
+
#
|
56
|
+
# Returns a {String}.
|
57
|
+
def to_plaintext(flags: DEFAULT, width: 120)
|
58
|
+
_render_plaintext(flags, width).force_encoding('utf-8')
|
59
|
+
end
|
57
60
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
yield child
|
62
|
-
child = nextchild
|
63
|
-
end
|
64
|
-
end
|
61
|
+
# Public: Iterate over the children (if any) of the current pointer.
|
62
|
+
def each
|
63
|
+
return enum_for(:each) unless block_given?
|
65
64
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
65
|
+
child = first_child
|
66
|
+
while child
|
67
|
+
next_child = child.next
|
68
|
+
yield child
|
69
|
+
child = next_child
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def find_header(title)
|
74
|
+
each do |child|
|
75
|
+
if child.type == :header && child.first_child.string_content == title
|
76
|
+
return child
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
# Delete all nodes until the block returns true.
|
82
|
+
#
|
83
|
+
# @returns [Markly::Node] the node that returned true.
|
84
|
+
def delete_until
|
85
|
+
current = self
|
86
|
+
while current
|
87
|
+
return current if yield(current)
|
88
|
+
next_node = current.next
|
89
|
+
current.delete
|
90
|
+
current = next_node
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# Replace a section (header + content) with a new node.
|
95
|
+
#
|
96
|
+
# @parameter title [String] the title of the section to replace.
|
97
|
+
# @parameter new_node [Markly::Node] the node to replace the section with.
|
98
|
+
# @parameter replace_header [Boolean] whether to replace the header itself or not.
|
99
|
+
# @parameter remove_subsections [Boolean] whether to remove subsections or not.
|
100
|
+
def replace_section(new_node, replace_header: true, remove_subsections: true)
|
101
|
+
# Delete until the next heading:
|
102
|
+
self.next&.delete_until do |node|
|
103
|
+
node.type == :heading && (!remove_subsections || node.header_level <= self.header_level)
|
104
|
+
end
|
105
|
+
|
106
|
+
self.append_after(new_node) if new_node
|
107
|
+
self.delete if replace_header
|
108
|
+
end
|
109
|
+
|
110
|
+
def next_heading
|
111
|
+
current = self.next
|
112
|
+
while current
|
113
|
+
if current.type == :heading
|
114
|
+
return current
|
115
|
+
end
|
116
|
+
current = current.next
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
# Append the given node after the current node.
|
121
|
+
#
|
122
|
+
# It's okay to provide a document node, it's children will be appended.
|
123
|
+
#
|
124
|
+
# @parameter node [Markly::Node] the node to append.
|
125
|
+
def append_after(node)
|
126
|
+
if node.type == :document
|
127
|
+
node = node.first_child
|
128
|
+
end
|
129
|
+
|
130
|
+
current = self
|
131
|
+
while node
|
132
|
+
next_node = node.next
|
133
|
+
current.insert_after(node)
|
134
|
+
current = node
|
135
|
+
node = next_node
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
72
139
|
end
|
@@ -1,131 +1,136 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# Released under the MIT License.
|
4
|
+
# Copyright, 2015-2019, by Garen Torikian.
|
5
|
+
# Copyright, 2016-2017, by Yuki Izumi.
|
6
|
+
# Copyright, 2020-2023, by Samuel Williams.
|
7
|
+
|
3
8
|
require 'set'
|
4
9
|
require 'stringio'
|
5
10
|
|
6
11
|
module Markly
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
12
|
+
module Renderer
|
13
|
+
class Generic
|
14
|
+
def initialize(flags: DEFAULT, extensions: [])
|
15
|
+
@flags = flags
|
16
|
+
@stream = StringIO.new(+'')
|
17
|
+
@need_blocksep = false
|
18
|
+
@in_tight = false
|
19
|
+
@in_plain = false
|
20
|
+
@tagfilter = extensions.include?(:tagfilter)
|
21
|
+
end
|
22
|
+
|
23
|
+
attr_accessor :in_tight
|
24
|
+
attr_accessor :in_plain
|
25
|
+
|
26
|
+
def out(*args)
|
27
|
+
args.each do |arg|
|
28
|
+
if arg == :children
|
29
|
+
@node.each { |child| out(child) }
|
30
|
+
elsif arg.is_a?(Array)
|
31
|
+
arg.each { |x| render(x) }
|
32
|
+
elsif arg.is_a?(Node)
|
33
|
+
render(arg)
|
34
|
+
else
|
35
|
+
@stream.write(arg)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def render(node)
|
41
|
+
@node = node
|
42
|
+
if node.type == :document
|
43
|
+
document(node)
|
44
|
+
@stream.string
|
45
|
+
elsif @in_plain && node.type != :text && node.type != :softbreak
|
46
|
+
node.each { |child| render(child) }
|
47
|
+
else
|
48
|
+
send(node.type, node)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def document(_node)
|
53
|
+
out(:children)
|
54
|
+
end
|
55
|
+
|
56
|
+
def code_block(node)
|
57
|
+
code_block(node)
|
58
|
+
end
|
59
|
+
|
60
|
+
def reference_def(_node); end
|
61
|
+
|
62
|
+
def cr
|
63
|
+
return if @stream.string.empty? || @stream.string[-1] == "\n"
|
64
|
+
|
65
|
+
out("\n")
|
66
|
+
end
|
67
|
+
|
68
|
+
def blocksep
|
69
|
+
out("\n")
|
70
|
+
end
|
71
|
+
|
72
|
+
def containersep
|
73
|
+
cr unless @in_tight
|
74
|
+
end
|
75
|
+
|
76
|
+
def block
|
77
|
+
cr
|
78
|
+
yield
|
79
|
+
cr
|
80
|
+
end
|
81
|
+
|
82
|
+
def container(starter, ender)
|
83
|
+
out(starter)
|
84
|
+
yield
|
85
|
+
out(ender)
|
86
|
+
end
|
87
|
+
|
88
|
+
def plain
|
89
|
+
old_in_plain = @in_plain
|
90
|
+
@in_plain = true
|
91
|
+
yield
|
92
|
+
@in_plain = old_in_plain
|
93
|
+
end
|
94
|
+
|
95
|
+
private
|
96
|
+
|
97
|
+
def escape_href(str)
|
98
|
+
@node.html_escape_href(str)
|
99
|
+
end
|
100
|
+
|
101
|
+
def escape_html(str)
|
102
|
+
@node.html_escape_html(str)
|
103
|
+
end
|
104
|
+
|
105
|
+
def tagfilter(str)
|
106
|
+
if @tagfilter
|
107
|
+
str.gsub(
|
108
|
+
%r{
|
109
|
+
<
|
110
|
+
(
|
111
|
+
title|textarea|style|xmp|iframe|
|
112
|
+
noembed|noframes|script|plaintext
|
113
|
+
)
|
114
|
+
(?=\s|>|/>)
|
115
|
+
}xi,
|
116
|
+
'<\1'
|
117
|
+
)
|
118
|
+
else
|
119
|
+
str
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def source_position(node)
|
124
|
+
return '' unless flag_enabled?(SOURCE_POSITION)
|
125
|
+
|
126
|
+
s = node.source_position
|
127
|
+
" data-sourcepos=\"#{s[:start_line]}:#{s[:start_column]}-" \
|
128
|
+
"#{s[:end_line]}:#{s[:end_column]}\""
|
129
|
+
end
|
130
|
+
|
131
|
+
def flag_enabled?(flag)
|
132
|
+
(@flags & flag) != 0
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
131
136
|
end
|