rtext 0.8.1 → 0.10.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.
- checksums.yaml +4 -4
- data/CHANGELOG +120 -89
- data/Project.yaml +15 -0
- data/RText_Protocol +47 -4
- 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/connector.rb +130 -56
- data/lib/rtext/instantiator.rb +11 -3
- data/lib/rtext/language.rb +5 -5
- data/lib/rtext/serializer.rb +3 -3
- data/lib/rtext/service.rb +281 -253
- data/lib/rtext/tokenizer.rb +2 -2
- metadata +33 -33
- data/Rakefile +0 -46
- data/test/completer_test.rb +0 -606
- data/test/context_builder_test.rb +0 -948
- data/test/frontend/context_test.rb +0 -301
- data/test/instantiator_test.rb +0 -1704
- data/test/integration/backend.out +0 -13
- data/test/integration/crash_on_request_editor.rb +0 -12
- data/test/integration/ecore_editor.rb +0 -50
- data/test/integration/frontend.log +0 -38203
- data/test/integration/model/invalid_encoding.invenc +0 -2
- data/test/integration/model/test.crash_on_request +0 -18
- data/test/integration/model/test.crashing_backend +0 -18
- data/test/integration/model/test.dont_open_socket +0 -0
- data/test/integration/model/test.invalid_cmd_line +0 -0
- data/test/integration/model/test.not_in_rtext +0 -0
- data/test/integration/model/test_large_with_errors.ect3 +0 -43523
- data/test/integration/model/test_metamodel.ect +0 -24
- data/test/integration/model/test_metamodel2.ect +0 -5
- data/test/integration/model/test_metamodel3.ect4 +0 -7
- data/test/integration/model/test_metamodel_error.ect2 +0 -3
- data/test/integration/model/test_metamodel_ok.ect2 +0 -18
- data/test/integration/test.rb +0 -966
- data/test/link_detector_test.rb +0 -287
- data/test/message_helper_test.rb +0 -118
- data/test/rtext_test.rb +0 -11
- data/test/serializer_test.rb +0 -1004
- data/test/tokenizer_test.rb +0 -173
@@ -21,18 +21,25 @@ def initialize(config, options={})
|
|
21
21
|
@outfile_provider = options[:outfile_provider]
|
22
22
|
@keep_outfile = options[:keep_outfile]
|
23
23
|
@connection_timeout = options[:connection_timeout] || 10
|
24
|
+
@process_id = nil
|
24
25
|
end
|
25
26
|
|
26
27
|
def execute_command(obj, options={})
|
27
|
-
timeout = options[:timeout] ||
|
28
|
+
timeout = options[:timeout] || 10
|
28
29
|
@busy = false if @busy_start_time && (Time.now > @busy_start_time + timeout)
|
29
30
|
if @busy
|
30
31
|
do_work
|
31
|
-
:backend_busy
|
32
|
-
|
32
|
+
return :backend_busy
|
33
|
+
end
|
34
|
+
unless connected?
|
35
|
+
connect unless connecting?
|
36
|
+
do_work
|
37
|
+
end
|
38
|
+
if connected?
|
33
39
|
obj["invocation_id"] = @invocation_id
|
34
40
|
obj["type"] = "request"
|
35
41
|
@socket.send(serialize_message(obj), 0)
|
42
|
+
@logger.debug('Sent request') if @logger
|
36
43
|
result = nil
|
37
44
|
@busy = true
|
38
45
|
@busy_start_time = Time.now
|
@@ -66,9 +73,8 @@ def execute_command(obj, options={})
|
|
66
73
|
result
|
67
74
|
end
|
68
75
|
else
|
69
|
-
|
70
|
-
|
71
|
-
:connecting
|
76
|
+
@logger.debug('connecting')
|
77
|
+
:connecting
|
72
78
|
end
|
73
79
|
end
|
74
80
|
|
@@ -83,27 +89,72 @@ def stop
|
|
83
89
|
end
|
84
90
|
if connected?
|
85
91
|
execute_command({"type" => "request", "command" => "stop"})
|
86
|
-
while do_work
|
92
|
+
while do_work
|
87
93
|
sleep(0.1)
|
88
94
|
end
|
89
95
|
end
|
96
|
+
ensure_process_cleanup(@process_id, @keep_outfile ? nil : @out_file, 10)
|
97
|
+
@process_id = nil
|
90
98
|
end
|
91
99
|
|
92
100
|
private
|
93
101
|
|
102
|
+
def wait_for_process_to_exit(process_id, timeout)
|
103
|
+
with_timeout timeout do
|
104
|
+
begin
|
105
|
+
waitpid(process_id, Process::WNOHANG)
|
106
|
+
process_id = nil
|
107
|
+
true
|
108
|
+
rescue Errno::ECHILD => _
|
109
|
+
false
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def ensure_process_cleanup(process_id, out_file, timeout)
|
115
|
+
Thread.new do
|
116
|
+
begin
|
117
|
+
unless process_id.nil?
|
118
|
+
process_id = nil if wait_for_process_to_exit(process_id, timeout)
|
119
|
+
end
|
120
|
+
ensure
|
121
|
+
unless process_id.nil?
|
122
|
+
begin
|
123
|
+
Process.kill('QUIT', process_id)
|
124
|
+
rescue Errno::ESRCH => _
|
125
|
+
end
|
126
|
+
end
|
127
|
+
File.unlink(out_file) if !out_file.nil? && File.exist?(out_file)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def with_timeout(timeout, sleep_time = 0.1, &block)
|
133
|
+
started = Time.now
|
134
|
+
while true do
|
135
|
+
return true if block.call
|
136
|
+
if Time.now > started + timeout
|
137
|
+
return false
|
138
|
+
end
|
139
|
+
sleep(sleep_time)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
|
94
144
|
def connected?
|
95
|
-
@state == :
|
145
|
+
!@process_id.nil? && @state == :read_from_socket && backend_running?
|
96
146
|
end
|
97
147
|
|
98
148
|
def connecting?
|
99
|
-
@state == :
|
149
|
+
!@process_id.nil? && (@state == :wait_for_file || @state == :wait_for_port)
|
100
150
|
end
|
101
151
|
|
102
152
|
def backend_running?
|
103
153
|
if @process_id
|
104
154
|
begin
|
105
|
-
|
106
|
-
|
155
|
+
waitpid(@process_id, Process::WNOHANG)
|
156
|
+
return true
|
157
|
+
rescue Errno::ECHILD => _
|
107
158
|
end
|
108
159
|
end
|
109
160
|
false
|
@@ -121,66 +172,87 @@ def tempfile_name
|
|
121
172
|
end
|
122
173
|
|
123
174
|
def connect
|
124
|
-
|
175
|
+
return if connected?
|
125
176
|
@connect_start_time = Time.now
|
126
177
|
|
127
178
|
@logger.info @config.command if @logger
|
128
179
|
|
129
180
|
if @outfile_provider
|
130
|
-
@out_file = @outfile_provider.call
|
181
|
+
@out_file = File.expand_path(@outfile_provider.call)
|
131
182
|
else
|
132
|
-
@out_file = tempfile_name
|
183
|
+
@out_file = File.expand_path(tempfile_name)
|
133
184
|
end
|
134
|
-
File.unlink(@out_file) if File.exist?(@out_file)
|
135
185
|
|
136
|
-
|
137
|
-
@
|
186
|
+
if @process_id.nil?
|
187
|
+
File.unlink(@out_file) if File.exist?(@out_file)
|
188
|
+
Dir.chdir(File.dirname(@config.file)) do
|
189
|
+
@logger.debug(@config.command.strip + " > #{@out_file} 2>&1") if @logger
|
190
|
+
@process_id = spawn(@config.command.strip + " > #{@out_file} 2>&1")
|
191
|
+
@state = :wait_for_file
|
192
|
+
end
|
138
193
|
end
|
139
|
-
@work_state = :wait_for_file
|
140
194
|
end
|
141
195
|
|
142
196
|
def do_work
|
143
|
-
|
144
|
-
|
145
|
-
if
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
197
|
+
unless @process_id
|
198
|
+
@state = :off
|
199
|
+
@logger.debug('No process id') if @logger
|
200
|
+
return false
|
201
|
+
end
|
202
|
+
if @state == :wait_for_port && !File.exist?(@out_file)
|
203
|
+
@state = :wait_for_file
|
204
|
+
end
|
205
|
+
if @state == :wait_for_file && File.exist?(@out_file)
|
206
|
+
@state = :wait_for_port
|
207
|
+
end
|
208
|
+
if @state == :wait_for_file
|
209
|
+
while true
|
210
|
+
if Time.now > @connect_start_time + @connection_timeout
|
211
|
+
cleanup
|
212
|
+
@connection_listener.call(:timeout) if @connection_listener
|
213
|
+
@state = :off
|
214
|
+
@logger.warn "process didn't startup (connection timeout)" if @logger
|
215
|
+
return false
|
216
|
+
end
|
217
|
+
sleep(0.1)
|
218
|
+
if File.exist?(@out_file)
|
219
|
+
@state = :wait_for_port
|
220
|
+
break
|
221
|
+
end
|
154
222
|
end
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
@
|
163
|
-
|
164
|
-
|
223
|
+
end
|
224
|
+
if @state == :wait_for_port
|
225
|
+
while true
|
226
|
+
break unless File.exist?(@out_file)
|
227
|
+
output = File.read(@out_file)
|
228
|
+
if output =~ /^RText service, listening on port (\d+)/
|
229
|
+
port = $1.to_i
|
230
|
+
@logger.info "connecting to #{port}" if @logger
|
231
|
+
begin
|
232
|
+
@socket = TCPSocket.new("127.0.0.1", port)
|
233
|
+
@socket.setsockopt(:SOCKET, :RCVBUF, 1000000)
|
234
|
+
rescue Errno::ECONNREFUSED
|
235
|
+
cleanup
|
236
|
+
@connection_listener.call(:timeout) if @connection_listener
|
237
|
+
@state = :off
|
238
|
+
@logger.warn "could not connect socket (connection timeout)" if @logger
|
239
|
+
return false
|
240
|
+
end
|
241
|
+
@state = :read_from_socket
|
242
|
+
@connection_listener.call(:connected) if @connection_listener
|
243
|
+
break
|
244
|
+
end
|
245
|
+
if Time.now > @connect_start_time + @connection_timeout
|
165
246
|
cleanup
|
166
247
|
@connection_listener.call(:timeout) if @connection_listener
|
167
|
-
@work_state = :done
|
168
248
|
@state = :off
|
169
249
|
@logger.warn "could not connect socket (connection timeout)" if @logger
|
250
|
+
return false
|
170
251
|
end
|
171
|
-
|
172
|
-
@work_state = :read_from_socket
|
173
|
-
@connection_listener.call(:connected) if @connection_listener
|
174
|
-
end
|
175
|
-
if Time.now > @connect_start_time + @connection_timeout
|
176
|
-
cleanup
|
177
|
-
@connection_listener.call(:timeout) if @connection_listener
|
178
|
-
@work_state = :done
|
179
|
-
@state = :off
|
180
|
-
@logger.warn "could not connect socket (connection timeout)" if @logger
|
252
|
+
sleep(0.1)
|
181
253
|
end
|
182
|
-
|
183
|
-
|
254
|
+
end
|
255
|
+
if @state == :read_from_socket
|
184
256
|
repeat = true
|
185
257
|
socket_closed = false
|
186
258
|
response_data = ""
|
@@ -195,6 +267,7 @@ def do_work
|
|
195
267
|
@logger.info "server socket closed (end of file)" if @logger
|
196
268
|
end
|
197
269
|
if data
|
270
|
+
@logger.debug('Got data') if @logger
|
198
271
|
repeat = true
|
199
272
|
response_data.concat(data)
|
200
273
|
while obj = extract_message(response_data)
|
@@ -208,13 +281,13 @@ def do_work
|
|
208
281
|
end
|
209
282
|
elsif !backend_running? || socket_closed
|
210
283
|
cleanup
|
211
|
-
@
|
284
|
+
@state = :off
|
285
|
+
@logger.debug("backend_running?: #{backend_running?}, socket_closed: #{socket_closed}") if @logger
|
212
286
|
return false
|
213
287
|
end
|
214
288
|
end
|
215
|
-
true
|
216
289
|
end
|
217
|
-
|
290
|
+
true
|
218
291
|
end
|
219
292
|
|
220
293
|
def cleanup
|
@@ -224,7 +297,8 @@ def cleanup
|
|
224
297
|
break unless backend_running?
|
225
298
|
sleep(0.1)
|
226
299
|
end
|
227
|
-
|
300
|
+
ensure_process_cleanup(@process_id, @keep_outfile ? nil : @out_file, 10)
|
301
|
+
@process_id = nil
|
228
302
|
end
|
229
303
|
|
230
304
|
end
|
data/lib/rtext/instantiator.rb
CHANGED
@@ -247,7 +247,15 @@ class Instantiator
|
|
247
247
|
element.setOrAddGeneric(feature.name, proxy)
|
248
248
|
else
|
249
249
|
begin
|
250
|
-
|
250
|
+
v_value = v.value
|
251
|
+
feature_instance_class = feature.eType.instanceClass
|
252
|
+
if feature_instance_class == String && (v_value.is_a?(Float) || v_value.is_a?(Fixnum))
|
253
|
+
element.setOrAddGeneric(feature.name, v_value.to_s)
|
254
|
+
elsif feature_instance_class == Float && v_value.is_a?(Fixnum)
|
255
|
+
element.setOrAddGeneric(feature.name, v_value.to_f)
|
256
|
+
else
|
257
|
+
element.setOrAddGeneric(feature.name, v_value)
|
258
|
+
end
|
251
259
|
rescue StandardError
|
252
260
|
# backward compatibility for RGen versions not supporting BigDecimal
|
253
261
|
if v.value.is_a?(BigDecimal)
|
@@ -302,9 +310,9 @@ class Instantiator
|
|
302
310
|
elsif feature.eType.is_a?(RGen::ECore::EEnum)
|
303
311
|
[:identifier, :string]
|
304
312
|
else
|
305
|
-
expected = { String => [:string, :identifier],
|
313
|
+
expected = { String => [:string, :identifier, :integer, :float],
|
306
314
|
Integer => [:integer],
|
307
|
-
Float => [:float],
|
315
|
+
Float => [:float, :integer],
|
308
316
|
RGen::MetamodelBuilder::DataTypes::Boolean => [:boolean],
|
309
317
|
Object => [:string, :identifier, :integer, :float, :boolean]
|
310
318
|
}[feature.eType.instanceClass]
|
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
@@ -152,7 +152,7 @@ class Serializer
|
|
152
152
|
result << v.to_s
|
153
153
|
end
|
154
154
|
elsif feature.eType.instanceClass == String
|
155
|
-
if @lang.unquoted?(feature) && v.to_s =~ /^[a-zA-Z_]\w*$/ && v.to_s != "true" && v.to_s != "false"
|
155
|
+
if @lang.unquoted?(feature) && v.to_s =~ /^[a-zA-Z_]\w*$/m && v.to_s != "true" && v.to_s != "false"
|
156
156
|
result << v.to_s
|
157
157
|
else
|
158
158
|
result << "\"#{v.gsub("\\","\\\\\\\\").gsub("\"","\\\"").gsub("\n","\\n").
|
@@ -166,7 +166,7 @@ class Serializer
|
|
166
166
|
# formatting not available for BigDecimals
|
167
167
|
else
|
168
168
|
if arg_format
|
169
|
-
|
169
|
+
result << sprintf(arg_format, v)
|
170
170
|
else
|
171
171
|
result << v.to_s
|
172
172
|
end
|
@@ -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").
|