ruber 0.0.9 → 0.0.10
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/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
|