rtext 0.8.0 → 0.9.1
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 +5 -5
- data/CHANGELOG +102 -84
- data/RText_Protocol +46 -3
- data/Rakefile +46 -46
- data/lib/rtext/context_builder.rb +49 -8
- data/lib/rtext/default_completer.rb +212 -163
- data/lib/rtext/default_service_provider.rb +3 -3
- data/lib/rtext/frontend/context.rb +12 -12
- data/lib/rtext/language.rb +5 -5
- data/lib/rtext/serializer.rb +1 -1
- data/lib/rtext/service.rb +264 -253
- data/lib/rtext/tokenizer.rb +1 -1
- data/test/completer_test.rb +606 -606
- data/test/context_builder_test.rb +948 -948
- data/test/frontend/context_test.rb +301 -205
- data/test/instantiator_test.rb +1732 -1691
- data/test/integration/frontend.log +401 -36030
- data/test/integration/model/test_metamodel3.ect4 +7 -0
- data/test/integration/test.rb +967 -918
- data/test/link_detector_test.rb +287 -287
- data/test/message_helper_test.rb +116 -118
- data/test/serializer_test.rb +1023 -1004
- data/test/tokenizer_test.rb +173 -173
- metadata +18 -19
- data/test/integration/backend.out +0 -13
@@ -1,163 +1,212 @@
|
|
1
|
-
require 'rgen/ecore/ecore_ext'
|
2
|
-
|
3
|
-
module RText
|
4
|
-
|
5
|
-
class DefaultCompleter
|
6
|
-
|
7
|
-
CompletionOption
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
(
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
end
|
72
|
-
|
73
|
-
def
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
end
|
161
|
-
|
162
|
-
end
|
163
|
-
|
1
|
+
require 'rgen/ecore/ecore_ext'
|
2
|
+
|
3
|
+
module RText
|
4
|
+
|
5
|
+
class DefaultCompleter
|
6
|
+
|
7
|
+
class CompletionOption
|
8
|
+
|
9
|
+
attr_accessor :insert
|
10
|
+
attr_accessor :display
|
11
|
+
attr_accessor :extra
|
12
|
+
attr_accessor :description
|
13
|
+
|
14
|
+
def self.from_text_extra(text, extra)
|
15
|
+
self.new(text, text + ' ' + extra, nil, extra)
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.for_curly_braces(context)
|
19
|
+
self.new("{\n#{context.line_indent}#{context.indent}||\n#{context.line_indent}}", '{}')
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.for_square_brackets
|
23
|
+
self.new('[ || ]', '[]', '')
|
24
|
+
end
|
25
|
+
|
26
|
+
def initialize(insert, display, description=nil, extra=nil)
|
27
|
+
@insert = insert
|
28
|
+
@display = display
|
29
|
+
@description = description
|
30
|
+
@extra = extra
|
31
|
+
end
|
32
|
+
|
33
|
+
def text
|
34
|
+
@insert
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
# Creates a completer for RText::Language +language+.
|
40
|
+
#
|
41
|
+
def initialize(language)
|
42
|
+
@lang = language
|
43
|
+
end
|
44
|
+
|
45
|
+
# Provides completion options
|
46
|
+
#
|
47
|
+
def complete(context, version=0)
|
48
|
+
clazz = context && context.element && context.element.class.ecore
|
49
|
+
if clazz
|
50
|
+
if context.position.in_block
|
51
|
+
block_options(context, clazz)
|
52
|
+
elsif !context.problem
|
53
|
+
result = []
|
54
|
+
add_value_options(context, result, version) if context.feature
|
55
|
+
add_label_options(context, clazz, result, version) unless context.position.after_label
|
56
|
+
result
|
57
|
+
else
|
58
|
+
# missing comma, after curly brace, etc.
|
59
|
+
if version > 0 && !context.position.before_brace &&
|
60
|
+
context.element.class.ecore.eAllReferences.any? { |r| r.containment }
|
61
|
+
[CompletionOption.for_curly_braces(context)]
|
62
|
+
else
|
63
|
+
[]
|
64
|
+
end
|
65
|
+
end
|
66
|
+
elsif context
|
67
|
+
root_options
|
68
|
+
else
|
69
|
+
[]
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def block_options(context, clazz)
|
74
|
+
types = []
|
75
|
+
labled_refs = []
|
76
|
+
if context.feature
|
77
|
+
if context.feature.is_a?(RGen::ECore::EReference) && context.feature.containment
|
78
|
+
types = @lang.concrete_types(context.feature.eType)
|
79
|
+
else
|
80
|
+
# invalid, ignore
|
81
|
+
end
|
82
|
+
else
|
83
|
+
# all target types which don't need a label
|
84
|
+
# and all lables which are needed by a potential target type
|
85
|
+
@lang.containments(clazz).each do |r|
|
86
|
+
([r.eType] + r.eType.eAllSubTypes).select{|t| t.concrete}.each do |t|
|
87
|
+
if @lang.labeled_containment?(clazz, r) || @lang.containments_by_target_type(clazz, t).size > 1
|
88
|
+
labled_refs << r
|
89
|
+
else
|
90
|
+
types << t
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
types.uniq.
|
96
|
+
sort{|a,b| a.name <=> b.name}.collect do |c|
|
97
|
+
class_completion_option(c)
|
98
|
+
end +
|
99
|
+
labled_refs.uniq.collect do |r|
|
100
|
+
CompletionOption.from_text_extra("#{r.name}:", "<#{r.eType.name}>")
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def add_value_options(context, result, version)
|
105
|
+
if context.feature.is_a?(RGen::ECore::EAttribute) || !context.feature.containment
|
106
|
+
if context.feature.is_a?(RGen::ECore::EReference)
|
107
|
+
result.concat(reference_options(context))
|
108
|
+
if version > 0 && !context.position.before_bracket && context.feature.upperBound != 1
|
109
|
+
result << CompletionOption.for_square_brackets
|
110
|
+
end
|
111
|
+
elsif context.feature.eType.is_a?(RGen::ECore::EEnum)
|
112
|
+
result.concat(enum_options(context))
|
113
|
+
elsif context.feature.eType.instanceClass == String
|
114
|
+
result.concat(string_options(context))
|
115
|
+
elsif context.feature.eType.instanceClass == Integer
|
116
|
+
result.concat(integer_options(context))
|
117
|
+
elsif context.feature.eType.instanceClass == Float
|
118
|
+
result.concat(float_options(context))
|
119
|
+
elsif context.feature.eType.instanceClass == RGen::MetamodelBuilder::DataTypes::Boolean
|
120
|
+
result.concat(boolean_options(context))
|
121
|
+
else
|
122
|
+
# no options
|
123
|
+
end
|
124
|
+
else
|
125
|
+
if version > 0 && !context.position.before_bracket && context.feature.upperBound != 1
|
126
|
+
result << CompletionOption.for_square_brackets
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def add_label_options(context, clazz, result, version)
|
132
|
+
result.concat(@lang.labled_arguments(clazz).
|
133
|
+
select{|f|
|
134
|
+
!context.element.eIsSet(f.name)}.collect do |f|
|
135
|
+
CompletionOption.from_text_extra("#{f.name}:", "<#{f.eType.name}>")
|
136
|
+
end )
|
137
|
+
if version > 0 && !context.position.after_comma &&
|
138
|
+
context.element.class.ecore.eAllReferences.any? { |r| r.containment } && !context.position.before_brace
|
139
|
+
result << CompletionOption.for_curly_braces(context)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def root_options
|
144
|
+
@lang.root_classes.
|
145
|
+
sort{|a,b| a.name <=> b.name}.collect do |c|
|
146
|
+
class_completion_option(c)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def reference_options(context)
|
151
|
+
[]
|
152
|
+
end
|
153
|
+
|
154
|
+
def enum_options(context)
|
155
|
+
context.feature.eType.eLiterals.collect do |l|
|
156
|
+
lname = l.name
|
157
|
+
if lname =~ /^\d|\W/ || lname == "true" || lname == "false"
|
158
|
+
lname = "\"#{lname.gsub("\\","\\\\\\\\").gsub("\"","\\\"").gsub("\n","\\n").
|
159
|
+
gsub("\r","\\r").gsub("\t","\\t").gsub("\f","\\f").gsub("\b","\\b")}\""
|
160
|
+
end
|
161
|
+
CompletionOption.from_text_extra("#{lname}", "<#{context.feature.eType.name}>")
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
def string_options(context)
|
166
|
+
if @lang.unquoted?(context.feature)
|
167
|
+
[ CompletionOption.from_text_extra("#{context.feature.name.gsub(/\W/,"")}", value_description(context)) ]
|
168
|
+
else
|
169
|
+
[ CompletionOption.from_text_extra("\"\"", value_description(context)) ]
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
def get_default_value_completion(context)
|
174
|
+
return nil unless context.feature.defaultValue
|
175
|
+
CompletionOption.from_text_extra("#{context.feature.defaultValue}", value_description(context))
|
176
|
+
end
|
177
|
+
|
178
|
+
def integer_options(context)
|
179
|
+
default_comp = get_default_value_completion(context)
|
180
|
+
return [default_comp] if default_comp
|
181
|
+
(0..0).collect{|i| CompletionOption.from_text_extra("#{i}", value_description(context)) }
|
182
|
+
end
|
183
|
+
|
184
|
+
def float_options(context)
|
185
|
+
default_comp = get_default_value_completion(context)
|
186
|
+
return [default_comp] if default_comp
|
187
|
+
(0..0).collect{|i| CompletionOption.from_text_extra("#{i}.0", value_description(context)) }
|
188
|
+
end
|
189
|
+
|
190
|
+
def boolean_options(context)
|
191
|
+
[true, false].collect{|b| CompletionOption.from_text_extra("#{b}", value_description(context)) }
|
192
|
+
end
|
193
|
+
|
194
|
+
private
|
195
|
+
|
196
|
+
def value_description(context)
|
197
|
+
if context.position.after_label
|
198
|
+
"<#{context.feature.eType.name}>"
|
199
|
+
else
|
200
|
+
"[#{context.feature.name}] <#{context.feature.eType.name}>"
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
def class_completion_option(eclass)
|
205
|
+
uargs = @lang.unlabled_arguments(eclass).collect{|a| "<#{a.name}>"}.join(", ")
|
206
|
+
CompletionOption.from_text_extra(@lang.command_by_class(eclass.instanceClass), uargs)
|
207
|
+
end
|
208
|
+
|
209
|
+
end
|
210
|
+
|
211
|
+
end
|
212
|
+
|
@@ -35,17 +35,17 @@ class DefaultServiceProvider
|
|
35
35
|
end
|
36
36
|
end
|
37
37
|
|
38
|
-
def get_completion_options(context)
|
38
|
+
def get_completion_options(context, version=0)
|
39
39
|
completer = RText::DefaultCompleter.new(@lang)
|
40
40
|
class << completer
|
41
41
|
attr_accessor :service_provider
|
42
42
|
def reference_options(context)
|
43
43
|
service_provider.get_reference_completion_options(context).collect {|o|
|
44
|
-
DefaultCompleter::CompletionOption.
|
44
|
+
DefaultCompleter::CompletionOption.from_text_extra(o.identifier, "<#{o.type}>")}
|
45
45
|
end
|
46
46
|
end
|
47
47
|
completer.service_provider = self
|
48
|
-
completer.complete(context)
|
48
|
+
completer.complete(context, version)
|
49
49
|
end
|
50
50
|
|
51
51
|
ReferenceCompletionOption = Struct.new(:identifier, :type)
|
@@ -55,27 +55,27 @@ def filter_lines(lines)
|
|
55
55
|
}
|
56
56
|
end
|
57
57
|
|
58
|
-
# when joining two lines, all whitespace
|
59
|
-
#
|
60
|
-
#
|
61
|
-
# (i.e. with whitespace after the last non-whitespace character)
|
58
|
+
# when joining two lines, all whitespace is preserved in order to simplify the algorithm
|
59
|
+
# whitespace after a backslash is also preserved, only the backslash itself is removed
|
60
|
+
# note that whitespace left of the cursor is important for proper context calculation
|
62
61
|
def join_lines(lines, pos)
|
63
62
|
outlines = []
|
64
63
|
while lines.size > 0
|
65
64
|
outlines << lines.shift
|
66
65
|
while lines.size > 0 &&
|
67
|
-
(outlines.last =~
|
68
|
-
|
69
|
-
|
70
|
-
|
66
|
+
(outlines.last =~ /[,\\]\s*$/ ||
|
67
|
+
# don't join after a child label
|
68
|
+
(outlines.last !~ /^\s*\w+:/ &&
|
69
|
+
(outlines.last =~ /\[\s*$/ ||
|
70
|
+
(lines.first =~ /^\s*\]/ && outlines.last =~ /\[/))))
|
71
71
|
l = lines.shift
|
72
|
+
outlines.last.gsub!("\\","")
|
72
73
|
if lines.size == 0
|
73
|
-
#
|
74
|
+
# the prefix might have whitespace on the
|
74
75
|
# right hand side which is relevant for the position
|
75
|
-
|
76
|
-
pos = outlines.last.size + non_ws_prefix.size
|
76
|
+
pos = outlines.last.size + pos
|
77
77
|
end
|
78
|
-
outlines.last.concat(l
|
78
|
+
outlines.last.concat(l)
|
79
79
|
end
|
80
80
|
end
|
81
81
|
[outlines, pos]
|
data/lib/rtext/language.rb
CHANGED
@@ -260,7 +260,7 @@ class Language
|
|
260
260
|
end
|
261
261
|
|
262
262
|
def concrete_types(clazz)
|
263
|
-
([clazz] + clazz.eAllSubTypes).select{|c|
|
263
|
+
([clazz] + clazz.eAllSubTypes).select{|c| c.concrete}
|
264
264
|
end
|
265
265
|
|
266
266
|
def containments_by_target_type(clazz, type)
|
@@ -299,7 +299,7 @@ class Language
|
|
299
299
|
@command_by_class = {}
|
300
300
|
@has_command = {}
|
301
301
|
root_epackage.eAllClasses.each do |c|
|
302
|
-
next
|
302
|
+
next unless c.concrete
|
303
303
|
cmd = command_name_provider.call(c)
|
304
304
|
@command_by_class[c.instanceClass] = cmd
|
305
305
|
@has_command[cmd] = true
|
@@ -307,7 +307,7 @@ class Language
|
|
307
307
|
@class_by_command[clazz] ||= {}
|
308
308
|
containments(c).collect{|r|
|
309
309
|
[r.eType] + r.eType.eAllSubTypes}.flatten.uniq.each do |t|
|
310
|
-
next
|
310
|
+
next unless t.concrete
|
311
311
|
cmw = command_name_provider.call(t)
|
312
312
|
raise "ambiguous command name #{cmw}" if @class_by_command[clazz][cmw]
|
313
313
|
@class_by_command[clazz][cmw] = t.instanceClass
|
@@ -315,7 +315,7 @@ class Language
|
|
315
315
|
end
|
316
316
|
@class_by_command[nil] = {}
|
317
317
|
@root_classes.each do |c|
|
318
|
-
next
|
318
|
+
next unless c.concrete
|
319
319
|
cmw = command_name_provider.call(c)
|
320
320
|
raise "ambiguous command name #{cmw}" if @class_by_command[nil][cmw]
|
321
321
|
@class_by_command[nil][cmw] = c.instanceClass
|
@@ -323,7 +323,7 @@ class Language
|
|
323
323
|
end
|
324
324
|
|
325
325
|
def default_root_classes(root_package)
|
326
|
-
root_epackage.eAllClasses.select{|c|
|
326
|
+
root_epackage.eAllClasses.select{|c| c.concrete &&
|
327
327
|
!c.eAllReferences.any?{|r| r.eOpposite && r.eOpposite.containment}}
|
328
328
|
end
|
329
329
|
|
data/lib/rtext/serializer.rb
CHANGED
@@ -179,7 +179,7 @@ class Serializer
|
|
179
179
|
result << v.to_s
|
180
180
|
end
|
181
181
|
elsif feature.eType.instanceClass == Object
|
182
|
-
if v.to_s =~
|
182
|
+
if v.to_s =~ /^-?\d+(\.\d+)?$|^\w+$|^true$|^false$/
|
183
183
|
result << v.to_s
|
184
184
|
else
|
185
185
|
result << "\"#{v.to_s.gsub("\\","\\\\\\\\").gsub("\"","\\\"").gsub("\n","\\n").
|