irb 1.6.4 → 1.8.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.
data/lib/irb/cmd/ls.rb CHANGED
@@ -1,7 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "reline"
4
+ require "stringio"
4
5
  require_relative "nop"
6
+ require_relative "../pager"
5
7
  require_relative "../color"
6
8
 
7
9
  module IRB
@@ -33,14 +35,18 @@ module IRB
33
35
  o.dump("instance variables", obj.instance_variables)
34
36
  o.dump("class variables", klass.class_variables)
35
37
  o.dump("locals", locals)
36
- nil
38
+ o.print_result
37
39
  end
38
40
 
39
41
  def dump_methods(o, klass, obj)
40
42
  singleton_class = begin obj.singleton_class; rescue TypeError; nil end
41
43
  dumped_mods = Array.new
44
+ ancestors = klass.ancestors
45
+ ancestors = ancestors.reject { |c| c >= Object } if klass < Object
46
+ singleton_ancestors = (singleton_class&.ancestors || []).reject { |c| c >= Class }
47
+
42
48
  # singleton_class' ancestors should be at the front
43
- maps = class_method_map(singleton_class&.ancestors || [], dumped_mods) + class_method_map(klass.ancestors, dumped_mods)
49
+ maps = class_method_map(singleton_ancestors, dumped_mods) + class_method_map(ancestors, dumped_mods)
44
50
  maps.each do |mod, methods|
45
51
  name = mod == singleton_class ? "#{klass}.methods" : "#{mod}#methods"
46
52
  o.dump(name, methods)
@@ -49,7 +55,6 @@ module IRB
49
55
 
50
56
  def class_method_map(classes, dumped_mods)
51
57
  dumped_methods = Array.new
52
- classes = classes.reject { |mod| mod >= Object }
53
58
  classes.map do |mod|
54
59
  next if dumped_mods.include? mod
55
60
 
@@ -74,6 +79,11 @@ module IRB
74
79
  def initialize(grep: nil)
75
80
  @grep = grep
76
81
  @line_width = screen_width - MARGIN.length # right padding
82
+ @io = StringIO.new
83
+ end
84
+
85
+ def print_result
86
+ Pager.page_content(@io.string)
77
87
  end
78
88
 
79
89
  def dump(name, strs)
@@ -82,12 +92,12 @@ module IRB
82
92
  return if strs.empty?
83
93
 
84
94
  # Attempt a single line
85
- print "#{Color.colorize(name, [:BOLD, :BLUE])}: "
95
+ @io.print "#{Color.colorize(name, [:BOLD, :BLUE])}: "
86
96
  if fits_on_line?(strs, cols: strs.size, offset: "#{name}: ".length)
87
- puts strs.join(MARGIN)
97
+ @io.puts strs.join(MARGIN)
88
98
  return
89
99
  end
90
- puts
100
+ @io.puts
91
101
 
92
102
  # Dump with the largest # of columns that fits on a line
93
103
  cols = strs.size
@@ -96,7 +106,7 @@ module IRB
96
106
  end
97
107
  widths = col_widths(strs, cols: cols)
98
108
  strs.each_slice(cols) do |ss|
99
- puts ss.map.with_index { |s, i| "#{MARGIN}%-#{widths[i]}s" % s }.join
109
+ @io.puts ss.map.with_index { |s, i| "#{MARGIN}%-#{widths[i]}s" % s }.join
100
110
  end
101
111
  end
102
112
 
data/lib/irb/cmd/nop.rb CHANGED
@@ -30,23 +30,19 @@ module IRB
30
30
  end
31
31
  end
32
32
 
33
- def self.execute(conf, *opts, **kwargs, &block)
34
- command = new(conf)
33
+ def self.execute(irb_context, *opts, **kwargs, &block)
34
+ command = new(irb_context)
35
35
  command.execute(*opts, **kwargs, &block)
36
36
  rescue CommandArgumentError => e
37
37
  puts e.message
38
38
  end
39
39
 
40
- def initialize(conf)
41
- @irb_context = conf
40
+ def initialize(irb_context)
41
+ @irb_context = irb_context
42
42
  end
43
43
 
44
44
  attr_reader :irb_context
45
45
 
46
- def irb
47
- @irb_context.irb
48
- end
49
-
50
46
  def execute(*opts)
51
47
  #nop
52
48
  end
@@ -12,7 +12,7 @@ module IRB
12
12
 
13
13
  module ExtendCommand
14
14
  class Workspaces < Nop
15
- category "IRB"
15
+ category "Workspace"
16
16
  description "Show workspaces."
17
17
 
18
18
  def execute(*obj)
@@ -21,7 +21,7 @@ module IRB
21
21
  end
22
22
 
23
23
  class PushWorkspace < Workspaces
24
- category "IRB"
24
+ category "Workspace"
25
25
  description "Push an object to the workspace stack."
26
26
 
27
27
  def execute(*obj)
@@ -31,7 +31,7 @@ module IRB
31
31
  end
32
32
 
33
33
  class PopWorkspace < Workspaces
34
- category "IRB"
34
+ category "Workspace"
35
35
  description "Pop a workspace from the workspace stack."
36
36
 
37
37
  def execute(*obj)
@@ -2,6 +2,7 @@
2
2
 
3
3
  require "stringio"
4
4
  require_relative "nop"
5
+ require_relative "../pager"
5
6
 
6
7
  module IRB
7
8
  # :stopdoc:
@@ -14,7 +15,17 @@ module IRB
14
15
  def execute(*args)
15
16
  commands_info = IRB::ExtendCommandBundle.all_commands_info
16
17
  commands_grouped_by_categories = commands_info.group_by { |cmd| cmd[:category] }
17
- longest_cmd_name_length = commands_info.map { |c| c[:display_name] }.max { |a, b| a.length <=> b.length }.length
18
+
19
+ if irb_context.with_debugger
20
+ # Remove the original "Debugging" category
21
+ commands_grouped_by_categories.delete("Debugging")
22
+ # Remove the `help` command as it's delegated to the debugger
23
+ commands_grouped_by_categories["Context"].delete_if { |cmd| cmd[:display_name] == :help }
24
+ # Add an empty "Debugging (from debug.gem)" category at the end
25
+ commands_grouped_by_categories["Debugging (from debug.gem)"] = []
26
+ end
27
+
28
+ longest_cmd_name_length = commands_info.map { |c| c[:display_name].length }.max
18
29
 
19
30
  output = StringIO.new
20
31
 
@@ -28,9 +39,12 @@ module IRB
28
39
  output.puts
29
40
  end
30
41
 
31
- puts output.string
42
+ # Append the debugger help at the end
43
+ if irb_context.with_debugger
44
+ output.puts DEBUGGER__.help
45
+ end
32
46
 
33
- nil
47
+ Pager.page_content(output.string)
34
48
  end
35
49
  end
36
50
  end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "nop"
4
+
5
+ module IRB
6
+ module ExtendCommand
7
+ class ShowDoc < Nop
8
+ class << self
9
+ def transform_args(args)
10
+ # Return a string literal as is for backward compatibility
11
+ if args.empty? || string_literal?(args)
12
+ args
13
+ else # Otherwise, consider the input as a String for convenience
14
+ args.strip.dump
15
+ end
16
+ end
17
+ end
18
+
19
+ category "Context"
20
+ description "Enter the mode to look up RI documents."
21
+
22
+ def execute(*names)
23
+ require 'rdoc/ri/driver'
24
+
25
+ unless ShowDoc.const_defined?(:Ri)
26
+ opts = RDoc::RI::Driver.process_args([])
27
+ ShowDoc.const_set(:Ri, RDoc::RI::Driver.new(opts))
28
+ end
29
+
30
+ if names.empty?
31
+ Ri.interactive
32
+ else
33
+ names.each do |name|
34
+ begin
35
+ Ri.display_name(name.to_s)
36
+ rescue RDoc::RI::Error
37
+ puts $!.message
38
+ end
39
+ end
40
+ end
41
+
42
+ nil
43
+ rescue LoadError, SystemExit
44
+ warn "Can't display document because `rdoc` is not installed."
45
+ end
46
+ end
47
+ end
48
+ end
@@ -1,12 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "nop"
4
+ require_relative "../source_finder"
4
5
  require_relative "../color"
5
- require_relative "../ruby-lex"
6
6
 
7
7
  module IRB
8
- # :stopdoc:
9
-
10
8
  module ExtendCommand
11
9
  class ShowSource < Nop
12
10
  category "Context"
@@ -21,50 +19,6 @@ module IRB
21
19
  args.strip.dump
22
20
  end
23
21
  end
24
-
25
- def find_source(str, irb_context)
26
- case str
27
- when /\A[A-Z]\w*(::[A-Z]\w*)*\z/ # Const::Name
28
- eval(str, irb_context.workspace.binding) # trigger autoload
29
- base = irb_context.workspace.binding.receiver.yield_self { |r| r.is_a?(Module) ? r : Object }
30
- file, line = base.const_source_location(str)
31
- when /\A(?<owner>[A-Z]\w*(::[A-Z]\w*)*)#(?<method>[^ :.]+)\z/ # Class#method
32
- owner = eval(Regexp.last_match[:owner], irb_context.workspace.binding)
33
- method = Regexp.last_match[:method]
34
- if owner.respond_to?(:instance_method) && owner.instance_methods.include?(method.to_sym)
35
- file, line = owner.instance_method(method).source_location
36
- end
37
- when /\A((?<receiver>.+)(\.|::))?(?<method>[^ :.]+)\z/ # method, receiver.method, receiver::method
38
- receiver = eval(Regexp.last_match[:receiver] || 'self', irb_context.workspace.binding)
39
- method = Regexp.last_match[:method]
40
- file, line = receiver.method(method).source_location if receiver.respond_to?(method)
41
- end
42
- if file && line
43
- Source.new(file: file, first_line: line, last_line: find_end(file, line, irb_context))
44
- end
45
- end
46
-
47
- private
48
-
49
- def find_end(file, first_line, irb_context)
50
- return first_line unless File.exist?(file)
51
- lex = RubyLex.new(irb_context)
52
- lines = File.read(file).lines[(first_line - 1)..-1]
53
- tokens = RubyLex.ripper_lex_without_warning(lines.join)
54
- prev_tokens = []
55
-
56
- # chunk with line number
57
- tokens.chunk { |tok| tok.pos[0] }.each do |lnum, chunk|
58
- code = lines[0..lnum].join
59
- prev_tokens.concat chunk
60
- continue = lex.process_continue(prev_tokens)
61
- code_block_open = lex.check_code_block(code, prev_tokens)
62
- if !continue && !code_block_open
63
- return first_line + lnum
64
- end
65
- end
66
- first_line
67
- end
68
22
  end
69
23
 
70
24
  def execute(str = nil)
@@ -73,8 +27,9 @@ module IRB
73
27
  return
74
28
  end
75
29
 
76
- source = self.class.find_source(str, @irb_context)
77
- if source && File.exist?(source.file)
30
+ source = SourceFinder.new(@irb_context).find_source(str)
31
+
32
+ if source
78
33
  show_source(source)
79
34
  else
80
35
  puts "Error: Couldn't locate a definition for #{str}"
@@ -84,7 +39,6 @@ module IRB
84
39
 
85
40
  private
86
41
 
87
- # @param [IRB::ExtendCommand::ShowSource::Source] source
88
42
  def show_source(source)
89
43
  puts
90
44
  puts "#{bold("From")}: #{source.file}:#{source.first_line}"
@@ -97,16 +51,6 @@ module IRB
97
51
  def bold(str)
98
52
  Color.colorize(str, [:BOLD])
99
53
  end
100
-
101
- Source = Struct.new(
102
- :file, # @param [String] - file name
103
- :first_line, # @param [String] - first line
104
- :last_line, # @param [String] - last line
105
- keyword_init: true,
106
- )
107
- private_constant :Source
108
54
  end
109
55
  end
110
-
111
- # :startdoc:
112
56
  end
@@ -11,52 +11,95 @@ module IRB
11
11
 
12
12
  module ExtendCommand
13
13
  class MultiIRBCommand < Nop
14
- def initialize(conf)
15
- super
14
+ def execute(*args)
16
15
  extend_irb_context
17
16
  end
18
17
 
19
18
  private
20
19
 
20
+ def print_deprecated_warning
21
+ warn <<~MSG
22
+ Multi-irb commands are deprecated and will be removed in IRB 2.0.0. Please use workspace commands instead.
23
+ If you have any use case for multi-irb, please leave a comment at https://github.com/ruby/irb/issues/653
24
+ MSG
25
+ end
26
+
21
27
  def extend_irb_context
22
28
  # this extension patches IRB context like IRB.CurrentContext
23
29
  require_relative "../ext/multi-irb"
24
30
  end
31
+
32
+ def print_debugger_warning
33
+ warn "Multi-IRB commands are not available when the debugger is enabled."
34
+ end
25
35
  end
26
36
 
27
37
  class IrbCommand < MultiIRBCommand
28
- category "IRB"
38
+ category "Multi-irb (DEPRECATED)"
29
39
  description "Start a child IRB."
30
40
 
31
41
  def execute(*obj)
42
+ print_deprecated_warning
43
+
44
+ if irb_context.with_debugger
45
+ print_debugger_warning
46
+ return
47
+ end
48
+
49
+ super
32
50
  IRB.irb(nil, *obj)
33
51
  end
34
52
  end
35
53
 
36
54
  class Jobs < MultiIRBCommand
37
- category "IRB"
55
+ category "Multi-irb (DEPRECATED)"
38
56
  description "List of current sessions."
39
57
 
40
58
  def execute
59
+ print_deprecated_warning
60
+
61
+ if irb_context.with_debugger
62
+ print_debugger_warning
63
+ return
64
+ end
65
+
66
+ super
41
67
  IRB.JobManager
42
68
  end
43
69
  end
44
70
 
45
71
  class Foreground < MultiIRBCommand
46
- category "IRB"
72
+ category "Multi-irb (DEPRECATED)"
47
73
  description "Switches to the session of the given number."
48
74
 
49
75
  def execute(key = nil)
76
+ print_deprecated_warning
77
+
78
+ if irb_context.with_debugger
79
+ print_debugger_warning
80
+ return
81
+ end
82
+
83
+ super
84
+
50
85
  raise CommandArgumentError.new("Please specify the id of target IRB job (listed in the `jobs` command).") unless key
51
86
  IRB.JobManager.switch(key)
52
87
  end
53
88
  end
54
89
 
55
90
  class Kill < MultiIRBCommand
56
- category "IRB"
91
+ category "Multi-irb (DEPRECATED)"
57
92
  description "Kills the session with the given number."
58
93
 
59
94
  def execute(*keys)
95
+ print_deprecated_warning
96
+
97
+ if irb_context.with_debugger
98
+ print_debugger_warning
99
+ return
100
+ end
101
+
102
+ super
60
103
  IRB.JobManager.kill(*keys)
61
104
  end
62
105
  end
data/lib/irb/color.rb CHANGED
@@ -9,12 +9,14 @@ module IRB # :nodoc:
9
9
  BOLD = 1
10
10
  UNDERLINE = 4
11
11
  REVERSE = 7
12
+ BLACK = 30
12
13
  RED = 31
13
14
  GREEN = 32
14
15
  YELLOW = 33
15
16
  BLUE = 34
16
17
  MAGENTA = 35
17
18
  CYAN = 36
19
+ WHITE = 37
18
20
 
19
21
  TOKEN_KEYWORDS = {
20
22
  on_kw: ['nil', 'self', 'true', 'false', '__FILE__', '__LINE__', '__ENCODING__'],
@@ -218,7 +218,7 @@ module IRB
218
218
  else
219
219
  sym = $1
220
220
  candidates = Symbol.all_symbols.collect do |s|
221
- ":" + s.id2name.encode(Encoding.default_external)
221
+ s.inspect
222
222
  rescue EncodingError
223
223
  # ignore
224
224
  end
@@ -233,7 +233,7 @@ module IRB
233
233
  if doc_namespace
234
234
  candidates.find { |i| i == receiver }
235
235
  else
236
- candidates.grep(/^#{receiver}/).collect{|e| "::" + e}
236
+ candidates.grep(/^#{Regexp.quote(receiver)}/).collect{|e| "::" + e}
237
237
  end
238
238
 
239
239
  when /^([A-Z].*)::([^:.]*)$/
data/lib/irb/context.rb CHANGED
@@ -129,8 +129,6 @@ module IRB
129
129
  else
130
130
  @io = input_method
131
131
  end
132
- self.save_history = IRB.conf[:SAVE_HISTORY] if IRB.conf[:SAVE_HISTORY]
133
-
134
132
  @extra_doc_dirs = IRB.conf[:EXTRA_DOC_DIRS]
135
133
 
136
134
  @echo = IRB.conf[:ECHO]
@@ -151,6 +149,24 @@ module IRB
151
149
  @command_aliases = IRB.conf[:COMMAND_ALIASES]
152
150
  end
153
151
 
152
+ def save_history=(val)
153
+ IRB.conf[:SAVE_HISTORY] = val
154
+ end
155
+
156
+ def save_history
157
+ IRB.conf[:SAVE_HISTORY]
158
+ end
159
+
160
+ # A copy of the default <code>IRB.conf[:HISTORY_FILE]</code>
161
+ def history_file
162
+ IRB.conf[:HISTORY_FILE]
163
+ end
164
+
165
+ # Set <code>IRB.conf[:HISTORY_FILE]</code> to the given +hist+.
166
+ def history_file=(hist)
167
+ IRB.conf[:HISTORY_FILE] = hist
168
+ end
169
+
154
170
  # The top-level workspace, see WorkSpace#main
155
171
  def main
156
172
  @workspace.main
@@ -213,8 +229,19 @@ module IRB
213
229
  #
214
230
  # See IRB@Customizing+the+IRB+Prompt for more information.
215
231
  attr_accessor :prompt_c
216
- # See IRB@Customizing+the+IRB+Prompt for more information.
217
- attr_accessor :prompt_n
232
+
233
+ # TODO: Remove this when developing v2.0
234
+ def prompt_n
235
+ warn "IRB::Context#prompt_n is deprecated and will be removed in the next major release."
236
+ ""
237
+ end
238
+
239
+ # TODO: Remove this when developing v2.0
240
+ def prompt_n=(_)
241
+ warn "IRB::Context#prompt_n= is deprecated and will be removed in the next major release."
242
+ ""
243
+ end
244
+
218
245
  # Can be either the default <code>IRB.conf[:AUTO_INDENT]</code>, or the
219
246
  # mode set by #prompt_mode=
220
247
  #
@@ -329,6 +356,8 @@ module IRB
329
356
  # User-defined IRB command aliases
330
357
  attr_accessor :command_aliases
331
358
 
359
+ attr_accessor :with_debugger
360
+
332
361
  # Alias for #use_multiline
333
362
  alias use_multiline? use_multiline
334
363
  # Alias for #use_singleline
@@ -396,7 +425,6 @@ module IRB
396
425
  @prompt_i = pconf[:PROMPT_I]
397
426
  @prompt_s = pconf[:PROMPT_S]
398
427
  @prompt_c = pconf[:PROMPT_C]
399
- @prompt_n = pconf[:PROMPT_N]
400
428
  @return_format = pconf[:RETURN]
401
429
  @return_format = "%s\n" if @return_format == nil
402
430
  if ai = pconf.include?(:AUTO_INDENT)
@@ -473,28 +501,31 @@ module IRB
473
501
  @inspect_mode
474
502
  end
475
503
 
476
- def evaluate(line, line_no, exception: nil) # :nodoc:
504
+ def evaluate(line, line_no) # :nodoc:
477
505
  @line_no = line_no
478
- if exception
479
- line_no -= 1
480
- line = "begin ::Kernel.raise _; rescue _.class\n#{line}\n""end"
481
- @workspace.local_variable_set(:_, exception)
482
- end
506
+ result = nil
483
507
 
484
- # Transform a non-identifier alias (@, $) or keywords (next, break)
485
- command, args = line.split(/\s/, 2)
486
- if original = command_aliases[command.to_sym]
487
- line = line.gsub(/\A#{Regexp.escape(command)}/, original.to_s)
488
- command = original
508
+ if IRB.conf[:MEASURE] && IRB.conf[:MEASURE_CALLBACKS].empty?
509
+ IRB.set_measure_callback
489
510
  end
490
511
 
491
- # Hook command-specific transformation
492
- command_class = ExtendCommandBundle.load_command(command)
493
- if command_class&.respond_to?(:transform_args)
494
- line = "#{command} #{command_class.transform_args(args)}"
512
+ if IRB.conf[:MEASURE] && !IRB.conf[:MEASURE_CALLBACKS].empty?
513
+ last_proc = proc do
514
+ result = @workspace.evaluate(line, irb_path, line_no)
515
+ end
516
+ IRB.conf[:MEASURE_CALLBACKS].inject(last_proc) do |chain, item|
517
+ _name, callback, arg = item
518
+ proc do
519
+ callback.(self, line, line_no, arg) do
520
+ chain.call
521
+ end
522
+ end
523
+ end.call
524
+ else
525
+ result = @workspace.evaluate(line, irb_path, line_no)
495
526
  end
496
527
 
497
- set_last_value(@workspace.evaluate(line, irb_path, line_no))
528
+ set_last_value(result)
498
529
  end
499
530
 
500
531
  def inspect_last_value # :nodoc:
@@ -0,0 +1,104 @@
1
+ require 'io/console/size'
2
+ require 'debug/console'
3
+
4
+ module IRB
5
+ module Debug
6
+ class UI < DEBUGGER__::UI_Base
7
+ def initialize(thread, irb)
8
+ @thread = thread
9
+ @irb = irb
10
+ end
11
+
12
+ def remote?
13
+ false
14
+ end
15
+
16
+ def activate session, on_fork: false
17
+ end
18
+
19
+ def deactivate
20
+ end
21
+
22
+ def width
23
+ if (w = IO.console_size[1]) == 0 # for tests PTY
24
+ 80
25
+ else
26
+ w
27
+ end
28
+ end
29
+
30
+ def quit n
31
+ yield
32
+ exit n
33
+ end
34
+
35
+ def ask prompt
36
+ setup_interrupt do
37
+ print prompt
38
+ ($stdin.gets || '').strip
39
+ end
40
+ end
41
+
42
+ def puts str = nil
43
+ case str
44
+ when Array
45
+ str.each{|line|
46
+ $stdout.puts line.chomp
47
+ }
48
+ when String
49
+ str.each_line{|line|
50
+ $stdout.puts line.chomp
51
+ }
52
+ when nil
53
+ $stdout.puts
54
+ end
55
+ end
56
+
57
+ def readline _
58
+ setup_interrupt do
59
+ tc = DEBUGGER__::SESSION.get_thread_client(@thread)
60
+ cmd = @irb.debug_readline(tc.current_frame.binding || TOPLEVEL_BINDING)
61
+
62
+ case cmd
63
+ when nil # when user types C-d
64
+ "continue"
65
+ else
66
+ cmd
67
+ end
68
+ end
69
+ end
70
+
71
+ def setup_interrupt
72
+ DEBUGGER__::SESSION.intercept_trap_sigint false do
73
+ current_thread = Thread.current # should be session_server thread
74
+
75
+ prev_handler = trap(:INT){
76
+ current_thread.raise Interrupt
77
+ }
78
+
79
+ yield
80
+ ensure
81
+ trap(:INT, prev_handler)
82
+ end
83
+ end
84
+
85
+ def after_fork_parent
86
+ parent_pid = Process.pid
87
+
88
+ at_exit{
89
+ DEBUGGER__::SESSION.intercept_trap_sigint_end
90
+ trap(:SIGINT, :IGNORE)
91
+
92
+ if Process.pid == parent_pid
93
+ # only check child process from its parent
94
+ begin
95
+ # wait for all child processes to keep terminal
96
+ Process.waitpid
97
+ rescue Errno::ESRCH, Errno::ECHILD
98
+ end
99
+ end
100
+ }
101
+ end
102
+ end
103
+ end
104
+ end