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,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../subcommands"
4
+
5
+ require_relative "../commands/info/breakpoints"
6
+ require_relative "../commands/info/display"
7
+ require_relative "../commands/info/file"
8
+ require_relative "../commands/info/line"
9
+ require_relative "../commands/info/program"
10
+
11
+ module Byebug
12
+ #
13
+ # Shows info about different aspects of the debugger.
14
+ #
15
+ class InfoCommand < Command
16
+ include Subcommands
17
+
18
+ self.allow_in_control = true
19
+ self.allow_in_post_mortem = true
20
+
21
+ def self.regexp
22
+ /^\s* i(?:nfo)? (?:\s+ (.+))? \s*$/x
23
+ end
24
+
25
+ def self.description
26
+ <<-DESCRIPTION
27
+ info[ subcommand]
28
+
29
+ #{short_description}
30
+ DESCRIPTION
31
+ end
32
+
33
+ def self.short_description
34
+ "Shows short description and information about the program being debugged"
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../command"
4
+
5
+ module Byebug
6
+ #
7
+ # Interrupting execution of current thread.
8
+ #
9
+ class InterruptCommand < Command
10
+ self.allow_in_control = true
11
+
12
+ def self.regexp
13
+ /^\s*int(?:errupt)?\s*$/
14
+ end
15
+
16
+ def self.description
17
+ <<-DESCRIPTION
18
+ int[errupt]
19
+
20
+ #{short_description}
21
+ DESCRIPTION
22
+ end
23
+
24
+ def self.short_description
25
+ "Interrupts the program"
26
+ end
27
+
28
+ def execute
29
+ Byebug.start
30
+
31
+ Byebug.thread_context(Thread.main).interrupt
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../command"
4
+ require "irb"
5
+ require "English"
6
+
7
+ module Byebug
8
+ #
9
+ # Enter IRB from byebug's prompt
10
+ #
11
+ class IrbCommand < Command
12
+ self.allow_in_post_mortem = true
13
+
14
+ def self.regexp
15
+ /^\s* irb \s*$/x
16
+ end
17
+
18
+ def self.description
19
+ <<-DESCRIPTION
20
+ irb
21
+
22
+ #{short_description}
23
+ DESCRIPTION
24
+ end
25
+
26
+ def self.short_description
27
+ "Starts an IRB session"
28
+ end
29
+
30
+ def execute
31
+ return errmsg(pr("base.errors.only_local")) unless processor.interface.instance_of?(LocalInterface)
32
+
33
+ # @todo IRB tries to parse $ARGV so we must clear it (see #197). Add a
34
+ # test case for it so we can remove this comment.
35
+ with_clean_argv { IRB.start }
36
+ end
37
+
38
+ private
39
+
40
+ def with_clean_argv
41
+ saved_argv = $ARGV.dup
42
+ $ARGV.clear
43
+ begin
44
+ yield
45
+ ensure
46
+ $ARGV.concat(saved_argv)
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../command"
4
+
5
+ module Byebug
6
+ #
7
+ # Send custom signals to the debugged program.
8
+ #
9
+ class KillCommand < Command
10
+ self.allow_in_control = true
11
+
12
+ def self.regexp
13
+ /^\s* kill \s* (?:\s+(\S+))? \s*$/x
14
+ end
15
+
16
+ def self.description
17
+ <<-DESCRIPTION
18
+ kill[ signal]
19
+
20
+ #{short_description}
21
+
22
+ Equivalent of Process.kill(Process.pid)
23
+ DESCRIPTION
24
+ end
25
+
26
+ def self.short_description
27
+ "Sends a signal to the current process"
28
+ end
29
+
30
+ def execute
31
+ if @match[1]
32
+ signame = @match[1]
33
+
34
+ return errmsg("signal name #{signame} is not a signal I know about\n") unless Signal.list.member?(signame)
35
+ else
36
+ return unless confirm("Really kill? (y/n) ")
37
+
38
+ signame = "KILL"
39
+ end
40
+
41
+ processor.interface.close if signame == "KILL"
42
+ Process.kill(signame, Process.pid)
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,159 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../command"
4
+ require_relative "../source_file_formatter"
5
+ require_relative "../helpers/file"
6
+ require_relative "../helpers/parse"
7
+
8
+ module Byebug
9
+ #
10
+ # List parts of the source code.
11
+ #
12
+ class ListCommand < Command
13
+ include Helpers::FileHelper
14
+ include Helpers::ParseHelper
15
+
16
+ self.allow_in_post_mortem = true
17
+
18
+ def self.regexp
19
+ /^\s* l(?:ist)? (?:\s*([-=])|\s+(\S+))? \s*$/x
20
+ end
21
+
22
+ def self.description
23
+ <<-DESCRIPTION
24
+ l[ist][[-=]][ nn-mm]
25
+
26
+ #{short_description}
27
+
28
+ Lists lines forward from current line or from the place where code was
29
+ last listed. If "list-" is specified, lists backwards instead. If
30
+ "list=" is specified, lists from current line regardless of where code
31
+ was last listed. A line range can also be specified to list specific
32
+ sections of code.
33
+ DESCRIPTION
34
+ end
35
+
36
+ def self.short_description
37
+ "Lists lines of source code"
38
+ end
39
+
40
+ def execute
41
+ msg = "No sourcefile available for #{frame.file}"
42
+ raise(msg) unless File.exist?(frame.file)
43
+
44
+ b, e = range(@match[2])
45
+
46
+ display_lines(b, e)
47
+
48
+ processor.prev_line = b
49
+ end
50
+
51
+ private
52
+
53
+ #
54
+ # Line range to be printed by `list`.
55
+ #
56
+ # If <input> is set, range is parsed from it.
57
+ #
58
+ # Otherwise it's automatically chosen.
59
+ #
60
+ def range(input)
61
+ return auto_range(@match[1] || "+") unless input
62
+
63
+ b, e = parse_range(input)
64
+ raise("Invalid line range") unless valid_range?(b, e)
65
+
66
+ [b, e]
67
+ end
68
+
69
+ def valid_range?(first, last)
70
+ first <= last && (1..max_line).cover?(first) && (1..max_line).cover?(last)
71
+ end
72
+
73
+ #
74
+ # Set line range to be printed by list
75
+ #
76
+ # @return first line number to list
77
+ # @return last line number to list
78
+ #
79
+ def auto_range(direction)
80
+ prev_line = processor.prev_line
81
+
82
+ if direction == "=" || prev_line.nil?
83
+ source_file_formatter.range_around(frame.line)
84
+ else
85
+ source_file_formatter.range_from(move(prev_line, size, direction))
86
+ end
87
+ end
88
+
89
+ def parse_range(input)
90
+ first, err = get_int(lower_bound(input), "List", 1, max_line)
91
+ raise(err) unless first
92
+
93
+ if upper_bound(input)
94
+ last, err = get_int(upper_bound(input), "List", 1, max_line)
95
+ raise(err) unless last
96
+
97
+ last = amend_final(last)
98
+ else
99
+ first -= (size / 2)
100
+ end
101
+
102
+ [first, last || move(first, size - 1)]
103
+ end
104
+
105
+ def move(line, size, direction = "+")
106
+ line.send(direction, size)
107
+ end
108
+
109
+ #
110
+ # Show a range of lines in the current file.
111
+ #
112
+ # @param min [Integer] Lower bound
113
+ # @param max [Integer] Upper bound
114
+ #
115
+ def display_lines(min, max)
116
+ puts "\n[#{min}, #{max}] in #{frame.file}"
117
+
118
+ puts source_file_formatter.lines(min, max).join
119
+ end
120
+
121
+ #
122
+ # @param range [String] A string with an integer range format
123
+ #
124
+ # @return [String] The lower bound of the given range
125
+ #
126
+ def lower_bound(range)
127
+ split_range(range)[0]
128
+ end
129
+
130
+ #
131
+ # @param range [String] A string with an integer range format
132
+ #
133
+ # @return [String] The upper bound of the given range
134
+ #
135
+ def upper_bound(range)
136
+ split_range(range)[1]
137
+ end
138
+
139
+ #
140
+ # @param str [String] A string with an integer range format
141
+ #
142
+ # @return [Array] The upper & lower bounds of the given range
143
+ #
144
+ def split_range(str)
145
+ str.split(/[-,]/)
146
+ end
147
+
148
+ extend Forwardable
149
+
150
+ def_delegators :source_file_formatter, :amend_final, :size, :max_line
151
+
152
+ def source_file_formatter
153
+ @source_file_formatter ||= SourceFileFormatter.new(
154
+ frame.file,
155
+ ->(n) { n == frame.line ? "=>" : " " }
156
+ )
157
+ end
158
+ end
159
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../command"
4
+ require_relative "../helpers/eval"
5
+
6
+ module Byebug
7
+ #
8
+ # Show methods of specific classes/modules/objects.
9
+ #
10
+ class MethodCommand < Command
11
+ include Helpers::EvalHelper
12
+
13
+ self.allow_in_post_mortem = true
14
+
15
+ def self.regexp
16
+ /^\s* m(?:ethod)? \s+ (i(:?nstance)?\s+)?/x
17
+ end
18
+
19
+ def self.description
20
+ <<-DESCRIPTION
21
+ m[ethod] (i[nstance][ <obj>]|<class|module>)
22
+
23
+ #{short_description}
24
+
25
+ When invoked with "instance", shows instance methods of the object
26
+ specified as argument or of self no object was specified.
27
+
28
+ When invoked only with a class or module, shows class methods of the
29
+ class or module specified as argument.
30
+ DESCRIPTION
31
+ end
32
+
33
+ def self.short_description
34
+ "Shows methods of an object, class or module"
35
+ end
36
+
37
+ def execute
38
+ obj = warning_eval(@match.post_match)
39
+
40
+ result =
41
+ if @match[1]
42
+ prc("method.methods", obj.methods.sort) { |item, _| { name: item } }
43
+ elsif !obj.is_a?(Module)
44
+ pr("variable.errors.not_module", object: @match.post_match)
45
+ else
46
+ prc("method.methods", obj.instance_methods(false).sort) do |item, _|
47
+ { name: item }
48
+ end
49
+ end
50
+ puts result
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../command"
4
+ require_relative "../helpers/parse"
5
+
6
+ module Byebug
7
+ #
8
+ # Implements the next functionality.
9
+ #
10
+ # Allows the user the continue execution until the next instruction in the
11
+ # current frame.
12
+ #
13
+ class NextCommand < Command
14
+ include Helpers::ParseHelper
15
+
16
+ def self.regexp
17
+ /^\s* n(?:ext)? (?:\s+(\S+))? \s*$/x
18
+ end
19
+
20
+ def self.description
21
+ <<-DESCRIPTION
22
+ n[ext][ nnn]
23
+
24
+ #{short_description}
25
+ DESCRIPTION
26
+ end
27
+
28
+ def self.short_description
29
+ "Runs one or more lines of code"
30
+ end
31
+
32
+ def execute
33
+ steps, err = parse_steps(@match[1], "Next")
34
+ return errmsg(err) unless steps
35
+
36
+ context.step_over(steps, context.frame.pos)
37
+ processor.proceed!
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../command"
4
+ require_relative "../helpers/eval"
5
+
6
+ module Byebug
7
+ #
8
+ # Enter Pry from byebug's prompt
9
+ #
10
+ class PryCommand < Command
11
+ self.allow_in_post_mortem = true
12
+
13
+ def self.regexp
14
+ /^\s* pry \s*$/x
15
+ end
16
+
17
+ def self.description
18
+ <<-DESCRIPTION
19
+ pry
20
+
21
+ #{short_description}
22
+ DESCRIPTION
23
+ end
24
+
25
+ def self.short_description
26
+ "Starts a Pry session"
27
+ end
28
+
29
+ def execute
30
+ return errmsg(pr("base.errors.only_local")) unless processor.interface.instance_of?(LocalInterface)
31
+
32
+ begin
33
+ require "pry"
34
+ rescue LoadError
35
+ return errmsg(pr("pry.errors.not_installed"))
36
+ end
37
+
38
+ Pry.start(context.frame._binding)
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../command"
4
+
5
+ module Byebug
6
+ #
7
+ # Exit from byebug.
8
+ #
9
+ class QuitCommand < Command
10
+ self.allow_in_control = true
11
+ self.allow_in_post_mortem = true
12
+
13
+ def self.regexp
14
+ /^\s* q(?:uit)? \s* (?:(!|\s+unconditionally))? \s*$/x
15
+ end
16
+
17
+ def self.description
18
+ <<-DESCRIPTION
19
+ q[uit][!| unconditionally]
20
+
21
+ #{short_description}
22
+
23
+ Normally we prompt before exiting. However, if the parameter
24
+ "unconditionally" is given or command is suffixed with "!", we exit
25
+ without asking further questions.
26
+ DESCRIPTION
27
+ end
28
+
29
+ def self.short_description
30
+ "Exits byebug"
31
+ end
32
+
33
+ def execute
34
+ return unless @match[1] || confirm(pr("quit.confirmations.really"))
35
+
36
+ processor.interface.autosave
37
+ processor.interface.close
38
+
39
+ Process.exit!
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../command"
4
+ require_relative "../helpers/bin"
5
+ require_relative "../helpers/path"
6
+ require "shellwords"
7
+ require "English"
8
+ require "rbconfig"
9
+
10
+ module Byebug
11
+ #
12
+ # Restart debugged program from within byebug.
13
+ #
14
+ class RestartCommand < Command
15
+ include Helpers::BinHelper
16
+ include Helpers::PathHelper
17
+
18
+ self.allow_in_control = true
19
+ self.allow_in_post_mortem = true
20
+
21
+ def self.regexp
22
+ /^\s* restart (?:\s+(?<args>.+))? \s*$/x
23
+ end
24
+
25
+ def self.description
26
+ <<-DESCRIPTION
27
+ restart [args]
28
+
29
+ #{short_description}
30
+
31
+ This is a re-exec - all byebug state is lost. If command arguments are
32
+ passed those are used.
33
+ DESCRIPTION
34
+ end
35
+
36
+ def self.short_description
37
+ "Restarts the debugged program"
38
+ end
39
+
40
+ def execute
41
+ cmd = [$PROGRAM_NAME]
42
+
43
+ cmd = prepend_byebug_bin(cmd)
44
+ cmd = prepend_ruby_bin(cmd)
45
+
46
+ cmd += (@match[:args] ? @match[:args].shellsplit : $ARGV)
47
+
48
+ puts pr("restart.success", cmd: cmd.shelljoin)
49
+ Kernel.exec(*cmd)
50
+ end
51
+
52
+ private
53
+
54
+ def prepend_byebug_bin(cmd)
55
+ cmd.unshift(bin_file) if Byebug.mode == :standalone
56
+ cmd
57
+ end
58
+
59
+ def prepend_ruby_bin(cmd)
60
+ cmd.unshift(RbConfig.ruby) if which("ruby") != which(cmd.first)
61
+ cmd
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../command"
4
+
5
+ module Byebug
6
+ #
7
+ # Save current settings to use them in another debug session.
8
+ #
9
+ class SaveCommand < Command
10
+ self.allow_in_control = true
11
+ self.allow_in_post_mortem = true
12
+
13
+ def self.regexp
14
+ /^\s* sa(?:ve)? (?:\s+(\S+))? \s*$/x
15
+ end
16
+
17
+ def self.description
18
+ <<-DESCRIPTION
19
+ save[ FILE]
20
+
21
+ #{short_description}
22
+
23
+ Byebug state is saved as a script file. This includes breakpoints,
24
+ catchpoints, display expressions and some settings. If no filename is
25
+ given, byebug will fabricate one.
26
+
27
+ Use the "source" command in another debug session to restore the saved
28
+ file.
29
+ DESCRIPTION
30
+ end
31
+
32
+ def self.short_description
33
+ "Saves current byebug session to a file"
34
+ end
35
+
36
+ def execute
37
+ file = File.open(@match[1] || Setting[:savefile], "w")
38
+
39
+ save_breakpoints(file)
40
+ save_catchpoints(file)
41
+ save_displays(file)
42
+ save_settings(file)
43
+
44
+ print pr("save.messages.done", path: file.path)
45
+ file.close
46
+ end
47
+
48
+ private
49
+
50
+ def save_breakpoints(file)
51
+ Byebug.breakpoints.each do |b|
52
+ file.puts "break #{b.source}:#{b.pos}#{" if #{b.expr}" if b.expr}"
53
+ end
54
+ end
55
+
56
+ def save_catchpoints(file)
57
+ Byebug.catchpoints.each_key do |c|
58
+ file.puts "catch #{c}"
59
+ end
60
+ end
61
+
62
+ def save_displays(file)
63
+ Byebug.displays.each { |d| file.puts "display #{d[1]}" if d[0] }
64
+ end
65
+
66
+ def save_settings(file)
67
+ %w[autoirb autolist basename].each do |setting|
68
+ file.puts "set #{setting} #{Setting[setting.to_sym]}"
69
+ end
70
+ end
71
+ end
72
+ end