enolib 0.5.0 → 0.5.2
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.
- checksums.yaml +4 -4
- data/lib/enolib/constants.rb +1 -1
- data/lib/enolib/context.rb +18 -18
- data/lib/enolib/elements/element_base.rb +8 -8
- data/lib/enolib/elements/field.rb +3 -25
- data/lib/enolib/elements/fieldset.rb +6 -6
- data/lib/enolib/elements/list.rb +3 -3
- data/lib/enolib/elements/section.rb +7 -9
- data/lib/enolib/elements/section_element.rb +4 -4
- data/lib/enolib/elements/value_element_base.rb +3 -3
- data/lib/enolib/errors/parsing.rb +23 -23
- data/lib/enolib/errors/selections.rb +1 -1
- data/lib/enolib/errors/validation.rb +62 -59
- data/lib/enolib/grammar_regexp.rb +1 -1
- data/lib/enolib/lookup.rb +4 -3
- data/lib/enolib/parser.rb +23 -31
- data/lib/enolib/reporters/html_reporter.rb +4 -4
- data/lib/enolib/reporters/reporter.rb +25 -25
- data/lib/enolib/reporters/terminal_reporter.rb +5 -5
- data/lib/enolib/reporters/text_reporter.rb +1 -1
- metadata +30 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6b0463dcfa44839c7fecdb048f3678504d0616264bb71e3fe8ada2f97b9b8130
|
4
|
+
data.tar.gz: f4c541da00922054b230d6ff35860bfa49832fc24b064b52948ebb09d90da5e5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 237cf74c59338475dfc4f0b8f4d8e5e5c7ced6aa57a41c9afb2a88d442d0c478e3459eadb956407d2f05109c6177cf9458d8e3c38e7fd489741d7774945f6b88
|
7
|
+
data.tar.gz: cd1266e224fbf84ef250dbbd71c65196a2d0d31eb9ef8ed1b6c0a903fc1b00f222966317b8e5c18e3d197b2c0dc9132253f4102634a0708a0d08ee034f136f53
|
data/lib/enolib/constants.rb
CHANGED
data/lib/enolib/context.rb
CHANGED
@@ -33,27 +33,27 @@ module Enolib
|
|
33
33
|
shared_indent = Float::INFINITY
|
34
34
|
|
35
35
|
element[:comments].each_with_index do |comment, index|
|
36
|
-
|
37
|
-
first_non_empty_line_index = index unless first_non_empty_line_index
|
38
|
-
last_non_empty_line_index = index
|
36
|
+
next unless comment.has_key?(:comment)
|
39
37
|
|
40
|
-
|
41
|
-
|
42
|
-
|
38
|
+
first_non_empty_line_index ||= index
|
39
|
+
last_non_empty_line_index = index
|
40
|
+
|
41
|
+
indent = comment[:ranges][:comment][RANGE_BEGIN] - comment[:ranges][:line][RANGE_BEGIN]
|
42
|
+
shared_indent = indent if indent < shared_indent
|
43
43
|
end
|
44
44
|
|
45
45
|
if first_non_empty_line_index
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
46
|
+
non_empty_lines = element[:comments][first_non_empty_line_index..last_non_empty_line_index]
|
47
|
+
|
48
|
+
non_empty_lines.map do |comment|
|
49
|
+
if !comment.has_key?(:comment)
|
50
|
+
''
|
51
|
+
elsif (comment[:ranges][:comment][RANGE_BEGIN] - comment[:ranges][:line][RANGE_BEGIN]) == shared_indent
|
52
|
+
comment[:comment]
|
53
|
+
else
|
54
|
+
(' ' * (comment[:ranges][:comment][RANGE_BEGIN] - comment[:ranges][:line][RANGE_BEGIN] - shared_indent)) + comment[:comment]
|
55
|
+
end
|
56
|
+
end.join("\n")
|
57
57
|
else
|
58
58
|
nil
|
59
59
|
end
|
@@ -214,7 +214,7 @@ module Enolib
|
|
214
214
|
end
|
215
215
|
end
|
216
216
|
|
217
|
-
|
217
|
+
element[:computed_value]
|
218
218
|
end
|
219
219
|
end
|
220
220
|
end
|
@@ -16,7 +16,7 @@ module Enolib
|
|
16
16
|
end
|
17
17
|
|
18
18
|
unless message
|
19
|
-
raise ArgumentError
|
19
|
+
raise ArgumentError, 'A message or message function must be provided'
|
20
20
|
end
|
21
21
|
|
22
22
|
Errors::Validation.comment_error(@context, message, @instruction)
|
@@ -30,7 +30,7 @@ module Enolib
|
|
30
30
|
end
|
31
31
|
|
32
32
|
unless message
|
33
|
-
raise ArgumentError
|
33
|
+
raise ArgumentError, 'A message or message function must be provided'
|
34
34
|
end
|
35
35
|
|
36
36
|
Errors::Validation.element_error(@context, message, @instruction)
|
@@ -42,7 +42,7 @@ module Enolib
|
|
42
42
|
@touched = true
|
43
43
|
|
44
44
|
unless loader
|
45
|
-
raise ArgumentError
|
45
|
+
raise ArgumentError, 'A loader function must be provided'
|
46
46
|
end
|
47
47
|
|
48
48
|
begin
|
@@ -60,7 +60,7 @@ module Enolib
|
|
60
60
|
end
|
61
61
|
|
62
62
|
unless message
|
63
|
-
raise ArgumentError
|
63
|
+
raise ArgumentError, 'A message or message function must be provided'
|
64
64
|
end
|
65
65
|
|
66
66
|
Errors::Validation.key_error(@context, message, @instruction)
|
@@ -70,13 +70,13 @@ module Enolib
|
|
70
70
|
loader = Proc.new if block_given?
|
71
71
|
|
72
72
|
unless loader
|
73
|
-
raise ArgumentError
|
73
|
+
raise ArgumentError, 'A loader function must be provided'
|
74
74
|
end
|
75
75
|
|
76
76
|
_comment(loader, required: false)
|
77
77
|
end
|
78
78
|
|
79
|
-
def optional_string_comment
|
79
|
+
def optional_string_comment
|
80
80
|
_comment(required: false)
|
81
81
|
end
|
82
82
|
|
@@ -88,13 +88,13 @@ module Enolib
|
|
88
88
|
loader = Proc.new if block_given?
|
89
89
|
|
90
90
|
unless loader
|
91
|
-
raise ArgumentError
|
91
|
+
raise ArgumentError, 'A loader function must be provided'
|
92
92
|
end
|
93
93
|
|
94
94
|
_comment(loader, required: true)
|
95
95
|
end
|
96
96
|
|
97
|
-
def required_string_comment
|
97
|
+
def required_string_comment
|
98
98
|
_comment(required: true)
|
99
99
|
end
|
100
100
|
|
@@ -10,14 +10,14 @@ module Enolib
|
|
10
10
|
loader = Proc.new if block_given?
|
11
11
|
|
12
12
|
unless loader
|
13
|
-
raise ArgumentError
|
13
|
+
raise ArgumentError, 'A loader function must be provided'
|
14
14
|
end
|
15
15
|
|
16
16
|
_value(loader, required: false)
|
17
17
|
end
|
18
18
|
|
19
19
|
def parent
|
20
|
-
|
20
|
+
@parent || Section.new(@context, @instruction[:parent])
|
21
21
|
end
|
22
22
|
|
23
23
|
def required_string_value
|
@@ -28,7 +28,7 @@ module Enolib
|
|
28
28
|
loader = Proc.new if block_given?
|
29
29
|
|
30
30
|
unless loader
|
31
|
-
raise ArgumentError
|
31
|
+
raise ArgumentError, 'A loader function must be provided'
|
32
32
|
end
|
33
33
|
|
34
34
|
_value(loader, required: true)
|
@@ -37,27 +37,5 @@ module Enolib
|
|
37
37
|
def to_s
|
38
38
|
"#<Enolib::Field key=#{@instruction[:key]} value=#{print_value}>"
|
39
39
|
end
|
40
|
-
|
41
|
-
private
|
42
|
-
|
43
|
-
def _value(loader = nil, required:)
|
44
|
-
@touched = true
|
45
|
-
|
46
|
-
value = @context.value(@instruction)
|
47
|
-
|
48
|
-
if value
|
49
|
-
return value unless loader
|
50
|
-
|
51
|
-
begin
|
52
|
-
loader.call(value)
|
53
|
-
rescue => message
|
54
|
-
raise Errors::Validation.value_error(@context, message, @instruction)
|
55
|
-
end
|
56
|
-
else
|
57
|
-
return nil unless required
|
58
|
-
|
59
|
-
raise Errors::Validation.missing_value(@context, @instruction)
|
60
|
-
end
|
61
|
-
end
|
62
40
|
end
|
63
41
|
end
|
@@ -33,13 +33,13 @@ module Enolib
|
|
33
33
|
next if except && except.include?(key) || only && !only.include?(key)
|
34
34
|
|
35
35
|
entries.each do |entry|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
raise Errors::Validation.unexpected_element(@context, message, entry[:instruction])
|
36
|
+
next if entry.touched # TODO: Revisit instance var existance question in ruby
|
37
|
+
|
38
|
+
if message.is_a?(Proc)
|
39
|
+
message = message.call(entry)
|
42
40
|
end
|
41
|
+
|
42
|
+
raise Errors::Validation.unexpected_element(@context, message, entry[:instruction])
|
43
43
|
end
|
44
44
|
end
|
45
45
|
end
|
data/lib/enolib/elements/list.rb
CHANGED
@@ -50,7 +50,7 @@ module Enolib
|
|
50
50
|
if loader
|
51
51
|
_comment(loader, required: false)
|
52
52
|
else
|
53
|
-
raise ArgumentError
|
53
|
+
raise ArgumentError, 'A loader block or Proc must be provided'
|
54
54
|
end
|
55
55
|
end
|
56
56
|
|
@@ -66,7 +66,7 @@ module Enolib
|
|
66
66
|
@touched = true
|
67
67
|
|
68
68
|
unless loader
|
69
|
-
raise ArgumentError
|
69
|
+
raise ArgumentError, 'A loader function must be provided'
|
70
70
|
end
|
71
71
|
|
72
72
|
_items.map { |item| item.optional_value(loader) }
|
@@ -88,7 +88,7 @@ module Enolib
|
|
88
88
|
@touched = true
|
89
89
|
|
90
90
|
unless loader
|
91
|
-
raise ArgumentError
|
91
|
+
raise ArgumentError, 'A loader function must be provided'
|
92
92
|
end
|
93
93
|
|
94
94
|
_items.map { |item| item.required_value(loader) }
|
@@ -61,13 +61,13 @@ module Enolib
|
|
61
61
|
elements.each do |element|
|
62
62
|
untouched = element._untouched
|
63
63
|
|
64
|
-
|
65
|
-
if message.is_a?(Proc)
|
66
|
-
message = message.call(Element.new(@context, untouched, self))
|
67
|
-
end
|
64
|
+
next unless untouched
|
68
65
|
|
69
|
-
|
66
|
+
if message.is_a?(Proc)
|
67
|
+
message = message.call(Element.new(@context, untouched, self))
|
70
68
|
end
|
69
|
+
|
70
|
+
raise Errors::Validation.unexpected_element(@context, message, untouched)
|
71
71
|
end
|
72
72
|
end
|
73
73
|
end
|
@@ -270,9 +270,7 @@ module Enolib
|
|
270
270
|
def touch
|
271
271
|
@touched = true
|
272
272
|
|
273
|
-
_elements.each
|
274
|
-
element.touch
|
275
|
-
end
|
273
|
+
_elements.each(&:touch)
|
276
274
|
end
|
277
275
|
|
278
276
|
private
|
@@ -291,7 +289,7 @@ module Enolib
|
|
291
289
|
if required || @all_elements_required
|
292
290
|
raise Errors::Validation.missing_element(@context, key, @instruction, 'missing_element')
|
293
291
|
elsif required == nil
|
294
|
-
return
|
292
|
+
return MissingSectionElement.new(key, self)
|
295
293
|
else
|
296
294
|
return nil
|
297
295
|
end
|
@@ -17,7 +17,7 @@ module Enolib
|
|
17
17
|
def to_empty
|
18
18
|
unless instance_variable_defined?(:@empty)
|
19
19
|
if instance_variable_defined?(:@yielded)
|
20
|
-
raise TypeError
|
20
|
+
raise TypeError, "This element was already yielded as #{PRETTY_TYPES[@yielded]} and can't be yielded again as an empty."
|
21
21
|
end
|
22
22
|
|
23
23
|
unless @instruction[:type] == :empty_element
|
@@ -35,7 +35,7 @@ module Enolib
|
|
35
35
|
def to_field
|
36
36
|
unless instance_variable_defined?(:@field)
|
37
37
|
if instance_variable_defined?(:@yielded)
|
38
|
-
raise TypeError
|
38
|
+
raise TypeError, "This element was already yielded as #{PRETTY_TYPES[@yielded]} and can't be yielded again as a field."
|
39
39
|
end
|
40
40
|
|
41
41
|
unless @instruction[:type] == :field ||
|
@@ -54,7 +54,7 @@ module Enolib
|
|
54
54
|
def to_fieldset
|
55
55
|
unless instance_variable_defined?(:@fieldset)
|
56
56
|
if instance_variable_defined?(:@yielded)
|
57
|
-
raise TypeError
|
57
|
+
raise TypeError, "This element was already yielded as #{PRETTY_TYPES[@yielded]} and can't be yielded again as a fieldset."
|
58
58
|
end
|
59
59
|
|
60
60
|
unless @instruction[:type] == :fieldset || @instruction[:type] == :empty_element
|
@@ -71,7 +71,7 @@ module Enolib
|
|
71
71
|
def to_list
|
72
72
|
unless instance_variable_defined?(:@list)
|
73
73
|
if instance_variable_defined?(:@yielded)
|
74
|
-
raise TypeError
|
74
|
+
raise TypeError, "This element was already yielded as #{PRETTY_TYPES[@yielded]} and can't be yielded again as a list."
|
75
75
|
end
|
76
76
|
|
77
77
|
unless @instruction[:type] == :list || @instruction[:type] == :empty_element
|
@@ -10,7 +10,7 @@ module Enolib
|
|
10
10
|
loader = Proc.new if block_given?
|
11
11
|
|
12
12
|
unless loader
|
13
|
-
raise ArgumentError
|
13
|
+
raise ArgumentError, 'A loader function must be provided'
|
14
14
|
end
|
15
15
|
|
16
16
|
_value(loader, required: false)
|
@@ -24,7 +24,7 @@ module Enolib
|
|
24
24
|
loader = Proc.new if block_given?
|
25
25
|
|
26
26
|
unless loader
|
27
|
-
raise ArgumentError
|
27
|
+
raise ArgumentError, 'A loader function must be provided'
|
28
28
|
end
|
29
29
|
|
30
30
|
_value(loader, required: true)
|
@@ -38,7 +38,7 @@ module Enolib
|
|
38
38
|
end
|
39
39
|
|
40
40
|
unless message
|
41
|
-
raise ArgumentError
|
41
|
+
raise ArgumentError, 'A message or message function must be provided'
|
42
42
|
end
|
43
43
|
|
44
44
|
Errors::Validation.value_error(@context, message, @instruction)
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module Enolib
|
4
4
|
module Errors
|
5
5
|
module Parsing
|
6
|
-
UNTERMINATED_ESCAPED_KEY = /^\s*#*\s*(`+)(?!`)((?:(?!\1).)+)
|
6
|
+
UNTERMINATED_ESCAPED_KEY = /^\s*#*\s*(`+)(?!`)((?:(?!\1).)+)$/.freeze
|
7
7
|
|
8
8
|
def self.cyclic_dependency(context, instruction, instruction_chain)
|
9
9
|
first_occurrence = instruction_chain.find_index(instruction)
|
@@ -24,9 +24,9 @@ module Enolib
|
|
24
24
|
end
|
25
25
|
|
26
26
|
ParseError.new(
|
27
|
-
context.messages
|
27
|
+
context.messages.cyclic_dependency(copy_instruction[:line] + Enolib::HUMAN_INDEXING, copy_instruction[:template]),
|
28
28
|
reporter.snippet,
|
29
|
-
Selections
|
29
|
+
Selections.select_template(copy_instruction)
|
30
30
|
)
|
31
31
|
end
|
32
32
|
|
@@ -37,41 +37,41 @@ module Enolib
|
|
37
37
|
return unterminated_escaped_key(context, instruction, match.end(1)) if match
|
38
38
|
|
39
39
|
ParseError.new(
|
40
|
-
context.messages
|
40
|
+
context.messages.invalid_line(instruction[:line] + Enolib::HUMAN_INDEXING),
|
41
41
|
context.reporter.new(context).report_line(instruction).snippet,
|
42
|
-
Selections
|
42
|
+
Selections.select_line(instruction)
|
43
43
|
)
|
44
44
|
end
|
45
45
|
|
46
46
|
def self.missing_element_for_continuation(context, continuation)
|
47
47
|
ParseError.new(
|
48
|
-
context.messages
|
48
|
+
context.messages.missing_element_for_continuation(continuation[:line] + Enolib::HUMAN_INDEXING),
|
49
49
|
context.reporter.new(context).report_line(continuation).snippet,
|
50
|
-
Selections
|
50
|
+
Selections.select_line(continuation)
|
51
51
|
)
|
52
52
|
end
|
53
53
|
|
54
54
|
def self.missing_fieldset_for_fieldset_entry(context, entry)
|
55
55
|
ParseError.new(
|
56
|
-
context.messages
|
56
|
+
context.messages.missing_fieldset_for_fieldset_entry(entry[:line] + Enolib::HUMAN_INDEXING),
|
57
57
|
context.reporter.new(context).report_line(entry).snippet,
|
58
|
-
Selections
|
58
|
+
Selections.select_line(entry)
|
59
59
|
)
|
60
60
|
end
|
61
61
|
|
62
62
|
def self.missing_list_for_list_item(context, item)
|
63
63
|
ParseError.new(
|
64
|
-
context.messages
|
64
|
+
context.messages.missing_list_for_list_item(item[:line] + Enolib::HUMAN_INDEXING),
|
65
65
|
context.reporter.new(context).report_line(item).snippet,
|
66
|
-
Selections
|
66
|
+
Selections.select_line(item)
|
67
67
|
)
|
68
68
|
end
|
69
69
|
|
70
70
|
def self.non_section_element_not_found(context, copy)
|
71
71
|
ParseError.new(
|
72
|
-
context.messages
|
72
|
+
context.messages.non_section_element_not_found(copy[:line] + Enolib::HUMAN_INDEXING, copy[:template]),
|
73
73
|
context.reporter.new(context).report_line(copy).snippet,
|
74
|
-
Selections
|
74
|
+
Selections.select_line(copy)
|
75
75
|
)
|
76
76
|
end
|
77
77
|
|
@@ -81,23 +81,23 @@ module Enolib
|
|
81
81
|
reporter.indicate_line(super_section) if super_section[:type] != :document
|
82
82
|
|
83
83
|
ParseError.new(
|
84
|
-
context.messages
|
84
|
+
context.messages.section_hierarchy_layer_skip(section[:line] + Enolib::HUMAN_INDEXING),
|
85
85
|
reporter.snippet,
|
86
|
-
Selections
|
86
|
+
Selections.select_line(section)
|
87
87
|
)
|
88
88
|
end
|
89
89
|
|
90
90
|
def self.section_not_found(context, copy)
|
91
91
|
ParseError.new(
|
92
|
-
context.messages
|
92
|
+
context.messages.section_not_found(copy[:line] + Enolib::HUMAN_INDEXING, copy[:template]),
|
93
93
|
context.reporter.new(context).report_line(copy).snippet,
|
94
|
-
Selections
|
94
|
+
Selections.select_line(copy)
|
95
95
|
)
|
96
96
|
end
|
97
97
|
|
98
98
|
def self.unterminated_escaped_key(context, instruction, selection_column)
|
99
99
|
ParseError.new(
|
100
|
-
context.messages
|
100
|
+
context.messages.unterminated_escaped_key(instruction[:line] + Enolib::HUMAN_INDEXING),
|
101
101
|
context.reporter.new(context).report_line(instruction).snippet,
|
102
102
|
{
|
103
103
|
from: {
|
@@ -105,16 +105,16 @@ module Enolib
|
|
105
105
|
index: instruction[:ranges][:line][RANGE_BEGIN] + selection_column,
|
106
106
|
line: instruction[:line]
|
107
107
|
},
|
108
|
-
to: Selections
|
108
|
+
to: Selections.cursor(instruction, :line, RANGE_END)
|
109
109
|
}
|
110
110
|
)
|
111
111
|
end
|
112
112
|
|
113
113
|
def self.two_or_more_templates_found(context, copy, first_template, second_template)
|
114
114
|
ParseError.new(
|
115
|
-
context.messages
|
115
|
+
context.messages.two_or_more_templates_found(copy[:template]),
|
116
116
|
context.reporter.new(context).report_line(copy).question_line(first_template).question_line(second_template).snippet,
|
117
|
-
Selections
|
117
|
+
Selections.select_line(copy)
|
118
118
|
)
|
119
119
|
end
|
120
120
|
|
@@ -126,9 +126,9 @@ module Enolib
|
|
126
126
|
end
|
127
127
|
|
128
128
|
ParseError.new(
|
129
|
-
context.messages
|
129
|
+
context.messages.unterminated_multiline_field(field[:key], field[:line] + Enolib::HUMAN_INDEXING),
|
130
130
|
reporter.snippet,
|
131
|
-
Selections
|
131
|
+
Selections.select_line(field)
|
132
132
|
)
|
133
133
|
end
|
134
134
|
end
|
@@ -6,40 +6,40 @@ module Enolib
|
|
6
6
|
def self.comment_error(context, message, element)
|
7
7
|
ValidationError.new(
|
8
8
|
context.messages.comment_error(message),
|
9
|
-
context.reporter.new(context).report_comments(element).snippet
|
10
|
-
Selections
|
9
|
+
context.reporter.new(context).report_comments(element).snippet,
|
10
|
+
Selections.select_comments(element)
|
11
11
|
)
|
12
12
|
end
|
13
13
|
|
14
14
|
def self.element_error(context, message, element)
|
15
15
|
ValidationError.new(
|
16
16
|
message,
|
17
|
-
context.reporter.new(context).report_element(element).snippet
|
18
|
-
Selections
|
17
|
+
context.reporter.new(context).report_element(element).snippet,
|
18
|
+
Selections.select_element(element)
|
19
19
|
)
|
20
20
|
end
|
21
21
|
|
22
22
|
def self.key_error(context, message, element)
|
23
23
|
ValidationError.new(
|
24
24
|
context.messages.key_error(message),
|
25
|
-
context.reporter.new(context).report_line(element).snippet
|
26
|
-
Selections
|
25
|
+
context.reporter.new(context).report_line(element).snippet,
|
26
|
+
Selections.select_key(element)
|
27
27
|
)
|
28
28
|
end
|
29
29
|
|
30
30
|
def self.missing_comment(context, element)
|
31
31
|
ValidationError.new(
|
32
32
|
context.messages::MISSING_COMMENT,
|
33
|
-
context.reporter.new(context).report_line(element).snippet
|
34
|
-
Selections
|
33
|
+
context.reporter.new(context).report_line(element).snippet, # TODO: Question-tag an empty line before an element with missing comment
|
34
|
+
Selections.selection(element, :line, RANGE_BEGIN)
|
35
35
|
)
|
36
36
|
end
|
37
37
|
|
38
38
|
def self.missing_element(context, key, parent, message)
|
39
39
|
ValidationError.new(
|
40
40
|
key ? context.messages.send(message + '_with_key', key) : context.messages.const_get(message.upcase), # TODO: Solve the upcase rather through a different generated message type, e.g. method instead of constant
|
41
|
-
context.reporter.new(context).report_missing_element(parent).snippet
|
42
|
-
parent[:type] == :document ? Selections::DOCUMENT_BEGIN : Selections
|
41
|
+
context.reporter.new(context).report_missing_element(parent).snippet,
|
42
|
+
parent[:type] == :document ? Selections::DOCUMENT_BEGIN : Selections.selection(parent, :line, RANGE_END)
|
43
43
|
)
|
44
44
|
end
|
45
45
|
|
@@ -51,28 +51,30 @@ module Enolib
|
|
51
51
|
element[:type] == :multiline_field_begin
|
52
52
|
message = context.messages.missing_field_value(element[:key])
|
53
53
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
54
|
+
selection[:from] =
|
55
|
+
if element[:ranges].has_key?(:template)
|
56
|
+
Selections.cursor(element, :template, RANGE_END)
|
57
|
+
elsif element[:ranges].has_key?(:element_operator)
|
58
|
+
Selections.cursor(element, :element_operator, RANGE_END)
|
59
|
+
else
|
60
|
+
Selections.cursor(element, :line, RANGE_END)
|
61
|
+
end
|
61
62
|
elsif element[:type] == :fieldset_entry
|
62
63
|
message = context.messages.missing_fieldset_entry_value(element[:key])
|
63
|
-
selection[:from] = Selections
|
64
|
+
selection[:from] = Selections.cursor(element, :entry_operator, RANGE_END)
|
64
65
|
elsif element[:type] == :list_item
|
65
66
|
message = context.messages.missing_list_item_value(element[:parent][:key])
|
66
|
-
selection[:from] = Selections
|
67
|
+
selection[:from] = Selections.cursor(element, :item_operator, RANGE_END)
|
67
68
|
end
|
68
69
|
|
69
|
-
snippet = context.reporter.new(context).report_element(element).snippet
|
70
|
+
snippet = context.reporter.new(context).report_element(element).snippet
|
70
71
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
72
|
+
selection[:to] =
|
73
|
+
if element[:type] == :field && element.has_key?(:continuations)
|
74
|
+
Selections.cursor(element[:continuations].last, :line, RANGE_END)
|
75
|
+
else
|
76
|
+
Selections.cursor(element, :line, RANGE_END)
|
77
|
+
end
|
76
78
|
|
77
79
|
ValidationError.new(message, snippet, selection)
|
78
80
|
end
|
@@ -80,63 +82,64 @@ module Enolib
|
|
80
82
|
def self.unexpected_element(context, message, element)
|
81
83
|
ValidationError.new(
|
82
84
|
message || context.messages::UNEXPECTED_ELEMENT,
|
83
|
-
context.reporter.new(context).report_element(element).snippet
|
84
|
-
Selections
|
85
|
+
context.reporter.new(context).report_element(element).snippet,
|
86
|
+
Selections.select_element(element)
|
85
87
|
)
|
86
88
|
end
|
87
89
|
|
88
90
|
def self.unexpected_multiple_elements(context, key, elements, message)
|
89
91
|
ValidationError.new(
|
90
92
|
key ? context.messages.send(message + '_with_key', key) : context.messages.const_get(message.upcase), # TODO: Solve the upcase rather through a different generated message type, e.g. method instead of constant
|
91
|
-
context.reporter.new(context).report_elements(elements).snippet
|
92
|
-
Selections
|
93
|
+
context.reporter.new(context).report_elements(elements).snippet,
|
94
|
+
Selections.select_element(elements[0])
|
93
95
|
)
|
94
96
|
end
|
95
97
|
|
96
98
|
def self.unexpected_element_type(context, key, section, message)
|
97
99
|
ValidationError.new(
|
98
100
|
key ? context.messages.send(message + '_with_key', key) : context.messages.const_get(message.upcase), # TODO: Solve the upcase rather through a different generated message type, e.g. method instead of constant
|
99
|
-
context.reporter.new(context).report_element(section).snippet
|
100
|
-
Selections
|
101
|
+
context.reporter.new(context).report_element(section).snippet,
|
102
|
+
Selections.select_element(section)
|
101
103
|
)
|
102
104
|
end
|
103
105
|
|
104
106
|
def self.value_error(context, message, element)
|
105
107
|
if element.has_key?(:mirror)
|
106
|
-
snippet = context.reporter.new(context).report_line(element).snippet
|
108
|
+
snippet = context.reporter.new(context).report_line(element).snippet
|
107
109
|
select = select_key(element)
|
108
110
|
elsif element[:type] == :multiline_field_begin
|
109
111
|
if element.has_key?(:lines)
|
110
|
-
snippet = context.reporter.new(context).report_multiline_value(element).snippet
|
111
|
-
select = Selections
|
112
|
+
snippet = context.reporter.new(context).report_multiline_value(element).snippet
|
113
|
+
select = Selections.selection(element[:lines][0], :line, RANGE_BEGIN, element[:lines][-1], :line, RANGE_END)
|
112
114
|
else
|
113
|
-
snippet = context.reporter.new(context).report_element(element).snippet
|
114
|
-
select = Selections
|
115
|
+
snippet = context.reporter.new(context).report_element(element).snippet
|
116
|
+
select = Selections.selection(element, :line, RANGE_END)
|
115
117
|
end
|
116
118
|
else
|
117
|
-
snippet = context.reporter.new(context).report_element(element).snippet
|
118
|
-
select = {
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
119
|
+
snippet = context.reporter.new(context).report_element(element).snippet
|
120
|
+
select = {
|
121
|
+
from:
|
122
|
+
if element[:ranges].has_key?(:value)
|
123
|
+
Selections.cursor(element, :value, RANGE_BEGIN)
|
124
|
+
elsif element[:ranges].has_key?(:element_operator)
|
125
|
+
Selections.cursor(element, :element_operator, RANGE_END)
|
126
|
+
elsif element[:ranges].has_key?(:entry_operator)
|
127
|
+
Selections.cursor(element, :entry_operator, RANGE_END)
|
128
|
+
elsif element[:type] == :list_item
|
129
|
+
Selections.cursor(element, :item_operator, RANGE_END)
|
130
|
+
else
|
131
|
+
# TODO: Possibly never reached - think through state permutations
|
132
|
+
Selections.cursor(element, :line, RANGE_END)
|
133
|
+
end,
|
134
|
+
to:
|
135
|
+
if element.has_key?(:continuations)
|
136
|
+
Selections.cursor(element[:continuations][-1], :line, RANGE_END)
|
137
|
+
elsif element[:ranges].has_key?(:value)
|
138
|
+
Selections.cursor(element, :value, RANGE_END)
|
139
|
+
else
|
140
|
+
Selections.cursor(element, :line, RANGE_END)
|
141
|
+
end
|
142
|
+
}
|
140
143
|
end
|
141
144
|
|
142
145
|
ValidationError.new(context.messages.value_error(message), snippet, select)
|
data/lib/enolib/lookup.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
RANGE_BEGIN = 0
|
2
4
|
RANGE_END = 1
|
3
5
|
|
@@ -63,7 +65,6 @@ def check_fieldset_by_line(fieldset, line)
|
|
63
65
|
return false unless fieldset.has_key?(:entries) &&
|
64
66
|
line <= fieldset[:entries].last[:line]
|
65
67
|
|
66
|
-
|
67
68
|
fieldset[:entries].each do |entry|
|
68
69
|
return { element: entry, instruction: entry } if line == entry[:line]
|
69
70
|
return { element: fieldset, instruction: nil } if line < entry[:line]
|
@@ -190,13 +191,13 @@ module Enolib
|
|
190
191
|
match = nil
|
191
192
|
if index
|
192
193
|
if index < 0 || index > context.input.length
|
193
|
-
raise IndexError
|
194
|
+
raise IndexError, "You are trying to look up an index (#{index}) outside of the document's index range (0-#{context.input.length})"
|
194
195
|
end
|
195
196
|
|
196
197
|
match = check_in_section_by_index(context.document, index)
|
197
198
|
else
|
198
199
|
if line < 0 || line >= context.line_count
|
199
|
-
raise IndexError
|
200
|
+
raise IndexError, "You are trying to look up a line (#{line}) outside of the document's line range (0-#{context.line_count - 1})"
|
200
201
|
end
|
201
202
|
|
202
203
|
match = check_in_section_by_line(context.document, line)
|
data/lib/enolib/parser.rb
CHANGED
@@ -7,8 +7,8 @@ module Enolib
|
|
7
7
|
@depth = 0
|
8
8
|
@index = 0
|
9
9
|
@line = 0
|
10
|
-
@unresolved_non_section_elements =
|
11
|
-
@unresolved_sections =
|
10
|
+
@unresolved_non_section_elements = {}
|
11
|
+
@unresolved_sections = {}
|
12
12
|
end
|
13
13
|
|
14
14
|
def run
|
@@ -164,7 +164,7 @@ module Enolib
|
|
164
164
|
|
165
165
|
elsif match[Grammar::SPACED_LINE_CONTINUATION_OPERATOR_INDEX]
|
166
166
|
|
167
|
-
|
167
|
+
unless last_continuable_element
|
168
168
|
instruction = tokenize_error_context
|
169
169
|
raise Errors::Parsing.missing_element_for_continuation(@context, instruction)
|
170
170
|
end
|
@@ -197,7 +197,7 @@ module Enolib
|
|
197
197
|
|
198
198
|
elsif match[Grammar::DIRECT_LINE_CONTINUATION_OPERATOR_INDEX]
|
199
199
|
|
200
|
-
|
200
|
+
unless last_continuable_element
|
201
201
|
instruction = tokenize_error_context
|
202
202
|
raise Errors::Parsing.missing_element_for_continuation(@context, instruction)
|
203
203
|
end
|
@@ -292,8 +292,6 @@ module Enolib
|
|
292
292
|
instruction[:ranges][:copy_operator] = copy_operator_offset
|
293
293
|
end
|
294
294
|
|
295
|
-
@unresolved_sections = {} unless @unresolved_sections
|
296
|
-
|
297
295
|
if @unresolved_sections.has_key?(template)
|
298
296
|
@unresolved_sections[template][:targets].push(instruction)
|
299
297
|
else
|
@@ -381,9 +379,9 @@ module Enolib
|
|
381
379
|
length: terminator_match.end(0),
|
382
380
|
line: @line,
|
383
381
|
ranges: {
|
384
|
-
key:
|
382
|
+
key: terminator_match.offset(2),
|
385
383
|
line: [@index, terminator_match.end(0)],
|
386
|
-
multiline_field_operator: terminator_match.offset(1)
|
384
|
+
multiline_field_operator: terminator_match.offset(1)
|
387
385
|
},
|
388
386
|
type: :multiline_field_end
|
389
387
|
}
|
@@ -444,8 +442,6 @@ module Enolib
|
|
444
442
|
last_continuable_element = nil
|
445
443
|
last_non_section_element = instruction
|
446
444
|
|
447
|
-
@unresolved_non_section_elements = {} unless @unresolved_non_section_elements
|
448
|
-
|
449
445
|
if @unresolved_non_section_elements.has_key?(template)
|
450
446
|
@unresolved_non_section_elements[template][:targets].push(instruction)
|
451
447
|
else
|
@@ -461,15 +457,10 @@ module Enolib
|
|
461
457
|
end
|
462
458
|
end
|
463
459
|
|
464
|
-
|
465
|
-
@context.line_count = @line + 1
|
466
|
-
else
|
467
|
-
@context.line_count = @line
|
468
|
-
end
|
469
|
-
|
460
|
+
@context.line_count = @context.input[-1] == "\n" ? @line + 1 : @line
|
470
461
|
@context.meta.concat(comments) if comments
|
471
462
|
|
472
|
-
resolve
|
463
|
+
resolve unless @unresolved_non_section_elements.empty? && @unresolved_sections.empty?
|
473
464
|
end
|
474
465
|
|
475
466
|
private
|
@@ -529,24 +520,25 @@ module Enolib
|
|
529
520
|
merge_map = {}
|
530
521
|
|
531
522
|
section[:elements].each do |section_element|
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
523
|
+
merge_map[section_element[:key]] =
|
524
|
+
if section_element[:type] != :section || merge_map.has_key?(section_element[:key])
|
525
|
+
false # non-mergable (no section or multiple instructions with same key)
|
526
|
+
else
|
527
|
+
{ section: section_element }
|
528
|
+
end
|
537
529
|
end
|
538
530
|
|
539
531
|
template[:elements].each do |section_element|
|
540
|
-
|
541
|
-
merger = merge_map[section_element[:key]]
|
532
|
+
next unless merge_map.has_key?(section_element[:key])
|
542
533
|
|
543
|
-
|
534
|
+
merger = merge_map[section_element[:key]]
|
544
535
|
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
536
|
+
next unless merger
|
537
|
+
|
538
|
+
if section_element[:type] != :section || merger.has_key?(:template)
|
539
|
+
merge_map[section_element[:key]] = false # non-mergable (no section or multiple template instructions with same key)
|
540
|
+
else
|
541
|
+
merger[:template] = section_element
|
550
542
|
end
|
551
543
|
end
|
552
544
|
|
@@ -692,7 +684,7 @@ module Enolib
|
|
692
684
|
else
|
693
685
|
instruction = {
|
694
686
|
line: @line,
|
695
|
-
ranges: { line: [@index, @context.input.length]}
|
687
|
+
ranges: { line: [@index, @context.input.length] }
|
696
688
|
}
|
697
689
|
|
698
690
|
instruction[:type] = :unparsed if first_instruction
|
@@ -86,10 +86,10 @@ module Enolib
|
|
86
86
|
end
|
87
87
|
|
88
88
|
def markup(gutter, content, tag_class = '')
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
89
|
+
"<div class=\"eno-report-line #{tag_class}\">" \
|
90
|
+
"<div class=\"eno-report-gutter\">#{gutter.rjust(10)}</div>" \
|
91
|
+
"<div class=\"eno-report-content\">#{escape(content)}</div>" \
|
92
|
+
'</div>'
|
93
93
|
end
|
94
94
|
|
95
95
|
def print
|
@@ -31,7 +31,7 @@ module Enolib
|
|
31
31
|
|
32
32
|
def report_element(element)
|
33
33
|
@snippet[element[:line]] = :emphasize
|
34
|
-
|
34
|
+
tag_children(element, :indicate)
|
35
35
|
|
36
36
|
self
|
37
37
|
end
|
@@ -39,7 +39,7 @@ module Enolib
|
|
39
39
|
def report_elements(elements)
|
40
40
|
elements.each do |element|
|
41
41
|
@snippet[element[:line]] = :emphasize
|
42
|
-
|
42
|
+
tag_children(element, :indicate)
|
43
43
|
end
|
44
44
|
|
45
45
|
self
|
@@ -65,7 +65,7 @@ module Enolib
|
|
65
65
|
if parent[:type] == :section
|
66
66
|
tag_section(parent, :question, false)
|
67
67
|
else
|
68
|
-
|
68
|
+
tag_children(parent, :question)
|
69
69
|
end
|
70
70
|
|
71
71
|
self
|
@@ -139,10 +139,10 @@ module Enolib
|
|
139
139
|
|
140
140
|
@index[item[:line]] = item
|
141
141
|
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
142
|
+
next unless item.has_key?(:continuations)
|
143
|
+
|
144
|
+
item[:continuations].each do |continuation|
|
145
|
+
@index[continuation[:line]] = continuation
|
146
146
|
end
|
147
147
|
end
|
148
148
|
end
|
@@ -153,10 +153,10 @@ module Enolib
|
|
153
153
|
|
154
154
|
@index[entry[:line]] = entry
|
155
155
|
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
156
|
+
next unless entry.has_key?(:continuations)
|
157
|
+
|
158
|
+
entry[:continuations].each do |continuation|
|
159
|
+
@index[continuation[:line]] = continuation
|
160
160
|
end
|
161
161
|
end
|
162
162
|
end
|
@@ -209,14 +209,15 @@ module Enolib
|
|
209
209
|
scan_line
|
210
210
|
end
|
211
211
|
|
212
|
-
def
|
213
|
-
|
212
|
+
def tag_children(element, tag)
|
213
|
+
case element[:type]
|
214
|
+
when :field, :list_item, :fieldset_entry
|
214
215
|
return tag_continuations(element, tag)
|
215
|
-
|
216
|
+
when :list
|
216
217
|
return tag_continuables(element, :items, tag)
|
217
|
-
|
218
|
+
when :fieldset
|
218
219
|
return tag_continuables(element, :entries, tag)
|
219
|
-
|
220
|
+
when :multiline_field_begin
|
220
221
|
if element.has_key?(:lines)
|
221
222
|
element[:lines].each do |line|
|
222
223
|
@snippet[line[:line]] = tag
|
@@ -226,30 +227,29 @@ module Enolib
|
|
226
227
|
if element.has_key?(:end)
|
227
228
|
@snippet[element[:end][:line]] = tag
|
228
229
|
return element[:end][:line] + 1
|
229
|
-
elsif element.has_key?(:lines)
|
230
|
-
return element[:lines][-1][:line] + 1
|
231
|
-
else
|
232
|
-
return element[:line] + 1
|
233
230
|
end
|
234
|
-
|
231
|
+
|
232
|
+
return element[:lines][-1][:line] + 1 if element.has_key?(:lines)
|
233
|
+
return element[:line] + 1
|
234
|
+
when :section
|
235
235
|
return tag_section(element, tag)
|
236
236
|
end
|
237
237
|
end
|
238
238
|
|
239
|
-
def tag_section(section, tag, recursive=true)
|
239
|
+
def tag_section(section, tag, recursive = true)
|
240
240
|
scan_line = section[:line] + 1
|
241
241
|
|
242
242
|
section[:elements].each do |element|
|
243
243
|
while scan_line < element[:line]
|
244
|
-
@snippet[scan_line] =
|
244
|
+
@snippet[scan_line] = tag
|
245
245
|
scan_line += 1
|
246
246
|
end
|
247
247
|
|
248
248
|
break if element[:type] == :section && !recursive
|
249
249
|
|
250
|
-
@snippet[element[:line]] =
|
250
|
+
@snippet[element[:line]] = tag
|
251
251
|
|
252
|
-
scan_line =
|
252
|
+
scan_line = tag_children(element, tag)
|
253
253
|
end
|
254
254
|
|
255
255
|
scan_line
|
@@ -18,14 +18,14 @@ INDICATORS = {
|
|
18
18
|
emphasize: '>',
|
19
19
|
indicate: '*',
|
20
20
|
question: '?'
|
21
|
-
}
|
21
|
+
}.freeze
|
22
22
|
|
23
23
|
GUTTER_STYLE = {
|
24
24
|
display: BRIGHT_BLACK_BACKGROUND,
|
25
25
|
emphasize: BLACK + BRIGHT_RED_BACKGROUND,
|
26
26
|
indicate: BLACK + WHITE_BACKGROUND,
|
27
27
|
question: BLACK + WHITE_BACKGROUND
|
28
|
-
}
|
28
|
+
}.freeze
|
29
29
|
|
30
30
|
RANGE_STYLE = {
|
31
31
|
'element_operator': WHITE,
|
@@ -42,7 +42,7 @@ RANGE_STYLE = {
|
|
42
42
|
'key': BOLD + BRIGHT_WHITE,
|
43
43
|
'template': BOLD + BRIGHT_WHITE,
|
44
44
|
'value': DIM + WHITE
|
45
|
-
}
|
45
|
+
}.freeze
|
46
46
|
|
47
47
|
module Enolib
|
48
48
|
class TerminalReporter < Reporter
|
@@ -76,12 +76,12 @@ module Enolib
|
|
76
76
|
|
77
77
|
content = ''
|
78
78
|
if instruction
|
79
|
-
if instruction[:type] == :comment
|
79
|
+
if instruction[:type] == :comment || instruction[:type] == :unparsed
|
80
80
|
content = BRIGHT_BLACK + @context.input[instruction[:ranges][:line][RANGE_BEGIN]...instruction[:ranges][:line][RANGE_END]] + RESET
|
81
81
|
else
|
82
82
|
content = @context.input[instruction[:ranges][:line][RANGE_BEGIN]...instruction[:ranges][:line][RANGE_END]]
|
83
83
|
|
84
|
-
instruction[:ranges].sort_by { |
|
84
|
+
instruction[:ranges].sort_by { |_type, range| range[0] }.reverse_each do |type, range|
|
85
85
|
next if type == :line
|
86
86
|
|
87
87
|
before = content[0...range[RANGE_BEGIN] - instruction[:ranges][:line][RANGE_BEGIN]]
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: enolib
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.
|
4
|
+
version: 0.5.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Simon Repp
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-05-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: deep-cover
|
@@ -52,6 +52,34 @@ dependencies:
|
|
52
52
|
- - '='
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: 0.1.0
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rubocop
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rubocop-performance
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
55
83
|
description: The cross-language eno standard library
|
56
84
|
email: simon@fdpl.io
|
57
85
|
executables: []
|