byebug 3.5.1 → 4.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.
Files changed (137) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +4 -1
  3. data/.rubocop.yml +18 -1
  4. data/.travis.yml +21 -1
  5. data/CHANGELOG.md +356 -308
  6. data/CONTRIBUTING.md +31 -15
  7. data/GUIDE.md +859 -475
  8. data/Gemfile +8 -10
  9. data/LICENSE +1 -1
  10. data/README.md +41 -45
  11. data/Rakefile +30 -28
  12. data/byebug.gemspec +18 -18
  13. data/ext/byebug/breakpoint.c +88 -75
  14. data/ext/byebug/byebug.c +253 -252
  15. data/ext/byebug/byebug.h +53 -53
  16. data/ext/byebug/context.c +188 -159
  17. data/ext/byebug/extconf.rb +9 -6
  18. data/ext/byebug/locker.c +53 -11
  19. data/ext/byebug/threads.c +137 -39
  20. data/lib/byebug/attacher.rb +7 -2
  21. data/lib/byebug/breakpoint.rb +30 -0
  22. data/lib/byebug/command.rb +36 -32
  23. data/lib/byebug/commands/break.rb +49 -48
  24. data/lib/byebug/commands/catch.rb +64 -0
  25. data/lib/byebug/commands/condition.rb +13 -9
  26. data/lib/byebug/commands/continue.rb +8 -4
  27. data/lib/byebug/commands/delete.rb +10 -4
  28. data/lib/byebug/commands/display.rb +33 -25
  29. data/lib/byebug/commands/edit.rb +18 -13
  30. data/lib/byebug/commands/enable_disable.rb +26 -24
  31. data/lib/byebug/commands/eval.rb +77 -35
  32. data/lib/byebug/commands/finish.rb +9 -5
  33. data/lib/byebug/commands/frame.rb +66 -125
  34. data/lib/byebug/commands/help.rb +14 -21
  35. data/lib/byebug/commands/history.rb +5 -1
  36. data/lib/byebug/commands/info.rb +41 -106
  37. data/lib/byebug/commands/interrupt.rb +6 -2
  38. data/lib/byebug/commands/irb.rb +5 -2
  39. data/lib/byebug/commands/kill.rb +6 -2
  40. data/lib/byebug/commands/list.rb +21 -14
  41. data/lib/byebug/commands/method.rb +17 -9
  42. data/lib/byebug/commands/pry.rb +13 -3
  43. data/lib/byebug/commands/quit.rb +10 -5
  44. data/lib/byebug/commands/restart.rb +12 -19
  45. data/lib/byebug/commands/save.rb +10 -6
  46. data/lib/byebug/commands/set.rb +15 -14
  47. data/lib/byebug/commands/show.rb +8 -8
  48. data/lib/byebug/commands/source.rb +14 -8
  49. data/lib/byebug/commands/stepping.rb +15 -29
  50. data/lib/byebug/commands/threads.rb +73 -49
  51. data/lib/byebug/commands/tracevar.rb +56 -0
  52. data/lib/byebug/commands/undisplay.rb +8 -4
  53. data/lib/byebug/commands/untracevar.rb +38 -0
  54. data/lib/byebug/commands/var.rb +107 -0
  55. data/lib/byebug/context.rb +78 -42
  56. data/lib/byebug/core.rb +78 -40
  57. data/lib/byebug/helper.rb +58 -42
  58. data/lib/byebug/history.rb +12 -1
  59. data/lib/byebug/interface.rb +91 -11
  60. data/lib/byebug/interfaces/local_interface.rb +12 -19
  61. data/lib/byebug/interfaces/remote_interface.rb +12 -15
  62. data/lib/byebug/interfaces/script_interface.rb +14 -18
  63. data/lib/byebug/interfaces/test_interface.rb +54 -0
  64. data/lib/byebug/printers/base.rb +64 -0
  65. data/lib/byebug/printers/plain.rb +53 -0
  66. data/lib/byebug/processor.rb +20 -1
  67. data/lib/byebug/processors/command_processor.rb +57 -172
  68. data/lib/byebug/processors/control_command_processor.rb +16 -43
  69. data/lib/byebug/remote.rb +13 -7
  70. data/lib/byebug/runner.rb +102 -54
  71. data/lib/byebug/setting.rb +45 -68
  72. data/lib/byebug/settings/autoeval.rb +2 -0
  73. data/lib/byebug/settings/autoirb.rb +3 -0
  74. data/lib/byebug/settings/autolist.rb +3 -0
  75. data/lib/byebug/settings/autosave.rb +2 -0
  76. data/lib/byebug/settings/basename.rb +2 -0
  77. data/lib/byebug/settings/callstyle.rb +2 -0
  78. data/lib/byebug/settings/fullpath.rb +2 -0
  79. data/lib/byebug/settings/histfile.rb +2 -0
  80. data/lib/byebug/settings/histsize.rb +2 -0
  81. data/lib/byebug/settings/linetrace.rb +2 -0
  82. data/lib/byebug/settings/listsize.rb +2 -0
  83. data/lib/byebug/settings/post_mortem.rb +7 -2
  84. data/lib/byebug/settings/stack_on_error.rb +2 -0
  85. data/lib/byebug/settings/verbose.rb +2 -0
  86. data/lib/byebug/settings/width.rb +2 -0
  87. data/lib/byebug/state.rb +12 -0
  88. data/lib/byebug/states/control_state.rb +26 -0
  89. data/lib/byebug/states/regular_state.rb +178 -0
  90. data/lib/byebug/version.rb +1 -1
  91. metadata +24 -109
  92. data/lib/byebug/commands/catchpoint.rb +0 -53
  93. data/lib/byebug/commands/reload.rb +0 -29
  94. data/lib/byebug/commands/trace.rb +0 -50
  95. data/lib/byebug/commands/variables.rb +0 -206
  96. data/lib/byebug/options.rb +0 -46
  97. data/lib/byebug/settings/autoreload.rb +0 -12
  98. data/lib/byebug/settings/forcestep.rb +0 -14
  99. data/lib/byebug/settings/testing.rb +0 -12
  100. data/lib/byebug/settings/tracing_plus.rb +0 -11
  101. data/test/commands/break_test.rb +0 -364
  102. data/test/commands/condition_test.rb +0 -85
  103. data/test/commands/continue_test.rb +0 -47
  104. data/test/commands/delete_test.rb +0 -26
  105. data/test/commands/display_test.rb +0 -37
  106. data/test/commands/edit_test.rb +0 -52
  107. data/test/commands/eval_test.rb +0 -89
  108. data/test/commands/finish_test.rb +0 -74
  109. data/test/commands/frame_test.rb +0 -223
  110. data/test/commands/help_test.rb +0 -66
  111. data/test/commands/history_test.rb +0 -61
  112. data/test/commands/info_test.rb +0 -238
  113. data/test/commands/interrupt_test.rb +0 -45
  114. data/test/commands/irb_test.rb +0 -28
  115. data/test/commands/kill_test.rb +0 -50
  116. data/test/commands/list_test.rb +0 -174
  117. data/test/commands/method_test.rb +0 -52
  118. data/test/commands/post_mortem_test.rb +0 -71
  119. data/test/commands/pry_test.rb +0 -26
  120. data/test/commands/quit_test.rb +0 -53
  121. data/test/commands/reload_test.rb +0 -39
  122. data/test/commands/restart_test.rb +0 -46
  123. data/test/commands/save_test.rb +0 -67
  124. data/test/commands/set_test.rb +0 -140
  125. data/test/commands/show_test.rb +0 -76
  126. data/test/commands/source_test.rb +0 -46
  127. data/test/commands/stepping_test.rb +0 -192
  128. data/test/commands/thread_test.rb +0 -164
  129. data/test/commands/trace_test.rb +0 -71
  130. data/test/commands/undisplay_test.rb +0 -75
  131. data/test/commands/variables_test.rb +0 -105
  132. data/test/debugger_alias_test.rb +0 -7
  133. data/test/runner_test.rb +0 -150
  134. data/test/support/matchers.rb +0 -65
  135. data/test/support/test_interface.rb +0 -59
  136. data/test/support/utils.rb +0 -122
  137. data/test/test_helper.rb +0 -58
@@ -1,3 +1,5 @@
1
+ require 'byebug/command'
2
+
1
3
  module Byebug
2
4
  #
3
5
  # Implements breakpoint functionality
@@ -6,78 +8,77 @@ module Byebug
6
8
  self.allow_in_post_mortem = false
7
9
  self.allow_in_control = true
8
10
 
9
- POSITION_REGEXP = '(?:(\d+)|(.+?)[:.#]([^.:\s]+))'
10
-
11
11
  def regexp
12
- /^\s* b(?:reak)? (?:\s+ #{POSITION_REGEXP})? (?:\s+(.+))? \s*$/x
12
+ /^\s* b(?:reak)? (?:\s+ (\S+))? (?:\s+ if \s+(.+))? \s*$/x
13
13
  end
14
14
 
15
15
  def execute
16
- return puts(self.class.help) if self.class.names.include?(@match[0])
16
+ return puts(self.class.help) unless @match[1]
17
17
 
18
- if @match[1]
19
- line, _, _, expr = @match.captures
20
- else
21
- _, file, line, expr = @match.captures
18
+ brkpt = line_breakpoint(@match[1]) || method_breakpoint(@match[1])
19
+ if syntax_valid?(@match[2])
20
+ return puts(
21
+ pr('break.created', id: brkpt.id, file: brkpt.source, line: brkpt.pos)
22
+ )
22
23
  end
23
24
 
24
- if expr && file.nil? && line.nil?
25
- return errmsg("Invalid breakpoint location: #{expr}")
26
- elsif expr && expr !~ /^\s*if\s+(.+)/
27
- return errmsg("Expecting \"if\" in breakpoint condition, got: #{expr}")
28
- else
29
- expr = $1
25
+ errmsg(pr('break.errors.expression', expr: @match[2]))
26
+ brkpt.enabled = false
27
+ rescue => e
28
+ errmsg(e.message)
29
+ end
30
+
31
+ class << self
32
+ def names
33
+ %w(break)
30
34
  end
31
35
 
32
- if file.nil? && !@state.context
33
- return errmsg('We are not in a state that has an associated file')
36
+ def description
37
+ prettify <<-EOD
38
+ b[reak] [file:]line [if expr]
39
+ b[reak] [module::...]class(.|#)method [if expr]
40
+
41
+ Set breakpoint to some position, (optionally) if expr == true
42
+ EOD
34
43
  end
44
+ end
35
45
 
36
- file = @state.file if file.nil?
37
- line = @state.line.to_s if line.nil?
46
+ private
38
47
 
39
- if line =~ /^\d+$/
40
- path = CommandProcessor.canonic_file(file)
41
- return errmsg("No file named #{path}") unless File.exist?(file)
48
+ def line_breakpoint(loc)
49
+ line, file_line = loc.match(/^(\d+)$/), loc.match(/^([^:]+):(\d+)$/)
50
+ return nil unless line || file_line
42
51
 
43
- l, n = line.to_i, File.foreach(file).count
44
- return errmsg("There are only #{n} lines in file #{path}") if l > n
52
+ f, l = line ? [@state.file, line[1]] : [file_line[1], file_line[2]]
45
53
 
46
- autoreload = Setting[:autoreload]
47
- possible_lines = LineCache.trace_line_numbers(file, autoreload)
48
- unless possible_lines.member?(l)
49
- return errmsg("Line #{l} is not a valid breakpoint in file #{path}")
50
- end
54
+ check_errors(f, l.to_i)
51
55
 
52
- b = Breakpoint.add(file, l, expr)
53
- puts "Created breakpoint #{b.id} at #{path}:#{l}"
56
+ Breakpoint.add(File.expand_path(f), l.to_i, @match[2])
57
+ end
54
58
 
55
- unless syntax_valid?(expr)
56
- errmsg("Incorrect expression \"#{expr}\"; breakpoint disabled.")
57
- b.enabled = false
58
- end
59
+ def method_breakpoint(location)
60
+ location.match(/([^.#]+)[.#](.+)/) do |match|
61
+ k, m = bb_warning_eval(match[1]), match[2]
59
62
 
60
- else
61
- kl = bb_warning_eval(file)
62
- return errmsg("Unknown class #{file}") unless kl && kl.is_a?(Module)
63
+ klass = k && k.is_a?(Module) ? k.name : match[1]
64
+ method = m.intern
63
65
 
64
- class_name, method = kl.name, line.intern
65
- b = Breakpoint.add(class_name, method, expr)
66
- puts "Created breakpoint #{b.id} at #{class_name}::#{method}"
66
+ Breakpoint.add(klass, method, @match[2])
67
67
  end
68
68
  end
69
69
 
70
- class << self
71
- def names
72
- %w(break)
73
- end
70
+ def check_errors(file, line)
71
+ path, deco_path = File.expand_path(file), normalize(file)
74
72
 
75
- def description
76
- %{b[reak] file:line [if expr]
77
- b[reak] class(.|#)method [if expr]
73
+ fail(pr('break.errors.source', file: deco_path)) unless File.exist?(path)
78
74
 
79
- Set breakpoint to some position, (optionally) if expr == true}
75
+ if line > n_lines(file)
76
+ fail(pr('break.errors.far_line', lines: n_lines(file), file: deco_path))
80
77
  end
78
+
79
+ return if Breakpoint.potential_line?(path, line)
80
+
81
+ fail(pr('break.errors.line', file: deco_path, line: line))
81
82
  end
82
83
  end
83
84
  end
@@ -0,0 +1,64 @@
1
+ require 'byebug/command'
2
+
3
+ module Byebug
4
+ #
5
+ # Implements exception catching.
6
+ #
7
+ # Enables the user to catch unhandled assertion when they happen.
8
+ #
9
+ class CatchCommand < Command
10
+ self.allow_in_control = false
11
+
12
+ def regexp
13
+ /^\s* cat(?:ch)? (?:\s+(\S+))? (?:\s+(off))? \s*$/x
14
+ end
15
+
16
+ def execute
17
+ ex = @match[1]
18
+ return info_catch unless ex
19
+
20
+ cmd = @match[2]
21
+ unless cmd
22
+ if 'off' == ex
23
+ Byebug.catchpoints.clear if
24
+ confirm(pr('catch.confirmations.delete_all'))
25
+
26
+ return
27
+ end
28
+
29
+ is_class = bb_eval("#{ex.is_a?(Class)}")
30
+ puts pr('catch.errors.not_class', class: ex) unless is_class
31
+
32
+ Byebug.add_catchpoint(ex)
33
+ return puts pr('catch.catching', exception: ex)
34
+ end
35
+
36
+ if cmd == 'off'
37
+ exists = Byebug.catchpoints.member?(ex)
38
+ return errmsg pr('catch.errors.not_found', exception: ex) unless exists
39
+
40
+ Byebug.catchpoints.delete(ex)
41
+ return errmsg pr('catch.errors.removed', exception: ex)
42
+ end
43
+
44
+ errmsg pr('catch.errors.off', off: cmd)
45
+ end
46
+
47
+ class << self
48
+ def names
49
+ %w(catch)
50
+ end
51
+
52
+ def description
53
+ prettify <<-EOD
54
+ cat[ch][ (off|<exception>[ off])]
55
+
56
+ "catch" lists catchpoints.
57
+ "catch off" deletes all catchpoints.
58
+ "catch <exception>" enables handling <exception>.
59
+ "catch <exception> off" disables handling <exception>.
60
+ EOD
61
+ end
62
+ end
63
+ end
64
+ end
@@ -1,3 +1,5 @@
1
+ require 'byebug/command'
2
+
1
3
  module Byebug
2
4
  #
3
5
  # Implements conditions on breakpoints.
@@ -12,20 +14,20 @@ module Byebug
12
14
  end
13
15
 
14
16
  def execute
15
- return puts(ConditionCommand.help) unless @match[1]
17
+ return puts(self.class.help) unless @match[1]
16
18
 
17
- breakpoints = Byebug.breakpoints.sort_by { |b| b.id }
18
- return errmsg('No breakpoints have been set') unless breakpoints.any?
19
+ breakpoints = Byebug.breakpoints.sort_by(&:id)
20
+ return errmsg(pr('condition.errors.no_breakpoints')) if breakpoints.empty?
19
21
 
20
22
  pos, err = get_int(@match[1], 'Condition', 1)
21
23
  return errmsg(err) if err
22
24
 
23
25
  breakpoint = breakpoints.find { |b| b.id == pos }
24
- return errmsg('Invalid breakpoint id. Use "info breakpoint" to find ' \
25
- 'out the correct id') unless breakpoint
26
+ return errmsg(pr('break.errors.no_breakpoint')) unless breakpoint
26
27
 
27
- return errmsg("Incorrect expression \"#{@match[2]}\", " \
28
- 'breakpoint not changed') unless syntax_valid?(@match[2])
28
+ unless syntax_valid?(@match[2])
29
+ return errmsg(pr('break.errors.not_changed', expr: @match[2]))
30
+ end
29
31
 
30
32
  breakpoint.expr = @match[2]
31
33
  end
@@ -36,12 +38,14 @@ module Byebug
36
38
  end
37
39
 
38
40
  def description
39
- %(cond[ition] <n>[ expr]
41
+ prettify <<-EOD
42
+ cond[ition] <n>[ expr]
40
43
 
41
44
  Specify breakpoint number <n> to break only if <expr> is true. <n> is
42
45
  an integer and <expr> is an expression to be evaluated whenever
43
46
  breakpoint <n> is reached. If no expression is specified, the
44
- condition is removed.)
47
+ condition is removed.
48
+ EOD
45
49
  end
46
50
  end
47
51
  end
@@ -1,3 +1,5 @@
1
+ require 'byebug/command'
2
+
1
3
  module Byebug
2
4
  #
3
5
  # Implements the continue command.
@@ -16,8 +18,8 @@ module Byebug
16
18
  return errmsg(err) unless num
17
19
 
18
20
  filename = File.expand_path(@state.file)
19
- unless LineCache.trace_line_numbers(filename).member?(num)
20
- return errmsg("Line #{num} is not a valid stopping point in file")
21
+ unless Breakpoint.potential_line?(filename, num)
22
+ return errmsg(pr('continue.errors.unstopped_line', line: num))
21
23
  end
22
24
 
23
25
  Breakpoint.add(filename, num)
@@ -32,9 +34,11 @@ module Byebug
32
34
  end
33
35
 
34
36
  def description
35
- %(c[ont[inue]][ <n>]
37
+ prettify <<-EOD
38
+ c[ont[inue]][ <n>]
36
39
 
37
- Run until program ends, hits a breakpoint or reaches line <n>.)
40
+ Run until program ends, hits a breakpoint or reaches line <n>.
41
+ EOD
38
42
  end
39
43
  end
40
44
  end
@@ -1,3 +1,5 @@
1
+ require 'byebug/command'
2
+
1
3
  module Byebug
2
4
  #
3
5
  # Implements breakpoint deletion.
@@ -12,7 +14,9 @@ module Byebug
12
14
 
13
15
  def execute
14
16
  unless @match[1]
15
- Byebug.breakpoints.clear if confirm('Delete all breakpoints? (y/n) ')
17
+ if confirm(pr('break.confirmations.delete_all'))
18
+ Byebug.breakpoints.clear
19
+ end
16
20
 
17
21
  return nil
18
22
  end
@@ -23,7 +27,7 @@ module Byebug
23
27
  return errmsg(err) unless pos
24
28
 
25
29
  unless Breakpoint.remove(pos)
26
- return errmsg("No breakpoint number #{pos}")
30
+ return errmsg(pr('break.errors.no_breakpoint_delete', pos: pos))
27
31
  end
28
32
  end
29
33
  end
@@ -34,10 +38,12 @@ module Byebug
34
38
  end
35
39
 
36
40
  def description
37
- %(del[ete][ nnn...]
41
+ prettify <<-EOD
42
+ del[ete][ nnn...]
38
43
 
39
44
  Without and argument, deletes all breakpoints. With integer
40
- arguments, it deletes specific breakpoints.)
45
+ arguments, it deletes specific breakpoints.
46
+ EOD
41
47
  end
42
48
  end
43
49
  end
@@ -1,25 +1,6 @@
1
- module Byebug
2
- #
3
- # Custom display utilities.
4
- #
5
- module DisplayFunctions
6
- def display_expression(exp)
7
- "#{exp} = #{bb_warning_eval(exp).inspect}"
8
- end
9
-
10
- def active_display_expressions?
11
- @state.display.select { |d| d[0] }.size > 0
12
- end
13
-
14
- def print_display_expressions
15
- n = 1
16
- @state.display.each do |d|
17
- puts "#{n}: #{display_expression(d[1])}" if d[0]
18
- n += 1
19
- end
20
- end
21
- end
1
+ require 'byebug/command'
22
2
 
3
+ module Byebug
23
4
  #
24
5
  # Implements the functionality of adding custom expressions to be displayed
25
6
  # every time the debugger stops.
@@ -34,7 +15,7 @@ module Byebug
34
15
  def execute
35
16
  exp = @match[1]
36
17
  @state.display.push [true, exp]
37
- puts "#{@state.display.size}: #{display_expression(exp)}"
18
+ display_expression(exp)
38
19
  end
39
20
 
40
21
  class << self
@@ -43,11 +24,22 @@ module Byebug
43
24
  end
44
25
 
45
26
  def description
46
- %(disp[lay] <expression>
27
+ prettify <<-EOD
28
+ disp[lay] <expression>
47
29
 
48
- Add <expression> into display expression list.)
30
+ Add <expression> into display expression list.
31
+ EOD
49
32
  end
50
33
  end
34
+
35
+ private
36
+
37
+ def display_expression(exp)
38
+ print pr('display.result',
39
+ n: @state.display.size,
40
+ exp: exp,
41
+ result: bb_warning_eval(exp).inspect)
42
+ end
51
43
  end
52
44
 
53
45
  #
@@ -74,8 +66,24 @@ module Byebug
74
66
  end
75
67
 
76
68
  def description
77
- %(disp[lay] Display expression list.)
69
+ prettify <<-EOD
70
+ disp[lay] Display expression list.
71
+ EOD
72
+ end
73
+ end
74
+
75
+ private
76
+
77
+ def print_display_expressions
78
+ result = prc('display.result', @state.display) do |item, index|
79
+ is_active, expression = item
80
+ if is_active
81
+ { n: index + 1,
82
+ exp: expression,
83
+ result: bb_warning_eval(expression).inspect }
84
+ end
78
85
  end
86
+ print result
79
87
  end
80
88
  end
81
89
  end
@@ -1,3 +1,5 @@
1
+ require 'byebug/command'
2
+
1
3
  module Byebug
2
4
  #
3
5
  # Edit a file from byebug's prompt.
@@ -11,27 +13,28 @@ module Byebug
11
13
 
12
14
  def execute
13
15
  if !@match[1]
14
- unless @state.file
15
- return errmsg "We are not in a state that has an associated file.\n"
16
- end
16
+ return errmsg(pr('edit.errors.state')) unless @state.file
17
17
  file = @state.file
18
18
  line = @state.line if @state.line
19
19
  elsif (@pos_match = /([^:]+)[:]([0-9]+)/.match(@match[1]))
20
20
  file, line = @pos_match.captures
21
- elsif File.exist?(@match[1])
22
- file = @match[1]
23
21
  else
24
- return errmsg "Invalid file[:line] number specification: #{@match[1]}\n"
22
+ file = @match[1]
25
23
  end
26
24
 
27
25
  editor = ENV['EDITOR'] || 'vim'
26
+ file = File.expand_path(file)
28
27
 
29
- if File.readable?(file)
30
- system("#{editor} +#{line} #{file}") if line
31
- system("#{editor} #{file}") unless line
32
- else
33
- errmsg "File \"#{file}\" is not readable.\n"
28
+ unless File.exist?(file)
29
+ return errmsg(pr('edit.errors.not_exist', file: file))
34
30
  end
31
+ unless File.readable?(file)
32
+ return errmsg(pr('edit.errors.not_readable', file: file))
33
+ end
34
+
35
+ cmd = line ? "#{editor} +#{line} #{file}" : "#{editor} #{file}"
36
+
37
+ system(cmd)
35
38
  end
36
39
 
37
40
  class << self
@@ -40,11 +43,13 @@ module Byebug
40
43
  end
41
44
 
42
45
  def description
43
- %(edit[ file:lineno] Edit specified files.
46
+ prettify <<-EOD
47
+ edit[ file:lineno] Edit specified files.
44
48
 
45
49
  With no argument, edits file containing most recent line listed.
46
50
  Editing targets can also be specified to start editing at a specific
47
- line in a specific file.)
51
+ line in a specific file.
52
+ EOD
48
53
  end
49
54
  end
50
55
  end