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.
- data/CHANGELOG +10 -2
- data/Rakefile +1 -1
- data/lib/rtext/completer.rb +84 -111
- data/lib/rtext/context_builder.rb +188 -0
- data/lib/rtext/default_loader.rb +8 -0
- data/lib/rtext/default_service_provider.rb +21 -18
- data/lib/rtext/instantiator.rb +28 -9
- data/lib/rtext/language.rb +51 -21
- data/lib/rtext/parser.rb +6 -4
- data/lib/rtext/serializer.rb +3 -2
- data/lib/rtext/service.rb +7 -9
- data/test/completer_test.rb +332 -0
- data/test/context_builder_test.rb +360 -0
- data/test/instantiator_test.rb +95 -13
- data/test/rtext_test.rb +2 -0
- data/test/serializer_test.rb +9 -8
- metadata +8 -11
- data/lib/rtext/context_element_builder.rb +0 -112
@@ -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
|
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
|
-
|
44
|
-
|
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
|
-
|
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
|
data/lib/rtext/instantiator.rb
CHANGED
@@ -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
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
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 = @
|
79
|
-
if
|
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
|
data/lib/rtext/language.rb
CHANGED
@@ -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
|
91
|
-
#
|
92
|
-
#
|
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
|
-
@
|
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
|
-
(
|
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[
|
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,
|
10
|
-
@
|
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
|
-
@
|
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
|
-
@
|
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)
|
data/lib/rtext/serializer.rb
CHANGED
@@ -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
|
-
|
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 =~
|
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/
|
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 =
|
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(
|
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
|
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
|
+
|