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,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Byebug
|
4
|
+
#
|
5
|
+
# Custom exception exception to signal "command not found" errors
|
6
|
+
#
|
7
|
+
class CommandNotFound < NoMethodError
|
8
|
+
def initialize(input, parent = nil)
|
9
|
+
@input = input
|
10
|
+
@parent = parent
|
11
|
+
|
12
|
+
super("Unknown command '#{name}'. Try '#{help}'")
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def name
|
18
|
+
build_cmd(@parent, @input)
|
19
|
+
end
|
20
|
+
|
21
|
+
def help
|
22
|
+
build_cmd("help", @parent)
|
23
|
+
end
|
24
|
+
|
25
|
+
def build_cmd(*args)
|
26
|
+
args.compact.join(" ")
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/byebug/frame.rb
ADDED
@@ -0,0 +1,185 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "helpers/file"
|
4
|
+
|
5
|
+
module Byebug
|
6
|
+
#
|
7
|
+
# Represents a frame in the stack trace
|
8
|
+
#
|
9
|
+
class Frame
|
10
|
+
include Helpers::FileHelper
|
11
|
+
|
12
|
+
attr_reader :pos
|
13
|
+
|
14
|
+
def initialize(context, pos)
|
15
|
+
@context = context
|
16
|
+
@pos = pos
|
17
|
+
end
|
18
|
+
|
19
|
+
def file
|
20
|
+
@context.frame_file(pos)
|
21
|
+
end
|
22
|
+
|
23
|
+
def line
|
24
|
+
@context.frame_line(pos)
|
25
|
+
end
|
26
|
+
|
27
|
+
def _self
|
28
|
+
@context.frame_self(pos)
|
29
|
+
end
|
30
|
+
|
31
|
+
def _binding
|
32
|
+
@context.frame_binding(pos)
|
33
|
+
end
|
34
|
+
|
35
|
+
def _class
|
36
|
+
@context.frame_class(pos)
|
37
|
+
end
|
38
|
+
|
39
|
+
def _method
|
40
|
+
@context.frame_method(pos)
|
41
|
+
end
|
42
|
+
|
43
|
+
def current?
|
44
|
+
@context.frame.pos == pos
|
45
|
+
end
|
46
|
+
|
47
|
+
#
|
48
|
+
# Gets local variables for the frame.
|
49
|
+
#
|
50
|
+
def locals
|
51
|
+
return [] unless _binding
|
52
|
+
|
53
|
+
_binding.local_variables.each_with_object({}) do |e, a|
|
54
|
+
a[e] = _binding.local_variable_get(e)
|
55
|
+
a
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
#
|
60
|
+
# Gets current method arguments for the frame.
|
61
|
+
#
|
62
|
+
def args
|
63
|
+
return c_args unless _binding
|
64
|
+
|
65
|
+
ruby_args
|
66
|
+
end
|
67
|
+
|
68
|
+
#
|
69
|
+
# Returns the current class in the frame or an empty string if the current
|
70
|
+
# +callstyle+ setting is 'short'
|
71
|
+
#
|
72
|
+
def deco_class
|
73
|
+
Setting[:callstyle] == "short" || _class.to_s.empty? ? "" : "#{_class}."
|
74
|
+
end
|
75
|
+
|
76
|
+
def deco_block
|
77
|
+
_method[/(?:block(?: \(\d+ levels\))?|rescue) in /] || ""
|
78
|
+
end
|
79
|
+
|
80
|
+
def deco_method
|
81
|
+
_method[/((?:block(?: \(\d+ levels\))?|rescue) in )?(.*)/]
|
82
|
+
end
|
83
|
+
|
84
|
+
#
|
85
|
+
# Builds a string containing all available args in the frame number, in a
|
86
|
+
# verbose or non verbose way according to the value of the +callstyle+
|
87
|
+
# setting
|
88
|
+
#
|
89
|
+
def deco_args
|
90
|
+
return "" if args.empty?
|
91
|
+
|
92
|
+
my_args = args.map do |arg|
|
93
|
+
prefix, default = prefix_and_default(arg[0])
|
94
|
+
|
95
|
+
kls = use_short_style?(arg) ? "" : "##{locals[arg[1]].class}"
|
96
|
+
|
97
|
+
"#{prefix}#{arg[1] || default}#{kls}"
|
98
|
+
end
|
99
|
+
|
100
|
+
"(#{my_args.join(', ')})"
|
101
|
+
end
|
102
|
+
|
103
|
+
#
|
104
|
+
# Builds a formatted string containing information about current method call
|
105
|
+
#
|
106
|
+
def deco_call
|
107
|
+
deco_block + deco_class + deco_method + deco_args
|
108
|
+
end
|
109
|
+
|
110
|
+
#
|
111
|
+
# Formatted filename in frame
|
112
|
+
#
|
113
|
+
def deco_file
|
114
|
+
Setting[:fullpath] ? File.expand_path(file) : shortpath(file)
|
115
|
+
end
|
116
|
+
|
117
|
+
#
|
118
|
+
# Properly formatted frame number of frame
|
119
|
+
#
|
120
|
+
def deco_pos
|
121
|
+
format("%-2<pos>d", pos: pos)
|
122
|
+
end
|
123
|
+
|
124
|
+
#
|
125
|
+
# Formatted mark for the frame.
|
126
|
+
#
|
127
|
+
# --> marks the current frame
|
128
|
+
# ͱ-- marks c-frames
|
129
|
+
# marks regular frames
|
130
|
+
#
|
131
|
+
def mark
|
132
|
+
return "-->" if current?
|
133
|
+
return " ͱ--" if c_frame?
|
134
|
+
|
135
|
+
" "
|
136
|
+
end
|
137
|
+
|
138
|
+
#
|
139
|
+
# Checks whether the frame is a c-frame
|
140
|
+
#
|
141
|
+
def c_frame?
|
142
|
+
_binding.nil?
|
143
|
+
end
|
144
|
+
|
145
|
+
def to_hash
|
146
|
+
{
|
147
|
+
mark: mark,
|
148
|
+
pos: deco_pos,
|
149
|
+
call: deco_call,
|
150
|
+
file: deco_file,
|
151
|
+
line: line,
|
152
|
+
full_path: File.expand_path(deco_file)
|
153
|
+
}
|
154
|
+
end
|
155
|
+
|
156
|
+
private
|
157
|
+
|
158
|
+
def c_args
|
159
|
+
return [] unless _self.to_s != "main"
|
160
|
+
|
161
|
+
_class.instance_method(_method).parameters
|
162
|
+
end
|
163
|
+
|
164
|
+
def ruby_args
|
165
|
+
meth_name = _binding.eval("__method__")
|
166
|
+
return [] unless meth_name
|
167
|
+
|
168
|
+
meth_obj = _class.instance_method(meth_name)
|
169
|
+
return [] unless meth_obj
|
170
|
+
|
171
|
+
meth_obj.parameters
|
172
|
+
end
|
173
|
+
|
174
|
+
def use_short_style?(arg)
|
175
|
+
Setting[:callstyle] == "short" || arg[1].nil? || locals.empty?
|
176
|
+
end
|
177
|
+
|
178
|
+
def prefix_and_default(arg_type)
|
179
|
+
return ["&", "block"] if arg_type == :block
|
180
|
+
return ["*", "args"] if arg_type == :rest
|
181
|
+
|
182
|
+
["", nil]
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Byebug
|
4
|
+
module Helpers
|
5
|
+
#
|
6
|
+
# Utilities for interaction with executables
|
7
|
+
#
|
8
|
+
module BinHelper
|
9
|
+
#
|
10
|
+
# Cross-platform way of finding an executable in the $PATH.
|
11
|
+
# Adapted from: https://gist.github.com/steakknife/88b6c3837a5e90a08296
|
12
|
+
#
|
13
|
+
def which(cmd)
|
14
|
+
return File.expand_path(cmd) if File.exist?(cmd)
|
15
|
+
|
16
|
+
[nil, *search_paths].each do |path|
|
17
|
+
exe = find_executable(path, cmd)
|
18
|
+
return exe if exe
|
19
|
+
end
|
20
|
+
|
21
|
+
nil
|
22
|
+
end
|
23
|
+
|
24
|
+
def find_executable(path, cmd)
|
25
|
+
executable_file_extensions.each do |ext|
|
26
|
+
exe = File.expand_path(cmd + ext, path)
|
27
|
+
|
28
|
+
return exe if real_executable?(exe)
|
29
|
+
end
|
30
|
+
|
31
|
+
nil
|
32
|
+
end
|
33
|
+
|
34
|
+
def search_paths
|
35
|
+
ENV["PATH"].split(File::PATH_SEPARATOR)
|
36
|
+
end
|
37
|
+
|
38
|
+
def executable_file_extensions
|
39
|
+
ENV["PATHEXT"] ? ENV["PATHEXT"].split(";") : [""]
|
40
|
+
end
|
41
|
+
|
42
|
+
def real_executable?(file)
|
43
|
+
File.executable?(file) && !File.directory?(file)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,134 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Byebug
|
4
|
+
module Helpers
|
5
|
+
#
|
6
|
+
# Utilities to assist evaluation of code strings
|
7
|
+
#
|
8
|
+
module EvalHelper
|
9
|
+
#
|
10
|
+
# Evaluates an +expression+ in a separate thread.
|
11
|
+
#
|
12
|
+
# @param expression [String] Expression to evaluate
|
13
|
+
#
|
14
|
+
def separate_thread_eval(expression)
|
15
|
+
allowing_other_threads do
|
16
|
+
in_new_thread { warning_eval(expression) }
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
#
|
21
|
+
# Evaluates an +expression+ that might use or defer execution to threads
|
22
|
+
# other than the current one.
|
23
|
+
#
|
24
|
+
# @note This is necessary because when in byebug's prompt, every thread is
|
25
|
+
# "frozen" so that nothing gets run. So we need to unlock threads prior
|
26
|
+
# to evaluation or we will run into a deadlock.
|
27
|
+
#
|
28
|
+
# @param expression [String] Expression to evaluate
|
29
|
+
#
|
30
|
+
def multiple_thread_eval(expression)
|
31
|
+
allowing_other_threads { warning_eval(expression) }
|
32
|
+
end
|
33
|
+
|
34
|
+
#
|
35
|
+
# Evaluates a string containing Ruby code in a specific binding,
|
36
|
+
# returning nil if an error happens.
|
37
|
+
#
|
38
|
+
def silent_eval(str, binding = frame._binding)
|
39
|
+
safe_eval(str, binding) { |_e| nil }
|
40
|
+
end
|
41
|
+
|
42
|
+
#
|
43
|
+
# Evaluates a string containing Ruby code in a specific binding,
|
44
|
+
# handling the errors at an error level.
|
45
|
+
#
|
46
|
+
def error_eval(str, binding = frame._binding)
|
47
|
+
safe_eval(str, binding) { |e| raise(e, msg(e)) }
|
48
|
+
end
|
49
|
+
|
50
|
+
#
|
51
|
+
# Evaluates a string containing Ruby code in a specific binding,
|
52
|
+
# handling the errors at a warning level.
|
53
|
+
#
|
54
|
+
def warning_eval(str, binding = frame._binding)
|
55
|
+
safe_eval(str, binding) { |e| errmsg(msg(e)) }
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def safe_eval(str, binding)
|
61
|
+
binding.eval(str.gsub(/\Aeval /, ""), "(byebug)", 1)
|
62
|
+
rescue StandardError, ScriptError => e
|
63
|
+
yield(e)
|
64
|
+
end
|
65
|
+
|
66
|
+
def msg(exception)
|
67
|
+
msg = Setting[:stack_on_error] ? error_msg(exception) : warning_msg(exception)
|
68
|
+
|
69
|
+
pr("eval.exception", text_message: msg)
|
70
|
+
end
|
71
|
+
|
72
|
+
def error_msg(exception)
|
73
|
+
at = exception.backtrace
|
74
|
+
|
75
|
+
locations = ["#{at.shift}: #{warning_msg(exception)}"]
|
76
|
+
locations += at.map { |path| " from #{path}" }
|
77
|
+
locations.join("\n")
|
78
|
+
end
|
79
|
+
|
80
|
+
def warning_msg(exception)
|
81
|
+
"#{exception.class} Exception: #{exception.message}"
|
82
|
+
end
|
83
|
+
|
84
|
+
#
|
85
|
+
# Run block temporarily ignoring all TracePoint events.
|
86
|
+
#
|
87
|
+
# Used to evaluate stuff within Byebug's prompt. Otherwise, any code
|
88
|
+
# creating new threads won't be properly evaluated because new threads
|
89
|
+
# will get blocked by byebug's main thread.
|
90
|
+
#
|
91
|
+
def allowing_other_threads(&block)
|
92
|
+
Byebug.unlock
|
93
|
+
|
94
|
+
tracepoint_allow_reentry(&block)
|
95
|
+
ensure
|
96
|
+
Byebug.lock
|
97
|
+
end
|
98
|
+
|
99
|
+
#
|
100
|
+
# Runs the given block in a new thread, waits for it to finish and
|
101
|
+
# returns the new thread's result.
|
102
|
+
#
|
103
|
+
def in_new_thread
|
104
|
+
res = nil
|
105
|
+
|
106
|
+
Thread.new { res = yield }.join
|
107
|
+
|
108
|
+
res
|
109
|
+
end
|
110
|
+
|
111
|
+
def safe_inspect(var)
|
112
|
+
var.inspect
|
113
|
+
rescue StandardError
|
114
|
+
safe_to_s(var)
|
115
|
+
end
|
116
|
+
|
117
|
+
def safe_to_s(var)
|
118
|
+
var.to_s
|
119
|
+
rescue StandardError
|
120
|
+
"*Error in evaluation*"
|
121
|
+
end
|
122
|
+
|
123
|
+
if TracePoint.respond_to?(:allow_reentry)
|
124
|
+
def tracepoint_allow_reentry(&block)
|
125
|
+
TracePoint.allow_reentry(&block)
|
126
|
+
end
|
127
|
+
else
|
128
|
+
def tracepoint_allow_reentry
|
129
|
+
yield
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Byebug
|
4
|
+
module Helpers
|
5
|
+
#
|
6
|
+
# Utilities for interaction with files
|
7
|
+
#
|
8
|
+
module FileHelper
|
9
|
+
#
|
10
|
+
# Reads lines of source file +filename+ into an array
|
11
|
+
#
|
12
|
+
def get_lines(filename)
|
13
|
+
File.foreach(filename).reduce([]) { |acc, elem| acc << elem.chomp }
|
14
|
+
end
|
15
|
+
|
16
|
+
#
|
17
|
+
# Reads line number +lineno+ from file named +filename+
|
18
|
+
#
|
19
|
+
def get_line(filename, lineno)
|
20
|
+
File.open(filename) do |f|
|
21
|
+
f.gets until f.lineno == lineno - 1
|
22
|
+
f.gets
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
#
|
27
|
+
# Returns the number of lines in file +filename+ in a portable,
|
28
|
+
# one-line-at-a-time way.
|
29
|
+
#
|
30
|
+
def n_lines(filename)
|
31
|
+
File.foreach(filename).reduce(0) { |acc, _elem| acc + 1 }
|
32
|
+
end
|
33
|
+
|
34
|
+
#
|
35
|
+
# Regularize file name.
|
36
|
+
#
|
37
|
+
def normalize(filename)
|
38
|
+
return filename if virtual_file?(filename)
|
39
|
+
|
40
|
+
return File.basename(filename) if Setting[:basename]
|
41
|
+
|
42
|
+
File.exist?(filename) ? File.realpath(filename) : filename
|
43
|
+
end
|
44
|
+
|
45
|
+
#
|
46
|
+
# A short version of a long path
|
47
|
+
#
|
48
|
+
def shortpath(fullpath)
|
49
|
+
components = Pathname(fullpath).each_filename.to_a
|
50
|
+
return fullpath if components.size <= 2
|
51
|
+
|
52
|
+
File.join("...", components[-3..-1])
|
53
|
+
end
|
54
|
+
|
55
|
+
#
|
56
|
+
# True for special files like -e, false otherwise
|
57
|
+
#
|
58
|
+
def virtual_file?(name)
|
59
|
+
["(irb)", "-e", "(byebug)", "(eval)"].include?(name)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Byebug
|
4
|
+
module Helpers
|
5
|
+
#
|
6
|
+
# Utilities to assist frame navigation
|
7
|
+
#
|
8
|
+
module FrameHelper
|
9
|
+
def switch_to_frame(frame)
|
10
|
+
new_frame = index_from_start(frame)
|
11
|
+
return frame_err("c_frame") if Frame.new(context, new_frame).c_frame?
|
12
|
+
|
13
|
+
adjust_frame(new_frame)
|
14
|
+
end
|
15
|
+
|
16
|
+
def jump_frames(steps)
|
17
|
+
adjust_frame(navigate_to_frame(steps))
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def adjust_frame(new_frame)
|
23
|
+
return frame_err("too_low") if new_frame >= context.stack_size
|
24
|
+
return frame_err("too_high") if new_frame.negative?
|
25
|
+
|
26
|
+
context.frame = new_frame
|
27
|
+
processor.prev_line = nil
|
28
|
+
end
|
29
|
+
|
30
|
+
def navigate_to_frame(jump_no)
|
31
|
+
current_jumps = 0
|
32
|
+
current_pos = context.frame.pos
|
33
|
+
|
34
|
+
loop do
|
35
|
+
current_pos += direction(jump_no)
|
36
|
+
break if out_of_bounds?(current_pos)
|
37
|
+
|
38
|
+
next if Frame.new(context, current_pos).c_frame?
|
39
|
+
|
40
|
+
current_jumps += 1
|
41
|
+
break if current_jumps == jump_no.abs
|
42
|
+
end
|
43
|
+
|
44
|
+
current_pos
|
45
|
+
end
|
46
|
+
|
47
|
+
def out_of_bounds?(pos)
|
48
|
+
!(0...context.stack_size).cover?(pos)
|
49
|
+
end
|
50
|
+
|
51
|
+
def frame_err(msg)
|
52
|
+
errmsg(pr("frame.errors.#{msg}"))
|
53
|
+
end
|
54
|
+
|
55
|
+
#
|
56
|
+
# @param step [Integer] A positive or negative integer
|
57
|
+
#
|
58
|
+
# @return [Integer] +1 if step is positive / -1 if negative
|
59
|
+
#
|
60
|
+
def direction(step)
|
61
|
+
step / step.abs
|
62
|
+
end
|
63
|
+
|
64
|
+
#
|
65
|
+
# Convert a possibly negative index to a positive index from the start
|
66
|
+
# of the callstack. -1 is the last position in the stack and so on.
|
67
|
+
#
|
68
|
+
# @param i [Integer] Integer to be converted in a proper positive index.
|
69
|
+
#
|
70
|
+
def index_from_start(index)
|
71
|
+
index >= 0 ? index : context.stack_size + index
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Byebug
|
4
|
+
module Helpers
|
5
|
+
#
|
6
|
+
# Utilities to assist command parsing
|
7
|
+
#
|
8
|
+
module ParseHelper
|
9
|
+
#
|
10
|
+
# Parses +str+ of command +cmd+ as an integer between +min+ and +max+.
|
11
|
+
#
|
12
|
+
# If either +min+ or +max+ is nil, that value has no bound.
|
13
|
+
#
|
14
|
+
# @todo Remove the `cmd` parameter. It has nothing to do with the method's
|
15
|
+
# purpose.
|
16
|
+
#
|
17
|
+
def get_int(str, cmd, min = nil, max = nil)
|
18
|
+
return nil, pr("parse.errors.int.not_number", cmd: cmd, str: str) unless /\A-?[0-9]+\z/.match?(str)
|
19
|
+
|
20
|
+
int = str.to_i
|
21
|
+
if min && int < min
|
22
|
+
err = pr("parse.errors.int.too_low", cmd: cmd, str: str, min: min)
|
23
|
+
return nil, err
|
24
|
+
elsif max && int > max
|
25
|
+
err = pr("parse.errors.int.too_high", cmd: cmd, str: str, max: max)
|
26
|
+
return nil, err
|
27
|
+
end
|
28
|
+
|
29
|
+
int
|
30
|
+
end
|
31
|
+
|
32
|
+
#
|
33
|
+
# @return true if code is syntactically correct for Ruby, false otherwise
|
34
|
+
#
|
35
|
+
def syntax_valid?(code)
|
36
|
+
return true unless code
|
37
|
+
|
38
|
+
if defined?(RubyVM::InstructionSequence.compile)
|
39
|
+
without_stderr do
|
40
|
+
RubyVM::InstructionSequence.compile(code)
|
41
|
+
true
|
42
|
+
rescue SyntaxError
|
43
|
+
false
|
44
|
+
end
|
45
|
+
else
|
46
|
+
require "ripper" unless defined?(Ripper)
|
47
|
+
without_stderr do
|
48
|
+
!Ripper.sexp(code).nil?
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
#
|
54
|
+
# @return +str+ as an integer or 1 if +str+ is empty.
|
55
|
+
#
|
56
|
+
def parse_steps(str, cmd)
|
57
|
+
return 1 unless str
|
58
|
+
|
59
|
+
steps, err = get_int(str, cmd, 1)
|
60
|
+
return nil, err unless steps
|
61
|
+
|
62
|
+
steps
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
#
|
68
|
+
# Temporarily disable output to $stderr
|
69
|
+
#
|
70
|
+
def without_stderr
|
71
|
+
old_stderr = $stderr
|
72
|
+
$stderr = StringIO.new
|
73
|
+
|
74
|
+
yield
|
75
|
+
ensure
|
76
|
+
$stderr = old_stderr
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Byebug
|
4
|
+
module Helpers
|
5
|
+
#
|
6
|
+
# Utilities for managing gem paths
|
7
|
+
#
|
8
|
+
module PathHelper
|
9
|
+
def bin_file
|
10
|
+
@bin_file ||= File.join(root_path, "exe", "byebug")
|
11
|
+
end
|
12
|
+
|
13
|
+
def root_path
|
14
|
+
@root_path ||= File.expand_path(File.join("..", "..", ".."), __dir__)
|
15
|
+
end
|
16
|
+
|
17
|
+
def lib_files
|
18
|
+
@lib_files ||= glob_for("lib")
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_files
|
22
|
+
@test_files ||= glob_for("test")
|
23
|
+
end
|
24
|
+
|
25
|
+
def gem_files
|
26
|
+
@gem_files ||= [bin_file] + lib_files
|
27
|
+
end
|
28
|
+
|
29
|
+
def all_files
|
30
|
+
@all_files ||= gem_files + test_files
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def glob_for(dir)
|
36
|
+
Dir.glob(File.join(root_path, dir, "**", "*.rb"))
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Byebug
|
4
|
+
module Helpers
|
5
|
+
#
|
6
|
+
# Reflection utility
|
7
|
+
#
|
8
|
+
module ReflectionHelper
|
9
|
+
#
|
10
|
+
# List of "command" classes in the including module
|
11
|
+
#
|
12
|
+
def commands
|
13
|
+
constants(false)
|
14
|
+
.map { |const| const_get(const, false) }
|
15
|
+
.select { |c| c.is_a?(Class) && c.name =~ /[a-z]Command$/ }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Byebug
|
4
|
+
module Helpers
|
5
|
+
#
|
6
|
+
# Utilities for interaction with strings
|
7
|
+
#
|
8
|
+
module StringHelper
|
9
|
+
#
|
10
|
+
# Converts +str+ from an_underscored-or-dasherized_string to
|
11
|
+
# ACamelizedString.
|
12
|
+
#
|
13
|
+
def camelize(str)
|
14
|
+
str.dup.split(/[_-]/).map(&:capitalize).join("")
|
15
|
+
end
|
16
|
+
|
17
|
+
#
|
18
|
+
# Improves indentation and spacing in +str+ for readability in Byebug's
|
19
|
+
# command prompt.
|
20
|
+
#
|
21
|
+
def prettify(str)
|
22
|
+
"\n" + deindent(str) + "\n"
|
23
|
+
end
|
24
|
+
|
25
|
+
#
|
26
|
+
# Removes a number of leading whitespace for each input line.
|
27
|
+
#
|
28
|
+
def deindent(str, leading_spaces: 6)
|
29
|
+
str.gsub(/^ {#{leading_spaces}}/, "")
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|