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.
Files changed (132) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +954 -0
  3. data/CONTRIBUTING.md +58 -0
  4. data/GUIDE.md +1806 -0
  5. data/LICENSE +23 -0
  6. data/README.md +199 -0
  7. data/exe/byebug +6 -0
  8. data/ext/byebug/breakpoint.c +521 -0
  9. data/ext/byebug/byebug.c +900 -0
  10. data/ext/byebug/byebug.h +145 -0
  11. data/ext/byebug/context.c +687 -0
  12. data/ext/byebug/extconf.rb +12 -0
  13. data/ext/byebug/locker.c +96 -0
  14. data/ext/byebug/threads.c +241 -0
  15. data/lib/byebug/attacher.rb +48 -0
  16. data/lib/byebug/breakpoint.rb +94 -0
  17. data/lib/byebug/command.rb +111 -0
  18. data/lib/byebug/command_list.rb +34 -0
  19. data/lib/byebug/commands/break.rb +114 -0
  20. data/lib/byebug/commands/catch.rb +78 -0
  21. data/lib/byebug/commands/condition.rb +55 -0
  22. data/lib/byebug/commands/continue.rb +68 -0
  23. data/lib/byebug/commands/debug.rb +38 -0
  24. data/lib/byebug/commands/delete.rb +55 -0
  25. data/lib/byebug/commands/disable/breakpoints.rb +42 -0
  26. data/lib/byebug/commands/disable/display.rb +43 -0
  27. data/lib/byebug/commands/disable.rb +33 -0
  28. data/lib/byebug/commands/display.rb +66 -0
  29. data/lib/byebug/commands/down.rb +45 -0
  30. data/lib/byebug/commands/edit.rb +69 -0
  31. data/lib/byebug/commands/enable/breakpoints.rb +42 -0
  32. data/lib/byebug/commands/enable/display.rb +43 -0
  33. data/lib/byebug/commands/enable.rb +33 -0
  34. data/lib/byebug/commands/finish.rb +57 -0
  35. data/lib/byebug/commands/frame.rb +57 -0
  36. data/lib/byebug/commands/help.rb +64 -0
  37. data/lib/byebug/commands/history.rb +39 -0
  38. data/lib/byebug/commands/info/breakpoints.rb +65 -0
  39. data/lib/byebug/commands/info/display.rb +49 -0
  40. data/lib/byebug/commands/info/file.rb +80 -0
  41. data/lib/byebug/commands/info/line.rb +35 -0
  42. data/lib/byebug/commands/info/program.rb +49 -0
  43. data/lib/byebug/commands/info.rb +37 -0
  44. data/lib/byebug/commands/interrupt.rb +34 -0
  45. data/lib/byebug/commands/irb.rb +50 -0
  46. data/lib/byebug/commands/kill.rb +45 -0
  47. data/lib/byebug/commands/list.rb +159 -0
  48. data/lib/byebug/commands/method.rb +53 -0
  49. data/lib/byebug/commands/next.rb +40 -0
  50. data/lib/byebug/commands/pry.rb +41 -0
  51. data/lib/byebug/commands/quit.rb +42 -0
  52. data/lib/byebug/commands/restart.rb +64 -0
  53. data/lib/byebug/commands/save.rb +72 -0
  54. data/lib/byebug/commands/set.rb +79 -0
  55. data/lib/byebug/commands/show.rb +45 -0
  56. data/lib/byebug/commands/skip.rb +85 -0
  57. data/lib/byebug/commands/source.rb +40 -0
  58. data/lib/byebug/commands/step.rb +40 -0
  59. data/lib/byebug/commands/thread/current.rb +37 -0
  60. data/lib/byebug/commands/thread/list.rb +43 -0
  61. data/lib/byebug/commands/thread/resume.rb +45 -0
  62. data/lib/byebug/commands/thread/stop.rb +43 -0
  63. data/lib/byebug/commands/thread/switch.rb +46 -0
  64. data/lib/byebug/commands/thread.rb +34 -0
  65. data/lib/byebug/commands/tracevar.rb +54 -0
  66. data/lib/byebug/commands/undisplay.rb +51 -0
  67. data/lib/byebug/commands/untracevar.rb +36 -0
  68. data/lib/byebug/commands/up.rb +45 -0
  69. data/lib/byebug/commands/var/all.rb +41 -0
  70. data/lib/byebug/commands/var/args.rb +39 -0
  71. data/lib/byebug/commands/var/const.rb +49 -0
  72. data/lib/byebug/commands/var/global.rb +37 -0
  73. data/lib/byebug/commands/var/instance.rb +39 -0
  74. data/lib/byebug/commands/var/local.rb +39 -0
  75. data/lib/byebug/commands/var.rb +37 -0
  76. data/lib/byebug/commands/where.rb +64 -0
  77. data/lib/byebug/commands.rb +40 -0
  78. data/lib/byebug/context.rb +157 -0
  79. data/lib/byebug/core.rb +115 -0
  80. data/lib/byebug/errors.rb +29 -0
  81. data/lib/byebug/frame.rb +185 -0
  82. data/lib/byebug/helpers/bin.rb +47 -0
  83. data/lib/byebug/helpers/eval.rb +134 -0
  84. data/lib/byebug/helpers/file.rb +63 -0
  85. data/lib/byebug/helpers/frame.rb +75 -0
  86. data/lib/byebug/helpers/parse.rb +80 -0
  87. data/lib/byebug/helpers/path.rb +40 -0
  88. data/lib/byebug/helpers/reflection.rb +19 -0
  89. data/lib/byebug/helpers/string.rb +33 -0
  90. data/lib/byebug/helpers/thread.rb +67 -0
  91. data/lib/byebug/helpers/toggle.rb +62 -0
  92. data/lib/byebug/helpers/var.rb +70 -0
  93. data/lib/byebug/history.rb +130 -0
  94. data/lib/byebug/interface.rb +146 -0
  95. data/lib/byebug/interfaces/local_interface.rb +63 -0
  96. data/lib/byebug/interfaces/remote_interface.rb +50 -0
  97. data/lib/byebug/interfaces/script_interface.rb +33 -0
  98. data/lib/byebug/interfaces/test_interface.rb +67 -0
  99. data/lib/byebug/option_setter.rb +95 -0
  100. data/lib/byebug/printers/base.rb +68 -0
  101. data/lib/byebug/printers/plain.rb +44 -0
  102. data/lib/byebug/printers/texts/base.yml +115 -0
  103. data/lib/byebug/printers/texts/plain.yml +33 -0
  104. data/lib/byebug/processors/command_processor.rb +173 -0
  105. data/lib/byebug/processors/control_processor.rb +24 -0
  106. data/lib/byebug/processors/post_mortem_processor.rb +18 -0
  107. data/lib/byebug/processors/script_processor.rb +49 -0
  108. data/lib/byebug/remote/client.rb +57 -0
  109. data/lib/byebug/remote/server.rb +47 -0
  110. data/lib/byebug/remote.rb +85 -0
  111. data/lib/byebug/runner.rb +198 -0
  112. data/lib/byebug/setting.rb +79 -0
  113. data/lib/byebug/settings/autoirb.rb +29 -0
  114. data/lib/byebug/settings/autolist.rb +29 -0
  115. data/lib/byebug/settings/autopry.rb +29 -0
  116. data/lib/byebug/settings/autosave.rb +17 -0
  117. data/lib/byebug/settings/basename.rb +16 -0
  118. data/lib/byebug/settings/callstyle.rb +20 -0
  119. data/lib/byebug/settings/fullpath.rb +16 -0
  120. data/lib/byebug/settings/histfile.rb +20 -0
  121. data/lib/byebug/settings/histsize.rb +20 -0
  122. data/lib/byebug/settings/linetrace.rb +22 -0
  123. data/lib/byebug/settings/listsize.rb +21 -0
  124. data/lib/byebug/settings/post_mortem.rb +27 -0
  125. data/lib/byebug/settings/savefile.rb +20 -0
  126. data/lib/byebug/settings/stack_on_error.rb +15 -0
  127. data/lib/byebug/settings/width.rb +20 -0
  128. data/lib/byebug/source_file_formatter.rb +71 -0
  129. data/lib/byebug/subcommands.rb +54 -0
  130. data/lib/byebug/version.rb +8 -0
  131. data/lib/byebug.rb +3 -0
  132. 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
@@ -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