byebug 3.4.2 → 3.5.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.
@@ -34,9 +34,9 @@ module Byebug
34
34
 
35
35
  int = str.to_i
36
36
  if min && int < min
37
- return nil, "\"#{cmd}\" argument \"#{str}\" needs to be at least #{min}"
37
+ return min, "\"#{cmd}\" argument \"#{str}\" needs to be at least #{min}"
38
38
  elsif max && int > max
39
- return nil, "\"#{cmd}\" argument \"#{str}\" needs to be at most #{max}"
39
+ return max, "\"#{cmd}\" argument \"#{str}\" needs to be at most #{max}"
40
40
  end
41
41
 
42
42
  int
@@ -5,38 +5,98 @@ module Byebug
5
5
  # Handles byebug's history of commands.
6
6
  #
7
7
  class History
8
- class << self
9
- def load
10
- open(Setting[:histfile], 'r') do |file|
11
- file.each do |line|
12
- line.chomp!
13
- Readline::HISTORY << line
14
- end
15
- end if File.exist?(Setting[:histfile])
16
- end
8
+ attr_accessor :size
17
9
 
18
- def save
19
- open(Setting[:histfile], 'w') do |file|
20
- Readline::HISTORY.to_a.last(Setting[:histsize]).each do |line|
21
- file.puts line unless line.strip.empty?
22
- end
23
- end
24
- end
10
+ def initialize
11
+ self.size = 0
12
+ end
25
13
 
26
- def to_s(size = Setting[:histsize])
27
- rl_size = Readline::HISTORY.length
28
- n_entries = rl_size < size ? rl_size : size
14
+ #
15
+ # Restores history from disk.
16
+ #
17
+ def restore
18
+ return unless File.exist?(Setting[:histfile])
29
19
 
30
- first = rl_size - n_entries
31
- commands = Readline::HISTORY.to_a.last(n_entries)
20
+ File.readlines(Setting[:histfile]).reverse.each { |l| push(l.chomp) }
21
+ end
32
22
 
33
- s = ''
34
- commands.each_with_index do |command, index|
35
- s += format("%5d %s\n", first + index + 1, command)
36
- end
23
+ #
24
+ # Saves history to disk.
25
+ #
26
+ def save
27
+ n_cmds = Setting[:histsize] > self.size ? self.size : Setting[:histsize]
37
28
 
38
- s
29
+ open(Setting[:histfile], 'w') do |file|
30
+ n_cmds.times { file.puts(pop) }
39
31
  end
32
+
33
+ clear
34
+ end
35
+
36
+ #
37
+ # Discards history.
38
+ #
39
+ def clear
40
+ self.size.times { pop }
41
+ end
42
+
43
+ #
44
+ # Adds a new command to Readline's history.
45
+ #
46
+ def push(cmd)
47
+ self.size += 1
48
+ Readline::HISTORY.push(cmd)
49
+ end
50
+
51
+ #
52
+ # Removes a command from Readline's history.
53
+ #
54
+ def pop
55
+ self.size -= 1
56
+ Readline::HISTORY.pop
57
+ end
58
+
59
+ #
60
+ # Prints the requested numbers of history entries.
61
+ #
62
+ def to_s(n_cmds)
63
+ show_size = n_cmds ? specific_max_size(n_cmds) : default_max_size
64
+
65
+ commands = Readline::HISTORY.to_a.last(show_size)
66
+
67
+ (self.size - show_size + 1..self.size).to_a.zip(commands).map do |l|
68
+ format("%5d %s", l[0], l[1])
69
+ end.join("\n") + "\n"
70
+ end
71
+
72
+ #
73
+ # Max number of commands to be displayed when no size has been specified.
74
+ #
75
+ # Never more than Setting[:histsize].
76
+ #
77
+ def default_max_size
78
+ [Setting[:histsize], self.size].min
79
+ end
80
+
81
+ #
82
+ # Max number of commands to be displayed when a size has been specified.
83
+ #
84
+ # The only bound here is not showing more items than available.
85
+ #
86
+ def specific_max_size(number)
87
+ [self.size, number].min
88
+ end
89
+
90
+ #
91
+ # Whether a specific command should not be stored in history.
92
+ #
93
+ # For now, empty lines and consecutive duplicates.
94
+ #
95
+ def ignore?(buf)
96
+ return true if /^\s*$/ =~ buf
97
+ return false if Readline::HISTORY.length == 0
98
+
99
+ Readline::HISTORY[Readline::HISTORY.length - 1] == buf
40
100
  end
41
101
  end
42
102
  end
@@ -1,3 +1,5 @@
1
+ require 'byebug/history'
2
+
1
3
  #
2
4
  # Namespace for all of byebug's code
3
5
  #
@@ -8,10 +10,10 @@ module Byebug
8
10
  # Contains common functionality to all implemented interfaces.
9
11
  #
10
12
  class Interface
11
- attr_accessor :command_queue, :restart_file
13
+ attr_accessor :command_queue, :history
12
14
 
13
15
  def initialize
14
- @command_queue, @restart_file = [], nil
16
+ @command_queue, @history = [], History.new
15
17
  end
16
18
 
17
19
  #
@@ -21,6 +23,15 @@ module Byebug
21
23
  def errmsg(message)
22
24
  print("*** #{message}\n")
23
25
  end
26
+
27
+ protected
28
+
29
+ #
30
+ # Stores <cmd> in commands history.
31
+ #
32
+ def save_history(cmd)
33
+ @history.push(cmd) unless @history.ignore?(cmd)
34
+ end
24
35
  end
25
36
 
26
37
  require 'byebug/interfaces/local_interface'
@@ -1,17 +1,8 @@
1
- require 'byebug/history'
2
-
3
1
  module Byebug
4
2
  #
5
3
  # Interface class for standard byebug use.
6
4
  #
7
5
  class LocalInterface < Interface
8
- attr_reader :history
9
-
10
- def initialize
11
- super
12
- History.load
13
- end
14
-
15
6
  def read_command(prompt)
16
7
  readline(prompt, true)
17
8
  end
@@ -25,16 +16,17 @@ module Byebug
25
16
  end
26
17
 
27
18
  def close
28
- History.save
29
19
  end
30
20
 
31
21
  private
32
22
 
33
23
  def readline(prompt, hist)
34
- Readline.readline(prompt, hist)
24
+ line = Readline.readline(prompt, false)
35
25
  rescue Interrupt
36
- puts '^C'
26
+ puts('^C')
37
27
  retry
28
+ ensure
29
+ save_history(line) unless !hist
38
30
  end
39
31
  end
40
32
  end
@@ -5,12 +5,9 @@ module Byebug
5
5
  # Interface class for remote use of byebug.
6
6
  #
7
7
  class RemoteInterface < Interface
8
- attr_reader :history
9
-
10
8
  def initialize(socket)
11
9
  super()
12
10
  @socket = socket
13
- @history = History.new
14
11
  end
15
12
 
16
13
  def close
@@ -127,10 +127,11 @@ module Byebug
127
127
  Setting[:autolist] = false if ['(irb)', '-e'].include?(file)
128
128
 
129
129
  # Bind commands to the current state.
130
- commands = cmds.map { |cmd| cmd.new(state) }
131
-
132
- commands.select { |cmd| cmd.class.always_run >= run_level }
133
- .each { |cmd| cmd.execute }
130
+ commands = cmds.map do |cmd_class|
131
+ cmd = cmd_class.new(state)
132
+ cmd.execute if cmd.class.always_run >= run_level
133
+ cmd
134
+ end
134
135
 
135
136
  [state, commands]
136
137
  end
@@ -158,24 +159,24 @@ module Byebug
158
159
  # Handle byebug commands.
159
160
  #
160
161
  def process_commands(context, file, line)
161
- state, commands = always_run(context, file, line, 1)
162
+ state, commands = preloop(context, file, line)
162
163
 
163
- if Setting[:testing]
164
- Thread.current.thread_variable_set('state', state)
165
- else
166
- Thread.current.thread_variable_set('state', nil)
167
- end
164
+ repl(state, commands, context)
168
165
 
169
- preloop(commands, context)
170
- puts(state.location) if Setting[:autolist] == 0
166
+ postloop
167
+ end
171
168
 
169
+ #
170
+ # Main byebug's REPL
171
+ #
172
+ def repl(state, commands, context)
172
173
  until state.proceed?
173
174
  input = if @interface.command_queue.empty?
174
175
  @interface.read_command(prompt(context))
175
176
  else
176
177
  @interface.command_queue.shift
177
178
  end
178
- break unless input
179
+ return unless input
179
180
 
180
181
  if input == ''
181
182
  next unless @last_cmd
@@ -183,6 +184,7 @@ module Byebug
183
184
  else
184
185
  @last_cmd = input
185
186
  end
187
+
186
188
  split_commands(input).each do |cmd|
187
189
  one_cmd(commands, context, cmd)
188
190
  end
@@ -215,24 +217,45 @@ module Byebug
215
217
  end
216
218
 
217
219
  #
218
- # Tasks to do before processor loop
220
+ # Tasks to do before processor loop.
219
221
  #
220
- def preloop(_commands, context)
222
+ def preloop(context, file, line)
223
+ state, commands = always_run(context, file, line, 1)
224
+
225
+ if Setting[:testing]
226
+ Thread.current.thread_variable_set('state', state)
227
+ else
228
+ Thread.current.thread_variable_set('state', nil)
229
+ end
230
+
221
231
  @context_was_dead = true if context.dead? && !@context_was_dead
222
- return unless @context_was_dead
232
+ if @context_was_dead
233
+ puts 'The program finished.'
234
+ @context_was_dead = false
235
+ end
223
236
 
224
- puts 'The program finished.'
225
- @context_was_dead = false
237
+ puts(state.location) if Setting[:autolist] == 0
238
+
239
+ @interface.history.restore if Setting[:autosave]
240
+
241
+ [state, commands]
242
+ end
243
+
244
+ #
245
+ # Tasks to do after processor loop.
246
+ #
247
+ def postloop
248
+ Setting[:autosave] ? @interface.history.save : @interface.history.clear
226
249
  end
227
250
 
228
251
  class State
229
- attr_accessor :commands, :context, :display, :file, :frame_pos
230
- attr_accessor :interface, :line, :previous_line
252
+ attr_accessor :commands, :context, :display, :file, :frame_pos,
253
+ :interface, :line, :prev_line
231
254
 
232
255
  def initialize(commands, context, display, file, interface, line)
233
256
  @commands, @context, @display = commands, context, display
234
- @file, @interface, @line = file, interface, line
235
- @frame_pos, @previous_line, @proceed = 0, nil, false
257
+ @file, @frame_pos, @interface = file, 0, interface
258
+ @line, @prev_line, @proceed = line, nil, false
236
259
  end
237
260
 
238
261
  extend Forwardable
@@ -35,10 +35,7 @@ module Byebug
35
35
  # Used for restarts.
36
36
  #
37
37
  def debugged_program_from_argv
38
- if ARGV.empty?
39
- Byebug.puts 'You must specify a program to debug...'
40
- abort
41
- end
38
+ abort_with_err('You must specify a program to debug...') if ARGV.empty?
42
39
 
43
40
  prog_script_try = which(ARGV.first)
44
41
  if prog_script_try == which('ruby')
@@ -49,6 +46,11 @@ module Byebug
49
46
  prog_script_try
50
47
  end
51
48
 
49
+ def abort_with_err(msg)
50
+ Byebug.errmsg(msg)
51
+ abort
52
+ end
53
+
52
54
  #
53
55
  # Starts byebug to debug a program
54
56
  #
@@ -66,6 +68,7 @@ module Byebug
66
68
  end
67
69
 
68
70
  Byebug.debugged_program = debugged_program_from_argv
71
+ abort_with_err("The script doesn't exist") unless Byebug.debugged_program
69
72
 
70
73
  # Set up trace hook for byebug
71
74
  Byebug.start
@@ -1,3 +1,3 @@
1
1
  module Byebug
2
- VERSION = '3.4.2'
2
+ VERSION = '3.5.0'
3
3
  end
@@ -3,43 +3,59 @@ module Byebug
3
3
  def setup
4
4
  @example = -> do
5
5
  byebug
6
+ a = 2
7
+ a = 3
6
8
  end
7
9
 
8
10
  super
11
+ end
9
12
 
10
- @old_readline = Readline::HISTORY
11
- force_set_const(Readline, 'HISTORY', %w(aaa bbb ccc ddd))
13
+ def test_history_displays_latest_records_from_readline_history
14
+ enter 'show', 'history'
15
+ debug_proc(@example)
16
+ check_output_includes("1 show\n 2 history")
12
17
  end
13
18
 
14
- def teardown
15
- force_set_const(Readline, 'HISTORY', @old_readline)
19
+ def test_history_n_displays_whole_history_if_n_is_bigger_than_history_size
20
+ enter 'show', 'history 3'
21
+ debug_proc(@example)
22
+
23
+ check_output_includes("1 show\n 2 history 3")
16
24
  end
17
25
 
18
- def test_history_displays_latest_records_from_readline_history
19
- enter 'set histsize 3', 'history'
26
+ def test_history_n_displays_lastest_n_records_from_readline_history
27
+ enter 'show width', 'show autolist', 'history 2'
28
+ debug_proc(@example)
29
+
30
+ check_output_includes("2 show autolist\n 3 history 2")
31
+ end
32
+
33
+ def test_history_does_not_save_empty_commands
34
+ enter 'show', 'show width', '', 'history 3'
20
35
  debug_proc(@example)
21
- check_output_includes(/2 bbb\n 3 ccc\n 4 ddd/)
22
- check_output_doesnt_include(/1 aaa/)
36
+
37
+ check_output_includes("1 show\n 2 show width\n 3 history 3")
23
38
  end
24
39
 
25
- def test_history_displays_whole_history_if_max_size_is_bigger_than_readline
26
- enter 'set histsize 7', 'history'
40
+ def test_history_does_not_save_duplicated_consecutive_commands
41
+ enter 'show', 'show width', 'show width', 'history 3'
27
42
  debug_proc(@example)
28
- check_output_includes(/1 aaa\n 2 bbb\n 3 ccc\n 4 ddd/)
43
+
44
+ check_output_includes("1 show\n 2 show width\n 3 history 3")
29
45
  end
30
46
 
31
- def test_history_n_displays_lastest_n_records_from_readline_history
32
- enter 'history 2'
47
+ def test_cmds_from_previous_repls_are_remembered_if_autosave_enabled
48
+ enter 'set autosave', 'next', 'history 2'
33
49
  debug_proc(@example)
34
- check_output_includes(/3 ccc\n 4 ddd/)
35
- check_output_doesnt_include(/1 aaa\n 2 bbb/)
50
+
51
+ check_output_includes("2 next\n 3 history 2")
36
52
  end
37
53
 
38
- def test_history_with_autosave_disabled_does_not_show_records_from_readline
39
- enter 'set noautosave', 'history'
54
+ def test_cmds_from_previous_repls_are_not_remembered_if_autosave_disabled
55
+ enter 'set noautosave', 'next', 'history 2'
40
56
  debug_proc(@example)
41
- check_error_includes "Not currently saving history. " \
42
- 'Enable it with "set autosave"'
57
+
58
+ check_output_includes("1 history 2")
43
59
  end
44
60
  end
45
61
  end