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.
@@ -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
- if length != code.bytesize
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 == :on_parse_error or token == :compile_error
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
@@ -7,7 +7,7 @@
7
7
  # From Original Idea of shugo@ruby-lang.org
8
8
  #
9
9
 
10
- autoload :RDoc, "rdoc"
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
- CompletionProc = proc { |input|
42
- retrieve_completion_data(input).compact.map{ |i| i.encode(Encoding.default_external) }
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 = Regexp.quote($3)
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 = Regexp.quote($2)
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 = Regexp.quote($2)
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 = Regexp.quote($2)
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 = Regexp.quote($2)
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 = Regexp.quote($3)
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 = Regexp.quote($~[:mes])
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 = Regexp.quote($3)
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 = Regexp.quote($3)
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 = Regexp.quote($1)
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
@@ -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 /^#{Regexp.quote(File::Separator)}/ =~ fn
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
- irb.suspend_input_method(FileInputMethod.new(path)) do
54
- |back_io|
55
- irb.signal_status(:IN_LOAD) do
56
- if back_io.kind_of?(FileInputMethod)
57
- irb.eval_input
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
- rescue LoadAbort
62
- print "load abort!!\n"
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
- irb.suspend_input_method(FileInputMethod.new(path)) do
83
- |back_io|
84
- irb.signal_status(:IN_LOAD) do
85
- if back_io.kind_of?(FileInputMethod)
86
- irb.eval_input
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
- rescue LoadAbort
91
- print "load abort!!\n"
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
@@ -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
- open(history_file, "w:#{IRB.conf[:LC_MESSAGES].encoding}", 0600) do |f|
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
- begin
111
- hist = hist.last(num) if hist.size > num and num > 0
112
- rescue RangeError # bignum too big to convert into `long'
113
- # Do nothing because the bignum should be treated as inifinity
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