rtext 0.8.0 → 0.9.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,163 +1,212 @@
1
- require 'rgen/ecore/ecore_ext'
2
-
3
- module RText
4
-
5
- class DefaultCompleter
6
-
7
- CompletionOption = Struct.new(:text, :extra)
8
-
9
- # Creates a completer for RText::Language +language+.
10
- #
11
- def initialize(language)
12
- @lang = language
13
- end
14
-
15
- # Provides completion options
16
- #
17
- def complete(context)
18
- clazz = context && context.element && context.element.class.ecore
19
- if clazz
20
- if context.in_block
21
- block_options(context, clazz)
22
- elsif !context.problem
23
- result = []
24
- if context.feature
25
- add_value_options(context, result)
26
- end
27
- if !context.after_label
28
- add_label_options(context, clazz, result)
29
- end
30
- result
31
- else
32
- # missing comma, after curly brace, etc.
33
- []
34
- end
35
- elsif context
36
- root_options
37
- else
38
- []
39
- end
40
- end
41
-
42
- def block_options(context, clazz)
43
- types = []
44
- labled_refs = []
45
- if context.feature
46
- if context.feature.is_a?(RGen::ECore::EReference) && context.feature.containment
47
- types = @lang.concrete_types(context.feature.eType)
48
- else
49
- # invalid, ignore
50
- end
51
- else
52
- # all target types which don't need a label
53
- # and all lables which are needed by a potential target type
54
- @lang.containments(clazz).each do |r|
55
- ([r.eType] + r.eType.eAllSubTypes).select{|t| !t.abstract}.each do |t|
56
- if @lang.labeled_containment?(clazz, r) || @lang.containments_by_target_type(clazz, t).size > 1
57
- labled_refs << r
58
- else
59
- types << t
60
- end
61
- end
62
- end
63
- end
64
- types.uniq.
65
- sort{|a,b| a.name <=> b.name}.collect do |c|
66
- class_completion_option(c)
67
- end +
68
- labled_refs.uniq.collect do |r|
69
- CompletionOption.new("#{r.name}:", "<#{r.eType.name}>")
70
- end
71
- end
72
-
73
- def add_value_options(context, result)
74
- if context.feature.is_a?(RGen::ECore::EAttribute) || !context.feature.containment
75
- if context.feature.is_a?(RGen::ECore::EReference)
76
- result.concat(reference_options(context))
77
- elsif context.feature.eType.is_a?(RGen::ECore::EEnum)
78
- result.concat(enum_options(context))
79
- elsif context.feature.eType.instanceClass == String
80
- result.concat(string_options(context))
81
- elsif context.feature.eType.instanceClass == Integer
82
- result.concat(integer_options(context))
83
- elsif context.feature.eType.instanceClass == Float
84
- result.concat(float_options(context))
85
- elsif context.feature.eType.instanceClass == RGen::MetamodelBuilder::DataTypes::Boolean
86
- result.concat(boolean_options(context))
87
- else
88
- # no options
89
- end
90
- else
91
- # containment reference, ignore
92
- end
93
- end
94
-
95
- def add_label_options(context, clazz, result)
96
- result.concat(@lang.labled_arguments(clazz).
97
- select{|f|
98
- !context.element.eIsSet(f.name)}.collect do |f|
99
- CompletionOption.new("#{f.name}:", "<#{f.eType.name}>")
100
- end )
101
- end
102
-
103
- def root_options
104
- @lang.root_classes.
105
- sort{|a,b| a.name <=> b.name}.collect do |c|
106
- class_completion_option(c)
107
- end
108
- end
109
-
110
- def reference_options(context)
111
- []
112
- end
113
-
114
- def enum_options(context)
115
- context.feature.eType.eLiterals.collect do |l|
116
- lname = l.name
117
- if lname =~ /^\d|\W/ || lname == "true" || lname == "false"
118
- lname = "\"#{lname.gsub("\\","\\\\\\\\").gsub("\"","\\\"").gsub("\n","\\n").
119
- gsub("\r","\\r").gsub("\t","\\t").gsub("\f","\\f").gsub("\b","\\b")}\""
120
- end
121
- CompletionOption.new("#{lname}", "<#{context.feature.eType.name}>")
122
- end
123
- end
124
-
125
- def string_options(context)
126
- if @lang.unquoted?(context.feature)
127
- [ CompletionOption.new("#{context.feature.name.gsub(/\W/,"")}", value_description(context)) ]
128
- else
129
- [ CompletionOption.new("\"\"", value_description(context)) ]
130
- end
131
- end
132
-
133
- def integer_options(context)
134
- (0..0).collect{|i| CompletionOption.new("#{i}", value_description(context)) }
135
- end
136
-
137
- def float_options(context)
138
- (0..0).collect{|i| CompletionOption.new("#{i}.0", value_description(context)) }
139
- end
140
-
141
- def boolean_options(context)
142
- [true, false].collect{|b| CompletionOption.new("#{b}", value_description(context)) }
143
- end
144
-
145
- private
146
-
147
- def value_description(context)
148
- if context.after_label
149
- "<#{context.feature.eType.name}>"
150
- else
151
- "[#{context.feature.name}] <#{context.feature.eType.name}>"
152
- end
153
- end
154
-
155
- def class_completion_option(eclass)
156
- uargs = @lang.unlabled_arguments(eclass).collect{|a| "<#{a.name}>"}.join(", ")
157
- CompletionOption.new(@lang.command_by_class(eclass.instanceClass), uargs)
158
- end
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.new(o.identifier, "<#{o.type}>")}
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 after the last character of the first line is removed
59
- # (after , and [); however whitespace at the end of the last of several joined lines is preserved;
60
- # this way the context is correct even if the cursor is after the end of the last line
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 =~ /,\s*$/ ||
68
- (outlines.last =~ /\[\s*$/ && outlines.last =~ /,/) ||
69
- (lines.first =~ /^\s*\]/ && outlines.last =~ /,/))
70
- outlines.last.rstrip!
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
- # strip only left part, the prefix might have whitespace on the
74
+ # the prefix might have whitespace on the
74
75
  # right hand side which is relevant for the position
75
- non_ws_prefix = l[0..pos-1].lstrip
76
- pos = outlines.last.size + non_ws_prefix.size
76
+ pos = outlines.last.size + pos
77
77
  end
78
- outlines.last.concat(l.lstrip)
78
+ outlines.last.concat(l)
79
79
  end
80
80
  end
81
81
  [outlines, pos]
@@ -260,7 +260,7 @@ class Language
260
260
  end
261
261
 
262
262
  def concrete_types(clazz)
263
- ([clazz] + clazz.eAllSubTypes).select{|c| !c.abstract}
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 if c.abstract
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 if t.abstract
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 if c.abstract
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| !c.abstract &&
326
+ root_epackage.eAllClasses.select{|c| c.concrete &&
327
327
  !c.eAllReferences.any?{|r| r.eOpposite && r.eOpposite.containment}}
328
328
  end
329
329
 
@@ -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 =~ /^\d+(\.\d+)?$|^\w+$|^true$|^false$/
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").