rubinius-debugger 2.0.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.
@@ -0,0 +1,147 @@
1
+ class Rubinius::Debugger
2
+ class BreakPoint
3
+
4
+ def self.for_ip(exec, ip, name=:anon)
5
+ line = exec.line_from_ip(ip)
6
+
7
+ BreakPoint.new(name, exec, ip, line)
8
+ end
9
+
10
+ def initialize(descriptor, method, ip, line, condition=nil)
11
+ @descriptor = descriptor
12
+ @method = method
13
+ @ip = ip
14
+ @line = line
15
+ @for_step = false
16
+ @paired_bp = nil
17
+ @temp = false
18
+ @commands = nil
19
+ @condition = condition
20
+
21
+ @set = false
22
+ end
23
+
24
+ attr_reader :method, :ip, :line, :paired_bp, :descriptor, :commands, :condition
25
+
26
+ def location
27
+ "#{@method.active_path}:#{@line} (+#{ip})"
28
+ end
29
+
30
+ def describe
31
+ "#{descriptor} - #{location}"
32
+ end
33
+
34
+ def for_step!(scope)
35
+ @temp = true
36
+ @for_step = scope
37
+ end
38
+
39
+ def set_temp!
40
+ @temp = true
41
+ end
42
+
43
+ def for_step?
44
+ @for_step
45
+ end
46
+
47
+ def paired_with(bp)
48
+ @paired_bp = bp
49
+ end
50
+
51
+ def activate
52
+ @set = true
53
+ @method.set_breakpoint @ip, self
54
+ end
55
+
56
+ def remove!
57
+ return unless @set
58
+
59
+ @set = false
60
+ @method.clear_breakpoint(@ip)
61
+ end
62
+
63
+ def hit!(loc)
64
+ return true unless @temp
65
+
66
+ if @for_step
67
+ return false unless loc.variables == @for_step
68
+ end
69
+
70
+ remove!
71
+
72
+ @paired_bp.remove! if @paired_bp
73
+
74
+ return true
75
+ end
76
+
77
+ def delete!
78
+ remove!
79
+ end
80
+
81
+ def set_commands(commands)
82
+ @commands = commands
83
+ end
84
+
85
+ def has_commands?
86
+ !@commands.nil?
87
+ end
88
+
89
+ def set_condition(condition)
90
+ @condition = condition
91
+ end
92
+
93
+ def has_condition?
94
+ !@condition.nil?
95
+ end
96
+ end
97
+
98
+ class DeferredBreakPoint
99
+ def initialize(debugger, frame, klass, which, name, line=nil, list=nil)
100
+ @debugger = debugger
101
+ @frame = frame
102
+ @klass_name = klass
103
+ @which = which
104
+ @name = name
105
+ @line = line
106
+ @list = list
107
+ end
108
+
109
+ def descriptor
110
+ "#{@klass_name}#{@which}#{@name}"
111
+ end
112
+
113
+ def resolve!
114
+ begin
115
+ klass = @frame.run(@klass_name)
116
+ rescue NameError
117
+ return false
118
+ end
119
+
120
+ begin
121
+ if @which == "#"
122
+ method = klass.instance_method(@name)
123
+ else
124
+ method = klass.method(@name)
125
+ end
126
+ rescue NameError
127
+ return false
128
+ end
129
+
130
+ @debugger.info "Resolved breakpoint for #{@klass_name}#{@which}#{@name}"
131
+
132
+ @debugger.set_breakpoint_method descriptor, method, @line
133
+
134
+ return true
135
+ end
136
+
137
+ def describe
138
+ "#{descriptor} - unknown location (deferred)"
139
+ end
140
+
141
+ def delete!
142
+ if @list
143
+ @list.delete self
144
+ end
145
+ end
146
+ end
147
+ end
@@ -0,0 +1,745 @@
1
+ require 'rubinius/debugger/display'
2
+
3
+ class Rubinius::Debugger
4
+ class CommandDescription
5
+ attr_accessor :klass, :patterns, :help, :ext_help
6
+
7
+ def initialize(klass)
8
+ @klass = klass
9
+ end
10
+
11
+ def name
12
+ @klass.name
13
+ end
14
+ end
15
+
16
+ class Command
17
+ include Rubinius::Debugger::Display
18
+
19
+ @commands = []
20
+
21
+ def self.commands
22
+ @commands
23
+ end
24
+
25
+ def self.descriptor
26
+ @descriptor ||= CommandDescription.new(self)
27
+ end
28
+
29
+ def self.pattern(*strs)
30
+ Command.commands << self
31
+ descriptor.patterns = strs
32
+ end
33
+
34
+ def self.help(str)
35
+ descriptor.help = str
36
+ end
37
+
38
+ def self.ext_help(str)
39
+ descriptor.ext_help = str
40
+ end
41
+
42
+ def self.match?(cmd)
43
+ descriptor.patterns.include?(cmd)
44
+ end
45
+
46
+ def initialize(debugger)
47
+ @debugger = debugger
48
+ end
49
+
50
+ def run_code(str)
51
+ @debugger.current_frame.run(str)
52
+ end
53
+
54
+ def current_method
55
+ @debugger.current_frame.method
56
+ end
57
+
58
+ def current_frame
59
+ @debugger.current_frame
60
+ end
61
+
62
+ def variables
63
+ @debugger.variables
64
+ end
65
+
66
+ def listen(step=false)
67
+ @debugger.listen(step)
68
+ end
69
+
70
+ # ===== Commands =====
71
+ #
72
+ # These classes are in the order they should appear in the help output.
73
+ # As such, they're grouped by similar action.
74
+
75
+ class Help < Command
76
+ pattern "help", "h"
77
+ help "Show information about debugger commands"
78
+
79
+ def run(args)
80
+
81
+ if args and !args.empty?
82
+ klass = Command.commands.find { |k| k.match?(args.strip) }
83
+ if klass
84
+ des = klass.descriptor
85
+ puts "Help for #{des.name}:"
86
+ puts " Accessed using: #{des.patterns.join(', ')}"
87
+ puts "\n#{des.help}."
88
+ puts "\n#{des.ext_help}" if des.ext_help
89
+ else
90
+ puts "Unknown command: #{args}"
91
+ end
92
+ else
93
+ Command.commands.each do |klass|
94
+ des = klass.descriptor
95
+
96
+ puts "%20s: #{des.help}" % des.patterns.join(', ')
97
+ end
98
+ end
99
+ end
100
+
101
+ end
102
+
103
+ class SetBreakPoint < Command
104
+ pattern "b", "break", "brk"
105
+ help "Set a breakpoint at a point in a method"
106
+ ext_help <<-HELP
107
+ The breakpoint must be specified using the following notation:
108
+ Klass[.#]method:line
109
+
110
+ Thus, to set a breakpoint for the instance method pop in
111
+ Array on line 33, use:
112
+ Array#pop:33
113
+
114
+ To breakpoint on class method start of Debugger line 4, use:
115
+ Debugger.start:4
116
+
117
+ Conditional breakpoints can be created in this way:
118
+ <breakpoint declaration> if <condition>
119
+ The breakpoint will be triggered only when the evaluation of the specified condition returns true.
120
+ HELP
121
+
122
+ # provide this method so it can be overriden for other languages wanting to use this debugger
123
+ def match_method(method_identifier)
124
+ /([A-Z]\w*(?:::[A-Z]\w*)*)([.#]|::)([a-zA-Z0-9_\[\]]+[!?=]?)(?:[:](\d+))?(\s+if\s+.*)?/.match(method_identifier)
125
+ end
126
+
127
+ def run(args, temp=false)
128
+ m = match_method(args)
129
+ unless m
130
+ error "Unrecognized position: '#{args}'"
131
+ return
132
+ end
133
+
134
+ klass_name = m[1]
135
+ which = m[2]
136
+ name = m[3]
137
+ line = m[4] ? m[4].to_i : nil
138
+ condition = m[5] ? m[5].sub(/\A\s+if\s+/, '') : nil
139
+
140
+ begin
141
+ klass = run_code(klass_name)
142
+ rescue NameError
143
+ error "Unable to find class/module: #{m[1]}"
144
+ ask_deferred klass_name, which, name, line
145
+ return
146
+ end
147
+
148
+ begin
149
+ if which == "#"
150
+ method = klass.instance_method(name)
151
+ else
152
+ method = klass.method(name)
153
+ end
154
+ rescue NameError
155
+ error "Unable to find method '#{name}' in #{klass}"
156
+ ask_deferred klass_name, which, name, line
157
+ return
158
+ end
159
+
160
+ bp = @debugger.set_breakpoint_method args.strip, method, line, condition
161
+
162
+ bp.set_temp! if temp
163
+
164
+ return bp
165
+ end
166
+
167
+ def ask_deferred(klass_name, which, name, line)
168
+ answer = ask "Would you like to defer this breakpoint to later? [y/n] "
169
+
170
+ if answer.strip.downcase[0] == ?y
171
+ @debugger.add_deferred_breakpoint(klass_name, which, name, line)
172
+ info "Deferred breakpoint created."
173
+ end
174
+ end
175
+
176
+ end
177
+
178
+ class SetTempBreakPoint < SetBreakPoint
179
+ pattern "tb", "tbreak", "tbrk"
180
+ help "Set a temporary breakpoint"
181
+ ext_help "Same as break, but the breakpoint is deleted when it is hit"
182
+
183
+ def run(args)
184
+ super args, true
185
+ end
186
+ end
187
+
188
+ class DeleteBreakpoint < Command
189
+ pattern "d", "del", "delete"
190
+ help "Delete a breakpoint"
191
+ ext_help "Specify the breakpoint by number, use 'info break' to see the numbers"
192
+
193
+ def run(args)
194
+ if !args or args.empty?
195
+ error "Please specify which breakpoint by number"
196
+ return
197
+ end
198
+
199
+ begin
200
+ i = Integer(args.strip)
201
+ rescue ArgumentError
202
+ error "'#{args}' is not a number"
203
+ return
204
+ end
205
+
206
+ @debugger.delete_breakpoint(i)
207
+ end
208
+ end
209
+
210
+ class Next < Command
211
+ pattern "n", "next"
212
+ help "Move to the next line or conditional branch"
213
+ ext_help <<-HELP
214
+ Attempt to continue execution and stop at the next line. If there is
215
+ a conditional branch between the current position and the next line,
216
+ execution is stopped within the conditional branch instead.
217
+
218
+ The optional argument is a number which specifies how many lines to
219
+ attempt to skip past before stopping execution.
220
+
221
+ If the current line is the last in a method, execution is stopped
222
+ at the current position of the caller.
223
+ HELP
224
+
225
+ def run(args)
226
+ if !args or args.empty?
227
+ step = 1
228
+ else
229
+ step = args.to_i
230
+ end
231
+
232
+ if step <= 0
233
+ error "Invalid step count - #{step}"
234
+ return
235
+ end
236
+
237
+ step_over_by(step)
238
+ @debugger.listen
239
+ end
240
+
241
+ def step_over_by(step)
242
+ f = current_frame
243
+
244
+ ip = -1
245
+
246
+ exec = f.method
247
+ possible_line = f.line + step
248
+ fin_ip = exec.first_ip_on_line possible_line, f.ip
249
+
250
+ if !fin_ip
251
+ return step_to_parent
252
+ end
253
+
254
+ set_breakpoints_between(exec, f.ip, fin_ip)
255
+ end
256
+
257
+ def step_to_parent
258
+ f = @debugger.frame(current_frame.number + 1)
259
+ unless f
260
+ info "Unable to find frame to step to next"
261
+ return
262
+ end
263
+
264
+ exec = f.method
265
+ ip = f.ip
266
+
267
+ bp = BreakPoint.for_ip(exec, ip)
268
+ bp.for_step!(f.variables)
269
+ bp.activate
270
+
271
+ return bp
272
+ end
273
+
274
+ def set_breakpoints_between(exec, start_ip, fin_ip)
275
+ ips = goto_between(exec, start_ip, fin_ip)
276
+ if ips.kind_of? Fixnum
277
+ ip = ips
278
+ else
279
+ one, two = ips
280
+ bp1 = BreakPoint.for_ip(exec, one)
281
+ bp2 = BreakPoint.for_ip(exec, two)
282
+
283
+ bp1.paired_with(bp2)
284
+ bp2.paired_with(bp1)
285
+
286
+ bp1.for_step!(current_frame.variables)
287
+ bp2.for_step!(current_frame.variables)
288
+
289
+ bp1.activate
290
+ bp2.activate
291
+
292
+ return bp1
293
+ end
294
+
295
+ if ip == -1
296
+ error "No place to step to"
297
+ return nil
298
+ end
299
+
300
+ bp = BreakPoint.for_ip(exec, ip)
301
+ bp.for_step!(current_frame.variables)
302
+ bp.activate
303
+
304
+ return bp
305
+ end
306
+
307
+ def next_interesting(exec, ip)
308
+ pop = Rubinius::InstructionSet.opcodes_map[:pop]
309
+
310
+ if exec.iseq[ip] == pop
311
+ return ip + 1
312
+ end
313
+
314
+ return ip
315
+ end
316
+
317
+ def goto_between(exec, start, fin)
318
+ goto = Rubinius::InstructionSet.opcodes_map[:goto]
319
+ git = Rubinius::InstructionSet.opcodes_map[:goto_if_true]
320
+ gif = Rubinius::InstructionSet.opcodes_map[:goto_if_false]
321
+
322
+ iseq = exec.iseq
323
+
324
+ i = start
325
+ while i < fin
326
+ op = iseq[i]
327
+ case op
328
+ when goto
329
+ return next_interesting(exec, iseq[i + 1]) # goto target
330
+ when git, gif
331
+ return [next_interesting(exec, iseq[i + 1]),
332
+ next_interesting(exec, i + 2)] # target and next ip
333
+ else
334
+ op = Rubinius::InstructionSet[op]
335
+ i += (op.arg_count + 1)
336
+ end
337
+ end
338
+
339
+ return next_interesting(exec, fin)
340
+ end
341
+
342
+ end
343
+
344
+ class StepInto < Next
345
+ pattern "s", "step"
346
+ help "Step into next method call or to next line"
347
+ ext_help <<-HELP
348
+ Behaves like next, but if there is a method call on the current line,
349
+ execution is stopped in the called method.
350
+ HELP
351
+
352
+ def run(args)
353
+ max = step_over_by(1)
354
+
355
+ listen(true)
356
+
357
+ # We remove the max position breakpoint no matter what
358
+ max.remove! if max
359
+
360
+ end
361
+ end
362
+
363
+ class NextInstruction < Next
364
+ pattern "ni", "nexti"
365
+ help "Move to the next bytecode instruction"
366
+ ext_help <<-HELP
367
+ Continue but stop execution at the next bytecode instruction.
368
+
369
+ Does not step into send instructions.
370
+ HELP
371
+
372
+ def run(args)
373
+ if args and !args.empty?
374
+ step = args.to_i
375
+ else
376
+ step = 1
377
+ end
378
+
379
+ exec = current_method
380
+ insn = Rubinius::InstructionSet[exec.iseq[current_frame.ip]]
381
+
382
+ next_ip = current_frame.ip + insn.width
383
+
384
+ if next_ip >= exec.iseq.size
385
+ step_to_parent
386
+ elsif is_a_goto(exec, current_frame.ip)
387
+ set_breakpoints_between(exec, current_frame.ip, next_ip)
388
+ else
389
+ line = exec.line_from_ip(next_ip)
390
+
391
+ bp = BreakPoint.for_ip(exec, next_ip)
392
+ bp.for_step!(current_frame.variables)
393
+ bp.activate
394
+ end
395
+
396
+ listen
397
+ end
398
+
399
+ def is_a_goto(exec, ip)
400
+ goto = Rubinius::InstructionSet.opcodes_map[:goto]
401
+ git = Rubinius::InstructionSet.opcodes_map[:goto_if_true]
402
+ gif = Rubinius::InstructionSet.opcodes_map[:goto_if_false]
403
+
404
+ i = exec.iseq[ip]
405
+
406
+ case i
407
+ when goto, git, gif
408
+ return true
409
+ end
410
+
411
+ return false
412
+ end
413
+ end
414
+
415
+ class SetFrame < Command
416
+ pattern "f", "frame"
417
+ help "Make a specific frame in the call stack the current frame"
418
+ ext_help <<-HELP
419
+ The argument must be a number corrisponding to the frame numbers reported by
420
+ 'bt'.
421
+
422
+ The frame specified is made the current frame.
423
+ HELP
424
+
425
+ def run(args)
426
+ unless m = /(\d+)/.match(args)
427
+ error "Invalid frame number: #{args}"
428
+ return
429
+ end
430
+
431
+ num = m[1].to_i
432
+
433
+ if num >= @debugger.locations.size
434
+ error "Frame #{num} too big"
435
+ return
436
+ end
437
+
438
+ @debugger.set_frame(num)
439
+
440
+ info current_frame.describe
441
+ @debugger.show_code
442
+ end
443
+ end
444
+
445
+ class Continue < Command
446
+ pattern "c", "cont", "continue"
447
+ help "Continue running the target thread"
448
+ ext_help <<-HELP
449
+ Continue execution until another breakpoint is hit.
450
+ HELP
451
+
452
+ def run(args)
453
+ listen
454
+ end
455
+ end
456
+
457
+ class Backtrace < Command
458
+ pattern "bt", "backtrace"
459
+ help "Show the current call stack"
460
+ ext_help <<-HELP
461
+ Show the call stack as a simple list.
462
+
463
+ Passing "-v" will also show the values of all locals variables
464
+ in each frame.
465
+ HELP
466
+
467
+ def run(args)
468
+ verbose = (args =~ /-v/)
469
+
470
+ if m = /(\d+)/.match(args)
471
+ count = m[1].to_i
472
+ else
473
+ count = nil
474
+ end
475
+
476
+ info "Backtrace:"
477
+
478
+ @debugger.each_frame(current_frame) do |frame|
479
+ return if count and frame.number >= count
480
+
481
+ info "%4d %s" % [frame.number, frame.describe]
482
+
483
+ if verbose
484
+ frame.local_variables.each do |local|
485
+ info " #{local} = #{frame.run(local.to_s).inspect}"
486
+ end
487
+ end
488
+ end
489
+ end
490
+ end
491
+
492
+ class EvalCode < Command
493
+ pattern "p", "eval"
494
+ help "Run code in the current context"
495
+ ext_help <<-HELP
496
+ Run code in the context of the current frame.
497
+
498
+ The value of the expression is stored into a global variable so it
499
+ may be used again easily. The name of the global variable is printed
500
+ next to the inspect output of the value.
501
+ HELP
502
+
503
+ def run(args)
504
+ @debugger.eval_code(args)
505
+ end
506
+ end
507
+
508
+ class Disassemble < Command
509
+ pattern "dis", "disassemble"
510
+ help "Show the bytecode for the current line or method"
511
+ ext_help <<-HELP
512
+ Disassemble bytecode for the current method. By default, the bytecode
513
+ for the current line is disassembled only.
514
+
515
+ If the argument is 'all', the entire method is shown as bytecode.
516
+ HELP
517
+
518
+ def run(args)
519
+ if args and args.strip == "all"
520
+ section "Bytecode for #{current_frame.method.name}"
521
+ puts current_method.decode
522
+ else
523
+ @debugger.show_bytecode
524
+ end
525
+ end
526
+ end
527
+
528
+ class ShowInfo < Command
529
+ pattern "i", "info"
530
+ help "Show information about things"
531
+ ext_help <<-HELP
532
+ Subcommands are:
533
+ break, breakpoints, bp: List all breakpoints
534
+ HELP
535
+
536
+ def run(args)
537
+ if args
538
+ case args.strip
539
+ when "break", "breakpoints", "bp"
540
+ section "Breakpoints"
541
+ if @debugger.breakpoints.empty?
542
+ info "No breakpoints set"
543
+ end
544
+
545
+ @debugger.breakpoints.each_with_index do |bp, i|
546
+ if bp
547
+ info "%3d: %s" % [i+1, bp.describe]
548
+ if bp.has_commands?
549
+ info " #{bp.commands}"
550
+ end
551
+ if bp.has_condition?
552
+ info " stop only if #{bp.condition}"
553
+ end
554
+ end
555
+ end
556
+ else
557
+ error "Unknown info: '#{args}'"
558
+ end
559
+ else
560
+ error "No info subcommand"
561
+ end
562
+ end
563
+ end
564
+
565
+ class SetVariable < Command
566
+ pattern "set"
567
+ help "Set a debugger config variable"
568
+ ext_help <<-HELP
569
+ Set a debugger configuration variable. Use 'show' to see all variables.
570
+ HELP
571
+
572
+ def run(args)
573
+ var, val = args.split(/\s+/, 2)
574
+
575
+ if val
576
+ case val.strip
577
+ when "true", "on", "yes", ""
578
+ val = true
579
+ when "false", "off", "no"
580
+ val = false
581
+ when "nil"
582
+ val = nil
583
+ when /\d+/
584
+ val = val.to_i
585
+ end
586
+ else
587
+ val = true
588
+ end
589
+
590
+ info "Set '#{var}' = #{val.inspect}"
591
+
592
+ @debugger.variables[var.to_sym] = val
593
+ end
594
+ end
595
+
596
+ class ShowVariable < Command
597
+ pattern "show"
598
+ help "Display the value of a variable or variables"
599
+ ext_help <<-HELP
600
+ Show debugger variables and user created variables. By default,
601
+ shows all variables.
602
+
603
+ The optional argument is which variable specificly to show the value of.
604
+ HELP
605
+
606
+ def run(args)
607
+ if !args or args.strip.empty?
608
+ variables.each do |name, val|
609
+ info "var '#{name}' = #{val.inspect}"
610
+ end
611
+
612
+ if @debugger.user_variables > 0
613
+ section "User variables"
614
+ (0...@debugger.user_variables).each do |i|
615
+ str = "$d#{i}"
616
+ val = Rubinius::Globals[str.to_sym]
617
+ info "var #{str} = #{val.inspect}"
618
+ end
619
+ end
620
+ else
621
+ var = args.strip.to_sym
622
+ if variables.key?(var)
623
+ info "var '#{var}' = #{variables[var].inspect}"
624
+ else
625
+ error "No variable set named '#{var}'"
626
+ end
627
+ end
628
+
629
+ end
630
+ end
631
+
632
+ class Quit < Command
633
+ pattern "quit", "q", "exit", "ex"
634
+ help "Quit the debugger"
635
+ ext_help <<-HELP
636
+ Quits your current session and shuts down the complete process
637
+ HELP
638
+
639
+ def run(args)
640
+ Process.exit!(1)
641
+ end
642
+ end
643
+
644
+ class CommandsList < Command
645
+ pattern "commands", "command"
646
+ help "execute code every time breakpoint is reached"
647
+ ext_help <<-HELP
648
+ Set commands to be executed when a breakpoint is hit.
649
+ Give breakpoint number as argument after "commands".
650
+ With no argument, the targeted breakpoint is the last one set.
651
+ The commands themselves follow starting on the next line.
652
+ Type a line containing "end" to indicate the end of them.
653
+ Give "silent" as the first line to make the breakpoint silent;
654
+ then no output is printed when it is hit, except what the commands print.
655
+ HELP
656
+
657
+ def run(args)
658
+ bp = @debugger.breakpoints[args[:bp_id] - 1]
659
+ bp.set_commands(args[:code])
660
+ end
661
+ end
662
+
663
+ class Condition < Command
664
+ pattern "condition", "cond"
665
+ help "New condition expression on breakpoint N"
666
+ ext_help <<-HELP
667
+ Specify breakpoint number N to break only if COND is true.
668
+ Usage is `condition N COND', where N is an integer and COND is an
669
+ expression to be evaluated whenever breakpoint N is reached.
670
+ HELP
671
+
672
+ def run(args)
673
+ bp_id, condition = args.split(/\s+/, 2)
674
+ bp_id = bp_id.to_i
675
+
676
+ if @debugger.breakpoints.empty?
677
+ error "No breakpoint set"
678
+ return
679
+ elsif bp_id > @debugger.breakpoints.size || bp_id < 1
680
+ error "Invalid breakpoint number."
681
+ return
682
+ end
683
+
684
+ bp = @debugger.breakpoints[bp_id - 1]
685
+ bp.set_condition(condition)
686
+ end
687
+ end
688
+
689
+ class ListCode < Command
690
+ pattern "l", "list"
691
+ help "List code"
692
+ ext_help <<-HELP
693
+ List specified function or line.
694
+ With no argument, lists ten more lines after or around previous listing.
695
+ "list -" lists the ten lines before a previous ten-line listing.
696
+ One argument specifies a line, and ten lines are listed around that line.
697
+ Two arguments with comma between specify starting and ending lines to list.
698
+ Lines can be specified in these ways:
699
+ LINENUM, to list around that line in current file,
700
+ FILE:LINENUM, to list around that line in that file,
701
+ HELP
702
+
703
+ def run(args)
704
+ path = nil
705
+ line = nil
706
+ lines_around = 10
707
+
708
+ if args =~ /^[\w#{File::Separator}]+(\.rb)?:\d+$/
709
+ path, line = args.split(':')
710
+ line = line.to_i
711
+ elsif args.nil?
712
+ line = if @debugger.variables[:list_command_history][:center_line]
713
+ @debugger.variables[:list_command_history][:center_line] + 1 + lines_around
714
+ else
715
+ @debugger.current_frame.line.to_i
716
+ end
717
+ path = @debugger.variables[:list_command_history][:path] || @debugger.current_frame.method.active_path
718
+ elsif args == "-"
719
+ if @debugger.variables[:list_command_history][:center_line].nil? || @debugger.variables[:list_command_history][:path].nil?
720
+ return
721
+ else
722
+ line = @debugger.variables[:list_command_history][:center_line] - lines_around
723
+ path = @debugger.variables[:list_command_history][:path]
724
+ end
725
+ elsif args =~ /^\d+$/
726
+ line = args.to_i
727
+ path = @debugger.current_frame.method.active_path
728
+ elsif match = /^(\d+),(\d+)$/.match(args)
729
+ start_line = match[1].to_i
730
+ end_line = match[2].to_i
731
+ path = @debugger.current_frame.method.active_path
732
+
733
+ @debugger.list_code_range(path, start_line, end_line, end_line)
734
+ return
735
+ else
736
+ error 'Invalid args for list'
737
+ return
738
+ end
739
+
740
+ @debugger.list_code_around_line(path, line, lines_around)
741
+ end
742
+ end
743
+ end
744
+
745
+ end