irb 1.3.0 → 1.3.5
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/Rakefile +7 -0
- data/irb.gemspec +1 -44
- data/lib/irb.rb +76 -46
- data/lib/irb/cmd/info.rb +25 -0
- data/lib/irb/cmd/ls.rb +83 -0
- data/lib/irb/cmd/measure.rb +10 -4
- data/lib/irb/cmd/nop.rb +10 -4
- data/lib/irb/cmd/show_source.rb +86 -0
- data/lib/irb/cmd/whereami.rb +20 -0
- data/lib/irb/color.rb +20 -4
- data/lib/irb/color_printer.rb +47 -0
- data/lib/irb/completion.rb +84 -14
- data/lib/irb/ext/loader.rb +46 -19
- data/lib/irb/ext/save-history.rb +15 -5
- data/lib/irb/extend-command.rb +21 -4
- data/lib/irb/init.rb +11 -1
- data/lib/irb/input-method.rb +19 -2
- data/lib/irb/inspector.rb +12 -14
- data/lib/irb/lc/help-message +6 -6
- data/lib/irb/ruby-lex.rb +232 -43
- data/lib/irb/version.rb +2 -2
- data/lib/irb/workspace.rb +2 -1
- metadata +7 -2
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "nop"
|
4
|
+
|
5
|
+
# :stopdoc:
|
6
|
+
module IRB
|
7
|
+
module ExtendCommand
|
8
|
+
class Whereami < Nop
|
9
|
+
def execute(*)
|
10
|
+
code = irb_context.workspace.code_around_binding
|
11
|
+
if code
|
12
|
+
puts code
|
13
|
+
else
|
14
|
+
puts "The current context doesn't have code."
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
# :startdoc:
|
data/lib/irb/color.rb
CHANGED
@@ -17,7 +17,7 @@ module IRB # :nodoc:
|
|
17
17
|
CYAN = 36
|
18
18
|
|
19
19
|
TOKEN_KEYWORDS = {
|
20
|
-
on_kw: ['nil', 'self', 'true', 'false', '__FILE__', '__LINE__'],
|
20
|
+
on_kw: ['nil', 'self', 'true', 'false', '__FILE__', '__LINE__', '__ENCODING__'],
|
21
21
|
on_const: ['ENV'],
|
22
22
|
}
|
23
23
|
private_constant :TOKEN_KEYWORDS
|
@@ -60,6 +60,11 @@ module IRB # :nodoc:
|
|
60
60
|
on_words_beg: [[RED, BOLD], ALL],
|
61
61
|
on_parse_error: [[RED, REVERSE], ALL],
|
62
62
|
compile_error: [[RED, REVERSE], ALL],
|
63
|
+
on_assign_error: [[RED, REVERSE], ALL],
|
64
|
+
on_alias_error: [[RED, REVERSE], ALL],
|
65
|
+
on_class_name_error:[[RED, REVERSE], ALL],
|
66
|
+
on_param_error: [[RED, REVERSE], ALL],
|
67
|
+
on___end__: [[GREEN], ALL],
|
63
68
|
}
|
64
69
|
rescue NameError
|
65
70
|
# Give up highlighting Ripper-incompatible older Ruby
|
@@ -67,6 +72,9 @@ module IRB # :nodoc:
|
|
67
72
|
end
|
68
73
|
private_constant :TOKEN_SEQ_EXPRS
|
69
74
|
|
75
|
+
ERROR_TOKENS = TOKEN_SEQ_EXPRS.keys.select { |k| k.to_s.end_with?('error') }
|
76
|
+
private_constant :ERROR_TOKENS
|
77
|
+
|
70
78
|
class << self
|
71
79
|
def colorable?
|
72
80
|
$stdout.tty? && supported? && (/mswin|mingw/ =~ RUBY_PLATFORM || (ENV.key?('TERM') && ENV['TERM'] != 'dumb'))
|
@@ -107,14 +115,20 @@ module IRB # :nodoc:
|
|
107
115
|
# If `complete` is false (code is incomplete), this does not warn compile_error.
|
108
116
|
# This option is needed to avoid warning a user when the compile_error is happening
|
109
117
|
# because the input is not wrong but just incomplete.
|
110
|
-
def colorize_code(code, complete: true)
|
118
|
+
def colorize_code(code, complete: true, ignore_error: false)
|
111
119
|
return code unless colorable?
|
112
120
|
|
113
121
|
symbol_state = SymbolState.new
|
114
122
|
colored = +''
|
115
123
|
length = 0
|
124
|
+
end_seen = false
|
116
125
|
|
117
126
|
scan(code, allow_last_error: !complete) do |token, str, expr|
|
127
|
+
# IRB::ColorPrinter skips colorizing fragments with any invalid token
|
128
|
+
if ignore_error && ERROR_TOKENS.include?(token)
|
129
|
+
return Reline::Unicode.escape_for_print(code)
|
130
|
+
end
|
131
|
+
|
118
132
|
in_symbol = symbol_state.scan_token(token)
|
119
133
|
str.each_line do |line|
|
120
134
|
line = Reline::Unicode.escape_for_print(line)
|
@@ -126,10 +140,11 @@ module IRB # :nodoc:
|
|
126
140
|
end
|
127
141
|
end
|
128
142
|
length += str.bytesize
|
143
|
+
end_seen = true if token == :on___end__
|
129
144
|
end
|
130
145
|
|
131
146
|
# give up colorizing incomplete Ripper tokens
|
132
|
-
|
147
|
+
unless end_seen or length == code.bytesize
|
133
148
|
return Reline::Unicode.escape_for_print(code)
|
134
149
|
end
|
135
150
|
|
@@ -180,11 +195,12 @@ module IRB # :nodoc:
|
|
180
195
|
end
|
181
196
|
end
|
182
197
|
end
|
198
|
+
ensure
|
183
199
|
$VERBOSE = verbose
|
184
200
|
end
|
185
201
|
|
186
202
|
def dispatch_seq(token, expr, str, in_symbol:)
|
187
|
-
if token
|
203
|
+
if ERROR_TOKENS.include?(token)
|
188
204
|
TOKEN_SEQ_EXPRS[token][0]
|
189
205
|
elsif in_symbol
|
190
206
|
[YELLOW]
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'pp'
|
3
|
+
require 'irb/color'
|
4
|
+
|
5
|
+
module IRB
|
6
|
+
class ColorPrinter < ::PP
|
7
|
+
class << self
|
8
|
+
def pp(obj, out = $>, width = screen_width)
|
9
|
+
q = ColorPrinter.new(out, width)
|
10
|
+
q.guard_inspect_key {q.pp obj}
|
11
|
+
q.flush
|
12
|
+
out << "\n"
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def screen_width
|
18
|
+
Reline.get_screen_size.last
|
19
|
+
rescue Errno::EINVAL # in `winsize': Invalid argument - <STDIN>
|
20
|
+
79
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def pp(obj)
|
25
|
+
if obj.is_a?(String)
|
26
|
+
# Avoid calling Ruby 2.4+ String#pretty_print that splits a string by "\n"
|
27
|
+
text(obj.inspect)
|
28
|
+
else
|
29
|
+
super
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def text(str, width = nil)
|
34
|
+
unless str.is_a?(String)
|
35
|
+
str = str.inspect
|
36
|
+
end
|
37
|
+
width ||= str.length
|
38
|
+
|
39
|
+
case str
|
40
|
+
when /\A#</, '=', '>'
|
41
|
+
super(Color.colorize(str, [:GREEN]), width)
|
42
|
+
else
|
43
|
+
super(Color.colorize_code(str, ignore_error: true), width)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
data/lib/irb/completion.rb
CHANGED
@@ -7,7 +7,7 @@
|
|
7
7
|
# From Original Idea of shugo@ruby-lang.org
|
8
8
|
#
|
9
9
|
|
10
|
-
|
10
|
+
require_relative 'ruby-lex'
|
11
11
|
|
12
12
|
module IRB
|
13
13
|
module InputCompletor # :nodoc:
|
@@ -38,8 +38,69 @@ module IRB
|
|
38
38
|
|
39
39
|
BASIC_WORD_BREAK_CHARACTERS = " \t\n`><=;|&{("
|
40
40
|
|
41
|
-
|
42
|
-
|
41
|
+
def self.retrieve_files_to_require_from_load_path
|
42
|
+
@@files_from_load_path ||= $LOAD_PATH.flat_map { |path|
|
43
|
+
begin
|
44
|
+
Dir.glob("**/*.{rb,#{RbConfig::CONFIG['DLEXT']}}", base: path)
|
45
|
+
rescue Errno::ENOENT
|
46
|
+
[]
|
47
|
+
end
|
48
|
+
}.uniq.map { |path|
|
49
|
+
path.sub(/\.(rb|#{RbConfig::CONFIG['DLEXT']})\z/, '')
|
50
|
+
}
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.retrieve_files_to_require_relative_from_current_dir
|
54
|
+
@@files_from_current_dir ||= Dir.glob("**/*.{rb,#{RbConfig::CONFIG['DLEXT']}}", base: '.').map { |path|
|
55
|
+
path.sub(/\.(rb|#{RbConfig::CONFIG['DLEXT']})\z/, '')
|
56
|
+
}
|
57
|
+
end
|
58
|
+
|
59
|
+
CompletionRequireProc = lambda { |target, preposing = nil, postposing = nil|
|
60
|
+
if target =~ /\A(['"])([^'"]+)\Z/
|
61
|
+
quote = $1
|
62
|
+
actual_target = $2
|
63
|
+
else
|
64
|
+
return nil # It's not String literal
|
65
|
+
end
|
66
|
+
tokens = RubyLex.ripper_lex_without_warning(preposing.gsub(/\s*\z/, ''))
|
67
|
+
tok = nil
|
68
|
+
tokens.reverse_each do |t|
|
69
|
+
unless [:on_lparen, :on_sp, :on_ignored_sp, :on_nl, :on_ignored_nl, :on_comment].include?(t.event)
|
70
|
+
tok = t
|
71
|
+
break
|
72
|
+
end
|
73
|
+
end
|
74
|
+
result = []
|
75
|
+
if tok && tok.event == :on_ident && tok.state == Ripper::EXPR_CMDARG
|
76
|
+
case tok.tok
|
77
|
+
when 'require'
|
78
|
+
result = retrieve_files_to_require_from_load_path.select { |path|
|
79
|
+
path.start_with?(actual_target)
|
80
|
+
}.map { |path|
|
81
|
+
quote + path
|
82
|
+
}
|
83
|
+
when 'require_relative'
|
84
|
+
result = retrieve_files_to_require_relative_from_current_dir.select { |path|
|
85
|
+
path.start_with?(actual_target)
|
86
|
+
}.map { |path|
|
87
|
+
quote + path
|
88
|
+
}
|
89
|
+
end
|
90
|
+
end
|
91
|
+
result
|
92
|
+
}
|
93
|
+
|
94
|
+
CompletionProc = lambda { |target, preposing = nil, postposing = nil|
|
95
|
+
if preposing && postposing
|
96
|
+
result = CompletionRequireProc.(target, preposing, postposing)
|
97
|
+
unless result
|
98
|
+
result = retrieve_completion_data(target).compact.map{ |i| i.encode(Encoding.default_external) }
|
99
|
+
end
|
100
|
+
result
|
101
|
+
else
|
102
|
+
retrieve_completion_data(target).compact.map{ |i| i.encode(Encoding.default_external) }
|
103
|
+
end
|
43
104
|
}
|
44
105
|
|
45
106
|
def self.retrieve_completion_data(input, bind: IRB.conf[:MAIN_CONTEXT].workspace.binding, doc_namespace: false)
|
@@ -47,7 +108,7 @@ module IRB
|
|
47
108
|
when /^((["'`]).*\2)\.([^.]*)$/
|
48
109
|
# String
|
49
110
|
receiver = $1
|
50
|
-
message =
|
111
|
+
message = $3
|
51
112
|
|
52
113
|
candidates = String.instance_methods.collect{|m| m.to_s}
|
53
114
|
if doc_namespace
|
@@ -59,7 +120,7 @@ module IRB
|
|
59
120
|
when /^(\/[^\/]*\/)\.([^.]*)$/
|
60
121
|
# Regexp
|
61
122
|
receiver = $1
|
62
|
-
message =
|
123
|
+
message = $2
|
63
124
|
|
64
125
|
candidates = Regexp.instance_methods.collect{|m| m.to_s}
|
65
126
|
if doc_namespace
|
@@ -71,7 +132,7 @@ module IRB
|
|
71
132
|
when /^([^\]]*\])\.([^.]*)$/
|
72
133
|
# Array
|
73
134
|
receiver = $1
|
74
|
-
message =
|
135
|
+
message = $2
|
75
136
|
|
76
137
|
candidates = Array.instance_methods.collect{|m| m.to_s}
|
77
138
|
if doc_namespace
|
@@ -83,7 +144,7 @@ module IRB
|
|
83
144
|
when /^([^\}]*\})\.([^.]*)$/
|
84
145
|
# Proc or Hash
|
85
146
|
receiver = $1
|
86
|
-
message =
|
147
|
+
message = $2
|
87
148
|
|
88
149
|
proc_candidates = Proc.instance_methods.collect{|m| m.to_s}
|
89
150
|
hash_candidates = Hash.instance_methods.collect{|m| m.to_s}
|
@@ -117,7 +178,7 @@ module IRB
|
|
117
178
|
when /^([A-Z].*)::([^:.]*)$/
|
118
179
|
# Constant or class methods
|
119
180
|
receiver = $1
|
120
|
-
message =
|
181
|
+
message = $2
|
121
182
|
begin
|
122
183
|
candidates = eval("#{receiver}.constants.collect{|m| m.to_s}", bind)
|
123
184
|
candidates |= eval("#{receiver}.methods.collect{|m| m.to_s}", bind)
|
@@ -134,7 +195,7 @@ module IRB
|
|
134
195
|
# Symbol
|
135
196
|
receiver = $1
|
136
197
|
sep = $2
|
137
|
-
message =
|
198
|
+
message = $3
|
138
199
|
|
139
200
|
candidates = Symbol.instance_methods.collect{|m| m.to_s}
|
140
201
|
if doc_namespace
|
@@ -147,7 +208,7 @@ module IRB
|
|
147
208
|
# Numeric
|
148
209
|
receiver = $~[:num]
|
149
210
|
sep = $~[:sep]
|
150
|
-
message =
|
211
|
+
message = $~[:mes]
|
151
212
|
|
152
213
|
begin
|
153
214
|
instance = eval(receiver, bind)
|
@@ -169,7 +230,7 @@ module IRB
|
|
169
230
|
# Numeric(0xFFFF)
|
170
231
|
receiver = $1
|
171
232
|
sep = $2
|
172
|
-
message =
|
233
|
+
message = $3
|
173
234
|
|
174
235
|
begin
|
175
236
|
instance = eval(receiver, bind)
|
@@ -201,7 +262,7 @@ module IRB
|
|
201
262
|
# variable.func or func.func
|
202
263
|
receiver = $1
|
203
264
|
sep = $2
|
204
|
-
message =
|
265
|
+
message = $3
|
205
266
|
|
206
267
|
gv = eval("global_variables", bind).collect{|m| m.to_s}.push("true", "false", "nil")
|
207
268
|
lv = eval("local_variables", bind).collect{|m| m.to_s}
|
@@ -244,7 +305,7 @@ module IRB
|
|
244
305
|
# unknown(maybe String)
|
245
306
|
|
246
307
|
receiver = ""
|
247
|
-
message =
|
308
|
+
message = $1
|
248
309
|
|
249
310
|
candidates = String.instance_methods(true).collect{|m| m.to_s}
|
250
311
|
if doc_namespace
|
@@ -266,13 +327,22 @@ module IRB
|
|
266
327
|
end
|
267
328
|
|
268
329
|
PerfectMatchedProc = ->(matched, bind: IRB.conf[:MAIN_CONTEXT].workspace.binding) {
|
330
|
+
begin
|
331
|
+
require 'rdoc'
|
332
|
+
rescue LoadError
|
333
|
+
return
|
334
|
+
end
|
335
|
+
|
269
336
|
RDocRIDriver ||= RDoc::RI::Driver.new
|
337
|
+
|
270
338
|
if matched =~ /\A(?:::)?RubyVM/ and not ENV['RUBY_YES_I_AM_NOT_A_NORMAL_USER']
|
271
339
|
IRB.__send__(:easter_egg)
|
272
340
|
return
|
273
341
|
end
|
342
|
+
|
274
343
|
namespace = retrieve_completion_data(matched, bind: bind, doc_namespace: true)
|
275
344
|
return unless namespace
|
345
|
+
|
276
346
|
if namespace.is_a?(Array)
|
277
347
|
out = RDoc::Markup::Document.new
|
278
348
|
namespace.each do |m|
|
@@ -294,7 +364,7 @@ module IRB
|
|
294
364
|
Operators = %w[% & * ** + - / < << <= <=> == === =~ > >= >> [] []= ^ ! != !~]
|
295
365
|
|
296
366
|
def self.select_message(receiver, message, candidates, sep = ".")
|
297
|
-
candidates.grep(/^#{message}/).collect do |e|
|
367
|
+
candidates.grep(/^#{Regexp.quote(message)}/).collect do |e|
|
298
368
|
case e
|
299
369
|
when /^[a-zA-Z_]/
|
300
370
|
receiver + sep + e
|
data/lib/irb/ext/loader.rb
CHANGED
@@ -31,8 +31,31 @@ module IRB # :nodoc:
|
|
31
31
|
load_file(path, priv)
|
32
32
|
end
|
33
33
|
|
34
|
+
if File.respond_to?(:absolute_path?)
|
35
|
+
def absolute_path?(path)
|
36
|
+
File.absolute_path?(path)
|
37
|
+
end
|
38
|
+
else
|
39
|
+
separator =
|
40
|
+
if File::ALT_SEPARATOR
|
41
|
+
"[#{Regexp.quote(File::SEPARATOR + File::ALT_SEPARATOR)}]"
|
42
|
+
else
|
43
|
+
File::SEPARATOR
|
44
|
+
end
|
45
|
+
ABSOLUTE_PATH_PATTERN = # :nodoc:
|
46
|
+
case Dir.pwd
|
47
|
+
when /\A\w:/, /\A#{separator}{2}/
|
48
|
+
/\A(?:\w:|#{separator})#{separator}/
|
49
|
+
else
|
50
|
+
/\A#{separator}/
|
51
|
+
end
|
52
|
+
def absolute_path?(path)
|
53
|
+
ABSOLUTE_PATH_PATTERN =~ path
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
34
57
|
def search_file_from_ruby_path(fn) # :nodoc:
|
35
|
-
if
|
58
|
+
if absolute_path?(fn)
|
36
59
|
return fn if File.exist?(fn)
|
37
60
|
return nil
|
38
61
|
end
|
@@ -50,16 +73,18 @@ module IRB # :nodoc:
|
|
50
73
|
# See Irb#suspend_input_method for more information.
|
51
74
|
def source_file(path)
|
52
75
|
irb.suspend_name(path, File.basename(path)) do
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
else
|
59
|
-
begin
|
76
|
+
FileInputMethod.open(path) do |io|
|
77
|
+
irb.suspend_input_method(io) do
|
78
|
+
|back_io|
|
79
|
+
irb.signal_status(:IN_LOAD) do
|
80
|
+
if back_io.kind_of?(FileInputMethod)
|
60
81
|
irb.eval_input
|
61
|
-
|
62
|
-
|
82
|
+
else
|
83
|
+
begin
|
84
|
+
irb.eval_input
|
85
|
+
rescue LoadAbort
|
86
|
+
print "load abort!!\n"
|
87
|
+
end
|
63
88
|
end
|
64
89
|
end
|
65
90
|
end
|
@@ -79,16 +104,18 @@ module IRB # :nodoc:
|
|
79
104
|
ws = WorkSpace.new
|
80
105
|
end
|
81
106
|
irb.suspend_workspace(ws) do
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
else
|
88
|
-
begin
|
107
|
+
FileInputMethod.open(path) do |io|
|
108
|
+
irb.suspend_input_method(io) do
|
109
|
+
|back_io|
|
110
|
+
irb.signal_status(:IN_LOAD) do
|
111
|
+
if back_io.kind_of?(FileInputMethod)
|
89
112
|
irb.eval_input
|
90
|
-
|
91
|
-
|
113
|
+
else
|
114
|
+
begin
|
115
|
+
irb.eval_input
|
116
|
+
rescue LoadAbort
|
117
|
+
print "load abort!!\n"
|
118
|
+
end
|
92
119
|
end
|
93
120
|
end
|
94
121
|
end
|
data/lib/irb/ext/save-history.rb
CHANGED
@@ -81,6 +81,8 @@ module IRB
|
|
81
81
|
end
|
82
82
|
}
|
83
83
|
end
|
84
|
+
@loaded_history_lines = history.size
|
85
|
+
@loaded_history_mtime = File.mtime(history_file)
|
84
86
|
end
|
85
87
|
end
|
86
88
|
|
@@ -105,12 +107,20 @@ module IRB
|
|
105
107
|
raise
|
106
108
|
end
|
107
109
|
|
108
|
-
|
110
|
+
if File.exist?(history_file) && @loaded_history_mtime &&
|
111
|
+
File.mtime(history_file) != @loaded_history_mtime
|
112
|
+
history = history[@loaded_history_lines..-1]
|
113
|
+
append_history = true
|
114
|
+
end
|
115
|
+
|
116
|
+
open(history_file, "#{append_history ? 'a' : 'w'}:#{IRB.conf[:LC_MESSAGES].encoding}", 0600) do |f|
|
109
117
|
hist = history.map{ |l| l.split("\n").join("\\\n") }
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
118
|
+
unless append_history
|
119
|
+
begin
|
120
|
+
hist = hist.last(num) if hist.size > num and num > 0
|
121
|
+
rescue RangeError # bignum too big to convert into `long'
|
122
|
+
# Do nothing because the bignum should be treated as inifinity
|
123
|
+
end
|
114
124
|
end
|
115
125
|
f.puts(hist)
|
116
126
|
end
|