ruby-debug-ide19 0.4.10
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +75 -0
- data/ChangeLog +465 -0
- data/ChangeLog.archive +1073 -0
- data/MIT-LICENSE +24 -0
- data/Rakefile +110 -0
- data/bin/rdebug-ide +88 -0
- data/ext/mkrf_conf.rb +28 -0
- data/lib/ruby-debug-ide.rb +172 -0
- data/lib/ruby-debug/command.rb +169 -0
- data/lib/ruby-debug/commands/breakpoints.rb +129 -0
- data/lib/ruby-debug/commands/catchpoint.rb +52 -0
- data/lib/ruby-debug/commands/condition.rb +51 -0
- data/lib/ruby-debug/commands/control.rb +129 -0
- data/lib/ruby-debug/commands/enable.rb +203 -0
- data/lib/ruby-debug/commands/eval.rb +64 -0
- data/lib/ruby-debug/commands/frame.rb +155 -0
- data/lib/ruby-debug/commands/inspect.rb +24 -0
- data/lib/ruby-debug/commands/jump.rb +73 -0
- data/lib/ruby-debug/commands/load.rb +18 -0
- data/lib/ruby-debug/commands/stepping.rb +108 -0
- data/lib/ruby-debug/commands/threads.rb +153 -0
- data/lib/ruby-debug/commands/variables.rb +136 -0
- data/lib/ruby-debug/event_processor.rb +74 -0
- data/lib/ruby-debug/helper.rb +33 -0
- data/lib/ruby-debug/interface.rb +39 -0
- data/lib/ruby-debug/printers.rb +2 -0
- data/lib/ruby-debug/processor.rb +152 -0
- data/lib/ruby-debug/xml_printer.rb +268 -0
- data/test/rd_basic_test.rb +10 -0
- data/test/rd_catchpoint_test.rb +20 -0
- data/test/rd_condition_test.rb +11 -0
- data/test/rd_enable_disable_test.rb +43 -0
- data/test/rd_inspect_test.rb +11 -0
- data/test/rd_stepping_breakpoints_test.rb +36 -0
- data/test/rd_test_base.rb +44 -0
- data/test/rd_threads_and_frames_test.rb +11 -0
- data/test/rd_variables_test.rb +11 -0
- data/test/ruby-debug/xml_printer_test.rb +105 -0
- metadata +103 -0
@@ -0,0 +1,153 @@
|
|
1
|
+
module Debugger
|
2
|
+
class ThreadListCommand < Command # :nodoc:
|
3
|
+
self.control = true
|
4
|
+
def regexp
|
5
|
+
/^\s*th(?:read)?\s+l(?:ist)?\s*$/
|
6
|
+
end
|
7
|
+
|
8
|
+
def execute
|
9
|
+
contexts = Debugger.contexts.sort_by{|c| c.thnum}
|
10
|
+
print_contexts(contexts)
|
11
|
+
end
|
12
|
+
|
13
|
+
class << self
|
14
|
+
def help_command
|
15
|
+
'thread'
|
16
|
+
end
|
17
|
+
|
18
|
+
def help(cmd)
|
19
|
+
%{
|
20
|
+
th[read] l[ist]\t\t\tlist all threads
|
21
|
+
}
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class ThreadSwitchCommand < Command # :nodoc:
|
27
|
+
self.control = true
|
28
|
+
self.need_context = true
|
29
|
+
|
30
|
+
def regexp
|
31
|
+
/^\s*th(?:read)?\s+(?:sw(?:itch)?\s+)?(\d+)\s*$/
|
32
|
+
end
|
33
|
+
|
34
|
+
def execute
|
35
|
+
c = get_context(@match[1].to_i)
|
36
|
+
case
|
37
|
+
when c == @state.context
|
38
|
+
print_msg "It's the current thread."
|
39
|
+
when c.ignored?
|
40
|
+
print_msg "Can't switch to the debugger thread."
|
41
|
+
else
|
42
|
+
print_context(c)
|
43
|
+
c.stop_next = 1
|
44
|
+
c.thread.run
|
45
|
+
@state.proceed
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
class << self
|
50
|
+
def help_command
|
51
|
+
'thread'
|
52
|
+
end
|
53
|
+
|
54
|
+
def help(cmd)
|
55
|
+
%{
|
56
|
+
th[read] [sw[itch]] <nnn>\tswitch thread context to nnn
|
57
|
+
}
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
class ThreadStopCommand < Command # :nodoc:
|
63
|
+
self.control = true
|
64
|
+
self.need_context = true
|
65
|
+
|
66
|
+
def regexp
|
67
|
+
/^\s*th(?:read)?\s+stop\s+(\d+)\s*$/
|
68
|
+
end
|
69
|
+
|
70
|
+
def execute
|
71
|
+
c = get_context(@match[1].to_i)
|
72
|
+
case
|
73
|
+
when c == @state.context
|
74
|
+
print_msg "It's the current thread."
|
75
|
+
when c.ignored?
|
76
|
+
print_msg "Can't stop the debugger thread."
|
77
|
+
else
|
78
|
+
c.suspend
|
79
|
+
print_context(c)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
class << self
|
84
|
+
def help_command
|
85
|
+
'thread'
|
86
|
+
end
|
87
|
+
|
88
|
+
def help(cmd)
|
89
|
+
%{
|
90
|
+
th[read] stop <nnn>\t\tstop thread nnn
|
91
|
+
}
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
class ThreadCurrentCommand < Command # :nodoc:
|
97
|
+
self.need_context = true
|
98
|
+
|
99
|
+
def regexp
|
100
|
+
/^\s*th(?:read)?\s+c(?:ur(?:rent)?)?\s*$/
|
101
|
+
end
|
102
|
+
|
103
|
+
def execute
|
104
|
+
print_context(@state.context)
|
105
|
+
end
|
106
|
+
|
107
|
+
class << self
|
108
|
+
def help_command
|
109
|
+
'thread'
|
110
|
+
end
|
111
|
+
|
112
|
+
def help(cmd)
|
113
|
+
%{
|
114
|
+
th[read] c[ur[rent]]\t\tshow current thread
|
115
|
+
}
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
class ThreadResumeCommand < Command # :nodoc:
|
121
|
+
self.control = true
|
122
|
+
self.need_context = true
|
123
|
+
|
124
|
+
def regexp
|
125
|
+
/^\s*th(?:read)?\s+resume\s+(\d+)\s*$/
|
126
|
+
end
|
127
|
+
|
128
|
+
def execute
|
129
|
+
c = get_context(@match[1].to_i)
|
130
|
+
case
|
131
|
+
when c == @state.context
|
132
|
+
print_msg "It's the current thread."
|
133
|
+
when c.ignored?
|
134
|
+
print_msg "Can't resume the debugger thread."
|
135
|
+
else
|
136
|
+
c.resume
|
137
|
+
print_context(c)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
class << self
|
142
|
+
def help_command
|
143
|
+
'thread'
|
144
|
+
end
|
145
|
+
|
146
|
+
def help(cmd)
|
147
|
+
%{
|
148
|
+
th[read] resume <nnn>\t\tresume thread nnn
|
149
|
+
}
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
module Debugger
|
2
|
+
class VarConstantCommand < Command # :nodoc:
|
3
|
+
def regexp
|
4
|
+
/^\s*v(?:ar)?\s+c(?:onst(?:ant)?)?\s+/
|
5
|
+
end
|
6
|
+
|
7
|
+
def execute
|
8
|
+
obj = debug_eval(@match.post_match)
|
9
|
+
unless obj.kind_of? Module
|
10
|
+
print_msg "Should be Class/Module: %s", @match.post_match
|
11
|
+
else
|
12
|
+
print_variables(obj.constants, "constant") do |var|
|
13
|
+
obj.const_get(var)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class << self
|
19
|
+
def help_command
|
20
|
+
'var'
|
21
|
+
end
|
22
|
+
|
23
|
+
def help(cmd)
|
24
|
+
%{
|
25
|
+
v[ar] c[onst] <object>\t\tshow constants of object
|
26
|
+
}
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class VarGlobalCommand < Command # :nodoc:
|
32
|
+
def regexp
|
33
|
+
/^\s*v(?:ar)?\s+g(?:lobal)?\s*$/
|
34
|
+
end
|
35
|
+
|
36
|
+
def execute
|
37
|
+
print_variables(global_variables, 'global') do |var|
|
38
|
+
debug_eval(var)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
class << self
|
43
|
+
def help_command
|
44
|
+
'var'
|
45
|
+
end
|
46
|
+
|
47
|
+
def help(cmd)
|
48
|
+
%{
|
49
|
+
v[ar] g[lobal]\t\t\tshow global variables
|
50
|
+
}
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
class VarInstanceCommand < Command # :nodoc:
|
56
|
+
def regexp
|
57
|
+
# id will be read as first match, name as post match
|
58
|
+
/^\s*v(?:ar)?\s+i(?:nstance)?\s+((?:[\\+-]0x)[\dabcdef]+)?/
|
59
|
+
end
|
60
|
+
|
61
|
+
def execute
|
62
|
+
if (@match[1])
|
63
|
+
obj = ObjectSpace._id2ref(@match[1].hex) rescue nil
|
64
|
+
unless obj
|
65
|
+
# TODO: ensure that empty variables frame will be printed
|
66
|
+
@printer.print_msg("Unknown object id : %s", @match[1])
|
67
|
+
end
|
68
|
+
else
|
69
|
+
obj = debug_eval(@match.post_match)
|
70
|
+
end
|
71
|
+
return unless obj
|
72
|
+
if (obj.is_a?(Array)) then
|
73
|
+
print_array(obj)
|
74
|
+
elsif (obj.is_a?(Hash)) then
|
75
|
+
print_hash(obj)
|
76
|
+
else
|
77
|
+
print_element("variables") do
|
78
|
+
# instance variables
|
79
|
+
kind = 'instance'
|
80
|
+
inst_vars = obj.instance_variables
|
81
|
+
instance_binding = obj.instance_eval{binding()}
|
82
|
+
# print self at top position
|
83
|
+
print_variable('self', debug_eval('self', instance_binding), kind) if inst_vars.include?('self')
|
84
|
+
inst_vars.sort.each do |var|
|
85
|
+
print_variable(var, debug_eval(var, instance_binding), kind) unless var == 'self'
|
86
|
+
end
|
87
|
+
|
88
|
+
# class variables
|
89
|
+
class_binding = obj.class.class_eval('binding()')
|
90
|
+
obj.class.class_variables.sort.each do |var|
|
91
|
+
print_variable(var, debug_eval(var, class_binding), 'class')
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
class << self
|
98
|
+
def help_command
|
99
|
+
'var'
|
100
|
+
end
|
101
|
+
|
102
|
+
def help(cmd)
|
103
|
+
%{
|
104
|
+
v[ar] i[nstance] <object>\tshow instance variables of object, object can be given by its id or an expression
|
105
|
+
}
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
class VarLocalCommand < Command # :nodoc:
|
111
|
+
def regexp
|
112
|
+
/^\s*v(?:ar)?\s+l(?:ocal)?\s*$/
|
113
|
+
end
|
114
|
+
|
115
|
+
def execute
|
116
|
+
locals = @state.context.frame_locals(@state.frame_pos)
|
117
|
+
_self = @state.context.frame_self(@state.frame_pos)
|
118
|
+
locals['self'] = _self unless _self.to_s == "main"
|
119
|
+
print_variables(locals.keys, 'local') do |var|
|
120
|
+
locals[var]
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
class << self
|
125
|
+
def help_command
|
126
|
+
'var'
|
127
|
+
end
|
128
|
+
|
129
|
+
def help(cmd)
|
130
|
+
%{
|
131
|
+
v[ar] l[ocal]\t\t\tshow local variables
|
132
|
+
}
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
if RUBY_VERSION < "1.9"
|
2
|
+
require 'ruby-debug/xml_printer'
|
3
|
+
else
|
4
|
+
require_relative 'xml_printer'
|
5
|
+
end
|
6
|
+
module Debugger
|
7
|
+
|
8
|
+
class EventProcessor
|
9
|
+
|
10
|
+
attr_accessor :line, :file, :context
|
11
|
+
|
12
|
+
def initialize(interface)
|
13
|
+
@printer = XmlPrinter.new(interface)
|
14
|
+
@line = nil
|
15
|
+
@file = nil
|
16
|
+
@last_breakpoint = nil
|
17
|
+
end
|
18
|
+
|
19
|
+
def at_breakpoint(context, breakpoint)
|
20
|
+
raise "@last_breakpoint supposed to be nil. is #{@last_breakpoint}" if @last_breakpoint
|
21
|
+
# at_breakpoint is immediately followed by #at_line event in
|
22
|
+
# ruby-debug-base. So postpone breakpoint printing until #at_line.
|
23
|
+
@last_breakpoint = breakpoint
|
24
|
+
end
|
25
|
+
|
26
|
+
def at_catchpoint(context, excpt)
|
27
|
+
@printer.print_catchpoint(excpt)
|
28
|
+
end
|
29
|
+
|
30
|
+
def at_tracing(context, file, line)
|
31
|
+
@printer.print_trace(context, file, line)
|
32
|
+
end
|
33
|
+
|
34
|
+
def at_line(context, file, line)
|
35
|
+
@printer.print_at_line(context, file, line) if context.nil? || context.stop_reason == :step
|
36
|
+
line_event(context, file, line)
|
37
|
+
end
|
38
|
+
|
39
|
+
def at_return(context, file, line)
|
40
|
+
@printer.print_at_line(context, file, line)
|
41
|
+
context.stop_frame = -1
|
42
|
+
line_event(context, file, line)
|
43
|
+
end
|
44
|
+
|
45
|
+
def line_event(context, file, line)
|
46
|
+
@line = line
|
47
|
+
@file = file
|
48
|
+
@context = context
|
49
|
+
if @last_breakpoint
|
50
|
+
# followed after #at_breakpoint in the same thread. Print breakpoint
|
51
|
+
# now when @line, @file and @context are correctly set to prevent race
|
52
|
+
# condition with `control thread'.
|
53
|
+
n = Debugger.breakpoints.index(@last_breakpoint) + 1
|
54
|
+
@printer.print_breakpoint n, @last_breakpoint
|
55
|
+
@last_breakpoint = nil
|
56
|
+
end
|
57
|
+
raise "DebuggerThread are not supposed to be traced (#{context.thread})" if context.thread.is_a?(Debugger::DebugThread)
|
58
|
+
@printer.print_debug("Stopping Thread %s", context.thread.to_s)
|
59
|
+
@printer.print_debug("Threads equal: %s", Thread.current == context.thread)
|
60
|
+
# will be resumed by commands like `step', `next', `continue', `finish'
|
61
|
+
# from `control thread'
|
62
|
+
Thread.stop
|
63
|
+
@printer.print_debug("Resumed Thread %s", context.thread.to_s)
|
64
|
+
@line = nil
|
65
|
+
@file = nil
|
66
|
+
@context = nil
|
67
|
+
end
|
68
|
+
|
69
|
+
def at_line?
|
70
|
+
@line
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Debugger
|
2
|
+
|
3
|
+
module ParseFunctions
|
4
|
+
# Parse 'str' of command 'cmd' as an integer between
|
5
|
+
# min and max. If either min or max is nil, that
|
6
|
+
# value has no bound.
|
7
|
+
def get_int(str, cmd, min=nil, max=nil, default=1)
|
8
|
+
return default unless str
|
9
|
+
begin
|
10
|
+
int = Integer(str)
|
11
|
+
if min and int < min
|
12
|
+
print_error "%s argument '%s' needs to at least %s.\n" % [cmd, str, min]
|
13
|
+
return nil
|
14
|
+
elsif max and int > max
|
15
|
+
print_error "%s argument '%s' needs to at most %s.\n" % [cmd, str, max]
|
16
|
+
return nil
|
17
|
+
end
|
18
|
+
return int
|
19
|
+
rescue
|
20
|
+
print_error "%s argument '%s' needs to be a number.\n" % [cmd, str]
|
21
|
+
return nil
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# Return true if code is syntactically correct for Ruby.
|
26
|
+
def syntax_valid?(code)
|
27
|
+
eval("BEGIN {return true}\n#{code}", nil, "", 0)
|
28
|
+
rescue Exception
|
29
|
+
false
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
class TCPSocket
|
2
|
+
|
3
|
+
# Workaround for JRuby issue http://jira.codehaus.org/browse/JRUBY-2063
|
4
|
+
def non_blocking_gets
|
5
|
+
loop do
|
6
|
+
result, _, _ = IO.select( [self], nil, nil, 0.2 )
|
7
|
+
next unless result
|
8
|
+
return result[0].gets
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
13
|
+
|
14
|
+
module Debugger
|
15
|
+
|
16
|
+
class RemoteInterface # :nodoc:
|
17
|
+
|
18
|
+
def initialize(socket)
|
19
|
+
@socket = socket
|
20
|
+
end
|
21
|
+
|
22
|
+
def read_command
|
23
|
+
result = @socket.non_blocking_gets
|
24
|
+
raise IOError unless result
|
25
|
+
result.chomp
|
26
|
+
end
|
27
|
+
|
28
|
+
def print(*args)
|
29
|
+
@socket.printf(*args)
|
30
|
+
end
|
31
|
+
|
32
|
+
def close
|
33
|
+
@socket.close
|
34
|
+
rescue Exception
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
@@ -0,0 +1,152 @@
|
|
1
|
+
if RUBY_VERSION < "1.9"
|
2
|
+
require 'ruby-debug/interface'
|
3
|
+
require 'ruby-debug/command'
|
4
|
+
else
|
5
|
+
require_relative 'interface'
|
6
|
+
require_relative 'command'
|
7
|
+
end
|
8
|
+
|
9
|
+
module Debugger
|
10
|
+
|
11
|
+
class ControlCommandProcessor # :nodoc:
|
12
|
+
def initialize(interface)
|
13
|
+
@interface = interface
|
14
|
+
@printer = XmlPrinter.new(@interface)
|
15
|
+
end
|
16
|
+
|
17
|
+
def print(*args)
|
18
|
+
@interface.print(*args)
|
19
|
+
end
|
20
|
+
|
21
|
+
def process_commands
|
22
|
+
@printer.print_debug("Starting command read loop")
|
23
|
+
ctrl_cmd_classes = Command.commands.select{|cmd| cmd.control}
|
24
|
+
state = ControlState.new(@interface)
|
25
|
+
ctrl_cmds = ctrl_cmd_classes.map{|cmd| cmd.new(state, @printer)}
|
26
|
+
|
27
|
+
while input = @interface.read_command
|
28
|
+
# escape % since print_debug might use printf
|
29
|
+
@printer.print_debug "Processing: #{input.gsub('%', '%%')}"
|
30
|
+
# sleep 0.3
|
31
|
+
catch(:debug_error) do
|
32
|
+
if cmd = ctrl_cmds.find{|c| c.match(input) }
|
33
|
+
cmd.execute
|
34
|
+
else
|
35
|
+
process_context_commands(input)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
rescue IOError, Errno::EPIPE
|
40
|
+
@printer.print_error "INTERNAL ERROR!!! #{$!}\n" rescue nil
|
41
|
+
@printer.print_error $!.backtrace.map{|l| "\t#{l}"}.join("\n") rescue nil
|
42
|
+
rescue Exception
|
43
|
+
@printer.print_error "INTERNAL ERROR!!! #{$!}\n" rescue nil
|
44
|
+
@printer.print_error $!.backtrace.map{|l| "\t#{l}"}.join("\n") rescue nil
|
45
|
+
ensure
|
46
|
+
@interface.close
|
47
|
+
end
|
48
|
+
|
49
|
+
def process_context_commands(input)
|
50
|
+
unless Debugger.event_processor.at_line?
|
51
|
+
@printer.print_error "There is no thread suspended at the time and therefore no context to execute '#{input.gsub('%', '%%')}'"
|
52
|
+
return
|
53
|
+
end
|
54
|
+
context = Debugger.event_processor.context
|
55
|
+
file = Debugger.event_processor.file
|
56
|
+
line = Debugger.event_processor.line
|
57
|
+
event_cmds_classes = Command.commands.select{|cmd| cmd.event}
|
58
|
+
state = State.new do |s|
|
59
|
+
s.context = context
|
60
|
+
s.file = file
|
61
|
+
s.line = line
|
62
|
+
s.binding = context.frame_binding(0)
|
63
|
+
s.interface = @interface
|
64
|
+
end
|
65
|
+
event_cmds = event_cmds_classes.map{|cmd| cmd.new(state, @printer) }
|
66
|
+
catch(:debug_error) do
|
67
|
+
splitter[input].each do |input|
|
68
|
+
# escape % since print_debug might use printf
|
69
|
+
@printer.print_debug "Processing context: #{input.gsub('%', '%%')}"
|
70
|
+
if cmd = event_cmds.find{ |c| c.match(input) }
|
71
|
+
if context.dead? && cmd.class.need_context
|
72
|
+
@printer.print_msg "Command is unavailable\n"
|
73
|
+
else
|
74
|
+
cmd.execute
|
75
|
+
end
|
76
|
+
else
|
77
|
+
@printer.print_msg "Unknown command: #{input}"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
context.thread.run if state.proceed?
|
83
|
+
end
|
84
|
+
|
85
|
+
def splitter
|
86
|
+
return lambda do |str|
|
87
|
+
str.split(/;/).inject([]) do |m, v|
|
88
|
+
if m.empty?
|
89
|
+
m << v
|
90
|
+
else
|
91
|
+
if m.last[-1] == ?\\
|
92
|
+
m.last[-1,1] = ''
|
93
|
+
m.last << ';' << v
|
94
|
+
else
|
95
|
+
m << v
|
96
|
+
end
|
97
|
+
end
|
98
|
+
m
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
class State # :nodoc:
|
105
|
+
|
106
|
+
attr_accessor :context, :file, :line, :binding
|
107
|
+
attr_accessor :frame_pos, :previous_line
|
108
|
+
attr_accessor :interface
|
109
|
+
|
110
|
+
def initialize
|
111
|
+
@frame_pos = 0
|
112
|
+
@previous_line = nil
|
113
|
+
@proceed = false
|
114
|
+
yield self
|
115
|
+
end
|
116
|
+
|
117
|
+
def print(*args)
|
118
|
+
@interface.print(*args)
|
119
|
+
end
|
120
|
+
|
121
|
+
def proceed?
|
122
|
+
@proceed
|
123
|
+
end
|
124
|
+
|
125
|
+
def proceed
|
126
|
+
@proceed = true
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
class ControlState # :nodoc:
|
131
|
+
|
132
|
+
def initialize(interface)
|
133
|
+
@interface = interface
|
134
|
+
end
|
135
|
+
|
136
|
+
def proceed
|
137
|
+
end
|
138
|
+
|
139
|
+
def print(*args)
|
140
|
+
@interface.print(*args)
|
141
|
+
end
|
142
|
+
|
143
|
+
def context
|
144
|
+
nil
|
145
|
+
end
|
146
|
+
|
147
|
+
def file
|
148
|
+
print "ERROR: No filename given.\n"
|
149
|
+
throw :debug_error
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|