irb 1.2.9 → 1.3.4

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cb526faa67bbfaad97480a6e642dc161725c6c444dcb6e6080498fae665173a6
4
- data.tar.gz: 58c89d71946d694826f2409eaf4a49ed88644bf640564e2f3a6c9ab3d8e9c0e1
3
+ metadata.gz: e897a7306cd4fde4a54fa2cc4c8dff34ccf4ecd5e69e324d9c91901c9a683cde
4
+ data.tar.gz: d424e3853889fef65b7e01df7d58e27bbd7467e081c733d7937aadde0544f4e9
5
5
  SHA512:
6
- metadata.gz: 9e101c1374b8ea161ec76217739ce5dbcd93c9e85e3cc147852717634085b97e5bd1aa71a9a6c316a660e9c60f0d16a03d08c26e7b7e65bdbb44886295bfa8dd
7
- data.tar.gz: 6243fb79d8747f0c507ebbd7e3f4efd486b3848ea7b2bf4db5ee4bb48591ee20857bcf5d86f61520c7d17b1d9307dc28a2ac785a64dda83ba1c78ebff90d53b6
6
+ metadata.gz: 77001229102437b40664c561a4b2ef1f5672e2e82661332be8a9f770356640a7de588b17c6d66164edbcd504158effd2b075b58172b2863b3862aa8735a1a72f
7
+ data.tar.gz: f83d98526ea74c4f015c85377f3c8d569a4d2a1f507331db80c41ab2225d4512c8ad6937d9bb90f703b7c95c214cb36e2f1c1e5107bed62554ecf2ba2cc2aa91
data/irb.gemspec CHANGED
@@ -32,11 +32,14 @@ Gem::Specification.new do |spec|
32
32
  "lib/irb/cmd/chws.rb",
33
33
  "lib/irb/cmd/fork.rb",
34
34
  "lib/irb/cmd/help.rb",
35
+ "lib/irb/cmd/info.rb",
35
36
  "lib/irb/cmd/load.rb",
37
+ "lib/irb/cmd/measure.rb",
36
38
  "lib/irb/cmd/nop.rb",
37
39
  "lib/irb/cmd/pushws.rb",
38
40
  "lib/irb/cmd/subirb.rb",
39
41
  "lib/irb/color.rb",
42
+ "lib/irb/color_printer.rb",
40
43
  "lib/irb/completion.rb",
41
44
  "lib/irb/context.rb",
42
45
  "lib/irb/easter-egg.rb",
data/lib/irb.rb CHANGED
@@ -525,7 +525,7 @@ module IRB
525
525
  printf "Use \"exit\" to leave %s\n", @context.ap_name
526
526
  end
527
527
  else
528
- print "\n"
528
+ print "\n" if @context.prompting?
529
529
  end
530
530
  end
531
531
  l
@@ -574,10 +574,35 @@ module IRB
574
574
  next
575
575
  end
576
576
  handle_exception(exc)
577
+ @context.workspace.local_variable_set(:_, exc)
578
+ exc = nil
577
579
  end
578
580
  end
579
581
  end
580
582
 
583
+ def convert_invalid_byte_sequence(str)
584
+ str = str.force_encoding(Encoding::ASCII_8BIT)
585
+ conv = Encoding::Converter.new(Encoding::ASCII_8BIT, Encoding::UTF_8)
586
+ dst = String.new
587
+ begin
588
+ ret = conv.primitive_convert(str, dst)
589
+ case ret
590
+ when :invalid_byte_sequence
591
+ conv.insert_output(conf.primitive_errinfo[3].dump[1..-2])
592
+ redo
593
+ when :undefined_conversion
594
+ c = conv.primitive_errinfo[3].dup.force_encoding(conv.primitive_errinfo[1])
595
+ conv.insert_output(c.dump[1..-2])
596
+ redo
597
+ when :incomplete_input
598
+ conv.insert_output(conv.primitive_errinfo[3].dump[1..-2])
599
+ when :finished
600
+ end
601
+ break
602
+ end while nil
603
+ dst
604
+ end
605
+
581
606
  def handle_exception(exc)
582
607
  if exc.backtrace && exc.backtrace[0] =~ /\/irb(2)?(\/.*|-.*|\.rb)?:/ && exc.class.to_s !~ /^IRB/ &&
583
608
  !(SyntaxError === exc) && !(EncodingError === exc)
@@ -587,49 +612,44 @@ module IRB
587
612
  irb_bug = false
588
613
  end
589
614
 
590
- if STDOUT.tty?
591
- attr = ATTR_TTY
592
- print "#{attr[1]}Traceback#{attr[]} (most recent call last):\n"
593
- else
594
- attr = ATTR_PLAIN
595
- end
596
- messages = []
597
- lasts = []
598
- levels = 0
599
615
  if exc.backtrace
600
- count = 0
601
- exc.backtrace.each do |m|
602
- m = @context.workspace.filter_backtrace(m) or next unless irb_bug
603
- count += 1
604
- if attr == ATTR_TTY
605
- m = sprintf("%9d: from %s", count, m)
616
+ order = nil
617
+ if '2.5.0' == RUBY_VERSION
618
+ # Exception#full_message doesn't have keyword arguments.
619
+ message = exc.full_message # the same of (highlight: true, order: bottom)
620
+ order = :bottom
621
+ elsif '2.5.1' <= RUBY_VERSION && RUBY_VERSION < '3.0.0'
622
+ if STDOUT.tty?
623
+ message = exc.full_message(order: :bottom)
624
+ order = :bottom
606
625
  else
607
- m = "\tfrom #{m}"
608
- end
609
- if messages.size < @context.back_trace_limit
610
- messages.push(m)
611
- elsif lasts.size < @context.back_trace_limit
612
- lasts.push(m).shift
613
- levels += 1
626
+ message = exc.full_message(order: :top)
627
+ order = :top
614
628
  end
629
+ else # '3.0.0' <= RUBY_VERSION
630
+ message = exc.full_message(order: :top)
631
+ order = :top
615
632
  end
616
- end
617
- if attr == ATTR_TTY
618
- unless lasts.empty?
619
- puts lasts.reverse
620
- printf "... %d levels...\n", levels if levels > 0
621
- end
622
- puts messages.reverse
623
- end
624
- m = exc.to_s.split(/\n/)
625
- print "#{attr[1]}#{exc.class} (#{attr[4]}#{m.shift}#{attr[0, 1]})#{attr[]}\n"
626
- puts m.map {|s| "#{attr[1]}#{s}#{attr[]}\n"}
627
- if attr == ATTR_PLAIN
628
- puts messages
629
- unless lasts.empty?
630
- puts lasts
631
- printf "... %d levels...\n", levels if levels > 0
632
- end
633
+ message = convert_invalid_byte_sequence(message)
634
+ message = message.gsub(/((?:^\t.+$\n)+)/) { |m|
635
+ case order
636
+ when :top
637
+ lines = m.split("\n")
638
+ when :bottom
639
+ lines = m.split("\n").reverse
640
+ end
641
+ unless irb_bug
642
+ lines = lines.map { |l| @context.workspace.filter_backtrace(l) }.compact
643
+ if lines.size > @context.back_trace_limit
644
+ omit = lines.size - @context.back_trace_limit
645
+ lines = lines[0..(@context.back_trace_limit - 1)]
646
+ lines << "\t... %d levels..." % omit
647
+ end
648
+ end
649
+ lines = lines.reverse if order == :bottom
650
+ lines.map{ |l| l + "\n" }.join
651
+ }
652
+ puts message
633
653
  end
634
654
  print "Maybe IRB bug!\n" if irb_bug
635
655
  end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: false
2
+
3
+ require_relative "nop"
4
+
5
+ # :stopdoc:
6
+ module IRB
7
+ module ExtendCommand
8
+ class Info < Nop
9
+ def execute
10
+ Class.new {
11
+ def inspect
12
+ str = "Ruby version: #{RUBY_VERSION}\n"
13
+ str += "IRB version: #{IRB.version}\n"
14
+ str += "InputMethod: #{IRB.CurrentContext.io.inspect}\n"
15
+ str += ".irbrc path: #{IRB.rc_file}\n" if File.exist?(IRB.rc_file)
16
+ str += "RUBY_PLATFORM: #{RUBY_PLATFORM}\n"
17
+ str
18
+ end
19
+ alias_method :to_s, :inspect
20
+ }.new
21
+ end
22
+ end
23
+ end
24
+ end
25
+ # :startdoc:
@@ -0,0 +1,40 @@
1
+ require_relative "nop"
2
+
3
+ # :stopdoc:
4
+ module IRB
5
+ module ExtendCommand
6
+ class Measure < Nop
7
+ def initialize(*args)
8
+ super(*args)
9
+ end
10
+
11
+ def execute(type = nil, arg = nil, &block)
12
+ case type
13
+ when :off
14
+ IRB.conf[:MEASURE] = nil
15
+ IRB.unset_measure_callback(arg)
16
+ when :list
17
+ IRB.conf[:MEASURE_CALLBACKS].each do |type_name, _, arg_val|
18
+ puts "- #{type_name}" + (arg_val ? "(#{arg_val.inspect})" : '')
19
+ end
20
+ when :on
21
+ IRB.conf[:MEASURE] = true
22
+ added = IRB.set_measure_callback(type, arg)
23
+ puts "#{added[0]} is added." if added
24
+ else
25
+ if block_given?
26
+ IRB.conf[:MEASURE] = true
27
+ added = IRB.set_measure_callback(&block)
28
+ puts "#{added[0]} is added." if added
29
+ else
30
+ IRB.conf[:MEASURE] = true
31
+ added = IRB.set_measure_callback(type, arg)
32
+ puts "#{added[0]} is added." if added
33
+ end
34
+ end
35
+ nil
36
+ end
37
+ end
38
+ end
39
+ end
40
+ # :startdoc:
data/lib/irb/cmd/nop.rb CHANGED
@@ -15,9 +15,9 @@ module IRB
15
15
  class Nop
16
16
 
17
17
 
18
- def self.execute(conf, *opts)
18
+ def self.execute(conf, *opts, &block)
19
19
  command = new(conf)
20
- command.execute(*opts)
20
+ command.execute(*opts, &block)
21
21
  end
22
22
 
23
23
  def initialize(conf)
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,10 @@ 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],
63
67
  }
64
68
  rescue NameError
65
69
  # Give up highlighting Ripper-incompatible older Ruby
@@ -67,6 +71,9 @@ module IRB # :nodoc:
67
71
  end
68
72
  private_constant :TOKEN_SEQ_EXPRS
69
73
 
74
+ ERROR_TOKENS = TOKEN_SEQ_EXPRS.keys.select { |k| k.to_s.end_with?('error') }
75
+ private_constant :ERROR_TOKENS
76
+
70
77
  class << self
71
78
  def colorable?
72
79
  $stdout.tty? && supported? && (/mswin|mingw/ =~ RUBY_PLATFORM || (ENV.key?('TERM') && ENV['TERM'] != 'dumb'))
@@ -107,7 +114,7 @@ module IRB # :nodoc:
107
114
  # If `complete` is false (code is incomplete), this does not warn compile_error.
108
115
  # This option is needed to avoid warning a user when the compile_error is happening
109
116
  # because the input is not wrong but just incomplete.
110
- def colorize_code(code, complete: true)
117
+ def colorize_code(code, complete: true, ignore_error: false)
111
118
  return code unless colorable?
112
119
 
113
120
  symbol_state = SymbolState.new
@@ -115,6 +122,11 @@ module IRB # :nodoc:
115
122
  length = 0
116
123
 
117
124
  scan(code, allow_last_error: !complete) do |token, str, expr|
125
+ # IRB::ColorPrinter skips colorizing fragments with any invalid token
126
+ if ignore_error && ERROR_TOKENS.include?(token)
127
+ return Reline::Unicode.escape_for_print(code)
128
+ end
129
+
118
130
  in_symbol = symbol_state.scan_token(token)
119
131
  str.each_line do |line|
120
132
  line = Reline::Unicode.escape_for_print(line)
@@ -180,11 +192,12 @@ module IRB # :nodoc:
180
192
  end
181
193
  end
182
194
  end
195
+ ensure
183
196
  $VERBOSE = verbose
184
197
  end
185
198
 
186
199
  def dispatch_seq(token, expr, str, in_symbol:)
187
- if token == :on_parse_error or token == :compile_error
200
+ if ERROR_TOKENS.include?(token)
188
201
  TOKEN_SEQ_EXPRS[token][0]
189
202
  elsif in_symbol
190
203
  [YELLOW]
@@ -0,0 +1,38 @@
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 text(str, width = nil)
25
+ unless str.is_a?(String)
26
+ str = str.inspect
27
+ end
28
+ width ||= str.length
29
+
30
+ case str
31
+ when /\A#</, '=', '>'
32
+ super(Color.colorize(str, [:GREEN]), width)
33
+ else
34
+ super(Color.colorize_code(str, ignore_error: true), width)
35
+ end
36
+ end
37
+ end
38
+ end
@@ -47,7 +47,7 @@ module IRB
47
47
  when /^((["'`]).*\2)\.([^.]*)$/
48
48
  # String
49
49
  receiver = $1
50
- message = Regexp.quote($3)
50
+ message = $3
51
51
 
52
52
  candidates = String.instance_methods.collect{|m| m.to_s}
53
53
  if doc_namespace
@@ -59,7 +59,7 @@ module IRB
59
59
  when /^(\/[^\/]*\/)\.([^.]*)$/
60
60
  # Regexp
61
61
  receiver = $1
62
- message = Regexp.quote($2)
62
+ message = $2
63
63
 
64
64
  candidates = Regexp.instance_methods.collect{|m| m.to_s}
65
65
  if doc_namespace
@@ -71,7 +71,7 @@ module IRB
71
71
  when /^([^\]]*\])\.([^.]*)$/
72
72
  # Array
73
73
  receiver = $1
74
- message = Regexp.quote($2)
74
+ message = $2
75
75
 
76
76
  candidates = Array.instance_methods.collect{|m| m.to_s}
77
77
  if doc_namespace
@@ -83,7 +83,7 @@ module IRB
83
83
  when /^([^\}]*\})\.([^.]*)$/
84
84
  # Proc or Hash
85
85
  receiver = $1
86
- message = Regexp.quote($2)
86
+ message = $2
87
87
 
88
88
  proc_candidates = Proc.instance_methods.collect{|m| m.to_s}
89
89
  hash_candidates = Hash.instance_methods.collect{|m| m.to_s}
@@ -117,7 +117,7 @@ module IRB
117
117
  when /^([A-Z].*)::([^:.]*)$/
118
118
  # Constant or class methods
119
119
  receiver = $1
120
- message = Regexp.quote($2)
120
+ message = $2
121
121
  begin
122
122
  candidates = eval("#{receiver}.constants.collect{|m| m.to_s}", bind)
123
123
  candidates |= eval("#{receiver}.methods.collect{|m| m.to_s}", bind)
@@ -134,7 +134,7 @@ module IRB
134
134
  # Symbol
135
135
  receiver = $1
136
136
  sep = $2
137
- message = Regexp.quote($3)
137
+ message = $3
138
138
 
139
139
  candidates = Symbol.instance_methods.collect{|m| m.to_s}
140
140
  if doc_namespace
@@ -147,7 +147,7 @@ module IRB
147
147
  # Numeric
148
148
  receiver = $~[:num]
149
149
  sep = $~[:sep]
150
- message = Regexp.quote($~[:mes])
150
+ message = $~[:mes]
151
151
 
152
152
  begin
153
153
  instance = eval(receiver, bind)
@@ -169,7 +169,7 @@ module IRB
169
169
  # Numeric(0xFFFF)
170
170
  receiver = $1
171
171
  sep = $2
172
- message = Regexp.quote($3)
172
+ message = $3
173
173
 
174
174
  begin
175
175
  instance = eval(receiver, bind)
@@ -201,7 +201,7 @@ module IRB
201
201
  # variable.func or func.func
202
202
  receiver = $1
203
203
  sep = $2
204
- message = Regexp.quote($3)
204
+ message = $3
205
205
 
206
206
  gv = eval("global_variables", bind).collect{|m| m.to_s}.push("true", "false", "nil")
207
207
  lv = eval("local_variables", bind).collect{|m| m.to_s}
@@ -244,7 +244,7 @@ module IRB
244
244
  # unknown(maybe String)
245
245
 
246
246
  receiver = ""
247
- message = Regexp.quote($1)
247
+ message = $1
248
248
 
249
249
  candidates = String.instance_methods(true).collect{|m| m.to_s}
250
250
  if doc_namespace
@@ -294,7 +294,7 @@ module IRB
294
294
  Operators = %w[% & * ** + - / < << <= <=> == === =~ > >= >> [] []= ^ ! != !~]
295
295
 
296
296
  def self.select_message(receiver, message, candidates, sep = ".")
297
- candidates.grep(/^#{message}/).collect do |e|
297
+ candidates.grep(/^#{Regexp.quote(message)}/).collect do |e|
298
298
  case e
299
299
  when /^[a-zA-Z_]/
300
300
  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
@@ -177,9 +177,9 @@ module IRB # :nodoc:
177
177
  args << "&block"
178
178
  args = args.join(", ")
179
179
  line = __LINE__; eval %[
180
- unless self.class.class_variable_defined?(:@@#{cmd_name}_)
181
- self.class.class_variable_set(:@@#{cmd_name}_, true)
182
- def #{cmd_name}_(\#{args})
180
+ unless singleton_class.class_variable_defined?(:@@#{cmd_name}_)
181
+ singleton_class.class_variable_set(:@@#{cmd_name}_, true)
182
+ def self.#{cmd_name}_(\#{args})
183
183
  ExtendCommand::#{cmd_class}.execute(irb_context, \#{args})
184
184
  end
185
185
  end
data/lib/irb/init.rb CHANGED
@@ -146,7 +146,7 @@ module IRB # :nodoc:
146
146
  @CONF[:AT_EXIT] = []
147
147
  end
148
148
 
149
- def IRB.set_measure_callback(type = nil, arg = nil)
149
+ def IRB.set_measure_callback(type = nil, arg = nil, &block)
150
150
  added = nil
151
151
  if type
152
152
  type_sym = type.upcase.to_sym
@@ -155,11 +155,31 @@ module IRB # :nodoc:
155
155
  end
156
156
  elsif IRB.conf[:MEASURE_PROC][:CUSTOM]
157
157
  added = [:CUSTOM, IRB.conf[:MEASURE_PROC][:CUSTOM], arg]
158
+ elsif block_given?
159
+ added = [:BLOCK, block, arg]
160
+ found = IRB.conf[:MEASURE_CALLBACKS].find{ |m| m[0] == added[0] && m[2] == added[2] }
161
+ if found
162
+ found[1] = block
163
+ return added
164
+ else
165
+ IRB.conf[:MEASURE_CALLBACKS] << added
166
+ return added
167
+ end
158
168
  else
159
169
  added = [:TIME, IRB.conf[:MEASURE_PROC][:TIME], arg]
160
170
  end
161
- IRB.conf[:MEASURE_CALLBACKS] << added if added
162
- added
171
+ if added
172
+ found = IRB.conf[:MEASURE_CALLBACKS].find{ |m| m[0] == added[0] && m[2] == added[2] }
173
+ if found
174
+ # already added
175
+ nil
176
+ else
177
+ IRB.conf[:MEASURE_CALLBACKS] << added if added
178
+ added
179
+ end
180
+ else
181
+ nil
182
+ end
163
183
  end
164
184
 
165
185
  def IRB.unset_measure_callback(type = nil)
@@ -83,7 +83,15 @@ module IRB
83
83
  #
84
84
  # See IO#eof? for more information.
85
85
  def eof?
86
- @stdin.eof?
86
+ rs, = IO.select([@stdin], [], [], 0.00001)
87
+ if rs and rs[0]
88
+ c = @stdin.getc
89
+ result = c.nil? ? true : false
90
+ @stdin.ungetc(c) unless c.nil?
91
+ result
92
+ else # buffer is empty
93
+ false
94
+ end
87
95
  end
88
96
 
89
97
  # Whether this input method is still readable when there is no more data to
@@ -116,10 +124,22 @@ module IRB
116
124
 
117
125
  # Use a File for IO with irb, see InputMethod
118
126
  class FileInputMethod < InputMethod
127
+ class << self
128
+ def open(file, &block)
129
+ begin
130
+ io = new(file)
131
+ block.call(io)
132
+ ensure
133
+ io&.close
134
+ end
135
+ end
136
+ end
137
+
119
138
  # Creates a new input method object
120
139
  def initialize(file)
121
140
  super
122
141
  @io = IRB::MagicFile.open(file)
142
+ @external_encoding = @io.external_encoding
123
143
  end
124
144
  # The file name of this input method, usually given during initialization.
125
145
  attr_reader :file_name
@@ -129,7 +149,7 @@ module IRB
129
149
  #
130
150
  # See IO#eof? for more information.
131
151
  def eof?
132
- @io.eof?
152
+ @io.closed? || @io.eof?
133
153
  end
134
154
 
135
155
  # Reads the next line from this input method.
@@ -142,13 +162,17 @@ module IRB
142
162
 
143
163
  # The external encoding for standard input.
144
164
  def encoding
145
- @io.external_encoding
165
+ @external_encoding
146
166
  end
147
167
 
148
168
  # For debug message
149
169
  def inspect
150
170
  'FileInputMethod'
151
171
  end
172
+
173
+ def close
174
+ @io.close
175
+ end
152
176
  end
153
177
 
154
178
  begin
data/lib/irb/inspector.rb CHANGED
@@ -100,29 +100,27 @@ module IRB # :nodoc:
100
100
  # Proc to call when the input is evaluated and output in irb.
101
101
  def inspect_value(v)
102
102
  @inspect.call(v)
103
+ rescue
104
+ puts "(Object doesn't support #inspect)"
105
+ ''
103
106
  end
104
107
  end
105
108
 
106
109
  Inspector.def_inspector([false, :to_s, :raw]){|v| v.to_s}
107
- Inspector.def_inspector([true, :p, :inspect]){|v|
108
- begin
109
- result = v.inspect
110
- if IRB.conf[:MAIN_CONTEXT]&.use_colorize? && Color.inspect_colorable?(v)
111
- result = Color.colorize_code(result)
112
- end
113
- result
114
- rescue NoMethodError
115
- puts "(Object doesn't support #inspect)"
116
- ''
117
- end
118
- }
119
- Inspector.def_inspector([:pp, :pretty_inspect], proc{require "pp"}){|v|
120
- result = v.pretty_inspect.chomp
110
+ Inspector.def_inspector([:p, :inspect]){|v|
111
+ result = v.inspect
121
112
  if IRB.conf[:MAIN_CONTEXT]&.use_colorize? && Color.inspect_colorable?(v)
122
113
  result = Color.colorize_code(result)
123
114
  end
124
115
  result
125
116
  }
117
+ Inspector.def_inspector([true, :pp, :pretty_inspect], proc{require "irb/color_printer"}){|v|
118
+ if IRB.conf[:MAIN_CONTEXT]&.use_colorize?
119
+ IRB::ColorPrinter.pp(v, '').chomp
120
+ else
121
+ v.pretty_inspect.chomp
122
+ end
123
+ }
126
124
  Inspector.def_inspector([:yaml, :YAML], proc{require "yaml"}){|v|
127
125
  begin
128
126
  YAML.dump(v)
data/lib/irb/ruby-lex.rb CHANGED
@@ -66,14 +66,19 @@ class RubyLex
66
66
  unprocessed_tokens = []
67
67
  line_num_offset = 0
68
68
  tokens.each do |t|
69
- code << t[2]
70
69
  partial_tokens << t
71
70
  unprocessed_tokens << t
72
71
  if t[2].include?("\n")
73
- ltype, indent, continue, code_block_open = check_state(code, partial_tokens)
74
- result << @prompt.call(ltype, indent, continue || code_block_open, @line_no + line_num_offset)
75
- line_num_offset += 1
72
+ t_str = t[2]
73
+ t_str.each_line("\n") do |s|
74
+ code << s << "\n"
75
+ ltype, indent, continue, code_block_open = check_state(code, partial_tokens)
76
+ result << @prompt.call(ltype, indent, continue || code_block_open, @line_no + line_num_offset)
77
+ line_num_offset += 1
78
+ end
76
79
  unprocessed_tokens = []
80
+ else
81
+ code << t[2]
77
82
  end
78
83
  end
79
84
  unless unprocessed_tokens.empty?
@@ -101,24 +106,72 @@ class RubyLex
101
106
  end
102
107
  end
103
108
 
109
+ ERROR_TOKENS = [
110
+ :on_parse_error,
111
+ :compile_error,
112
+ :on_assign_error,
113
+ :on_alias_error,
114
+ :on_class_name_error,
115
+ :on_param_error
116
+ ]
117
+
104
118
  def ripper_lex_without_warning(code)
105
119
  verbose, $VERBOSE = $VERBOSE, nil
106
120
  tokens = nil
107
121
  self.class.compile_with_errors_suppressed(code) do |inner_code, line_no|
108
- tokens = Ripper.lex(inner_code, '-', line_no)
122
+ lexer = Ripper::Lexer.new(inner_code, '-', line_no)
123
+ if lexer.respond_to?(:scan) # Ruby 2.7+
124
+ tokens = []
125
+ pos_to_index = {}
126
+ lexer.scan.each do |t|
127
+ if pos_to_index.has_key?(t[0])
128
+ index = pos_to_index[t[0]]
129
+ found_tk = tokens[index]
130
+ if ERROR_TOKENS.include?(found_tk[1]) && !ERROR_TOKENS.include?(t[1])
131
+ tokens[index] = t
132
+ end
133
+ else
134
+ pos_to_index[t[0]] = tokens.size
135
+ tokens << t
136
+ end
137
+ end
138
+ else
139
+ tokens = lexer.parse
140
+ end
109
141
  end
110
- $VERBOSE = verbose
111
142
  tokens
143
+ ensure
144
+ $VERBOSE = verbose
145
+ end
146
+
147
+ def find_prev_spaces(line_index)
148
+ return 0 if @tokens.size == 0
149
+ md = @tokens[0][2].match(/(\A +)/)
150
+ prev_spaces = md.nil? ? 0 : md[1].count(' ')
151
+ line_count = 0
152
+ @tokens.each_with_index do |t, i|
153
+ if t[2].include?("\n")
154
+ line_count += t[2].count("\n")
155
+ if line_count >= line_index
156
+ return prev_spaces
157
+ end
158
+ if (@tokens.size - 1) > i
159
+ md = @tokens[i + 1][2].match(/(\A +)/)
160
+ prev_spaces = md.nil? ? 0 : md[1].count(' ')
161
+ end
162
+ end
163
+ end
164
+ prev_spaces
112
165
  end
113
166
 
114
167
  def set_auto_indent(context)
115
168
  if @io.respond_to?(:auto_indent) and context.auto_indent_mode
116
169
  @io.auto_indent do |lines, line_index, byte_pointer, is_newline|
117
170
  if is_newline
118
- md = lines[line_index - 1].match(/(\A +)/)
119
- prev_spaces = md.nil? ? 0 : md[1].count(' ')
120
171
  @tokens = ripper_lex_without_warning(lines[0..line_index].join("\n"))
172
+ prev_spaces = find_prev_spaces(line_index)
121
173
  depth_difference = check_newline_depth_difference
174
+ depth_difference = 0 if depth_difference < 0
122
175
  prev_spaces + depth_difference * 2
123
176
  else
124
177
  code = line_index.zero? ? '' : lines[0..(line_index - 1)].map{ |l| l + "\n" }.join
@@ -170,7 +223,10 @@ class RubyLex
170
223
  throw :TERM_INPUT if @line == ''
171
224
  else
172
225
  @line_no += l.count("\n")
173
- next if l == "\n"
226
+ if l == "\n"
227
+ @exp_line_no += 1
228
+ next
229
+ end
174
230
  @line.concat l
175
231
  if @code_block_open or @ltype or @continue or @indent > 0
176
232
  next
@@ -180,7 +236,7 @@ class RubyLex
180
236
  @line.force_encoding(@io.encoding)
181
237
  yield @line, @exp_line_no
182
238
  end
183
- break if @io.eof?
239
+ raise TerminateLineInput if @io.eof?
184
240
  @line = ''
185
241
  @exp_line_no = @line_no
186
242
 
@@ -355,14 +411,8 @@ class RubyLex
355
411
  next if index > 0 and tokens[index - 1][3].allbits?(Ripper::EXPR_FNAME)
356
412
  case t[2]
357
413
  when 'do'
358
- if index > 0 and tokens[index - 1][3].anybits?(Ripper::EXPR_CMDARG | Ripper::EXPR_ENDFN | Ripper::EXPR_ARG)
359
- # method_with_block do; end
360
- indent += 1
361
- else
362
- # while cond do; end # also "until" or "for"
363
- # This "do" doesn't increment indent because "while" already
364
- # incremented.
365
- end
414
+ syntax_of_do = take_corresponding_syntax_to_kw_do(tokens, index)
415
+ indent += 1 if syntax_of_do == :method_calling
366
416
  when 'def', 'case', 'for', 'begin', 'class', 'module'
367
417
  indent += 1
368
418
  when 'if', 'unless', 'while', 'until'
@@ -377,6 +427,83 @@ class RubyLex
377
427
  indent
378
428
  end
379
429
 
430
+ def is_method_calling?(tokens, index)
431
+ tk = tokens[index]
432
+ if tk[3].anybits?(Ripper::EXPR_CMDARG) and tk[1] == :on_ident
433
+ # The target method call to pass the block with "do".
434
+ return true
435
+ elsif tk[3].anybits?(Ripper::EXPR_ARG) and tk[1] == :on_ident
436
+ non_sp_index = tokens[0..(index - 1)].rindex{ |t| t[1] != :on_sp }
437
+ if non_sp_index
438
+ prev_tk = tokens[non_sp_index]
439
+ if prev_tk[3].anybits?(Ripper::EXPR_DOT) and prev_tk[1] == :on_period
440
+ # The target method call with receiver to pass the block with "do".
441
+ return true
442
+ end
443
+ end
444
+ end
445
+ false
446
+ end
447
+
448
+ def take_corresponding_syntax_to_kw_do(tokens, index)
449
+ syntax_of_do = nil
450
+ # Finding a syntax correnponding to "do".
451
+ index.downto(0) do |i|
452
+ tk = tokens[i]
453
+ # In "continue", the token isn't the corresponding syntax to "do".
454
+ non_sp_index = tokens[0..(i - 1)].rindex{ |t| t[1] != :on_sp }
455
+ first_in_fomula = false
456
+ if non_sp_index.nil?
457
+ first_in_fomula = true
458
+ elsif [:on_ignored_nl, :on_nl, :on_comment].include?(tokens[non_sp_index][1])
459
+ first_in_fomula = true
460
+ end
461
+ if is_method_calling?(tokens, i)
462
+ syntax_of_do = :method_calling
463
+ break if first_in_fomula
464
+ elsif tk[1] == :on_kw && %w{while until for}.include?(tk[2])
465
+ # A loop syntax in front of "do" found.
466
+ #
467
+ # while cond do # also "until" or "for"
468
+ # end
469
+ #
470
+ # This "do" doesn't increment indent because the loop syntax already
471
+ # incremented.
472
+ syntax_of_do = :loop_syntax
473
+ break if first_in_fomula
474
+ end
475
+ end
476
+ syntax_of_do
477
+ end
478
+
479
+ def is_the_in_correspond_to_a_for(tokens, index)
480
+ syntax_of_in = nil
481
+ # Finding a syntax correnponding to "do".
482
+ index.downto(0) do |i|
483
+ tk = tokens[i]
484
+ # In "continue", the token isn't the corresponding syntax to "do".
485
+ non_sp_index = tokens[0..(i - 1)].rindex{ |t| t[1] != :on_sp }
486
+ first_in_fomula = false
487
+ if non_sp_index.nil?
488
+ first_in_fomula = true
489
+ elsif [:on_ignored_nl, :on_nl, :on_comment].include?(tokens[non_sp_index][1])
490
+ first_in_fomula = true
491
+ end
492
+ if tk[1] == :on_kw && tk[2] == 'for'
493
+ # A loop syntax in front of "do" found.
494
+ #
495
+ # while cond do # also "until" or "for"
496
+ # end
497
+ #
498
+ # This "do" doesn't increment indent because the loop syntax already
499
+ # incremented.
500
+ syntax_of_in = :for
501
+ end
502
+ break if first_in_fomula
503
+ end
504
+ syntax_of_in
505
+ end
506
+
380
507
  def check_newline_depth_difference
381
508
  depth_difference = 0
382
509
  open_brace_on_line = 0
@@ -405,7 +532,7 @@ class RubyLex
405
532
 
406
533
  case t[1]
407
534
  when :on_ignored_nl, :on_nl, :on_comment
408
- if index != (@tokens.size - 1)
535
+ if index != (@tokens.size - 1) and in_oneliner_def != :BODY
409
536
  depth_difference = 0
410
537
  open_brace_on_line = 0
411
538
  end
@@ -423,14 +550,8 @@ class RubyLex
423
550
  next if index > 0 and @tokens[index - 1][3].allbits?(Ripper::EXPR_FNAME)
424
551
  case t[2]
425
552
  when 'do'
426
- if index > 0 and @tokens[index - 1][3].anybits?(Ripper::EXPR_CMDARG | Ripper::EXPR_ENDFN | Ripper::EXPR_ARG)
427
- # method_with_block do; end
428
- depth_difference += 1
429
- else
430
- # while cond do; end # also "until" or "for"
431
- # This "do" doesn't increment indent because "while" already
432
- # incremented.
433
- end
553
+ syntax_of_do = take_corresponding_syntax_to_kw_do(@tokens, index)
554
+ depth_difference += 1 if syntax_of_do == :method_calling
434
555
  when 'def', 'case', 'for', 'begin', 'class', 'module'
435
556
  depth_difference += 1
436
557
  when 'if', 'unless', 'while', 'until', 'rescue'
@@ -438,8 +559,14 @@ class RubyLex
438
559
  unless t[3].allbits?(Ripper::EXPR_LABEL)
439
560
  depth_difference += 1
440
561
  end
441
- when 'else', 'elsif', 'ensure', 'when', 'in'
562
+ when 'else', 'elsif', 'ensure', 'when'
442
563
  depth_difference += 1
564
+ when 'in'
565
+ unless is_the_in_correspond_to_a_for(@tokens, index)
566
+ depth_difference += 1
567
+ end
568
+ when 'end'
569
+ depth_difference -= 1
443
570
  end
444
571
  end
445
572
  end
@@ -483,11 +610,13 @@ class RubyLex
483
610
 
484
611
  case t[1]
485
612
  when :on_ignored_nl, :on_nl, :on_comment
486
- corresponding_token_depth = nil
487
- spaces_at_line_head = 0
488
- is_first_spaces_of_line = true
489
- is_first_printable_of_line = true
490
- open_brace_on_line = 0
613
+ if in_oneliner_def != :BODY
614
+ corresponding_token_depth = nil
615
+ spaces_at_line_head = 0
616
+ is_first_spaces_of_line = true
617
+ is_first_printable_of_line = true
618
+ open_brace_on_line = 0
619
+ end
491
620
  next
492
621
  when :on_sp
493
622
  spaces_at_line_head = t[2].count(' ') if is_first_spaces_of_line
@@ -509,7 +638,12 @@ class RubyLex
509
638
  when :on_kw
510
639
  next if index > 0 and @tokens[index - 1][3].allbits?(Ripper::EXPR_FNAME)
511
640
  case t[2]
512
- when 'def', 'do', 'case', 'for', 'begin', 'class', 'module'
641
+ when 'do'
642
+ syntax_of_do = take_corresponding_syntax_to_kw_do(@tokens, index)
643
+ if syntax_of_do == :method_calling
644
+ spaces_of_nest.push(spaces_at_line_head)
645
+ end
646
+ when 'def', 'case', 'for', 'begin', 'class', 'module'
513
647
  spaces_of_nest.push(spaces_at_line_head)
514
648
  when 'rescue'
515
649
  unless t[3].allbits?(Ripper::EXPR_LABEL)
@@ -552,7 +686,7 @@ class RubyLex
552
686
  end_type << :on_regexp_end
553
687
  when :on_symbeg
554
688
  acceptable_single_tokens = %i{on_ident on_const on_op on_cvar on_ivar on_gvar on_kw}
555
- if (i + 1) < tokens.size and acceptable_single_tokens.all?{ |t| tokens[i + 1][1] != t }
689
+ if (i + 1) < tokens.size and acceptable_single_tokens.all?{ |st| tokens[i + 1][1] != st }
556
690
  start_token << t
557
691
  end_type << :on_tstring_end
558
692
  end
data/lib/irb/version.rb CHANGED
@@ -11,7 +11,7 @@
11
11
  #
12
12
 
13
13
  module IRB # :nodoc:
14
- VERSION = "1.2.9"
14
+ VERSION = "1.3.4"
15
15
  @RELEASE_VERSION = VERSION
16
- @LAST_UPDATE_DATE = "2020-12-22"
16
+ @LAST_UPDATE_DATE = "2021-02-25"
17
17
  end
data/lib/irb/workspace.rb CHANGED
@@ -128,6 +128,7 @@ EOF
128
128
  def filter_backtrace(bt)
129
129
  return nil if bt =~ /\/irb\/.*\.rb/
130
130
  return nil if bt =~ /\/irb\.rb/
131
+ return nil if bt =~ /tool\/lib\/.*\.rb|runner\.rb/ # for tests in Ruby repository
131
132
  case IRB.conf[:CONTEXT_MODE]
132
133
  when 1
133
134
  return nil if bt =~ %r!/tmp/irb-binding!
@@ -174,7 +175,7 @@ EOF
174
175
  body = (start_pos..end_pos).map do |current_pos|
175
176
  sprintf(fmt, pos == current_pos ? '=>' : '', current_pos + 1, lines[current_pos])
176
177
  end.join("")
177
- "\nFrom: #{file} @ line #{pos + 1} :\n\n#{body}#{Color.clear}\n"
178
+ "\nFrom: #{file} @ line #{pos + 1} :\n\n#{body}#{Color.clear if use_colorize}\n"
178
179
  end
179
180
 
180
181
  def IRB.delete_caller
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: irb
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.9
4
+ version: 1.3.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Keiju ISHITSUKA
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-12-21 00:00:00.000000000 Z
11
+ date: 2021-02-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: reline
@@ -75,11 +75,14 @@ files:
75
75
  - lib/irb/cmd/chws.rb
76
76
  - lib/irb/cmd/fork.rb
77
77
  - lib/irb/cmd/help.rb
78
+ - lib/irb/cmd/info.rb
78
79
  - lib/irb/cmd/load.rb
80
+ - lib/irb/cmd/measure.rb
79
81
  - lib/irb/cmd/nop.rb
80
82
  - lib/irb/cmd/pushws.rb
81
83
  - lib/irb/cmd/subirb.rb
82
84
  - lib/irb/color.rb
85
+ - lib/irb/color_printer.rb
83
86
  - lib/irb/completion.rb
84
87
  - lib/irb/context.rb
85
88
  - lib/irb/easter-egg.rb
@@ -134,7 +137,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
134
137
  - !ruby/object:Gem::Version
135
138
  version: '0'
136
139
  requirements: []
137
- rubygems_version: 3.1.4
140
+ rubygems_version: 3.0.6
138
141
  signing_key:
139
142
  specification_version: 4
140
143
  summary: Interactive Ruby command-line tool for REPL (Read Eval Print Loop).