ruber 0.0.9 → 0.0.10
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +42 -1
- data/lib/ruber/application/application.rb +25 -5
- data/lib/ruber/application/plugin.yaml +2 -10
- data/lib/ruber/component_manager.rb +2 -2
- data/lib/ruber/document_project.rb +5 -3
- data/lib/ruber/editor/document.rb +5 -4
- data/lib/ruber/editor/ktexteditor_wrapper.rb +1 -1
- data/lib/ruber/exception_widgets.rb +1 -1
- data/lib/ruber/main_window/main_window.rb +4 -3
- data/lib/ruber/main_window/main_window_actions.rb +2 -2
- data/lib/ruber/main_window/main_window_internal.rb +1 -1
- data/lib/ruber/output_widget.rb +17 -5
- data/lib/ruber/project.rb +34 -3
- data/lib/ruber/project_dir_scanner.rb +171 -0
- data/lib/ruber/settings_container.rb +7 -7
- data/lib/ruber/settings_dialog.rb +24 -24
- data/lib/ruber/version.rb +1 -1
- data/lib/ruber/world/environment.rb +1 -0
- data/lib/ruber/world/plugin.yaml +7 -2
- data/lib/ruber/{application → world}/project_files_widget.rb +0 -0
- data/lib/ruber/{application → world}/ui/project_files_rule_chooser_widget.rb +2 -2
- data/lib/ruber/{application → world}/ui/project_files_rule_chooser_widget.ui +0 -0
- data/lib/ruber/{application → world}/ui/project_files_widget.rb +2 -2
- data/lib/ruber/{application → world}/ui/project_files_widget.ui +0 -0
- data/plugins/auto_end/auto_end.rb +21 -18
- data/plugins/autosave/autosave.rb +1 -1
- data/plugins/find_in_files/find_in_files.rb +1 -1
- data/plugins/irb/irb.png +0 -0
- data/plugins/irb/irb.rb +142 -0
- data/plugins/irb/irb.svg +240 -0
- data/plugins/irb/irb_controller.rb +541 -0
- data/plugins/irb/irbrc.rb +21 -0
- data/plugins/irb/plugin.yaml +19 -0
- data/plugins/irb/ui/irb_config_widget.rb +151 -0
- data/plugins/irb/ui/irb_config_widget.ui +123 -0
- data/plugins/irb/ui/irb_tool_widget.rb +97 -0
- data/plugins/irb/ui/irb_tool_widget.ui +86 -0
- data/plugins/project_browser/project_browser.rb +1 -1
- data/plugins/rspec/plugin.yaml +6 -3
- data/plugins/rspec/rspec.rb +172 -473
- data/plugins/rspec/tool_widget.rb +462 -0
- data/plugins/rspec/ui/rspec_project_widget.rb +58 -38
- data/plugins/rspec/ui/rspec_project_widget.ui +68 -64
- data/plugins/ruberri/class_formatter.rb +126 -0
- data/plugins/ruberri/method_formatter.rb +90 -0
- data/plugins/ruberri/plugin.yaml +13 -0
- data/plugins/ruberri/ruberri.rb +226 -0
- data/plugins/ruberri/search.rb +111 -0
- data/plugins/ruberri/ui/tool_widget.rb +73 -0
- data/plugins/ruberri/ui/tool_widget.ui +49 -0
- data/plugins/ruby_runner/ruby_runner.rb +2 -2
- data/plugins/ruby_syntax_checker/plugin.yaml +11 -0
- data/plugins/ruby_syntax_checker/ruby_syntax_checker.rb +147 -0
- data/plugins/syntax_checker/plugin.yaml +10 -6
- data/plugins/syntax_checker/syntax_checker.rb +216 -520
- data/plugins/syntax_checker/ui/config_widget.rb +61 -0
- data/plugins/syntax_checker/ui/config_widget.ui +44 -0
- data/plugins/yaml_syntax_checker/plugin.yaml +11 -0
- data/plugins/yaml_syntax_checker/yaml_syntax_checker.rb +62 -0
- data/ruber.desktop +0 -0
- data/spec/auto_end_spec.rb +224 -186
- data/spec/document_project_spec.rb +9 -1
- data/spec/document_spec.rb +9 -0
- data/spec/environment_spec.rb +12 -0
- data/spec/output_widget_spec.rb +69 -2
- data/spec/project_dir_scanner_spec.rb +195 -0
- data/spec/project_spec.rb +43 -73
- data/spec/ruby_syntax_checker_spec.rb +361 -0
- data/spec/syntax_checker_spec.rb +1132 -0
- data/spec/yaml_syntax_checker_spec.rb +130 -0
- metadata +232 -225
- data/lib/ruber/application/project_files_list.rb +0 -320
- data/spec/project_files_list_spec.rb +0 -411
@@ -0,0 +1,541 @@
|
|
1
|
+
=begin
|
2
|
+
Copyright (C) 2011 by Stefano Crocco
|
3
|
+
stefano.crocco@alice.it
|
4
|
+
|
5
|
+
This program is free software; you can redistribute it andor modify
|
6
|
+
it under the terms of the GNU General Public License as published by
|
7
|
+
the Free Software Foundation; either version 2 of the License, or
|
8
|
+
(at your option) any later version.
|
9
|
+
|
10
|
+
This program is distributed in the hope that it will be useful,
|
11
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13
|
+
GNU General Public License for more details.
|
14
|
+
|
15
|
+
You should have received a copy of the GNU General Public License
|
16
|
+
along with this program; if not, write to the
|
17
|
+
Free Software Foundation, Inc.,
|
18
|
+
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
19
|
+
=end
|
20
|
+
|
21
|
+
module Ruber
|
22
|
+
|
23
|
+
module IRB
|
24
|
+
|
25
|
+
# Incapsulates the information about the special prompt used by the plugin
|
26
|
+
class PromptMatcher
|
27
|
+
|
28
|
+
# Mapping between prompt types and characters used to represent them in the prompt
|
29
|
+
TYPES = {'n' => :normal, 's' => :string, 'c' => :statement, 'i' => :indent, 'r' => :return }
|
30
|
+
|
31
|
+
# Mapping between prompt names used by IRB and characters used to represent them in the prompt
|
32
|
+
PROMPTS = {:PROMPT_I => 'n', :PROMPT_N => 'i', :PROMPT_S => 's', :PROMPT_C => 'c', :RETURN => 'r'}
|
33
|
+
|
34
|
+
# @return [{Symbol=>String}] the prompt to be used in IRB
|
35
|
+
attr_reader :prompts
|
36
|
+
|
37
|
+
# @param [String] id the identifier used to mark the beginning and end of a prompt
|
38
|
+
# @param [{Symbol=>String}] prompts the prompts to use. The recognized keys are:
|
39
|
+
# @:PROMPT_I@, @:PROMPT_N@, @:PROMPT_S@, @:PROMPT_C@, @:RETURN@. They have the
|
40
|
+
# same meaninig as the keys of any entry in @IRB.conf[:PROMPT]@
|
41
|
+
def initialize id, prompts
|
42
|
+
@id = id
|
43
|
+
@prompts = {}
|
44
|
+
prompts.each_pair do |k, v|
|
45
|
+
@prompts[k] = "quirb:#{@id}:#{v}:quirb:#{@id}:#{PROMPTS[k]}:"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Checks whether a string begins with a prompt
|
50
|
+
#
|
51
|
+
# @param [String] str the string to check
|
52
|
+
# @return [IrbLine, nil] an {IrbLine} with the information about the prompt and the
|
53
|
+
# string if the string starts with a prompt or *nil* if the string doesn't start
|
54
|
+
# with a prompt
|
55
|
+
def match str
|
56
|
+
res = str.split "quirb:#{@id}:", 3
|
57
|
+
return if !res or !res[0].empty? or res.size < 3
|
58
|
+
res[1].slice! -1 #removes an ending :
|
59
|
+
letter, sep, line = res[2].partition ':'
|
60
|
+
type = TYPES[letter]
|
61
|
+
if type then IrbLine.new type, line, res[1]
|
62
|
+
else nil
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
# A parsed line from IRB
|
69
|
+
#
|
70
|
+
# It contains information about the prompt for the line, the text of the line and the
|
71
|
+
# type and category of prompt
|
72
|
+
class IrbLine
|
73
|
+
|
74
|
+
# @return [Symbol] the type of line. It can be:
|
75
|
+
# * @:output@ if there's no prompt
|
76
|
+
# * @:return@ if the line starts with a @:RETURN@ prompt
|
77
|
+
# * @:normal@ if the line starts with a @:PROMPT_I@ prompt
|
78
|
+
# * @:string@ if the line starts with a @:PROMPT_S@ prompt
|
79
|
+
# * @:statement@ if the line starts with a @:PROMPT_C@ prompt
|
80
|
+
# * @:indent@ if the line starts with a @:PROMPT_N@ prompt
|
81
|
+
attr_reader :type
|
82
|
+
|
83
|
+
# @return [String] the content of the line
|
84
|
+
attr_reader :text
|
85
|
+
|
86
|
+
# @return [Symbol] the category of the line (basing on the prompt {#type}). It is
|
87
|
+
# @:output@ if the prompt type is @:output@ or @:return@ and @:input@ otherwise
|
88
|
+
attr_reader :category
|
89
|
+
|
90
|
+
# @return [String] the text of the prompt
|
91
|
+
attr_reader :prompt
|
92
|
+
|
93
|
+
# @return [String] the full text of the line, made from the IRB prompt and the line
|
94
|
+
# contents
|
95
|
+
attr_reader :full_text
|
96
|
+
|
97
|
+
# The category corresponding to each line type
|
98
|
+
CATEGORIES = {
|
99
|
+
:output => :output,
|
100
|
+
:return => :output,
|
101
|
+
:normal => :input,
|
102
|
+
:string => :input,
|
103
|
+
:statement => :input,
|
104
|
+
:indent => :input
|
105
|
+
}
|
106
|
+
|
107
|
+
# @param [Symbol] type the type of the line. It can be: @:output@, @:return@, @:normal@,
|
108
|
+
# @:string@, @:statement@ or @:indent@
|
109
|
+
# @param [String] text the text of the line
|
110
|
+
# @param [String] the text of the prompt
|
111
|
+
def initialize type, text, prompt
|
112
|
+
@text = text
|
113
|
+
@type = type
|
114
|
+
@prompt = prompt
|
115
|
+
@full_text = @prompt + @text
|
116
|
+
@category = (type == :return or type == :output) ? :output : :input
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
120
|
+
|
121
|
+
class IRBController < Qt::Object
|
122
|
+
|
123
|
+
# Signal emitted when there's output from irb to be read
|
124
|
+
#
|
125
|
+
# This signal can be emitted either as soon as IRB writes the output or at regular
|
126
|
+
# intervals (provided there's actually output availlable) according to the
|
127
|
+
# value of {#interval}
|
128
|
+
signals :output_ready
|
129
|
+
|
130
|
+
# Signal emitted just before IRB is stopped, for whatever reason. Connecting
|
131
|
+
# to this signal is the last opportunity to read the output from it.
|
132
|
+
signals :about_to_stop_irb
|
133
|
+
|
134
|
+
# Signal emitted just before interrupting IRB by sending a @SIGINT@ signal
|
135
|
+
signals :interrupting_evaluation
|
136
|
+
|
137
|
+
# Signal emitted after IRB has been interrupted by sending a @SIGINT@ signal
|
138
|
+
signals :evaluation_interrupted
|
139
|
+
|
140
|
+
# Signal emitted when IRB is ready to receive input (that is, when it gives
|
141
|
+
# a prompt when there aren't lines yet to be sent)
|
142
|
+
signals :ready
|
143
|
+
|
144
|
+
# @return [<String>] the options to pass to IRB. Note that changing this
|
145
|
+
# after IRB has been started won't have any effect until you restart IRB.
|
146
|
+
attr_accessor :irb_options
|
147
|
+
|
148
|
+
# @return [String] the path if the IRB program. Note that changing this
|
149
|
+
# after IRB has been started won't have any effect until you restart IRB.
|
150
|
+
attr_accessor :irb_program
|
151
|
+
|
152
|
+
# @return [Integer,nil] the minimum time interval (in milliseconds) between
|
153
|
+
# two {#output_ready} methods. If *nil*, the {#output_ready} signal will
|
154
|
+
# be emitted as soon as output is received from IRB. The default value is
|
155
|
+
# 100 milliseconds
|
156
|
+
attr_accessor :interval
|
157
|
+
|
158
|
+
# @param [String] irb the path of the IRB program
|
159
|
+
# @param [<String>] options the options to pass to the IRB program
|
160
|
+
# @param [Qt::Object,nil] parent the parent object
|
161
|
+
def initialize irb, options, parent = nil
|
162
|
+
super parent
|
163
|
+
@irb_program = irb
|
164
|
+
@irb_options = options.dup
|
165
|
+
@timer = Qt::Timer.new self
|
166
|
+
connect @timer, SIGNAL(:timeout), self, SLOT(:timer_ticked)
|
167
|
+
@pending_prompt = nil
|
168
|
+
@interrupting = false
|
169
|
+
@evaluating = false
|
170
|
+
@input = []
|
171
|
+
@output = []
|
172
|
+
@interval = 100
|
173
|
+
end
|
174
|
+
|
175
|
+
# Whether or not there's output ready to be read
|
176
|
+
# @return [Boolean] *true* if there's output to be read using {#output} and
|
177
|
+
# *false* otherwise
|
178
|
+
def has_output?
|
179
|
+
!@output.empty?
|
180
|
+
end
|
181
|
+
|
182
|
+
# Interrupts IRB evaluation by sending IRB the @SIGINT@ signal
|
183
|
+
#
|
184
|
+
# This will also clear any pending input. The {#output_ready} signal won't
|
185
|
+
# be emitted until IRB sends an empty prompt. The {#interrupting_evaluation}
|
186
|
+
# signal is emitted before sending IRB the @SIGINT@ signal
|
187
|
+
# @return nil
|
188
|
+
def interrupt
|
189
|
+
@interrupting = true
|
190
|
+
@timer.stop
|
191
|
+
@input.clear
|
192
|
+
emit interrupting_evaluation
|
193
|
+
Process.kill :INT, @irb.pid
|
194
|
+
nil
|
195
|
+
end
|
196
|
+
|
197
|
+
# Sends input to IRB
|
198
|
+
#
|
199
|
+
# The input lines are sent one by one, expecting a prompt before sending a
|
200
|
+
# new one. The {#ready} signal is emitted at the first prompt after the last
|
201
|
+
# line has been sent. If there are lines waiting to be sent, the new ones
|
202
|
+
# will be added at the end of the queue.
|
203
|
+
# @param [<String>] input the lines to send IRB
|
204
|
+
# @return [nil]
|
205
|
+
def send_to_irb input
|
206
|
+
@input.concat input
|
207
|
+
unless @evaluating
|
208
|
+
@evaluating = true
|
209
|
+
send_next_line
|
210
|
+
end
|
211
|
+
nil
|
212
|
+
end
|
213
|
+
slots :send_to_irb
|
214
|
+
|
215
|
+
# Stops the IRB process
|
216
|
+
#
|
217
|
+
# The {#about_to_stop_irb} is emitted before stopping IRB. No further
|
218
|
+
# {#output_ready} signals are emitted.
|
219
|
+
#
|
220
|
+
# You'll need to call {#start_irb} if you want IRB to be restarted after
|
221
|
+
# calling this method. If all you need is to stop IRB and immediately restart
|
222
|
+
# it, however, use {#restart_irb} rather than {#stop}
|
223
|
+
#
|
224
|
+
# @return [nil]
|
225
|
+
# @note this method will wait for up to two seconds for IRB to stop. If it
|
226
|
+
# doesn't stop by that time, it will return all the same.
|
227
|
+
def stop_irb
|
228
|
+
disconnect @irb, SIGNAL('finished(int, QProcess::ExitStatus)'), self, SLOT(:irb_finished)
|
229
|
+
stop :kill
|
230
|
+
# It seems it takes some time before irb is killed.
|
231
|
+
@irb.wait_for_finished 2000
|
232
|
+
end
|
233
|
+
|
234
|
+
# Stops and immediately restarts the IRB process
|
235
|
+
# The {#about_to_stop_irb} is emitted before stopping IRB. After emitting
|
236
|
+
# this signal, all output will be cleared, so you won't be able to access it
|
237
|
+
# anymore.
|
238
|
+
#
|
239
|
+
# @return [nil]
|
240
|
+
def restart_irb
|
241
|
+
stop :terminate
|
242
|
+
nil
|
243
|
+
end
|
244
|
+
slots :restart_irb
|
245
|
+
|
246
|
+
# Sends IRB a signal
|
247
|
+
#
|
248
|
+
# @param [String,Symbol,Integer] signal the name or number of the signal.
|
249
|
+
# Signal names may be with or without the @SIG@ prefix
|
250
|
+
# @raise [ArgumentError] if the signal name or number is not valid
|
251
|
+
# @raise [RuntimeError] if it wasn't possible to send the signal
|
252
|
+
# @return [nil]
|
253
|
+
# @note if you want to stop evaluation (for example, to exit and endless loop),
|
254
|
+
# do not use this method to send a @SIGINT@, but use {#interrupt}. Simply
|
255
|
+
# sending a @SIGINT@ signal may not work very well in that case, as, if IRB
|
256
|
+
# keeps sending output, {#output_ready} signals will kept being emitted
|
257
|
+
def send_signal signal
|
258
|
+
pid = @irb.pid
|
259
|
+
begin Process.kill signal, pid
|
260
|
+
rescue Errno::EINVAL, RangeError, ArgumentError
|
261
|
+
raise ArgumentError, "Invalid signal #{signal}"
|
262
|
+
rescue Errno::ESRCH, Errno::EPERM
|
263
|
+
raise RuntimeError, "It wasn't possible to send IRB a signal"
|
264
|
+
end
|
265
|
+
nil
|
266
|
+
end
|
267
|
+
|
268
|
+
# Changes the prompt
|
269
|
+
#
|
270
|
+
# Calling this method is the same as adding a new entry to @IRB.conf[:PROMPT]@
|
271
|
+
# and changing the current prompt using @IRB.conf.prompt_mode=@.
|
272
|
+
#
|
273
|
+
# @param [{Symbol=>String}] the new prompt. The recognized keys are:
|
274
|
+
# @:PROMPT_I@, @:PROMPT_N@, @:PROMPT_S@, @:PROMPT_C@, @:RETURN@. They have the
|
275
|
+
# same meaninig as the keys of any entry in @IRB.conf[:PROMPT]@
|
276
|
+
# @return [nil]
|
277
|
+
# @note in the return prompt, don't add the ending @%s\n@, as it will be
|
278
|
+
# added automatically
|
279
|
+
# @note users must not use @IRB.conf[:PROMPT]@ or @IRB.conf.prompt_mode=@
|
280
|
+
# to change IRB's prompt, but always call this method. This is
|
281
|
+
# because {IRBController}, behind the scenes, needs to add a special string to the
|
282
|
+
# prompt chosen by the user to work correctly
|
283
|
+
# @note changing the prompt takes effect immediately, even if IRB has already
|
284
|
+
# been started
|
285
|
+
def prompts= prompts
|
286
|
+
id = Array.new(5){rand(10)}.join ''
|
287
|
+
@prompt = PromptMatcher.new id, prompts
|
288
|
+
change_irb_prompt if running?
|
289
|
+
end
|
290
|
+
|
291
|
+
# Starts IRB
|
292
|
+
#
|
293
|
+
# If IRB is already running, it'll be stopped. The {#ready} signal will be
|
294
|
+
# emitted when IRB is ready to accept output.
|
295
|
+
#
|
296
|
+
# This method will wait for IRB to start for up to two seconds before returning.
|
297
|
+
# @return [Boolean] *true* if IRB was started successfully and *false* if
|
298
|
+
# it didn't start within two seconds
|
299
|
+
def start_irb
|
300
|
+
if @irb
|
301
|
+
@irb.kill
|
302
|
+
@irb.delete_later
|
303
|
+
end
|
304
|
+
@irb = Qt::Process.new self
|
305
|
+
@irb.process_channel_mode = Qt::Process::MergedChannels
|
306
|
+
set_irb_env
|
307
|
+
connect @irb, SIGNAL('finished(int, QProcess::ExitStatus)'), self, SLOT(:irb_finished)
|
308
|
+
options = @irb_options + ['--noreadline']
|
309
|
+
@irb.start @irb_program, options
|
310
|
+
@irb.wait_for_started 2000
|
311
|
+
if @irb.state == Qt::Process::Running
|
312
|
+
change_irb_prompt
|
313
|
+
true
|
314
|
+
else false
|
315
|
+
end
|
316
|
+
end
|
317
|
+
|
318
|
+
# @return [Boolean] whether IRB is running or not
|
319
|
+
def running?
|
320
|
+
@irb and (@irb.state == Qt::Process::Running)
|
321
|
+
end
|
322
|
+
|
323
|
+
# The state of the IRB process
|
324
|
+
#
|
325
|
+
# @return [Symbol] @:running@ if the IRB process is running, @:starting@ if
|
326
|
+
# an attempt to start the IRB process was made but IRB hasn't started yet
|
327
|
+
# and @:not_running@ if either IRB has not yet been started or if the attempt
|
328
|
+
# to start it has failed
|
329
|
+
def state
|
330
|
+
if @irb
|
331
|
+
case @irb.state
|
332
|
+
when Qt::Process::Running then :running
|
333
|
+
when Qt::Process::Starting then :starting
|
334
|
+
else :not_running
|
335
|
+
end
|
336
|
+
else :not_running
|
337
|
+
end
|
338
|
+
end
|
339
|
+
|
340
|
+
# Reads a number of lines of output
|
341
|
+
#
|
342
|
+
# Lines which have been read are removed from the output buffer
|
343
|
+
# @param [Integer,nil] n the number of lines to read
|
344
|
+
# @return [<IrbLine>] an array containing the first _n_ lines of output. If
|
345
|
+
# there are less than _n_ lines of output, all of them are returned. If
|
346
|
+
# there's no output, an empty array is returned. If _n_ is *nil*, the whole
|
347
|
+
# content of the output buffer is returned
|
348
|
+
def output n = 100
|
349
|
+
if n then n = [n, @output.size].min
|
350
|
+
else n = @output.count
|
351
|
+
end
|
352
|
+
@output.shift n
|
353
|
+
end
|
354
|
+
|
355
|
+
private
|
356
|
+
|
357
|
+
# Sends IRB the command to change the prompt
|
358
|
+
#
|
359
|
+
# IRB should not be given input until this command has been executed
|
360
|
+
# @return [nil]
|
361
|
+
def change_irb_prompt
|
362
|
+
prompts = @prompt.prompts.dup
|
363
|
+
prompts[:RETURN] += "%s\n"
|
364
|
+
disconnect @irb, SIGNAL(:readyReadStandardOutput), self, SLOT(:process_output)
|
365
|
+
connect @irb, SIGNAL(:readyReadStandardOutput), self, SLOT(:wait_for_prompt_changed)
|
366
|
+
cmd = "IRB.conf[:PROMPT][:QUIRB]=#{prompts.inspect}\nconf.prompt_mode = :QUIRB\n"
|
367
|
+
@irb.write cmd
|
368
|
+
nil
|
369
|
+
end
|
370
|
+
|
371
|
+
# Helper method to stop IRB
|
372
|
+
#
|
373
|
+
# It's used by both {#stop_irb} and {#restart_irb}. It takes care of reading
|
374
|
+
# any unread output from IRB, emitting the {#about_to_stop_irb} signal,
|
375
|
+
# stopping the timer and so on.
|
376
|
+
# @param [Symbol] meth the method to call to stop IRB. It can be either
|
377
|
+
# :kill or :terminate, which will call respectively @Qt::Process#kill@ or
|
378
|
+
# @Qt::Process#terminate@
|
379
|
+
# @return [nil]
|
380
|
+
def stop meth
|
381
|
+
parse_output @irb.read_all_standard_output.to_s.split("\n")
|
382
|
+
emit about_to_stop_irb
|
383
|
+
@timer.stop
|
384
|
+
@irb.disconnect SIGNAL(:readyReadStandardOutput)
|
385
|
+
@irb.send meth
|
386
|
+
nil
|
387
|
+
end
|
388
|
+
|
389
|
+
# Changes the environment associated with the IRB process
|
390
|
+
#
|
391
|
+
# The environment is changed by adding an @IRBRC@ variable which points to
|
392
|
+
# the @irbrc.rb@ file in the same directory as @irb_controller.rb@.
|
393
|
+
# @return [nil]
|
394
|
+
def set_irb_env
|
395
|
+
dir = File.dirname(__FILE__)
|
396
|
+
vars = ['IRBRC', File.join(dir, 'irbrc.rb')]
|
397
|
+
if defined? Qt::ProcessEnvironment
|
398
|
+
env = Qt::ProcessEnvironment.system_environment
|
399
|
+
env.insert *vars
|
400
|
+
@irb.process_environment = env
|
401
|
+
else
|
402
|
+
env = @irb.system_environment
|
403
|
+
env << vars.join('=')
|
404
|
+
@irb.environment = env
|
405
|
+
end
|
406
|
+
nil
|
407
|
+
end
|
408
|
+
|
409
|
+
# Slot called whenever IRB sends output before changes to the prompt have
|
410
|
+
# taken effect
|
411
|
+
#
|
412
|
+
# If the lines sent by IRB contain the correct prompt, the @readyReadStandardOutput@
|
413
|
+
# signal from the IRB process will be connected to the {#process_output}
|
414
|
+
# signal and the {#ready} signal will be emitted
|
415
|
+
def wait_for_prompt_changed
|
416
|
+
lines = @irb.read_all_standard_output.to_s.split "\n"
|
417
|
+
found = false
|
418
|
+
lines.each do |l|
|
419
|
+
if @prompt.match l
|
420
|
+
disconnect @irb, SIGNAL(:readyReadStandardOutput), self, SLOT(:wait_for_prompt_changed)
|
421
|
+
connect @irb, SIGNAL(:readyReadStandardOutput), self, SLOT(:process_output)
|
422
|
+
found = true
|
423
|
+
break
|
424
|
+
end
|
425
|
+
end
|
426
|
+
if found
|
427
|
+
empty_prompt = lines.reverse_each.find do |l|
|
428
|
+
res = @prompt.match l
|
429
|
+
res and res.category == :input and res.text.empty?
|
430
|
+
end
|
431
|
+
@pending_prompt = @prompt.match empty_prompt if empty_prompt
|
432
|
+
end
|
433
|
+
emit ready
|
434
|
+
end
|
435
|
+
slots :wait_for_prompt_changed
|
436
|
+
|
437
|
+
# Restarts IRB after it has been terminated
|
438
|
+
#
|
439
|
+
# This method is connected to the IRB process's @finished(int, QProcess::ExitStatus)@
|
440
|
+
# signal, so that a new IRB process can be automatically started. This doesn't
|
441
|
+
# happen when {#stop_irb} is used to kill the IRB process
|
442
|
+
# @return [nil]
|
443
|
+
def irb_finished
|
444
|
+
@irb.delete_later
|
445
|
+
start_irb
|
446
|
+
nil
|
447
|
+
end
|
448
|
+
slots :irb_finished
|
449
|
+
|
450
|
+
# Sends the next line of input to the IRB process
|
451
|
+
#
|
452
|
+
# If there's no queued input line, the {#ready} signal will be emitted
|
453
|
+
# @return [nil]
|
454
|
+
def send_next_line
|
455
|
+
if @input.empty?
|
456
|
+
@evaluating = false
|
457
|
+
emit ready
|
458
|
+
else @irb.write @input.shift + "\n"
|
459
|
+
end
|
460
|
+
end
|
461
|
+
|
462
|
+
# Slot called whenever IRB sends output after the prompt has correctly
|
463
|
+
# been set up
|
464
|
+
#
|
465
|
+
# It adds the output lines to the output buffer, after converting them to
|
466
|
+
# {IrbLine} objects. If the last line is a prompt line and the controller
|
467
|
+
# is sending input to IRB, it calls {#send_next_line}.
|
468
|
+
#
|
469
|
+
# If the controller is interrupting IRB and a prompt is found, the {#evaluation_interrupted}
|
470
|
+
# signal is emitted, followed by the {#ready} signal
|
471
|
+
# @return [nil]
|
472
|
+
def process_output
|
473
|
+
new_lines = @irb.read_all_standard_output.to_s.split "\n"
|
474
|
+
lines = parse_output new_lines
|
475
|
+
return unless @prompt
|
476
|
+
return unless lines
|
477
|
+
@output.concat lines
|
478
|
+
if lines.last and lines.last.category == :input and lines.last.text.empty?
|
479
|
+
is_ready = true
|
480
|
+
@pending_prompt = @output.pop
|
481
|
+
end
|
482
|
+
if @prompt and !@interrupting
|
483
|
+
emit output_ready if !@interval or @output.count <= 100
|
484
|
+
@timer.start(@interval) if @interval and !@output.empty? and !@timer.active?
|
485
|
+
send_next_line if (@evaluating and is_ready) or !@evaluating
|
486
|
+
elsif @interrupting
|
487
|
+
if is_ready
|
488
|
+
@interrupting = false
|
489
|
+
emit evaluation_interrupted
|
490
|
+
@output.clear
|
491
|
+
send_next_line
|
492
|
+
end
|
493
|
+
end
|
494
|
+
nil
|
495
|
+
end
|
496
|
+
slots :process_output
|
497
|
+
|
498
|
+
# Slot called in response to the timer timing out
|
499
|
+
#
|
500
|
+
# It emits the {#output_ready} signal and stops the timer if there's no
|
501
|
+
# more output
|
502
|
+
# @return [nil]
|
503
|
+
def timer_ticked
|
504
|
+
emit output_ready
|
505
|
+
@timer.stop if @output.empty?
|
506
|
+
nil
|
507
|
+
end
|
508
|
+
slots :timer_ticked
|
509
|
+
|
510
|
+
# Parses output lines from IRB, converting them to {IrbLine} objects
|
511
|
+
#
|
512
|
+
# If the prompt has been set up, the lines will be processed using {PromptMatcher#match},
|
513
|
+
# otherwise they'll all be considered output lines with no prompt.
|
514
|
+
#
|
515
|
+
# If the controller is sending input to IRB and there's a pending prompt,
|
516
|
+
# that prompt will be added to the beginning of the first line, then removed
|
517
|
+
# @param [<String>] lines the lines to process
|
518
|
+
# @return [<IrbLine>, nil] an array of {IrbLine} objects corresponding to
|
519
|
+
# the processed lines or *nil* if _lines_ is empty
|
520
|
+
def parse_output lines
|
521
|
+
return if lines.empty?
|
522
|
+
if @prompt
|
523
|
+
parsed_lines = lines.map do |l|
|
524
|
+
@prompt.match(l) || IrbLine.new( :output, l, '')
|
525
|
+
end
|
526
|
+
else
|
527
|
+
parsed_lines = lines.map{|l| IrbLine.new :output, l, ''}
|
528
|
+
end
|
529
|
+
if @evaluating and @pending_prompt and parsed_lines[0].type == :output
|
530
|
+
parsed_lines[0] = IrbLine.new :normal, lines[0], @pending_prompt.prompt
|
531
|
+
@pending_prompt = nil
|
532
|
+
end
|
533
|
+
parsed_lines
|
534
|
+
end
|
535
|
+
slots :parse_output
|
536
|
+
|
537
|
+
end
|
538
|
+
|
539
|
+
end
|
540
|
+
|
541
|
+
end
|