runger_byebug 11.2.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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +954 -0
- data/CONTRIBUTING.md +58 -0
- data/GUIDE.md +1806 -0
- data/LICENSE +23 -0
- data/README.md +199 -0
- data/exe/byebug +6 -0
- data/ext/byebug/breakpoint.c +521 -0
- data/ext/byebug/byebug.c +900 -0
- data/ext/byebug/byebug.h +145 -0
- data/ext/byebug/context.c +687 -0
- data/ext/byebug/extconf.rb +12 -0
- data/ext/byebug/locker.c +96 -0
- data/ext/byebug/threads.c +241 -0
- data/lib/byebug/attacher.rb +48 -0
- data/lib/byebug/breakpoint.rb +94 -0
- data/lib/byebug/command.rb +111 -0
- data/lib/byebug/command_list.rb +34 -0
- data/lib/byebug/commands/break.rb +114 -0
- data/lib/byebug/commands/catch.rb +78 -0
- data/lib/byebug/commands/condition.rb +55 -0
- data/lib/byebug/commands/continue.rb +68 -0
- data/lib/byebug/commands/debug.rb +38 -0
- data/lib/byebug/commands/delete.rb +55 -0
- data/lib/byebug/commands/disable/breakpoints.rb +42 -0
- data/lib/byebug/commands/disable/display.rb +43 -0
- data/lib/byebug/commands/disable.rb +33 -0
- data/lib/byebug/commands/display.rb +66 -0
- data/lib/byebug/commands/down.rb +45 -0
- data/lib/byebug/commands/edit.rb +69 -0
- data/lib/byebug/commands/enable/breakpoints.rb +42 -0
- data/lib/byebug/commands/enable/display.rb +43 -0
- data/lib/byebug/commands/enable.rb +33 -0
- data/lib/byebug/commands/finish.rb +57 -0
- data/lib/byebug/commands/frame.rb +57 -0
- data/lib/byebug/commands/help.rb +64 -0
- data/lib/byebug/commands/history.rb +39 -0
- data/lib/byebug/commands/info/breakpoints.rb +65 -0
- data/lib/byebug/commands/info/display.rb +49 -0
- data/lib/byebug/commands/info/file.rb +80 -0
- data/lib/byebug/commands/info/line.rb +35 -0
- data/lib/byebug/commands/info/program.rb +49 -0
- data/lib/byebug/commands/info.rb +37 -0
- data/lib/byebug/commands/interrupt.rb +34 -0
- data/lib/byebug/commands/irb.rb +50 -0
- data/lib/byebug/commands/kill.rb +45 -0
- data/lib/byebug/commands/list.rb +159 -0
- data/lib/byebug/commands/method.rb +53 -0
- data/lib/byebug/commands/next.rb +40 -0
- data/lib/byebug/commands/pry.rb +41 -0
- data/lib/byebug/commands/quit.rb +42 -0
- data/lib/byebug/commands/restart.rb +64 -0
- data/lib/byebug/commands/save.rb +72 -0
- data/lib/byebug/commands/set.rb +79 -0
- data/lib/byebug/commands/show.rb +45 -0
- data/lib/byebug/commands/skip.rb +85 -0
- data/lib/byebug/commands/source.rb +40 -0
- data/lib/byebug/commands/step.rb +40 -0
- data/lib/byebug/commands/thread/current.rb +37 -0
- data/lib/byebug/commands/thread/list.rb +43 -0
- data/lib/byebug/commands/thread/resume.rb +45 -0
- data/lib/byebug/commands/thread/stop.rb +43 -0
- data/lib/byebug/commands/thread/switch.rb +46 -0
- data/lib/byebug/commands/thread.rb +34 -0
- data/lib/byebug/commands/tracevar.rb +54 -0
- data/lib/byebug/commands/undisplay.rb +51 -0
- data/lib/byebug/commands/untracevar.rb +36 -0
- data/lib/byebug/commands/up.rb +45 -0
- data/lib/byebug/commands/var/all.rb +41 -0
- data/lib/byebug/commands/var/args.rb +39 -0
- data/lib/byebug/commands/var/const.rb +49 -0
- data/lib/byebug/commands/var/global.rb +37 -0
- data/lib/byebug/commands/var/instance.rb +39 -0
- data/lib/byebug/commands/var/local.rb +39 -0
- data/lib/byebug/commands/var.rb +37 -0
- data/lib/byebug/commands/where.rb +64 -0
- data/lib/byebug/commands.rb +40 -0
- data/lib/byebug/context.rb +157 -0
- data/lib/byebug/core.rb +115 -0
- data/lib/byebug/errors.rb +29 -0
- data/lib/byebug/frame.rb +185 -0
- data/lib/byebug/helpers/bin.rb +47 -0
- data/lib/byebug/helpers/eval.rb +134 -0
- data/lib/byebug/helpers/file.rb +63 -0
- data/lib/byebug/helpers/frame.rb +75 -0
- data/lib/byebug/helpers/parse.rb +80 -0
- data/lib/byebug/helpers/path.rb +40 -0
- data/lib/byebug/helpers/reflection.rb +19 -0
- data/lib/byebug/helpers/string.rb +33 -0
- data/lib/byebug/helpers/thread.rb +67 -0
- data/lib/byebug/helpers/toggle.rb +62 -0
- data/lib/byebug/helpers/var.rb +70 -0
- data/lib/byebug/history.rb +130 -0
- data/lib/byebug/interface.rb +146 -0
- data/lib/byebug/interfaces/local_interface.rb +63 -0
- data/lib/byebug/interfaces/remote_interface.rb +50 -0
- data/lib/byebug/interfaces/script_interface.rb +33 -0
- data/lib/byebug/interfaces/test_interface.rb +67 -0
- data/lib/byebug/option_setter.rb +95 -0
- data/lib/byebug/printers/base.rb +68 -0
- data/lib/byebug/printers/plain.rb +44 -0
- data/lib/byebug/printers/texts/base.yml +115 -0
- data/lib/byebug/printers/texts/plain.yml +33 -0
- data/lib/byebug/processors/command_processor.rb +173 -0
- data/lib/byebug/processors/control_processor.rb +24 -0
- data/lib/byebug/processors/post_mortem_processor.rb +18 -0
- data/lib/byebug/processors/script_processor.rb +49 -0
- data/lib/byebug/remote/client.rb +57 -0
- data/lib/byebug/remote/server.rb +47 -0
- data/lib/byebug/remote.rb +85 -0
- data/lib/byebug/runner.rb +198 -0
- data/lib/byebug/setting.rb +79 -0
- data/lib/byebug/settings/autoirb.rb +29 -0
- data/lib/byebug/settings/autolist.rb +29 -0
- data/lib/byebug/settings/autopry.rb +29 -0
- data/lib/byebug/settings/autosave.rb +17 -0
- data/lib/byebug/settings/basename.rb +16 -0
- data/lib/byebug/settings/callstyle.rb +20 -0
- data/lib/byebug/settings/fullpath.rb +16 -0
- data/lib/byebug/settings/histfile.rb +20 -0
- data/lib/byebug/settings/histsize.rb +20 -0
- data/lib/byebug/settings/linetrace.rb +22 -0
- data/lib/byebug/settings/listsize.rb +21 -0
- data/lib/byebug/settings/post_mortem.rb +27 -0
- data/lib/byebug/settings/savefile.rb +20 -0
- data/lib/byebug/settings/stack_on_error.rb +15 -0
- data/lib/byebug/settings/width.rb +20 -0
- data/lib/byebug/source_file_formatter.rb +71 -0
- data/lib/byebug/subcommands.rb +54 -0
- data/lib/byebug/version.rb +8 -0
- data/lib/byebug.rb +3 -0
- metadata +194 -0
@@ -0,0 +1,67 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Byebug
|
4
|
+
module Helpers
|
5
|
+
#
|
6
|
+
# Utilities for thread subcommands
|
7
|
+
#
|
8
|
+
module ThreadHelper
|
9
|
+
def display_context(ctx)
|
10
|
+
puts pr("thread.context", thread_arguments(ctx))
|
11
|
+
end
|
12
|
+
|
13
|
+
def thread_arguments(ctx)
|
14
|
+
{
|
15
|
+
status_flag: status_flag(ctx),
|
16
|
+
debug_flag: debug_flag(ctx),
|
17
|
+
id: ctx.thnum,
|
18
|
+
thread: ctx.thread.inspect,
|
19
|
+
file_line: location(ctx),
|
20
|
+
pid: Process.pid,
|
21
|
+
status: ctx.thread.status,
|
22
|
+
current: current_thread?(ctx)
|
23
|
+
}
|
24
|
+
end
|
25
|
+
|
26
|
+
def current_thread?(ctx)
|
27
|
+
ctx.thread == Thread.current
|
28
|
+
end
|
29
|
+
|
30
|
+
def context_from_thread(thnum)
|
31
|
+
ctx = Byebug.contexts.find { |c| c.thnum.to_s == thnum }
|
32
|
+
|
33
|
+
err = if ctx.nil?
|
34
|
+
pr("thread.errors.no_thread")
|
35
|
+
elsif ctx == context
|
36
|
+
pr("thread.errors.current_thread")
|
37
|
+
elsif ctx.ignored?
|
38
|
+
pr("thread.errors.ignored", arg: thnum)
|
39
|
+
end
|
40
|
+
|
41
|
+
[ctx, err]
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
# @todo Check whether it is Byebug.current_context or context
|
47
|
+
def location(ctx)
|
48
|
+
return context.location if ctx == Byebug.current_context
|
49
|
+
|
50
|
+
backtrace = ctx.thread.backtrace_locations
|
51
|
+
return "" unless backtrace && backtrace[0]
|
52
|
+
|
53
|
+
"#{backtrace[0].path}:#{backtrace[0].lineno}"
|
54
|
+
end
|
55
|
+
|
56
|
+
def status_flag(ctx)
|
57
|
+
return "$" if ctx.suspended?
|
58
|
+
|
59
|
+
current_thread?(ctx) ? "+" : " "
|
60
|
+
end
|
61
|
+
|
62
|
+
def debug_flag(ctx)
|
63
|
+
ctx.ignored? ? "!" : " "
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "parse"
|
4
|
+
|
5
|
+
module Byebug
|
6
|
+
module Helpers
|
7
|
+
#
|
8
|
+
# Utilities to assist breakpoint/display enabling/disabling.
|
9
|
+
#
|
10
|
+
module ToggleHelper
|
11
|
+
include ParseHelper
|
12
|
+
|
13
|
+
def enable_disable_breakpoints(is_enable, args)
|
14
|
+
raise pr("toggle.errors.no_breakpoints") if Breakpoint.none?
|
15
|
+
|
16
|
+
select_breakpoints(is_enable, args).each do |b|
|
17
|
+
enabled = (is_enable == "enable")
|
18
|
+
raise pr("toggle.errors.expression", expr: b.expr) if enabled && !syntax_valid?(b.expr)
|
19
|
+
|
20
|
+
puts pr("toggle.messages.toggled", bpnum: b.id,
|
21
|
+
endis: enabled ? "en" : "dis")
|
22
|
+
b.enabled = enabled
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def enable_disable_display(is_enable, args)
|
27
|
+
raise pr("toggle.errors.no_display") if n_displays.zero?
|
28
|
+
|
29
|
+
selected_displays = args ? args.split(/ +/) : [1..n_displays + 1]
|
30
|
+
|
31
|
+
selected_displays.each do |pos|
|
32
|
+
pos, err = get_int(pos, "#{is_enable} display", 1, n_displays)
|
33
|
+
raise err unless err.nil?
|
34
|
+
|
35
|
+
Byebug.displays[pos - 1][0] = (is_enable == "enable")
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def select_breakpoints(is_enable, args)
|
42
|
+
all_breakpoints = Byebug.breakpoints.sort_by(&:id)
|
43
|
+
return all_breakpoints if args.nil?
|
44
|
+
|
45
|
+
selected_ids = []
|
46
|
+
args.split(/ +/).each do |pos|
|
47
|
+
last_id = all_breakpoints.last.id
|
48
|
+
pos, err = get_int(pos, "#{is_enable} breakpoints", 1, last_id)
|
49
|
+
raise(ArgumentError, err) unless pos
|
50
|
+
|
51
|
+
selected_ids << pos
|
52
|
+
end
|
53
|
+
|
54
|
+
all_breakpoints.select { |b| selected_ids.include?(b.id) }
|
55
|
+
end
|
56
|
+
|
57
|
+
def n_displays
|
58
|
+
Byebug.displays.size
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "eval"
|
4
|
+
|
5
|
+
module Byebug
|
6
|
+
module Helpers
|
7
|
+
#
|
8
|
+
# Utilities for variable subcommands
|
9
|
+
#
|
10
|
+
module VarHelper
|
11
|
+
include EvalHelper
|
12
|
+
|
13
|
+
def var_list(ary, binding = context.frame._binding)
|
14
|
+
vars = ary.sort.map do |name|
|
15
|
+
code = name.to_s
|
16
|
+
|
17
|
+
if code == "$SAFE" && Gem.ruby_version >= Gem::Version.new("2.7.0.preview3")
|
18
|
+
code = <<~RUBY
|
19
|
+
original_stderr = $stderr
|
20
|
+
|
21
|
+
begin
|
22
|
+
$stderr = StringIO.new
|
23
|
+
|
24
|
+
#{code}
|
25
|
+
ensure
|
26
|
+
$stderr = original_stderr
|
27
|
+
end
|
28
|
+
RUBY
|
29
|
+
end
|
30
|
+
|
31
|
+
[name, safe_inspect(silent_eval(code, binding))]
|
32
|
+
end
|
33
|
+
|
34
|
+
puts prv(vars, "instance")
|
35
|
+
end
|
36
|
+
|
37
|
+
def var_global
|
38
|
+
globals = global_variables.reject do |v|
|
39
|
+
%i[$IGNORECASE $= $KCODE $-K $binding].include?(v)
|
40
|
+
end
|
41
|
+
|
42
|
+
var_list(globals)
|
43
|
+
end
|
44
|
+
|
45
|
+
def var_instance(str)
|
46
|
+
obj = warning_eval(str || "self")
|
47
|
+
|
48
|
+
var_list(obj.instance_variables, obj.instance_eval { binding })
|
49
|
+
end
|
50
|
+
|
51
|
+
def var_local
|
52
|
+
locals = context.frame.locals
|
53
|
+
cur_self = context.frame._self
|
54
|
+
locals[:self] = cur_self unless cur_self.to_s == "main"
|
55
|
+
puts prv(locals.keys.sort.map { |k| [k, locals[k]] }, "instance")
|
56
|
+
end
|
57
|
+
|
58
|
+
def var_args
|
59
|
+
args = context.frame.args
|
60
|
+
return if args == [[:rest]]
|
61
|
+
|
62
|
+
all_locals = context.frame.locals
|
63
|
+
arg_values = args.map { |arg| arg[1] }
|
64
|
+
|
65
|
+
locals = all_locals.select { |k, _| arg_values.include?(k) }
|
66
|
+
puts prv(locals.keys.sort.map { |k| [k, locals[k]] }, "instance")
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
begin
|
4
|
+
require "readline"
|
5
|
+
rescue LoadError
|
6
|
+
warn <<-MESSAGE
|
7
|
+
Sorry, you can't use byebug without Readline. To solve this, you need to
|
8
|
+
rebuild Ruby with Readline support. If using Ubuntu, try `sudo apt-get
|
9
|
+
install libreadline-dev` and then reinstall your Ruby.
|
10
|
+
MESSAGE
|
11
|
+
|
12
|
+
raise
|
13
|
+
end
|
14
|
+
|
15
|
+
module Byebug
|
16
|
+
#
|
17
|
+
# Handles byebug's history of commands.
|
18
|
+
#
|
19
|
+
class History
|
20
|
+
attr_reader :size
|
21
|
+
|
22
|
+
def initialize
|
23
|
+
@size = 0
|
24
|
+
end
|
25
|
+
|
26
|
+
#
|
27
|
+
# Array holding the list of commands in history
|
28
|
+
#
|
29
|
+
def buffer
|
30
|
+
Readline::HISTORY.to_a
|
31
|
+
end
|
32
|
+
|
33
|
+
#
|
34
|
+
# Restores history from disk.
|
35
|
+
#
|
36
|
+
def restore
|
37
|
+
return unless File.exist?(Setting[:histfile])
|
38
|
+
|
39
|
+
File.readlines(Setting[:histfile]).reverse_each { |l| push(l.chomp) }
|
40
|
+
end
|
41
|
+
|
42
|
+
#
|
43
|
+
# Saves history to disk.
|
44
|
+
#
|
45
|
+
def save
|
46
|
+
n_cmds = Setting[:histsize] > size ? size : Setting[:histsize]
|
47
|
+
|
48
|
+
File.open(Setting[:histfile], "w") do |file|
|
49
|
+
n_cmds.times { file.puts(pop) }
|
50
|
+
end
|
51
|
+
|
52
|
+
clear
|
53
|
+
end
|
54
|
+
|
55
|
+
#
|
56
|
+
# Discards history.
|
57
|
+
#
|
58
|
+
def clear
|
59
|
+
size.times { pop }
|
60
|
+
end
|
61
|
+
|
62
|
+
#
|
63
|
+
# Adds a new command to Readline's history.
|
64
|
+
#
|
65
|
+
def push(cmd)
|
66
|
+
return if ignore?(cmd)
|
67
|
+
|
68
|
+
@size += 1
|
69
|
+
Readline::HISTORY.push(cmd)
|
70
|
+
end
|
71
|
+
|
72
|
+
#
|
73
|
+
# Removes a command from Readline's history.
|
74
|
+
#
|
75
|
+
def pop
|
76
|
+
@size -= 1
|
77
|
+
Readline::HISTORY.pop
|
78
|
+
end
|
79
|
+
|
80
|
+
#
|
81
|
+
# Prints the requested numbers of history entries.
|
82
|
+
#
|
83
|
+
def to_s(n_cmds)
|
84
|
+
show_size = n_cmds ? specific_max_size(n_cmds) : default_max_size
|
85
|
+
|
86
|
+
commands = buffer.last(show_size)
|
87
|
+
|
88
|
+
last_ids(show_size).zip(commands).map do |l|
|
89
|
+
format("%<position>5d %<command>s", position: l[0], command: l[1])
|
90
|
+
end.join("\n") + "\n"
|
91
|
+
end
|
92
|
+
|
93
|
+
#
|
94
|
+
# Array of ids of the last +number+ commands.
|
95
|
+
#
|
96
|
+
def last_ids(number)
|
97
|
+
(1 + size - number..size).to_a
|
98
|
+
end
|
99
|
+
|
100
|
+
#
|
101
|
+
# Max number of commands to be displayed when no size has been specified.
|
102
|
+
#
|
103
|
+
# Never more than Setting[:histsize].
|
104
|
+
#
|
105
|
+
def default_max_size
|
106
|
+
[Setting[:histsize], size].min
|
107
|
+
end
|
108
|
+
|
109
|
+
#
|
110
|
+
# Max number of commands to be displayed when a size has been specified.
|
111
|
+
#
|
112
|
+
# The only bound here is not showing more items than available.
|
113
|
+
#
|
114
|
+
def specific_max_size(number)
|
115
|
+
[size, number].min
|
116
|
+
end
|
117
|
+
|
118
|
+
#
|
119
|
+
# Whether a specific command should not be stored in history.
|
120
|
+
#
|
121
|
+
# For now, empty lines and consecutive duplicates.
|
122
|
+
#
|
123
|
+
def ignore?(buf)
|
124
|
+
return true if /^\s*$/.match?(buf)
|
125
|
+
return false if Readline::HISTORY.empty?
|
126
|
+
|
127
|
+
buffer[Readline::HISTORY.length - 1] == buf
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
@@ -0,0 +1,146 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "setting"
|
4
|
+
require_relative "history"
|
5
|
+
require_relative "helpers/file"
|
6
|
+
|
7
|
+
#
|
8
|
+
# Namespace for all of byebug's code
|
9
|
+
#
|
10
|
+
module Byebug
|
11
|
+
#
|
12
|
+
# Main Interface class
|
13
|
+
#
|
14
|
+
# Contains common functionality to all implemented interfaces.
|
15
|
+
#
|
16
|
+
class Interface
|
17
|
+
include Helpers::FileHelper
|
18
|
+
|
19
|
+
attr_accessor :command_queue, :history
|
20
|
+
attr_reader :input, :output, :error
|
21
|
+
|
22
|
+
def initialize
|
23
|
+
@command_queue = []
|
24
|
+
@history = History.new
|
25
|
+
@last_line = ""
|
26
|
+
end
|
27
|
+
|
28
|
+
def last_if_empty(input)
|
29
|
+
@last_line = input.empty? ? @last_line : input
|
30
|
+
end
|
31
|
+
|
32
|
+
#
|
33
|
+
# Pops a command from the input stream.
|
34
|
+
#
|
35
|
+
def read_command(prompt)
|
36
|
+
return command_queue.shift unless command_queue.empty?
|
37
|
+
|
38
|
+
read_input(prompt)
|
39
|
+
end
|
40
|
+
|
41
|
+
#
|
42
|
+
# Pushes lines in +filename+ to the command queue.
|
43
|
+
#
|
44
|
+
def read_file(filename)
|
45
|
+
command_queue.concat(get_lines(filename))
|
46
|
+
end
|
47
|
+
|
48
|
+
#
|
49
|
+
# Reads a new line from the interface's input stream, parses it into
|
50
|
+
# commands and saves it to history.
|
51
|
+
#
|
52
|
+
# @return [String] Representing something to be run by the debugger.
|
53
|
+
#
|
54
|
+
def read_input(prompt, save_hist = true)
|
55
|
+
line = prepare_input(prompt)
|
56
|
+
return unless line
|
57
|
+
|
58
|
+
history.push(line) if save_hist
|
59
|
+
|
60
|
+
command_queue.concat(split_commands(line))
|
61
|
+
command_queue.shift
|
62
|
+
end
|
63
|
+
|
64
|
+
#
|
65
|
+
# Reads a new line from the interface's input stream.
|
66
|
+
#
|
67
|
+
# @return [String] New string read or the previous string if the string
|
68
|
+
# read now was empty.
|
69
|
+
#
|
70
|
+
def prepare_input(prompt)
|
71
|
+
line = readline(prompt)
|
72
|
+
return unless line
|
73
|
+
|
74
|
+
last_if_empty(line)
|
75
|
+
end
|
76
|
+
|
77
|
+
#
|
78
|
+
# Prints an error message to the error stream.
|
79
|
+
#
|
80
|
+
def errmsg(message)
|
81
|
+
error.print("*** #{message}\n")
|
82
|
+
end
|
83
|
+
|
84
|
+
#
|
85
|
+
# Prints an output message to the output stream.
|
86
|
+
#
|
87
|
+
def puts(message)
|
88
|
+
output.puts(message)
|
89
|
+
end
|
90
|
+
|
91
|
+
#
|
92
|
+
# Prints an output message to the output stream without a final "\n".
|
93
|
+
#
|
94
|
+
def print(message)
|
95
|
+
output.print(message)
|
96
|
+
end
|
97
|
+
|
98
|
+
#
|
99
|
+
# Confirms user introduced an affirmative response to the input stream.
|
100
|
+
#
|
101
|
+
def confirm(prompt)
|
102
|
+
readline(prompt) == "y"
|
103
|
+
end
|
104
|
+
|
105
|
+
def close
|
106
|
+
end
|
107
|
+
|
108
|
+
#
|
109
|
+
# Saves or clears history according to +autosave+ setting.
|
110
|
+
#
|
111
|
+
def autosave
|
112
|
+
Setting[:autosave] ? history.save : history.clear
|
113
|
+
end
|
114
|
+
|
115
|
+
#
|
116
|
+
# Restores history according to +autosave+ setting.
|
117
|
+
#
|
118
|
+
def autorestore
|
119
|
+
history.restore if Setting[:autosave]
|
120
|
+
end
|
121
|
+
|
122
|
+
private
|
123
|
+
|
124
|
+
#
|
125
|
+
# Splits a command line of the form "cmd1 ; cmd2 ; ... ; cmdN" into an
|
126
|
+
# array of commands: [cmd1, cmd2, ..., cmdN]
|
127
|
+
#
|
128
|
+
def split_commands(cmd_line)
|
129
|
+
return [""] if cmd_line.empty?
|
130
|
+
|
131
|
+
cmd_line.split(/;/).each_with_object([]) do |v, m|
|
132
|
+
if m.empty? || m.last[-1] != "\\"
|
133
|
+
m << v.strip
|
134
|
+
next
|
135
|
+
end
|
136
|
+
|
137
|
+
m.last[-1, 1] = ""
|
138
|
+
m.last << ";" << v
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
require_relative "interfaces/local_interface"
|
145
|
+
require_relative "interfaces/script_interface"
|
146
|
+
require_relative "interfaces/remote_interface"
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Byebug
|
4
|
+
#
|
5
|
+
# Interface class for standard byebug use.
|
6
|
+
#
|
7
|
+
class LocalInterface < Interface
|
8
|
+
EOF_ALIAS = "continue"
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
super()
|
12
|
+
@input = $stdin
|
13
|
+
@output = $stdout
|
14
|
+
@error = $stderr
|
15
|
+
end
|
16
|
+
|
17
|
+
#
|
18
|
+
# Reads a single line of input using Readline. If Ctrl-D is pressed, it
|
19
|
+
# returns "continue", meaning that program's execution will go on.
|
20
|
+
#
|
21
|
+
# @param prompt Prompt to be displayed.
|
22
|
+
#
|
23
|
+
def readline(prompt)
|
24
|
+
with_repl_like_sigint { without_readline_completion { Readline.readline(prompt) || EOF_ALIAS } }
|
25
|
+
end
|
26
|
+
|
27
|
+
#
|
28
|
+
# Yields the block handling Ctrl-C the following way: if pressed while
|
29
|
+
# waiting for input, the line is reset to only the prompt and we ask for
|
30
|
+
# input again.
|
31
|
+
#
|
32
|
+
# @note Any external 'INT' traps are overriden during this method.
|
33
|
+
#
|
34
|
+
def with_repl_like_sigint
|
35
|
+
orig_handler = trap("INT") { raise Interrupt }
|
36
|
+
yield
|
37
|
+
rescue Interrupt
|
38
|
+
puts("^C")
|
39
|
+
retry
|
40
|
+
ensure
|
41
|
+
trap("INT", orig_handler)
|
42
|
+
end
|
43
|
+
|
44
|
+
#
|
45
|
+
# Disable any Readline completion procs.
|
46
|
+
#
|
47
|
+
# Other gems, for example, IRB could've installed completion procs that are
|
48
|
+
# dependent on them being loaded. Disable those while byebug is the REPL
|
49
|
+
# making use of Readline.
|
50
|
+
#
|
51
|
+
def without_readline_completion
|
52
|
+
orig_completion = Readline.completion_proc
|
53
|
+
return yield unless orig_completion
|
54
|
+
|
55
|
+
begin
|
56
|
+
Readline.completion_proc = ->(_) { nil }
|
57
|
+
yield
|
58
|
+
ensure
|
59
|
+
Readline.completion_proc = orig_completion
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../history"
|
4
|
+
|
5
|
+
module Byebug
|
6
|
+
#
|
7
|
+
# Interface class for remote use of byebug.
|
8
|
+
#
|
9
|
+
class RemoteInterface < Interface
|
10
|
+
def initialize(socket)
|
11
|
+
super()
|
12
|
+
@input = socket
|
13
|
+
@output = socket
|
14
|
+
@error = socket
|
15
|
+
end
|
16
|
+
|
17
|
+
def read_command(prompt)
|
18
|
+
super("PROMPT #{prompt}")
|
19
|
+
rescue Errno::EPIPE, Errno::ECONNABORTED
|
20
|
+
"continue"
|
21
|
+
end
|
22
|
+
|
23
|
+
def confirm(prompt)
|
24
|
+
super("CONFIRM #{prompt}")
|
25
|
+
rescue Errno::EPIPE, Errno::ECONNABORTED
|
26
|
+
false
|
27
|
+
end
|
28
|
+
|
29
|
+
def print(message)
|
30
|
+
super(message)
|
31
|
+
rescue Errno::EPIPE, Errno::ECONNABORTED
|
32
|
+
nil
|
33
|
+
end
|
34
|
+
|
35
|
+
def puts(message)
|
36
|
+
super(message)
|
37
|
+
rescue Errno::EPIPE, Errno::ECONNABORTED
|
38
|
+
nil
|
39
|
+
end
|
40
|
+
|
41
|
+
def close
|
42
|
+
output.close
|
43
|
+
end
|
44
|
+
|
45
|
+
def readline(prompt)
|
46
|
+
puts(prompt)
|
47
|
+
(input.gets || "continue").chomp
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Byebug
|
4
|
+
#
|
5
|
+
# Interface class for command execution from script files.
|
6
|
+
#
|
7
|
+
class ScriptInterface < Interface
|
8
|
+
def initialize(file, verbose = false)
|
9
|
+
super()
|
10
|
+
@verbose = verbose
|
11
|
+
@input = File.open(file)
|
12
|
+
@output = verbose ? $stdout : StringIO.new
|
13
|
+
@error = $stderr
|
14
|
+
end
|
15
|
+
|
16
|
+
def read_command(prompt)
|
17
|
+
readline(prompt, false)
|
18
|
+
end
|
19
|
+
|
20
|
+
def close
|
21
|
+
input.close
|
22
|
+
end
|
23
|
+
|
24
|
+
def readline(*)
|
25
|
+
while (result = input.gets)
|
26
|
+
output.puts "+ #{result}" if @verbose
|
27
|
+
next if /^\s*#/.match?(result)
|
28
|
+
|
29
|
+
return result.chomp
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Byebug
|
4
|
+
#
|
5
|
+
# Custom interface for easier assertions
|
6
|
+
#
|
7
|
+
class TestInterface < Interface
|
8
|
+
attr_accessor :test_block
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
super()
|
12
|
+
|
13
|
+
clear
|
14
|
+
end
|
15
|
+
|
16
|
+
def errmsg(message)
|
17
|
+
error.concat(prepare(message))
|
18
|
+
end
|
19
|
+
|
20
|
+
def print(message)
|
21
|
+
output.concat(prepare(message))
|
22
|
+
end
|
23
|
+
|
24
|
+
def puts(message)
|
25
|
+
output.concat(prepare(message))
|
26
|
+
end
|
27
|
+
|
28
|
+
def read_command(prompt)
|
29
|
+
cmd = super(prompt)
|
30
|
+
|
31
|
+
return cmd unless cmd.nil? && test_block
|
32
|
+
|
33
|
+
test_block.call
|
34
|
+
self.test_block = nil
|
35
|
+
end
|
36
|
+
|
37
|
+
def clear
|
38
|
+
@input = []
|
39
|
+
@output = []
|
40
|
+
@error = []
|
41
|
+
history.clear
|
42
|
+
end
|
43
|
+
|
44
|
+
def inspect
|
45
|
+
[
|
46
|
+
"Input:", input.join("\n"),
|
47
|
+
"Output:", output.join("\n"),
|
48
|
+
"Error:", error.join("\n")
|
49
|
+
].join("\n")
|
50
|
+
end
|
51
|
+
|
52
|
+
def readline(prompt)
|
53
|
+
puts(prompt)
|
54
|
+
|
55
|
+
cmd = input.shift
|
56
|
+
cmd.is_a?(Proc) ? cmd.call : cmd
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def prepare(message)
|
62
|
+
return message.map(&:to_s) if message.respond_to?(:map)
|
63
|
+
|
64
|
+
message.to_s.split("\n")
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|