byebug 4.0.5 → 5.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 (96) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +72 -34
  3. data/CONTRIBUTING.md +26 -31
  4. data/README.md +3 -3
  5. data/lib/byebug/breakpoint.rb +2 -1
  6. data/lib/byebug/command.rb +27 -49
  7. data/lib/byebug/commands/break.rb +21 -15
  8. data/lib/byebug/commands/catch.rb +9 -15
  9. data/lib/byebug/commands/condition.rb +12 -15
  10. data/lib/byebug/commands/continue.rb +8 -11
  11. data/lib/byebug/commands/delete.rb +9 -12
  12. data/lib/byebug/commands/disable.rb +32 -0
  13. data/lib/byebug/commands/disable/breakpoints.rb +38 -0
  14. data/lib/byebug/commands/disable/display.rb +39 -0
  15. data/lib/byebug/commands/display.rb +18 -51
  16. data/lib/byebug/commands/down.rb +39 -0
  17. data/lib/byebug/commands/edit.rb +8 -14
  18. data/lib/byebug/commands/enable.rb +25 -0
  19. data/lib/byebug/commands/enable/breakpoints.rb +38 -0
  20. data/lib/byebug/commands/enable/display.rb +39 -0
  21. data/lib/byebug/commands/eval.rb +10 -192
  22. data/lib/byebug/commands/finish.rb +11 -12
  23. data/lib/byebug/commands/frame.rb +17 -182
  24. data/lib/byebug/commands/help.rb +18 -18
  25. data/lib/byebug/commands/history.rb +9 -10
  26. data/lib/byebug/commands/info.rb +17 -190
  27. data/lib/byebug/commands/info/args.rb +39 -0
  28. data/lib/byebug/commands/info/breakpoints.rb +59 -0
  29. data/lib/byebug/commands/info/catch.rb +39 -0
  30. data/lib/byebug/commands/info/display.rb +42 -0
  31. data/lib/byebug/commands/info/file.rb +81 -0
  32. data/lib/byebug/commands/info/line.rb +31 -0
  33. data/lib/byebug/commands/info/program.rb +51 -0
  34. data/lib/byebug/commands/interrupt.rb +5 -9
  35. data/lib/byebug/commands/irb.rb +5 -9
  36. data/lib/byebug/commands/kill.rb +6 -12
  37. data/lib/byebug/commands/list.rb +47 -19
  38. data/lib/byebug/commands/method.rb +8 -14
  39. data/lib/byebug/commands/next.rb +36 -0
  40. data/lib/byebug/commands/pp.rb +41 -0
  41. data/lib/byebug/commands/pry.rb +5 -9
  42. data/lib/byebug/commands/ps.rb +44 -0
  43. data/lib/byebug/commands/putl.rb +43 -0
  44. data/lib/byebug/commands/quit.rb +8 -12
  45. data/lib/byebug/commands/restart.rb +6 -12
  46. data/lib/byebug/commands/save.rb +30 -39
  47. data/lib/byebug/commands/set.rb +19 -21
  48. data/lib/byebug/commands/show.rb +10 -16
  49. data/lib/byebug/commands/source.rb +6 -12
  50. data/lib/byebug/commands/step.rb +36 -0
  51. data/lib/byebug/commands/thread.rb +13 -130
  52. data/lib/byebug/commands/thread/current.rb +35 -0
  53. data/lib/byebug/commands/thread/list.rb +41 -0
  54. data/lib/byebug/commands/thread/resume.rb +45 -0
  55. data/lib/byebug/commands/thread/stop.rb +41 -0
  56. data/lib/byebug/commands/thread/switch.rb +43 -0
  57. data/lib/byebug/commands/tracevar.rb +8 -14
  58. data/lib/byebug/commands/undisplay.rb +12 -15
  59. data/lib/byebug/commands/untracevar.rb +5 -11
  60. data/lib/byebug/commands/up.rb +39 -0
  61. data/lib/byebug/commands/var.rb +15 -94
  62. data/lib/byebug/commands/var/all.rb +37 -0
  63. data/lib/byebug/commands/var/const.rb +38 -0
  64. data/lib/byebug/commands/var/global.rb +33 -0
  65. data/lib/byebug/commands/var/instance.rb +35 -0
  66. data/lib/byebug/commands/var/local.rb +35 -0
  67. data/lib/byebug/commands/where.rb +47 -0
  68. data/lib/byebug/core.rb +10 -0
  69. data/lib/byebug/helpers/eval.rb +47 -0
  70. data/lib/byebug/helpers/file.rb +46 -0
  71. data/lib/byebug/helpers/frame.rb +76 -0
  72. data/lib/byebug/helpers/parse.rb +74 -0
  73. data/lib/byebug/helpers/string.rb +24 -0
  74. data/lib/byebug/helpers/thread.rb +53 -0
  75. data/lib/byebug/helpers/toggle.rb +56 -0
  76. data/lib/byebug/helpers/var.rb +45 -0
  77. data/lib/byebug/history.rb +2 -4
  78. data/lib/byebug/interface.rb +5 -3
  79. data/lib/byebug/interfaces/local_interface.rb +3 -1
  80. data/lib/byebug/interfaces/remote_interface.rb +3 -1
  81. data/lib/byebug/interfaces/test_interface.rb +6 -2
  82. data/lib/byebug/printers/plain.rb +1 -1
  83. data/lib/byebug/processors/command_processor.rb +9 -11
  84. data/lib/byebug/processors/control_command_processor.rb +1 -1
  85. data/lib/byebug/remote.rb +3 -0
  86. data/lib/byebug/runner.rb +5 -3
  87. data/lib/byebug/setting.rb +2 -18
  88. data/lib/byebug/settings/savefile.rb +21 -0
  89. data/lib/byebug/states/regular_state.rb +15 -6
  90. data/lib/byebug/subcommand_list.rb +33 -0
  91. data/lib/byebug/subcommands.rb +53 -0
  92. data/lib/byebug/version.rb +1 -1
  93. metadata +45 -6
  94. data/lib/byebug/commands/enable_disable.rb +0 -132
  95. data/lib/byebug/commands/stepping.rb +0 -75
  96. data/lib/byebug/helper.rb +0 -131
@@ -0,0 +1,46 @@
1
+ module Byebug
2
+ module Helpers
3
+ #
4
+ # Utilities for interaction with files
5
+ #
6
+ module FileHelper
7
+ #
8
+ # Reads lines of source file +filename+ into an array
9
+ #
10
+ def get_lines(filename)
11
+ File.foreach(filename).reduce([]) { |a, e| a << e.chomp }
12
+ end
13
+
14
+ #
15
+ # Reads line number +lineno+ from file named +filename+
16
+ #
17
+ def get_line(filename, lineno)
18
+ File.open(filename) do |f|
19
+ f.gets until f.lineno == lineno - 1
20
+ f.gets
21
+ end
22
+ end
23
+
24
+ #
25
+ # Returns the number of lines in file +filename+ in a portable,
26
+ # one-line-at-a-time way.
27
+ #
28
+ def n_lines(filename)
29
+ File.foreach(filename).reduce(0) { |a, _e| a + 1 }
30
+ end
31
+
32
+ #
33
+ # Regularize file name.
34
+ #
35
+ def normalize(filename)
36
+ return filename if ['(irb)', '-e'].include?(filename)
37
+
38
+ return File.basename(filename) if Setting[:basename]
39
+
40
+ path = File.expand_path(filename)
41
+
42
+ File.exist?(path) ? File.realpath(path) : filename
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,76 @@
1
+ module Byebug
2
+ module Helpers
3
+ #
4
+ # Utilities to assist frame navigation
5
+ #
6
+ module FrameHelper
7
+ def switch_to_frame(frame_no)
8
+ frame_no >= 0 ? frame_no : @state.context.stack_size + frame_no
9
+ end
10
+
11
+ def navigate_to_frame(jump_no)
12
+ return if jump_no == 0
13
+
14
+ current_jumps = 0
15
+ current_pos = @state.frame
16
+
17
+ loop do
18
+ current_pos += direction(jump_no)
19
+ break if current_pos < 0 || current_pos >= @state.context.stack_size
20
+
21
+ next if @state.c_frame?(current_pos)
22
+
23
+ current_jumps += 1
24
+ break if current_jumps == jump_no.abs
25
+ end
26
+
27
+ current_pos
28
+ end
29
+
30
+ def adjust_frame(frame, absolute)
31
+ if absolute
32
+ abs_frame = switch_to_frame(frame)
33
+ if @state.c_frame?(abs_frame)
34
+ return errmsg(pr('frame.errors.c_frame'))
35
+ end
36
+ else
37
+ abs_frame = navigate_to_frame(frame)
38
+ end
39
+
40
+ if abs_frame >= @state.context.stack_size
41
+ return errmsg(pr('frame.errors.too_low'))
42
+ elsif abs_frame < 0
43
+ return errmsg(pr('frame.errors.too_high'))
44
+ end
45
+
46
+ @state.frame = abs_frame
47
+ @state.file = @state.context.frame_file(@state.frame)
48
+ @state.line = @state.context.frame_line(@state.frame)
49
+ @state.prev_line = nil
50
+ end
51
+
52
+ def get_pr_arguments(frame_no)
53
+ file = @state.frame_file(frame_no)
54
+ full_path = File.expand_path(file)
55
+ line = @state.frame_line(frame_no)
56
+ call = @state.frame_call(frame_no)
57
+ mark = @state.frame_mark(frame_no)
58
+ pos = @state.frame_pos(frame_no)
59
+
60
+ { mark: mark, pos: pos, call: call, file: file, line: line,
61
+ full_path: full_path }
62
+ end
63
+
64
+ private
65
+
66
+ #
67
+ # @param [Integer] A positive or negative integer
68
+ #
69
+ # @return [Integer] +1 if step is positive / -1 if negative
70
+ #
71
+ def direction(step)
72
+ step / step.abs
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,74 @@
1
+ module Byebug
2
+ module Helpers
3
+ #
4
+ # Utilities to assist command parsing
5
+ #
6
+ module ParseHelper
7
+ #
8
+ # Parses +str+ of command +cmd+ as an integer between +min+ and +max+.
9
+ #
10
+ # If either +min+ or +max+ is nil, that value has no bound.
11
+ #
12
+ # TODO: Remove the `cmd` parameter. It has nothing to do with the methods
13
+ # purpose.
14
+ #
15
+ def get_int(str, cmd, min = nil, max = nil)
16
+ if str !~ /\A-?[0-9]+\z/
17
+ err = pr('parse.errors.int.not_number', cmd: cmd, str: str)
18
+ return nil, errmsg(err)
19
+ end
20
+
21
+ int = str.to_i
22
+ if min && int < min
23
+ err = pr('parse.errors.int.too_low', cmd: cmd, str: str, min: min)
24
+ return min, errmsg(err)
25
+ elsif max && int > max
26
+ err = pr('parse.errors.int.too_high', cmd: cmd, str: str, max: max)
27
+ return max, errmsg(err)
28
+ end
29
+
30
+ int
31
+ end
32
+
33
+ #
34
+ # @return true if code is syntactically correct for Ruby, false otherwise
35
+ #
36
+ def syntax_valid?(code)
37
+ return true unless code
38
+
39
+ without_stderr do
40
+ begin
41
+ RubyVM::InstructionSequence.compile(code)
42
+ true
43
+ rescue SyntaxError
44
+ false
45
+ end
46
+ end
47
+ end
48
+
49
+ #
50
+ # Temporarily disable output to $stderr
51
+ #
52
+ def without_stderr
53
+ stderr = $stderr
54
+ $stderr.reopen(IO::NULL)
55
+
56
+ yield
57
+ ensure
58
+ $stderr.reopen(stderr)
59
+ end
60
+
61
+ #
62
+ # @return +str+ as an integer or 1 if +str+ is empty.
63
+ #
64
+ def parse_steps(str, cmd)
65
+ return 1 unless str
66
+
67
+ steps, err = get_int(str, cmd, 1)
68
+ return nil, err unless steps
69
+
70
+ steps
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,24 @@
1
+ module Byebug
2
+ module Helpers
3
+ #
4
+ # Utilities for interaction with strings
5
+ #
6
+ module StringHelper
7
+ #
8
+ # Converts +str+ from an_underscored-or-dasherized_string to
9
+ # ACamelizedString.
10
+ #
11
+ def camelize(str)
12
+ str.dup.split(/[_-]/).map(&:capitalize).join('')
13
+ end
14
+
15
+ #
16
+ # Improves indentation and spacing in +str+ for readability in Byebug's
17
+ # command prompt.
18
+ #
19
+ def prettify(str)
20
+ "\n" + str.gsub(/^ {6}/, '') + "\n"
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,53 @@
1
+ module Byebug
2
+ module Helpers
3
+ #
4
+ # Utilities for thread subcommands
5
+ #
6
+ module ThreadHelper
7
+ def display_context(context)
8
+ puts pr('thread.context', thread_arguments(context))
9
+ end
10
+
11
+ def thread_arguments(context)
12
+ status_flag = if context.suspended?
13
+ '$'
14
+ else
15
+ context.thread == Thread.current ? '+' : ' '
16
+ end
17
+ debug_flag = context.ignored? ? '!' : ' '
18
+
19
+ if context == Byebug.current_context
20
+ file_line = "#{@state.file}:#{@state.line}"
21
+ else
22
+ backtrace = context.thread.backtrace_locations
23
+ if backtrace && backtrace[0]
24
+ file_line = "#{backtrace[0].path}:#{backtrace[0].lineno}"
25
+ end
26
+ end
27
+
28
+ {
29
+ status_flag: status_flag,
30
+ debug_flag: debug_flag,
31
+ id: context.thnum,
32
+ thread: context.thread.inspect,
33
+ file_line: file_line || '',
34
+ pid: Process.pid,
35
+ status: context.thread.status,
36
+ current: (context.thread == Thread.current)
37
+ }
38
+ end
39
+
40
+ def context_from_thread(thnum)
41
+ ctx = Byebug.contexts.find { |c| c.thnum.to_s == thnum }
42
+
43
+ err = case
44
+ when ctx.nil? then pr('thread.errors.no_thread')
45
+ when ctx == @state.context then pr('thread.errors.current_thread')
46
+ when ctx.ignored? then pr('thread.errors.ignored', arg: thnum)
47
+ end
48
+
49
+ [ctx, err]
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,56 @@
1
+ require 'byebug/helpers/parse'
2
+
3
+ module Byebug
4
+ module Helpers
5
+ #
6
+ # Utilities to assist breakpoint/display enabling/disabling.
7
+ #
8
+ module ToggleHelper
9
+ include ParseHelper
10
+
11
+ def enable_disable_breakpoints(is_enable, args)
12
+ return errmsg(pr('toggle.errors.no_breakpoints')) if Breakpoint.none?
13
+
14
+ all_breakpoints = Byebug.breakpoints.sort_by(&:id)
15
+ if args.nil?
16
+ selected_breakpoints = all_breakpoints
17
+ else
18
+ selected_ids = []
19
+ args.split(/ +/).each do |pos|
20
+ last_id = all_breakpoints.last.id
21
+ pos, err = get_int(pos, "#{is_enable} breakpoints", 1, last_id)
22
+ return errmsg(err) unless pos
23
+
24
+ selected_ids << pos
25
+ end
26
+ selected_breakpoints = all_breakpoints.select do |b|
27
+ selected_ids.include?(b.id)
28
+ end
29
+ end
30
+
31
+ selected_breakpoints.each do |b|
32
+ enabled = ('enable' == is_enable)
33
+ if enabled && !syntax_valid?(b.expr)
34
+ return errmsg(pr('toggle.errors.expression', expr: b.expr))
35
+ end
36
+
37
+ b.enabled = enabled
38
+ end
39
+ end
40
+
41
+ def enable_disable_display(is_enable, args)
42
+ display = @state.display
43
+ return errmsg(pr('toggle.errors.no_display')) if 0 == display.size
44
+
45
+ selected_displays = args.nil? ? [1..display.size + 1] : args.split(/ +/)
46
+
47
+ selected_displays.each do |pos|
48
+ pos, err = get_int(pos, "#{is_enable} display", 1, display.size)
49
+ return errmsg(err) unless err.nil?
50
+
51
+ display[pos - 1][0] = ('enable' == is_enable)
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,45 @@
1
+ module Byebug
2
+ module Helpers
3
+ #
4
+ # Utilities for variable subcommands
5
+ #
6
+ module VarHelper
7
+ def var_list(ary, b = get_binding)
8
+ vars = ary.sort.map do |v|
9
+ s = begin
10
+ b.eval(v.to_s).inspect
11
+ rescue
12
+ begin
13
+ b.eval(v.to_s).to_s
14
+ rescue
15
+ '*Error in evaluation*'
16
+ end
17
+ end
18
+ [v, s]
19
+ end
20
+ puts prv(vars, 'instance')
21
+ end
22
+
23
+ def var_global
24
+ globals = global_variables.reject do |v|
25
+ [:$IGNORECASE, :$=, :$KCODE, :$-K, :$binding].include?(v)
26
+ end
27
+
28
+ var_list(globals)
29
+ end
30
+
31
+ def var_instance(str)
32
+ obj = bb_warning_eval(str || 'self')
33
+
34
+ var_list(obj.instance_variables, obj.instance_eval { binding })
35
+ end
36
+
37
+ def var_local
38
+ locals = @state.context.frame_locals
39
+ cur_self = @state.context.frame_self(@state.frame)
40
+ locals[:self] = cur_self unless cur_self.to_s == 'main'
41
+ puts prv(locals.keys.sort.map { |k| [k, locals[k]] }, 'instance')
42
+ end
43
+ end
44
+ end
45
+ end
@@ -27,7 +27,7 @@ module Byebug
27
27
  def restore
28
28
  return unless File.exist?(Setting[:histfile])
29
29
 
30
- File.readlines(Setting[:histfile]).reverse.each { |l| push(l.chomp) }
30
+ File.readlines(Setting[:histfile]).reverse_each { |l| push(l.chomp) }
31
31
  end
32
32
 
33
33
  #
@@ -85,9 +85,7 @@ module Byebug
85
85
  # Array of ids of the last n commands.
86
86
  #
87
87
  def last_ids(n)
88
- from, to = 1 + self.size - n, self.size
89
-
90
- (from..to).to_a
88
+ (1 + size - n..size).to_a
91
89
  end
92
90
 
93
91
  #
@@ -1,5 +1,5 @@
1
1
  require 'byebug/history'
2
- require 'byebug/helper'
2
+ require 'byebug/helpers/file'
3
3
 
4
4
  #
5
5
  # Namespace for all of byebug's code
@@ -11,11 +11,14 @@ module Byebug
11
11
  # Contains common functionality to all implemented interfaces.
12
12
  #
13
13
  class Interface
14
+ include Helpers::FileHelper
15
+
14
16
  attr_accessor :command_queue, :history
15
17
  attr_reader :input, :output, :error
16
18
 
17
19
  def initialize
18
- @command_queue, @history = [], History.new
20
+ @command_queue = []
21
+ @history = History.new
19
22
  end
20
23
 
21
24
  #
@@ -31,7 +34,6 @@ module Byebug
31
34
  command_queue.shift
32
35
  end
33
36
 
34
- include FileFunctions
35
37
  #
36
38
  # Pushes lines in +filename+ to the command queue.
37
39
  #
@@ -5,7 +5,9 @@ module Byebug
5
5
  class LocalInterface < Interface
6
6
  def initialize
7
7
  super()
8
- @input, @output, @error = STDIN, STDOUT, STDERR
8
+ @input = STDIN
9
+ @output = STDOUT
10
+ @error = STDERR
9
11
  end
10
12
 
11
13
  #
@@ -7,7 +7,9 @@ module Byebug
7
7
  class RemoteInterface < Interface
8
8
  def initialize(socket)
9
9
  super()
10
- @input, @output, @error = socket, socket, socket
10
+ @input = socket
11
+ @output = socket
12
+ @error = socket
11
13
  end
12
14
 
13
15
  def read_command(prompt)