paru 0.2.4.2 → 0.2.4.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/paru.rb +8 -20
- data/lib/paru/error.rb +4 -6
- data/lib/paru/filter.rb +144 -110
- data/lib/paru/filter/ast_manipulation.rb +75 -39
- data/lib/paru/filter/attr.rb +72 -36
- data/lib/paru/filter/block.rb +14 -8
- data/lib/paru/filter/block_quote.rb +12 -9
- data/lib/paru/filter/bullet_list.rb +6 -6
- data/lib/paru/filter/citation.rb +51 -25
- data/lib/paru/filter/cite.rb +29 -20
- data/lib/paru/filter/code.rb +41 -24
- data/lib/paru/filter/code_block.rb +36 -21
- data/lib/paru/filter/definition_list.rb +19 -15
- data/lib/paru/filter/definition_list_item.rb +30 -17
- data/lib/paru/filter/div.rb +29 -21
- data/lib/paru/filter/document.rb +73 -46
- data/lib/paru/filter/emph.rb +6 -6
- data/lib/paru/filter/empty_block.rb +17 -13
- data/lib/paru/filter/empty_inline.rb +24 -17
- data/lib/paru/filter/header.rb +38 -23
- data/lib/paru/filter/image.rb +13 -11
- data/lib/paru/filter/inline.rb +21 -10
- data/lib/paru/filter/line_block.rb +6 -6
- data/lib/paru/filter/line_break.rb +6 -6
- data/lib/paru/filter/link.rb +33 -21
- data/lib/paru/filter/list.rb +26 -17
- data/lib/paru/filter/list_attributes.rb +53 -32
- data/lib/paru/filter/markdown.rb +102 -59
- data/lib/paru/filter/math.rb +65 -38
- data/lib/paru/filter/meta.rb +26 -16
- data/lib/paru/filter/meta_blocks.rb +12 -9
- data/lib/paru/filter/meta_bool.rb +6 -6
- data/lib/paru/filter/meta_inlines.rb +12 -9
- data/lib/paru/filter/meta_list.rb +6 -6
- data/lib/paru/filter/meta_map.rb +49 -33
- data/lib/paru/filter/meta_string.rb +6 -6
- data/lib/paru/filter/meta_value.rb +22 -14
- data/lib/paru/filter/node.rb +204 -129
- data/lib/paru/filter/note.rb +31 -20
- data/lib/paru/filter/null.rb +6 -6
- data/lib/paru/filter/ordered_list.rb +34 -18
- data/lib/paru/filter/para.rb +20 -13
- data/lib/paru/filter/plain.rb +21 -12
- data/lib/paru/filter/quoted.rb +27 -18
- data/lib/paru/filter/raw_block.rb +32 -19
- data/lib/paru/filter/raw_inline.rb +40 -22
- data/lib/paru/filter/small_caps.rb +7 -6
- data/lib/paru/filter/soft_break.rb +6 -6
- data/lib/paru/filter/space.rb +6 -6
- data/lib/paru/filter/span.rb +28 -18
- data/lib/paru/filter/str.rb +29 -18
- data/lib/paru/filter/strikeout.rb +6 -6
- data/lib/paru/filter/strong.rb +6 -6
- data/lib/paru/filter/subscript.rb +6 -6
- data/lib/paru/filter/superscript.rb +6 -6
- data/lib/paru/filter/table.rb +51 -29
- data/lib/paru/filter/table_row.rb +21 -14
- data/lib/paru/filter/target.rb +29 -15
- data/lib/paru/filter/version.rb +23 -14
- data/lib/paru/pandoc.rb +165 -111
- data/lib/paru/pandoc_options.yaml +3 -3
- data/lib/paru/selector.rb +176 -153
- metadata +2 -3
- data/lib/paru/filter/alignment.rb +0 -30
@@ -1,5 +1,5 @@
|
|
1
1
|
#--
|
2
|
-
# Copyright 2015, 2016 Huub de Beer <Huub@heerdebeer.org>
|
2
|
+
# Copyright 2015, 2016, 2017 Huub de Beer <Huub@heerdebeer.org>
|
3
3
|
#
|
4
4
|
# This file is part of Paru
|
5
5
|
#
|
@@ -16,7 +16,7 @@
|
|
16
16
|
# You should have received a copy of the GNU General Public License
|
17
17
|
# along with Paru. If not, see <http://www.gnu.org/licenses/>.
|
18
18
|
# See http://pandoc.org/README.html for an overview of all options
|
19
|
-
|
19
|
+
#++
|
20
20
|
# General options
|
21
21
|
from: ""
|
22
22
|
read: ""
|
@@ -41,7 +41,7 @@ file_scope: true
|
|
41
41
|
filter: [""]
|
42
42
|
metadata: [""]
|
43
43
|
normalize: true
|
44
|
-
preserve_tabs:
|
44
|
+
preserve_tabs: true
|
45
45
|
tab_stop: 4
|
46
46
|
track_changes: "accept"
|
47
47
|
extract_media: true
|
data/lib/paru/selector.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
#--
|
2
|
-
# Copyright 2015, 2016 Huub de Beer <Huub@heerdebeer.org>
|
2
|
+
# Copyright 2015, 2016, 2017 Huub de Beer <Huub@heerdebeer.org>
|
3
3
|
#
|
4
4
|
# This file is part of Paru
|
5
5
|
#
|
@@ -17,157 +17,180 @@
|
|
17
17
|
# along with Paru. If not, see <http://www.gnu.org/licenses/>.
|
18
18
|
#++
|
19
19
|
module Paru
|
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
|
-
|
20
|
+
require_relative './filter'
|
21
|
+
require_relative './error'
|
22
|
+
|
23
|
+
# SelectorParseError is thrown when there is an error parsing a selector
|
24
|
+
# used in a filter.
|
25
|
+
class SelectorParseError < Error
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
# A Selector models a relationship between Pandoc AST nodes, such as
|
30
|
+
# parent-child or sibling. Selectors in paru are like CSS selectors, but
|
31
|
+
# more limited because the Pandoc AST is quite simple.
|
32
|
+
#
|
33
|
+
# Given a selector expression, Selector determines if a node complies with
|
34
|
+
# that selector expression or not.
|
35
|
+
class Selector
|
36
|
+
|
37
|
+
# Create a new Selector based on the selector string
|
38
|
+
#
|
39
|
+
# @param selector [String] the selector string
|
40
|
+
def initialize(selector)
|
41
|
+
@type = 'Unknown'
|
42
|
+
@relations = []
|
43
|
+
parse selector
|
44
|
+
end
|
45
|
+
|
46
|
+
# Does node get selected by this Selector in the context of the already filtered
|
47
|
+
# nodes?
|
48
|
+
#
|
49
|
+
# @param node [Node] the node to check against this Selector
|
50
|
+
# @param filtered_nodes [Array<Node>] the context of filtered nodes to take
|
51
|
+
# into account as well
|
52
|
+
#
|
53
|
+
# @return [Boolean] True if the node in the context of the
|
54
|
+
# filtered_nodes is selected by this Selector
|
55
|
+
def matches? node, filtered_nodes
|
56
|
+
node.type == @type and
|
57
|
+
@classes.all? {|c| node.has_class? c } and
|
58
|
+
@relations.all? {|r| r.matches? node, filtered_nodes}
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
S = /\s*/
|
64
|
+
TYPE = /(?<type>(?<name>[A-Z][a-zA-Z]*)(?<classes>(\.[a-zA-Z-]+)*))/
|
65
|
+
OTHER_TYPE = /(?<other_type>(?<other_name>[A-Z][a-zA-Z]*)(?<other_classes>(\.[a-zA-Z-]+)*))/
|
66
|
+
OPERATOR = /(?<operator>\+|-|>)/
|
67
|
+
DISTANCE = /(?<distance>[1-9][0-9]*)/
|
68
|
+
RELATION = /(?<relation>#{S}#{OTHER_TYPE}#{S}#{OPERATOR}#{S}#{DISTANCE}?#{S})/
|
69
|
+
RELATIONS = /(?<relations>#{RELATION}+)/
|
70
|
+
SELECTOR = /\A#{S}(?<selector>#{RELATIONS}?#{S}#{TYPE})#{S}\Z/
|
71
|
+
|
72
|
+
# Parse the selector_string to construct this Selector
|
73
|
+
def parse(selector_string)
|
74
|
+
partial_match = expect_match SELECTOR, selector_string
|
75
|
+
@type, @classes = expect_pandoc_type partial_match
|
76
|
+
|
77
|
+
while continue_parsing? partial_match
|
78
|
+
operator = expect partial_match, :operator
|
79
|
+
distance = expect_integer partial_match, :distance
|
80
|
+
type, classes = expect_pandoc_other_type partial_match
|
81
|
+
|
82
|
+
@relations.push Relation.new(operator, distance, type, classes)
|
83
|
+
|
84
|
+
partial_match = rest partial_match
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# Is type actually a Pandoc AST node type?
|
89
|
+
def is_pandoc_type(type)
|
90
|
+
Paru::PANDOC_TYPES.include? type
|
91
|
+
end
|
92
|
+
|
93
|
+
def expect(parts, part)
|
94
|
+
raise SelectorParseError.new "Expected #{part}" if parts[part].nil?
|
95
|
+
parts[part]
|
96
|
+
end
|
97
|
+
|
98
|
+
def expect_match(regexp, string)
|
99
|
+
match = regexp.match string
|
100
|
+
raise SelectorParseError.new "Unable to parse '#{string}'" if match.nil?
|
101
|
+
match
|
102
|
+
end
|
103
|
+
|
104
|
+
def expect_pandoc_type(parts)
|
105
|
+
type = expect parts, :name
|
106
|
+
classes = parts[:classes].split(".").select {|c| not c.empty?} if not parts[:classes].nil?
|
107
|
+
raise SelectorParseError.new "Expected a Pandoc type, got '#{type}' instead" if not is_pandoc_type type
|
108
|
+
[type, classes]
|
109
|
+
end
|
110
|
+
|
111
|
+
def expect_pandoc_other_type(parts)
|
112
|
+
type = expect parts, :other_name
|
113
|
+
classes = parts[:other_classes].split('.').select {|c| not c.empty?} if not parts[:other_classes].nil?
|
114
|
+
raise SelectorParseError.new "Expected a Pandoc type, got '#{type}' instead" if not is_pandoc_type type
|
115
|
+
[type, classes]
|
116
|
+
end
|
117
|
+
|
118
|
+
def expect_integer(parts, part)
|
119
|
+
if parts[part].nil?
|
120
|
+
number = 0
|
121
|
+
else
|
122
|
+
number = parts[part].to_i
|
123
|
+
raise SelectorParseError.new "Expected a positive #{part}, got '#{parts[part]}' instead" if number <= 0
|
124
|
+
end
|
125
|
+
number
|
126
|
+
end
|
127
|
+
|
128
|
+
def continue_parsing?(parts)
|
129
|
+
not parts.nil? and not parts[:relations].nil?
|
130
|
+
end
|
131
|
+
|
132
|
+
def rest(parts)
|
133
|
+
rest_string = parts[:relations].slice 0, parts[:relations].size - parts[:relation].size
|
134
|
+
RELATIONS.match rest_string
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
# @private
|
139
|
+
class Relation
|
140
|
+
def initialize(selector, distance, type, classes)
|
141
|
+
@selector = selector
|
142
|
+
@distance = distance
|
143
|
+
@type = type
|
144
|
+
@classes = classes
|
145
|
+
end
|
146
|
+
|
147
|
+
def matches?(node, filtered_nodes)
|
148
|
+
level_nodes = filtered_nodes.keep_if do |n|
|
149
|
+
node.is_inline? == n.is_inline? or
|
150
|
+
node.can_act_as_both_block_and_inline?
|
151
|
+
end
|
152
|
+
previous_nodes = previous level_nodes, @distance
|
153
|
+
case @selector
|
154
|
+
when '+'
|
155
|
+
in_sequence? node, previous_nodes
|
156
|
+
when '-'
|
157
|
+
not_in_sequence? node, previous_nodes
|
158
|
+
when '>'
|
159
|
+
is_descendant? node
|
160
|
+
else
|
161
|
+
false
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
def in_sequence?(node, previous_nodes)
|
166
|
+
previous_nodes.any? do |other|
|
167
|
+
other.type == @type and @classes.all? {|c| other.has_class? c}
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
def not_in_sequence?(node, previous_nodes)
|
172
|
+
previous_nodes.all? do |other|
|
173
|
+
other.type != @type or not @classes.all? {|c| other.has_class? c}
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
def is_descendant?(node)
|
178
|
+
distance = 0
|
179
|
+
begin
|
180
|
+
distance += 1 if @distance > 0
|
181
|
+
parent = node.parent
|
182
|
+
ancestry = parent.type == @type and @classes.all? {|c| parent.has_class? c}
|
183
|
+
end while not ancestry and not parent.is_root? and distance <= @distance
|
184
|
+
ancestry
|
185
|
+
end
|
186
|
+
|
187
|
+
def previous(filtered_nodes, distance)
|
188
|
+
distance = [distance, filtered_nodes.size - 1].min
|
189
|
+
if distance <= 0
|
190
|
+
filtered_nodes.slice(0, filtered_nodes.size - 1)
|
191
|
+
else
|
192
|
+
filtered_nodes.slice(-1 * distance - 1, distance)
|
193
|
+
end
|
194
|
+
end
|
73
195
|
end
|
74
|
-
|
75
|
-
def expect_match regexp, string
|
76
|
-
match = regexp.match string
|
77
|
-
raise SelectorParseError.new "Unable to parse '#{string}'" if match.nil?
|
78
|
-
match
|
79
|
-
end
|
80
|
-
|
81
|
-
def expect_pandoc_type parts
|
82
|
-
type = expect parts, :name
|
83
|
-
classes = parts[:classes].split(".").select {|c| not c.empty?} if not parts[:classes].nil?
|
84
|
-
raise SelectorParseError.new "Expected a Pandoc type, got '#{type}' instead" if not is_pandoc_type type
|
85
|
-
[type, classes]
|
86
|
-
end
|
87
|
-
|
88
|
-
def expect_pandoc_other_type parts
|
89
|
-
type = expect parts, :other_name
|
90
|
-
classes = parts[:other_classes].split(".").select {|c| not c.empty?} if not parts[:other_classes].nil?
|
91
|
-
raise SelectorParseError.new "Expected a Pandoc type, got '#{type}' instead" if not is_pandoc_type type
|
92
|
-
[type, classes]
|
93
|
-
end
|
94
|
-
|
95
|
-
def expect_integer parts, part
|
96
|
-
if parts[part].nil?
|
97
|
-
number = 0
|
98
|
-
else
|
99
|
-
number = parts[part].to_i
|
100
|
-
raise SelectorParseError.new "Expected a positive #{part}, got '#{parts[part]}' instead" if number <= 0
|
101
|
-
end
|
102
|
-
number
|
103
|
-
end
|
104
|
-
|
105
|
-
def continue_parsing? parts
|
106
|
-
not parts.nil? and not parts[:relations].nil?
|
107
|
-
end
|
108
|
-
|
109
|
-
def rest parts
|
110
|
-
rest_string = parts[:relations].slice 0, parts[:relations].size - parts[:relation].size
|
111
|
-
RELATIONS.match rest_string
|
112
|
-
end
|
113
|
-
end
|
114
|
-
|
115
|
-
class Relation
|
116
|
-
def initialize selector, distance, type, classes
|
117
|
-
@selector = selector
|
118
|
-
@distance = distance
|
119
|
-
@type = type
|
120
|
-
@classes = classes
|
121
|
-
end
|
122
|
-
|
123
|
-
def matches? node, filtered_nodes
|
124
|
-
level_nodes = filtered_nodes.keep_if do |n|
|
125
|
-
node.is_inline? == n.is_inline? or
|
126
|
-
node.can_act_as_both_block_and_inline?
|
127
|
-
end
|
128
|
-
previous_nodes = previous level_nodes, @distance
|
129
|
-
case @selector
|
130
|
-
when "+"
|
131
|
-
in_sequence? node, previous_nodes
|
132
|
-
when "-"
|
133
|
-
not_in_sequence? node, previous_nodes
|
134
|
-
when ">"
|
135
|
-
is_descendant? node
|
136
|
-
else
|
137
|
-
false
|
138
|
-
end
|
139
|
-
end
|
140
|
-
|
141
|
-
def in_sequence? node, previous_nodes
|
142
|
-
previous_nodes.any? do |other|
|
143
|
-
other.type == @type and @classes.all? {|c| other.has_class? c}
|
144
|
-
end
|
145
|
-
end
|
146
|
-
|
147
|
-
def not_in_sequence? node, previous_nodes
|
148
|
-
previous_nodes.all? do |other|
|
149
|
-
other.type != @type or not @classes.all? {|c| other.has_class? c}
|
150
|
-
end
|
151
|
-
end
|
152
|
-
|
153
|
-
def is_descendant? node
|
154
|
-
distance = 0
|
155
|
-
begin
|
156
|
-
distance += 1 if @distance > 0
|
157
|
-
parent = node.parent
|
158
|
-
ancestry = parent.type == @type and @classes.all? {|c| parent.has_class? c}
|
159
|
-
end while not ancestry and not parent.is_root? and distance <= @distance
|
160
|
-
ancestry
|
161
|
-
end
|
162
|
-
|
163
|
-
def previous filtered_nodes, distance
|
164
|
-
distance = [distance, filtered_nodes.size - 1].min
|
165
|
-
if distance <= 0
|
166
|
-
filtered_nodes.slice(0, filtered_nodes.size - 1)
|
167
|
-
else
|
168
|
-
filtered_nodes.slice(-1 * distance - 1, distance)
|
169
|
-
end
|
170
|
-
end
|
171
|
-
end
|
172
|
-
|
173
196
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: paru
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.4.
|
4
|
+
version: 0.2.4.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Huub de Beer
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-05-19 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Use Pandoc (http://www.pandoc.org) with ruby
|
14
14
|
email: Huub@heerdebeer.org
|
@@ -23,7 +23,6 @@ files:
|
|
23
23
|
- lib/paru.rb
|
24
24
|
- lib/paru/error.rb
|
25
25
|
- lib/paru/filter.rb
|
26
|
-
- lib/paru/filter/alignment.rb
|
27
26
|
- lib/paru/filter/ast_manipulation.rb
|
28
27
|
- lib/paru/filter/attr.rb
|
29
28
|
- lib/paru/filter/block.rb
|
@@ -1,30 +0,0 @@
|
|
1
|
-
#--
|
2
|
-
# Copyright 2015, 2016 Huub de Beer <Huub@heerdebeer.org>
|
3
|
-
#
|
4
|
-
# This file is part of Paru
|
5
|
-
#
|
6
|
-
# Paru is free software: you can redistribute it and/or modify
|
7
|
-
# it under the terms of the GNU General Public License as published by
|
8
|
-
# the Free Software Foundation, either version 3 of the License, or
|
9
|
-
# (at your option) any later version.
|
10
|
-
#
|
11
|
-
# Paru is distributed in the hope that it will be useful,
|
12
|
-
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
-
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14
|
-
# GNU General Public License for more details.
|
15
|
-
#
|
16
|
-
# You should have received a copy of the GNU General Public License
|
17
|
-
# along with Paru. If not, see <http://www.gnu.org/licenses/>.
|
18
|
-
#++
|
19
|
-
module Paru
|
20
|
-
module PandocFilter
|
21
|
-
|
22
|
-
class Alignment
|
23
|
-
ALIGNMENTS = ["AlignLeft", "AlignRight", "AlignCenter", "AlignDefault"]
|
24
|
-
|
25
|
-
def initialize config
|
26
|
-
@config = config
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|