debugger2 1.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (183) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +12 -0
  3. data/.travis.yml +3 -0
  4. data/AUTHORS +10 -0
  5. data/CHANGELOG.md +65 -0
  6. data/CONTRIBUTING.md +1 -0
  7. data/Gemfile +3 -0
  8. data/LICENSE +23 -0
  9. data/OLDER_CHANGELOG +334 -0
  10. data/OLD_CHANGELOG +5655 -0
  11. data/OLD_README +122 -0
  12. data/README.md +108 -0
  13. data/Rakefile +78 -0
  14. data/bin/rdebug +397 -0
  15. data/debugger2.gemspec +29 -0
  16. data/doc/.cvsignore +42 -0
  17. data/doc/Makefile.am +63 -0
  18. data/doc/emacs-notes.txt +38 -0
  19. data/doc/hanoi.rb +35 -0
  20. data/doc/primes.rb +28 -0
  21. data/doc/rdebug-emacs.texi +1030 -0
  22. data/doc/ruby-debug.texi +3791 -0
  23. data/doc/test-tri2.rb +18 -0
  24. data/doc/tri3.rb +8 -0
  25. data/doc/triangle.rb +12 -0
  26. data/emacs/Makefile.am +130 -0
  27. data/emacs/rdebug-annotate.el +385 -0
  28. data/emacs/rdebug-breaks.el +407 -0
  29. data/emacs/rdebug-cmd.el +92 -0
  30. data/emacs/rdebug-core.el +502 -0
  31. data/emacs/rdebug-dbg.el +62 -0
  32. data/emacs/rdebug-error.el +79 -0
  33. data/emacs/rdebug-fns.el +111 -0
  34. data/emacs/rdebug-frames.el +230 -0
  35. data/emacs/rdebug-gud.el +242 -0
  36. data/emacs/rdebug-help.el +104 -0
  37. data/emacs/rdebug-info.el +83 -0
  38. data/emacs/rdebug-layouts.el +180 -0
  39. data/emacs/rdebug-locring.el +118 -0
  40. data/emacs/rdebug-output.el +106 -0
  41. data/emacs/rdebug-regexp.el +118 -0
  42. data/emacs/rdebug-secondary.el +260 -0
  43. data/emacs/rdebug-shortkey.el +175 -0
  44. data/emacs/rdebug-source.el +568 -0
  45. data/emacs/rdebug-track.el +392 -0
  46. data/emacs/rdebug-varbuf.el +150 -0
  47. data/emacs/rdebug-vars.el +125 -0
  48. data/emacs/rdebug-watch.el +132 -0
  49. data/emacs/rdebug.el +326 -0
  50. data/emacs/test/elk-test.el +242 -0
  51. data/emacs/test/test-annotate.el +103 -0
  52. data/emacs/test/test-cmd.el +116 -0
  53. data/emacs/test/test-core.el +104 -0
  54. data/emacs/test/test-fns.el +65 -0
  55. data/emacs/test/test-frames.el +62 -0
  56. data/emacs/test/test-gud.el +35 -0
  57. data/emacs/test/test-indent.el +58 -0
  58. data/emacs/test/test-regexp.el +144 -0
  59. data/emacs/test/test-shortkey.el +61 -0
  60. data/ext/ruby_debug/breakpoint.c +630 -0
  61. data/ext/ruby_debug/extconf.rb +11 -0
  62. data/ext/ruby_debug/ruby_debug.c +2203 -0
  63. data/ext/ruby_debug/ruby_debug.h +151 -0
  64. data/lib/debugger.rb +5 -0
  65. data/lib/debugger/version.rb +5 -0
  66. data/lib/debugger2.rb +6 -0
  67. data/lib/ruby-debug-base.rb +307 -0
  68. data/lib/ruby-debug.rb +176 -0
  69. data/lib/ruby-debug/command.rb +227 -0
  70. data/lib/ruby-debug/commands/breakpoints.rb +153 -0
  71. data/lib/ruby-debug/commands/catchpoint.rb +55 -0
  72. data/lib/ruby-debug/commands/condition.rb +49 -0
  73. data/lib/ruby-debug/commands/continue.rb +38 -0
  74. data/lib/ruby-debug/commands/control.rb +107 -0
  75. data/lib/ruby-debug/commands/display.rb +120 -0
  76. data/lib/ruby-debug/commands/edit.rb +48 -0
  77. data/lib/ruby-debug/commands/enable.rb +202 -0
  78. data/lib/ruby-debug/commands/eval.rb +176 -0
  79. data/lib/ruby-debug/commands/finish.rb +42 -0
  80. data/lib/ruby-debug/commands/frame.rb +301 -0
  81. data/lib/ruby-debug/commands/help.rb +56 -0
  82. data/lib/ruby-debug/commands/info.rb +467 -0
  83. data/lib/ruby-debug/commands/irb.rb +123 -0
  84. data/lib/ruby-debug/commands/jump.rb +66 -0
  85. data/lib/ruby-debug/commands/kill.rb +51 -0
  86. data/lib/ruby-debug/commands/list.rb +94 -0
  87. data/lib/ruby-debug/commands/method.rb +84 -0
  88. data/lib/ruby-debug/commands/quit.rb +39 -0
  89. data/lib/ruby-debug/commands/reload.rb +40 -0
  90. data/lib/ruby-debug/commands/save.rb +90 -0
  91. data/lib/ruby-debug/commands/set.rb +223 -0
  92. data/lib/ruby-debug/commands/show.rb +247 -0
  93. data/lib/ruby-debug/commands/skip.rb +35 -0
  94. data/lib/ruby-debug/commands/source.rb +36 -0
  95. data/lib/ruby-debug/commands/stepping.rb +81 -0
  96. data/lib/ruby-debug/commands/threads.rb +189 -0
  97. data/lib/ruby-debug/commands/tmate.rb +36 -0
  98. data/lib/ruby-debug/commands/trace.rb +57 -0
  99. data/lib/ruby-debug/commands/variables.rb +199 -0
  100. data/lib/ruby-debug/debugger.rb +5 -0
  101. data/lib/ruby-debug/helper.rb +69 -0
  102. data/lib/ruby-debug/interface.rb +232 -0
  103. data/lib/ruby-debug/processor.rb +474 -0
  104. data/man/rdebug.1 +241 -0
  105. data/old_scripts/Makefile.am +14 -0
  106. data/old_scripts/README.md +2 -0
  107. data/old_scripts/autogen.sh +4 -0
  108. data/old_scripts/configure.ac +12 -0
  109. data/old_scripts/rdbg.rb +33 -0
  110. data/old_scripts/runner.sh +7 -0
  111. data/old_scripts/svn2cl_usermap +3 -0
  112. data/test/.cvsignore +1 -0
  113. data/test/breakpoints_test.rb +366 -0
  114. data/test/conditions_test.rb +77 -0
  115. data/test/continue_test.rb +28 -0
  116. data/test/display_test.rb +143 -0
  117. data/test/edit_test.rb +55 -0
  118. data/test/eval_test.rb +94 -0
  119. data/test/examples/breakpoint1.rb +15 -0
  120. data/test/examples/breakpoint2.rb +7 -0
  121. data/test/examples/conditions.rb +4 -0
  122. data/test/examples/continue.rb +4 -0
  123. data/test/examples/display.rb +5 -0
  124. data/test/examples/edit.rb +3 -0
  125. data/test/examples/edit2.rb +3 -0
  126. data/test/examples/eval.rb +4 -0
  127. data/test/examples/finish.rb +20 -0
  128. data/test/examples/frame.rb +31 -0
  129. data/test/examples/help.rb +2 -0
  130. data/test/examples/info.rb +48 -0
  131. data/test/examples/info2.rb +3 -0
  132. data/test/examples/irb.rb +6 -0
  133. data/test/examples/jump.rb +14 -0
  134. data/test/examples/kill.rb +2 -0
  135. data/test/examples/list.rb +12 -0
  136. data/test/examples/method.rb +15 -0
  137. data/test/examples/post_mortem.rb +19 -0
  138. data/test/examples/quit.rb +2 -0
  139. data/test/examples/reload.rb +6 -0
  140. data/test/examples/restart.rb +6 -0
  141. data/test/examples/save.rb +3 -0
  142. data/test/examples/set.rb +3 -0
  143. data/test/examples/set_annotate.rb +12 -0
  144. data/test/examples/settings.rb +1 -0
  145. data/test/examples/show.rb +2 -0
  146. data/test/examples/source.rb +3 -0
  147. data/test/examples/stepping.rb +21 -0
  148. data/test/examples/thread.rb +32 -0
  149. data/test/examples/tmate.rb +10 -0
  150. data/test/examples/trace.rb +7 -0
  151. data/test/examples/trace_threads.rb +20 -0
  152. data/test/examples/variables.rb +26 -0
  153. data/test/finish_test.rb +49 -0
  154. data/test/frame_test.rb +140 -0
  155. data/test/help_test.rb +51 -0
  156. data/test/info_test.rb +326 -0
  157. data/test/irb_test.rb +82 -0
  158. data/test/jump_test.rb +70 -0
  159. data/test/kill_test.rb +49 -0
  160. data/test/list_test.rb +147 -0
  161. data/test/method_test.rb +72 -0
  162. data/test/post_mortem_test.rb +25 -0
  163. data/test/quit_test.rb +56 -0
  164. data/test/reload_test.rb +47 -0
  165. data/test/restart_test.rb +145 -0
  166. data/test/save_test.rb +94 -0
  167. data/test/set_test.rb +183 -0
  168. data/test/show_test.rb +294 -0
  169. data/test/source_test.rb +46 -0
  170. data/test/stepping_test.rb +122 -0
  171. data/test/support/breakpoint.rb +12 -0
  172. data/test/support/context.rb +14 -0
  173. data/test/support/matchers.rb +67 -0
  174. data/test/support/mocha_extensions.rb +71 -0
  175. data/test/support/processor.rb +7 -0
  176. data/test/support/test_dsl.rb +206 -0
  177. data/test/support/test_interface.rb +66 -0
  178. data/test/test_helper.rb +9 -0
  179. data/test/thread_test.rb +124 -0
  180. data/test/tmate_test.rb +45 -0
  181. data/test/trace_test.rb +156 -0
  182. data/test/variables_test.rb +116 -0
  183. metadata +319 -0
@@ -0,0 +1,176 @@
1
+ module Debugger
2
+ module EvalFunctions # :nodoc:
3
+ def run_with_binding
4
+ binding = @state.context ? get_binding : TOPLEVEL_BINDING
5
+ $__dbg_interface = @state.interface
6
+ eval(<<-EOC, binding)
7
+ __dbg_verbose_save=$VERBOSE; $VERBOSE=false
8
+ def dbg_print(*args)
9
+ $__dbg_interface.print(*args)
10
+ end
11
+ remove_method :puts if self.respond_to?(:puts) &&
12
+ defined?(remove_method)
13
+ def dbg_puts(*args)
14
+ $__dbg_interface.print(*args)
15
+ $__dbg_interface.print("\n")
16
+ end
17
+ $VERBOSE=__dbg_verbose_save
18
+ EOC
19
+ yield binding
20
+ ensure
21
+ $__dbg_interface = nil
22
+ end
23
+ end
24
+
25
+ class EvalCommand < Command # :nodoc:
26
+ self.allow_in_control = true
27
+
28
+ register_setting_get(:autoeval) do
29
+ EvalCommand.unknown
30
+ end
31
+ register_setting_set(:autoeval) do |value|
32
+ EvalCommand.unknown = value
33
+ end
34
+
35
+ def match(input)
36
+ @input = input
37
+ super
38
+ end
39
+
40
+ def regexp
41
+ /^\s*(p|e(?:val)?)\s+/
42
+ end
43
+
44
+ def execute
45
+ expr = @match ? @match.post_match : @input
46
+ run_with_binding do |b|
47
+ print "%s\n", debug_eval(expr, b).inspect
48
+ end
49
+ end
50
+
51
+ class << self
52
+ def help_command
53
+ %w|p eval|
54
+ end
55
+
56
+ def help(cmd)
57
+ if cmd == 'p'
58
+ %{
59
+ p expression\tevaluate expression and print its value
60
+ }
61
+ else
62
+ %{
63
+ e[val] expression\tevaluate expression and print its value,
64
+ \t\t\talias for p.
65
+ * NOTE - to turn on autoeval, use 'set autoeval'
66
+ }
67
+ end
68
+ end
69
+ end
70
+ end
71
+
72
+ class PPCommand < Command # :nodoc:
73
+ self.allow_in_control = true
74
+
75
+ def regexp
76
+ /^\s*pp\s+/
77
+ end
78
+
79
+ def execute
80
+ out = StringIO.new
81
+ run_with_binding do |b|
82
+ PP.pp(debug_eval(@match.post_match, b), out)
83
+ end
84
+ print out.string
85
+ rescue
86
+ out.puts $!.message
87
+ end
88
+
89
+ class << self
90
+ def help_command
91
+ 'pp'
92
+ end
93
+
94
+ def help(cmd)
95
+ %{
96
+ pp expression\tevaluate expression and pretty-print its value
97
+ }
98
+ end
99
+ end
100
+ end
101
+
102
+ class PutLCommand < Command # :nodoc:
103
+ self.allow_in_control = true
104
+
105
+ def regexp
106
+ /^\s*putl\s+/
107
+ end
108
+
109
+ def execute
110
+ out = StringIO.new
111
+ run_with_binding do |b|
112
+ vals = debug_eval(@match.post_match, b)
113
+ if vals.is_a?(Array)
114
+ vals = vals.map{|item| item.to_s}
115
+ print "%s\n", columnize(vals, self.class.settings[:width])
116
+ else
117
+ PP.pp(vals, out)
118
+ print out.string
119
+ end
120
+ end
121
+ rescue
122
+ out.puts $!.message
123
+ end
124
+
125
+ class << self
126
+ def help_command
127
+ 'putl'
128
+ end
129
+
130
+ def help(cmd)
131
+ %{
132
+ putl expression\t\tevaluate expression, an array, and columnize its value
133
+ }
134
+ end
135
+ end
136
+ end
137
+
138
+ class PSCommand < Command # :nodoc:
139
+ self.allow_in_control = true
140
+
141
+ include EvalFunctions
142
+
143
+ def regexp
144
+ /^\s*ps\s+/
145
+ end
146
+
147
+ def execute
148
+ out = StringIO.new
149
+ run_with_binding do |b|
150
+ vals = debug_eval(@match.post_match, b)
151
+ if vals.is_a?(Array)
152
+ vals = vals.map{|item| item.to_s}
153
+ print "%s\n", columnize(vals.sort!, self.class.settings[:width])
154
+ else
155
+ PP.pp(vals, out)
156
+ print out.string
157
+ end
158
+ end
159
+ rescue
160
+ out.puts $!.message
161
+ end
162
+
163
+ class << self
164
+ def help_command
165
+ 'ps'
166
+ end
167
+
168
+ def help(cmd)
169
+ %{
170
+ ps expression\tevaluate expression, an array, sort, and columnize its value
171
+ }
172
+ end
173
+ end
174
+ end
175
+
176
+ end
@@ -0,0 +1,42 @@
1
+ module Debugger
2
+ # Implements the debugger 'finish' command.
3
+ class FinishCommand < Command
4
+ self.allow_in_post_mortem = false
5
+ self.need_context = true
6
+
7
+ def regexp
8
+ /^\s*fin(?:ish)? (?:\s+(.*))?$/x
9
+ end
10
+
11
+ def execute
12
+ max_frame = @state.context.stack_size - @state.frame_pos
13
+ if !@match[1] or @match[1].empty?
14
+ frame_pos = @state.frame_pos
15
+ else
16
+ frame_pos = get_int(@match[1], "Finish", 0, max_frame-1, 0)
17
+ return nil unless frame_pos
18
+ end
19
+ @state.context.stop_frame = frame_pos
20
+ @state.frame_pos = 0
21
+ @state.proceed
22
+ end
23
+
24
+ class << self
25
+ def help_command
26
+ 'finish'
27
+ end
28
+
29
+ def help(cmd)
30
+ %{
31
+ fin[ish] [frame-number]\tExecute until selected stack frame returns.
32
+
33
+ If no frame number is given, we run until the currently selected frame
34
+ returns. The currently selected frame starts out the most-recent
35
+ frame or 0 if no frame positioning (e.g "up", "down" or "frame") has
36
+ been performed. If a frame number is given we run until that frame
37
+ returns.
38
+ }
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,301 @@
1
+ module Debugger
2
+ # Mix-in module to assist in command parsing.
3
+ module FrameFunctions # :nodoc:
4
+ def adjust_frame(frame_pos, absolute, context=@state.context)
5
+ @state.frame_pos = 0 if context != @state.context
6
+ if absolute
7
+ if frame_pos < 0
8
+ abs_frame_pos = context.stack_size + frame_pos
9
+ else
10
+ abs_frame_pos = frame_pos
11
+ end
12
+ else
13
+ abs_frame_pos = @state.frame_pos + frame_pos
14
+ end
15
+
16
+ if abs_frame_pos >= context.stack_size then
17
+ errmsg "Adjusting would put us beyond the oldest (initial) frame.\n"
18
+ return
19
+ elsif abs_frame_pos < 0 then
20
+ errmsg "Adjusting would put us beyond the newest (innermost) frame.\n"
21
+ return
22
+ end
23
+ if @state.frame_pos != abs_frame_pos then
24
+ @state.previous_line = nil
25
+ @state.frame_pos = abs_frame_pos
26
+ end
27
+
28
+ @state.file = context.frame_file(@state.frame_pos)
29
+ @state.line = context.frame_line(@state.frame_pos)
30
+
31
+ print_frame(@state.frame_pos, true)
32
+ end
33
+
34
+ def get_frame_call(prefix, pos, context)
35
+ id = context.frame_method(pos)
36
+ klass = context.frame_class(pos)
37
+ call_str = ""
38
+ if id
39
+ args = context.frame_args(pos)
40
+ locals = context.frame_locals(pos)
41
+ if Command.settings[:callstyle] != :short && klass
42
+ if Command.settings[:callstyle] == :tracked
43
+ arg_info = context.frame_args_info(pos)
44
+ end
45
+ call_str << "#{klass}."
46
+ end
47
+ call_str << id.id2name
48
+ if args.any?
49
+ call_str << "("
50
+ args.each_with_index do |name, i|
51
+ case Command.settings[:callstyle]
52
+ when :short
53
+ call_str += "%s, " % [name]
54
+ when :last
55
+ klass = locals[name].class
56
+ if klass.inspect.size > 20+3
57
+ klass = klass.inspect[0..20]+"..."
58
+ end
59
+ call_str += "%s#%s, " % [name, klass]
60
+ when :tracked
61
+ if arg_info && arg_info.size > i
62
+ call_str += "#{name}: #{arg_info[i].inspect}, "
63
+ else
64
+ call_str += "%s, " % name
65
+ end
66
+ end
67
+ if call_str.size > self.class.settings[:width] - prefix.size
68
+ # Strip off trailing ', ' if any but add stuff for later trunc
69
+ call_str[-2..-1] = ",...XX"
70
+ break
71
+ end
72
+ end
73
+ call_str[-2..-1] = ")" # Strip off trailing ', ' if any
74
+ end
75
+ end
76
+ return call_str
77
+ end
78
+
79
+ def print_frame(pos, adjust = false, context=@state.context)
80
+ file = context.frame_file(pos)
81
+ line = context.frame_line(pos)
82
+ klass = context.frame_class(pos)
83
+
84
+ unless Command.settings[:full_path]
85
+ path_components = file.split(/[\\\/]/)
86
+ if path_components.size > 3
87
+ path_components[0...-3] = '...'
88
+ file = path_components.join(File::ALT_SEPARATOR || File::SEPARATOR)
89
+ end
90
+ end
91
+
92
+ frame_num = "#%d " % pos
93
+ call_str = get_frame_call(frame_num, pos, context)
94
+ file_line = "at line %s:%d\n" % [CommandProcessor.canonic_file(file), line]
95
+ print frame_num
96
+ unless call_str.empty?
97
+ print call_str
98
+ print ' '
99
+ if call_str.size + frame_num.size + file_line.size > self.class.settings[:width]
100
+ print "\n "
101
+ end
102
+ end
103
+ print file_line
104
+ if ENV['EMACS'] && adjust
105
+ fmt = (Debugger.annotate.to_i > 1 ?
106
+ "\032\032source %s:%d\n" : "\032\032%s:%d\n")
107
+ print fmt % [CommandProcessor.canonic_file(file), line]
108
+ end
109
+ end
110
+
111
+ # Check if call stack is truncated. This can happen if
112
+ # Debugger.start is not called low enough in the call stack. An
113
+ # array of additional callstack lines from caller is returned if
114
+ # definitely truncated, false if not, and nil if we don't know.
115
+ #
116
+ # We determine truncation based on a passed in sentinal set via
117
+ # caller which can be nil.
118
+ #
119
+ # First we see if we can find our position in caller. If so, then
120
+ # we compare context position to that in caller using sentinal
121
+ # as a place to start ignoring additional caller entries. sentinal
122
+ # is set by rdebug, but if it's not set, i.e. nil then additional
123
+ # entries are presumably ones that we haven't recorded in context
124
+ def truncated_callstack?(context, sentinal=nil, cs=caller)
125
+ recorded_size = context.stack_size
126
+ to_find_fl = "#{context.frame_file(0)}:#{context.frame_line(0)}"
127
+ top_discard = false
128
+ cs.each_with_index do |fl, i|
129
+ fl.gsub!(/in `.*'$/, '')
130
+ fl.gsub!(/:$/, '')
131
+ if fl == to_find_fl
132
+ top_discard = i
133
+ break
134
+ end
135
+ end
136
+ if top_discard
137
+ cs = cs[top_discard..-1]
138
+ return false unless cs
139
+ return cs unless sentinal
140
+ if cs.size > recorded_size+2 && cs[recorded_size+2] != sentinal
141
+ # caller seems to truncate recursive calls and we don't.
142
+ # See if we can find sentinal in the first 0..recorded_size+1 entries
143
+ return false if cs[0..recorded_size+1].any?{ |f| f==sentinal }
144
+ return cs
145
+ end
146
+ return false
147
+ end
148
+ return nil
149
+ end
150
+
151
+
152
+ end
153
+
154
+ # Implements debugger "where" or "backtrace" command.
155
+ class WhereCommand < Command
156
+ def regexp
157
+ /^\s*(?:w(?:here)?|bt|backtrace)$/
158
+ end
159
+
160
+ def execute
161
+ (0...@state.context.stack_size).each do |idx|
162
+ if idx == @state.frame_pos
163
+ print "--> "
164
+ else
165
+ print " "
166
+ end
167
+ print_frame(idx)
168
+
169
+ end
170
+ if truncated_callstack?(@state.context, Debugger.start_sentinal)
171
+ # print "Warning: saved frames may be incomplete; compare with caller(0).\n"
172
+ end
173
+ end
174
+
175
+ class << self
176
+ def help_command
177
+ %w|where backtrace|
178
+ end
179
+
180
+ def help(cmd)
181
+ s = if cmd == 'where'
182
+ %{
183
+ w[here]\tdisplay stack frames
184
+ }
185
+ else
186
+ %{
187
+ bt|backtrace\t\talias for where - display stack frames
188
+ }
189
+ end
190
+ s += %{
191
+ Print the entire stack frame. Each frame is numbered, the most recent
192
+ frame is 0. frame number can be referred to in the "frame" command;
193
+ "up" and "down" add or subtract respectively to frame numbers shown.
194
+ The position of the current frame is marked with -->. }
195
+ end
196
+ end
197
+ end
198
+
199
+ class UpCommand < Command # :nodoc:
200
+ def regexp
201
+ /^\s* u(?:p)? (?:\s+(.*))?$/x
202
+ end
203
+
204
+ def execute
205
+ pos = get_int(@match[1], "Up")
206
+ return unless pos
207
+ adjust_frame(pos, false)
208
+ end
209
+
210
+ class << self
211
+ def help_command
212
+ 'up'
213
+ end
214
+
215
+ def help(cmd)
216
+ %{
217
+ up[count]\tmove to higher frame
218
+ }
219
+ end
220
+ end
221
+ end
222
+
223
+ class DownCommand < Command # :nodoc:
224
+ def regexp
225
+ /^\s* down (?:\s+(.*))? .*$/x
226
+ end
227
+
228
+ def execute
229
+ pos = get_int(@match[1], "Down")
230
+ return unless pos
231
+ adjust_frame(-pos, false)
232
+ end
233
+
234
+ class << self
235
+ def help_command
236
+ 'down'
237
+ end
238
+
239
+ def help(cmd)
240
+ %{
241
+ down[count]\tmove to lower frame
242
+ }
243
+ end
244
+ end
245
+ end
246
+
247
+ class FrameCommand < Command # :nodoc:
248
+ def regexp
249
+ / ^\s*
250
+ f(?:rame)?
251
+ (?: \s+ (\S+))? \s*
252
+ (?: thread \s+ (.*))? \s*
253
+ $/x
254
+ end
255
+
256
+ def execute
257
+ if not @match[1]
258
+ pos = 0
259
+ else
260
+ pos = get_int(@match[1], "Frame")
261
+ return unless pos
262
+ end
263
+ if @match[2]
264
+ context = parse_thread_num('frame', @match[2])
265
+ unless context
266
+ errmsg "Thread #{@match[2]} doesn't exist.\n"
267
+ return
268
+ end
269
+ else
270
+ context = @state.context
271
+ end
272
+ adjust_frame(pos, true, context)
273
+ end
274
+
275
+ class << self
276
+ def help_command
277
+ 'frame'
278
+ end
279
+
280
+ def help(cmd)
281
+ %{
282
+ f[rame] [frame-number [thread thread-number]]
283
+ Move the current frame to the specified frame number, or the
284
+ 0 if no frame-number has been given.
285
+
286
+ A negative number indicates position from the other end. So
287
+ 'frame -1' moves to the oldest frame, and 'frame 0' moves to
288
+ the newest frame.
289
+
290
+ Without an argument, the command prints the current stack
291
+ frame. Since the current position is redisplayed, it may trigger a
292
+ resyncronization if there is a front end also watching over
293
+ things.
294
+
295
+ If a thread number is given then we set the context for evaluating
296
+ expressions to that frame of that thread.
297
+ }
298
+ end
299
+ end
300
+ end
301
+ end