rtext 0.7.0 → 0.8.0.pre1
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 +7 -0
- data/CHANGELOG +4 -0
- data/RText_Protocol +8 -0
- data/Rakefile +1 -1
- data/lib/rtext/context_builder.rb +4 -2
- data/lib/rtext/frontend/context.rb +64 -32
- data/lib/rtext/language.rb +33 -4
- data/lib/rtext/parser.rb +12 -4
- data/lib/rtext/serializer.rb +29 -6
- data/lib/rtext/tokenizer.rb +12 -2
- data/test/context_builder_test.rb +8 -0
- data/test/frontend/context_test.rb +205 -0
- data/test/instantiator_test.rb +122 -31
- data/test/integration/backend.out +12 -12
- data/test/integration/frontend.log +2699 -0
- data/test/integration/model/test_metamodel.ect +8 -2
- data/test/integration/test.rb +247 -19
- data/test/link_detector_test.rb +11 -0
- data/test/rtext_test.rb +1 -0
- data/test/serializer_test.rb +388 -0
- data/test/tokenizer_test.rb +16 -0
- metadata +11 -14
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: dfdb25504469a30431bda0d53f6ed450809ca7ba
|
4
|
+
data.tar.gz: 93de80eedec085dfb25d6fb1d5f134236f692c57
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: dfe6ce9af736e941413be2ec99c4ad301ded2192affc6334dcf80eb795b2b9a98370b1174c385f852f0ba0788e9d2cf6db1130955eda6e845eca4758f5580e8d
|
7
|
+
data.tar.gz: 80f81b162444e33c346f5edacd961828c2311c9d99f1f404a94a62cac40a9f2dd6403608e7555da8f60210918f102cc92d308861dcf06ce871cdc2b0b00be79b
|
data/CHANGELOG
CHANGED
data/RText_Protocol
CHANGED
@@ -493,3 +493,11 @@ context in the frontend simple and the amount of data transmitted to the backend
|
|
493
493
|
It's also a way to keep the parsing time of the context low in the backend and thus to minimize
|
494
494
|
the user noticable delay.
|
495
495
|
|
496
|
+
In case of line breaks, the frontend is responsible to join the lines before sending the
|
497
|
+
context information. For commands which use a column position, the position is the position
|
498
|
+
within the joined line. This means that, when sending a command, the frontend must convert
|
499
|
+
the column position in the broken line into the new position in the joined line.
|
500
|
+
When reading back column information in a response (e.g. link command) the frontend must
|
501
|
+
convert the column position in the joined line into a position in the respective broken
|
502
|
+
fragment of a line.
|
503
|
+
|
data/Rakefile
CHANGED
@@ -103,8 +103,10 @@ module ContextBuilder
|
|
103
103
|
problem = nil
|
104
104
|
line = context_lines.last
|
105
105
|
if line =~ /\{\s*$/
|
106
|
-
# remove curly brace from last line, required for correct counting of num_elements
|
107
|
-
line
|
106
|
+
# remove curly brace from last line, required for correct counting of num_elements;
|
107
|
+
# also make sure that there is whitespace at the end of line, otherwise a word
|
108
|
+
# might get removed as "just being completed"
|
109
|
+
line.sub!(/\{\s*$/," ")
|
108
110
|
problem = :after_curly
|
109
111
|
end
|
110
112
|
|
@@ -1,10 +1,14 @@
|
|
1
1
|
module RText
|
2
2
|
module Frontend
|
3
3
|
|
4
|
-
|
4
|
+
class Context
|
5
5
|
|
6
|
-
# lines
|
7
|
-
|
6
|
+
# lines: all lines from the beginning up to and including the current line
|
7
|
+
# pos: position of the cursor in the last lines
|
8
|
+
# returns the extracted lines and the new position in the last line
|
9
|
+
def extract(lines, pos)
|
10
|
+
lines = filter_lines(lines)
|
11
|
+
lines, new_pos = join_lines(lines, pos)
|
8
12
|
non_ignored_lines = 0
|
9
13
|
array_nesting = 0
|
10
14
|
block_nesting = 0
|
@@ -14,39 +18,67 @@ def self.extract(lines)
|
|
14
18
|
if i == 0
|
15
19
|
result.unshift(l)
|
16
20
|
else
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
block_nesting -= 1
|
26
|
-
elsif block_nesting == 0
|
27
|
-
result.unshift(l)
|
28
|
-
last_element_line = non_ignored_lines
|
29
|
-
end
|
30
|
-
when "}"
|
31
|
-
block_nesting += 1
|
32
|
-
when "["
|
33
|
-
if array_nesting > 0
|
34
|
-
array_nesting -= 1
|
35
|
-
elsif array_nesting == 0
|
36
|
-
result.unshift(l)
|
37
|
-
end
|
38
|
-
when "]"
|
39
|
-
array_nesting += 1
|
40
|
-
when ":"
|
41
|
-
# lable directly above element
|
42
|
-
if non_ignored_lines == last_element_line + 1
|
43
|
-
result.unshift(l)
|
44
|
-
end
|
21
|
+
non_ignored_lines += 1
|
22
|
+
case l.strip[-1..-1]
|
23
|
+
when "{"
|
24
|
+
if block_nesting > 0
|
25
|
+
block_nesting -= 1
|
26
|
+
elsif block_nesting == 0
|
27
|
+
result.unshift(l)
|
28
|
+
last_element_line = non_ignored_lines
|
45
29
|
end
|
30
|
+
when "}"
|
31
|
+
block_nesting += 1
|
32
|
+
when "["
|
33
|
+
if array_nesting > 0
|
34
|
+
array_nesting -= 1
|
35
|
+
elsif array_nesting == 0
|
36
|
+
result.unshift(l)
|
37
|
+
end
|
38
|
+
when "]"
|
39
|
+
array_nesting += 1
|
40
|
+
when ":"
|
41
|
+
# lable directly above element
|
42
|
+
if non_ignored_lines == last_element_line + 1
|
43
|
+
result.unshift(l)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
[result, new_pos]
|
49
|
+
end
|
50
|
+
|
51
|
+
def filter_lines(lines)
|
52
|
+
lines.reject { |l|
|
53
|
+
ls = l.strip
|
54
|
+
ls[0..0] == "@" || ls[0..0] == "#"
|
55
|
+
}
|
56
|
+
end
|
57
|
+
|
58
|
+
# when joining two lines, all whitespace after the last character of the first line is removed
|
59
|
+
# (after , and [); however whitespace at the end of the last of several joined lines is preserved;
|
60
|
+
# this way the context is correct even if the cursor is after the end of the last line
|
61
|
+
# (i.e. with whitespace after the last non-whitespace character)
|
62
|
+
def join_lines(lines, pos)
|
63
|
+
outlines = []
|
64
|
+
while lines.size > 0
|
65
|
+
outlines << lines.shift
|
66
|
+
while lines.size > 0 &&
|
67
|
+
(outlines.last =~ /,\s*$/ ||
|
68
|
+
(outlines.last =~ /\[\s*$/ && outlines.last =~ /,/) ||
|
69
|
+
(lines.first =~ /^\s*\]/ && outlines.last =~ /,/))
|
70
|
+
outlines.last.rstrip!
|
71
|
+
l = lines.shift
|
72
|
+
if lines.size == 0
|
73
|
+
# strip only left part, the prefix might have whitespace on the
|
74
|
+
# right hand side which is relevant for the position
|
75
|
+
non_ws_prefix = l[0..pos-1].lstrip
|
76
|
+
pos = outlines.last.size + non_ws_prefix.size
|
46
77
|
end
|
78
|
+
outlines.last.concat(l.lstrip)
|
47
79
|
end
|
48
80
|
end
|
49
|
-
|
81
|
+
[outlines, pos]
|
50
82
|
end
|
51
83
|
|
52
84
|
end
|
data/lib/rtext/language.rb
CHANGED
@@ -42,6 +42,16 @@ class Language
|
|
42
42
|
# (in sprintf syntax) which will be used by the serializer for integers and floats.
|
43
43
|
# default: if not present or the proc returns nil, then #to_s is used
|
44
44
|
#
|
45
|
+
# :newline_arguments
|
46
|
+
# a Proc which receives an EClass and should return the names of this EClass's features
|
47
|
+
# which are to be serialized after adding a line break.
|
48
|
+
# default: no attributes on new lines
|
49
|
+
#
|
50
|
+
# :newline_arrays
|
51
|
+
# a Proc which receives an EClass and should return the names of this EClass's features
|
52
|
+
# for which the array elements of their values are to be serialized after a line break.
|
53
|
+
# default: no attributes on new lines
|
54
|
+
#
|
45
55
|
# :reference_regexp
|
46
56
|
# a Regexp which is used by the tokenizer for identifying references
|
47
57
|
# it must only match at the beginning of a string, i.e. it should start with \A
|
@@ -53,20 +63,25 @@ class Language
|
|
53
63
|
# :identifier_provider
|
54
64
|
# a Proc which receives an element, its containing element, the feature through which the
|
55
65
|
# element is referenced and the index position of the reference within the feature's values.
|
56
|
-
# the latter 3
|
57
|
-
# the identifier must be unique for the element unless
|
66
|
+
# the latter 3 arguments may be nil. it should return the element's identifier as a string.
|
67
|
+
# the identifier must be unique for the element unless :per_type_identifier is set to true,
|
58
68
|
# in which case they must be unique for each element of the same type.
|
59
69
|
# identifiers may be relative to the given containing element, depending on the given
|
60
70
|
# feature and index position. in this case a globally unique
|
61
|
-
# identifier must be
|
71
|
+
# identifier must be reconstructed by the proc specified using the :reference_qualifier option.
|
62
72
|
# if the containing element is nil, the identifier returned must be globally unique.
|
63
|
-
# default: identifiers calculated by QualifiedNameProvider
|
73
|
+
# default: identifiers calculated by QualifiedNameProvider using the attribute provided using
|
74
|
+
# the :attribute_name option
|
64
75
|
# in this case options to QualifiedNameProvider may be provided and will be passed through
|
65
76
|
#
|
66
77
|
# :per_type_identifier
|
67
78
|
# if set to true, identifiers may be reused for elements of different type
|
68
79
|
# default: false
|
69
80
|
#
|
81
|
+
# :attribute_name
|
82
|
+
# the name of the attribute used to calculate identifiers by the default identifier provider
|
83
|
+
# default: "name"
|
84
|
+
#
|
70
85
|
# :reference_qualifier
|
71
86
|
# a Proc which receives RGen unresolved references and either a FragmentedModel or a ModelFragment.
|
72
87
|
# it should modify the unresolved references' targetIdentifiers to make them globally unique.
|
@@ -142,6 +157,8 @@ class Language
|
|
142
157
|
@unquoted_arguments = options[:unquoted_arguments]
|
143
158
|
@labeled_containments = options[:labeled_containments]
|
144
159
|
@argument_format_provider = options[:argument_format_provider]
|
160
|
+
@newline_arguments = options[:newline_arguments]
|
161
|
+
@newline_arrays = options[:newline_arrays]
|
145
162
|
@root_classes = options[:root_classes] || default_root_classes(root_epackage)
|
146
163
|
command_name_provider = options[:command_name_provider] || proc{|c| c.name}
|
147
164
|
setup_commands(root_epackage, command_name_provider)
|
@@ -232,6 +249,16 @@ class Language
|
|
232
249
|
@argument_format_provider && @argument_format_provider.call(feature)
|
233
250
|
end
|
234
251
|
|
252
|
+
def newline_argument?(clazz, feature)
|
253
|
+
return false unless @newline_arguments
|
254
|
+
@newline_arguments.call(clazz).include?(feature.name)
|
255
|
+
end
|
256
|
+
|
257
|
+
def newline_array?(clazz, feature)
|
258
|
+
return false unless @newline_arrays
|
259
|
+
@newline_arrays.call(clazz).include?(feature.name)
|
260
|
+
end
|
261
|
+
|
235
262
|
def concrete_types(clazz)
|
236
263
|
([clazz] + clazz.eAllSubTypes).select{|c| !c.abstract}
|
237
264
|
end
|
@@ -312,6 +339,8 @@ class Language
|
|
312
339
|
:labled_arguments,
|
313
340
|
:unquoted?,
|
314
341
|
:labeled_containment?,
|
342
|
+
:newline_argument?,
|
343
|
+
:newline_array?,
|
315
344
|
:argument_format,
|
316
345
|
:concrete_types,
|
317
346
|
:containments_by_target_type,
|
data/lib/rtext/parser.rb
CHANGED
@@ -130,7 +130,10 @@ class Parser
|
|
130
130
|
def parse_argument_list(arg_list)
|
131
131
|
first = true
|
132
132
|
while (AnyValue + [",", "[", :label, :error]).include?(next_token_kind)
|
133
|
-
|
133
|
+
unless first
|
134
|
+
success = consume(",")
|
135
|
+
consume(:newline) if success && next_token_kind == :newline
|
136
|
+
end
|
134
137
|
first = false
|
135
138
|
parse_argument(arg_list)
|
136
139
|
end
|
@@ -155,13 +158,18 @@ class Parser
|
|
155
158
|
|
156
159
|
def parse_argument_value_list
|
157
160
|
consume("[")
|
161
|
+
consume(:newline) if next_token_kind == :newline
|
158
162
|
first = true
|
159
163
|
result = []
|
160
164
|
while (AnyValue + [",", :error]).include?(next_token_kind)
|
161
|
-
|
165
|
+
unless first
|
166
|
+
success = consume(",")
|
167
|
+
consume(:newline) if success && next_token_kind == :newline
|
168
|
+
end
|
162
169
|
first = false
|
163
170
|
result << parse_value
|
164
171
|
end
|
172
|
+
consume(:newline) if next_token_kind == :newline && next_token_kind(1) == "]"
|
165
173
|
consume("]")
|
166
174
|
result
|
167
175
|
end
|
@@ -172,8 +180,8 @@ class Parser
|
|
172
180
|
consume(*AnyValue)
|
173
181
|
end
|
174
182
|
|
175
|
-
def next_token_kind
|
176
|
-
@tokens
|
183
|
+
def next_token_kind(idx=0)
|
184
|
+
@tokens[idx] && @tokens[idx].kind
|
177
185
|
end
|
178
186
|
|
179
187
|
def discard_until(kind)
|
data/lib/rtext/serializer.rb
CHANGED
@@ -72,13 +72,24 @@ class Serializer
|
|
72
72
|
args = []
|
73
73
|
@lang.unlabled_arguments(clazz).each do |f|
|
74
74
|
values = serialize_values(element, f)
|
75
|
-
args << values if values
|
75
|
+
args << [f, values] if values
|
76
76
|
end
|
77
77
|
@lang.labled_arguments(clazz).each do |f|
|
78
78
|
values = serialize_values(element, f)
|
79
|
-
args << "#{f.name}: #{values}" if values
|
79
|
+
args << [f, "#{f.name}: #{values}"] if values
|
80
|
+
end
|
81
|
+
newline_arguments = false
|
82
|
+
args.each_with_index do |arg, index|
|
83
|
+
if @lang.newline_argument?(clazz, arg[0])
|
84
|
+
headline += " \\" if index == 0
|
85
|
+
headline += "\n" + @lang.indent_string * (@indent + 1)
|
86
|
+
newline_arguments = true
|
87
|
+
else
|
88
|
+
headline += " "
|
89
|
+
end
|
90
|
+
headline += arg[1]
|
91
|
+
headline += "," unless index == args.size-1
|
80
92
|
end
|
81
|
-
headline += " "+args.join(", ") if args.size > 0
|
82
93
|
contained_elements = {}
|
83
94
|
@lang.containments(clazz).each do |f|
|
84
95
|
contained_elements[f] = element.getGenericAsArray(f.name)
|
@@ -87,6 +98,10 @@ class Serializer
|
|
87
98
|
headline += " {"
|
88
99
|
write(headline)
|
89
100
|
iinc
|
101
|
+
# additional indentation needed if there are arguments on separate lines;
|
102
|
+
# note that this increment doesn't affect indentation of features of this element
|
103
|
+
# that have array values, because they have already been formatted in serialize_values
|
104
|
+
iinc if newline_arguments
|
90
105
|
@lang.containments(clazz).each do |f|
|
91
106
|
childs = contained_elements[f]
|
92
107
|
if childs.size > 0
|
@@ -111,6 +126,7 @@ class Serializer
|
|
111
126
|
end
|
112
127
|
end
|
113
128
|
idec
|
129
|
+
idec if newline_arguments
|
114
130
|
write("}")
|
115
131
|
else
|
116
132
|
write(headline)
|
@@ -173,10 +189,17 @@ class Serializer
|
|
173
189
|
result << @lang.identifier_provider.call(v, element, feature, index)
|
174
190
|
end
|
175
191
|
end
|
176
|
-
if result.size > 1
|
177
|
-
|
192
|
+
if result.size > 1
|
193
|
+
if @lang.newline_array?(element.class.ecore, feature)
|
194
|
+
# inside an array, indent two steps further than the command
|
195
|
+
"[\n" + @lang.indent_string * (@indent + 2) +
|
196
|
+
result.join(",\n" + @lang.indent_string * (@indent + 2)) +
|
197
|
+
"\n" + @lang.indent_string * (@indent + 1) + "]"
|
198
|
+
else
|
199
|
+
"[#{result.join(", ")}]"
|
200
|
+
end
|
178
201
|
elsif result.size == 1
|
179
|
-
result.first
|
202
|
+
result.first
|
180
203
|
else
|
181
204
|
nil
|
182
205
|
end
|
data/lib/rtext/tokenizer.rb
CHANGED
@@ -10,6 +10,7 @@ module Tokenizer
|
|
10
10
|
def tokenize(str, reference_regexp, options={})
|
11
11
|
result = []
|
12
12
|
on_command_token_proc = options[:on_command_token]
|
13
|
+
linebreak = false
|
13
14
|
str.split(/\r?\n/).each_with_index do |str, idx|
|
14
15
|
idx += 1
|
15
16
|
if idx == 1
|
@@ -27,7 +28,12 @@ module Tokenizer
|
|
27
28
|
end
|
28
29
|
else
|
29
30
|
col = 1
|
30
|
-
|
31
|
+
if linebreak
|
32
|
+
# do not regard as the first token, if previous line ended in a linebreak
|
33
|
+
linebreak = false
|
34
|
+
else
|
35
|
+
first_token_in_line = true
|
36
|
+
end
|
31
37
|
until str.empty?
|
32
38
|
whitespace = false
|
33
39
|
case str
|
@@ -94,6 +100,10 @@ module Tokenizer
|
|
94
100
|
col += $&.size
|
95
101
|
whitespace = true
|
96
102
|
# ignore
|
103
|
+
when /\A\\\s*\Z/
|
104
|
+
str = $'
|
105
|
+
linebreak = true
|
106
|
+
# ignore
|
97
107
|
when /\A<%((?:(?!%>).)*)%>/, /\A<([^>]*)>/
|
98
108
|
str = $'
|
99
109
|
result << Token.new(:generic, RText::Generic.new($1), idx, col, col+$&.size-1)
|
@@ -107,7 +117,7 @@ module Tokenizer
|
|
107
117
|
end
|
108
118
|
end
|
109
119
|
result << Token.new(:newline, nil, idx) \
|
110
|
-
unless result.empty? || result.last.kind == :newline
|
120
|
+
unless linebreak || result.empty? || result.last.kind == :newline
|
111
121
|
end
|
112
122
|
result
|
113
123
|
end
|
@@ -397,6 +397,14 @@ TestNode {|
|
|
397
397
|
assert(c.element.is_a?(TestMM::TestNode))
|
398
398
|
end
|
399
399
|
|
400
|
+
def test_root_after_curly_no_ws
|
401
|
+
c = build_context TestMM, <<-END
|
402
|
+
TestNode{|
|
403
|
+
END
|
404
|
+
assert_context c, :prefix => "", :feature => nil, :in_array => false, :in_block => false, :problem => :after_curly
|
405
|
+
assert(c.element.is_a?(TestMM::TestNode))
|
406
|
+
end
|
407
|
+
|
400
408
|
def test_in_cmd_after_cmd
|
401
409
|
c = build_context TestMM, <<-END
|
402
410
|
TestNode text: a {
|
@@ -0,0 +1,205 @@
|
|
1
|
+
$:.unshift File.join(File.dirname(__FILE__),"..","..","lib")
|
2
|
+
|
3
|
+
require 'test/unit'
|
4
|
+
require 'rtext/frontend/context'
|
5
|
+
|
6
|
+
class ContextTest < Test::Unit::TestCase
|
7
|
+
|
8
|
+
def test_simple
|
9
|
+
assert_context(
|
10
|
+
%Q(
|
11
|
+
A {
|
12
|
+
B {
|
13
|
+
|F bla
|
14
|
+
),
|
15
|
+
%Q(
|
16
|
+
A {
|
17
|
+
B {
|
18
|
+
C a1: v1, a2: "v2"
|
19
|
+
D {
|
20
|
+
E a1: 5
|
21
|
+
}
|
22
|
+
|F bla
|
23
|
+
))
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_child_label
|
27
|
+
assert_context(
|
28
|
+
%Q(
|
29
|
+
A {
|
30
|
+
sub:
|
31
|
+
B {
|
32
|
+
F bla|
|
33
|
+
),
|
34
|
+
%Q(
|
35
|
+
A {
|
36
|
+
sub:
|
37
|
+
B {
|
38
|
+
C a1: v1, a2: "v2"
|
39
|
+
D {
|
40
|
+
E a1: 5
|
41
|
+
}
|
42
|
+
F bla|
|
43
|
+
))
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_child_label_array
|
47
|
+
assert_context(
|
48
|
+
%Q(
|
49
|
+
A {
|
50
|
+
sub: [
|
51
|
+
B {
|
52
|
+
F| bla
|
53
|
+
),
|
54
|
+
%Q(
|
55
|
+
A {
|
56
|
+
sub: [
|
57
|
+
B {
|
58
|
+
C
|
59
|
+
}
|
60
|
+
B {
|
61
|
+
C a1: v1, a2: "v2"
|
62
|
+
D {
|
63
|
+
E a1: 5
|
64
|
+
}
|
65
|
+
F| bla
|
66
|
+
))
|
67
|
+
end
|
68
|
+
|
69
|
+
def test_ignore_child_lables
|
70
|
+
assert_context(
|
71
|
+
%Q(
|
72
|
+
A {
|
73
|
+
B {
|
74
|
+
F bl|a
|
75
|
+
),
|
76
|
+
%Q(
|
77
|
+
A {
|
78
|
+
B {
|
79
|
+
sub:
|
80
|
+
C a1: v1, a2: "v2"
|
81
|
+
sub2: [
|
82
|
+
D {
|
83
|
+
E a1: 5
|
84
|
+
}
|
85
|
+
]
|
86
|
+
F bl|a
|
87
|
+
))
|
88
|
+
end
|
89
|
+
|
90
|
+
def test_linebreak
|
91
|
+
assert_context(
|
92
|
+
%Q(
|
93
|
+
A {
|
94
|
+
B {
|
95
|
+
C name,a1: v1,a2: "v2"|
|
96
|
+
),
|
97
|
+
%Q(
|
98
|
+
A {
|
99
|
+
B {
|
100
|
+
C name,
|
101
|
+
a1: v1,
|
102
|
+
a2: "v2"|
|
103
|
+
))
|
104
|
+
end
|
105
|
+
|
106
|
+
def test_linebreak_arg_array
|
107
|
+
assert_context(
|
108
|
+
%Q(
|
109
|
+
A {
|
110
|
+
B {
|
111
|
+
C name,a1: [v1,v2],a2: |5
|
112
|
+
),
|
113
|
+
%Q(
|
114
|
+
A {
|
115
|
+
B {
|
116
|
+
C name,
|
117
|
+
a1: [
|
118
|
+
v1,
|
119
|
+
v2
|
120
|
+
],
|
121
|
+
a2: |5
|
122
|
+
))
|
123
|
+
end
|
124
|
+
|
125
|
+
def test_linebreak_empty_last_line
|
126
|
+
assert_context(
|
127
|
+
%Q(
|
128
|
+
A {
|
129
|
+
B name,|
|
130
|
+
),
|
131
|
+
%Q(
|
132
|
+
A {
|
133
|
+
B name,
|
134
|
+
|
|
135
|
+
))
|
136
|
+
end
|
137
|
+
|
138
|
+
def test_linebreak_empty_last_line2
|
139
|
+
assert_context(
|
140
|
+
%Q(
|
141
|
+
A {
|
142
|
+
B name,|
|
143
|
+
),
|
144
|
+
%Q(
|
145
|
+
A {
|
146
|
+
B name,
|
147
|
+
|
|
148
|
+
))
|
149
|
+
end
|
150
|
+
|
151
|
+
def test_linebreak_empty_lines
|
152
|
+
assert_context(
|
153
|
+
%Q(
|
154
|
+
A {
|
155
|
+
B name,a1: |
|
156
|
+
),
|
157
|
+
%Q(
|
158
|
+
A {
|
159
|
+
B name,
|
160
|
+
|
161
|
+
a1: |
|
162
|
+
))
|
163
|
+
end
|
164
|
+
|
165
|
+
def test_comment_annotation
|
166
|
+
assert_context(
|
167
|
+
%Q(
|
168
|
+
A {
|
169
|
+
B {
|
170
|
+
|F bla
|
171
|
+
),
|
172
|
+
%Q(
|
173
|
+
A {
|
174
|
+
# bla
|
175
|
+
B {
|
176
|
+
C a1: v1, a2: "v2"
|
177
|
+
# bla
|
178
|
+
D {
|
179
|
+
E a1: 5
|
180
|
+
}
|
181
|
+
@ anno
|
182
|
+
|F bla
|
183
|
+
))
|
184
|
+
end
|
185
|
+
|
186
|
+
def assert_context(expected, text)
|
187
|
+
# remove first and last lines
|
188
|
+
# these are empty because of the use of %Q
|
189
|
+
exp_lines = expected.split("\n")[1..-2]
|
190
|
+
exp_col = exp_lines.last.index("|")
|
191
|
+
exp_lines.last.sub!("|","")
|
192
|
+
in_lines = text.split("\n")[1..-2]
|
193
|
+
in_col = in_lines.last.index("|")
|
194
|
+
in_lines.last.sub!("|","")
|
195
|
+
ctx = RText::Frontend::Context.new
|
196
|
+
lines, out_col = ctx.extract(in_lines, in_col)
|
197
|
+
assert_equal exp_lines, lines
|
198
|
+
if exp_col && in_col
|
199
|
+
assert_equal exp_col, out_col
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
end
|
204
|
+
|
205
|
+
|