irb 1.14.0 → 1.18.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/.rdoc_options +5 -0
- data/CONTRIBUTING.md +52 -0
- data/EXTEND_IRB.md +3 -0
- data/Gemfile +11 -1
- data/README.md +13 -293
- data/doc/COMMAND_LINE_OPTIONS.md +69 -0
- data/doc/COMPARED_WITH_PRY.md +22 -0
- data/doc/Configurations.md +274 -0
- data/doc/EXTEND_IRB.md +122 -0
- data/doc/Index.md +705 -0
- data/doc/irb/irb.rd.ja +1 -1
- data/lib/irb/color.rb +251 -162
- data/lib/irb/color_printer.rb +10 -9
- data/lib/irb/command/base.rb +46 -13
- data/lib/irb/command/copy.rb +83 -0
- data/lib/irb/command/debug.rb +8 -6
- data/lib/irb/command/help.rb +1 -1
- data/lib/irb/command/history.rb +1 -1
- data/lib/irb/command/internal_helpers.rb +7 -4
- data/lib/irb/command/ls.rb +29 -15
- data/lib/irb/completion.rb +81 -57
- data/lib/irb/context.rb +132 -53
- data/lib/irb/debug/ui.rb +2 -4
- data/lib/irb/debug.rb +9 -12
- data/lib/irb/default_commands.rb +19 -8
- data/lib/irb/easter-egg.rb +13 -7
- data/lib/irb/ext/multi-irb.rb +2 -0
- data/lib/irb/history.rb +84 -55
- data/lib/irb/init.rb +7 -2
- data/lib/irb/input-method.rb +167 -130
- data/lib/irb/inspector.rb +42 -36
- data/lib/irb/lc/help-message +2 -2
- data/lib/irb/lc/ja/help-message +4 -4
- data/lib/irb/nesting_parser.rb +364 -213
- data/lib/irb/pager.rb +137 -7
- data/lib/irb/ruby-lex.rb +230 -302
- data/lib/irb/ruby_logo.aa +79 -37
- data/lib/irb/source_finder.rb +15 -22
- data/lib/irb/startup_message.rb +83 -0
- data/lib/irb/statement.rb +21 -0
- data/lib/irb/version.rb +2 -2
- data/lib/irb/workspace.rb +22 -31
- data/lib/irb.rb +148 -986
- data/man/irb.1 +37 -2
- metadata +42 -12
- data/.document +0 -4
- data/Rakefile +0 -54
- data/bin/console +0 -6
- data/bin/setup +0 -6
- data/irb.gemspec +0 -46
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,26 +152,9 @@ module IRB
|
|
|
148
152
|
@newline_before_multiline_output = true
|
|
149
153
|
end
|
|
150
154
|
|
|
151
|
-
@
|
|
152
|
-
@command_aliases = @user_aliases.merge(KEYWORD_ALIASES)
|
|
153
|
-
end
|
|
154
|
-
|
|
155
|
-
private def term_interactive?
|
|
156
|
-
return true if ENV['TEST_IRB_FORCE_INTERACTIVE']
|
|
157
|
-
STDIN.tty? && ENV['TERM'] != 'dumb'
|
|
155
|
+
@command_aliases = IRB.conf[:COMMAND_ALIASES].dup
|
|
158
156
|
end
|
|
159
157
|
|
|
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
158
|
def use_tracer=(val)
|
|
172
159
|
require_relative "ext/tracer" if val
|
|
173
160
|
IRB.conf[:USE_TRACER] = val
|
|
@@ -185,39 +172,6 @@ module IRB
|
|
|
185
172
|
__send__(__method__, val)
|
|
186
173
|
end
|
|
187
174
|
|
|
188
|
-
private def build_completor
|
|
189
|
-
completor_type = IRB.conf[:COMPLETOR]
|
|
190
|
-
case completor_type
|
|
191
|
-
when :regexp
|
|
192
|
-
return RegexpCompletor.new
|
|
193
|
-
when :type
|
|
194
|
-
completor = build_type_completor
|
|
195
|
-
return completor if completor
|
|
196
|
-
else
|
|
197
|
-
warn "Invalid value for IRB.conf[:COMPLETOR]: #{completor_type}"
|
|
198
|
-
end
|
|
199
|
-
# Fallback to RegexpCompletor
|
|
200
|
-
RegexpCompletor.new
|
|
201
|
-
end
|
|
202
|
-
|
|
203
|
-
private def build_type_completor
|
|
204
|
-
if RUBY_ENGINE == 'truffleruby'
|
|
205
|
-
# Avoid SyntaxError. truffleruby does not support endless method definition yet.
|
|
206
|
-
warn 'TypeCompletor is not supported on TruffleRuby yet'
|
|
207
|
-
return
|
|
208
|
-
end
|
|
209
|
-
|
|
210
|
-
begin
|
|
211
|
-
require 'repl_type_completor'
|
|
212
|
-
rescue LoadError => e
|
|
213
|
-
warn "TypeCompletor requires `gem repl_type_completor`: #{e.message}"
|
|
214
|
-
return
|
|
215
|
-
end
|
|
216
|
-
|
|
217
|
-
ReplTypeCompletor.preload_rbs
|
|
218
|
-
TypeCompletor.new(self)
|
|
219
|
-
end
|
|
220
|
-
|
|
221
175
|
def save_history=(val)
|
|
222
176
|
IRB.conf[:SAVE_HISTORY] = val
|
|
223
177
|
end
|
|
@@ -310,6 +264,8 @@ module IRB
|
|
|
310
264
|
attr_reader :use_autocomplete
|
|
311
265
|
# A copy of the default <code>IRB.conf[:INSPECT_MODE]</code>
|
|
312
266
|
attr_reader :inspect_mode
|
|
267
|
+
# Inspector for the current context
|
|
268
|
+
attr_reader :inspect_method
|
|
313
269
|
|
|
314
270
|
# A copy of the default <code>IRB.conf[:PROMPT_MODE]</code>
|
|
315
271
|
attr_reader :prompt_mode
|
|
@@ -602,6 +558,8 @@ module IRB
|
|
|
602
558
|
set_last_value(result)
|
|
603
559
|
when Statement::Command
|
|
604
560
|
statement.command_class.execute(self, statement.arg)
|
|
561
|
+
when Statement::IncorrectAlias
|
|
562
|
+
warn statement.message
|
|
605
563
|
end
|
|
606
564
|
|
|
607
565
|
nil
|
|
@@ -635,8 +593,78 @@ module IRB
|
|
|
635
593
|
result
|
|
636
594
|
end
|
|
637
595
|
|
|
638
|
-
def
|
|
639
|
-
|
|
596
|
+
def parse_input(code, is_assignment_expression)
|
|
597
|
+
command_name, arg = code.strip.split(/\s+/, 2)
|
|
598
|
+
arg ||= ''
|
|
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)
|
|
610
|
+
end
|
|
611
|
+
|
|
612
|
+
command = command_name.to_sym
|
|
613
|
+
|
|
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
|
|
634
|
+
|
|
635
|
+
if command_class
|
|
636
|
+
# Check whether the command conflicts with existing methods
|
|
637
|
+
public_method = !!KERNEL_PUBLIC_METHOD.bind_call(main, command) rescue false
|
|
638
|
+
private_method = !public_method && !!KERNEL_METHOD.bind_call(main, command) rescue false
|
|
639
|
+
if Command.execute_as_command?(command, public_method: public_method, private_method: private_method)
|
|
640
|
+
return Statement::Command.new(code, command_class, arg)
|
|
641
|
+
end
|
|
642
|
+
end
|
|
643
|
+
Statement::Expression.new(code, is_assignment_expression)
|
|
644
|
+
end
|
|
645
|
+
|
|
646
|
+
def colorize_input(input, complete:)
|
|
647
|
+
if IRB.conf[:USE_COLORIZE] && IRB::Color.colorable?
|
|
648
|
+
lvars = local_variables || []
|
|
649
|
+
parsed_input = parse_input(input, false)
|
|
650
|
+
if parsed_input.is_a?(Statement::Command)
|
|
651
|
+
name, sep, arg = input.split(/(\s+)/, 2)
|
|
652
|
+
arg = IRB::Color.colorize_code(arg, complete: complete, local_variables: lvars) if arg
|
|
653
|
+
"#{IRB::Color.colorize(name, [:BOLD])}\e[m#{sep}#{arg}"
|
|
654
|
+
else
|
|
655
|
+
IRB::Color.colorize_code(input, complete: complete, local_variables: lvars)
|
|
656
|
+
end
|
|
657
|
+
else
|
|
658
|
+
Reline::Unicode.escape_for_print(input)
|
|
659
|
+
end
|
|
660
|
+
end
|
|
661
|
+
|
|
662
|
+
def inspect_last_value(output = +'') # :nodoc:
|
|
663
|
+
@inspect_method.inspect_value(@last_value, output)
|
|
664
|
+
end
|
|
665
|
+
|
|
666
|
+
def inspector_support_stream_output?
|
|
667
|
+
@inspect_method.support_stream_output?
|
|
640
668
|
end
|
|
641
669
|
|
|
642
670
|
NOPRINTING_IVARS = ["@last_value"] # :nodoc:
|
|
@@ -669,5 +697,56 @@ module IRB
|
|
|
669
697
|
def local_variables # :nodoc:
|
|
670
698
|
workspace.binding.local_variables
|
|
671
699
|
end
|
|
700
|
+
|
|
701
|
+
def safe_method_call_on_main(method_name)
|
|
702
|
+
main_object = main
|
|
703
|
+
Object === main_object ? main_object.__send__(method_name) : Object.instance_method(method_name).bind_call(main_object)
|
|
704
|
+
end
|
|
705
|
+
|
|
706
|
+
private
|
|
707
|
+
|
|
708
|
+
def term_interactive?
|
|
709
|
+
return true if ENV['TEST_IRB_FORCE_INTERACTIVE']
|
|
710
|
+
STDIN.tty? && ENV['TERM'] != 'dumb'
|
|
711
|
+
end
|
|
712
|
+
|
|
713
|
+
def build_completor
|
|
714
|
+
completor_type = IRB.conf[:COMPLETOR]
|
|
715
|
+
|
|
716
|
+
# Gem repl_type_completor is added to bundled gems in Ruby 3.4.
|
|
717
|
+
# Use :type as default completor only in Ruby 3.4 or later.
|
|
718
|
+
verbose = !!completor_type
|
|
719
|
+
completor_type ||= RUBY_VERSION >= '3.4' ? :type : :regexp
|
|
720
|
+
|
|
721
|
+
case completor_type
|
|
722
|
+
when :regexp
|
|
723
|
+
return RegexpCompletor.new
|
|
724
|
+
when :type
|
|
725
|
+
completor = build_type_completor(verbose: verbose)
|
|
726
|
+
return completor if completor
|
|
727
|
+
else
|
|
728
|
+
warn "Invalid value for IRB.conf[:COMPLETOR]: #{completor_type}"
|
|
729
|
+
end
|
|
730
|
+
# Fallback to RegexpCompletor
|
|
731
|
+
RegexpCompletor.new
|
|
732
|
+
end
|
|
733
|
+
|
|
734
|
+
def build_type_completor(verbose:)
|
|
735
|
+
if RUBY_ENGINE == 'truffleruby'
|
|
736
|
+
# Avoid SyntaxError. truffleruby does not support endless method definition yet.
|
|
737
|
+
warn 'TypeCompletor is not supported on TruffleRuby yet' if verbose
|
|
738
|
+
return
|
|
739
|
+
end
|
|
740
|
+
|
|
741
|
+
begin
|
|
742
|
+
require 'repl_type_completor'
|
|
743
|
+
rescue LoadError => e
|
|
744
|
+
warn "TypeCompletor requires `gem repl_type_completor`: #{e.message}" if verbose
|
|
745
|
+
return
|
|
746
|
+
end
|
|
747
|
+
|
|
748
|
+
ReplTypeCompletor.preload_rbs
|
|
749
|
+
TypeCompletor.new(self)
|
|
750
|
+
end
|
|
672
751
|
end
|
|
673
752
|
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
|
|
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.
|
|
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
|
@@ -49,7 +49,7 @@ module IRB
|
|
|
49
49
|
def DEBUGGER__.capture_frames(*args)
|
|
50
50
|
frames = capture_frames_without_irb(*args)
|
|
51
51
|
frames.reject! do |frame|
|
|
52
|
-
frame.realpath&.start_with?(IRB_DIR) || frame.path
|
|
52
|
+
frame.realpath&.start_with?(IRB_DIR) || frame.path&.start_with?("<internal:")
|
|
53
53
|
end
|
|
54
54
|
frames
|
|
55
55
|
end
|
|
@@ -57,22 +57,18 @@ module IRB
|
|
|
57
57
|
DEBUGGER__::ThreadClient.prepend(SkipPathHelperForIRB)
|
|
58
58
|
end
|
|
59
59
|
|
|
60
|
-
if
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
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[/\S+/]
|
|
66
64
|
|
|
67
65
|
if !complete && DEBUGGER__.commands.key?(cmd)
|
|
68
|
-
|
|
66
|
+
input = input.sub(/\n$/, " # debug command\n")
|
|
69
67
|
end
|
|
70
68
|
end
|
|
71
69
|
|
|
72
|
-
|
|
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
|
|
@@ -85,12 +81,13 @@ module IRB
|
|
|
85
81
|
IRB.instance_variable_set(:@debugger_irb, irb)
|
|
86
82
|
irb.context.with_debugger = true
|
|
87
83
|
irb.context.irb_name += ":rdbg"
|
|
84
|
+
irb.context.io.load_history if irb.context.io.class < HistorySavingAbility
|
|
88
85
|
end
|
|
89
86
|
|
|
90
87
|
module SkipPathHelperForIRB
|
|
91
88
|
def skip_internal_path?(path)
|
|
92
89
|
# The latter can be removed once https://github.com/ruby/debug/issues/866 is resolved
|
|
93
|
-
super || path
|
|
90
|
+
super || path&.match?(IRB_DIR) || path&.match?('<internal:prelude>')
|
|
94
91
|
end
|
|
95
92
|
end
|
|
96
93
|
|
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"
|
|
@@ -181,9 +182,15 @@ module IRB
|
|
|
181
182
|
[:edit, NO_OVERRIDE]
|
|
182
183
|
)
|
|
183
184
|
|
|
184
|
-
_register_with_aliases(:irb_break, Command::Break
|
|
185
|
-
|
|
186
|
-
|
|
185
|
+
_register_with_aliases(:irb_break, Command::Break,
|
|
186
|
+
[:break, OVERRIDE_ALL]
|
|
187
|
+
)
|
|
188
|
+
_register_with_aliases(:irb_catch, Command::Catch,
|
|
189
|
+
[:catch, OVERRIDE_PRIVATE_ONLY]
|
|
190
|
+
)
|
|
191
|
+
_register_with_aliases(:irb_next, Command::Next,
|
|
192
|
+
[:next, OVERRIDE_ALL]
|
|
193
|
+
)
|
|
187
194
|
_register_with_aliases(:irb_delete, Command::Delete,
|
|
188
195
|
[:delete, NO_OVERRIDE]
|
|
189
196
|
)
|
|
@@ -212,7 +219,8 @@ module IRB
|
|
|
212
219
|
)
|
|
213
220
|
|
|
214
221
|
_register_with_aliases(:irb_show_doc, Command::ShowDoc,
|
|
215
|
-
[:show_doc, NO_OVERRIDE]
|
|
222
|
+
[:show_doc, NO_OVERRIDE],
|
|
223
|
+
[:ri, NO_OVERRIDE]
|
|
216
224
|
)
|
|
217
225
|
|
|
218
226
|
_register_with_aliases(:irb_info, Command::IrbInfo)
|
|
@@ -243,6 +251,7 @@ module IRB
|
|
|
243
251
|
)
|
|
244
252
|
|
|
245
253
|
register(:cd, Command::CD)
|
|
254
|
+
register(:copy, Command::Copy)
|
|
246
255
|
end
|
|
247
256
|
|
|
248
257
|
ExtendCommand = Command
|
|
@@ -259,10 +268,12 @@ module IRB
|
|
|
259
268
|
# Deprecated. Doesn't have any effect.
|
|
260
269
|
@EXTEND_COMMANDS = []
|
|
261
270
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
271
|
+
class << self
|
|
272
|
+
# Drepcated. Use Command.regiser instead.
|
|
273
|
+
def def_extend_command(cmd_name, cmd_class, _, *aliases)
|
|
274
|
+
Command._register_with_aliases(cmd_name, cmd_class, *aliases)
|
|
275
|
+
Command.class_variable_set(:@@command_override_policies, nil)
|
|
276
|
+
end
|
|
266
277
|
end
|
|
267
278
|
end
|
|
268
279
|
end
|
data/lib/irb/easter-egg.rb
CHANGED
|
@@ -100,31 +100,34 @@ 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-
|
|
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
|
-
|
|
114
|
-
|
|
115
|
-
io.write easter_egg_logo(
|
|
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
|
|
119
121
|
interrupted = false
|
|
120
122
|
prev_trap = trap("SIGINT") { interrupted = true }
|
|
121
123
|
canvas = Canvas.new(Reline.get_screen_size)
|
|
124
|
+
otio = Reline::IOGate.prep
|
|
122
125
|
Reline::IOGate.set_winch_handler do
|
|
123
126
|
canvas = Canvas.new(Reline.get_screen_size)
|
|
124
127
|
end
|
|
125
128
|
ruby_model = RubyModel.new
|
|
126
|
-
print "\e[?
|
|
127
|
-
0.
|
|
129
|
+
print "\e[?25l" # hide cursor
|
|
130
|
+
(0..).each do |i|
|
|
128
131
|
buff = canvas.draw do
|
|
129
132
|
ruby_model.render_frame(i) do |p1, p2|
|
|
130
133
|
canvas.line(p1, p2)
|
|
@@ -137,10 +140,13 @@ module IRB
|
|
|
137
140
|
end
|
|
138
141
|
rescue Interrupt
|
|
139
142
|
ensure
|
|
140
|
-
|
|
143
|
+
Reline::IOGate.deprep(otio)
|
|
144
|
+
print "\e[?25h" # show cursor
|
|
141
145
|
trap("SIGINT", prev_trap)
|
|
142
146
|
end
|
|
143
147
|
end
|
|
148
|
+
ensure
|
|
149
|
+
print "\e[0m\e[?1049l"
|
|
144
150
|
end
|
|
145
151
|
end
|
|
146
152
|
end
|
data/lib/irb/ext/multi-irb.rb
CHANGED
|
@@ -65,6 +65,7 @@ module IRB
|
|
|
65
65
|
@current_job = irb
|
|
66
66
|
th.run
|
|
67
67
|
Thread.stop
|
|
68
|
+
Thread.pass
|
|
68
69
|
@current_job = irb(Thread.current)
|
|
69
70
|
end
|
|
70
71
|
|
|
@@ -220,6 +221,7 @@ module IRB
|
|
|
220
221
|
end
|
|
221
222
|
end
|
|
222
223
|
Thread.stop
|
|
224
|
+
Thread.pass
|
|
223
225
|
@JobManager.current_job = @JobManager.irb(Thread.current)
|
|
224
226
|
end
|
|
225
227
|
|
data/lib/irb/history.rb
CHANGED
|
@@ -1,6 +1,36 @@
|
|
|
1
1
|
require "pathname"
|
|
2
2
|
|
|
3
3
|
module IRB
|
|
4
|
+
module History
|
|
5
|
+
DEFAULT_ENTRY_LIMIT = 1000
|
|
6
|
+
|
|
7
|
+
class << self
|
|
8
|
+
# Integer representation of <code>IRB.conf[:HISTORY_FILE]</code>.
|
|
9
|
+
def save_history
|
|
10
|
+
return 0 if IRB.conf[:SAVE_HISTORY] == false
|
|
11
|
+
return DEFAULT_ENTRY_LIMIT if IRB.conf[:SAVE_HISTORY] == true
|
|
12
|
+
IRB.conf[:SAVE_HISTORY].to_i
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def save_history?
|
|
16
|
+
!save_history.zero?
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def infinite?
|
|
20
|
+
save_history.negative?
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Might be nil when HOME and XDG_CONFIG_HOME are not available.
|
|
24
|
+
def history_file
|
|
25
|
+
if (history_file = IRB.conf[:HISTORY_FILE])
|
|
26
|
+
File.expand_path(history_file)
|
|
27
|
+
else
|
|
28
|
+
IRB.rc_file("_history")
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
4
34
|
module HistorySavingAbility # :nodoc:
|
|
5
35
|
def support_history_saving?
|
|
6
36
|
true
|
|
@@ -11,76 +41,75 @@ module IRB
|
|
|
11
41
|
end
|
|
12
42
|
|
|
13
43
|
def load_history
|
|
44
|
+
history_file = History.history_file
|
|
45
|
+
return unless File.exist?(history_file.to_s)
|
|
46
|
+
|
|
14
47
|
history = self.class::HISTORY
|
|
15
48
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
history.last << "\n" << l
|
|
27
|
-
else
|
|
28
|
-
history << l
|
|
29
|
-
end
|
|
30
|
-
}
|
|
31
|
-
end
|
|
32
|
-
@loaded_history_lines = history.size
|
|
33
|
-
@loaded_history_mtime = File.mtime(history_file)
|
|
49
|
+
File.open(history_file, "r:#{IRB.conf[:LC_MESSAGES].encoding}") do |f|
|
|
50
|
+
f.each { |l|
|
|
51
|
+
l = l.chomp
|
|
52
|
+
if self.class == RelineInputMethod and history.last&.end_with?("\\")
|
|
53
|
+
history.last.delete_suffix!("\\")
|
|
54
|
+
history.last << "\n" << l
|
|
55
|
+
else
|
|
56
|
+
history << l
|
|
57
|
+
end
|
|
58
|
+
}
|
|
34
59
|
end
|
|
60
|
+
@loaded_history_lines = history.size
|
|
61
|
+
@loaded_history_mtime = File.mtime(history_file)
|
|
35
62
|
end
|
|
36
63
|
|
|
37
64
|
def save_history
|
|
65
|
+
return unless History.save_history?
|
|
66
|
+
return unless (history_file = History.history_file)
|
|
67
|
+
unless ensure_history_file_writable(history_file)
|
|
68
|
+
warn <<~WARN
|
|
69
|
+
Can't write history to #{History.history_file.inspect} due to insufficient permissions.
|
|
70
|
+
Please verify the value of `IRB.conf[:HISTORY_FILE]`. Ensure the folder exists and that both the folder and file (if it exists) are writable.
|
|
71
|
+
WARN
|
|
72
|
+
return
|
|
73
|
+
end
|
|
74
|
+
|
|
38
75
|
history = self.class::HISTORY.to_a
|
|
39
76
|
|
|
40
|
-
if
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
77
|
+
if File.exist?(history_file) &&
|
|
78
|
+
File.mtime(history_file) != @loaded_history_mtime
|
|
79
|
+
history = history[@loaded_history_lines..-1] if @loaded_history_lines
|
|
80
|
+
append_history = true
|
|
81
|
+
end
|
|
45
82
|
|
|
46
|
-
|
|
47
|
-
|
|
83
|
+
File.open(history_file, (append_history ? "a" : "w"), 0o600, encoding: IRB.conf[:LC_MESSAGES]&.encoding) do |f|
|
|
84
|
+
hist = history.map { |l| l.scrub.split("\n").join("\\\n") }
|
|
48
85
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
File.chmod(0600, history_file)
|
|
53
|
-
end
|
|
54
|
-
rescue Errno::ENOENT
|
|
55
|
-
rescue Errno::EPERM
|
|
56
|
-
return
|
|
57
|
-
rescue
|
|
58
|
-
raise
|
|
86
|
+
unless append_history || History.infinite?
|
|
87
|
+
# Check size before slicing because array.last(huge_number) raises RangeError.
|
|
88
|
+
hist = hist.last(History.save_history) if hist.size > History.save_history
|
|
59
89
|
end
|
|
60
90
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
append_history = true
|
|
65
|
-
end
|
|
91
|
+
f.puts(hist)
|
|
92
|
+
end
|
|
93
|
+
end
|
|
66
94
|
|
|
67
|
-
|
|
68
|
-
unless Dir.exist?(pathname.dirname)
|
|
69
|
-
warn "Warning: The directory to save IRB's history file does not exist. Please double check `IRB.conf[:HISTORY_FILE]`'s value."
|
|
70
|
-
return
|
|
71
|
-
end
|
|
95
|
+
private
|
|
72
96
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
97
|
+
# Returns boolean whether writing to +history_file+ will be possible.
|
|
98
|
+
# Permissions of already existing +history_file+ are changed to
|
|
99
|
+
# owner-only-readable if necessary [BUG #7694].
|
|
100
|
+
def ensure_history_file_writable(history_file)
|
|
101
|
+
history_file = Pathname.new(history_file)
|
|
102
|
+
|
|
103
|
+
return false unless history_file.dirname.writable?
|
|
104
|
+
return true unless history_file.exist?
|
|
105
|
+
|
|
106
|
+
begin
|
|
107
|
+
if history_file.stat.mode & 0o66 != 0
|
|
108
|
+
history_file.chmod 0o600
|
|
83
109
|
end
|
|
110
|
+
true
|
|
111
|
+
rescue Errno::EPERM # no permissions
|
|
112
|
+
false
|
|
84
113
|
end
|
|
85
114
|
end
|
|
86
115
|
end
|
data/lib/irb/init.rb
CHANGED
|
@@ -80,20 +80,21 @@ module IRB # :nodoc:
|
|
|
80
80
|
@CONF[:USE_SINGLELINE] = false unless defined?(ReadlineInputMethod)
|
|
81
81
|
@CONF[:USE_COLORIZE] = (nc = ENV['NO_COLOR']).nil? || nc.empty?
|
|
82
82
|
@CONF[:USE_AUTOCOMPLETE] = ENV.fetch("IRB_USE_AUTOCOMPLETE", "true") != "false"
|
|
83
|
-
@CONF[:COMPLETOR] = ENV
|
|
83
|
+
@CONF[:COMPLETOR] = ENV["IRB_COMPLETOR"]&.to_sym
|
|
84
84
|
@CONF[:INSPECT_MODE] = true
|
|
85
85
|
@CONF[:USE_TRACER] = false
|
|
86
86
|
@CONF[:USE_LOADER] = false
|
|
87
87
|
@CONF[:IGNORE_SIGINT] = true
|
|
88
88
|
@CONF[:IGNORE_EOF] = false
|
|
89
89
|
@CONF[:USE_PAGER] = true
|
|
90
|
+
@CONF[:SHOW_BANNER] = true
|
|
90
91
|
@CONF[:EXTRA_DOC_DIRS] = []
|
|
91
92
|
@CONF[:ECHO] = nil
|
|
92
93
|
@CONF[:ECHO_ON_ASSIGNMENT] = nil
|
|
93
94
|
@CONF[:VERBOSE] = nil
|
|
94
95
|
|
|
95
96
|
@CONF[:EVAL_HISTORY] = nil
|
|
96
|
-
@CONF[:SAVE_HISTORY] =
|
|
97
|
+
@CONF[:SAVE_HISTORY] = History::DEFAULT_ENTRY_LIMIT
|
|
97
98
|
|
|
98
99
|
@CONF[:BACK_TRACE_LIMIT] = 16
|
|
99
100
|
|
|
@@ -194,6 +195,8 @@ module IRB # :nodoc:
|
|
|
194
195
|
:'$' => :show_source,
|
|
195
196
|
:'@' => :whereami,
|
|
196
197
|
}
|
|
198
|
+
|
|
199
|
+
@CONF[:COPY_COMMAND] = ENV.fetch("IRB_COPY_COMMAND", nil)
|
|
197
200
|
end
|
|
198
201
|
|
|
199
202
|
def IRB.set_measure_callback(type = nil, arg = nil, &block)
|
|
@@ -343,6 +346,8 @@ module IRB # :nodoc:
|
|
|
343
346
|
opt = $1 || argv.shift
|
|
344
347
|
prompt_mode = opt.upcase.tr("-", "_").intern
|
|
345
348
|
@CONF[:PROMPT_MODE] = prompt_mode
|
|
349
|
+
when "--nobanner"
|
|
350
|
+
@CONF[:SHOW_BANNER] = false
|
|
346
351
|
when "--noprompt"
|
|
347
352
|
@CONF[:PROMPT_MODE] = :NULL
|
|
348
353
|
when "--script"
|