rtext 0.5.1 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +29 -0
- data/MIT-LICENSE +1 -1
- data/RText_Protocol +102 -51
- data/Rakefile +1 -1
- data/lib/rtext/context_builder.rb +8 -1
- data/lib/rtext/default_completer.rb +163 -0
- data/lib/rtext/default_loader.rb +28 -20
- data/lib/rtext/default_resolver.rb +42 -0
- data/lib/rtext/default_service_provider.rb +49 -21
- data/lib/rtext/frontend/connector.rb +2 -1
- data/lib/rtext/instantiator.rb +38 -16
- data/lib/rtext/json_interface.rb +31 -0
- data/lib/rtext/language.rb +24 -4
- data/lib/rtext/message_helper.rb +35 -25
- data/lib/rtext/parser.rb +27 -45
- data/lib/rtext/serializer.rb +13 -5
- data/lib/rtext/service.rb +27 -18
- data/lib/rtext/tokenizer.rb +18 -2
- data/test/completer_test.rb +35 -18
- data/test/context_builder_test.rb +7 -7
- data/test/instantiator_test.rb +116 -48
- data/test/integration/backend.out +13 -10
- data/test/integration/frontend.log +7664 -0
- data/test/integration/test.rb +6 -0
- data/test/message_helper_test.rb +4 -4
- data/test/serializer_test.rb +101 -3
- data/test/tokenizer_test.rb +33 -1
- metadata +7 -5
- data/lib/rtext/completer.rb +0 -128
data/lib/rtext/parser.rb
CHANGED
@@ -1,28 +1,21 @@
|
|
1
1
|
require 'rtext/generic'
|
2
|
-
require 'rtext/tokenizer'
|
3
2
|
|
4
3
|
module RText
|
5
4
|
|
6
5
|
class Parser
|
7
|
-
include RText::Tokenizer
|
8
|
-
|
9
6
|
Problem = Struct.new(:message, :line)
|
10
7
|
|
11
|
-
def
|
12
|
-
@reference_regexp = reference_regexp
|
13
|
-
end
|
14
|
-
|
15
|
-
def parse(str, options)
|
8
|
+
def parse(tokens, options)
|
16
9
|
@dsc_visitor = options[:descent_visitor]
|
17
10
|
@asc_visitor = options[:ascent_visitor]
|
18
11
|
@problems = options[:problems] || []
|
19
12
|
@non_consume_count = 0
|
20
13
|
@consume_problem_reported = false
|
21
|
-
@tokens =
|
14
|
+
@tokens = tokens
|
22
15
|
@last_line = @tokens.last && @tokens.last.line
|
23
16
|
#@debug = true
|
24
17
|
begin
|
25
|
-
while
|
18
|
+
while next_token_kind
|
26
19
|
statement = parse_statement(true, true)
|
27
20
|
end
|
28
21
|
rescue InternalError
|
@@ -34,7 +27,7 @@ class Parser
|
|
34
27
|
comments = []
|
35
28
|
comment = parse_comment
|
36
29
|
annotation = parse_annotation
|
37
|
-
if (
|
30
|
+
if (next_token_kind && next_token_kind == :identifier) || !allow_unassociated_comment
|
38
31
|
comments << [ comment, :above] if comment
|
39
32
|
command = consume(:identifier)
|
40
33
|
if command
|
@@ -42,12 +35,10 @@ class Parser
|
|
42
35
|
arg_list = []
|
43
36
|
parse_argument_list(arg_list)
|
44
37
|
element_list = []
|
45
|
-
if
|
38
|
+
if next_token_kind == "{"
|
46
39
|
parse_statement_block(element_list, comments)
|
47
40
|
end
|
48
|
-
|
49
|
-
comments << [ eol_comment, :eol ] if eol_comment
|
50
|
-
consume(:newline)
|
41
|
+
parse_eol_comment(comments)
|
51
42
|
@asc_visitor.call(command, arg_list, element_list, comments, annotation, is_root)
|
52
43
|
else
|
53
44
|
discard_until(:newline)
|
@@ -68,7 +59,7 @@ class Parser
|
|
68
59
|
|
69
60
|
def parse_comment
|
70
61
|
result = nil
|
71
|
-
while
|
62
|
+
while next_token_kind == :comment
|
72
63
|
result ||= []
|
73
64
|
result << consume(:comment)
|
74
65
|
consume(:newline)
|
@@ -78,7 +69,7 @@ class Parser
|
|
78
69
|
|
79
70
|
def parse_annotation
|
80
71
|
result = nil
|
81
|
-
while
|
72
|
+
while next_token_kind == :annotation
|
82
73
|
result ||= []
|
83
74
|
result << consume(:annotation)
|
84
75
|
consume(:newline)
|
@@ -86,27 +77,26 @@ class Parser
|
|
86
77
|
result
|
87
78
|
end
|
88
79
|
|
89
|
-
def parse_eol_comment
|
90
|
-
if
|
91
|
-
consume(:comment)
|
92
|
-
|
93
|
-
nil
|
80
|
+
def parse_eol_comment(comments)
|
81
|
+
if next_token_kind == :comment
|
82
|
+
comment = consume(:comment)
|
83
|
+
comments << [comment, :eol]
|
94
84
|
end
|
85
|
+
nl = consume(:newline)
|
86
|
+
discard_until(:newline) unless nl
|
95
87
|
end
|
96
88
|
|
97
89
|
def parse_statement_block(element_list, comments)
|
98
90
|
consume("{")
|
99
|
-
|
100
|
-
|
101
|
-
consume(:newline)
|
102
|
-
while next_token && next_token != "}"
|
91
|
+
parse_eol_comment(comments)
|
92
|
+
while next_token_kind && next_token_kind != "}"
|
103
93
|
parse_block_element(element_list, comments)
|
104
94
|
end
|
105
95
|
consume("}")
|
106
96
|
end
|
107
97
|
|
108
98
|
def parse_block_element(element_list, comments)
|
109
|
-
if
|
99
|
+
if next_token_kind == :label
|
110
100
|
label = consume(:label)
|
111
101
|
element_list << [label, parse_labeled_block_element(comments)]
|
112
102
|
else
|
@@ -116,38 +106,30 @@ class Parser
|
|
116
106
|
end
|
117
107
|
|
118
108
|
def parse_labeled_block_element(comments)
|
119
|
-
if
|
109
|
+
if next_token_kind == "["
|
120
110
|
parse_element_list(comments)
|
121
111
|
else
|
122
|
-
|
123
|
-
comments << [ eol_comment, :eol ] if eol_comment
|
124
|
-
nl = consume(:newline)
|
125
|
-
discard_until(:newline) unless nl
|
112
|
+
parse_eol_comment(comments)
|
126
113
|
parse_statement
|
127
114
|
end
|
128
115
|
end
|
129
116
|
|
130
117
|
def parse_element_list(comments)
|
131
118
|
consume("[")
|
132
|
-
|
133
|
-
comments << [ eol_comment, :eol ] if eol_comment
|
134
|
-
nl = consume(:newline)
|
135
|
-
discard_until(:newline) unless nl
|
119
|
+
parse_eol_comment(comments)
|
136
120
|
result = []
|
137
|
-
while
|
121
|
+
while next_token_kind && next_token_kind != "]"
|
138
122
|
statement = parse_statement(false, true)
|
139
123
|
result << statement if statement
|
140
124
|
end
|
141
125
|
consume("]")
|
142
|
-
|
143
|
-
comments << [ eol_comment, :eol ] if eol_comment
|
144
|
-
consume(:newline)
|
126
|
+
parse_eol_comment(comments)
|
145
127
|
result
|
146
128
|
end
|
147
129
|
|
148
130
|
def parse_argument_list(arg_list)
|
149
131
|
first = true
|
150
|
-
while (AnyValue + [",", "[", :label, :error]).include?(
|
132
|
+
while (AnyValue + [",", "[", :label, :error]).include?(next_token_kind)
|
151
133
|
consume(",") unless first
|
152
134
|
first = false
|
153
135
|
parse_argument(arg_list)
|
@@ -155,7 +137,7 @@ class Parser
|
|
155
137
|
end
|
156
138
|
|
157
139
|
def parse_argument(arg_list)
|
158
|
-
if
|
140
|
+
if next_token_kind == :label
|
159
141
|
label = consume(:label)
|
160
142
|
arg_list << [label, parse_argument_value]
|
161
143
|
else
|
@@ -164,7 +146,7 @@ class Parser
|
|
164
146
|
end
|
165
147
|
|
166
148
|
def parse_argument_value
|
167
|
-
if
|
149
|
+
if next_token_kind == "["
|
168
150
|
parse_argument_value_list
|
169
151
|
else
|
170
152
|
parse_value
|
@@ -175,7 +157,7 @@ class Parser
|
|
175
157
|
consume("[")
|
176
158
|
first = true
|
177
159
|
result = []
|
178
|
-
while (AnyValue + [",", :error]).include?(
|
160
|
+
while (AnyValue + [",", :error]).include?(next_token_kind)
|
179
161
|
consume(",") unless first
|
180
162
|
first = false
|
181
163
|
result << parse_value
|
@@ -190,7 +172,7 @@ class Parser
|
|
190
172
|
consume(*AnyValue)
|
191
173
|
end
|
192
174
|
|
193
|
-
def
|
175
|
+
def next_token_kind
|
194
176
|
@tokens.first && @tokens.first.kind
|
195
177
|
end
|
196
178
|
|
data/lib/rtext/serializer.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'bigdecimal'
|
1
2
|
require 'rtext/language'
|
2
3
|
require 'rtext/generic'
|
3
4
|
|
@@ -47,6 +48,7 @@ class Serializer
|
|
47
48
|
end
|
48
49
|
|
49
50
|
def serialize_element(element)
|
51
|
+
# TODO: remove setting of fragment ref (doesn't belong into serializer)
|
50
52
|
set_fragment_ref(element)
|
51
53
|
set_line_number(element, @line_number) if @set_line_number
|
52
54
|
clazz = element.class.ecore
|
@@ -89,7 +91,8 @@ class Serializer
|
|
89
91
|
childs = contained_elements[f]
|
90
92
|
if childs.size > 0
|
91
93
|
child_classes = childs.collect{|c| c.class.ecore}.uniq
|
92
|
-
if
|
94
|
+
if @lang.labeled_containment?(clazz, f) ||
|
95
|
+
child_classes.any?{|c| @lang.containments_by_target_type(element.class.ecore, c).size > 1}
|
93
96
|
if childs.size > 1
|
94
97
|
write("#{f.name}: [")
|
95
98
|
iinc
|
@@ -142,13 +145,18 @@ class Serializer
|
|
142
145
|
elsif feature.eType.instanceClass == RGen::MetamodelBuilder::DataTypes::Boolean
|
143
146
|
result << v.to_s
|
144
147
|
elsif feature.eType.instanceClass == Float
|
145
|
-
if
|
146
|
-
result <<
|
148
|
+
if v.is_a?(BigDecimal)
|
149
|
+
result << v.to_s("F")
|
150
|
+
# formatting not available for BigDecimals
|
147
151
|
else
|
148
|
-
|
152
|
+
if arg_format
|
153
|
+
result << sprintf(arg_format, v)
|
154
|
+
else
|
155
|
+
result << v.to_s
|
156
|
+
end
|
149
157
|
end
|
150
158
|
elsif feature.eType.is_a?(RGen::ECore::EEnum)
|
151
|
-
if v.to_s =~ /^\d|\W/
|
159
|
+
if v.to_s =~ /^\d|\W/ || v.to_s == "true" || v.to_s == "false"
|
152
160
|
result << "\"#{v.to_s.gsub("\\","\\\\\\\\").gsub("\"","\\\"").gsub("\n","\\n").
|
153
161
|
gsub("\r","\\r").gsub("\t","\\t").gsub("\f","\\f").gsub("\b","\\b")}\""
|
154
162
|
else
|
data/lib/rtext/service.rb
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
require 'socket'
|
2
|
-
require 'rtext/completer'
|
3
2
|
require 'rtext/context_builder'
|
4
3
|
require 'rtext/message_helper'
|
5
4
|
require 'rtext/link_detector'
|
6
5
|
|
6
|
+
# optimization: garbage collect while service is idle
|
7
|
+
|
7
8
|
module RText
|
8
9
|
|
9
10
|
class Service
|
@@ -93,6 +94,7 @@ class Service
|
|
93
94
|
|
94
95
|
def message_received(sock, obj)
|
95
96
|
if check_request(obj)
|
97
|
+
request_start = Time.now
|
96
98
|
@logger.debug("request: "+obj.inspect) if @logger
|
97
99
|
response = { "type" => "response", "invocation_id" => obj["invocation_id"] }
|
98
100
|
case obj["command"]
|
@@ -112,8 +114,10 @@ class Service
|
|
112
114
|
response["type"] = "unknown_command_error"
|
113
115
|
response["command"] = obj["command"]
|
114
116
|
end
|
115
|
-
@logger.debug("response: "+response.inspect)
|
117
|
+
@logger.debug("response: "+truncate_response_for_debug_output(response).inspect) \
|
118
|
+
if response && @logger
|
116
119
|
send_response(sock, response)
|
120
|
+
@logger.info("request complete (#{Time.now-request_start}s)")
|
117
121
|
end
|
118
122
|
end
|
119
123
|
|
@@ -154,6 +158,9 @@ class Service
|
|
154
158
|
response["total_problems"] = total
|
155
159
|
end
|
156
160
|
|
161
|
+
InsertString = "insert"
|
162
|
+
DisplayString = "display"
|
163
|
+
|
157
164
|
def content_complete(sock, request, response)
|
158
165
|
# column numbers start at 1
|
159
166
|
linepos = request["column"]-1
|
@@ -164,13 +171,11 @@ class Service
|
|
164
171
|
context = ContextBuilder.build_context(lang, lines, linepos)
|
165
172
|
@logger.debug("context element: #{lang.identifier_provider.call(context.element, nil, nil, nil)}") \
|
166
173
|
if context && context.element && @logger
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
Completer::CompletionOption.new(o.identifier, "<#{o.type}>")}
|
171
|
-
})
|
174
|
+
options = @service_provider.get_completion_options(context)
|
175
|
+
insert_str = "insert"
|
176
|
+
display_str = "display"
|
172
177
|
response["options"] = options.collect do |o|
|
173
|
-
{
|
178
|
+
{ insert_str => o.text, display_str => "#{o.text} #{o.extra}" }
|
174
179
|
end
|
175
180
|
end
|
176
181
|
|
@@ -186,16 +191,8 @@ class Service
|
|
186
191
|
response["begin_column"] = link_descriptor.scol
|
187
192
|
response["end_column"] = link_descriptor.ecol
|
188
193
|
targets = []
|
189
|
-
|
190
|
-
|
191
|
-
link_descriptor.value, link_descriptor.element, link_descriptor.feature, link_descriptor.index).each do |t|
|
192
|
-
targets << { "file" => t.file, "line" => t.line, "display" => t.display_name }
|
193
|
-
end
|
194
|
-
else
|
195
|
-
@service_provider.get_reference_targets(
|
196
|
-
link_descriptor.value, link_descriptor.element, link_descriptor.feature, link_descriptor.index).each do |t|
|
197
|
-
targets << { "file" => t.file, "line" => t.line, "display" => t.display_name }
|
198
|
-
end
|
194
|
+
@service_provider.get_link_targets(link_descriptor).each do |t|
|
195
|
+
targets << { "file" => t.file, "line" => t.line, "display" => t.display_name }
|
199
196
|
end
|
200
197
|
response["targets"] = targets
|
201
198
|
end
|
@@ -225,6 +222,18 @@ class Service
|
|
225
222
|
end
|
226
223
|
end
|
227
224
|
|
225
|
+
def truncate_response_for_debug_output(response_hash)
|
226
|
+
result = {}
|
227
|
+
response_hash.each_pair do |k,v|
|
228
|
+
if v.is_a?(Array) && v.size > 100
|
229
|
+
result[k] = v[0..99] + ["<truncated>"]
|
230
|
+
else
|
231
|
+
result[k] = v
|
232
|
+
end
|
233
|
+
end
|
234
|
+
result
|
235
|
+
end
|
236
|
+
|
228
237
|
def create_server
|
229
238
|
port = PortRangeStart
|
230
239
|
serv = nil
|
data/lib/rtext/tokenizer.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'rtext/generic'
|
2
|
+
require 'bigdecimal'
|
2
3
|
|
3
4
|
module RText
|
4
5
|
|
@@ -11,6 +12,13 @@ module Tokenizer
|
|
11
12
|
on_command_token_proc = options[:on_command_token]
|
12
13
|
str.split(/\r?\n/).each_with_index do |str, idx|
|
13
14
|
idx += 1
|
15
|
+
if idx == 1
|
16
|
+
# remove UTF-8 BOM if present
|
17
|
+
enc = str.encoding
|
18
|
+
str.force_encoding("binary")
|
19
|
+
str = str[3..-1] if str.index("\xEF\xBB\xBF".force_encoding("binary")) == 0
|
20
|
+
str.force_encoding(enc)
|
21
|
+
end
|
14
22
|
if str =~ /^\s*([\#@])(.*)/
|
15
23
|
if $1 == "#"
|
16
24
|
result << Token.new(:comment, $2, idx, str.size-$2.size, str.size)
|
@@ -29,7 +37,15 @@ module Tokenizer
|
|
29
37
|
col += $&.size
|
30
38
|
when /\A[-+]?\d+\.\d+(?:e[+-]\d+)?\b/
|
31
39
|
str = $'
|
32
|
-
|
40
|
+
val = $&
|
41
|
+
# if string size isn't more than 16, Float precision will be sufficient
|
42
|
+
# otherwise use BigDecimal even though the value might still fit in a Float
|
43
|
+
if val.size <= 16
|
44
|
+
val = val.to_f
|
45
|
+
else
|
46
|
+
val = BigDecimal.new(val)
|
47
|
+
end
|
48
|
+
result << Token.new(:float, val, idx, col, col+$&.size-1)
|
33
49
|
col += $&.size
|
34
50
|
when /\A0[xX][0-9a-fA-F]+\b/
|
35
51
|
str = $'
|
@@ -82,7 +98,7 @@ module Tokenizer
|
|
82
98
|
str = $'
|
83
99
|
result << Token.new(:generic, RText::Generic.new($1), idx, col, col+$&.size-1)
|
84
100
|
col += $&.size
|
85
|
-
when /\A\S
|
101
|
+
when /\A\S/
|
86
102
|
str = $'
|
87
103
|
result << Token.new(:error, $&, idx, col, col+$&.size-1)
|
88
104
|
col += $&.size
|
data/test/completer_test.rb
CHANGED
@@ -4,7 +4,7 @@ require 'test/unit'
|
|
4
4
|
require 'rgen/metamodel_builder'
|
5
5
|
require 'rtext/language'
|
6
6
|
require 'rtext/context_builder'
|
7
|
-
require 'rtext/
|
7
|
+
require 'rtext/default_completer'
|
8
8
|
|
9
9
|
class CompleterTest < Test::Unit::TestCase
|
10
10
|
|
@@ -14,6 +14,8 @@ module TestMM
|
|
14
14
|
has_attr 'text', String
|
15
15
|
has_attr 'unlabled', String
|
16
16
|
end
|
17
|
+
class TestNode3 < RGen::MetamodelBuilder::MMBase
|
18
|
+
end
|
17
19
|
class TestNode < RGen::MetamodelBuilder::MMBase
|
18
20
|
has_attr 'text', String
|
19
21
|
has_attr 'unlabled1', String
|
@@ -24,6 +26,7 @@ module TestMM
|
|
24
26
|
contains_many 'childs', TestNode, 'parent'
|
25
27
|
contains_one 'child2RoleA', TestNode2, 'parentA'
|
26
28
|
contains_many 'child2RoleB', TestNode2, 'parentB'
|
29
|
+
contains_one_uni 'testNode3', TestNode3
|
27
30
|
end
|
28
31
|
SomeEnum = RGen::MetamodelBuilder::DataTypes::Enum.new(
|
29
32
|
:name => "SomeEnum", :literals => [:A, :B, :'non-word*chars'])
|
@@ -271,7 +274,7 @@ TestNode3 enum: |
|
|
271
274
|
assert_options([
|
272
275
|
["A", "<SomeEnum>"],
|
273
276
|
["B", "<SomeEnum>"],
|
274
|
-
["non-word*chars", "<SomeEnum>"]
|
277
|
+
["\"non-word*chars\"", "<SomeEnum>"]
|
275
278
|
], options)
|
276
279
|
end
|
277
280
|
|
@@ -282,7 +285,7 @@ TestNode3 enum: A|
|
|
282
285
|
assert_options([
|
283
286
|
["A", "<SomeEnum>"],
|
284
287
|
["B", "<SomeEnum>"],
|
285
|
-
["non-word*chars", "<SomeEnum>"]
|
288
|
+
["\"non-word*chars\"", "<SomeEnum>"]
|
286
289
|
], options)
|
287
290
|
end
|
288
291
|
|
@@ -308,8 +311,8 @@ def test_reference_value
|
|
308
311
|
options = complete(TestMM, %Q(\
|
309
312
|
TestNode related: |\
|
310
313
|
), lambda { |r| [
|
311
|
-
RText::
|
312
|
-
RText::
|
314
|
+
RText::DefaultCompleter::CompletionOption.new("A", "a"),
|
315
|
+
RText::DefaultCompleter::CompletionOption.new("B", "b") ] })
|
313
316
|
assert_options([
|
314
317
|
["A", "a"],
|
315
318
|
["B", "b"],
|
@@ -320,8 +323,8 @@ def test_reference_value_part
|
|
320
323
|
options = complete(TestMM, %Q(\
|
321
324
|
TestNode related: /My/|\
|
322
325
|
), lambda { |r| [
|
323
|
-
RText::
|
324
|
-
RText::
|
326
|
+
RText::DefaultCompleter::CompletionOption.new("/My/Target", "a"),
|
327
|
+
RText::DefaultCompleter::CompletionOption.new("/MyOther/Target", "b") ] })
|
325
328
|
assert_options([
|
326
329
|
["/My/Target", "a"],
|
327
330
|
["/MyOther/Target", "b"]
|
@@ -340,8 +343,8 @@ def test_reference_value_in_array
|
|
340
343
|
options = complete(TestMM, %Q(\
|
341
344
|
TestNode others: |
|
342
345
|
), lambda { |r| [
|
343
|
-
RText::
|
344
|
-
RText::
|
346
|
+
RText::DefaultCompleter::CompletionOption.new("A", "a"),
|
347
|
+
RText::DefaultCompleter::CompletionOption.new("B", "b") ] })
|
345
348
|
assert_options([
|
346
349
|
["A", "a"],
|
347
350
|
["B", "b"],
|
@@ -352,8 +355,8 @@ def test_reference_value_in_array_after_bracket
|
|
352
355
|
options = complete(TestMM, %Q(\
|
353
356
|
TestNode others: [|
|
354
357
|
), lambda { |r| [
|
355
|
-
RText::
|
356
|
-
RText::
|
358
|
+
RText::DefaultCompleter::CompletionOption.new("A", "a"),
|
359
|
+
RText::DefaultCompleter::CompletionOption.new("B", "b") ] })
|
357
360
|
assert_options([
|
358
361
|
["A", "a"],
|
359
362
|
["B", "b"],
|
@@ -364,8 +367,8 @@ def test_reference_value_in_array_second_value
|
|
364
367
|
options = complete(TestMM, %Q(\
|
365
368
|
TestNode others: [xxx, |
|
366
369
|
), lambda { |r| [
|
367
|
-
RText::
|
368
|
-
RText::
|
370
|
+
RText::DefaultCompleter::CompletionOption.new("A", "a"),
|
371
|
+
RText::DefaultCompleter::CompletionOption.new("B", "b") ] })
|
369
372
|
assert_options([
|
370
373
|
["A", "a"],
|
371
374
|
["B", "b"],
|
@@ -377,8 +380,8 @@ def test_reference_value_nested
|
|
377
380
|
TestNode {
|
378
381
|
TestNode SimpleState, others: [|/StatemachineMM/State]
|
379
382
|
), lambda { |r| [
|
380
|
-
RText::
|
381
|
-
RText::
|
383
|
+
RText::DefaultCompleter::CompletionOption.new("A", "a"),
|
384
|
+
RText::DefaultCompleter::CompletionOption.new("B", "b") ] })
|
382
385
|
assert_options([
|
383
386
|
["A", "a"],
|
384
387
|
["B", "b"],
|
@@ -401,7 +404,8 @@ TestNode {
|
|
401
404
|
assert_options([
|
402
405
|
["TestNode", "<unlabled1>, <unlabled2>"],
|
403
406
|
["child2RoleA:", "<TestNode2>"],
|
404
|
-
["child2RoleB:", "<TestNode2>"]
|
407
|
+
["child2RoleB:", "<TestNode2>"],
|
408
|
+
["testNode3:", "<TestNode3>"]
|
405
409
|
], options)
|
406
410
|
end
|
407
411
|
|
@@ -580,9 +584,22 @@ def complete(mm, text, ref_comp_option_provider=nil)
|
|
580
584
|
lang = RText::Language.new(mm.ecore,
|
581
585
|
:root_classes => mm.ecore.eAllClasses,
|
582
586
|
:unlabled_arguments => lambda {|c| ["unlabled1", "unlabled2", "unlabled"]},
|
583
|
-
:unquoted_arguments => lambda {|c| c.name == "TestNode2" ? ["text", "unlabled"] : []}
|
587
|
+
:unquoted_arguments => lambda {|c| c.name == "TestNode2" ? ["text", "unlabled"] : []},
|
588
|
+
:labeled_containments => lambda {|c| ["testNode3"]})
|
584
589
|
context = RText::ContextBuilder.build_context(lang, context_lines, pos_in_line)
|
585
|
-
RText::
|
590
|
+
completer = RText::DefaultCompleter.new(lang)
|
591
|
+
class << completer
|
592
|
+
attr_accessor :ref_comp_option_provider
|
593
|
+
def reference_options(context)
|
594
|
+
if ref_comp_option_provider
|
595
|
+
ref_comp_option_provider.call(context.feature)
|
596
|
+
else
|
597
|
+
super
|
598
|
+
end
|
599
|
+
end
|
600
|
+
end
|
601
|
+
completer.ref_comp_option_provider = ref_comp_option_provider
|
602
|
+
completer.complete(context)
|
586
603
|
end
|
587
604
|
|
588
605
|
end
|