debug 1.6.3 → 1.7.1

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.
@@ -6,6 +6,22 @@ module DEBUGGER__
6
6
  class SourceRepository
7
7
  include Color
8
8
 
9
+ def file_src iseq
10
+ if (path = (iseq.absolute_path || iseq.path)) && File.exist?(path)
11
+ File.readlines(path, chomp: true)
12
+ end
13
+ end
14
+
15
+ def get iseq
16
+ return unless iseq
17
+
18
+ if CONFIG[:show_evaledsrc]
19
+ orig_src(iseq) || file_src(iseq)
20
+ else
21
+ file_src(iseq) || orig_src(iseq)
22
+ end
23
+ end
24
+
9
25
  if RubyVM.respond_to? :keep_script_lines
10
26
  # Ruby 3.1 and later
11
27
  RubyVM.keep_script_lines = true
@@ -29,17 +45,13 @@ module DEBUGGER__
29
45
  end
30
46
  end
31
47
 
32
- def get iseq
33
- return unless iseq
34
-
35
- if lines = iseq.script_lines&.map(&:chomp)
36
- lines
48
+ def orig_src iseq
49
+ lines = iseq.script_lines&.map(&:chomp)
50
+ line = iseq.first_line
51
+ if line > 1
52
+ lines = [*([''] * (line - 1)), *lines]
37
53
  else
38
- if (path = (iseq.absolute_path || iseq.path)) && File.exist?(path)
39
- File.readlines(path, chomp: true)
40
- else
41
- nil
42
- end
54
+ lines
43
55
  end
44
56
  end
45
57
 
@@ -63,15 +75,22 @@ module DEBUGGER__
63
75
  end
64
76
 
65
77
  def add iseq, src
66
- if (path = (iseq.absolute_path || iseq.path)) && File.exist?(path)
78
+ path = (iseq.absolute_path || iseq.path)
79
+
80
+ if path && File.exist?(path)
67
81
  reloaded = @files.has_key? path
68
- add_path path
69
- return path, reloaded
70
- elsif src
82
+
83
+ if src
84
+ add_iseq iseq, src
85
+ return path, reloaded
86
+ else
87
+ add_path path
88
+ return path, reloaded
89
+ end
90
+ else
71
91
  add_iseq iseq, src
92
+ nil
72
93
  end
73
-
74
- nil
75
94
  end
76
95
 
77
96
  private def all_iseq iseq, rs = []
@@ -87,7 +106,8 @@ module DEBUGGER__
87
106
  if line > 1
88
107
  src = ("\n" * (line - 1)) + src
89
108
  end
90
- si = SrcInfo.new(src.lines)
109
+
110
+ si = SrcInfo.new(src.each_line.map{|l| l.chomp})
91
111
  all_iseq(iseq).each{|e|
92
112
  e.instance_variable_set(:@debugger_si, si)
93
113
  e.freeze
@@ -102,7 +122,7 @@ module DEBUGGER__
102
122
 
103
123
  private def get_si iseq
104
124
  return unless iseq
105
-
125
+
106
126
  if iseq.instance_variable_defined?(:@debugger_si)
107
127
  iseq.instance_variable_get(:@debugger_si)
108
128
  elsif @files.has_key?(path = (iseq.absolute_path || iseq.path))
@@ -112,7 +132,7 @@ module DEBUGGER__
112
132
  end
113
133
  end
114
134
 
115
- def get iseq
135
+ def orig_src iseq
116
136
  if si = get_si(iseq)
117
137
  si.src
118
138
  end
data/lib/debug/start.rb CHANGED
@@ -2,4 +2,4 @@
2
2
 
3
3
  require_relative 'session'
4
4
  return unless defined?(DEBUGGER__)
5
- DEBUGGER__.start
5
+ DEBUGGER__.start no_sigint_hook: false
@@ -18,7 +18,7 @@ module DEBUGGER__
18
18
  module SkipPathHelper
19
19
  def skip_path?(path)
20
20
  !path ||
21
- CONFIG.skip? ||
21
+ DEBUGGER__.skip? ||
22
22
  ThreadClient.current.management? ||
23
23
  skip_internal_path?(path) ||
24
24
  skip_config_skip_path?(path)
@@ -29,7 +29,7 @@ module DEBUGGER__
29
29
  end
30
30
 
31
31
  def skip_internal_path?(path)
32
- path.start_with?(__dir__) || path.start_with?('<internal:')
32
+ path.start_with?(__dir__) || path.delete_prefix('!eval:').start_with?('<internal:')
33
33
  end
34
34
 
35
35
  def skip_location?(loc)
@@ -38,6 +38,13 @@ module DEBUGGER__
38
38
  end
39
39
  end
40
40
 
41
+ module GlobalVariablesHelper
42
+ SKIP_GLOBAL_LIST = %i[$= $KCODE $-K $SAFE].freeze
43
+ def safe_global_variables
44
+ global_variables.reject{|name| SKIP_GLOBAL_LIST.include? name }
45
+ end
46
+ end
47
+
41
48
  class ThreadClient
42
49
  def self.current
43
50
  if thc = Thread.current[:DEBUGGER__ThreadClient]
@@ -50,6 +57,7 @@ module DEBUGGER__
50
57
 
51
58
  include Color
52
59
  include SkipPathHelper
60
+ include GlobalVariablesHelper
53
61
 
54
62
  attr_reader :thread, :id, :recorder, :check_bp_fulfillment_map
55
63
 
@@ -291,8 +299,10 @@ module DEBUGGER__
291
299
  end
292
300
 
293
301
  if event != :pause
294
- show_src
295
- show_frames CONFIG[:show_frames]
302
+ unless bp&.skip_src
303
+ show_src
304
+ show_frames CONFIG[:show_frames]
305
+ end
296
306
 
297
307
  set_mode :waiting
298
308
 
@@ -328,11 +338,15 @@ module DEBUGGER__
328
338
  @step_tp.disable if @step_tp
329
339
 
330
340
  thread = Thread.current
341
+ subsession_id = SESSION.subsession_id
331
342
 
332
343
  if SUPPORT_TARGET_THREAD
333
344
  @step_tp = TracePoint.new(*events){|tp|
334
- next if SESSION.break_at? tp.path, tp.lineno
335
- next if !yield(tp.event)
345
+ if SESSION.stop_stepping? tp.path, tp.lineno, subsession_id
346
+ tp.disable
347
+ next
348
+ end
349
+ next if !yield(tp)
336
350
  next if tp.path.start_with?(__dir__)
337
351
  next if tp.path.start_with?('<internal:trace_point>')
338
352
  next unless File.exist?(tp.path) if CONFIG[:skip_nosrc]
@@ -347,8 +361,11 @@ module DEBUGGER__
347
361
  else
348
362
  @step_tp = TracePoint.new(*events){|tp|
349
363
  next if thread != Thread.current
350
- next if SESSION.break_at? tp.path, tp.lineno
351
- next if !yield(tp.event)
364
+ if SESSION.stop_stepping? tp.path, tp.lineno, subsession_id
365
+ tp.disable
366
+ next
367
+ end
368
+ next if !yield(tp)
352
369
  next if tp.path.start_with?(__dir__)
353
370
  next if tp.path.start_with?('<internal:trace_point>')
354
371
  next unless File.exist?(tp.path) if CONFIG[:skip_nosrc]
@@ -384,15 +401,19 @@ module DEBUGGER__
384
401
  end
385
402
  end
386
403
 
387
- def frame_eval_core src, b
404
+ def frame_eval_core src, b, binding_location: false
388
405
  saved_target_frames = @target_frames
389
406
  saved_current_frame_index = @current_frame_index
390
407
 
391
408
  if b
392
- f, _l = b.source_location
409
+ file, lineno = b.source_location
393
410
 
394
411
  tp_allow_reentry do
395
- b.eval(src, "(rdbg)/#{f}")
412
+ if binding_location
413
+ b.eval(src, file, lineno)
414
+ else
415
+ b.eval(src, "(rdbg)/#{file}")
416
+ end
396
417
  end
397
418
  else
398
419
  frame_self = current_frame.self
@@ -411,16 +432,16 @@ module DEBUGGER__
411
432
  [:return_value, "_return"],
412
433
  ]
413
434
 
414
- def frame_eval src, re_raise: false
435
+ def frame_eval src, re_raise: false, binding_location: false
415
436
  @success_last_eval = false
416
437
 
417
- b = current_frame.eval_binding
438
+ b = current_frame&.eval_binding || TOPLEVEL_BINDING
418
439
 
419
440
  special_local_variables current_frame do |name, var|
420
441
  b.local_variable_set(name, var) if /\%/ !~ name
421
442
  end
422
443
 
423
- result = frame_eval_core(src, b)
444
+ result = frame_eval_core(src, b, binding_location: binding_location)
424
445
 
425
446
  @success_last_eval = true
426
447
  result
@@ -482,19 +503,28 @@ module DEBUGGER__
482
503
  exit!
483
504
  end
484
505
 
485
- def show_src(frame_index: @current_frame_index, update_line: false, max_lines: CONFIG[:show_src_lines], **options)
506
+ def show_src(frame_index: @current_frame_index, update_line: false, ignore_show_line: false, max_lines: CONFIG[:show_src_lines], **options)
486
507
  if frame = get_frame(frame_index)
487
- start_line, end_line, lines = *get_src(frame, max_lines: max_lines, **options)
488
-
489
- if start_line
490
- if update_line
491
- frame.show_line = end_line
508
+ begin
509
+ if ignore_show_line
510
+ prev_show_line = frame.show_line
511
+ frame.show_line = nil
492
512
  end
493
513
 
494
- puts "[#{start_line+1}, #{end_line}] in #{frame.pretty_path}" if !update_line && max_lines != 1
495
- puts lines[start_line...end_line]
496
- else
497
- puts "# No sourcefile available for #{frame.path}"
514
+ start_line, end_line, lines = *get_src(frame, max_lines: max_lines, **options)
515
+
516
+ if start_line
517
+ if update_line
518
+ frame.show_line = end_line
519
+ end
520
+
521
+ puts "[#{start_line+1}, #{end_line}] in #{frame.pretty_path}" if !update_line && max_lines != 1
522
+ puts lines[start_line...end_line]
523
+ else
524
+ puts "# No sourcefile available for #{frame.path}"
525
+ end
526
+ ensure
527
+ frame.show_line = prev_show_line if prev_show_line
498
528
  end
499
529
  end
500
530
  end
@@ -546,27 +576,55 @@ module DEBUGGER__
546
576
  end
547
577
  end
548
578
 
549
- def show_ivars pat
550
- if s = current_frame&.self
551
- M_INSTANCE_VARIABLES.bind_call(s).sort.each{|iv|
552
- value = M_INSTANCE_VARIABLE_GET.bind_call(s, iv)
579
+ def show_ivars pat, expr = nil
580
+ if expr && !expr.empty?
581
+ _self = frame_eval(expr);
582
+ elsif _self = current_frame&.self
583
+ else
584
+ _self = nil
585
+ end
586
+
587
+ if _self
588
+ M_INSTANCE_VARIABLES.bind_call(_self).sort.each{|iv|
589
+ value = M_INSTANCE_VARIABLE_GET.bind_call(_self, iv)
553
590
  puts_variable_info iv, value, pat
554
591
  }
555
592
  end
556
593
  end
557
594
 
558
- def show_consts pat, only_self: false
559
- if s = current_frame&.self
595
+ def iter_consts c, names = {}
596
+ c.constants(false).sort.each{|name|
597
+ next if names.has_key? name
598
+ names[name] = nil
599
+ begin
600
+ value = c.const_get(name)
601
+ rescue Exception => e
602
+ value = e
603
+ end
604
+ yield name, value
605
+ }
606
+ end
607
+
608
+ def get_consts expr = nil, only_self: false, &block
609
+ if expr && !expr.empty?
610
+ _self = frame_eval(expr)
611
+ if M_KIND_OF_P.bind_call(_self, Module)
612
+ iter_consts _self, &block
613
+ return
614
+ else
615
+ raise "#{_self.inspect} (by #{expr}) is not a Module."
616
+ end
617
+ elsif _self = current_frame&.self
560
618
  cs = {}
561
- if M_KIND_OF_P.bind_call(s, Module)
562
- cs[s] = :self
619
+ if M_KIND_OF_P.bind_call(_self, Module)
620
+ cs[_self] = :self
563
621
  else
564
- s = M_CLASS.bind_call(s)
565
- cs[s] = :self unless only_self
622
+ _self = M_CLASS.bind_call(_self)
623
+ cs[_self] = :self unless only_self
566
624
  end
567
625
 
568
626
  unless only_self
569
- s.ancestors.each{|c| break if c == Object; cs[c] = :ancestors}
627
+ _self.ancestors.each{|c| break if c == Object; cs[c] = :ancestors}
570
628
  if b = current_frame&.binding
571
629
  b.eval('::Module.nesting').each{|c| cs[c] = :nesting unless cs.has_key? c}
572
630
  end
@@ -575,19 +633,19 @@ module DEBUGGER__
575
633
  names = {}
576
634
 
577
635
  cs.each{|c, _|
578
- c.constants(false).sort.each{|name|
579
- next if names.has_key? name
580
- names[name] = nil
581
- value = c.const_get(name)
582
- puts_variable_info name, value, pat
583
- }
636
+ iter_consts c, names, &block
584
637
  }
585
638
  end
586
639
  end
587
640
 
588
- SKIP_GLOBAL_LIST = %i[$= $KCODE $-K $SAFE].freeze
641
+ def show_consts pat, expr = nil, only_self: false
642
+ get_consts expr, only_self: only_self do |name, value|
643
+ puts_variable_info name, value, pat
644
+ end
645
+ end
646
+
589
647
  def show_globals pat
590
- global_variables.sort.each{|name|
648
+ safe_global_variables.sort.each{|name|
591
649
  next if SKIP_GLOBAL_LIST.include? name
592
650
 
593
651
  value = eval(name.to_s)
@@ -643,7 +701,8 @@ module DEBUGGER__
643
701
  if editor = (ENV['RUBY_DEBUG_EDITOR'] || ENV['EDITOR'])
644
702
  puts "command: #{editor}"
645
703
  puts " path: #{path}"
646
- system(editor, path)
704
+ require 'shellwords'
705
+ system(*Shellwords.split(editor), path)
647
706
  else
648
707
  puts "can not find editor setting: ENV['RUBY_DEBUG_EDITOR'] or ENV['EDITOR']"
649
708
  end
@@ -758,7 +817,7 @@ module DEBUGGER__
758
817
  case args.first
759
818
  when :method
760
819
  klass_name, op, method_name, cond, cmd, path = args[1..]
761
- bp = MethodBreakpoint.new(current_frame.eval_binding, klass_name, op, method_name, cond: cond, command: cmd, path: path)
820
+ bp = MethodBreakpoint.new(current_frame&.eval_binding || TOPLEVEL_BINDING, klass_name, op, method_name, cond: cond, command: cmd, path: path)
762
821
  begin
763
822
  bp.enable
764
823
  rescue NameError => e
@@ -814,6 +873,7 @@ module DEBUGGER__
814
873
  set_mode :waiting if !waiting?
815
874
  cmds = @q_cmd.pop
816
875
  # pp [self, cmds: cmds]
876
+
817
877
  break unless cmds
818
878
  ensure
819
879
  set_mode :running
@@ -831,8 +891,9 @@ module DEBUGGER__
831
891
 
832
892
  case step_type
833
893
  when :in
894
+ iter = iter || 1
834
895
  if @recorder&.replaying?
835
- @recorder.step_forward
896
+ @recorder.step_forward iter
836
897
  raise SuspendReplay
837
898
  else
838
899
  step_tp iter do
@@ -845,6 +906,7 @@ module DEBUGGER__
845
906
  frame = @target_frames.first
846
907
  path = frame.location.absolute_path || "!eval:#{frame.path}"
847
908
  line = frame.location.lineno
909
+ label = frame.location.base_label
848
910
 
849
911
  if frame.iseq
850
912
  frame.iseq.traceable_lines_norec(lines = {})
@@ -856,35 +918,89 @@ module DEBUGGER__
856
918
 
857
919
  depth = @target_frames.first.frame_depth
858
920
 
859
- step_tp iter do
921
+ step_tp iter do |tp|
860
922
  loc = caller_locations(2, 1).first
861
923
  loc_path = loc.absolute_path || "!eval:#{loc.path}"
924
+ loc_label = loc.base_label
925
+ loc_depth = DEBUGGER__.frame_depth - 3
862
926
 
863
- # same stack depth
864
- (DEBUGGER__.frame_depth - 3 <= depth) ||
865
-
866
- # different frame
867
- (next_line && loc_path == path &&
868
- (loc_lineno = loc.lineno) > line &&
869
- loc_lineno <= next_line)
927
+ case
928
+ when loc_depth == depth && loc_label == label
929
+ true
930
+ when loc_depth < depth
931
+ # lower stack depth
932
+ true
933
+ when (next_line &&
934
+ loc_path == path &&
935
+ (loc_lineno = loc.lineno) > line &&
936
+ loc_lineno <= next_line)
937
+ # different frame (maybe block) but the line is before next_line
938
+ true
939
+ end
870
940
  end
871
941
  break
872
942
 
873
943
  when :finish
874
944
  finish_frames = (iter || 1) - 1
875
- goal_depth = @target_frames.first.frame_depth - finish_frames
945
+ frame = @target_frames.first
946
+ goal_depth = frame.frame_depth - finish_frames - (frame.has_return_value ? 1 : 0)
876
947
 
877
948
  step_tp nil, [:return, :b_return] do
878
949
  DEBUGGER__.frame_depth - 3 <= goal_depth ? true : false
879
950
  end
880
951
  break
881
952
 
953
+ when :until
954
+ location = iter&.strip
955
+ frame = @target_frames.first
956
+ depth = frame.frame_depth - (frame.has_return_value ? 1 : 0)
957
+ target_location_label = frame.location.base_label
958
+
959
+ case location
960
+ when nil, /\A(?:(.+):)?(\d+)\z/
961
+ no_loc = !location
962
+ file = $1 || frame.location.path
963
+ line = ($2 || frame.location.lineno + 1).to_i
964
+
965
+ step_tp nil, [:line, :return] do |tp|
966
+ if tp.event == :line
967
+ next false if no_loc && depth < DEBUGGER__.frame_depth - 3
968
+ next false unless tp.path.end_with?(file)
969
+ next false unless tp.lineno >= line
970
+ true
971
+ else
972
+ true if depth >= DEBUGGER__.frame_depth - 3 &&
973
+ caller_locations(2, 1).first.label == target_location_label
974
+ # TODO: imcomplete condition
975
+ end
976
+ end
977
+ else
978
+ pat = location
979
+ if /\A\/(.+)\/\z/ =~ pat
980
+ pat = Regexp.new($1)
981
+ end
982
+
983
+ step_tp nil, [:call, :c_call, :return] do |tp|
984
+ case tp.event
985
+ when :call, :c_call
986
+ true if pat === tp.callee_id.to_s
987
+ else # :return, :b_return
988
+ true if depth >= DEBUGGER__.frame_depth - 3 &&
989
+ caller_locations(2, 1).first.label == target_location_label
990
+ # TODO: imcomplete condition
991
+ end
992
+ end
993
+ end
994
+
995
+ break
996
+
882
997
  when :back
998
+ iter = iter || 1
883
999
  if @recorder&.can_step_back?
884
1000
  unless @recorder.backup_frames
885
1001
  @recorder.backup_frames = @target_frames
886
1002
  end
887
- @recorder.step_back
1003
+ @recorder.step_back iter
888
1004
  raise SuspendReplay
889
1005
  else
890
1006
  puts "Can not step back more."
@@ -922,8 +1038,9 @@ module DEBUGGER__
922
1038
  when :call
923
1039
  result = frame_eval(eval_src)
924
1040
  when :irb
1041
+ require 'irb' # prelude's binding.irb doesn't have show_code option
925
1042
  begin
926
- result = frame_eval('binding.irb')
1043
+ result = frame_eval('binding.irb(show_code: false)', binding_location: true)
927
1044
  ensure
928
1045
  # workaround: https://github.com/ruby/debug/issues/308
929
1046
  Reline.prompt_proc = nil if defined? Reline
@@ -988,6 +1105,10 @@ module DEBUGGER__
988
1105
  when :list
989
1106
  show_src(update_line: true, **(args.first || {}))
990
1107
 
1108
+ when :whereami
1109
+ show_src ignore_show_line: true
1110
+ show_frames CONFIG[:show_frames]
1111
+
991
1112
  when :edit
992
1113
  show_by_editor(args.first)
993
1114
 
@@ -1003,11 +1124,13 @@ module DEBUGGER__
1003
1124
 
1004
1125
  when :ivars
1005
1126
  pat = args.shift
1006
- show_ivars pat
1127
+ expr = args.shift
1128
+ show_ivars pat, expr
1007
1129
 
1008
1130
  when :consts
1009
1131
  pat = args.shift
1010
- show_consts pat
1132
+ expr = args.shift
1133
+ show_consts pat, expr
1011
1134
 
1012
1135
  when :globals
1013
1136
  pat = args.shift
@@ -1093,6 +1216,8 @@ module DEBUGGER__
1093
1216
  end
1094
1217
  event! :result, nil
1095
1218
 
1219
+ when :quit
1220
+ sleep # wait for SystemExit
1096
1221
  when :dap
1097
1222
  process_dap args
1098
1223
  when :cdp
@@ -1107,6 +1232,8 @@ module DEBUGGER__
1107
1232
  rescue Exception => e
1108
1233
  pp ["DEBUGGER Exception: #{__FILE__}:#{__LINE__}", e, e.backtrace]
1109
1234
  raise
1235
+ ensure
1236
+ @returning = false
1110
1237
  end
1111
1238
 
1112
1239
  def debug_event(ev, args)
@@ -1187,12 +1314,18 @@ module DEBUGGER__
1187
1314
  @tp_recorder.enabled?
1188
1315
  end
1189
1316
 
1190
- def step_back
1191
- @index += 1
1317
+ def step_back iter
1318
+ @index += iter
1319
+ if @index > @log.size
1320
+ @index = @log.size
1321
+ end
1192
1322
  end
1193
1323
 
1194
- def step_forward
1195
- @index -= 1
1324
+ def step_forward iter
1325
+ @index -= iter
1326
+ if @index < 0
1327
+ @index = 0
1328
+ end
1196
1329
  end
1197
1330
 
1198
1331
  def step_reset
data/lib/debug/tracer.rb CHANGED
File without changes
data/lib/debug/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DEBUGGER__
4
- VERSION = "1.6.3"
4
+ VERSION = "1.7.1"
5
5
  end
data/lib/debug.rb CHANGED
@@ -1,5 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'debug/session'
4
- return unless defined?(DEBUGGER__)
5
- DEBUGGER__::start no_sigint_hook: true, nonstop: true
3
+ if ENV['RUBY_DEBUG_LAZY']
4
+ require_relative 'debug/prelude'
5
+ else
6
+ require_relative 'debug/session'
7
+ return unless defined?(DEBUGGER__)
8
+ DEBUGGER__::start no_sigint_hook: true, nonstop: true
9
+ end
data/misc/README.md.erb CHANGED
@@ -498,7 +498,8 @@ There are additional features:
498
498
  * `<expr>` without debug command is almost same as `pp <expr>`.
499
499
  * If the input line `<expr>` does *NOT* start with any debug command, the line `<expr>` will be evaluated as a Ruby expression and the result will be printed with `pp` method. So that the input `foo.bar` is same as `pp foo.bar`.
500
500
  * If `<expr>` is recognized as a debug command, of course it is not evaluated as a Ruby expression, but is executed as debug command. For example, you can not evaluate such single letter local variables `i`, `b`, `n`, `c` because they are single letter debug commands. Use `p i` instead.
501
- * `Enter` without any input repeats the last command (useful when repeating `step`s).
501
+ * So the author (Koichi Sasada) recommends to use `p`, `pp` or `eval` command to evaluate the Ruby expression everytime.
502
+ * `Enter` without any input repeats the last command (useful when repeating `step`s) for some commands.
502
503
  * `Ctrl-D` is equal to `quit` command.
503
504
  * [debug command compare sheet - Google Sheets](https://docs.google.com/spreadsheets/d/1TlmmUDsvwK4sSIyoMv-io52BUUz__R5wpu-ComXlsw0/edit?usp=sharing)
504
505
 
@@ -567,11 +568,11 @@ It is useful if you only want to call a debug command and don't want to stop the
567
568
  ```
568
569
  def initialize
569
570
  @a = 1
570
- binding.b do: 'watch @a'
571
+ binding.b do: 'info \n watch @a'
571
572
  end
572
573
  ```
573
574
 
574
- On this case, register a watch breakpoint for `@a` and continue to run.
575
+ On this case, execute the `info` command then register a watch breakpoint for `@a` and continue to run. You can also use `;;` instead of `\n` to separate your commands.
575
576
 
576
577
  If `pre: 'command'` is specified, the debugger suspends the program and run the `command` as a debug command, and keep suspend.
577
578
  It is useful if you have operations before suspend.
@@ -591,6 +592,11 @@ On this case, you can see the result of `bar()` every time you stop there.
591
592
  <%= `exe/rdbg --help` %>
592
593
  ```
593
594
 
595
+ # Additional Resources
596
+
597
+ - [From byebug to ruby/debug](https://st0012.dev/from-byebug-to-ruby-debug) by Stan Lo - A migration guide for `byebug` users.
598
+ - [ruby/debug cheatsheet](https://st0012.dev/ruby-debug-cheatsheet) by Stan Lo
599
+
594
600
  # Contributing
595
601
 
596
602
  Bug reports and pull requests are welcome on GitHub at https://github.com/ruby/debug.
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: debug
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.6.3
4
+ version: 1.7.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Koichi Sasada
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-10-24 00:00:00.000000000 Z
11
+ date: 2022-12-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: irb
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 1.3.6
19
+ version: 1.5.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: 1.3.6
26
+ version: 1.5.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: reline
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -60,6 +60,7 @@ files:
60
60
  - ext/debug/extconf.rb
61
61
  - ext/debug/iseq_collector.c
62
62
  - lib/debug.rb
63
+ - lib/debug/abbrev_command.rb
63
64
  - lib/debug/breakpoint.rb
64
65
  - lib/debug/client.rb
65
66
  - lib/debug/color.rb