irb 1.14.0 → 1.14.2

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b96add3ef12d981b483ae225bfd8bca8a1a477f0b03c8040a0dfbf047bd0d1fb
4
- data.tar.gz: efe91e97fab6b11a239c4774da9d5ecfe39bbc364d61a6b2c7fd6ec8b9c01a72
3
+ metadata.gz: 606da5c01c0630f572812c844da8f331fd487fa50060db8c95a61aef14660d68
4
+ data.tar.gz: 5d1749fea9672c734b9bdce740f6f43d90b2479e2a06de09d9604265317fab1e
5
5
  SHA512:
6
- metadata.gz: 0eaa119e765d958cdab2ba80fa12e47a9727d389a37f255e174ec260279af8e2c2edb510689364541fe40f3c202a3b3d4f2d7dc7fbe30030b04e89bc2c851a3a
7
- data.tar.gz: 71904280b978ac48763d7bc965f053fd5580ec7876c40cba76a1a9c26ac7aec2f4ad98deb1d9c8d3635f5222a0b757d458d7fded9264411b05f19bfe77ad2526
6
+ metadata.gz: 478ceef6ddf60e4bf8ddf2f1586129eab9bcff9e40bb9af8662a1c7f46d7c406f7d896395d3459cbde5ac2a427b73e474327033647e6c742691c185a553a1d71
7
+ data.tar.gz: da1f92f1403c9fee631f7fc85f1bf60120db710bd171a98be1b428a22c4545fed4d4e4dd7692d90220520609047bd4fc6d00882da0bce7bf1a67e05dc41dd8e1
data/README.md CHANGED
@@ -114,6 +114,7 @@ Help
114
114
  help List all available commands. Use `help <command>` to get information about a specific command.
115
115
 
116
116
  IRB
117
+ context Displays current configuration.
117
118
  exit Exit the current irb session.
118
119
  exit! Exit the current process.
119
120
  irb_load Load a Ruby file.
@@ -121,6 +122,7 @@ IRB
121
122
  source Loads a given file in the current session.
122
123
  irb_info Show information about IRB.
123
124
  history Shows the input history. `-g [query]` or `-G [query]` allows you to filter the output.
125
+ disable_irb Disable binding.irb.
124
126
 
125
127
  Workspace
126
128
  cwws Show the current workspace.
@@ -128,6 +130,7 @@ Workspace
128
130
  workspaces Show workspaces.
129
131
  pushws Push an object to the workspace stack.
130
132
  popws Pop a workspace from the workspace stack.
133
+ cd Move into the given object or leave the current context.
131
134
 
132
135
  Multi-irb (DEPRECATED)
133
136
  irb Start a child IRB.
@@ -152,11 +155,14 @@ Misc
152
155
  measure `measure` enables the mode to measure processing time. `measure :off` disables it.
153
156
 
154
157
  Context
155
- show_doc Enter the mode to look up RI documents.
158
+ show_doc Look up documentation with RI.
156
159
  ls Show methods, constants, and variables.
157
- show_source Show the source code of a given method or constant.
160
+ show_source Show the source code of a given method, class/module, or constant.
158
161
  whereami Show the source code around binding.irb again.
159
162
 
163
+ Helper methods
164
+ conf Returns the current IRB context.
165
+
160
166
  Aliases
161
167
  $ Alias for `show_source`
162
168
  @ Alias for `whereami`
@@ -164,6 +170,13 @@ Aliases
164
170
 
165
171
  ## Debugging with IRB
166
172
 
173
+ ### Getting Started
174
+
175
+ - In `binding.irb`, use the `debug` command to start a `irb:rdbg` session with access to all `debug.gem` commands.
176
+ - Use `RUBY_DEBUG_IRB_CONSOLE=1` environment variable to make `debug.gem` use IRB as the debugging console.
177
+
178
+ ### Details
179
+
167
180
  Starting from version 1.8.0, IRB boasts a powerful integration with `debug.gem`, providing a debugging experience akin to `pry-byebug`.
168
181
 
169
182
  After hitting a `binding.irb` breakpoint, you can activate the debugger with the `debug` command. Alternatively, if the `debug` method happens to already be defined in the current scope, you can call `irb_debug`.
@@ -348,6 +361,10 @@ irb(main):002> a.first. # Completes Integer methods
348
361
  - `VISUAL`: Its value would be used to open files by the `edit` command.
349
362
  - `EDITOR`: Its value would be used to open files by the `edit` command if `VISUAL` is unset.
350
363
  - `IRBRC`: The file specified would be evaluated as IRB's rc-file.
364
+ - `XDG_CONFIG_HOME`: If it is set and `IRBRC` is unset, the file `$XDG_CONFIG_HOME/irb/irbrc` would be evaluated as IRB's rc-file.
365
+ - `RI_PAGER`: The command specified would be used as a pager.
366
+ - `PAGER`: The command specified would be used as a pager if `RI_PAGER` is unset.
367
+ - `IRB_LANG`, `LC_MESSAGES`, `LC_ALL`, `LANG`: The first of these that is set would be used as the locale value.
351
368
 
352
369
  ## Documentation
353
370
 
@@ -10,8 +10,10 @@ module IRB
10
10
  module Command
11
11
  class CommandArgumentError < StandardError; end
12
12
 
13
- def self.extract_ruby_args(*args, **kwargs)
14
- throw :EXTRACT_RUBY_ARGS, [args, kwargs]
13
+ class << self
14
+ def extract_ruby_args(*args, **kwargs)
15
+ throw :EXTRACT_RUBY_ARGS, [args, kwargs]
16
+ end
15
17
  end
16
18
 
17
19
  class Base
@@ -31,6 +33,12 @@ module IRB
31
33
  @help_message
32
34
  end
33
35
 
36
+ def execute(irb_context, arg)
37
+ new(irb_context).execute(arg)
38
+ rescue CommandArgumentError => e
39
+ puts e.message
40
+ end
41
+
34
42
  private
35
43
 
36
44
  def highlight(text)
@@ -38,12 +46,6 @@ module IRB
38
46
  end
39
47
  end
40
48
 
41
- def self.execute(irb_context, arg)
42
- new(irb_context).execute(arg)
43
- rescue CommandArgumentError => e
44
- puts e.message
45
- end
46
-
47
49
  def initialize(irb_context)
48
50
  @irb_context = irb_context
49
51
  end
@@ -58,13 +58,15 @@ module IRB
58
58
  end
59
59
 
60
60
  class DebugCommand < Debug
61
- def self.category
62
- "Debugging"
63
- end
61
+ class << self
62
+ def category
63
+ "Debugging"
64
+ end
64
65
 
65
- def self.description
66
- command_name = self.name.split("::").last.downcase
67
- "Start the debugger of debug.gem and run its `#{command_name}` command."
66
+ def description
67
+ command_name = self.name.split("::").last.downcase
68
+ "Start the debugger of debug.gem and run its `#{command_name}` command."
69
+ end
68
70
  end
69
71
  end
70
72
  end
@@ -39,7 +39,7 @@ module IRB
39
39
 
40
40
  help_cmds = commands_grouped_by_categories.delete("Help")
41
41
  no_category_cmds = commands_grouped_by_categories.delete("No category")
42
- aliases = irb_context.instance_variable_get(:@user_aliases).map do |alias_name, target|
42
+ aliases = irb_context.instance_variable_get(:@command_aliases).map do |alias_name, target|
43
43
  { display_name: alias_name, description: "Alias for `#{target}`" }
44
44
  end
45
45
 
@@ -19,7 +19,7 @@ module IRB
19
19
  # Use throw and catch to handle arg that includes `;`
20
20
  # For example: "1, kw: (2; 3); 4" will be parsed to [[1], { kw: 3 }]
21
21
  catch(:EXTRACT_RUBY_ARGS) do
22
- @irb_context.workspace.binding.eval "IRB::Command.extract_ruby_args #{arg}"
22
+ @irb_context.workspace.binding.eval "::IRB::Command.extract_ruby_args #{arg}"
23
23
  end || [[], {}]
24
24
  end
25
25
  end
@@ -11,7 +11,7 @@ module IRB
11
11
 
12
12
  module Command
13
13
  class Ls < Base
14
- include RubyArgsExtractor
14
+ class EvaluationError < StandardError; end
15
15
 
16
16
  category "Context"
17
17
  description "Show methods, constants, and variables."
@@ -22,24 +22,35 @@ module IRB
22
22
  -g [query] Filter the output with a query.
23
23
  HELP_MESSAGE
24
24
 
25
+ def evaluate(code)
26
+ @irb_context.workspace.binding.eval(code)
27
+ rescue Exception => e
28
+ puts "#{e.class}: #{e.message}"
29
+ raise EvaluationError
30
+ end
31
+
25
32
  def execute(arg)
26
33
  if match = arg.match(/\A(?<target>.+\s|)(-g|-G)\s+(?<grep>.+)$/)
27
- if match[:target].empty?
28
- use_main = true
29
- else
30
- obj = @irb_context.workspace.binding.eval(match[:target])
31
- end
34
+ target = match[:target]
32
35
  grep = Regexp.new(match[:grep])
36
+ elsif match = arg.match(/\A((?<target>.+),|)\s*grep:(?<grep>.+)/)
37
+ # Legacy style `ls obj, grep: /regexp/`
38
+ # Evaluation order should be eval(target) then eval(grep)
39
+ target = match[:target] || ''
40
+ grep_regexp_code = match[:grep]
33
41
  else
34
- args, kwargs = ruby_args(arg)
35
- use_main = args.empty?
36
- obj = args.first
37
- grep = kwargs[:grep]
42
+ target = arg.strip
38
43
  end
39
44
 
40
- if use_main
45
+ if target.empty?
41
46
  obj = irb_context.workspace.main
42
47
  locals = irb_context.workspace.binding.local_variables
48
+ else
49
+ obj = evaluate(target)
50
+ end
51
+
52
+ if grep_regexp_code
53
+ grep = evaluate(grep_regexp_code)
43
54
  end
44
55
 
45
56
  o = Output.new(grep: grep)
@@ -52,6 +63,7 @@ module IRB
52
63
  o.dump("class variables", klass.class_variables)
53
64
  o.dump("locals", locals) if locals
54
65
  o.print_result
66
+ rescue EvaluationError
55
67
  end
56
68
 
57
69
  def dump_methods(o, klass, obj)
@@ -137,26 +137,33 @@ module IRB
137
137
  end
138
138
 
139
139
  class RegexpCompletor < BaseCompletor # :nodoc:
140
+ KERNEL_METHODS = ::Kernel.instance_method(:methods)
141
+ KERNEL_PRIVATE_METHODS = ::Kernel.instance_method(:private_methods)
142
+ KERNEL_INSTANCE_VARIABLES = ::Kernel.instance_method(:instance_variables)
143
+ OBJECT_CLASS_INSTANCE_METHOD = ::Object.instance_method(:class)
144
+ MODULE_CONSTANTS_INSTANCE_METHOD = ::Module.instance_method(:constants)
145
+
140
146
  using Module.new {
141
147
  refine ::Binding do
142
148
  def eval_methods
143
- ::Kernel.instance_method(:methods).bind(eval("self")).call
149
+ KERNEL_METHODS.bind_call(receiver)
144
150
  end
145
151
 
146
152
  def eval_private_methods
147
- ::Kernel.instance_method(:private_methods).bind(eval("self")).call
153
+ KERNEL_PRIVATE_METHODS.bind_call(receiver)
148
154
  end
149
155
 
150
156
  def eval_instance_variables
151
- ::Kernel.instance_method(:instance_variables).bind(eval("self")).call
157
+ KERNEL_INSTANCE_VARIABLES.bind_call(receiver)
152
158
  end
153
159
 
154
160
  def eval_global_variables
155
- ::Kernel.instance_method(:global_variables).bind(eval("self")).call
161
+ ::Kernel.global_variables
156
162
  end
157
163
 
158
164
  def eval_class_constants
159
- ::Module.instance_method(:constants).bind(eval("self.class")).call
165
+ klass = OBJECT_CLASS_INSTANCE_METHOD.bind_call(receiver)
166
+ MODULE_CONSTANTS_INSTANCE_METHOD.bind_call(klass)
160
167
  end
161
168
  end
162
169
  }
data/lib/irb/context.rb CHANGED
@@ -13,6 +13,10 @@ module IRB
13
13
  # A class that wraps the current state of the irb session, including the
14
14
  # configuration of IRB.conf.
15
15
  class Context
16
+ KERNEL_PUBLIC_METHOD = ::Kernel.instance_method(:public_method)
17
+ KERNEL_METHOD = ::Kernel.instance_method(:method)
18
+
19
+ ASSIGN_OPERATORS_REGEXP = Regexp.union(%w[= += -= *= /= %= **= &= |= &&= ||= ^= <<= >>=])
16
20
  # Creates a new IRB context.
17
21
  #
18
22
  # The optional +input_method+ argument:
@@ -148,8 +152,7 @@ module IRB
148
152
  @newline_before_multiline_output = true
149
153
  end
150
154
 
151
- @user_aliases = IRB.conf[:COMMAND_ALIASES].dup
152
- @command_aliases = @user_aliases.merge(KEYWORD_ALIASES)
155
+ @command_aliases = IRB.conf[:COMMAND_ALIASES].dup
153
156
  end
154
157
 
155
158
  private def term_interactive?
@@ -157,17 +160,6 @@ module IRB
157
160
  STDIN.tty? && ENV['TERM'] != 'dumb'
158
161
  end
159
162
 
160
- # because all input will eventually be evaluated as Ruby code,
161
- # command names that conflict with Ruby keywords need special workaround
162
- # we can remove them once we implemented a better command system for IRB
163
- KEYWORD_ALIASES = {
164
- :break => :irb_break,
165
- :catch => :irb_catch,
166
- :next => :irb_next,
167
- }.freeze
168
-
169
- private_constant :KEYWORD_ALIASES
170
-
171
163
  def use_tracer=(val)
172
164
  require_relative "ext/tracer" if val
173
165
  IRB.conf[:USE_TRACER] = val
@@ -187,11 +179,17 @@ module IRB
187
179
 
188
180
  private def build_completor
189
181
  completor_type = IRB.conf[:COMPLETOR]
182
+
183
+ # Gem repl_type_completor is added to bundled gems in Ruby 3.4.
184
+ # Use :type as default completor only in Ruby 3.4 or later.
185
+ verbose = !!completor_type
186
+ completor_type ||= RUBY_VERSION >= '3.4' ? :type : :regexp
187
+
190
188
  case completor_type
191
189
  when :regexp
192
190
  return RegexpCompletor.new
193
191
  when :type
194
- completor = build_type_completor
192
+ completor = build_type_completor(verbose: verbose)
195
193
  return completor if completor
196
194
  else
197
195
  warn "Invalid value for IRB.conf[:COMPLETOR]: #{completor_type}"
@@ -200,17 +198,17 @@ module IRB
200
198
  RegexpCompletor.new
201
199
  end
202
200
 
203
- private def build_type_completor
201
+ private def build_type_completor(verbose:)
204
202
  if RUBY_ENGINE == 'truffleruby'
205
203
  # Avoid SyntaxError. truffleruby does not support endless method definition yet.
206
- warn 'TypeCompletor is not supported on TruffleRuby yet'
204
+ warn 'TypeCompletor is not supported on TruffleRuby yet' if verbose
207
205
  return
208
206
  end
209
207
 
210
208
  begin
211
209
  require 'repl_type_completor'
212
210
  rescue LoadError => e
213
- warn "TypeCompletor requires `gem repl_type_completor`: #{e.message}"
211
+ warn "TypeCompletor requires `gem repl_type_completor`: #{e.message}" if verbose
214
212
  return
215
213
  end
216
214
 
@@ -635,6 +633,46 @@ module IRB
635
633
  result
636
634
  end
637
635
 
636
+ def parse_command(code)
637
+ command_name, arg = code.strip.split(/\s+/, 2)
638
+ return unless code.lines.size == 1 && command_name
639
+
640
+ arg ||= ''
641
+ command = command_name.to_sym
642
+ # Command aliases are always command. example: $, @
643
+ if (alias_name = command_aliases[command])
644
+ return [alias_name, arg]
645
+ end
646
+
647
+ # Assignment-like expression is not a command
648
+ return if arg.start_with?(ASSIGN_OPERATORS_REGEXP) && !arg.start_with?(/==|=~/)
649
+
650
+ # Local variable have precedence over command
651
+ return if local_variables.include?(command)
652
+
653
+ # Check visibility
654
+ public_method = !!KERNEL_PUBLIC_METHOD.bind_call(main, command) rescue false
655
+ private_method = !public_method && !!KERNEL_METHOD.bind_call(main, command) rescue false
656
+ if Command.execute_as_command?(command, public_method: public_method, private_method: private_method)
657
+ [command, arg]
658
+ end
659
+ end
660
+
661
+ def colorize_input(input, complete:)
662
+ if IRB.conf[:USE_COLORIZE] && IRB::Color.colorable?
663
+ lvars = local_variables || []
664
+ if parse_command(input)
665
+ name, sep, arg = input.split(/(\s+)/, 2)
666
+ arg = IRB::Color.colorize_code(arg, complete: complete, local_variables: lvars)
667
+ "#{IRB::Color.colorize(name, [:BOLD])}\e[m#{sep}#{arg}"
668
+ else
669
+ IRB::Color.colorize_code(input, complete: complete, local_variables: lvars)
670
+ end
671
+ else
672
+ Reline::Unicode.escape_for_print(input)
673
+ end
674
+ end
675
+
638
676
  def inspect_last_value # :nodoc:
639
677
  @inspect_method.inspect_value(@last_value)
640
678
  end
@@ -669,5 +707,10 @@ module IRB
669
707
  def local_variables # :nodoc:
670
708
  workspace.binding.local_variables
671
709
  end
710
+
711
+ def safe_method_call_on_main(method_name)
712
+ main_object = main
713
+ Object === main_object ? main_object.__send__(method_name) : Object.instance_method(method_name).bind_call(main_object)
714
+ end
672
715
  end
673
716
  end
data/lib/irb/debug/ui.rb CHANGED
@@ -45,9 +45,7 @@ module IRB
45
45
  $stdout.puts line.chomp
46
46
  }
47
47
  when String
48
- str.each_line{|line|
49
- $stdout.puts line.chomp
50
- }
48
+ Pager.page_content(str, retain_content: true)
51
49
  when nil
52
50
  $stdout.puts
53
51
  end
@@ -56,7 +54,7 @@ module IRB
56
54
  def readline _
57
55
  setup_interrupt do
58
56
  tc = DEBUGGER__::SESSION.instance_variable_get(:@tc)
59
- cmd = @irb.debug_readline(tc.current_frame.binding || TOPLEVEL_BINDING)
57
+ cmd = @irb.debug_readline(tc.current_frame.eval_binding || TOPLEVEL_BINDING)
60
58
 
61
59
  case cmd
62
60
  when nil # when user types C-d
data/lib/irb/debug.rb CHANGED
@@ -57,22 +57,18 @@ module IRB
57
57
  DEBUGGER__::ThreadClient.prepend(SkipPathHelperForIRB)
58
58
  end
59
59
 
60
- if !@output_modifier_defined && !DEBUGGER__::CONFIG[:no_hint]
61
- irb_output_modifier_proc = Reline.output_modifier_proc
62
-
63
- Reline.output_modifier_proc = proc do |output, complete:|
64
- unless output.strip.empty?
65
- cmd = output.split(/\s/, 2).first
60
+ if !DEBUGGER__::CONFIG[:no_hint] && irb.context.io.is_a?(RelineInputMethod)
61
+ Reline.output_modifier_proc = proc do |input, complete:|
62
+ unless input.strip.empty?
63
+ cmd = input.split(/\s/, 2).first
66
64
 
67
65
  if !complete && DEBUGGER__.commands.key?(cmd)
68
- output = output.sub(/\n$/, " # debug command\n")
66
+ input = input.sub(/\n$/, " # debug command\n")
69
67
  end
70
68
  end
71
69
 
72
- irb_output_modifier_proc.call(output, complete: complete)
70
+ irb.context.colorize_input(input, complete: complete)
73
71
  end
74
-
75
- @output_modifier_defined = true
76
72
  end
77
73
 
78
74
  true
@@ -181,9 +181,15 @@ module IRB
181
181
  [:edit, NO_OVERRIDE]
182
182
  )
183
183
 
184
- _register_with_aliases(:irb_break, Command::Break)
185
- _register_with_aliases(:irb_catch, Command::Catch)
186
- _register_with_aliases(:irb_next, Command::Next)
184
+ _register_with_aliases(:irb_break, Command::Break,
185
+ [:break, OVERRIDE_ALL]
186
+ )
187
+ _register_with_aliases(:irb_catch, Command::Catch,
188
+ [:catch, OVERRIDE_PRIVATE_ONLY]
189
+ )
190
+ _register_with_aliases(:irb_next, Command::Next,
191
+ [:next, OVERRIDE_ALL]
192
+ )
187
193
  _register_with_aliases(:irb_delete, Command::Delete,
188
194
  [:delete, NO_OVERRIDE]
189
195
  )
@@ -259,10 +265,12 @@ module IRB
259
265
  # Deprecated. Doesn't have any effect.
260
266
  @EXTEND_COMMANDS = []
261
267
 
262
- # Drepcated. Use Command.regiser instead.
263
- def self.def_extend_command(cmd_name, cmd_class, _, *aliases)
264
- Command._register_with_aliases(cmd_name, cmd_class, *aliases)
265
- Command.class_variable_set(:@@command_override_policies, nil)
268
+ class << self
269
+ # Drepcated. Use Command.regiser instead.
270
+ def def_extend_command(cmd_name, cmd_class, _, *aliases)
271
+ Command._register_with_aliases(cmd_name, cmd_class, *aliases)
272
+ Command.class_variable_set(:@@command_override_policies, nil)
273
+ end
266
274
  end
267
275
  end
268
276
  end
@@ -100,19 +100,21 @@ module IRB
100
100
 
101
101
  private def easter_egg_logo(type)
102
102
  @easter_egg_logos ||= File.read(File.join(__dir__, 'ruby_logo.aa'), encoding: 'UTF-8:UTF-8')
103
- .split(/TYPE: ([A-Z]+)\n/)[1..]
103
+ .split(/TYPE: ([A-Z_]+)\n/)[1..]
104
104
  .each_slice(2)
105
105
  .to_h
106
106
  @easter_egg_logos[type.to_s.upcase]
107
107
  end
108
108
 
109
109
  private def easter_egg(type = nil)
110
+ print "\e[?1049h"
110
111
  type ||= [:logo, :dancing].sample
111
112
  case type
112
113
  when :logo
113
- require "rdoc"
114
- RDoc::RI::Driver.new.page do |io|
115
- io.write easter_egg_logo(:large)
114
+ Pager.page do |io|
115
+ logo_type = STDOUT.external_encoding == Encoding::UTF_8 ? :unicode_large : :ascii_large
116
+ io.write easter_egg_logo(logo_type)
117
+ STDIN.raw { STDIN.getc } if io == STDOUT
116
118
  end
117
119
  when :dancing
118
120
  STDOUT.cooked do
@@ -123,7 +125,7 @@ module IRB
123
125
  canvas = Canvas.new(Reline.get_screen_size)
124
126
  end
125
127
  ruby_model = RubyModel.new
126
- print "\e[?1049h"
128
+ print "\e[?25l" # hide cursor
127
129
  0.step do |i| # TODO (0..).each needs Ruby 2.6 or later
128
130
  buff = canvas.draw do
129
131
  ruby_model.render_frame(i) do |p1, p2|
@@ -137,10 +139,12 @@ module IRB
137
139
  end
138
140
  rescue Interrupt
139
141
  ensure
140
- print "\e[0m\e[?1049l"
142
+ print "\e[?25h" # show cursor
141
143
  trap("SIGINT", prev_trap)
142
144
  end
143
145
  end
146
+ ensure
147
+ print "\e[0m\e[?1049l"
144
148
  end
145
149
  end
146
150
  end