byebug 3.4.2 → 3.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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