rtext 0.2.1 → 0.3.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.
@@ -2,11 +2,12 @@ module RText
2
2
 
3
3
  class DefaultServiceProvider
4
4
 
5
- def initialize(language, fragmented_model, model_loader)
5
+ def initialize(language, fragmented_model, model_loader, options={})
6
6
  @lang = language
7
7
  @model = fragmented_model
8
8
  @loader = model_loader
9
9
  @element_name_index = nil
10
+ @result_limit = options[:result_limit]
10
11
  @model.add_fragment_change_listener(proc {|fragment, kind|
11
12
  @element_name_index = nil
12
13
  })
@@ -25,7 +26,7 @@ class DefaultServiceProvider
25
26
  targets = @model.index.values.flatten.select{|e| e.is_a?(clazz)}
26
27
  end
27
28
  targets.collect{|t|
28
- ident = @lang.identifier_provider.call(t, context)
29
+ ident = @lang.identifier_provider.call(t, context.element)
29
30
  if ident
30
31
  ReferenceCompletionOption.new(ident, t.class.ecore.name)
31
32
  else
@@ -35,20 +36,13 @@ class DefaultServiceProvider
35
36
  end
36
37
 
37
38
  ReferenceTarget = Struct.new(:file, :line, :display_name)
38
- def get_reference_targets(identifier, context, lines, linepos)
39
+ def get_reference_targets(identifier, context)
39
40
  result = []
40
- identifier = @lang.qualify_reference(identifier, context)
41
+ identifier = @lang.qualify_reference(identifier, context.element)
41
42
  targets = @model.index[identifier]
42
43
  if targets && @lang.per_type_identifier
43
- current_line = lines.last
44
- linestart = current_line[0..linepos-1]
45
- if linestart =~ /\s*(\w+)\s+(?:[^,]+,)*\s*(\w+):\s*(\S*)$/
46
- command, fn, prefix = $1, $2, $3
47
- clazz = @lang.class_by_command(command)
48
- feature = clazz && @lang.non_containments(clazz.ecore).find{|f| f.name == fn}
49
- if feature
50
- targets = targets.select{|t| t.is_a?(feature.eType.instanceClass)}
51
- end
44
+ if context.feature
45
+ targets = targets.select{|t| t.is_a?(context.feature.eType.instanceClass)}
52
46
  end
53
47
  end
54
48
  targets && targets.each do |t|
@@ -62,9 +56,9 @@ class DefaultServiceProvider
62
56
 
63
57
  def get_referencing_elements(identifier, context)
64
58
  result = []
65
- targets = @model.index[@lang.identifier_provider.call(context, nil)]
59
+ targets = @model.index[@lang.identifier_provider.call(context.element, nil)]
66
60
  if targets && @lang.per_type_identifier
67
- targets = targets.select{|t| t.class == context.class}
61
+ targets = targets.select{|t| t.class == context.element.class}
68
62
  end
69
63
  if targets && targets.size == 1
70
64
  target = targets.first
@@ -114,8 +108,9 @@ class DefaultServiceProvider
114
108
  result = []
115
109
  return result unless pattern
116
110
  sub_index = element_name_index[pattern[0..0].downcase]
111
+ truncate_result = false
117
112
  sub_index && sub_index.each_pair do |ident, elements|
118
- if ident.split(/\W/).last.downcase.index(pattern.downcase) == 0
113
+ if !truncate_result && ident.split(/\W/).last.downcase.index(pattern.downcase) == 0
119
114
  elements.each do |e|
120
115
  if @lang.fragment_ref(e)
121
116
  non_word_index = ident.rindex(/\W/)
@@ -129,12 +124,20 @@ class DefaultServiceProvider
129
124
  display_name = "#{name} [#{e.class.ecore.name}]"
130
125
  display_name += " - #{scope}" if scope.size > 0
131
126
  path = File.expand_path(@lang.fragment_ref(e).fragment.location)
132
- result << OpenElementChoice.new(display_name, path, @lang.line_number(e))
127
+ if !@result_limit || result.size < @result_limit
128
+ result << OpenElementChoice.new(display_name, path, @lang.line_number(e))
129
+ else
130
+ truncate_result = true
131
+ end
133
132
  end
134
133
  end
135
134
  end
136
135
  end
137
- result.sort{|a,b| a.display_name <=> b.display_name}
136
+ result = result.sort{|a,b| a.display_name <=> b.display_name}
137
+ if truncate_result
138
+ result << OpenElementChoice.new("--- result truncated, showing first #{@result_limit} entries ---", "/", 1)
139
+ end
140
+ result
138
141
  end
139
142
 
140
143
  def element_name_index
@@ -44,16 +44,26 @@ class Instantiator
44
44
  @root_elements = options[:root_elements] || []
45
45
  @file_name = options[:file_name]
46
46
  @fragment_ref = options[:fragment_ref]
47
+ @context_class_stack = []
47
48
  parser = Parser.new(@lang.reference_regexp)
48
49
  begin
49
50
  @root_elements.clear
50
- parser.parse(str) do |*args|
51
- if args[0]
52
- create_element(*args)
53
- else
54
- unassociated_comments(args[3])
55
- end
56
- end
51
+ parser.parse(str,
52
+ :descent_visitor => lambda do |command|
53
+ clazz = @lang.class_by_command(command.value, @context_class_stack.last)
54
+ # in case no class is found, nil will be pushed, this will case the next command
55
+ # lookup to act as if called from toplevel
56
+ @context_class_stack.push(clazz)
57
+ end,
58
+ :ascent_visitor => lambda do |*args|
59
+ if args[0]
60
+ element =create_element(*args)
61
+ @context_class_stack.pop
62
+ element
63
+ else
64
+ unassociated_comments(args[3])
65
+ end
66
+ end)
57
67
  rescue Parser::Error => e
58
68
  problem(e.message, e.line)
59
69
  @unresolved_refs.clear if @unresolved_refs
@@ -75,10 +85,18 @@ class Instantiator
75
85
  end
76
86
 
77
87
  def create_element(command, arg_list, element_list, comments, is_root)
78
- clazz = @lang.class_by_command(command.value)
79
- if !clazz
88
+ clazz = @context_class_stack.last
89
+ if !@lang.has_command(command.value)
80
90
  problem("Unknown command '#{command.value}'", command.line)
81
91
  return
92
+ elsif !clazz
93
+ if is_root
94
+ problem("Command '#{command.value}' can not be used on root level", command.line)
95
+ return
96
+ else
97
+ problem("Command '#{command.value}' can not be used in this context", command.line)
98
+ return
99
+ end
82
100
  end
83
101
  if clazz.ecore.abstract
84
102
  problem("Unknown command '#{command.value}' (metaclass is abstract)", command.line)
@@ -153,6 +171,7 @@ class Instantiator
153
171
  return if child.nil?
154
172
  feature = @lang.containments_by_target_type(element.class.ecore, child.class.ecore)
155
173
  if feature.size == 0
174
+ # this should never happen since the scope of an element is already checked when it's created
156
175
  problem("This kind of element can not be contained here", line_number(child))
157
176
  return
158
177
  end
@@ -35,11 +35,6 @@ class Language
35
35
  # (in sprintf syntax) which will be used by the serializer for integers and floats.
36
36
  # default: if not present or the proc returns nil, then #to_s is used
37
37
  #
38
- # :short_class_names
39
- # if true, the metamodel is searched for classes by unqualified class name recursively
40
- # if false, classes can only be found in the root package, not in subpackages
41
- # default: true
42
- #
43
38
  # :reference_regexp
44
39
  # a Regexp which is used by the tokenizer for identifying references
45
40
  # it must only match at the beginning of a string, i.e. it should start with \A
@@ -75,6 +70,10 @@ class Language
75
70
  # default: no reference qualifier, i.e. all identifiers returned by the identifier provider
76
71
  # must be globally unique
77
72
  #
73
+ # :root_classes
74
+ # an Array of EClass objects representing classes which can be used on root level
75
+ # default: all classes which can't be contained by any class
76
+ #
78
77
  # :line_number_attribute
79
78
  # the name of the attribute which will be used to associate the line number with a model element
80
79
  # default: no line number
@@ -87,9 +86,10 @@ class Language
87
86
  # the name of the attribute which will be used to associate a model fragment with a model element
88
87
  #
89
88
  # :comment_handler
90
- # a Proc which will be invoked when a new element has been instantiated. receives an
91
- # element, the comment as a string, and the environment to which the element has been
92
- # added to. then environment may be nil. it should add the comment to the element and
89
+ # a Proc which will be invoked when a new element has been instantiated. receives
90
+ # the comment as a string, the comment kind (one of [:above, :eol, :unassociated]), the
91
+ # element and the environment to which the element has been added to.
92
+ # the environment may be nil. it should add the comment to the element and
93
93
  # return true. if the element can take no comment, it should return false.
94
94
  # default: no handling of comments
95
95
  #
@@ -113,18 +113,9 @@ class Language
113
113
  @unlabled_arguments = options[:unlabled_arguments]
114
114
  @unquoted_arguments = options[:unquoted_arguments]
115
115
  @argument_format_provider = options[:argument_format_provider]
116
- @class_by_command = {}
116
+ @root_classes = options[:root_classes] || default_root_classes(root_epackage)
117
117
  command_name_provider = options[:command_name_provider] || proc{|c| c.name}
118
- ((!options.has_key?(:short_class_names) || options[:short_class_names]) ?
119
- root_epackage.eAllClasses : root_epackage.eClasses).each do |c|
120
- next if c.abstract
121
- command_name = command_name_provider.call(c)
122
- raise "ambiguous command name #{command_name}" if @class_by_command[command_name]
123
- @class_by_command[command_name] = c.instanceClass
124
- end
125
- # there can't be multiple commands for the same class as the command name provider
126
- # can only return one command per class
127
- @command_by_class = @class_by_command.invert
118
+ setup_commands(root_epackage, command_name_provider)
128
119
  @reference_regexp = options[:reference_regexp] || /\A\w*(\/\w*)+/
129
120
  @identifier_provider = options[:identifier_provider] ||
130
121
  proc { |element, context|
@@ -142,6 +133,7 @@ class Language
142
133
  end
143
134
 
144
135
  attr_reader :root_epackage
136
+ attr_reader :root_classes
145
137
  attr_reader :reference_regexp
146
138
  attr_reader :identifier_provider
147
139
  attr_reader :line_number_attribute
@@ -152,8 +144,13 @@ class Language
152
144
  attr_reader :indent_string
153
145
  attr_reader :per_type_identifier
154
146
 
155
- def class_by_command(command)
156
- @class_by_command[command]
147
+ def class_by_command(command, context_class)
148
+ map = @class_by_command[context_class]
149
+ map && map[command]
150
+ end
151
+
152
+ def has_command(command)
153
+ @has_command[command]
157
154
  end
158
155
 
159
156
  def command_by_class(clazz)
@@ -225,6 +222,39 @@ class Language
225
222
 
226
223
  private
227
224
 
225
+ def setup_commands(root_epackage, command_name_provider)
226
+ @class_by_command = {}
227
+ @command_by_class = {}
228
+ @has_command = {}
229
+ root_epackage.eAllClasses.each do |c|
230
+ next if c.abstract
231
+ cmd = command_name_provider.call(c)
232
+ @command_by_class[c.instanceClass] = cmd
233
+ @has_command[cmd] = true
234
+ clazz = c.instanceClass
235
+ @class_by_command[clazz] ||= {}
236
+ c.eAllReferences.select{|r| r.containment}.collect{|r|
237
+ [r.eType] + r.eType.eAllSubTypes}.flatten.uniq.each do |t|
238
+ next if t.abstract
239
+ cmw = command_name_provider.call(t)
240
+ raise "ambiguous command name #{cmw}" if @class_by_command[clazz][cmw]
241
+ @class_by_command[clazz][cmw] = t.instanceClass
242
+ end
243
+ end
244
+ @class_by_command[nil] = {}
245
+ @root_classes.each do |c|
246
+ next if c.abstract
247
+ cmw = command_name_provider.call(c)
248
+ raise "ambiguous command name #{cmw}" if @class_by_command[nil][cmw]
249
+ @class_by_command[nil][cmw] = c.instanceClass
250
+ end
251
+ end
252
+
253
+ def default_root_classes(root_package)
254
+ root_epackage.eAllClasses.select{|c| !c.abstract &&
255
+ !c.eAllReferences.any?{|r| r.eOpposite && r.eOpposite.containment}}
256
+ end
257
+
228
258
  def features(clazz)
229
259
  @feature_provider.call(clazz)
230
260
  end
data/lib/rtext/parser.rb CHANGED
@@ -6,8 +6,9 @@ class Parser
6
6
  @reference_regexp = reference_regexp
7
7
  end
8
8
 
9
- def parse(str, &visitor)
10
- @visitor = visitor
9
+ def parse(str, options)
10
+ @dsc_visitor = options[:descent_visitor]
11
+ @asc_visitor = options[:ascent_visitor]
11
12
  @tokens = tokenize(str, @reference_regexp)
12
13
  @last_line = @tokens.last && @tokens.last.line
13
14
  while next_token
@@ -22,6 +23,7 @@ class Parser
22
23
  if (next_token && next_token == :identifier) || !allow_unassociated_comment
23
24
  comments << [ comment, :above] if comment
24
25
  command = consume(:identifier)
26
+ @dsc_visitor.call(command)
25
27
  arg_list = []
26
28
  parse_argument_list(arg_list)
27
29
  element_list = []
@@ -31,11 +33,11 @@ class Parser
31
33
  eol_comment = parse_eol_comment
32
34
  comments << [ eol_comment, :eol ] if eol_comment
33
35
  consume(:newline)
34
- @visitor.call(command, arg_list, element_list, comments, is_root)
36
+ @asc_visitor.call(command, arg_list, element_list, comments, is_root)
35
37
  elsif comment
36
38
  # if there is no statement, the comment is non-optional
37
39
  comments << [ comment, :unassociated ]
38
- @visitor.call(nil, nil, nil, comments, nil)
40
+ @asc_visitor.call(nil, nil, nil, comments, nil)
39
41
  nil
40
42
  else
41
43
  # die expecting an identifier (next token is not an identifier)
@@ -80,7 +80,8 @@ class Serializer
80
80
  @lang.containments(clazz).each do |f|
81
81
  childs = contained_elements[f]
82
82
  if childs.size > 0
83
- if @lang.containments_by_target_type(f.eContainingClass, f.eType).size > 1
83
+ child_classes = childs.collect{|c| c.class.ecore}.uniq
84
+ if child_classes.any?{|c| @lang.containments_by_target_type(element.class.ecore, c).size > 1}
84
85
  if childs.size > 1
85
86
  write("#{f.name}: [")
86
87
  iinc
@@ -132,7 +133,7 @@ class Serializer
132
133
  result << v.to_s
133
134
  end
134
135
  elsif feature.eType.is_a?(RGen::ECore::EEnum)
135
- if v.to_s =~ /^\d|\W/
136
+ if v.to_s =~ /\W/
136
137
  result << "\"#{v.to_s.gsub("\\","\\\\\\\\").gsub("\"","\\\"").gsub("\n","\\n").
137
138
  gsub("\r","\\r").gsub("\t","\\t").gsub("\f","\\f").gsub("\b","\\b")}\""
138
139
  else
data/lib/rtext/service.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  require 'socket'
2
2
  require 'rtext/completer'
3
- require 'rtext/context_element_builder'
3
+ require 'rtext/context_builder'
4
4
 
5
5
  module RText
6
6
 
@@ -119,13 +119,11 @@ class Service
119
119
 
120
120
  def complete(lines)
121
121
  linepos = lines.shift.to_i
122
- context = ContextElementBuilder.build_context_element(@lang, lines, linepos)
123
- @logger.debug("context element: #{@lang.identifier_provider.call(context, nil)}") if @logger
122
+ context = ContextBuilder.build_context(@lang, lines, linepos)
123
+ @logger.debug("context element: #{@lang.identifier_provider.call(context.element, nil)}") if @logger
124
124
  current_line = lines.pop
125
125
  current_line ||= ""
126
- options = @completer.complete(current_line, linepos,
127
- proc {|i| lines[-i]},
128
- proc {|ref|
126
+ options = @completer.complete(context, lambda {|ref|
129
127
  @service_provider.get_reference_completion_options(ref, context).collect {|o|
130
128
  Completer::CompletionOption.new(o.identifier, "<#{o.type}>")}
131
129
  })
@@ -148,10 +146,10 @@ class Service
148
146
 
149
147
  def get_reference_targets(lines)
150
148
  linepos = lines.shift.to_i
151
- context = ContextElementBuilder.build_context_element(@lang, lines, linepos)
152
149
  current_line = lines.last
150
+ context = ContextBuilder.build_context(@lang, lines, lines.last.size)
153
151
  result = []
154
- if current_line[linepos..linepos] =~ /[\w\/]/
152
+ if context && current_line[linepos..linepos] =~ /[\w\/]/
155
153
  ident_start = (current_line.rindex(/[^\w\/]/, linepos) || -1)+1
156
154
  ident_end = (current_line.index(/[^\w\/]/, linepos) || current_line.size)-1
157
155
  ident = current_line[ident_start..ident_end]
@@ -161,7 +159,7 @@ class Service
161
159
  result << "#{t.file};#{t.line};#{t.display_name}\n"
162
160
  end
163
161
  else
164
- @service_provider.get_reference_targets(ident, context, lines, linepos).each do |t|
162
+ @service_provider.get_reference_targets(ident, context).each do |t|
165
163
  result << "#{t.file};#{t.line};#{t.display_name}\n"
166
164
  end
167
165
  end
@@ -0,0 +1,332 @@
1
+ $:.unshift File.join(File.dirname(__FILE__),"..","lib")
2
+
3
+ require 'test/unit'
4
+ require 'rgen/metamodel_builder'
5
+ require 'rtext/language'
6
+ require 'rtext/context_builder'
7
+ require 'rtext/completer'
8
+
9
+ class CompleterTest < Test::Unit::TestCase
10
+
11
+ module TestMM
12
+ extend RGen::MetamodelBuilder::ModuleExtension
13
+ class TestNode2 < RGen::MetamodelBuilder::MMBase
14
+ has_attr 'text', String
15
+ end
16
+ class TestNode < RGen::MetamodelBuilder::MMBase
17
+ has_attr 'text', String
18
+ has_attr 'unlabled1', String
19
+ has_attr 'unlabled2', Integer
20
+ has_many_attr 'nums', Integer
21
+ has_one 'related', TestNode
22
+ has_many 'others', TestNode
23
+ contains_many 'childs', TestNode, 'parent'
24
+ contains_one 'child2RoleA', TestNode2, 'parentA'
25
+ contains_many 'child2RoleB', TestNode2, 'parentB'
26
+ end
27
+ SomeEnum = RGen::MetamodelBuilder::DataTypes::Enum.new(
28
+ :name => "SomeEnum", :literals => [:A, :B, :'non-word*chars'])
29
+ class TestNode3 < RGen::MetamodelBuilder::MMBase
30
+ has_attr 'bool', Boolean
31
+ has_attr 'float', Float
32
+ has_attr 'enum', SomeEnum
33
+ end
34
+ class TextNode < RGen::MetamodelBuilder::MMBase
35
+ end
36
+ end
37
+
38
+ def test_after_command
39
+ options = complete TestMM, <<-END
40
+ TestNode |
41
+ END
42
+ assert_options([
43
+ ["<unlabled1>", "<EString>"],
44
+ ["nums:", "<EInt>"],
45
+ ["others:", "<TestNode>"],
46
+ ["related:", "<TestNode>"],
47
+ ["text:", "<EString>"]
48
+ ], options)
49
+ end
50
+
51
+ def test_lable_prefix
52
+ options = complete TestMM, <<-END
53
+ TestNode t|
54
+ END
55
+ assert_options([
56
+ ["text:", "<EString>"]
57
+ ], options)
58
+ end
59
+
60
+ def test_unlabled_prefix
61
+ options = complete TestMM, <<-END
62
+ TestNode u|
63
+ END
64
+ assert_options([
65
+ ["<unlabled1>", "<EString>"]
66
+ ], options)
67
+ end
68
+
69
+ def test_after_labled_value
70
+ options = complete TestMM, <<-END
71
+ TestNode nums: 1, |
72
+ END
73
+ assert_options([
74
+ ["others:", "<TestNode>"],
75
+ ["related:", "<TestNode>"],
76
+ ["text:", "<EString>"]
77
+ ], options)
78
+ end
79
+
80
+ def test_after_unlabled_value
81
+ options = complete TestMM, <<-END
82
+ TestNode "bla", |
83
+ END
84
+ assert_options([
85
+ ["<unlabled2>", "<EInt>"],
86
+ ["nums:", "<EInt>"],
87
+ ["others:", "<TestNode>"],
88
+ ["related:", "<TestNode>"],
89
+ ["text:", "<EString>"]
90
+ ], options)
91
+ end
92
+
93
+ def test_after_unlabled_value2
94
+ options = complete TestMM, <<-END
95
+ TestNode "bla", 1, |
96
+ END
97
+ assert_options([
98
+ ["nums:", "<EInt>"],
99
+ ["others:", "<TestNode>"],
100
+ ["related:", "<TestNode>"],
101
+ ["text:", "<EString>"]
102
+ ], options)
103
+ end
104
+
105
+ def test_after_array
106
+ options = complete TestMM, <<-END
107
+ TestNode nums: [1, 2], |
108
+ END
109
+ assert_options([
110
+ ["others:", "<TestNode>"],
111
+ ["related:", "<TestNode>"],
112
+ ["text:", "<EString>"]
113
+ ], options)
114
+ end
115
+
116
+ def test_after_array_direct
117
+ options = complete TestMM, <<-END
118
+ TestNode nums: [1, 2]|
119
+ END
120
+ assert_options([
121
+ ], options)
122
+ end
123
+
124
+ def test_value_int
125
+ options = complete TestMM, <<-END
126
+ TestNode nums: |
127
+ END
128
+ assert_options([
129
+ ["0", nil],
130
+ ["1", nil],
131
+ ["2", nil],
132
+ ["3", nil],
133
+ ["4", nil]
134
+ ], options)
135
+ end
136
+
137
+ def test_value_boolean
138
+ options = complete TestMM, <<-END
139
+ TestNode3 bool: |
140
+ END
141
+ assert_options([
142
+ ["true", nil],
143
+ ["false", nil],
144
+ ], options)
145
+ end
146
+
147
+ def test_value_float
148
+ options = complete TestMM, <<-END
149
+ TestNode3 float: |
150
+ END
151
+ assert_options([
152
+ ["0.0", nil],
153
+ ["1.0", nil],
154
+ ["2.0", nil],
155
+ ["3.0", nil],
156
+ ["4.0", nil]
157
+ ], options)
158
+ end
159
+
160
+ def test_value_enum
161
+ options = complete TestMM, <<-END
162
+ TestNode3 enum: |
163
+ END
164
+ assert_options([
165
+ ["A", nil],
166
+ ["B", nil],
167
+ ["non-word*chars", nil]
168
+ ], options)
169
+ end
170
+
171
+ def test_array_value
172
+ options = complete TestMM, <<-END
173
+ TestNode nums: [|
174
+ END
175
+ assert_options([
176
+ ["0", nil],
177
+ ["1", nil],
178
+ ["2", nil],
179
+ ["3", nil],
180
+ ["4", nil]
181
+ ], options)
182
+ end
183
+
184
+ def test_array_value2
185
+ options = complete TestMM, <<-END
186
+ TestNode nums: [1,|
187
+ END
188
+ assert_options([
189
+ ["0", nil],
190
+ ["1", nil],
191
+ ["2", nil],
192
+ ["3", nil],
193
+ ["4", nil]
194
+ ], options)
195
+ end
196
+
197
+ def test_reference_value
198
+ options = complete(TestMM, %Q(\
199
+ TestNode related: |\
200
+ ), lambda { |r| [
201
+ RText::Completer::CompletionOption.new("A", "a"),
202
+ RText::Completer::CompletionOption.new("B", "b") ] })
203
+ assert_options([
204
+ ["A", "a"],
205
+ ["B", "b"],
206
+ ], options)
207
+ end
208
+
209
+ def test_reference_value_no_ref_completion_provider
210
+ options = complete TestMM, <<-END
211
+ TestNode related: |
212
+ END
213
+ assert_options([
214
+ ], options)
215
+ end
216
+
217
+ def test_children
218
+ options = complete TestMM, <<-END
219
+ TestNode {
220
+ |
221
+ END
222
+ assert_options([
223
+ ["TestNode", "<unlabled1>, <unlabled2>"],
224
+ ["child2RoleA:", "<TestNode2>"],
225
+ ["child2RoleB:", "<TestNode2>"]
226
+ ], options)
227
+ end
228
+
229
+ def test_children_with_role
230
+ options = complete TestMM, <<-END
231
+ TestNode {
232
+ child2RoleA:
233
+ |
234
+ END
235
+ assert_options([
236
+ ["TestNode2", ""],
237
+ ], options)
238
+ end
239
+
240
+ def test_children_with_role_array
241
+ options = complete TestMM, <<-END
242
+ TestNode {
243
+ child2RoleB: [
244
+ |
245
+ END
246
+ assert_options([
247
+ ["TestNode2", ""],
248
+ ], options)
249
+ end
250
+
251
+ def test_children_prefix
252
+ options = complete TestMM, <<-END
253
+ TestNode {
254
+ child2RoleB: [
255
+ X|
256
+ END
257
+ assert_options([
258
+ ], options)
259
+ end
260
+
261
+ def test_children_inside_childrole
262
+ options = complete TestMM, <<-END
263
+ TestNode {
264
+ child2RoleA:
265
+ TestNode2 |
266
+ END
267
+ assert_options([
268
+ ["text:", "<EString>"]
269
+ ], options)
270
+ end
271
+
272
+ def test_children_inside_childrole_array
273
+ options = complete TestMM, <<-END
274
+ TestNode {
275
+ child2RoleB: [
276
+ TestNode2 |
277
+ END
278
+ assert_options([
279
+ ["text:", "<EString>"]
280
+ ], options)
281
+ end
282
+
283
+ def test_root
284
+ options = complete TestMM, <<-END
285
+ |
286
+ END
287
+ assert_options([
288
+ ["TestNode", "<unlabled1>, <unlabled2>"],
289
+ ["TestNode2", ""],
290
+ ["TestNode3", ""],
291
+ ["TextNode", ""]
292
+ ], options)
293
+ end
294
+
295
+ def test_root_no_context_lines
296
+ options = complete TestMM, ""
297
+ assert_options([
298
+ ["TestNode", "<unlabled1>, <unlabled2>"],
299
+ ["TestNode2", ""],
300
+ ["TestNode3", ""],
301
+ ["TextNode", ""]
302
+ ], options)
303
+ end
304
+
305
+ def test_root_prefix
306
+ options = complete TestMM, <<-END
307
+ Text|
308
+ END
309
+ assert_options([
310
+ ["TextNode", ""]
311
+ ], options)
312
+ end
313
+
314
+ def assert_options(expected, options)
315
+ assert_equal(expected, options.collect { |o| [o.text, o.extra] })
316
+ end
317
+
318
+ def complete(mm, text, ref_comp_option_provider=nil)
319
+ context_lines = text.split("\n")
320
+ if context_lines.last
321
+ pos_in_line = context_lines.last.index("|")
322
+ context_lines.last.sub!("|", "")
323
+ end
324
+ lang = RText::Language.new(mm.ecore,
325
+ :root_classes => mm.ecore.eAllClasses,
326
+ :unlabled_arguments => lambda {|c| ["unlabled1", "unlabled2"]})
327
+ context = RText::ContextBuilder.build_context(lang, context_lines, pos_in_line)
328
+ RText::Completer.new(lang).complete(context, ref_comp_option_provider)
329
+ end
330
+
331
+ end
332
+