irb 1.9.0 → 1.10.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +3 -5
- data/README.md +11 -9
- data/Rakefile +2 -2
- data/lib/irb/cmd/history.rb +47 -0
- data/lib/irb/cmd/show_cmds.rb +6 -0
- data/lib/irb/cmd/show_source.rb +9 -2
- data/lib/irb/completion.rb +25 -4
- data/lib/irb/context.rb +22 -14
- data/lib/irb/debug.rb +18 -0
- data/lib/irb/extend-command.rb +6 -0
- data/lib/irb/history.rb +1 -1
- data/lib/irb/init.rb +4 -5
- data/lib/irb/input-method.rb +3 -0
- data/lib/irb/lc/help-message +1 -0
- data/lib/irb/pager.rb +12 -11
- data/lib/irb/source_finder.rb +26 -6
- data/lib/irb/version.rb +2 -2
- data/lib/irb.rb +48 -40
- metadata +4 -8
- data/lib/irb/type_completion/completor.rb +0 -235
- data/lib/irb/type_completion/methods.rb +0 -13
- data/lib/irb/type_completion/scope.rb +0 -412
- data/lib/irb/type_completion/type_analyzer.rb +0 -1169
- data/lib/irb/type_completion/types.rb +0 -426
data/lib/irb.rb
CHANGED
@@ -20,6 +20,7 @@ require_relative "irb/color"
|
|
20
20
|
require_relative "irb/version"
|
21
21
|
require_relative "irb/easter-egg"
|
22
22
|
require_relative "irb/debug"
|
23
|
+
require_relative "irb/pager"
|
23
24
|
|
24
25
|
# IRB stands for "interactive Ruby" and is a tool to interactively execute Ruby
|
25
26
|
# expressions read from the standard input.
|
@@ -697,7 +698,7 @@ module IRB
|
|
697
698
|
end
|
698
699
|
|
699
700
|
def handle_exception(exc)
|
700
|
-
if exc.backtrace
|
701
|
+
if exc.backtrace[0] =~ /\/irb(2)?(\/.*|-.*|\.rb)?:/ && exc.class.to_s !~ /^IRB/ &&
|
701
702
|
!(SyntaxError === exc) && !(EncodingError === exc)
|
702
703
|
# The backtrace of invalid encoding hash (ex. {"\xAE": 1}) raises EncodingError without lineno.
|
703
704
|
irb_bug = true
|
@@ -705,45 +706,49 @@ module IRB
|
|
705
706
|
irb_bug = false
|
706
707
|
end
|
707
708
|
|
708
|
-
if
|
709
|
-
|
710
|
-
|
711
|
-
|
712
|
-
|
713
|
-
order = :bottom
|
714
|
-
else
|
715
|
-
message = exc.full_message(order: :top)
|
716
|
-
order = :top
|
717
|
-
end
|
718
|
-
else # '3.0.0' <= RUBY_VERSION
|
709
|
+
if RUBY_VERSION < '3.0.0'
|
710
|
+
if STDOUT.tty?
|
711
|
+
message = exc.full_message(order: :bottom)
|
712
|
+
order = :bottom
|
713
|
+
else
|
719
714
|
message = exc.full_message(order: :top)
|
720
715
|
order = :top
|
721
716
|
end
|
722
|
-
|
723
|
-
message =
|
724
|
-
|
725
|
-
|
726
|
-
|
727
|
-
|
728
|
-
|
729
|
-
|
730
|
-
|
731
|
-
|
732
|
-
|
733
|
-
|
734
|
-
|
735
|
-
|
736
|
-
|
737
|
-
|
717
|
+
else # '3.0.0' <= RUBY_VERSION
|
718
|
+
message = exc.full_message(order: :top)
|
719
|
+
order = :top
|
720
|
+
end
|
721
|
+
message = convert_invalid_byte_sequence(message, exc.message.encoding)
|
722
|
+
message = encode_with_invalid_byte_sequence(message, IRB.conf[:LC_MESSAGES].encoding) unless message.encoding.to_s.casecmp?(IRB.conf[:LC_MESSAGES].encoding.to_s)
|
723
|
+
message = message.gsub(/((?:^\t.+$\n)+)/) { |m|
|
724
|
+
case order
|
725
|
+
when :top
|
726
|
+
lines = m.split("\n")
|
727
|
+
when :bottom
|
728
|
+
lines = m.split("\n").reverse
|
729
|
+
end
|
730
|
+
unless irb_bug
|
731
|
+
lines = lines.map { |l| @context.workspace.filter_backtrace(l) }.compact
|
732
|
+
if lines.size > @context.back_trace_limit
|
733
|
+
omit = lines.size - @context.back_trace_limit
|
734
|
+
lines = lines[0..(@context.back_trace_limit - 1)]
|
735
|
+
lines << "\t... %d levels..." % omit
|
738
736
|
end
|
739
|
-
|
740
|
-
|
741
|
-
}
|
742
|
-
|
743
|
-
|
744
|
-
|
737
|
+
end
|
738
|
+
lines = lines.reverse if order == :bottom
|
739
|
+
lines.map{ |l| l + "\n" }.join
|
740
|
+
}
|
741
|
+
# The "<top (required)>" in "(irb)" may be the top level of IRB so imitate the main object.
|
742
|
+
message = message.gsub(/\(irb\):(?<num>\d+):in `<(?<frame>top \(required\))>'/) { "(irb):#{$~[:num]}:in `<main>'" }
|
743
|
+
puts message
|
744
|
+
puts 'Maybe IRB bug!' if irb_bug
|
745
|
+
rescue Exception => handler_exc
|
746
|
+
begin
|
747
|
+
puts exc.inspect
|
748
|
+
puts "backtraces are hidden because #{handler_exc} was raised when processing them"
|
749
|
+
rescue Exception
|
750
|
+
puts 'Uninspectable exception occurred'
|
745
751
|
end
|
746
|
-
print "Maybe IRB bug!\n" if irb_bug
|
747
752
|
end
|
748
753
|
|
749
754
|
# Evaluates the given block using the given +path+ as the Context#irb_path
|
@@ -855,11 +860,12 @@ module IRB
|
|
855
860
|
end
|
856
861
|
end
|
857
862
|
end
|
863
|
+
|
858
864
|
if multiline_p && @context.newline_before_multiline_output?
|
859
|
-
|
860
|
-
else
|
861
|
-
printf @context.return_format, str
|
865
|
+
str = "\n" + str
|
862
866
|
end
|
867
|
+
|
868
|
+
Pager.page_content(format(@context.return_format, str), retain_content: true)
|
863
869
|
end
|
864
870
|
|
865
871
|
# Outputs the local variables to this current session, including
|
@@ -926,9 +932,11 @@ module IRB
|
|
926
932
|
when "N"
|
927
933
|
@context.irb_name
|
928
934
|
when "m"
|
929
|
-
|
935
|
+
main_str = @context.main.to_s rescue "!#{$!.class}"
|
936
|
+
truncate_prompt_main(main_str)
|
930
937
|
when "M"
|
931
|
-
|
938
|
+
main_str = @context.main.inspect rescue "!#{$!.class}"
|
939
|
+
truncate_prompt_main(main_str)
|
932
940
|
when "l"
|
933
941
|
ltype
|
934
942
|
when "i"
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: irb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.10.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- aycabta
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: exe
|
11
11
|
cert_chain: []
|
12
|
-
date: 2023-
|
12
|
+
date: 2023-12-03 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: reline
|
@@ -70,6 +70,7 @@ files:
|
|
70
70
|
- lib/irb/cmd/edit.rb
|
71
71
|
- lib/irb/cmd/finish.rb
|
72
72
|
- lib/irb/cmd/help.rb
|
73
|
+
- lib/irb/cmd/history.rb
|
73
74
|
- lib/irb/cmd/info.rb
|
74
75
|
- lib/irb/cmd/irb_info.rb
|
75
76
|
- lib/irb/cmd/load.rb
|
@@ -118,11 +119,6 @@ files:
|
|
118
119
|
- lib/irb/ruby_logo.aa
|
119
120
|
- lib/irb/source_finder.rb
|
120
121
|
- lib/irb/statement.rb
|
121
|
-
- lib/irb/type_completion/completor.rb
|
122
|
-
- lib/irb/type_completion/methods.rb
|
123
|
-
- lib/irb/type_completion/scope.rb
|
124
|
-
- lib/irb/type_completion/type_analyzer.rb
|
125
|
-
- lib/irb/type_completion/types.rb
|
126
122
|
- lib/irb/version.rb
|
127
123
|
- lib/irb/workspace.rb
|
128
124
|
- lib/irb/ws-for-case-2.rb
|
@@ -152,7 +148,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
152
148
|
- !ruby/object:Gem::Version
|
153
149
|
version: '0'
|
154
150
|
requirements: []
|
155
|
-
rubygems_version: 3.
|
151
|
+
rubygems_version: 3.4.10
|
156
152
|
signing_key:
|
157
153
|
specification_version: 4
|
158
154
|
summary: Interactive Ruby command-line tool for REPL (Read Eval Print Loop).
|
@@ -1,235 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'prism'
|
4
|
-
require 'irb/completion'
|
5
|
-
require_relative 'type_analyzer'
|
6
|
-
|
7
|
-
module IRB
|
8
|
-
module TypeCompletion
|
9
|
-
class Completor < BaseCompletor # :nodoc:
|
10
|
-
HIDDEN_METHODS = %w[Namespace TypeName] # defined by rbs, should be hidden
|
11
|
-
|
12
|
-
class << self
|
13
|
-
attr_accessor :last_completion_error
|
14
|
-
end
|
15
|
-
|
16
|
-
def inspect
|
17
|
-
name = 'TypeCompletion::Completor'
|
18
|
-
prism_info = "Prism: #{Prism::VERSION}"
|
19
|
-
if Types.rbs_builder
|
20
|
-
"#{name}(#{prism_info}, RBS: #{RBS::VERSION})"
|
21
|
-
elsif Types.rbs_load_error
|
22
|
-
"#{name}(#{prism_info}, RBS: #{Types.rbs_load_error.inspect})"
|
23
|
-
else
|
24
|
-
"#{name}(#{prism_info}, RBS: loading)"
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
def completion_candidates(preposing, target, _postposing, bind:)
|
29
|
-
@preposing = preposing
|
30
|
-
verbose, $VERBOSE = $VERBOSE, nil
|
31
|
-
code = "#{preposing}#{target}"
|
32
|
-
@result = analyze code, bind
|
33
|
-
name, candidates = candidates_from_result(@result)
|
34
|
-
|
35
|
-
all_symbols_pattern = /\A[ -\/:-@\[-`\{-~]*\z/
|
36
|
-
candidates.map(&:to_s).select { !_1.match?(all_symbols_pattern) && _1.start_with?(name) }.uniq.sort.map do
|
37
|
-
target + _1[name.size..]
|
38
|
-
end
|
39
|
-
rescue SyntaxError, StandardError => e
|
40
|
-
Completor.last_completion_error = e
|
41
|
-
handle_error(e)
|
42
|
-
[]
|
43
|
-
ensure
|
44
|
-
$VERBOSE = verbose
|
45
|
-
end
|
46
|
-
|
47
|
-
def doc_namespace(preposing, matched, postposing, bind:)
|
48
|
-
name = matched[/[a-zA-Z_0-9]*[!?=]?\z/]
|
49
|
-
method_doc = -> type do
|
50
|
-
type = type.types.find { _1.all_methods.include? name.to_sym }
|
51
|
-
case type
|
52
|
-
when Types::SingletonType
|
53
|
-
"#{Types.class_name_of(type.module_or_class)}.#{name}"
|
54
|
-
when Types::InstanceType
|
55
|
-
"#{Types.class_name_of(type.klass)}##{name}"
|
56
|
-
end
|
57
|
-
end
|
58
|
-
call_or_const_doc = -> type do
|
59
|
-
if name =~ /\A[A-Z]/
|
60
|
-
type = type.types.grep(Types::SingletonType).find { _1.module_or_class.const_defined?(name) }
|
61
|
-
type.module_or_class == Object ? name : "#{Types.class_name_of(type.module_or_class)}::#{name}" if type
|
62
|
-
else
|
63
|
-
method_doc.call(type)
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
value_doc = -> type do
|
68
|
-
return unless type
|
69
|
-
type.types.each do |t|
|
70
|
-
case t
|
71
|
-
when Types::SingletonType
|
72
|
-
return Types.class_name_of(t.module_or_class)
|
73
|
-
when Types::InstanceType
|
74
|
-
return Types.class_name_of(t.klass)
|
75
|
-
end
|
76
|
-
end
|
77
|
-
nil
|
78
|
-
end
|
79
|
-
|
80
|
-
case @result
|
81
|
-
in [:call_or_const, type, _name, _self_call]
|
82
|
-
call_or_const_doc.call type
|
83
|
-
in [:const, type, _name, scope]
|
84
|
-
if type
|
85
|
-
call_or_const_doc.call type
|
86
|
-
else
|
87
|
-
value_doc.call scope[name]
|
88
|
-
end
|
89
|
-
in [:gvar, _name, scope]
|
90
|
-
value_doc.call scope["$#{name}"]
|
91
|
-
in [:ivar, _name, scope]
|
92
|
-
value_doc.call scope["@#{name}"]
|
93
|
-
in [:cvar, _name, scope]
|
94
|
-
value_doc.call scope["@@#{name}"]
|
95
|
-
in [:call, type, _name, _self_call]
|
96
|
-
method_doc.call type
|
97
|
-
in [:lvar_or_method, _name, scope]
|
98
|
-
if scope.local_variables.include?(name)
|
99
|
-
value_doc.call scope[name]
|
100
|
-
else
|
101
|
-
method_doc.call scope.self_type
|
102
|
-
end
|
103
|
-
else
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
|
-
def candidates_from_result(result)
|
108
|
-
candidates = case result
|
109
|
-
in [:require, name]
|
110
|
-
retrieve_files_to_require_from_load_path
|
111
|
-
in [:require_relative, name]
|
112
|
-
retrieve_files_to_require_relative_from_current_dir
|
113
|
-
in [:call_or_const, type, name, self_call]
|
114
|
-
((self_call ? type.all_methods : type.methods).map(&:to_s) - HIDDEN_METHODS) | type.constants
|
115
|
-
in [:const, type, name, scope]
|
116
|
-
if type
|
117
|
-
scope_constants = type.types.flat_map do |t|
|
118
|
-
scope.table_module_constants(t.module_or_class) if t.is_a?(Types::SingletonType)
|
119
|
-
end
|
120
|
-
(scope_constants.compact | type.constants.map(&:to_s)).sort
|
121
|
-
else
|
122
|
-
scope.constants.sort | ReservedWords
|
123
|
-
end
|
124
|
-
in [:ivar, name, scope]
|
125
|
-
ivars = scope.instance_variables.sort
|
126
|
-
name == '@' ? ivars + scope.class_variables.sort : ivars
|
127
|
-
in [:cvar, name, scope]
|
128
|
-
scope.class_variables
|
129
|
-
in [:gvar, name, scope]
|
130
|
-
scope.global_variables
|
131
|
-
in [:symbol, name]
|
132
|
-
Symbol.all_symbols.map { _1.inspect[1..] }
|
133
|
-
in [:call, type, name, self_call]
|
134
|
-
(self_call ? type.all_methods : type.methods).map(&:to_s) - HIDDEN_METHODS
|
135
|
-
in [:lvar_or_method, name, scope]
|
136
|
-
scope.self_type.all_methods.map(&:to_s) | scope.local_variables | ReservedWords
|
137
|
-
else
|
138
|
-
[]
|
139
|
-
end
|
140
|
-
[name || '', candidates]
|
141
|
-
end
|
142
|
-
|
143
|
-
def analyze(code, binding = Object::TOPLEVEL_BINDING)
|
144
|
-
# Workaround for https://github.com/ruby/prism/issues/1592
|
145
|
-
return if code.match?(/%[qQ]\z/)
|
146
|
-
|
147
|
-
ast = Prism.parse(code, scopes: [binding.local_variables]).value
|
148
|
-
name = code[/(@@|@|\$)?\w*[!?=]?\z/]
|
149
|
-
*parents, target_node = find_target ast, code.bytesize - name.bytesize
|
150
|
-
return unless target_node
|
151
|
-
|
152
|
-
calculate_scope = -> { TypeAnalyzer.calculate_target_type_scope(binding, parents, target_node).last }
|
153
|
-
calculate_type_scope = ->(node) { TypeAnalyzer.calculate_target_type_scope binding, [*parents, target_node], node }
|
154
|
-
|
155
|
-
case target_node
|
156
|
-
when Prism::StringNode, Prism::InterpolatedStringNode
|
157
|
-
call_node, args_node = parents.last(2)
|
158
|
-
return unless call_node.is_a?(Prism::CallNode) && call_node.receiver.nil?
|
159
|
-
return unless args_node.is_a?(Prism::ArgumentsNode) && args_node.arguments.size == 1
|
160
|
-
|
161
|
-
case call_node.name
|
162
|
-
when :require
|
163
|
-
[:require, name.rstrip]
|
164
|
-
when :require_relative
|
165
|
-
[:require_relative, name.rstrip]
|
166
|
-
end
|
167
|
-
when Prism::SymbolNode
|
168
|
-
if parents.last.is_a? Prism::BlockArgumentNode # method(&:target)
|
169
|
-
receiver_type, _scope = calculate_type_scope.call target_node
|
170
|
-
[:call, receiver_type, name, false]
|
171
|
-
else
|
172
|
-
[:symbol, name] unless name.empty?
|
173
|
-
end
|
174
|
-
when Prism::CallNode
|
175
|
-
return [:lvar_or_method, name, calculate_scope.call] if target_node.receiver.nil?
|
176
|
-
|
177
|
-
self_call = target_node.receiver.is_a? Prism::SelfNode
|
178
|
-
op = target_node.call_operator
|
179
|
-
receiver_type, _scope = calculate_type_scope.call target_node.receiver
|
180
|
-
receiver_type = receiver_type.nonnillable if op == '&.'
|
181
|
-
[op == '::' ? :call_or_const : :call, receiver_type, name, self_call]
|
182
|
-
when Prism::LocalVariableReadNode, Prism::LocalVariableTargetNode
|
183
|
-
[:lvar_or_method, name, calculate_scope.call]
|
184
|
-
when Prism::ConstantReadNode, Prism::ConstantTargetNode
|
185
|
-
if parents.last.is_a? Prism::ConstantPathNode
|
186
|
-
path_node = parents.last
|
187
|
-
if path_node.parent # A::B
|
188
|
-
receiver, scope = calculate_type_scope.call(path_node.parent)
|
189
|
-
[:const, receiver, name, scope]
|
190
|
-
else # ::A
|
191
|
-
scope = calculate_scope.call
|
192
|
-
[:const, Types::SingletonType.new(Object), name, scope]
|
193
|
-
end
|
194
|
-
else
|
195
|
-
[:const, nil, name, calculate_scope.call]
|
196
|
-
end
|
197
|
-
when Prism::GlobalVariableReadNode, Prism::GlobalVariableTargetNode
|
198
|
-
[:gvar, name, calculate_scope.call]
|
199
|
-
when Prism::InstanceVariableReadNode, Prism::InstanceVariableTargetNode
|
200
|
-
[:ivar, name, calculate_scope.call]
|
201
|
-
when Prism::ClassVariableReadNode, Prism::ClassVariableTargetNode
|
202
|
-
[:cvar, name, calculate_scope.call]
|
203
|
-
end
|
204
|
-
end
|
205
|
-
|
206
|
-
def find_target(node, position)
|
207
|
-
location = (
|
208
|
-
case node
|
209
|
-
when Prism::CallNode
|
210
|
-
node.message_loc
|
211
|
-
when Prism::SymbolNode
|
212
|
-
node.value_loc
|
213
|
-
when Prism::StringNode
|
214
|
-
node.content_loc
|
215
|
-
when Prism::InterpolatedStringNode
|
216
|
-
node.closing_loc if node.parts.empty?
|
217
|
-
end
|
218
|
-
)
|
219
|
-
return [node] if location&.start_offset == position
|
220
|
-
|
221
|
-
node.compact_child_nodes.each do |n|
|
222
|
-
match = find_target(n, position)
|
223
|
-
next unless match
|
224
|
-
match.unshift node
|
225
|
-
return match
|
226
|
-
end
|
227
|
-
|
228
|
-
[node] if node.location.start_offset == position
|
229
|
-
end
|
230
|
-
|
231
|
-
def handle_error(e)
|
232
|
-
end
|
233
|
-
end
|
234
|
-
end
|
235
|
-
end
|
@@ -1,13 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module IRB
|
4
|
-
module TypeCompletion
|
5
|
-
module Methods
|
6
|
-
OBJECT_SINGLETON_CLASS_METHOD = Object.instance_method(:singleton_class)
|
7
|
-
OBJECT_INSTANCE_VARIABLES_METHOD = Object.instance_method(:instance_variables)
|
8
|
-
OBJECT_INSTANCE_VARIABLE_GET_METHOD = Object.instance_method(:instance_variable_get)
|
9
|
-
OBJECT_CLASS_METHOD = Object.instance_method(:class)
|
10
|
-
MODULE_NAME_METHOD = Module.instance_method(:name)
|
11
|
-
end
|
12
|
-
end
|
13
|
-
end
|