byebug 5.0.0 → 6.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 (110) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +26 -1
  3. data/CONTRIBUTING.md +35 -13
  4. data/GUIDE.md +256 -198
  5. data/README.md +5 -11
  6. data/ext/byebug/byebug.c +5 -43
  7. data/ext/byebug/byebug.h +6 -1
  8. data/ext/byebug/context.c +4 -5
  9. data/lib/byebug/command.rb +64 -64
  10. data/lib/byebug/command_list.rb +32 -0
  11. data/lib/byebug/commands.rb +37 -0
  12. data/lib/byebug/commands/break.rb +45 -37
  13. data/lib/byebug/commands/catch.rb +52 -28
  14. data/lib/byebug/commands/condition.rb +19 -13
  15. data/lib/byebug/commands/continue.rb +15 -11
  16. data/lib/byebug/commands/delete.rb +18 -12
  17. data/lib/byebug/commands/disable.rb +9 -10
  18. data/lib/byebug/commands/disable/breakpoints.rb +13 -11
  19. data/lib/byebug/commands/disable/display.rb +13 -11
  20. data/lib/byebug/commands/display.rb +32 -24
  21. data/lib/byebug/commands/down.rb +18 -14
  22. data/lib/byebug/commands/edit.rb +42 -26
  23. data/lib/byebug/commands/enable.rb +9 -3
  24. data/lib/byebug/commands/enable/breakpoints.rb +13 -11
  25. data/lib/byebug/commands/enable/display.rb +13 -11
  26. data/lib/byebug/commands/finish.rb +23 -14
  27. data/lib/byebug/commands/frame.rb +21 -18
  28. data/lib/byebug/commands/help.rb +39 -16
  29. data/lib/byebug/commands/history.rb +16 -10
  30. data/lib/byebug/commands/info.rb +8 -5
  31. data/lib/byebug/commands/info/breakpoints.rb +16 -14
  32. data/lib/byebug/commands/info/display.rb +18 -18
  33. data/lib/byebug/commands/info/file.rb +22 -22
  34. data/lib/byebug/commands/info/line.rb +13 -11
  35. data/lib/byebug/commands/info/program.rb +13 -17
  36. data/lib/byebug/commands/interrupt.rb +13 -11
  37. data/lib/byebug/commands/irb.rb +16 -10
  38. data/lib/byebug/commands/kill.rb +19 -13
  39. data/lib/byebug/commands/list.rb +35 -24
  40. data/lib/byebug/commands/method.rb +25 -15
  41. data/lib/byebug/commands/next.rb +15 -13
  42. data/lib/byebug/commands/pry.rb +18 -11
  43. data/lib/byebug/commands/ps.rb +21 -23
  44. data/lib/byebug/commands/quit.rb +17 -11
  45. data/lib/byebug/commands/restart.rb +28 -24
  46. data/lib/byebug/commands/save.rb +23 -15
  47. data/lib/byebug/commands/set.rb +26 -19
  48. data/lib/byebug/commands/show.rb +20 -14
  49. data/lib/byebug/commands/source.rb +15 -14
  50. data/lib/byebug/commands/step.rb +15 -13
  51. data/lib/byebug/commands/thread.rb +8 -4
  52. data/lib/byebug/commands/thread/current.rb +11 -11
  53. data/lib/byebug/commands/thread/list.rb +14 -14
  54. data/lib/byebug/commands/thread/resume.rb +14 -14
  55. data/lib/byebug/commands/thread/stop.rb +14 -14
  56. data/lib/byebug/commands/thread/switch.rb +15 -14
  57. data/lib/byebug/commands/tracevar.rb +20 -16
  58. data/lib/byebug/commands/undisplay.rb +22 -18
  59. data/lib/byebug/commands/untracevar.rb +13 -11
  60. data/lib/byebug/commands/up.rb +18 -14
  61. data/lib/byebug/commands/var.rb +10 -3
  62. data/lib/byebug/commands/var/all.rb +15 -13
  63. data/lib/byebug/commands/var/args.rb +37 -0
  64. data/lib/byebug/commands/var/const.rb +25 -14
  65. data/lib/byebug/commands/var/global.rb +13 -11
  66. data/lib/byebug/commands/var/instance.rb +13 -11
  67. data/lib/byebug/commands/var/local.rb +13 -11
  68. data/lib/byebug/commands/where.rb +15 -11
  69. data/lib/byebug/context.rb +71 -73
  70. data/lib/byebug/core.rb +45 -26
  71. data/lib/byebug/errors.rb +27 -0
  72. data/lib/byebug/frame.rb +181 -0
  73. data/lib/byebug/helpers/eval.rb +67 -26
  74. data/lib/byebug/helpers/file.rb +18 -3
  75. data/lib/byebug/helpers/frame.rb +36 -39
  76. data/lib/byebug/helpers/parse.rb +15 -13
  77. data/lib/byebug/helpers/path.rb +21 -0
  78. data/lib/byebug/helpers/reflection.rb +17 -0
  79. data/lib/byebug/helpers/thread.rb +20 -14
  80. data/lib/byebug/helpers/toggle.rb +10 -5
  81. data/lib/byebug/helpers/var.rb +36 -15
  82. data/lib/byebug/interface.rb +27 -9
  83. data/lib/byebug/option_setter.rb +93 -0
  84. data/lib/byebug/printers/base.rb +3 -0
  85. data/lib/byebug/printers/plain.rb +4 -14
  86. data/lib/byebug/printers/texts/base.yml +2 -7
  87. data/lib/byebug/processors/command_processor.rb +101 -102
  88. data/lib/byebug/processors/control_processor.rb +20 -0
  89. data/lib/byebug/processors/post_mortem_processor.rb +16 -0
  90. data/lib/byebug/processors/script_processor.rb +49 -0
  91. data/lib/byebug/remote.rb +13 -7
  92. data/lib/byebug/runner.rb +39 -65
  93. data/lib/byebug/setting.rb +4 -1
  94. data/lib/byebug/settings/post_mortem.rb +0 -16
  95. data/lib/byebug/settings/savefile.rb +1 -4
  96. data/lib/byebug/subcommands.rb +27 -29
  97. data/lib/byebug/version.rb +4 -1
  98. metadata +14 -29
  99. data/lib/byebug/commands/eval.rb +0 -43
  100. data/lib/byebug/commands/info/args.rb +0 -39
  101. data/lib/byebug/commands/info/catch.rb +0 -39
  102. data/lib/byebug/commands/pp.rb +0 -41
  103. data/lib/byebug/commands/putl.rb +0 -43
  104. data/lib/byebug/processor.rb +0 -43
  105. data/lib/byebug/processors/control_command_processor.rb +0 -48
  106. data/lib/byebug/settings/verbose.rb +0 -20
  107. data/lib/byebug/state.rb +0 -12
  108. data/lib/byebug/states/control_state.rb +0 -26
  109. data/lib/byebug/states/regular_state.rb +0 -187
  110. data/lib/byebug/subcommand_list.rb +0 -33
@@ -19,6 +19,11 @@ module Byebug
19
19
  def initialize
20
20
  @command_queue = []
21
21
  @history = History.new
22
+ @last_line = ''
23
+ end
24
+
25
+ def last_if_empty(input)
26
+ @last_line = input.empty? ? @last_line : input
22
27
  end
23
28
 
24
29
  #
@@ -27,11 +32,7 @@ module Byebug
27
32
  def read_command(prompt)
28
33
  return command_queue.shift unless command_queue.empty?
29
34
 
30
- cmds = read_input(prompt)
31
- return unless cmds
32
-
33
- command_queue.concat(cmds)
34
- command_queue.shift
35
+ read_input(prompt)
35
36
  end
36
37
 
37
38
  #
@@ -42,15 +43,32 @@ module Byebug
42
43
  end
43
44
 
44
45
  #
45
- # Reads a new line from the interface's input stream.
46
+ # Reads a new line from the interface's input stream, parses it into
47
+ # commands and saves it to history.
48
+ #
49
+ # @return [String] Representing something to be run by the debugger.
46
50
  #
47
51
  def read_input(prompt, save_hist = true)
48
- line = readline(prompt)
52
+ line = prepare_input(prompt)
49
53
  return unless line
50
54
 
51
55
  history.push(line) if save_hist
52
56
 
53
- split_commands(line)
57
+ command_queue.concat(split_commands(line))
58
+ command_queue.shift
59
+ end
60
+
61
+ #
62
+ # Reads a new line from the interface's input stream.
63
+ #
64
+ # @return [String] New string read or the previous string if the string
65
+ # read now was empty.
66
+ #
67
+ def prepare_input(prompt)
68
+ line = readline(prompt)
69
+ return unless line
70
+
71
+ last_if_empty(line)
54
72
  end
55
73
 
56
74
  #
@@ -106,7 +124,7 @@ module Byebug
106
124
 
107
125
  cmd_line.split(/;/).each_with_object([]) do |v, m|
108
126
  if m.empty? || m.last[-1] != '\\'
109
- m << v
127
+ m << v.strip
110
128
  next
111
129
  end
112
130
 
@@ -0,0 +1,93 @@
1
+ module Byebug
2
+ #
3
+ # Handles byebug's command line options
4
+ #
5
+ class OptionSetter
6
+ def initialize(runner, opts)
7
+ @runner = runner
8
+ @opts = opts
9
+ end
10
+
11
+ def setup
12
+ debug
13
+ include_flag
14
+ post_mortem
15
+ quit
16
+ rc
17
+ stop
18
+ require_flag
19
+ remote
20
+ trace
21
+ version
22
+ help
23
+ end
24
+
25
+ private
26
+
27
+ def debug
28
+ @opts.on '-d', '--debug', 'Set $DEBUG=true' do
29
+ $DEBUG = true
30
+ end
31
+ end
32
+
33
+ def include_flag
34
+ @opts.on('-I', '--include list', 'Add to paths to $LOAD_PATH') do |list|
35
+ $LOAD_PATH.push(list.split(':')).flatten!
36
+ end
37
+ end
38
+
39
+ def post_mortem
40
+ @opts.on '-m', '--[no-]post-mortem', 'Use post-mortem mode' do |v|
41
+ Setting[:post_mortem] = v
42
+ end
43
+ end
44
+
45
+ def quit
46
+ @opts.on '-q', '--[no-]quit', 'Quit when script finishes' do |v|
47
+ @runner.quit = v
48
+ end
49
+ end
50
+
51
+ def rc
52
+ @opts.on '-x', '--[no-]rc', 'Run byebug initialization file' do |v|
53
+ Byebug.run_init_script if v
54
+ end
55
+ end
56
+
57
+ def stop
58
+ @opts.on '-s', '--[no-]stop', 'Stop when script is loaded' do |v|
59
+ @runner.stop = v
60
+ end
61
+ end
62
+
63
+ def require_flag
64
+ @opts.on '-r', '--require file', 'Require library before script' do |lib|
65
+ require lib
66
+ end
67
+ end
68
+
69
+ def remote
70
+ @opts.on '-R', '--remote [host:]port', 'Remote debug [host:]port' do |p|
71
+ @runner.remote = p
72
+ end
73
+ end
74
+
75
+ def trace
76
+ @opts.on '-t', '--[no-]trace', 'Turn on line tracing' do |v|
77
+ Setting[:linetrace] = v
78
+ end
79
+ end
80
+
81
+ def version
82
+ @opts.on '-v', '--version', 'Print program version' do
83
+ @runner.version = Byebug::VERSION
84
+ end
85
+ end
86
+
87
+ def help
88
+ @opts.on('-h', '--help', 'Display this message') do
89
+ @runner.help = @opts.help
90
+ end
91
+ end
92
+ end
93
+ end
@@ -2,6 +2,9 @@ require 'yaml'
2
2
 
3
3
  module Byebug
4
4
  module Printers
5
+ #
6
+ # Base printer
7
+ #
5
8
  class Base
6
9
  class MissedPath < StandardError; end
7
10
  class MissedArgument < StandardError; end
@@ -2,9 +2,10 @@ require 'byebug/printers/base'
2
2
 
3
3
  module Byebug
4
4
  module Printers
5
+ #
6
+ # Plain text printer
7
+ #
5
8
  class Plain < Base
6
- include Columnize
7
-
8
9
  def print(path, args = {})
9
10
  message = translate(locate(path), args)
10
11
  tail = parts(path).include?('confirmations') ? ' (y/n) ' : "\n"
@@ -12,16 +13,11 @@ module Byebug
12
13
  end
13
14
 
14
15
  def print_collection(path, collection, &block)
15
- modifier = get_modifier(path)
16
16
  lines = array_of_args(collection, &block).map do |args|
17
17
  print(path, args)
18
18
  end
19
19
 
20
- if modifier == 'c'
21
- columnize(lines.map { |l| l.gsub(/\n$/, '') }, Setting[:width])
22
- else
23
- lines.join('')
24
- end
20
+ lines.join
25
21
  end
26
22
 
27
23
  def print_variables(variables, *_)
@@ -38,12 +34,6 @@ module Byebug
38
34
 
39
35
  private
40
36
 
41
- def get_modifier(path)
42
- modifier_regexp = /\|(\w+)$/
43
- modifier_match = locate(path).match(modifier_regexp)
44
- modifier_match && modifier_match[1]
45
- end
46
-
47
37
  def contents_files
48
38
  [File.expand_path(File.join('..', 'texts', 'plain.yml'), __FILE__)] +
49
39
  super
@@ -18,11 +18,11 @@ break:
18
18
  delete_all: "Delete all breakpoints?"
19
19
 
20
20
  catch:
21
- catching: "Catching exception {exception}."
21
+ added: "Catching exception {exception}."
22
+ removed: "Catch for exception {exception} removed"
22
23
  errors:
23
24
  off: "Off expected. Got {off}"
24
25
  not_class: "Warning {class} is not known to be a Class"
25
- removed: "Catch for exception {exception} removed"
26
26
  not_found: "Catch for exception {exception} not found"
27
27
  confirmations:
28
28
  delete_all: "Delete all catchpoints? (y or n) "
@@ -54,10 +54,6 @@ frame:
54
54
  too_high: "Can't navigate beyond the newest frame"
55
55
  c_frame: "Can't navigate to c-frame"
56
56
 
57
- help:
58
- errors:
59
- undefined: "Undefined command: {cmd}. Try: help"
60
-
61
57
  info:
62
58
  errors:
63
59
  undefined_file: "{file} is not a valid source file"
@@ -87,7 +83,6 @@ show:
87
83
  source:
88
84
  errors:
89
85
  not_found: "File \"{file}\" not found"
90
- not_available: "Source commmand not available at this time"
91
86
 
92
87
  thread:
93
88
  errors:
@@ -1,164 +1,163 @@
1
- require 'byebug/states/regular_state'
2
- require 'byebug/helpers/file'
1
+ require 'forwardable'
2
+
3
+ require 'byebug/helpers/eval'
4
+ require 'byebug/errors'
3
5
 
4
6
  module Byebug
5
7
  #
6
- # Processes commands in regular mode
8
+ # Processes commands in regular mode.
9
+ #
10
+ # You can override this class to create your own command processor that, for
11
+ # example, whitelists only certain commands to be executed.
12
+ #
13
+ # @see PostMortemProcessor for a example
7
14
  #
8
- class CommandProcessor < Processor
9
- include Helpers::FileHelper
15
+ class CommandProcessor
16
+ include Helpers::EvalHelper
10
17
 
11
- attr_reader :display, :state
18
+ attr_accessor :prev_line
19
+ attr_reader :context
12
20
 
13
- def initialize(interface = LocalInterface.new)
14
- super(interface)
21
+ def initialize(context)
22
+ @context = context
15
23
 
16
- @display = []
17
- @last_cmd = nil # To allow empty (just <RET>) commands
18
- @context_was_dead = false # Assume we haven't started.
24
+ @proceed = false
25
+ @prev_line = nil
19
26
  end
20
27
 
21
- def interface=(interface)
22
- @interface.close if @interface
23
- @interface = interface
28
+ def interface
29
+ @interface ||= context.class.interface
24
30
  end
25
31
 
26
- def at_breakpoint(_context, breakpoint)
27
- n = Byebug.breakpoints.index(breakpoint) + 1
28
- file = normalize(breakpoint.source)
29
- line = breakpoint.pos
32
+ def printer
33
+ @printer ||= Printers::Plain.new
34
+ end
30
35
 
31
- puts "Stopped by breakpoint #{n} at #{file}:#{line}"
36
+ def frame
37
+ @context.frame
32
38
  end
33
39
 
34
- def at_catchpoint(context, excpt)
35
- file = normalize(context.frame_file(0))
36
- line = context.frame_line(0)
40
+ extend Forwardable
41
+ def_delegator :printer, :print, :pr
42
+ def_delegator :printer, :print_collection, :prc
43
+ def_delegator :printer, :print_variables, :prv
37
44
 
38
- puts "Catchpoint at #{file}:#{line}: `#{excpt}' (#{excpt.class})"
39
- end
45
+ def_delegators :interface, :errmsg, :puts, :confirm
40
46
 
41
- def at_tracing(context, file, line)
42
- puts "Tracing: #{normalize(file)}:#{line} #{get_line(file, line)}"
47
+ #
48
+ # Available commands
49
+ #
50
+ def command_list
51
+ @command_list ||= CommandList.new(commands)
52
+ end
43
53
 
44
- always_run(context, file, line, 2)
54
+ def commands
55
+ Byebug.commands
45
56
  end
46
57
 
47
- def at_line(context, file, line)
48
- process_commands(context, file, line)
58
+ def at_breakpoint(brkpt)
59
+ number = Byebug.breakpoints.index(brkpt) + 1
60
+
61
+ puts "Stopped by breakpoint #{number} at #{frame.file}:#{frame.line}"
49
62
  end
50
63
 
51
- def at_return(context, file, line)
52
- process_commands(context, file, line)
64
+ def at_catchpoint(exception)
65
+ puts "Catchpoint at #{context.location}: `#{exception}'"
53
66
  end
54
67
 
55
- private
68
+ def at_tracing
69
+ puts "Tracing: #{context.full_location}"
56
70
 
57
- #
58
- # Prompt shown before reading a command.
59
- #
60
- def prompt(context)
61
- "(byebug#{context.dead? ? ':post-mortem' : ''}) "
71
+ run_auto_commands(2)
62
72
  end
63
73
 
64
- #
65
- # Run commands everytime.
66
- #
67
- # For example display commands or possibly the list or irb in an "autolist"
68
- # or "autoirb".
69
- #
70
- # @return List of commands acceptable to run bound to the current state
71
- #
72
- def always_run(context, file, line, run_level)
73
- @state = RegularState.new(context, @display, file, @interface, line)
74
-
75
- # Change default when in irb or code included in command line
76
- Setting[:autolist] = false if ['(irb)', '-e'].include?(file)
74
+ def at_line
75
+ process_commands
76
+ end
77
77
 
78
- # Bind commands to the current state.
79
- commands.each { |cmd| cmd.execute if cmd.class.always_run >= run_level }
78
+ def at_return
79
+ process_commands
80
80
  end
81
81
 
82
- def commands
83
- Byebug.commands.map { |cmd| cmd.new(state) }
82
+ #
83
+ # Let the execution continue
84
+ #
85
+ def proceed!
86
+ @proceed = true
84
87
  end
85
88
 
86
89
  #
87
90
  # Handle byebug commands.
88
91
  #
89
- def process_commands(context, file, line)
90
- always_run(context, file, line, 1)
91
-
92
- puts 'The program finished.' if program_just_finished?(context)
93
- puts(state.location) if Setting[:autolist] == 0
94
-
95
- @interface.autorestore
92
+ def process_commands
93
+ before_repl
96
94
 
97
- repl(context)
95
+ repl
98
96
  ensure
99
- @interface.autosave
97
+ after_repl
100
98
  end
101
99
 
100
+ protected
101
+
102
102
  #
103
- # Main byebug's REPL
103
+ # Prompt shown before reading a command.
104
104
  #
105
- def repl(context)
106
- until state.proceed?
107
- cmd = @interface.read_command(prompt(context))
108
- return unless cmd
109
-
110
- next if cmd == '' && @last_cmd.nil?
105
+ def prompt
106
+ '(byebug) '
107
+ end
111
108
 
112
- cmd.empty? ? cmd = @last_cmd : @last_cmd = cmd
109
+ private
113
110
 
114
- one_cmd(context, cmd)
115
- end
111
+ def auto_commands_for(run_level)
112
+ command_list.select { |cmd| cmd.always_run >= run_level }
116
113
  end
117
114
 
118
115
  #
119
- # Autoevals a single command
116
+ # Run permanent commands.
120
117
  #
121
- def one_unknown_cmd(input)
122
- unless Setting[:autoeval]
123
- return errmsg("Unknown command: \"#{input}\". Try \"help\"")
124
- end
118
+ def run_auto_commands(run_level)
119
+ auto_commands_for(run_level).each { |cmd| cmd.new(self).execute }
120
+ end
121
+
122
+ def before_repl
123
+ @proceed = false
124
+ @prev_line = nil
125
125
 
126
- eval_cmd = EvalCommand.new(state)
127
- eval_cmd.match(input)
128
- eval_cmd.execute
126
+ run_auto_commands(1)
127
+ interface.autorestore
128
+ end
129
+
130
+ def after_repl
131
+ interface.autosave
129
132
  end
130
133
 
131
134
  #
135
+ # Main byebug's REPL
132
136
  #
133
- # Executes a single byebug command
134
- #
135
- def one_cmd(context, input)
136
- cmd = match_cmd(input)
137
+ def repl
138
+ until @proceed
139
+ cmd = interface.read_command(prompt)
140
+ return if cmd.nil?
137
141
 
138
- return one_unknown_cmd(input) unless cmd
142
+ next if cmd == ''
139
143
 
140
- if context.dead? && !cmd.class.allow_in_post_mortem
141
- return errmsg('Command unavailable in post mortem mode.')
144
+ run_cmd(cmd)
142
145
  end
143
-
144
- cmd.execute
145
146
  end
146
147
 
147
148
  #
148
- # Finds a matches the command matching the input
149
+ # Executes the received input
149
150
  #
150
- def match_cmd(input)
151
- commands.find { |cmd| cmd.match(input) }
152
- end
153
-
151
+ # Instantiates a command matching the input and runs it. If a matching
152
+ # command is not found, it evaluates the unknown input.
154
153
  #
155
- # Returns true first time control is given to the user after program
156
- # termination.
157
- #
158
- def program_just_finished?(context)
159
- result = context.dead? && !@context_was_dead
160
- @context_was_dead = false if result == true
161
- result
154
+ def run_cmd(input)
155
+ command = command_list.match(input)
156
+ return command.new(self, input).execute if command
157
+
158
+ puts thread_safe_eval(input)
159
+ rescue => e
160
+ errmsg(e)
162
161
  end
163
162
  end
164
163
  end