rtext 0.5.1 → 0.7.0
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.
- 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
|