irb 1.14.3 → 1.15.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.
- checksums.yaml +4 -4
- data/Gemfile +2 -0
- data/README.md +13 -310
- data/Rakefile +4 -4
- data/irb.gemspec +2 -2
- data/lib/irb/color.rb +1 -0
- data/lib/irb/color_printer.rb +10 -9
- data/lib/irb/command/copy.rb +73 -0
- data/lib/irb/command/history.rb +1 -1
- data/lib/irb/context.rb +95 -60
- data/lib/irb/default_commands.rb +4 -1
- data/lib/irb/history.rb +4 -0
- data/lib/irb/init.rb +3 -1
- data/lib/irb/inspector.rb +11 -6
- data/lib/irb/pager.rb +116 -6
- data/lib/irb/statement.rb +21 -0
- data/lib/irb/version.rb +2 -2
- data/lib/irb.rb +37 -894
- data/man/irb.1 +2 -0
- metadata +18 -4
- data/.document +0 -8
data/lib/irb/context.rb
CHANGED
@@ -155,11 +155,6 @@ module IRB
|
|
155
155
|
@command_aliases = IRB.conf[:COMMAND_ALIASES].dup
|
156
156
|
end
|
157
157
|
|
158
|
-
private def term_interactive?
|
159
|
-
return true if ENV['TEST_IRB_FORCE_INTERACTIVE']
|
160
|
-
STDIN.tty? && ENV['TERM'] != 'dumb'
|
161
|
-
end
|
162
|
-
|
163
158
|
def use_tracer=(val)
|
164
159
|
require_relative "ext/tracer" if val
|
165
160
|
IRB.conf[:USE_TRACER] = val
|
@@ -177,45 +172,6 @@ module IRB
|
|
177
172
|
__send__(__method__, val)
|
178
173
|
end
|
179
174
|
|
180
|
-
private def build_completor
|
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
|
-
|
188
|
-
case completor_type
|
189
|
-
when :regexp
|
190
|
-
return RegexpCompletor.new
|
191
|
-
when :type
|
192
|
-
completor = build_type_completor(verbose: verbose)
|
193
|
-
return completor if completor
|
194
|
-
else
|
195
|
-
warn "Invalid value for IRB.conf[:COMPLETOR]: #{completor_type}"
|
196
|
-
end
|
197
|
-
# Fallback to RegexpCompletor
|
198
|
-
RegexpCompletor.new
|
199
|
-
end
|
200
|
-
|
201
|
-
private def build_type_completor(verbose:)
|
202
|
-
if RUBY_ENGINE == 'truffleruby'
|
203
|
-
# Avoid SyntaxError. truffleruby does not support endless method definition yet.
|
204
|
-
warn 'TypeCompletor is not supported on TruffleRuby yet' if verbose
|
205
|
-
return
|
206
|
-
end
|
207
|
-
|
208
|
-
begin
|
209
|
-
require 'repl_type_completor'
|
210
|
-
rescue LoadError => e
|
211
|
-
warn "TypeCompletor requires `gem repl_type_completor`: #{e.message}" if verbose
|
212
|
-
return
|
213
|
-
end
|
214
|
-
|
215
|
-
ReplTypeCompletor.preload_rbs
|
216
|
-
TypeCompletor.new(self)
|
217
|
-
end
|
218
|
-
|
219
175
|
def save_history=(val)
|
220
176
|
IRB.conf[:SAVE_HISTORY] = val
|
221
177
|
end
|
@@ -308,6 +264,8 @@ module IRB
|
|
308
264
|
attr_reader :use_autocomplete
|
309
265
|
# A copy of the default <code>IRB.conf[:INSPECT_MODE]</code>
|
310
266
|
attr_reader :inspect_mode
|
267
|
+
# Inspector for the current context
|
268
|
+
attr_reader :inspect_method
|
311
269
|
|
312
270
|
# A copy of the default <code>IRB.conf[:PROMPT_MODE]</code>
|
313
271
|
attr_reader :prompt_mode
|
@@ -600,6 +558,8 @@ module IRB
|
|
600
558
|
set_last_value(result)
|
601
559
|
when Statement::Command
|
602
560
|
statement.command_class.execute(self, statement.arg)
|
561
|
+
when Statement::IncorrectAlias
|
562
|
+
warn statement.message
|
603
563
|
end
|
604
564
|
|
605
565
|
nil
|
@@ -633,35 +593,60 @@ module IRB
|
|
633
593
|
result
|
634
594
|
end
|
635
595
|
|
636
|
-
def
|
596
|
+
def parse_input(code, is_assignment_expression)
|
637
597
|
command_name, arg = code.strip.split(/\s+/, 2)
|
638
|
-
return unless code.lines.size == 1 && command_name
|
639
|
-
|
640
598
|
arg ||= ''
|
641
|
-
|
642
|
-
#
|
643
|
-
if
|
644
|
-
|
599
|
+
|
600
|
+
# command can only be 1 line
|
601
|
+
if code.lines.size != 1 ||
|
602
|
+
# command name is required
|
603
|
+
command_name.nil? ||
|
604
|
+
# local variable have precedence over command
|
605
|
+
local_variables.include?(command_name.to_sym) ||
|
606
|
+
# assignment expression is not a command
|
607
|
+
(is_assignment_expression ||
|
608
|
+
(arg.start_with?(ASSIGN_OPERATORS_REGEXP) && !arg.start_with?(/==|=~/)))
|
609
|
+
return Statement::Expression.new(code, is_assignment_expression)
|
645
610
|
end
|
646
611
|
|
647
|
-
|
648
|
-
return if arg.start_with?(ASSIGN_OPERATORS_REGEXP) && !arg.start_with?(/==|=~/)
|
612
|
+
command = command_name.to_sym
|
649
613
|
|
650
|
-
#
|
651
|
-
|
614
|
+
# Check command aliases
|
615
|
+
if aliased_name = command_aliases[command]
|
616
|
+
if command_class = Command.load_command(aliased_name)
|
617
|
+
command = aliased_name
|
618
|
+
elsif HelperMethod.helper_methods[aliased_name]
|
619
|
+
message = <<~MESSAGE
|
620
|
+
Using command alias `#{command}` for helper method `#{aliased_name}` is not supported.
|
621
|
+
Please check the value of `IRB.conf[:COMMAND_ALIASES]`.
|
622
|
+
MESSAGE
|
623
|
+
return Statement::IncorrectAlias.new(message)
|
624
|
+
else
|
625
|
+
message = <<~MESSAGE
|
626
|
+
You're trying to use command alias `#{command}` for command `#{aliased_name}`, but `#{aliased_name}` does not exist.
|
627
|
+
Please check the value of `IRB.conf[:COMMAND_ALIASES]`.
|
628
|
+
MESSAGE
|
629
|
+
return Statement::IncorrectAlias.new(message)
|
630
|
+
end
|
631
|
+
else
|
632
|
+
command_class = Command.load_command(command)
|
633
|
+
end
|
652
634
|
|
653
635
|
# Check visibility
|
654
636
|
public_method = !!KERNEL_PUBLIC_METHOD.bind_call(main, command) rescue false
|
655
637
|
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
|
-
|
638
|
+
if command_class && Command.execute_as_command?(command, public_method: public_method, private_method: private_method)
|
639
|
+
Statement::Command.new(code, command_class, arg)
|
640
|
+
else
|
641
|
+
Statement::Expression.new(code, is_assignment_expression)
|
658
642
|
end
|
659
643
|
end
|
660
644
|
|
661
645
|
def colorize_input(input, complete:)
|
662
646
|
if IRB.conf[:USE_COLORIZE] && IRB::Color.colorable?
|
663
647
|
lvars = local_variables || []
|
664
|
-
|
648
|
+
parsed_input = parse_input(input, false)
|
649
|
+
if parsed_input.is_a?(Statement::Command)
|
665
650
|
name, sep, arg = input.split(/(\s+)/, 2)
|
666
651
|
arg = IRB::Color.colorize_code(arg, complete: complete, local_variables: lvars)
|
667
652
|
"#{IRB::Color.colorize(name, [:BOLD])}\e[m#{sep}#{arg}"
|
@@ -673,8 +658,12 @@ module IRB
|
|
673
658
|
end
|
674
659
|
end
|
675
660
|
|
676
|
-
def inspect_last_value # :nodoc:
|
677
|
-
@inspect_method.inspect_value(@last_value)
|
661
|
+
def inspect_last_value(output = +'') # :nodoc:
|
662
|
+
@inspect_method.inspect_value(@last_value, output)
|
663
|
+
end
|
664
|
+
|
665
|
+
def inspector_support_stream_output?
|
666
|
+
@inspect_method.support_stream_output?
|
678
667
|
end
|
679
668
|
|
680
669
|
NOPRINTING_IVARS = ["@last_value"] # :nodoc:
|
@@ -712,5 +701,51 @@ module IRB
|
|
712
701
|
main_object = main
|
713
702
|
Object === main_object ? main_object.__send__(method_name) : Object.instance_method(method_name).bind_call(main_object)
|
714
703
|
end
|
704
|
+
|
705
|
+
private
|
706
|
+
|
707
|
+
def term_interactive?
|
708
|
+
return true if ENV['TEST_IRB_FORCE_INTERACTIVE']
|
709
|
+
STDIN.tty? && ENV['TERM'] != 'dumb'
|
710
|
+
end
|
711
|
+
|
712
|
+
def build_completor
|
713
|
+
completor_type = IRB.conf[:COMPLETOR]
|
714
|
+
|
715
|
+
# Gem repl_type_completor is added to bundled gems in Ruby 3.4.
|
716
|
+
# Use :type as default completor only in Ruby 3.4 or later.
|
717
|
+
verbose = !!completor_type
|
718
|
+
completor_type ||= RUBY_VERSION >= '3.4' ? :type : :regexp
|
719
|
+
|
720
|
+
case completor_type
|
721
|
+
when :regexp
|
722
|
+
return RegexpCompletor.new
|
723
|
+
when :type
|
724
|
+
completor = build_type_completor(verbose: verbose)
|
725
|
+
return completor if completor
|
726
|
+
else
|
727
|
+
warn "Invalid value for IRB.conf[:COMPLETOR]: #{completor_type}"
|
728
|
+
end
|
729
|
+
# Fallback to RegexpCompletor
|
730
|
+
RegexpCompletor.new
|
731
|
+
end
|
732
|
+
|
733
|
+
def build_type_completor(verbose:)
|
734
|
+
if RUBY_ENGINE == 'truffleruby'
|
735
|
+
# Avoid SyntaxError. truffleruby does not support endless method definition yet.
|
736
|
+
warn 'TypeCompletor is not supported on TruffleRuby yet' if verbose
|
737
|
+
return
|
738
|
+
end
|
739
|
+
|
740
|
+
begin
|
741
|
+
require 'repl_type_completor'
|
742
|
+
rescue LoadError => e
|
743
|
+
warn "TypeCompletor requires `gem repl_type_completor`: #{e.message}" if verbose
|
744
|
+
return
|
745
|
+
end
|
746
|
+
|
747
|
+
ReplTypeCompletor.preload_rbs
|
748
|
+
TypeCompletor.new(self)
|
749
|
+
end
|
715
750
|
end
|
716
751
|
end
|
data/lib/irb/default_commands.rb
CHANGED
@@ -9,6 +9,7 @@ require_relative "command/cd"
|
|
9
9
|
require_relative "command/chws"
|
10
10
|
require_relative "command/context"
|
11
11
|
require_relative "command/continue"
|
12
|
+
require_relative "command/copy"
|
12
13
|
require_relative "command/debug"
|
13
14
|
require_relative "command/delete"
|
14
15
|
require_relative "command/disable_irb"
|
@@ -218,7 +219,8 @@ module IRB
|
|
218
219
|
)
|
219
220
|
|
220
221
|
_register_with_aliases(:irb_show_doc, Command::ShowDoc,
|
221
|
-
[:show_doc, NO_OVERRIDE]
|
222
|
+
[:show_doc, NO_OVERRIDE],
|
223
|
+
[:ri, NO_OVERRIDE]
|
222
224
|
)
|
223
225
|
|
224
226
|
_register_with_aliases(:irb_info, Command::IrbInfo)
|
@@ -249,6 +251,7 @@ module IRB
|
|
249
251
|
)
|
250
252
|
|
251
253
|
register(:cd, Command::CD)
|
254
|
+
register(:copy, Command::Copy)
|
252
255
|
end
|
253
256
|
|
254
257
|
ExtendCommand = Command
|
data/lib/irb/history.rb
CHANGED
@@ -2,9 +2,13 @@ require "pathname"
|
|
2
2
|
|
3
3
|
module IRB
|
4
4
|
module History
|
5
|
+
DEFAULT_ENTRY_LIMIT = 1000
|
6
|
+
|
5
7
|
class << self
|
6
8
|
# Integer representation of <code>IRB.conf[:HISTORY_FILE]</code>.
|
7
9
|
def save_history
|
10
|
+
return 0 if IRB.conf[:SAVE_HISTORY] == false
|
11
|
+
return DEFAULT_ENTRY_LIMIT if IRB.conf[:SAVE_HISTORY] == true
|
8
12
|
IRB.conf[:SAVE_HISTORY].to_i
|
9
13
|
end
|
10
14
|
|
data/lib/irb/init.rb
CHANGED
@@ -93,7 +93,7 @@ module IRB # :nodoc:
|
|
93
93
|
@CONF[:VERBOSE] = nil
|
94
94
|
|
95
95
|
@CONF[:EVAL_HISTORY] = nil
|
96
|
-
@CONF[:SAVE_HISTORY] =
|
96
|
+
@CONF[:SAVE_HISTORY] = History::DEFAULT_ENTRY_LIMIT
|
97
97
|
|
98
98
|
@CONF[:BACK_TRACE_LIMIT] = 16
|
99
99
|
|
@@ -194,6 +194,8 @@ module IRB # :nodoc:
|
|
194
194
|
:'$' => :show_source,
|
195
195
|
:'@' => :whereami,
|
196
196
|
}
|
197
|
+
|
198
|
+
@CONF[:COPY_COMMAND] = ENV.fetch("IRB_COPY_COMMAND", nil)
|
197
199
|
end
|
198
200
|
|
199
201
|
def IRB.set_measure_callback(type = nil, arg = nil, &block)
|
data/lib/irb/inspector.rb
CHANGED
@@ -92,9 +92,14 @@ module IRB # :nodoc:
|
|
92
92
|
@init.call if @init
|
93
93
|
end
|
94
94
|
|
95
|
+
def support_stream_output?
|
96
|
+
second_parameter_type = @inspect.parameters[1]&.first
|
97
|
+
second_parameter_type == :req || second_parameter_type == :opt
|
98
|
+
end
|
99
|
+
|
95
100
|
# Proc to call when the input is evaluated and output in irb.
|
96
|
-
def inspect_value(v)
|
97
|
-
@inspect.call(v)
|
101
|
+
def inspect_value(v, output, colorize: true)
|
102
|
+
support_stream_output? ? @inspect.call(v, output, colorize: colorize) : output << @inspect.call(v, colorize: colorize)
|
98
103
|
rescue => e
|
99
104
|
puts "An error occurred when inspecting the object: #{e.inspect}"
|
100
105
|
|
@@ -110,11 +115,11 @@ module IRB # :nodoc:
|
|
110
115
|
end
|
111
116
|
|
112
117
|
Inspector.def_inspector([false, :to_s, :raw]){|v| v.to_s}
|
113
|
-
Inspector.def_inspector([:p, :inspect]){|v|
|
114
|
-
Color.colorize_code(v.inspect, colorable: Color.colorable? && Color.inspect_colorable?(v))
|
118
|
+
Inspector.def_inspector([:p, :inspect]){|v, colorize: true|
|
119
|
+
Color.colorize_code(v.inspect, colorable: colorize && Color.colorable? && Color.inspect_colorable?(v))
|
115
120
|
}
|
116
|
-
Inspector.def_inspector([true, :pp, :pretty_inspect], proc{require_relative "color_printer"}){|v|
|
117
|
-
IRB::ColorPrinter.pp(v,
|
121
|
+
Inspector.def_inspector([true, :pp, :pretty_inspect], proc{require_relative "color_printer"}){|v, output, colorize: true|
|
122
|
+
IRB::ColorPrinter.pp(v, output, colorize: colorize)
|
118
123
|
}
|
119
124
|
Inspector.def_inspector([:yaml, :YAML], proc{require "yaml"}){|v|
|
120
125
|
begin
|
data/lib/irb/pager.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'reline'
|
4
|
+
|
3
5
|
module IRB
|
4
6
|
# The implementation of this class is borrowed from RDoc's lib/rdoc/ri/driver.rb.
|
5
7
|
# Please do NOT use this class directly outside of IRB.
|
@@ -47,12 +49,42 @@ module IRB
|
|
47
49
|
rescue Errno::EPIPE
|
48
50
|
end
|
49
51
|
|
50
|
-
private
|
51
|
-
|
52
52
|
def should_page?
|
53
53
|
IRB.conf[:USE_PAGER] && STDIN.tty? && (ENV.key?("TERM") && ENV["TERM"] != "dumb")
|
54
54
|
end
|
55
55
|
|
56
|
+
def page_with_preview(width, height, formatter_proc)
|
57
|
+
overflow_callback = ->(lines) do
|
58
|
+
modified_output = formatter_proc.call(lines.join, true)
|
59
|
+
content, = take_first_page(width, [height - 2, 0].max) {|o| o.write modified_output }
|
60
|
+
content = content.chomp
|
61
|
+
content = "#{content}\e[0m" if Color.colorable?
|
62
|
+
$stdout.puts content
|
63
|
+
$stdout.puts 'Preparing full inspection value...'
|
64
|
+
end
|
65
|
+
out = PageOverflowIO.new(width, height, overflow_callback, delay: 0.1)
|
66
|
+
yield out
|
67
|
+
content = formatter_proc.call(out.string, out.multipage?)
|
68
|
+
if out.multipage?
|
69
|
+
page(retain_content: true) do |io|
|
70
|
+
io.puts content
|
71
|
+
end
|
72
|
+
else
|
73
|
+
$stdout.puts content
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def take_first_page(width, height)
|
78
|
+
overflow_callback = proc do |lines|
|
79
|
+
return lines.join, true
|
80
|
+
end
|
81
|
+
out = Pager::PageOverflowIO.new(width, height, overflow_callback)
|
82
|
+
yield out
|
83
|
+
[out.string, false]
|
84
|
+
end
|
85
|
+
|
86
|
+
private
|
87
|
+
|
56
88
|
def content_exceeds_screen_height?(content)
|
57
89
|
screen_height, screen_width = begin
|
58
90
|
Reline.get_screen_size
|
@@ -62,10 +94,10 @@ module IRB
|
|
62
94
|
|
63
95
|
pageable_height = screen_height - 3 # leave some space for previous and the current prompt
|
64
96
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
97
|
+
return true if content.lines.size > pageable_height
|
98
|
+
|
99
|
+
_, overflow = take_first_page(screen_width, pageable_height) {|out| out.write content }
|
100
|
+
overflow
|
69
101
|
end
|
70
102
|
|
71
103
|
def setup_pager(retain_content:)
|
@@ -96,5 +128,83 @@ module IRB
|
|
96
128
|
nil
|
97
129
|
end
|
98
130
|
end
|
131
|
+
|
132
|
+
# Writable IO that has page overflow callback
|
133
|
+
class PageOverflowIO
|
134
|
+
attr_reader :string, :first_page_lines
|
135
|
+
|
136
|
+
# Maximum size of a single cell in terminal
|
137
|
+
# Assumed worst case: "\e[1;3;4;9;38;2;255;128;128;48;2;128;128;255mA\e[0m"
|
138
|
+
# bold, italic, underline, crossed_out, RGB forgound, RGB background
|
139
|
+
MAX_CHAR_PER_CELL = 50
|
140
|
+
|
141
|
+
def initialize(width, height, overflow_callback, delay: nil)
|
142
|
+
@lines = []
|
143
|
+
@first_page_lines = nil
|
144
|
+
@width = width
|
145
|
+
@height = height
|
146
|
+
@buffer = +''
|
147
|
+
@overflow_callback = overflow_callback
|
148
|
+
@col = 0
|
149
|
+
@string = +''
|
150
|
+
@multipage = false
|
151
|
+
@delay_until = (Time.now + delay if delay)
|
152
|
+
end
|
153
|
+
|
154
|
+
def puts(text = '')
|
155
|
+
write(text)
|
156
|
+
write("\n") unless text.end_with?("\n")
|
157
|
+
end
|
158
|
+
|
159
|
+
def write(text)
|
160
|
+
@string << text
|
161
|
+
if @multipage
|
162
|
+
if @delay_until && Time.now > @delay_until
|
163
|
+
@overflow_callback.call(@first_page_lines)
|
164
|
+
@delay_until = nil
|
165
|
+
end
|
166
|
+
return
|
167
|
+
end
|
168
|
+
|
169
|
+
overflow_size = (@width * (@height - @lines.size) + @width - @col) * MAX_CHAR_PER_CELL
|
170
|
+
if text.size >= overflow_size
|
171
|
+
text = text[0, overflow_size]
|
172
|
+
overflow = true
|
173
|
+
end
|
174
|
+
|
175
|
+
@buffer << text
|
176
|
+
@col += Reline::Unicode.calculate_width(text)
|
177
|
+
if text.include?("\n") || @col >= @width
|
178
|
+
@buffer.lines.each do |line|
|
179
|
+
wrapped_lines = Reline::Unicode.split_by_width(line.chomp, @width).first.compact
|
180
|
+
wrapped_lines.pop if wrapped_lines.last == ''
|
181
|
+
@lines.concat(wrapped_lines)
|
182
|
+
if @lines.empty?
|
183
|
+
@lines << "\n"
|
184
|
+
elsif line.end_with?("\n")
|
185
|
+
@lines[-1] += "\n"
|
186
|
+
end
|
187
|
+
end
|
188
|
+
@buffer.clear
|
189
|
+
@buffer << @lines.pop unless @lines.last.end_with?("\n")
|
190
|
+
@col = Reline::Unicode.calculate_width(@buffer)
|
191
|
+
end
|
192
|
+
if overflow || @lines.size > @height || (@lines.size == @height && @col > 0)
|
193
|
+
@first_page_lines = @lines.take(@height)
|
194
|
+
if !@delay_until || Time.now > @delay_until
|
195
|
+
@overflow_callback.call(@first_page_lines)
|
196
|
+
@delay_until = nil
|
197
|
+
end
|
198
|
+
@multipage = true
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
def multipage?
|
203
|
+
@multipage
|
204
|
+
end
|
205
|
+
|
206
|
+
alias print write
|
207
|
+
alias << write
|
208
|
+
end
|
99
209
|
end
|
100
210
|
end
|
data/lib/irb/statement.rb
CHANGED
@@ -54,6 +54,27 @@ module IRB
|
|
54
54
|
end
|
55
55
|
end
|
56
56
|
|
57
|
+
class IncorrectAlias < Statement
|
58
|
+
attr_reader :message
|
59
|
+
|
60
|
+
def initialize(message)
|
61
|
+
@code = ""
|
62
|
+
@message = message
|
63
|
+
end
|
64
|
+
|
65
|
+
def should_be_handled_by_debugger?
|
66
|
+
false
|
67
|
+
end
|
68
|
+
|
69
|
+
def is_assignment?
|
70
|
+
false
|
71
|
+
end
|
72
|
+
|
73
|
+
def suppresses_echo?
|
74
|
+
true
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
57
78
|
class Command < Statement
|
58
79
|
attr_reader :command_class, :arg
|
59
80
|
|