enolib 0.5.0 → 0.5.2
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|