byebug 5.0.0 → 6.0.0

Sign up to get free protection for your applications and to get access to all the features.
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