1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00

Merge IRB 1.2.6

This commit is contained in:
aycabta 2020-09-16 11:19:17 +09:00 committed by nagachika
parent 3bb503e0e8
commit 2159798f4c
29 changed files with 912 additions and 105 deletions

View file

@ -10,18 +10,19 @@
# #
# #
require "ripper" require "ripper"
require "reline"
require "irb/init" require_relative "irb/init"
require "irb/context" require_relative "irb/context"
require "irb/extend-command" require_relative "irb/extend-command"
require "irb/ruby-lex" require_relative "irb/ruby-lex"
require "irb/input-method" require_relative "irb/input-method"
require "irb/locale" require_relative "irb/locale"
require "irb/color" require_relative "irb/color"
require "irb/version" require_relative "irb/version"
require "irb/easter-egg" require_relative "irb/easter-egg"
# IRB stands for "interactive Ruby" and is a tool to interactively execute Ruby # IRB stands for "interactive Ruby" and is a tool to interactively execute Ruby
# expressions read from the standard input. # expressions read from the standard input.
@ -271,7 +272,7 @@ require "irb/easter-egg"
# On the other hand, each conf in IRB@Command+line+options is used to # On the other hand, each conf in IRB@Command+line+options is used to
# individually configure IRB.irb. # individually configure IRB.irb.
# #
# If a proc is set for IRB.conf[:IRB_RC], its will be invoked after execution # If a proc is set for <code>IRB.conf[:IRB_RC]</code>, its will be invoked after execution
# of that proc with the context of the current session as its argument. Each # of that proc with the context of the current session as its argument. Each
# session can be configured using this mechanism. # session can be configured using this mechanism.
# #
@ -399,7 +400,7 @@ module IRB
irb.run(@CONF) irb.run(@CONF)
end end
# Calls each event hook of IRB.conf[:AT_EXIT] when the current session quits. # Calls each event hook of <code>IRB.conf[:TA_EXIT]</code> when the current session quits.
def IRB.irb_at_exit def IRB.irb_at_exit
@CONF[:AT_EXIT].each{|hook| hook.call} @CONF[:AT_EXIT].each{|hook| hook.call}
end end
@ -538,7 +539,15 @@ module IRB
begin begin
line.untaint if RUBY_VERSION < '2.7' line.untaint if RUBY_VERSION < '2.7'
@context.evaluate(line, line_no, exception: exc) @context.evaluate(line, line_no, exception: exc)
output_value if @context.echo? && (@context.echo_on_assignment? || !assignment_expression?(line)) if @context.echo?
if assignment_expression?(line)
if @context.echo_on_assignment?
output_value(@context.omit_on_assignment?)
end
else
output_value
end
end
rescue Interrupt => exc rescue Interrupt => exc
rescue SystemExit, SignalException rescue SystemExit, SignalException
raise raise
@ -737,9 +746,32 @@ module IRB
p p
end end
def output_value # :nodoc: def output_value(omit = false) # :nodoc:
str = @context.inspect_last_value str = @context.inspect_last_value
multiline_p = str.include?("\n") multiline_p = str.include?("\n")
if omit
winwidth = @context.io.winsize.last
if multiline_p
first_line = str.split("\n").first
result = @context.newline_before_multiline_output? ? (@context.return_format % first_line) : first_line
output_width = Reline::Unicode.calculate_width(result, true)
diff_size = output_width - Reline::Unicode.calculate_width(first_line, true)
if diff_size.positive? and output_width > winwidth
lines, _ = Reline::Unicode.split_by_width(first_line, winwidth - diff_size - 3)
str = "%s...\e[0m" % lines.first
multiline_p = false
else
str.gsub!(/(\A.*?\n).*/m, "\\1...")
end
else
output_width = Reline::Unicode.calculate_width(@context.return_format % str, true)
diff_size = output_width - Reline::Unicode.calculate_width(str, true)
if diff_size.positive? and output_width > winwidth
lines, _ = Reline::Unicode.split_by_width(str, winwidth - diff_size - 3)
str = "%s...\e[0m" % lines.first
end
end
end
if multiline_p && @context.newline_before_multiline_output? if multiline_p && @context.newline_before_multiline_output?
printf @context.return_format, "\n#{str}" printf @context.return_format, "\n#{str}"
else else

View file

@ -35,5 +35,3 @@ module IRB
end end
end end
# :startdoc: # :startdoc:

24
lib/irb/cmd/info.rb Normal file
View file

@ -0,0 +1,24 @@
# frozen_string_literal: false
require_relative "nop"
# :stopdoc:
module IRB
module ExtendCommand
class Info < Nop
def execute
Class.new {
def inspect
str = "Ruby version: #{RUBY_VERSION}\n"
str += "IRB version: #{IRB.version}\n"
str += "InputMethod: #{IRB.CurrentContext.io.inspect}\n"
str += ".irbrc path: #{IRB.rc_file}\n" if File.exist?(IRB.rc_file)
str
end
alias_method :to_s, :inspect
}.new
end
end
end
end
# :startdoc:

View file

@ -38,4 +38,3 @@ module IRB
end end
end end
# :startdoc: # :startdoc:

View file

@ -1,6 +1,7 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'reline' require 'reline'
require 'ripper' require 'ripper'
require 'irb/ruby-lex'
module IRB # :nodoc: module IRB # :nodoc:
module Color module Color
@ -145,37 +146,38 @@ module IRB # :nodoc:
seen.delete(obj) seen.delete(obj)
end end
# Ripper::Lexer::Elem#state is supported on Ruby 2.5+
def supported? def supported?
return @supported if defined?(@supported) return @supported if defined?(@supported)
@supported = Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.5.0') @supported = Ripper::Lexer::Elem.method_defined?(:state)
end end
def scan(code, allow_last_error:) def scan(code, allow_last_error:)
pos = [1, 0] pos = [1, 0]
verbose, $VERBOSE = $VERBOSE, nil verbose, $VERBOSE = $VERBOSE, nil
lexer = Ripper::Lexer.new(code) RubyLex.compile_with_errors_suppressed(code) do |inner_code, line_no|
if lexer.respond_to?(:scan) # Ruby 2.7+ lexer = Ripper::Lexer.new(inner_code, '(ripper)', line_no)
lexer.scan.each do |elem| if lexer.respond_to?(:scan) # Ruby 2.7+
str = elem.tok lexer.scan.each do |elem|
next if allow_last_error and /meets end of file|unexpected end-of-input/ =~ elem.message str = elem.tok
next if ([elem.pos[0], elem.pos[1] + str.bytesize] <=> pos) <= 0 next if allow_last_error and /meets end of file|unexpected end-of-input/ =~ elem.message
next if ([elem.pos[0], elem.pos[1] + str.bytesize] <=> pos) <= 0
str.each_line do |line| str.each_line do |line|
if line.end_with?("\n") if line.end_with?("\n")
pos[0] += 1 pos[0] += 1
pos[1] = 0 pos[1] = 0
else else
pos[1] += line.bytesize pos[1] += line.bytesize
end
end end
end
yield(elem.event, str, elem.state) yield(elem.event, str, elem.state)
end end
else else
lexer.parse.each do |elem| lexer.parse.each do |elem|
yield(elem.event, elem.tok, elem.state) yield(elem.event, elem.tok, elem.state)
end
end end
end end
$VERBOSE = verbose $VERBOSE = verbose

View file

@ -7,7 +7,6 @@
# From Original Idea of shugo@ruby-lang.org # From Original Idea of shugo@ruby-lang.org
# #
require "readline"
autoload :RDoc, "rdoc" autoload :RDoc, "rdoc"
module IRB module IRB
@ -97,17 +96,13 @@ module IRB
when /^(:[^:.]*)$/ when /^(:[^:.]*)$/
# Symbol # Symbol
return nil if doc_namespace return nil if doc_namespace
if Symbol.respond_to?(:all_symbols) sym = $1
sym = $1 candidates = Symbol.all_symbols.collect do |s|
candidates = Symbol.all_symbols.collect do |s| ":" + s.id2name.encode(Encoding.default_external)
":" + s.id2name.encode(Encoding.default_external) rescue Encoding::UndefinedConversionError
rescue Encoding::UndefinedConversionError # ignore
# ignore
end
candidates.grep(/^#{Regexp.quote(sym)}/)
else
[]
end end
candidates.grep(/^#{Regexp.quote(sym)}/)
when /^::([A-Z][^:\.\(]*)$/ when /^::([A-Z][^:\.\(]*)$/
# Absolute Constant or class methods # Absolute Constant or class methods

View file

@ -131,7 +131,12 @@ module IRB
@echo_on_assignment = IRB.conf[:ECHO_ON_ASSIGNMENT] @echo_on_assignment = IRB.conf[:ECHO_ON_ASSIGNMENT]
if @echo_on_assignment.nil? if @echo_on_assignment.nil?
@echo_on_assignment = false @echo_on_assignment = true
end
@omit_on_assignment = IRB.conf[:OMIT_ON_ASSIGNMENT]
if @omit_on_assignment.nil?
@omit_on_assignment = true
end end
@newline_before_multiline_output = IRB.conf[:NEWLINE_BEFORE_MULTILINE_OUTPUT] @newline_before_multiline_output = IRB.conf[:NEWLINE_BEFORE_MULTILINE_OUTPUT]
@ -240,7 +245,7 @@ module IRB
attr_accessor :ignore_eof attr_accessor :ignore_eof
# Whether to echo the return value to output or not. # Whether to echo the return value to output or not.
# #
# Uses IRB.conf[:ECHO] if available, or defaults to +true+. # Uses <code>IRB.conf[:ECHO]</code> if available, or defaults to +true+.
# #
# puts "hello" # puts "hello"
# # hello # # hello
@ -251,16 +256,30 @@ module IRB
attr_accessor :echo attr_accessor :echo
# Whether to echo for assignment expressions # Whether to echo for assignment expressions
# #
# Uses IRB.conf[:ECHO_ON_ASSIGNMENT] if available, or defaults to +false+. # Uses <code>IRB.conf[:ECHO_ON_ASSIGNMENT]</code> if available, or defaults to +true+.
# #
# a = "omg" # a = "omg"
# IRB.CurrentContext.echo_on_assignment = true
# a = "omg"
# #=> omg # #=> omg
# IRB.CurrentContext.echo_on_assignment = false
# a = "omg"
attr_accessor :echo_on_assignment attr_accessor :echo_on_assignment
# Whether to omit echo for assignment expressions
#
# Uses <code>IRB.conf[:OMIT_ON_ASSIGNMENT]</code> if available, or defaults to +true+.
#
# a = [1] * 10
# #=> [1, 1, 1, 1, 1, 1, 1, 1, ...
# [1] * 10
# #=> [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
# IRB.CurrentContext.omit_on_assignment = false
# a = [1] * 10
# #=> [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
# [1] * 10
# #=> [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
attr_accessor :omit_on_assignment
# Whether a newline is put before multiline output. # Whether a newline is put before multiline output.
# #
# Uses IRB.conf[:NEWLINE_BEFORE_MULTILINE_OUTPUT] if available, # Uses <code>IRB.conf[:NEWLINE_BEFORE_MULTILINE_OUTPUT]</code> if available,
# or defaults to +true+. # or defaults to +true+.
# #
# "abc\ndef" # "abc\ndef"
@ -306,6 +325,7 @@ module IRB
alias ignore_eof? ignore_eof alias ignore_eof? ignore_eof
alias echo? echo alias echo? echo
alias echo_on_assignment? echo_on_assignment alias echo_on_assignment? echo_on_assignment
alias omit_on_assignment? omit_on_assignment
alias newline_before_multiline_output? newline_before_multiline_output alias newline_before_multiline_output? newline_before_multiline_output
# Returns whether messages are displayed or not. # Returns whether messages are displayed or not.

View file

@ -43,4 +43,3 @@ module IRB # :nodoc:
end end
end end
end end

View file

@ -153,5 +153,3 @@ module IRB # :nodoc:
end end
end end
end end

View file

@ -126,4 +126,3 @@ module IRB # :nodoc:
end end
end end
end end

View file

@ -9,8 +9,6 @@
# #
# #
require "readline"
module IRB module IRB
module HistorySavingAbility # :nodoc: module HistorySavingAbility # :nodoc:
end end
@ -27,7 +25,7 @@ module IRB
IRB.conf[:SAVE_HISTORY] IRB.conf[:SAVE_HISTORY]
end end
remove_method :save_history= if method_defined?(:save_history=) remove_method(:save_history=) if method_defined?(:save_history=)
# Sets <code>IRB.conf[:SAVE_HISTORY]</code> to the given +val+ and calls # Sets <code>IRB.conf[:SAVE_HISTORY]</code> to the given +val+ and calls
# #init_save_history with this context. # #init_save_history with this context.
# #
@ -89,7 +87,7 @@ module IRB
def save_history def save_history
return unless self.class.const_defined?(:HISTORY) return unless self.class.const_defined?(:HISTORY)
history = self.class::HISTORY history = self.class::HISTORY
if num = IRB.conf[:SAVE_HISTORY] and (num = num.to_i) > 0 if num = IRB.conf[:SAVE_HISTORY] and (num = num.to_i) != 0
if history_file = IRB.conf[:HISTORY_FILE] if history_file = IRB.conf[:HISTORY_FILE]
history_file = File.expand_path(history_file) history_file = File.expand_path(history_file)
end end
@ -109,7 +107,12 @@ module IRB
open(history_file, "w:#{IRB.conf[:LC_MESSAGES].encoding}", 0600) do |f| open(history_file, "w:#{IRB.conf[:LC_MESSAGES].encoding}", 0600) do |f|
hist = history.map{ |l| l.split("\n").join("\\\n") } hist = history.map{ |l| l.split("\n").join("\\\n") }
f.puts(hist[-num..-1] || hist) begin
hist = hist.last(num) if hist.size > num and num > 0
rescue RangeError # bignum too big to convert into `long'
# Do nothing because the bignum should be treated as inifinity
end
f.puts(hist)
end end
end end
end end

View file

@ -82,4 +82,3 @@ module IRB
IRB.initialize_tracer IRB.initialize_tracer
end end

View file

@ -47,7 +47,7 @@ module IRB
alias use_loader? use_loader alias use_loader? use_loader
remove_method :use_loader= if method_defined?(:use_loader=) remove_method :use_loader= if method_defined?(:use_loader=)
# Sets IRB.conf[:USE_LOADER] # Sets <code>IRB.conf[:USE_LOADER]</code>
# #
# See #use_loader for more information. # See #use_loader for more information.
def use_loader=(opt) def use_loader=(opt)
@ -73,5 +73,3 @@ module IRB
end end
end end
end end

View file

@ -64,4 +64,3 @@ module IRB # :nodoc:
end end
end end
end end

View file

@ -121,6 +121,10 @@ module IRB # :nodoc:
[:help, NO_OVERRIDE], [:help, NO_OVERRIDE],
], ],
[
:irb_info, :Info, "irb/cmd/info"
],
] ]
# Installs the default irb commands: # Installs the default irb commands:
@ -169,11 +173,14 @@ module IRB # :nodoc:
args << "&block" args << "&block"
args = args.join(", ") args = args.join(", ")
line = __LINE__; eval %[ line = __LINE__; eval %[
def #{cmd_name}(\#{args}) unless self.class.class_variable_defined?(:@@#{cmd_name}_)
ExtendCommand::#{cmd_class}.execute(irb_context, \#{args}) self.class.class_variable_set(:@@#{cmd_name}_, true)
def #{cmd_name}_(\#{args})
ExtendCommand::#{cmd_class}.execute(irb_context, \#{args})
end
end end
], nil, __FILE__, line ], nil, __FILE__, line
send :#{cmd_name}, *opts, &b send :#{cmd_name}_, *opts, &b
end end
], nil, __FILE__, line ], nil, __FILE__, line
else else

View file

@ -34,4 +34,3 @@ module IRB
} }
end end
end end

View file

@ -52,6 +52,7 @@ module IRB # :nodoc:
@CONF[:IGNORE_EOF] = false @CONF[:IGNORE_EOF] = false
@CONF[:ECHO] = nil @CONF[:ECHO] = nil
@CONF[:ECHO_ON_ASSIGNMENT] = nil @CONF[:ECHO_ON_ASSIGNMENT] = nil
@CONF[:OMIT_ON_ASSIGNMENT] = nil
@CONF[:VERBOSE] = nil @CONF[:VERBOSE] = nil
@CONF[:EVAL_HISTORY] = nil @CONF[:EVAL_HISTORY] = nil
@ -177,6 +178,10 @@ module IRB # :nodoc:
@CONF[:ECHO_ON_ASSIGNMENT] = true @CONF[:ECHO_ON_ASSIGNMENT] = true
when "--noecho-on-assignment" when "--noecho-on-assignment"
@CONF[:ECHO_ON_ASSIGNMENT] = false @CONF[:ECHO_ON_ASSIGNMENT] = false
when "--omit-on-assignment"
@CONF[:OMIT_ON_ASSIGNMENT] = true
when "--noomit-on-assignment"
@CONF[:OMIT_ON_ASSIGNMENT] = false
when "--verbose" when "--verbose"
@CONF[:VERBOSE] = true @CONF[:VERBOSE] = true
when "--noverbose" when "--noverbose"
@ -271,10 +276,19 @@ module IRB # :nodoc:
if irbrc = ENV["IRBRC"] if irbrc = ENV["IRBRC"]
yield proc{|rc| rc == "rc" ? irbrc : irbrc+rc} yield proc{|rc| rc == "rc" ? irbrc : irbrc+rc}
end end
if xdg_config_home = ENV["XDG_CONFIG_HOME"]
irb_home = File.join(xdg_config_home, "irb")
unless File.exist? irb_home
require 'fileutils'
FileUtils.mkdir_p irb_home
end
yield proc{|rc| irb_home + "/irb#{rc}"}
end
if home = ENV["HOME"] if home = ENV["HOME"]
yield proc{|rc| home+"/.irb#{rc}"} yield proc{|rc| home+"/.irb#{rc}"}
end end
current_dir = Dir.pwd current_dir = Dir.pwd
yield proc{|rc| current_dir+"/.config/irb/irb#{rc}"}
yield proc{|rc| current_dir+"/.irb#{rc}"} yield proc{|rc| current_dir+"/.irb#{rc}"}
yield proc{|rc| current_dir+"/irb#{rc.sub(/\A_?/, '.')}"} yield proc{|rc| current_dir+"/irb#{rc.sub(/\A_?/, '.')}"}
yield proc{|rc| current_dir+"/_irb#{rc}"} yield proc{|rc| current_dir+"/_irb#{rc}"}

View file

@ -12,6 +12,7 @@
require_relative 'src_encoding' require_relative 'src_encoding'
require_relative 'magic-file' require_relative 'magic-file'
require_relative 'completion' require_relative 'completion'
require 'io/console'
require 'reline' require 'reline'
module IRB module IRB
@ -36,6 +37,14 @@ module IRB
end end
public :gets public :gets
def winsize
if instance_variable_defined?(:@stdout)
@stdout.winsize
else
[24, 80]
end
end
# Whether this input method is still readable when there is no more data to # Whether this input method is still readable when there is no more data to
# read. # read.
# #
@ -43,6 +52,11 @@ module IRB
def readable_after_eof? def readable_after_eof?
false false
end end
# For debug message
def inspect
'Abstract InputMethod'
end
end end
class StdioInputMethod < InputMethod class StdioInputMethod < InputMethod
@ -93,6 +107,11 @@ module IRB
def encoding def encoding
@stdin.external_encoding @stdin.external_encoding
end end
# For debug message
def inspect
'StdioInputMethod'
end
end end
# Use a File for IO with irb, see InputMethod # Use a File for IO with irb, see InputMethod
@ -125,14 +144,25 @@ module IRB
def encoding def encoding
@io.external_encoding @io.external_encoding
end end
# For debug message
def inspect
'FileInputMethod'
end
end end
begin begin
require "readline"
class ReadlineInputMethod < InputMethod class ReadlineInputMethod < InputMethod
include Readline def self.initialize_readline
require "readline"
rescue LoadError
else
include ::Readline
end
# Creates a new input method object using Readline # Creates a new input method object using Readline
def initialize def initialize
self.class.initialize_readline
if Readline.respond_to?(:encoding_system_needs) if Readline.respond_to?(:encoding_system_needs)
IRB.__send__(:set_encoding, Readline.encoding_system_needs.name, override: false) IRB.__send__(:set_encoding, Readline.encoding_system_needs.name, override: false)
end end
@ -197,13 +227,15 @@ module IRB
@stdin.external_encoding @stdin.external_encoding
end end
if Readline.respond_to?("basic_word_break_characters=") # For debug message
Readline.basic_word_break_characters = IRB::InputCompletor::BASIC_WORD_BREAK_CHARACTERS def inspect
readline_impl = (defined?(Reline) && Readline == Reline) ? 'Reline' : 'ext/readline'
str = "ReadlineInputMethod with #{readline_impl} #{Readline::VERSION}"
inputrc_path = File.expand_path(ENV['INPUTRC'] || '~/.inputrc')
str += " and #{inputrc_path}" if File.exist?(inputrc_path)
str
end end
Readline.completion_append_character = nil
Readline.completion_proc = IRB::InputCompletor::CompletionProc
end end
rescue LoadError
end end
class ReidlineInputMethod < InputMethod class ReidlineInputMethod < InputMethod
@ -227,7 +259,7 @@ module IRB
Reline.completion_proc = IRB::InputCompletor::CompletionProc Reline.completion_proc = IRB::InputCompletor::CompletionProc
Reline.output_modifier_proc = Reline.output_modifier_proc =
if IRB.conf[:USE_COLORIZE] if IRB.conf[:USE_COLORIZE]
proc do |output, complete:| proc do |output, complete: |
next unless IRB::Color.colorable? next unless IRB::Color.colorable?
IRB::Color.colorize_code(output, complete: complete) IRB::Color.colorize_code(output, complete: complete)
end end
@ -297,5 +329,18 @@ module IRB
def encoding def encoding
@stdin.external_encoding @stdin.external_encoding
end end
# For debug message
def inspect
config = Reline::Config.new
str = "ReidlineInputMethod with Reline #{Reline::VERSION}"
if config.respond_to?(:inputrc_path)
inputrc_path = File.expand_path(config.inputrc_path)
else
inputrc_path = File.expand_path(ENV['INPUTRC'] || '~/.inputrc')
end
str += " and #{inputrc_path}" if File.exist?(inputrc_path)
str
end
end end
end end

View file

@ -113,6 +113,7 @@ module IRB # :nodoc:
result result
rescue NoMethodError rescue NoMethodError
puts "(Object doesn't support #inspect)" puts "(Object doesn't support #inspect)"
''
end end
} }
Inspector.def_inspector([:pp, :pretty_inspect], proc{require "pp"}){|v| Inspector.def_inspector([:pp, :pretty_inspect], proc{require "pp"}){|v|
@ -135,8 +136,3 @@ module IRB # :nodoc:
Marshal.dump(v) Marshal.dump(v)
} }
end end

View file

@ -14,7 +14,7 @@ Gem::Specification.new do |spec|
spec.summary = %q{Interactive Ruby command-line tool for REPL (Read Eval Print Loop).} spec.summary = %q{Interactive Ruby command-line tool for REPL (Read Eval Print Loop).}
spec.description = %q{Interactive Ruby command-line tool for REPL (Read Eval Print Loop).} spec.description = %q{Interactive Ruby command-line tool for REPL (Read Eval Print Loop).}
spec.homepage = "https://github.com/ruby/irb" spec.homepage = "https://github.com/ruby/irb"
spec.license = "BSD-2-Clause" spec.licenses = ["Ruby", "BSD-2-Clause"]
spec.files = [ spec.files = [
".document", ".document",
@ -78,7 +78,7 @@ Gem::Specification.new do |spec|
spec.required_ruby_version = Gem::Requirement.new(">= 2.5") spec.required_ruby_version = Gem::Requirement.new(">= 2.5")
spec.add_dependency "reline", ">= 0.0.1" spec.add_dependency "reline", ">= 0.1.5"
spec.add_development_dependency "bundler" spec.add_development_dependency "bundler"
spec.add_development_dependency "rake" spec.add_development_dependency "rake"
end end

View file

@ -11,6 +11,7 @@
# #
require "ripper" require "ripper"
require "jruby" if RUBY_ENGINE == "jruby"
# :stopdoc: # :stopdoc:
class RubyLex class RubyLex
@ -29,6 +30,18 @@ class RubyLex
@prompt = nil @prompt = nil
end end
def self.compile_with_errors_suppressed(code)
line_no = 1
begin
result = yield code, line_no
rescue ArgumentError
code = ";\n#{code}"
line_no = 0
result = yield code, line_no
end
result
end
# io functions # io functions
def set_input(io, p = nil, &block) def set_input(io, p = nil, &block)
@io = io @io = io
@ -75,7 +88,10 @@ class RubyLex
def ripper_lex_without_warning(code) def ripper_lex_without_warning(code)
verbose, $VERBOSE = $VERBOSE, nil verbose, $VERBOSE = $VERBOSE, nil
tokens = Ripper.lex(code) tokens = nil
self.class.compile_with_errors_suppressed(code) do |inner_code, line_no|
tokens = Ripper.lex(inner_code, '-', line_no)
end
$VERBOSE = verbose $VERBOSE = verbose
tokens tokens
end end
@ -209,7 +225,9 @@ class RubyLex
when 'jruby' when 'jruby'
JRuby.compile_ir(code) JRuby.compile_ir(code)
else else
RubyVM::InstructionSequence.compile(code) self.class.compile_with_errors_suppressed(code) do |inner_code, line_no|
RubyVM::InstructionSequence.compile(inner_code, nil, nil, line_no)
end
end end
rescue EncodingError rescue EncodingError
# This is for a hash with invalid encoding symbol, {"\xAE": 1} # This is for a hash with invalid encoding symbol, {"\xAE": 1}
@ -285,9 +303,33 @@ class RubyLex
def process_nesting_level def process_nesting_level
indent = 0 indent = 0
in_oneliner_def = nil
@tokens.each_with_index { |t, index| @tokens.each_with_index { |t, index|
# detecting one-liner method definition
if in_oneliner_def.nil?
if t[3].allbits?(Ripper::EXPR_ENDFN)
in_oneliner_def = :ENDFN
end
else
if t[3].allbits?(Ripper::EXPR_ENDFN)
# continuing
elsif t[3].allbits?(Ripper::EXPR_BEG)
if t[2] == '='
in_oneliner_def = :BODY
end
elsif t[3].allbits?(Ripper::EXPR_END)
if in_oneliner_def == :BODY
# one-liner method definition
indent -= 1
end
in_oneliner_def = nil
else
in_oneliner_def = nil
end
end
case t[1] case t[1]
when :on_lbracket, :on_lbrace, :on_lparen when :on_lbracket, :on_lbrace, :on_lparen, :on_tlambeg
indent += 1 indent += 1
when :on_rbracket, :on_rbrace, :on_rparen when :on_rbracket, :on_rbrace, :on_rparen
indent -= 1 indent -= 1
@ -306,7 +348,7 @@ class RubyLex
when 'def', 'case', 'for', 'begin', 'class', 'module' when 'def', 'case', 'for', 'begin', 'class', 'module'
indent += 1 indent += 1
when 'if', 'unless', 'while', 'until' when 'if', 'unless', 'while', 'until'
# postfix if/unless/while/until/rescue must be Ripper::EXPR_LABEL # postfix if/unless/while/until must be Ripper::EXPR_LABEL
indent += 1 unless t[3].allbits?(Ripper::EXPR_LABEL) indent += 1 unless t[3].allbits?(Ripper::EXPR_LABEL)
when 'end' when 'end'
indent -= 1 indent -= 1
@ -320,7 +362,31 @@ class RubyLex
def check_newline_depth_difference def check_newline_depth_difference
depth_difference = 0 depth_difference = 0
open_brace_on_line = 0 open_brace_on_line = 0
in_oneliner_def = nil
@tokens.each_with_index do |t, index| @tokens.each_with_index do |t, index|
# detecting one-liner method definition
if in_oneliner_def.nil?
if t[3].allbits?(Ripper::EXPR_ENDFN)
in_oneliner_def = :ENDFN
end
else
if t[3].allbits?(Ripper::EXPR_ENDFN)
# continuing
elsif t[3].allbits?(Ripper::EXPR_BEG)
if t[2] == '='
in_oneliner_def = :BODY
end
elsif t[3].allbits?(Ripper::EXPR_END)
if in_oneliner_def == :BODY
# one[-liner method definition
depth_difference -= 1
end
in_oneliner_def = nil
else
in_oneliner_def = nil
end
end
case t[1] case t[1]
when :on_ignored_nl, :on_nl, :on_comment when :on_ignored_nl, :on_nl, :on_comment
if index != (@tokens.size - 1) if index != (@tokens.size - 1)
@ -332,7 +398,7 @@ class RubyLex
next next
end end
case t[1] case t[1]
when :on_lbracket, :on_lbrace, :on_lparen when :on_lbracket, :on_lbrace, :on_lparen, :on_tlambeg
depth_difference += 1 depth_difference += 1
open_brace_on_line += 1 open_brace_on_line += 1
when :on_rbracket, :on_rbrace, :on_rparen when :on_rbracket, :on_rbrace, :on_rparen
@ -351,12 +417,12 @@ class RubyLex
end end
when 'def', 'case', 'for', 'begin', 'class', 'module' when 'def', 'case', 'for', 'begin', 'class', 'module'
depth_difference += 1 depth_difference += 1
when 'if', 'unless', 'while', 'until' when 'if', 'unless', 'while', 'until', 'rescue'
# postfix if/unless/while/until/rescue must be Ripper::EXPR_LABEL # postfix if/unless/while/until/rescue must be Ripper::EXPR_LABEL
unless t[3].allbits?(Ripper::EXPR_LABEL) unless t[3].allbits?(Ripper::EXPR_LABEL)
depth_difference += 1 depth_difference += 1
end end
when 'else', 'elsif', 'rescue', 'ensure', 'when', 'in' when 'else', 'elsif', 'ensure', 'when', 'in'
depth_difference += 1 depth_difference += 1
end end
end end
@ -371,7 +437,36 @@ class RubyLex
spaces_of_nest = [] spaces_of_nest = []
spaces_at_line_head = 0 spaces_at_line_head = 0
open_brace_on_line = 0 open_brace_on_line = 0
in_oneliner_def = nil
@tokens.each_with_index do |t, index| @tokens.each_with_index do |t, index|
# detecting one-liner method definition
if in_oneliner_def.nil?
if t[3].allbits?(Ripper::EXPR_ENDFN)
in_oneliner_def = :ENDFN
end
else
if t[3].allbits?(Ripper::EXPR_ENDFN)
# continuing
elsif t[3].allbits?(Ripper::EXPR_BEG)
if t[2] == '='
in_oneliner_def = :BODY
end
elsif t[3].allbits?(Ripper::EXPR_END)
if in_oneliner_def == :BODY
# one-liner method definition
if is_first_printable_of_line
corresponding_token_depth = spaces_of_nest.pop
else
spaces_of_nest.pop
corresponding_token_depth = nil
end
end
in_oneliner_def = nil
else
in_oneliner_def = nil
end
end
case t[1] case t[1]
when :on_ignored_nl, :on_nl, :on_comment when :on_ignored_nl, :on_nl, :on_comment
corresponding_token_depth = nil corresponding_token_depth = nil
@ -386,7 +481,7 @@ class RubyLex
next next
end end
case t[1] case t[1]
when :on_lbracket, :on_lbrace, :on_lparen when :on_lbracket, :on_lbrace, :on_lparen, :on_tlambeg
spaces_of_nest.push(spaces_at_line_head + open_brace_on_line * 2) spaces_of_nest.push(spaces_at_line_head + open_brace_on_line * 2)
open_brace_on_line += 1 open_brace_on_line += 1
when :on_rbracket, :on_rbrace, :on_rparen when :on_rbracket, :on_rbrace, :on_rparen
@ -402,12 +497,16 @@ class RubyLex
case t[2] case t[2]
when 'def', 'do', 'case', 'for', 'begin', 'class', 'module' when 'def', 'do', 'case', 'for', 'begin', 'class', 'module'
spaces_of_nest.push(spaces_at_line_head) spaces_of_nest.push(spaces_at_line_head)
when 'rescue'
unless t[3].allbits?(Ripper::EXPR_LABEL)
corresponding_token_depth = spaces_of_nest.last
end
when 'if', 'unless', 'while', 'until' when 'if', 'unless', 'while', 'until'
# postfix if/unless/while/until/rescue must be Ripper::EXPR_LABEL # postfix if/unless/while/until must be Ripper::EXPR_LABEL
unless t[3].allbits?(Ripper::EXPR_LABEL) unless t[3].allbits?(Ripper::EXPR_LABEL)
spaces_of_nest.push(spaces_at_line_head) spaces_of_nest.push(spaces_at_line_head)
end end
when 'else', 'elsif', 'rescue', 'ensure', 'when', 'in' when 'else', 'elsif', 'ensure', 'when', 'in'
corresponding_token_depth = spaces_of_nest.last corresponding_token_depth = spaces_of_nest.last
when 'end' when 'end'
if is_first_printable_of_line if is_first_printable_of_line

View file

@ -35,4 +35,3 @@
m7 NW H N HSVO1z=?11- m7 NW H N HSVO1z=?11-
NgTH bB kH WBHWWHBHWmQgg&gggggNNN NgTH bB kH WBHWWHBHWmQgg&gggggNNN
NNggggggNN NNggggggNN

View file

@ -11,7 +11,7 @@
# #
module IRB # :nodoc: module IRB # :nodoc:
VERSION = "1.2.3" VERSION = "1.2.6"
@RELEASE_VERSION = VERSION @RELEASE_VERSION = VERSION
@LAST_UPDATE_DATE = "2020-02-15" @LAST_UPDATE_DATE = "2020-09-14"
end end

View file

@ -10,7 +10,7 @@
# #
# #
require "irb" require_relative "../irb"
require_relative "frame" require_relative "frame"
# An example printer for irb. # An example printer for irb.

127
test/irb/test_cmd.rb Normal file
View file

@ -0,0 +1,127 @@
# frozen_string_literal: false
require "test/unit"
require "irb"
require "irb/extend-command"
module TestIRB
class ExtendCommand < Test::Unit::TestCase
def setup
@pwd = Dir.pwd
@tmpdir = File.join(Dir.tmpdir, "test_reline_config_#{$$}")
begin
Dir.mkdir(@tmpdir)
rescue Errno::EEXIST
FileUtils.rm_rf(@tmpdir)
Dir.mkdir(@tmpdir)
end
Dir.chdir(@tmpdir)
@home_backup = ENV["HOME"]
ENV["HOME"] = @tmpdir
@default_encoding = [Encoding.default_external, Encoding.default_internal]
@stdio_encodings = [STDIN, STDOUT, STDERR].map {|io| [io.external_encoding, io.internal_encoding] }
IRB.instance_variable_get(:@CONF).clear
end
def teardown
ENV["HOME"] = @home_backup
Dir.chdir(@pwd)
FileUtils.rm_rf(@tmpdir)
EnvUtil.suppress_warning {
Encoding.default_external, Encoding.default_internal = *@default_encoding
[STDIN, STDOUT, STDERR].zip(@stdio_encodings) do |io, encs|
io.set_encoding(*encs)
end
}
end
def test_irb_info_multiline
FileUtils.touch("#{@tmpdir}/.inputrc")
FileUtils.touch("#{@tmpdir}/.irbrc")
IRB.setup(__FILE__, argv: [])
IRB.conf[:USE_MULTILINE] = true
IRB.conf[:USE_SINGLELINE] = false
IRB.conf[:VERBOSE] = false
workspace = IRB::WorkSpace.new(self)
irb = IRB::Irb.new(workspace)
IRB.conf[:MAIN_CONTEXT] = irb.context
expected = %r{
Ruby\sversion: .+\n
IRB\sversion:\sirb .+\n
InputMethod:\sReidlineInputMethod\swith\sReline .+ and .+\n
\.irbrc\spath: .+
}x
assert_match expected, irb.context.main.irb_info.to_s
end
def test_irb_info_singleline
FileUtils.touch("#{@tmpdir}/.inputrc")
FileUtils.touch("#{@tmpdir}/.irbrc")
IRB.setup(__FILE__, argv: [])
IRB.conf[:USE_MULTILINE] = false
IRB.conf[:USE_SINGLELINE] = true
IRB.conf[:VERBOSE] = false
workspace = IRB::WorkSpace.new(self)
irb = IRB::Irb.new(workspace)
IRB.conf[:MAIN_CONTEXT] = irb.context
expected = %r{
Ruby\sversion: .+\n
IRB\sversion:\sirb .+\n
InputMethod:\sReadlineInputMethod\swith .+ and .+\n
\.irbrc\spath: .+
}x
assert_match expected, irb.context.main.irb_info.to_s
end
def test_irb_info_multiline_without_rc_files
inputrc_backup = ENV["INPUTRC"]
ENV["INPUTRC"] = "unknown_inpurc"
ext_backup = IRB::IRBRC_EXT
IRB.__send__(:remove_const, :IRBRC_EXT)
IRB.const_set(:IRBRC_EXT, "unknown_ext")
IRB.setup(__FILE__, argv: [])
IRB.conf[:USE_MULTILINE] = true
IRB.conf[:USE_SINGLELINE] = false
IRB.conf[:VERBOSE] = false
workspace = IRB::WorkSpace.new(self)
irb = IRB::Irb.new(workspace)
IRB.conf[:MAIN_CONTEXT] = irb.context
expected = %r{
Ruby\sversion: .+\n
IRB\sversion:\sirb .+\n
InputMethod:\sReidlineInputMethod\swith\sReline\s[^ ]+(?!\sand\s.+)\n
\z
}x
assert_match expected, irb.context.main.irb_info.to_s
ensure
ENV["INPUTRC"] = inputrc_backup
IRB.__send__(:remove_const, :IRBRC_EXT)
IRB.const_set(:IRBRC_EXT, ext_backup)
end
def test_irb_info_singleline_without_rc_files
inputrc_backup = ENV["INPUTRC"]
ENV["INPUTRC"] = "unknown_inpurc"
ext_backup = IRB::IRBRC_EXT
IRB.__send__(:remove_const, :IRBRC_EXT)
IRB.const_set(:IRBRC_EXT, "unknown_ext")
IRB.setup(__FILE__, argv: [])
IRB.conf[:USE_MULTILINE] = false
IRB.conf[:USE_SINGLELINE] = true
IRB.conf[:VERBOSE] = false
workspace = IRB::WorkSpace.new(self)
irb = IRB::Irb.new(workspace)
IRB.conf[:MAIN_CONTEXT] = irb.context
expected = %r{
Ruby\sversion: .+\n
IRB\sversion:\sirb .+\n
InputMethod:\sReadlineInputMethod\swith\s(?~.*\sand\s.+)\n
\z
}x
assert_match expected, irb.context.main.irb_info.to_s
ensure
ENV["INPUTRC"] = inputrc_backup
IRB.__send__(:remove_const, :IRBRC_EXT)
IRB.const_set(:IRBRC_EXT, ext_backup)
end
end
end

View file

@ -30,6 +30,10 @@ module TestIRB
def reset def reset
@line_no = 0 @line_no = 0
end end
def winsize
[10, 20]
end
end end
def setup def setup
@ -98,6 +102,21 @@ module TestIRB
$VERBOSE = verbose $VERBOSE = verbose
end end
def test_eval_object_without_inspect_method
verbose, $VERBOSE = $VERBOSE, nil
input = TestInputMethod.new([
"BasicObject.new\n",
])
irb = IRB::Irb.new(IRB::WorkSpace.new(Object.new), input)
out, err = capture_output do
irb.eval_input
end
assert_empty err
assert(/\(Object doesn't support #inspect\)\n(=> )?\n/, out)
ensure
$VERBOSE = verbose
end
def test_default_config def test_default_config
assert_equal(true, @context.use_colorize?) assert_equal(true, @context.use_colorize?)
end end
@ -198,6 +217,151 @@ module TestIRB
assert_equal("", out) assert_equal("", out)
end end
def test_omit_on_assignment
input = TestInputMethod.new([
"a = [1] * 100\n",
"a\n",
])
value = [1] * 100
irb = IRB::Irb.new(IRB::WorkSpace.new(Object.new), input)
irb.context.return_format = "=> %s\n"
irb.context.echo = true
irb.context.echo_on_assignment = false
irb.context.omit_on_assignment = true
out, err = capture_io do
irb.eval_input
end
assert_empty err
assert_equal("=> #{value.inspect}\n", out)
input.reset
irb.context.echo = true
irb.context.echo_on_assignment = true
irb.context.omit_on_assignment = true
out, err = capture_io do
irb.eval_input
end
assert_empty err
assert_equal("=> #{value.inspect[0..(input.winsize.last - 9)]}...\e[0m\n=> #{value.inspect}\n", out)
input.reset
irb.context.echo = true
irb.context.echo_on_assignment = true
irb.context.omit_on_assignment = false
out, err = capture_io do
irb.eval_input
end
assert_empty err
assert_equal("=> #{value.inspect}\n=> #{value.inspect}\n", out)
input.reset
irb.context.echo = false
irb.context.echo_on_assignment = false
irb.context.omit_on_assignment = true
out, err = capture_io do
irb.eval_input
end
assert_empty err
assert_equal("", out)
input.reset
irb.context.echo = false
irb.context.echo_on_assignment = true
irb.context.omit_on_assignment = true
out, err = capture_io do
irb.eval_input
end
assert_empty err
assert_equal("", out)
input.reset
irb.context.echo = false
irb.context.echo_on_assignment = true
irb.context.omit_on_assignment = false
out, err = capture_io do
irb.eval_input
end
assert_empty err
assert_equal("", out)
end
def test_omit_multiline_on_assignment
input = TestInputMethod.new([
"class A; def inspect; ([?* * 1000] * 3).join(%{\\n}); end; end; a = A.new\n",
"a\n"
])
value = ([?* * 1000] * 3).join(%{\n})
value_first_line = (?* * 1000).to_s
irb = IRB::Irb.new(IRB::WorkSpace.new(Object.new), input)
irb.context.return_format = "=> %s\n"
irb.context.echo = true
irb.context.echo_on_assignment = false
irb.context.omit_on_assignment = true
out, err = capture_io do
irb.eval_input
end
assert_empty err
assert_equal("=> \n#{value}\n", out)
irb.context.evaluate('A.remove_method(:inspect)', 0)
input.reset
irb.context.echo = true
irb.context.echo_on_assignment = true
irb.context.omit_on_assignment = true
out, err = capture_io do
irb.eval_input
end
assert_empty err
assert_equal("=> #{value_first_line[0..(input.winsize.last - 9)]}...\e[0m\n=> \n#{value}\n", out)
irb.context.evaluate('A.remove_method(:inspect)', 0)
input.reset
irb.context.echo = true
irb.context.echo_on_assignment = true
irb.context.omit_on_assignment = false
out, err = capture_io do
irb.eval_input
end
assert_empty err
assert_equal("=> \n#{value}\n=> \n#{value}\n", out)
irb.context.evaluate('A.remove_method(:inspect)', 0)
input.reset
irb.context.echo = false
irb.context.echo_on_assignment = false
irb.context.omit_on_assignment = true
out, err = capture_io do
irb.eval_input
end
assert_empty err
assert_equal("", out)
irb.context.evaluate('A.remove_method(:inspect)', 0)
input.reset
irb.context.echo = false
irb.context.echo_on_assignment = true
irb.context.omit_on_assignment = true
out, err = capture_io do
irb.eval_input
end
assert_empty err
assert_equal("", out)
irb.context.evaluate('A.remove_method(:inspect)', 0)
input.reset
irb.context.echo = false
irb.context.echo_on_assignment = true
irb.context.omit_on_assignment = false
out, err = capture_io do
irb.eval_input
end
assert_empty err
assert_equal("", out)
irb.context.evaluate('A.remove_method(:inspect)', 0)
end
def test_echo_on_assignment_conf def test_echo_on_assignment_conf
# Default # Default
IRB.conf[:ECHO] = nil IRB.conf[:ECHO] = nil
@ -206,22 +370,26 @@ module TestIRB
irb = IRB::Irb.new(IRB::WorkSpace.new(Object.new), input) irb = IRB::Irb.new(IRB::WorkSpace.new(Object.new), input)
assert(irb.context.echo?, "echo? should be true by default") assert(irb.context.echo?, "echo? should be true by default")
refute(irb.context.echo_on_assignment?, "echo_on_assignment? should be false by default") assert(irb.context.echo_on_assignment?, "echo_on_assignment? should be true by default")
assert(irb.context.omit_on_assignment?, "omit_on_assignment? should be true by default")
# Explicitly set :ECHO to false # Explicitly set :ECHO to false
IRB.conf[:ECHO] = false IRB.conf[:ECHO] = false
irb = IRB::Irb.new(IRB::WorkSpace.new(Object.new), input) irb = IRB::Irb.new(IRB::WorkSpace.new(Object.new), input)
refute(irb.context.echo?, "echo? should be false when IRB.conf[:ECHO] is set to false") refute(irb.context.echo?, "echo? should be false when IRB.conf[:ECHO] is set to false")
refute(irb.context.echo_on_assignment?, "echo_on_assignment? should be false by default") assert(irb.context.echo_on_assignment?, "echo_on_assignment? should be true by default")
assert(irb.context.omit_on_assignment?, "omit_on_assignment? should be true by default")
# Explicitly set :ECHO_ON_ASSIGNMENT to true # Explicitly set :ECHO_ON_ASSIGNMENT to true
IRB.conf[:ECHO] = nil IRB.conf[:ECHO] = nil
IRB.conf[:ECHO_ON_ASSIGNMENT] = true IRB.conf[:ECHO_ON_ASSIGNMENT] = false
IRB.conf[:OMIT_ON_ASSIGNMENT] = false
irb = IRB::Irb.new(IRB::WorkSpace.new(Object.new), input) irb = IRB::Irb.new(IRB::WorkSpace.new(Object.new), input)
assert(irb.context.echo?, "echo? should be true by default") assert(irb.context.echo?, "echo? should be true by default")
assert(irb.context.echo_on_assignment?, "echo_on_assignment? should be true when IRB.conf[:ECHO_ON_ASSIGNMENT] is set to true") refute(irb.context.echo_on_assignment?, "echo_on_assignment? should be false when IRB.conf[:ECHO_ON_ASSIGNMENT] is set to false")
refute(irb.context.omit_on_assignment?, "omit_on_assignment? should be false when IRB.conf[:OMIT_ON_ASSIGNMENT] is set to false")
end end
def test_multiline_output_on_default_inspector def test_multiline_output_on_default_inspector

153
test/irb/test_history.rb Normal file
View file

@ -0,0 +1,153 @@
# frozen_string_literal: false
require 'test/unit'
require 'irb'
require 'readline'
module TestIRB
class TestHistory < Test::Unit::TestCase
def setup
IRB.conf[:RC_NAME_GENERATOR] = nil
end
def teardown
IRB.conf[:RC_NAME_GENERATOR] = nil
end
def test_history_save_1
omit "Skip Editline" if /EditLine/n.match(Readline::VERSION)
_result_output, result_history_file = launch_irb_with_irbrc_and_irb_history(<<~IRBRC, <<~IRB_HISTORY) do |stdin|
IRB.conf[:USE_READLINE] = true
IRB.conf[:SAVE_HISTORY] = 1
IRB.conf[:USE_READLINE] = true
IRBRC
1
2
3
4
IRB_HISTORY
stdin.write("5\nexit\n")
end
assert_equal(<<~HISTORY_FILE, result_history_file)
exit
HISTORY_FILE
end
def test_history_save_100
omit "Skip Editline" if /EditLine/n.match(Readline::VERSION)
_result_output, result_history_file = launch_irb_with_irbrc_and_irb_history(<<~IRBRC, <<~IRB_HISTORY) do |stdin|
IRB.conf[:USE_READLINE] = true
IRB.conf[:SAVE_HISTORY] = 100
IRB.conf[:USE_READLINE] = true
IRBRC
1
2
3
4
IRB_HISTORY
stdin.write("5\nexit\n")
end
assert_equal(<<~HISTORY_FILE, result_history_file)
1
2
3
4
5
exit
HISTORY_FILE
end
def test_history_save_bignum
omit "Skip Editline" if /EditLine/n.match(Readline::VERSION)
_result_output, result_history_file = launch_irb_with_irbrc_and_irb_history(<<~IRBRC, <<~IRB_HISTORY) do |stdin|
IRB.conf[:USE_READLINE] = true
IRB.conf[:SAVE_HISTORY] = 10 ** 19
IRB.conf[:USE_READLINE] = true
IRBRC
1
2
3
4
IRB_HISTORY
stdin.write("5\nexit\n")
end
assert_equal(<<~HISTORY_FILE, result_history_file)
1
2
3
4
5
exit
HISTORY_FILE
end
def test_history_save_minus_as_infinity
omit "Skip Editline" if /EditLine/n.match(Readline::VERSION)
_result_output, result_history_file = launch_irb_with_irbrc_and_irb_history(<<~IRBRC, <<~IRB_HISTORY) do |stdin|
IRB.conf[:USE_READLINE] = true
IRB.conf[:SAVE_HISTORY] = -1 # infinity
IRB.conf[:USE_READLINE] = true
IRBRC
1
2
3
4
IRB_HISTORY
stdin.write("5\nexit\n")
end
assert_equal(<<~HISTORY_FILE, result_history_file)
1
2
3
4
5
exit
HISTORY_FILE
end
private
def launch_irb_with_irbrc_and_irb_history(irbrc, irb_history)
result = nil
result_history = nil
backup_irbrc = ENV.delete("IRBRC")
backup_home = ENV["HOME"]
Dir.mktmpdir("test_irb_history_#{$$}") do |tmpdir|
ENV["HOME"] = tmpdir
open(IRB.rc_file, "w") do |f|
f.write(irbrc)
end
open(IRB.rc_file("_history"), "w") do |f|
f.write(irb_history)
end
with_temp_stdio do |stdin, stdout|
yield(stdin, stdout)
stdin.close
stdout.flush
system('ruby', '-Ilib', '-Itest', '-W0', '-rirb', '-e', 'IRB.start(__FILE__)', in: stdin.path, out: stdout.path)
result = stdout.read
stdout.close
end
open(IRB.rc_file("_history"), "r") do |f|
result_history = f.read
end
end
[result, result_history]
ensure
ENV["HOME"] = backup_home
ENV["IRBRC"] = backup_irbrc
end
def with_temp_stdio
Tempfile.create("test_readline_stdin") do |stdin|
Tempfile.create("test_readline_stdout") do |stdout|
yield stdin, stdout
end
end
end
end
end if not RUBY_PLATFORM.match?(/solaris|mswin|mingw/i)

View file

@ -5,7 +5,7 @@ require 'ostruct'
module TestIRB module TestIRB
class TestRubyLex < Test::Unit::TestCase class TestRubyLex < Test::Unit::TestCase
Row = Struct.new(:content, :current_line_spaces, :new_line_spaces) Row = Struct.new(:content, :current_line_spaces, :new_line_spaces, :nesting_level)
class MockIO class MockIO
def initialize(params, &assertion) def initialize(params, &assertion)
@ -34,6 +34,15 @@ module TestIRB
ruby_lex.set_auto_indent(context) ruby_lex.set_auto_indent(context)
end end
def assert_nesting_level(lines, expected)
ruby_lex = RubyLex.new()
io = proc{ lines.join("\n") }
ruby_lex.set_input(io, io)
ruby_lex.lex
error_message = "Calculated the wrong number of nesting level for:\n #{lines.join("\n")}"
assert_equal(expected, ruby_lex.instance_variable_get(:@indent), error_message)
end
def test_auto_indent def test_auto_indent
input_with_correct_indents = [ input_with_correct_indents = [
Row.new(%q(def each_top_level_statement), nil, 2), Row.new(%q(def each_top_level_statement), nil, 2),
@ -126,5 +135,130 @@ module TestIRB
assert_indenting(lines, row.new_line_spaces, true) assert_indenting(lines, row.new_line_spaces, true)
end end
end end
def test_incomplete_coding_magic_comment
input_with_correct_indents = [
Row.new(%q(#coding:u), nil, 0),
]
lines = []
input_with_correct_indents.each do |row|
lines << row.content
assert_indenting(lines, row.current_line_spaces, false)
assert_indenting(lines, row.new_line_spaces, true)
end
end
def test_incomplete_encoding_magic_comment
input_with_correct_indents = [
Row.new(%q(#encoding:u), nil, 0),
]
lines = []
input_with_correct_indents.each do |row|
lines << row.content
assert_indenting(lines, row.current_line_spaces, false)
assert_indenting(lines, row.new_line_spaces, true)
end
end
def test_incomplete_emacs_coding_magic_comment
input_with_correct_indents = [
Row.new(%q(# -*- coding: u), nil, 0),
]
lines = []
input_with_correct_indents.each do |row|
lines << row.content
assert_indenting(lines, row.current_line_spaces, false)
assert_indenting(lines, row.new_line_spaces, true)
end
end
def test_incomplete_vim_coding_magic_comment
input_with_correct_indents = [
Row.new(%q(# vim:set fileencoding=u), nil, 0),
]
lines = []
input_with_correct_indents.each do |row|
lines << row.content
assert_indenting(lines, row.current_line_spaces, false)
assert_indenting(lines, row.new_line_spaces, true)
end
end
def test_mixed_rescue
input_with_correct_indents = [
Row.new(%q(def m), nil, 2),
Row.new(%q( begin), nil, 4),
Row.new(%q( begin), nil, 6),
Row.new(%q( x = a rescue 4), nil, 6),
Row.new(%q( y = [(a rescue 5)]), nil, 6),
Row.new(%q( [x, y]), nil, 6),
Row.new(%q( rescue => e), 4, 6),
Row.new(%q( raise e rescue 8), nil, 6),
Row.new(%q( end), 4, 4),
Row.new(%q( rescue), 2, 4),
Row.new(%q( raise rescue 11), nil, 4),
Row.new(%q( end), 2, 2),
Row.new(%q(rescue => e), 0, 2),
Row.new(%q( raise e rescue 14), nil, 2),
Row.new(%q(end), 0, 0),
]
lines = []
input_with_correct_indents.each do |row|
lines << row.content
assert_indenting(lines, row.current_line_spaces, false)
assert_indenting(lines, row.new_line_spaces, true)
end
end
def test_oneliner_method_definition
input_with_correct_indents = [
Row.new(%q(class A), nil, 2),
Row.new(%q( def foo0), nil, 4),
Row.new(%q( 3), nil, 4),
Row.new(%q( end), 2, 2),
Row.new(%q( def foo1()), nil, 4),
Row.new(%q( 3), nil, 4),
Row.new(%q( end), 2, 2),
Row.new(%q( def foo2(a, b)), nil, 4),
Row.new(%q( a + b), nil, 4),
Row.new(%q( end), 2, 2),
Row.new(%q( def foo3 a, b), nil, 4),
Row.new(%q( a + b), nil, 4),
Row.new(%q( end), 2, 2),
Row.new(%q( def bar0() = 3), nil, 2),
Row.new(%q( def bar1(a) = a), nil, 2),
Row.new(%q( def bar2(a, b) = a + b), nil, 2),
Row.new(%q(end), 0, 0),
]
lines = []
input_with_correct_indents.each do |row|
lines << row.content
assert_indenting(lines, row.current_line_spaces, false)
assert_indenting(lines, row.new_line_spaces, true)
end
end
def test_tlambda
input_with_correct_indents = [
Row.new(%q(if true), nil, 2, 1),
Row.new(%q( -> {), nil, 4, 2),
Row.new(%q( }), 2, 2, 1),
Row.new(%q(end), 0, 0, 0),
]
lines = []
input_with_correct_indents.each do |row|
lines << row.content
assert_indenting(lines, row.current_line_spaces, false)
assert_indenting(lines, row.new_line_spaces, true)
assert_nesting_level(lines, row.nesting_level)
end
end
end end
end end

View file

@ -1,6 +1,7 @@
# frozen_string_literal: false # frozen_string_literal: false
require 'test/unit' require 'test/unit'
require 'tempfile' require 'tempfile'
require 'rubygems'
require 'irb' require 'irb'
require 'irb/workspace' require 'irb/workspace'
require 'irb/color' require 'irb/color'