diff options
57 files changed, 1169 insertions, 260 deletions
diff --git a/ext/io/console/console.c b/ext/io/console/console.c index 3688fd4240..9baad2bf17 100644 --- a/ext/io/console/console.c +++ b/ext/io/console/console.c @@ -111,6 +111,9 @@ rawmode_opt(int *argcp, VALUE *argv, int min_argc, int max_argc, rawmode_arg_t * int argc = *argcp; rawmode_arg_t *optp = NULL; VALUE vopts = Qnil; +#ifdef RB_SCAN_ARGS_PASS_CALLED_KEYWORDS + argc = rb_scan_args(argc, argv, "*:", NULL, &vopts); +#else if (argc > min_argc) { vopts = rb_check_hash_type(argv[argc-1]); if (!NIL_P(vopts)) { @@ -120,6 +123,7 @@ rawmode_opt(int *argcp, VALUE *argv, int min_argc, int max_argc, rawmode_arg_t * if (!vopts) vopts = Qnil; } } +#endif rb_check_arity(argc, min_argc, max_argc); if (!NIL_P(vopts)) { VALUE vmin = rb_hash_aref(vopts, ID2SYM(id_min)); @@ -188,7 +192,7 @@ set_rawmode(conmode *t, void *arg) #endif #ifdef ISIG if (r->intr) { - t->c_iflag |= BRKINT|IXON; + t->c_iflag |= BRKINT; t->c_lflag |= ISIG; t->c_oflag |= OPOST; } @@ -356,9 +360,9 @@ ttymode_with_io(VALUE io, VALUE (*func)(VALUE, VALUE), VALUE farg, void (*setter /* * call-seq: - * io.raw(min: nil, time: nil) {|io| } + * io.raw(min: nil, time: nil, intr: nil) {|io| } * - * Yields +self+ within raw mode. + * Yields +self+ within raw mode, and returns the result of the block. * * STDIN.raw(&:gets) * @@ -370,6 +374,9 @@ ttymode_with_io(VALUE io, VALUE (*func)(VALUE, VALUE), VALUE farg, void (*setter * The parameter +time+ specifies the timeout in _seconds_ with a * precision of 1/10 of a second. (default: 0) * + * If the parameter +intr+ is +true+, enables break, interrupt, quit, + * and suspend special characters. + * * Refer to the manual page of termios for further details. * * You must require 'io/console' to use this method. @@ -383,11 +390,11 @@ console_raw(int argc, VALUE *argv, VALUE io) /* * call-seq: - * io.raw!(min: nil, time: nil) + * io.raw!(min: nil, time: nil, intr: nil) -> io * - * Enables raw mode. + * Enables raw mode, and returns +io+. * - * If the terminal mode needs to be back, use io.raw { ... }. + * If the terminal mode needs to be back, use <code>io.raw { ... }</code>. * * See IO#raw for details on the parameters. * @@ -483,7 +490,7 @@ nogvl_getch(void *p) /* * call-seq: - * io.getch(min: nil, time: nil) -> char + * io.getch(min: nil, time: nil, intr: nil) -> char * * Reads and returns a character in raw mode. * @@ -1490,7 +1497,7 @@ console_dev(int argc, VALUE *argv, VALUE klass) /* * call-seq: - * io.getch(min: nil, time: nil) -> char + * io.getch(min: nil, time: nil, intr: nil) -> char * * See IO#getch. */ diff --git a/ext/io/console/extconf.rb b/ext/io/console/extconf.rb index a6049da667..3d7e75e2af 100644 --- a/ext/io/console/extconf.rb +++ b/ext/io/console/extconf.rb @@ -1,7 +1,7 @@ # frozen_string_literal: false require 'mkmf' -ok = true +ok = true if RUBY_ENGINE == "ruby" hdr = nil case when macro_defined?("_WIN32", "") @@ -14,8 +14,9 @@ when have_header(hdr = "sgtty.h") %w"stty gtty".each {|f| have_func(f, hdr)} else ok = false -end -if ok +end if ok +case ok +when true have_header("sys/ioctl.h") if hdr # rb_check_hash_type: 1.9.3 # rb_io_get_write_io: 1.9.1 @@ -27,4 +28,6 @@ if ok create_makefile("io/console") {|conf| conf << "\n""VK_HEADER = #{vk_header}\n" } +when nil + File.write("Makefile", dummy_makefile($srcdir).join("")) end diff --git a/ext/io/console/io-console.gemspec b/ext/io/console/io-console.gemspec index fac9bff9b7..814bd4ef7d 100644 --- a/ext/io/console/io-console.gemspec +++ b/ext/io/console/io-console.gemspec @@ -1,5 +1,5 @@ # -*- ruby -*- -_VERSION = "0.5.3" +_VERSION = "0.5.6" date = %w$Date:: $[1] Gem::Specification.new do |s| @@ -9,7 +9,7 @@ Gem::Specification.new do |s| s.summary = "Console interface" s.email = "nobu@ruby-lang.org" s.description = "add console capabilities to IO instances." - s.required_ruby_version = ">= 2.2.0" + s.required_ruby_version = ">= 2.4.0" s.homepage = "https://github.com/ruby/io-console" s.metadata["source_code_url"] = s.homepage s.authors = ["Nobu Nakada"] diff --git a/ext/readline/extconf.rb b/ext/readline/extconf.rb index fcc62921ae..d3e7872e65 100644 --- a/ext/readline/extconf.rb +++ b/ext/readline/extconf.rb @@ -109,5 +109,4 @@ unless readline.have_type("rl_hook_func_t*") $defs << "-Drl_hook_func_t=Function" end -$INCFLAGS << " -I$(top_srcdir)" create_makefile("readline") diff --git a/ext/readline/readline-ext.gemspec b/ext/readline/readline-ext.gemspec index a611a8ea9a..b85e07dd93 100644 --- a/ext/readline/readline-ext.gemspec +++ b/ext/readline/readline-ext.gemspec @@ -8,14 +8,19 @@ Gem::Specification.new do |spec| spec.description = %q{Provides an interface for GNU Readline and Edit Line (libedit).} spec.homepage = "https://github.com/ruby/readline-ext" spec.license = "BSD-2-Clause" + spec.extensions = %w[ext/readline/extconf.rb] spec.metadata["homepage_uri"] = spec.homepage spec.metadata["source_code_uri"] = spec.homepage spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do - `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } + `git ls-files -z 2>/dev/null`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } end spec.bindir = "exe" spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } spec.require_paths = ["lib"] + + spec.add_development_dependency "bundler" + spec.add_development_dependency "rake" + spec.add_development_dependency "rake-compiler" end diff --git a/lib/benchmark/benchmark.gemspec b/lib/benchmark/benchmark.gemspec index aad5205f8d..773cab19b0 100644 --- a/lib/benchmark/benchmark.gemspec +++ b/lib/benchmark/benchmark.gemspec @@ -21,7 +21,7 @@ Gem::Specification.new do |spec| # Specify which files should be added to the gem when it is released. # The `git ls-files -z` loads the files in the RubyGem that have been added into git. spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do - `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } + `git ls-files -z 2>/dev/null`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } end spec.bindir = "exe" spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } diff --git a/lib/cgi/cgi.gemspec b/lib/cgi/cgi.gemspec index 403d31c978..58bd77027d 100644 --- a/lib/cgi/cgi.gemspec +++ b/lib/cgi/cgi.gemspec @@ -19,7 +19,7 @@ Gem::Specification.new do |spec| spec.metadata["source_code_uri"] = spec.homepage spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do - `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } + `git ls-files -z 2>/dev/null`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } end spec.bindir = "exe" spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } diff --git a/lib/delegate/delegate.gemspec b/lib/delegate/delegate.gemspec index e51b50a98f..268cc5a817 100644 --- a/lib/delegate/delegate.gemspec +++ b/lib/delegate/delegate.gemspec @@ -19,7 +19,7 @@ Gem::Specification.new do |spec| spec.metadata["source_code_uri"] = spec.homepage spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do - `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } + `git ls-files -z 2>/dev/null`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } end spec.bindir = "exe" spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } diff --git a/lib/getoptlong/getoptlong.gemspec b/lib/getoptlong/getoptlong.gemspec index 198bba83ac..5e218b8e93 100644 --- a/lib/getoptlong/getoptlong.gemspec +++ b/lib/getoptlong/getoptlong.gemspec @@ -21,7 +21,7 @@ Gem::Specification.new do |spec| # Specify which files should be added to the gem when it is released. # The `git ls-files -z` loads the files in the RubyGem that have been added into git. spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do - `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } + `git ls-files -z 2>/dev/null`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } end spec.bindir = "exe" spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } diff --git a/lib/irb.rb b/lib/irb.rb index bcd6599af9..ee6979c6d0 100644 --- a/lib/irb.rb +++ b/lib/irb.rb @@ -21,6 +21,7 @@ require "irb/locale" require "irb/color" require "irb/version" +require "irb/easter-egg" # IRB stands for "interactive Ruby" and is a tool to interactively execute Ruby # expressions read from the standard input. @@ -553,7 +554,8 @@ module IRB def handle_exception(exc) if exc.backtrace && exc.backtrace[0] =~ /\/irb(2)?(\/.*|-.*|\.rb)?:/ && exc.class.to_s !~ /^IRB/ && - !(SyntaxError === exc) + !(SyntaxError === exc) && !(EncodingError === exc) + # The backtrace of invalid encoding hash (ex. {"\xAE": 1}) raises EncodingError without lineno. irb_bug = true else irb_bug = false @@ -736,7 +738,13 @@ module IRB end def output_value # :nodoc: - printf @context.return_format, @context.inspect_last_value + str = @context.inspect_last_value + multiline_p = str.include?("\n") + if multiline_p && @context.newline_before_multiline_output? + printf @context.return_format, "\n#{str}" + else + printf @context.return_format, str + end end # Outputs the local variables to this current session, including diff --git a/lib/irb/.document b/lib/irb/.document new file mode 100644 index 0000000000..3b0d6fa4ed --- /dev/null +++ b/lib/irb/.document @@ -0,0 +1 @@ +**/*.rb diff --git a/lib/irb/completion.rb b/lib/irb/completion.rb index 3536e8ec87..c44aa9039e 100644 --- a/lib/irb/completion.rb +++ b/lib/irb/completion.rb @@ -17,11 +17,12 @@ module IRB # Set of reserved words used by Ruby, you should not use these for # constants or variables ReservedWords = %w[ + __ENCODING__ __LINE__ __FILE__ BEGIN END alias and begin break case class - def defined do + def defined? do else elsif end ensure false for if in @@ -98,7 +99,11 @@ module IRB return nil if doc_namespace if Symbol.respond_to?(:all_symbols) sym = $1 - candidates = Symbol.all_symbols.collect{|s| ":" + s.id2name} + candidates = Symbol.all_symbols.collect do |s| + ":" + s.id2name.encode(Encoding.default_external) + rescue Encoding::UndefinedConversionError + # ignore + end candidates.grep(/^#{Regexp.quote(sym)}/) else [] @@ -143,7 +148,7 @@ module IRB select_message(receiver, message, candidates, sep) end - when /^(?<num>-?(0[dbo])?[0-9_]+(\.[0-9_]+)?(([eE][+-]?[0-9]+)?i?|r)?)(?<sep>\.|::)(?<mes>[^.]*)$/ + when /^(?<num>-?(?:0[dbo])?[0-9_]+(?:\.[0-9_]+)?(?:(?:[eE][+-]?[0-9]+)?i?|r)?)(?<sep>\.|::)(?<mes>[^.]*)$/ # Numeric receiver = $~[:num] sep = $~[:sep] @@ -203,7 +208,7 @@ module IRB sep = $2 message = Regexp.quote($3) - gv = eval("global_variables", bind).collect{|m| m.to_s}.append("true", "false", "nil") + gv = eval("global_variables", bind).collect{|m| m.to_s}.push("true", "false", "nil") lv = eval("local_variables", bind).collect{|m| m.to_s} iv = eval("instance_variables", bind).collect{|m| m.to_s} cv = eval("self.class.constants", bind).collect{|m| m.to_s} @@ -255,7 +260,7 @@ module IRB else candidates = eval("methods | private_methods | local_variables | instance_variables | self.class.constants", bind).collect{|m| m.to_s} - conditions |= ReservedWords + candidates |= ReservedWords if doc_namespace candidates.find{ |i| i == input } @@ -265,18 +270,14 @@ module IRB end end - PerfectMatchedProc = ->(matched) { + PerfectMatchedProc = ->(matched, bind: IRB.conf[:MAIN_CONTEXT].workspace.binding) { RDocRIDriver ||= RDoc::RI::Driver.new if matched =~ /\A(?:::)?RubyVM/ and not ENV['RUBY_YES_I_AM_NOT_A_NORMAL_USER'] - File.open(File.join(__dir__, 'ruby_logo.aa')) do |f| - RDocRIDriver.page do |io| - IO.copy_stream(f, io) - end - end + IRB.send(:easter_egg) return end - namespace = retrieve_completion_data(matched, doc_namespace: true) - return unless matched + namespace = retrieve_completion_data(matched, bind: bind, doc_namespace: true) + return unless namespace if namespace.is_a?(Array) out = RDoc::Markup::Document.new namespace.each do |m| diff --git a/lib/irb/context.rb b/lib/irb/context.rb index 686738cd40..218f7c6037 100644 --- a/lib/irb/context.rb +++ b/lib/irb/context.rb @@ -133,6 +133,11 @@ module IRB if @echo_on_assignment.nil? @echo_on_assignment = false end + + @newline_before_multiline_output = IRB.conf[:NEWLINE_BEFORE_MULTILINE_OUTPUT] + if @newline_before_multiline_output.nil? + @newline_before_multiline_output = true + end end # The top-level workspace, see WorkSpace#main @@ -253,6 +258,20 @@ module IRB # a = "omg" # #=> omg attr_accessor :echo_on_assignment + # Whether a newline is put before multiline output. + # + # Uses IRB.conf[:NEWLINE_BEFORE_MULTILINE_OUTPUT] if available, + # or defaults to +true+. + # + # "abc\ndef" + # #=> + # abc + # def + # IRB.CurrentContext.newline_before_multiline_output = false + # "abc\ndef" + # #=> abc + # def + attr_accessor :newline_before_multiline_output # Whether verbose messages are displayed or not. # # A copy of the default <code>IRB.conf[:VERBOSE]</code> @@ -287,6 +306,7 @@ module IRB alias ignore_eof? ignore_eof alias echo? echo alias echo_on_assignment? echo_on_assignment + alias newline_before_multiline_output? newline_before_multiline_output # Returns whether messages are displayed or not. def verbose? diff --git a/lib/irb/easter-egg.rb b/lib/irb/easter-egg.rb new file mode 100644 index 0000000000..64869d85fa --- /dev/null +++ b/lib/irb/easter-egg.rb @@ -0,0 +1,137 @@ +require "reline" + +module IRB + class << self + class Vec + def initialize(x, y, z) + @x, @y, @z = x, y, z + end + + attr_reader :x, :y, :z + + def sub(other) + Vec.new(@x - other.x, @y - other.y, @z - other.z) + end + + def dot(other) + @x*other.x + @y*other.y + @z*other.z + end + + def cross(other) + ox, oy, oz = other.x, other.y, other.z + Vec.new(@y*oz-@z*oy, @z*ox-@x*oz, @x*oy-@y*ox) + end + + def normalize + r = Math.sqrt(self.dot(self)) + Vec.new(@x / r, @y / r, @z / r) + end + end + + class Canvas + def initialize((h, w)) + @data = (0..h-2).map { [0] * w } + @scale = [w / 2.0, h-2].min + @center = Complex(w / 2, h-2) + end + + def line((x1, y1), (x2, y2)) + p1 = Complex(x1, y1) / 2 * @scale + @center + p2 = Complex(x2, y2) / 2 * @scale + @center + line0(p1, p2) + end + + private def line0(p1, p2) + mid = (p1 + p2) / 2 + if (p1 - p2).abs < 1 + x, y = mid.rect + @data[y / 2][x] |= (y % 2 > 1 ? 2 : 1) + else + line0(p1, mid) + line0(p2, mid) + end + end + + def draw + @data.each {|row| row.fill(0) } + yield + @data.map {|row| row.map {|n| " ',;"[n] }.join }.join("\n") + end + end + + class RubyModel + def initialize + @faces = init_ruby_model + end + + def init_ruby_model + cap_vertices = (0..5).map {|i| Vec.new(*Complex.polar(1, i * Math::PI / 3).rect, 1) } + middle_vertices = (0..5).map {|i| Vec.new(*Complex.polar(2, (i + 0.5) * Math::PI / 3).rect, 0) } + bottom_vertex = Vec.new(0, 0, -2) + + faces = [cap_vertices] + 6.times do |j| + i = j-1 + faces << [cap_vertices[i], middle_vertices[i], cap_vertices[j]] + faces << [cap_vertices[j], middle_vertices[i], middle_vertices[j]] + faces << [middle_vertices[i], bottom_vertex, middle_vertices[j]] + end + + faces + end + + def render_frame(i) + angle = i / 10.0 + dir = Vec.new(*Complex.polar(1, angle).rect, Math.sin(angle)).normalize + dir2 = Vec.new(*Complex.polar(1, angle - Math::PI/2).rect, 0) + up = dir.cross(dir2) + nm = dir.cross(up) + @faces.each do |vertices| + v0, v1, v2, = vertices + if v1.sub(v0).cross(v2.sub(v0)).dot(dir) > 0 + points = vertices.map {|p| [nm.dot(p), up.dot(p)] } + (points + [points[0]]).each_cons(2) do |p1, p2| + yield p1, p2 + end + end + end + end + end + + private def easter_egg(type = nil) + type ||= [:logo, :dancing].sample + case type + when :logo + File.open(File.join(__dir__, 'ruby_logo.aa')) do |f| + require "rdoc" + RDoc::RI::Driver.new.page do |io| + IO.copy_stream(f, io) + end + end + when :dancing + begin + canvas = Canvas.new(Reline.get_screen_size) + Reline::IOGate.set_winch_handler do + canvas = Canvas.new(Reline.get_screen_size) + end + ruby_model = RubyModel.new + print "\e[?1049h" + 0.step do |i| # TODO (0..).each needs Ruby 2.6 or later + buff = canvas.draw do + ruby_model.render_frame(i) do |p1, p2| + canvas.line(p1, p2) + end + end + buff[0, 20] = "\e[0mPress Ctrl+C to stop\e[31m\e[1m" + print "\e[H" + buff + sleep 0.05 + end + ensure + print "\e[0m\e[?1049l" + end + end + end + end +end + +IRB.send(:easter_egg, ARGV[0]&.to_sym) if $0 == __FILE__ diff --git a/lib/irb/ext/save-history.rb b/lib/irb/ext/save-history.rb index c0d4d372b7..edc4f234cc 100644 --- a/lib/irb/ext/save-history.rb +++ b/lib/irb/ext/save-history.rb @@ -72,7 +72,7 @@ module IRB end history_file = IRB.rc_file("_history") unless history_file if File.exist?(history_file) - open(history_file) do |f| + open(history_file, "r:#{IRB.conf[:LC_MESSAGES].encoding}") do |f| f.each { |l| l = l.chomp if self.class == ReidlineInputMethod and history.last&.end_with?("\\") @@ -107,7 +107,7 @@ module IRB raise end - open(history_file, 'w', 0600 ) do |f| + open(history_file, "w:#{IRB.conf[:LC_MESSAGES].encoding}", 0600) do |f| hist = history.map{ |l| l.split("\n").join("\\\n") } f.puts(hist[-num..-1] || hist) end diff --git a/lib/irb/init.rb b/lib/irb/init.rb index 2af872fd03..37d1f8d609 100644 --- a/lib/irb/init.rb +++ b/lib/irb/init.rb @@ -296,15 +296,18 @@ module IRB # :nodoc: DefaultEncodings = Struct.new(:external, :internal) class << IRB private - def set_encoding(extern, intern = nil) + def set_encoding(extern, intern = nil, override: true) verbose, $VERBOSE = $VERBOSE, nil Encoding.default_external = extern unless extern.nil? || extern.empty? Encoding.default_internal = intern unless intern.nil? || intern.empty? - @CONF[:ENCODINGS] = IRB::DefaultEncodings.new(extern, intern) [$stdin, $stdout, $stderr].each do |io| io.set_encoding(extern, intern) end - @CONF[:LC_MESSAGES].instance_variable_set(:@encoding, extern) + if override + @CONF[:LC_MESSAGES].instance_variable_set(:@override_encoding, extern) + else + @CONF[:LC_MESSAGES].instance_variable_set(:@encoding, extern) + end ensure $VERBOSE = verbose end diff --git a/lib/irb/input-method.rb b/lib/irb/input-method.rb index a1777d7904..9fbbaeb0f3 100644 --- a/lib/irb/input-method.rb +++ b/lib/irb/input-method.rb @@ -133,6 +133,9 @@ module IRB include Readline # Creates a new input method object using Readline def initialize + if Readline.respond_to?(:encoding_system_needs) + IRB.__send__(:set_encoding, Readline.encoding_system_needs.name, override: false) + end super @line_no = 0 @@ -207,6 +210,7 @@ module IRB include Reline # Creates a new input method object using Readline def initialize + IRB.__send__(:set_encoding, Reline.encoding_system_needs.name, override: false) super @line_no = 0 diff --git a/lib/irb/irb.gemspec b/lib/irb/irb.gemspec index c3208954c5..658fe813f1 100644 --- a/lib/irb/irb.gemspec +++ b/lib/irb/irb.gemspec @@ -17,6 +17,7 @@ Gem::Specification.new do |spec| spec.license = "BSD-2-Clause" spec.files = [ + ".document", "Gemfile", "LICENSE.txt", "README.md", @@ -38,6 +39,7 @@ Gem::Specification.new do |spec| "lib/irb/color.rb", "lib/irb/completion.rb", "lib/irb/context.rb", + "lib/irb/easter-egg.rb", "lib/irb/ext/change-ws.rb", "lib/irb/ext/history.rb", "lib/irb/ext/loader.rb", @@ -52,7 +54,6 @@ Gem::Specification.new do |spec| "lib/irb/init.rb", "lib/irb/input-method.rb", "lib/irb/inspector.rb", - "lib/irb/lc/.document", "lib/irb/lc/error.rb", "lib/irb/lc/help-message", "lib/irb/lc/ja/encoding_aliases.rb", diff --git a/lib/irb/lc/.document b/lib/irb/lc/.document deleted file mode 100644 index 524bb9430b..0000000000 --- a/lib/irb/lc/.document +++ /dev/null @@ -1,4 +0,0 @@ -# hide help-message files which contain usage information -error.rb -ja/encoding_aliases.rb -ja/error.rb diff --git a/lib/irb/locale.rb b/lib/irb/locale.rb index ba833eced4..bb44b41002 100644 --- a/lib/irb/locale.rb +++ b/lib/irb/locale.rb @@ -24,6 +24,7 @@ module IRB # :nodoc: @@loaded = [] def initialize(locale = nil) + @override_encoding = nil @lang = @territory = @encoding_name = @modifier = nil @locale = locale || ENV["IRB_LANG"] || ENV["LC_MESSAGES"] || ENV["LC_ALL"] || ENV["LANG"] || "C" if m = LOCALE_NAME_RE.match(@locale) @@ -40,12 +41,16 @@ module IRB # :nodoc: @encoding ||= (Encoding.find('locale') rescue Encoding::ASCII_8BIT) end - attr_reader :lang, :territory, :encoding, :modifier + attr_reader :lang, :territory, :modifier + + def encoding + @override_encoding || @encoding + end def String(mes) mes = super(mes) - if @encoding - mes.encode(@encoding, undef: :replace) + if encoding + mes.encode(encoding, undef: :replace) else mes end diff --git a/lib/irb/ruby-lex.rb b/lib/irb/ruby-lex.rb index b4c31c16fe..d5630c8b52 100644 --- a/lib/irb/ruby-lex.rb +++ b/lib/irb/ruby-lex.rb @@ -211,6 +211,8 @@ class RubyLex else RubyVM::InstructionSequence.compile(code) end + rescue EncodingError + # This is for a hash with invalid encoding symbol, {"\xAE": 1} rescue SyntaxError => e case e.message when /unterminated (?:string|regexp) meets end of file/ @@ -317,11 +319,13 @@ class RubyLex def check_newline_depth_difference depth_difference = 0 + open_brace_on_line = 0 @tokens.each_with_index do |t, index| case t[1] when :on_ignored_nl, :on_nl, :on_comment if index != (@tokens.size - 1) depth_difference = 0 + open_brace_on_line = 0 end next when :on_sp @@ -330,8 +334,9 @@ class RubyLex case t[1] when :on_lbracket, :on_lbrace, :on_lparen depth_difference += 1 + open_brace_on_line += 1 when :on_rbracket, :on_rbrace, :on_rparen - depth_difference -= 1 + depth_difference -= 1 if open_brace_on_line > 0 when :on_kw next if index > 0 and @tokens[index - 1][3].allbits?(Ripper::EXPR_FNAME) case t[2] @@ -365,6 +370,7 @@ class RubyLex is_first_printable_of_line = true spaces_of_nest = [] spaces_at_line_head = 0 + open_brace_on_line = 0 @tokens.each_with_index do |t, index| case t[1] when :on_ignored_nl, :on_nl, :on_comment @@ -372,6 +378,7 @@ class RubyLex spaces_at_line_head = 0 is_first_spaces_of_line = true is_first_printable_of_line = true + open_brace_on_line = 0 next when :on_sp spaces_at_line_head = t[2].count(' ') if is_first_spaces_of_line @@ -380,7 +387,8 @@ class RubyLex end case t[1] when :on_lbracket, :on_lbrace, :on_lparen - spaces_of_nest.push(spaces_at_line_head) + spaces_of_nest.push(spaces_at_line_head + open_brace_on_line * 2) + open_brace_on_line += 1 when :on_rbracket, :on_rbrace, :on_rparen if is_first_printable_of_line corresponding_token_depth = spaces_of_nest.pop @@ -388,6 +396,7 @@ class RubyLex spaces_of_nest.pop corresponding_token_depth = nil end + open_brace_on_line -= 1 when :on_kw next if index > 0 and @tokens[index - 1][3].allbits?(Ripper::EXPR_FNAME) case t[2] diff --git a/lib/irb/version.rb b/lib/irb/version.rb index 9b8bd9b161..7669c1c535 100644 --- a/lib/irb/version.rb +++ b/lib/irb/version.rb @@ -11,7 +11,7 @@ # module IRB # :nodoc: - VERSION = "1.2.1" + VERSION = "1.2.3" @RELEASE_VERSION = VERSION - @LAST_UPDATE_DATE = "2019-12-24" + @LAST_UPDATE_DATE = "2020-02-15" end diff --git a/lib/net/pop/net-pop.gemspec b/lib/net/pop/net-pop.gemspec index 8166968d7d..c1b0ffbd2b 100644 --- a/lib/net/pop/net-pop.gemspec +++ b/lib/net/pop/net-pop.gemspec @@ -19,7 +19,7 @@ Gem::Specification.new do |spec| spec.metadata["source_code_uri"] = spec.homepage spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do - `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } + `git ls-files -z 2>/dev/null`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } end spec.bindir = "exe" spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } diff --git a/lib/net/smtp/net-smtp.gemspec b/lib/net/smtp/net-smtp.gemspec index e9635cd091..1dddfa7ca8 100644 --- a/lib/net/smtp/net-smtp.gemspec +++ b/lib/net/smtp/net-smtp.gemspec @@ -19,7 +19,7 @@ Gem::Specification.new do |spec| spec.metadata["source_code_uri"] = spec.homepage spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do - `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } + `git ls-files -z 2>/dev/null`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } end spec.bindir = "exe" spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } diff --git a/lib/observer/observer.gemspec b/lib/observer/observer.gemspec index 625a30eada..188c6bae76 100644 --- a/lib/observer/observer.gemspec +++ b/lib/observer/observer.gemspec @@ -21,7 +21,7 @@ Gem::Specification.new do |spec| # Specify which files should be added to the gem when it is released. # The `git ls-files -z` loads the files in the RubyGem that have been added into git. spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do - `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } + `git ls-files -z 2>/dev/null`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } end spec.bindir = "exe" spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } diff --git a/lib/open3/open3.gemspec b/lib/open3/open3.gemspec index 65ddaa4723..543416e427 100644 --- a/lib/open3/open3.gemspec +++ b/lib/open3/open3.gemspec @@ -21,7 +21,7 @@ Gem::Specification.new do |spec| # Specify which files should be added to the gem when it is released. # The `git ls-files -z` loads the files in the RubyGem that have been added into git. spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do - `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } + `git ls-files -z 2>/dev/null`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } end spec.bindir = "exe" spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } diff --git a/lib/pstore/pstore.gemspec b/lib/pstore/pstore.gemspec index 408af0a078..e781c77043 100644 --- a/lib/pstore/pstore.gemspec +++ b/lib/pstore/pstore.gemspec @@ -21,7 +21,7 @@ Gem::Specification.new do |spec| # Specify which files should be added to the gem when it is released. # The `git ls-files -z` loads the files in the RubyGem that have been added into git. spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do - `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } + `git ls-files -z 2>/dev/null`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } end spec.bindir = "exe" spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } diff --git a/lib/reline.rb b/lib/reline.rb index 9a9f742909..1537ee7e69 100644 --- a/lib/reline.rb +++ b/lib/reline.rb @@ -45,40 +45,44 @@ module Reline @completion_quote_character = nil end + def encoding + Reline::IOGate.encoding + end + def completion_append_character=(val) if val.nil? @completion_append_character = nil elsif val.size == 1 - @completion_append_character = val.encode(Encoding::default_external) + @completion_append_character = val.encode(Reline::IOGate.encoding) elsif val.size > 1 - @completion_append_character = val[0].encode(Encoding::default_external) + @completion_append_character = val[0].encode(Reline::IOGate.encoding) else @completion_append_character = nil end end def basic_word_break_characters=(v) - @basic_word_break_characters = v.encode(Encoding::default_external) + @basic_word_break_characters = v.encode(Reline::IOGate.encoding) end def completer_word_break_characters=(v) - @completer_word_break_characters = v.encode(Encoding::default_external) + @completer_word_break_characters = v.encode(Reline::IOGate.encoding) end def basic_quote_characters=(v) - @basic_quote_characters = v.encode(Encoding::default_external) + @basic_quote_characters = v.encode(Reline::IOGate.encoding) end def completer_quote_characters=(v) - @completer_quote_characters = v.encode(Encoding::default_external) + @completer_quote_characters = v.encode(Reline::IOGate.encoding) end def filename_quote_characters=(v) - @filename_quote_characters = v.encode(Encoding::default_external) + @filename_quote_characters = v.encode(Reline::IOGate.encoding) end def special_prefixes=(v) - @special_prefixes = v.encode(Encoding::default_external) + @special_prefixes = v.encode(Reline::IOGate.encoding) end def completion_case_fold=(v) @@ -171,7 +175,7 @@ module Reline whole_buffer = line_editor.whole_buffer.dup whole_buffer.taint if RUBY_VERSION < '2.7' - if add_hist and whole_buffer and whole_buffer.chomp.size > 0 + if add_hist and whole_buffer and whole_buffer.chomp("\n").size > 0 Reline::HISTORY << whole_buffer end @@ -184,8 +188,8 @@ module Reline line = line_editor.line.dup line.taint if RUBY_VERSION < '2.7' - if add_hist and line and line.chomp.size > 0 - Reline::HISTORY << line.chomp + if add_hist and line and line.chomp("\n").size > 0 + Reline::HISTORY << line.chomp("\n") end line_editor.reset_line if line_editor.line.nil? @@ -201,7 +205,7 @@ module Reline otio = Reline::IOGate.prep may_req_ambiguous_char_width - line_editor.reset(prompt) + line_editor.reset(prompt, encoding: Reline::IOGate.encoding) if multiline line_editor.multiline_on if block_given? @@ -332,8 +336,14 @@ module Reline @ambiguous_width = 2 if Reline::IOGate == Reline::GeneralIO or STDOUT.is_a?(File) return if ambiguous_width Reline::IOGate.move_cursor_column(0) - print "\u{25bd}" - @ambiguous_width = Reline::IOGate.cursor_pos.x + begin + output.write "\u{25bd}" + rescue Encoding::UndefinedConversionError + # LANG=C + @ambiguous_width = 1 + else + @ambiguous_width = Reline::IOGate.cursor_pos.x + end Reline::IOGate.move_cursor_column(0) Reline::IOGate.erase_after_cursor end @@ -387,11 +397,15 @@ module Reline def_instance_delegators self, :readmultiline private :readmultiline + def self.encoding_system_needs + self.core.encoding + end + def self.core @core ||= Core.new { |core| core.config = Reline::Config.new core.key_stroke = Reline::KeyStroke.new(core.config) - core.line_editor = Reline::LineEditor.new(core.config) + core.line_editor = Reline::LineEditor.new(core.config, Reline::IOGate.encoding) core.basic_word_break_characters = " \t\n`><=;|&{(" core.completer_word_break_characters = " \t\n`><=;|&{(" @@ -405,14 +419,11 @@ module Reline def self.line_editor core.line_editor end - - HISTORY = History.new(core.config) end if RbConfig::CONFIG['host_os'] =~ /mswin|msys|mingw|cygwin|bccwin|wince|emc/ require 'reline/windows' - if Reline::Windows.get_screen_size == [0, 0] - # Maybe Mintty on Cygwin + if Reline::Windows.msys_tty? require 'reline/ansi' Reline::IOGate = Reline::ANSI else @@ -422,4 +433,5 @@ else require 'reline/ansi' Reline::IOGate = Reline::ANSI end +Reline::HISTORY = Reline::History.new(Reline.core.config) require 'reline/general_io' diff --git a/lib/reline/ansi.rb b/lib/reline/ansi.rb index 8d83da854c..3ef02d6e7a 100644 --- a/lib/reline/ansi.rb +++ b/lib/reline/ansi.rb @@ -1,20 +1,49 @@ require 'io/console' class Reline::ANSI + def self.encoding + Encoding.default_external + end + + def self.win? + false + end + RAW_KEYSTROKE_CONFIG = { + # Console (80x25) + [27, 91, 49, 126] => :ed_move_to_beg, # Home + [27, 91, 52, 126] => :ed_move_to_end, # End + [27, 91, 51, 126] => :key_delete, # Del [27, 91, 65] => :ed_prev_history, # ↑ [27, 91, 66] => :ed_next_history, # ↓ [27, 91, 67] => :ed_next_char, # → [27, 91, 68] => :ed_prev_char, # ← - [27, 91, 51, 126] => :key_delete, # Del - [27, 91, 49, 126] => :ed_move_to_beg, # Home - [27, 91, 52, 126] => :ed_move_to_end, # End + + # KDE [27, 91, 72] => :ed_move_to_beg, # Home [27, 91, 70] => :ed_move_to_end, # End + # Del is 0x08 + [27, 71, 65] => :ed_prev_history, # ↑ + [27, 71, 66] => :ed_next_history, # ↓ + [27, 71, 67] => :ed_next_char, # → + [27, 71, 68] => :ed_prev_char, # ← + + # GNOME + [27, 79, 72] => :ed_move_to_beg, # Home + [27, 79, 70] => :ed_move_to_end, # End + # Del is 0x08 + # Arrow keys are the same of KDE + + # others [27, 32] => :em_set_mark, # M-<space> [24, 24] => :em_exchange_mark, # C-x C-x TODO also add Windows [27, 91, 49, 59, 53, 67] => :em_next_word, # Ctrl+→ [27, 91, 49, 59, 53, 68] => :ed_prev_word, # Ctrl+← + + [27, 79, 65] => :ed_prev_history, # ↑ + [27, 79, 66] => :ed_next_history, # ↓ + [27, 79, 67] => :ed_next_char, # → + [27, 79, 68] => :ed_prev_char, # ← } @@input = STDIN @@ -41,16 +70,23 @@ class Reline::ANSI end def self.retrieve_keybuffer + begin result = select([@@input], [], [], 0.001) return if result.nil? str = @@input.read_nonblock(1024) str.bytes.each do |c| @@buf.push(c) end + rescue EOFError + end end def self.get_screen_size - @@input.winsize + s = @@input.winsize + return s if s[0] > 0 && s[1] > 0 + s = [ENV["LINES"].to_i, ENV["COLUMNS"].to_i] + return s if s[0] > 0 && s[1] > 0 + [24, 80] rescue Errno::ENOTTY [24, 80] end @@ -88,12 +124,12 @@ class Reline::ANSI end def self.move_cursor_column(x) - print "\e[#{x + 1}G" + @@output.write "\e[#{x + 1}G" end def self.move_cursor_up(x) if x > 0 - print "\e[#{x}A" if x > 0 + @@output.write "\e[#{x}A" if x > 0 elsif x < 0 move_cursor_down(-x) end @@ -101,24 +137,24 @@ class Reline::ANSI def self.move_cursor_down(x) if x > 0 - print "\e[#{x}B" if x > 0 + @@output.write "\e[#{x}B" if x > 0 elsif x < 0 move_cursor_up(-x) end end def self.erase_after_cursor - print "\e[K" + @@output.write "\e[K" end def self.scroll_down(x) return if x.zero? - print "\e[#{x}S" + @@output.write "\e[#{x}S" end def self.clear_screen - print "\e[2J" - print "\e[1;1H" + @@output.write "\e[2J" + @@output.write "\e[1;1H" end @@old_winch_handler = nil diff --git a/lib/reline/config.rb b/lib/reline/config.rb index fdc2b39c1b..53b868fd2e 100644 --- a/lib/reline/config.rb +++ b/lib/reline/config.rb @@ -83,8 +83,17 @@ class Reline::Config @key_actors[@keymap_label] end + def inputrc_path + case ENV['INPUTRC'] + when nil, '' + DEFAULT_PATH + else + ENV['INPUTRC'] + end + end + def read(file = nil) - file ||= File.expand_path(ENV['INPUTRC'] || DEFAULT_PATH) + file ||= File.expand_path(inputrc_path) begin if file.respond_to?(:readlines) lines = file.readlines @@ -184,9 +193,8 @@ class Reline::Config def bind_variable(name, value) case name - when *VARIABLE_NAMES then - variable_name = :"@#{name.tr(?-, ?_)}" - instance_variable_set(variable_name, value.nil? || value == '1' || value == 'on') + when 'history-size' + @history_size = value.to_i when 'bell-style' @bell_style = case value @@ -225,6 +233,9 @@ class Reline::Config end when 'keyseq-timeout' @keyseq_timeout = value.to_i + when *VARIABLE_NAMES then + variable_name = :"@#{name.tr(?-, ?_)}" + instance_variable_set(variable_name, value.nil? || value == '1' || value == 'on') end end diff --git a/lib/reline/general_io.rb b/lib/reline/general_io.rb index 291c14c7b3..85f1f13eed 100644 --- a/lib/reline/general_io.rb +++ b/lib/reline/general_io.rb @@ -1,6 +1,14 @@ require 'timeout' class Reline::GeneralIO + def self.encoding + RUBY_PLATFORM =~ /mswin|mingw/ ? Encoding::UTF_8 : Encoding::default_external + end + + def self.win? + false + end + RAW_KEYSTROKE_CONFIG = {} @@buf = [] diff --git a/lib/reline/history.rb b/lib/reline/history.rb index 238fcf2a76..d95f1cebc3 100644 --- a/lib/reline/history.rb +++ b/lib/reline/history.rb @@ -19,7 +19,7 @@ class Reline::History < Array def []=(index, val) index = check_index(index) - super(index, String.new(val, encoding: Encoding::default_external)) + super(index, String.new(val, encoding: Reline.encoding_system_needs)) end def concat(*val) @@ -39,12 +39,12 @@ class Reline::History < Array val.shift(diff) end end - super(*(val.map{ |v| String.new(v, encoding: Encoding::default_external) })) + super(*(val.map{ |v| String.new(v, encoding: Reline.encoding_system_needs) })) end def <<(val) shift if size + 1 > @config.history_size - super(String.new(val, encoding: Encoding::default_external)) + super(String.new(val, encoding: Reline.encoding_system_needs)) end private def check_index(index) diff --git a/lib/reline/line_editor.rb b/lib/reline/line_editor.rb index 75af50a908..095a7b5a09 100644 --- a/lib/reline/line_editor.rb +++ b/lib/reline/line_editor.rb @@ -57,10 +57,10 @@ class Reline::LineEditor NON_PRINTING_END = "\2" WIDTH_SCANNER = /\G(?:#{NON_PRINTING_START}|#{NON_PRINTING_END}|#{CSI_REGEXP}|#{OSC_REGEXP}|\X)/ - def initialize(config) + def initialize(config, encoding) @config = config @completion_append_character = '' - reset_variables + reset_variables(encoding: encoding) end private def check_multiline_prompt(buffer, prompt) @@ -85,10 +85,10 @@ class Reline::LineEditor end end - def reset(prompt = '', encoding = Encoding.default_external) + def reset(prompt = '', encoding:) @rest_height = (Reline::IOGate.get_screen_size.first - 1) - Reline::IOGate.cursor_pos.y @screen_size = Reline::IOGate.get_screen_size - reset_variables(prompt, encoding) + reset_variables(prompt, encoding: encoding) @old_trap = Signal.trap('SIGINT') { @old_trap.call if @old_trap.respond_to?(:call) # can also be string, ex: "DEFAULT" raise Interrupt @@ -139,7 +139,7 @@ class Reline::LineEditor @eof end - def reset_variables(prompt = '', encoding = Encoding.default_external) + def reset_variables(prompt = '', encoding:) @prompt = prompt @mark_pointer = nil @encoding = encoding @@ -317,9 +317,9 @@ class Reline::LineEditor if @menu_info scroll_down(@highest_in_all - @first_line_started_from) @rerender_all = true - @menu_info.list.each do |item| + @menu_info.list.sort!.each do |item| Reline::IOGate.move_cursor_column(0) - @output.print item + @output.write item @output.flush scroll_down(1) end @@ -507,12 +507,20 @@ class Reline::LineEditor Reline::IOGate.move_cursor_column(0) visual_lines.each_with_index do |line, index| if line.nil? - Reline::IOGate.erase_after_cursor - move_cursor_down(1) - Reline::IOGate.move_cursor_column(0) + if Reline::IOGate.win? and calculate_width(visual_lines[index - 1], true) == Reline::IOGate.get_screen_size.last + # A newline is automatically inserted if a character is rendered at eol on command prompt. + else + Reline::IOGate.erase_after_cursor + move_cursor_down(1) + Reline::IOGate.move_cursor_column(0) + end next end - @output.print line + @output.write line + if Reline::IOGate.win? and calculate_width(line, true) == Reline::IOGate.get_screen_size.last + # A newline is automatically inserted if a character is rendered at eol on command prompt. + @rest_height -= 1 if @rest_height > 0 + end @output.flush if @first_prompt @first_prompt = false @@ -535,7 +543,7 @@ class Reline::LineEditor return before if before.nil? || before.empty? if after = @output_modifier_proc&.call("#{before.join("\n")}\n", complete: finished?) - after.lines(chomp: true) + after.lines("\n", chomp: true) else before end @@ -905,7 +913,6 @@ class Reline::LineEditor quote = nil i += 1 rest = nil - break_pointer = nil elsif quote and slice.start_with?(escaped_quote) # skip i += 2 @@ -915,7 +922,7 @@ class Reline::LineEditor closing_quote = /(?!\\)#{Regexp.escape(quote)}/ escaped_quote = /\\#{Regexp.escape(quote)}/ i += 1 - break_pointer = i + break_pointer = i - 1 elsif not quote and slice =~ word_break_regexp rest = $' i += 1 @@ -937,6 +944,11 @@ class Reline::LineEditor end else preposing = '' + if break_pointer + preposing = @line.byteslice(0, break_pointer) + else + preposing = '' + end target = before end [preposing.encode(@encoding), target.encode(@encoding), postposing.encode(@encoding)] @@ -1091,6 +1103,11 @@ class Reline::LineEditor private def ed_insert(key) if key.instance_of?(String) + begin + key.encode(Encoding::UTF_8) + rescue Encoding::UndefinedConversionError + return + end width = Reline::Unicode.get_mbchar_width(key) if @cursor == @cursor_max @line += key @@ -1101,6 +1118,11 @@ class Reline::LineEditor @cursor += width @cursor_max += width else + begin + key.chr.encode(Encoding::UTF_8) + rescue Encoding::UndefinedConversionError + return + end if @cursor == @cursor_max @line += key.chr else @@ -1876,6 +1898,16 @@ class Reline::LineEditor end end + private def vi_insert_at_bol(key) + ed_move_to_beg(key) + @config.editing_mode = :vi_insert + end + + private def vi_add_at_eol(key) + ed_move_to_end(key) + @config.editing_mode = :vi_insert + end + private def ed_delete_prev_char(key, arg: 1) deleted = '' arg.times do @@ -1898,6 +1930,18 @@ class Reline::LineEditor end private def vi_change_meta(key) + @waiting_operator_proc = proc { |cursor_diff, byte_pointer_diff| + if byte_pointer_diff > 0 + @line, cut = byteslice!(@line, @byte_pointer, byte_pointer_diff) + elsif byte_pointer_diff < 0 + @line, cut = byteslice!(@line, @byte_pointer + byte_pointer_diff, -byte_pointer_diff) + end + copy_for_vi(cut) + @cursor += cursor_diff if cursor_diff < 0 + @cursor_max -= cursor_diff.abs + @byte_pointer += byte_pointer_diff if byte_pointer_diff < 0 + @config.editing_mode = :vi_insert + } end private def vi_delete_meta(key) @@ -2063,12 +2107,17 @@ class Reline::LineEditor @waiting_proc = ->(key_for_proc) { search_next_char(key_for_proc, arg) } end - private def search_next_char(key, arg) + private def vi_to_next_char(key, arg: 1) + @waiting_proc = ->(key_for_proc) { search_next_char(key_for_proc, arg, true) } + end + + private def search_next_char(key, arg, need_prev_char = false) if key.instance_of?(String) inputed_char = key else inputed_char = key.chr end + prev_total = nil total = nil found = false @line.byteslice(@byte_pointer..-1).grapheme_clusters.each do |mbchar| @@ -2086,13 +2135,66 @@ class Reline::LineEditor end end width = Reline::Unicode.get_mbchar_width(mbchar) + prev_total = total total = [total.first + mbchar.bytesize, total.last + width] end end - if found and total + if not need_prev_char and found and total byte_size, width = total @byte_pointer += byte_size @cursor += width + elsif need_prev_char and found and prev_total + byte_size, width = prev_total + @byte_pointer += byte_size + @cursor += width + end + @waiting_proc = nil + end + + private def vi_prev_char(key, arg: 1) + @waiting_proc = ->(key_for_proc) { search_prev_char(key_for_proc, arg) } + end + + private def vi_to_prev_char(key, arg: 1) + @waiting_proc = ->(key_for_proc) { search_prev_char(key_for_proc, arg, true) } + end + + private def search_prev_char(key, arg, need_next_char = false) + if key.instance_of?(String) + inputed_char = key + else + inputed_char = key.chr + end + prev_total = nil + total = nil + found = false + @line.byteslice(0..@byte_pointer).grapheme_clusters.reverse_each do |mbchar| + # total has [byte_size, cursor] + unless total + # skip cursor point + width = Reline::Unicode.get_mbchar_width(mbchar) + total = [mbchar.bytesize, width] + else + if inputed_char == mbchar + arg -= 1 + if arg.zero? + found = true + break + end + end + width = Reline::Unicode.get_mbchar_width(mbchar) + prev_total = total + total = [total.first + mbchar.bytesize, total.last + width] + end + end + if not need_next_char and found and total + byte_size, width = total + @byte_pointer -= byte_size + @cursor -= width + elsif need_next_char and found and prev_total + byte_size, width = prev_total + @byte_pointer -= byte_size + @cursor -= width end @waiting_proc = nil end diff --git a/lib/reline/version.rb b/lib/reline/version.rb index 47b29bd946..1bf544d74b 100644 --- a/lib/reline/version.rb +++ b/lib/reline/version.rb @@ -1,3 +1,3 @@ module Reline - VERSION = '0.1.2' + VERSION = '0.1.3' end diff --git a/lib/reline/windows.rb b/lib/reline/windows.rb index aef3073a7e..c229c8536f 100644 --- a/lib/reline/windows.rb +++ b/lib/reline/windows.rb @@ -1,6 +1,14 @@ require 'fiddle/import' class Reline::Windows + def self.encoding + Encoding::UTF_8 + end + + def self.win? + true + end + RAW_KEYSTROKE_CONFIG = { [224, 72] => :ed_prev_history, # ↑ [224, 80] => :ed_next_history, # ↓ @@ -68,6 +76,8 @@ class Reline::Windows STD_INPUT_HANDLE = -10 STD_OUTPUT_HANDLE = -11 WINDOW_BUFFER_SIZE_EVENT = 0x04 + FILE_TYPE_PIPE = 0x0003 + FILE_NAME_INFO = 2 @@getwch = Win32API.new('msvcrt', '_getwch', [], 'I') @@kbhit = Win32API.new('msvcrt', '_kbhit', [], 'I') @@GetKeyState = Win32API.new('user32', 'GetKeyState', ['L'], 'L') @@ -80,9 +90,36 @@ class Reline::Windows @@hConsoleInputHandle = @@GetStdHandle.call(STD_INPUT_HANDLE) @@GetNumberOfConsoleInputEvents = Win32API.new('kernel32', 'GetNumberOfConsoleInputEvents', ['L', 'P'], 'L') @@ReadConsoleInput = Win32API.new('kernel32', 'ReadConsoleInput', ['L', 'P', 'L', 'P'], 'L') + @@GetFileType = Win32API.new('kernel32', 'GetFileType', ['L'], 'L') + @@GetFileInformationByHandleEx = Win32API.new('kernel32', 'GetFileInformationByHandleEx', ['L', 'I', 'P', 'L'], 'I') + @@input_buf = [] @@output_buf = [] + def self.msys_tty?(io=@@hConsoleInputHandle) + # check if fd is a pipe + if @@GetFileType.call(io) != FILE_TYPE_PIPE + return false + end + + bufsize = 1024 + p_buffer = "\0" * bufsize + res = @@GetFileInformationByHandleEx.call(io, FILE_NAME_INFO, p_buffer, bufsize - 2) + return false if res == 0 + + # get pipe name: p_buffer layout is: + # struct _FILE_NAME_INFO { + # DWORD FileNameLength; + # WCHAR FileName[1]; + # } FILE_NAME_INFO + len = p_buffer[0, 4].unpack("L")[0] + name = p_buffer[4, len].encode(Encoding::UTF_8, Encoding::UTF_16LE, invalid: :replace) + + # Check if this could be a MSYS2 pty pipe ('\msys-XXXX-ptyN-XX') + # or a cygwin pty pipe ('\cygwin-XXXX-ptyN-XX') + name =~ /(msys-|cygwin-).*-pty/ ? true : false + end + def self.getwch unless @@input_buf.empty? return @@input_buf.shift @@ -99,7 +136,7 @@ class Reline::Windows return @@input_buf.shift end begin - bytes = ret.chr(Encoding::UTF_8).encode(Encoding.default_external).bytes + bytes = ret.chr(Encoding::UTF_8).bytes @@input_buf.push(*bytes) rescue Encoding::UndefinedConversionError @@input_buf << ret @@ -205,7 +242,7 @@ class Reline::Windows def self.scroll_down(val) return if val.zero? - scroll_rectangle = [0, val, get_screen_size.first, get_screen_size.last].pack('s4') + scroll_rectangle = [0, val, get_screen_size.last, get_screen_size.first].pack('s4') destination_origin = 0 # y * 65536 + x fill = [' '.ord, 0].pack('SS') @@ScrollConsoleScreenBuffer.call(@@hConsoleHandle, scroll_rectangle, nil, destination_origin, fill) @@ -213,8 +250,8 @@ class Reline::Windows def self.clear_screen # TODO: Use FillConsoleOutputCharacter and FillConsoleOutputAttribute - print "\e[2J" - print "\e[1;1H" + write "\e[2J" + write "\e[1;1H" end def self.set_screen_size(rows, columns) diff --git a/lib/singleton/singleton.gemspec b/lib/singleton/singleton.gemspec index f7ee6bb2dc..c6a273a839 100644 --- a/lib/singleton/singleton.gemspec +++ b/lib/singleton/singleton.gemspec @@ -19,7 +19,7 @@ Gem::Specification.new do |spec| spec.metadata["source_code_uri"] = spec.homepage spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do - `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } + `git ls-files -z 2>/dev/null`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } end spec.bindir = "exe" spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } diff --git a/lib/timeout/timeout.gemspec b/lib/timeout/timeout.gemspec index 88babe9f8b..7b650bdc31 100644 --- a/lib/timeout/timeout.gemspec +++ b/lib/timeout/timeout.gemspec @@ -19,7 +19,7 @@ Gem::Specification.new do |spec| spec.metadata["source_code_uri"] = spec.homepage spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do - `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } + `git ls-files -z 2>/dev/null`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } end spec.bindir = "exe" spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } diff --git a/lib/uri/uri.gemspec b/lib/uri/uri.gemspec index 5c51721755..95cb8e2d42 100644 --- a/lib/uri/uri.gemspec +++ b/lib/uri/uri.gemspec @@ -21,7 +21,7 @@ Gem::Specification.new do |spec| # Specify which files should be added to the gem when it is released. # The `git ls-files -z` loads the files in the RubyGem that have been added into git. spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do - `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } + `git ls-files -z 2>/dev/null`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } end spec.bindir = "exe" spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } diff --git a/lib/yaml/yaml.gemspec b/lib/yaml/yaml.gemspec index d78e8164a4..ba5027a9b6 100644 --- a/lib/yaml/yaml.gemspec +++ b/lib/yaml/yaml.gemspec @@ -15,7 +15,7 @@ Gem::Specification.new do |spec| # Specify which files should be added to the gem when it is released. # The `git ls-files -z` loads the files in the RubyGem that have been added into git. spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do - `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } + `git ls-files -z 2>/dev/null`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } end spec.bindir = "exe" spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } diff --git a/test/io/console/test_io_console.rb b/test/io/console/test_io_console.rb index d71e42603f..a02605dd1e 100644 --- a/test/io/console/test_io_console.rb +++ b/test/io/console/test_io_console.rb @@ -364,6 +364,11 @@ defined?(PTY) and defined?(IO.console) and TestIO_Console.class_eval do assert_ctrl("#{cc.ord}", cc, r, w) assert_ctrl("#{cc.ord}", cc, r, w) end + if cc = ctrl["stop"] + assert_ctrl("#{cc.ord}", cc, r, w) + assert_ctrl("#{cc.ord}", cc, r, w) + assert_ctrl("#{cc.ord}", cc, r, w) + end end end @@ -457,7 +462,7 @@ defined?(IO.console) and TestIO_Console.class_eval do noctty = [EnvUtil.rubybin, "-e", "Process.daemon(true)"] when !(rubyw = RbConfig::CONFIG["RUBYW_INSTALL_NAME"]).empty? dir, base = File.split(EnvUtil.rubybin) - noctty = [File.join(dir, base.sub(/ruby/, rubyw))] + noctty = [File.join(dir, base.sub(RUBY_ENGINE, rubyw))] end if noctty diff --git a/test/irb/test_color.rb b/test/irb/test_color.rb index 3ced640004..cb90d29c9d 100644 --- a/test/irb/test_color.rb +++ b/test/irb/test_color.rb @@ -29,7 +29,7 @@ module TestIRB "def self.foo; bar; end" => "#{GREEN}def#{CLEAR} #{CYAN}#{BOLD}self#{CLEAR}.#{BLUE}#{BOLD}foo#{CLEAR}; bar; #{GREEN}end#{CLEAR}", 'erb = ERB.new("a#{nil}b", trim_mode: "-")' => "erb = #{BLUE}#{BOLD}#{UNDERLINE}ERB#{CLEAR}.new(#{RED}#{BOLD}\"#{CLEAR}#{RED}a#{CLEAR}#{RED}\#{#{CLEAR}#{CYAN}#{BOLD}nil#{CLEAR}#{RED}}#{CLEAR}#{RED}b#{CLEAR}#{RED}#{BOLD}\"#{CLEAR}, #{MAGENTA}trim_mode:#{CLEAR} #{RED}#{BOLD}\"#{CLEAR}#{RED}-#{CLEAR}#{RED}#{BOLD}\"#{CLEAR})", "# comment" => "#{BLUE}#{BOLD}# comment#{CLEAR}", - "yield(hello)" => "#{GREEN}yield#{CLEAR}(hello)", + "def f;yield(hello);end" => "#{GREEN}def#{CLEAR} #{BLUE}#{BOLD}f#{CLEAR};#{GREEN}yield#{CLEAR}(hello);#{GREEN}end#{CLEAR}", '"##@var]"' => "#{RED}#{BOLD}\"#{CLEAR}#{RED}\##{CLEAR}#{RED}\##{CLEAR}@var#{RED}]#{CLEAR}#{RED}#{BOLD}\"#{CLEAR}", '"foo#{a} #{b}"' => "#{RED}#{BOLD}\"#{CLEAR}#{RED}foo#{CLEAR}#{RED}\#{#{CLEAR}a#{RED}}#{CLEAR}#{RED} #{CLEAR}#{RED}\#{#{CLEAR}b#{RED}}#{CLEAR}#{RED}#{BOLD}\"#{CLEAR}", '/r#{e}g/' => "#{RED}#{BOLD}/#{CLEAR}#{RED}r#{CLEAR}#{RED}\#{#{CLEAR}e#{RED}}#{CLEAR}#{RED}g#{CLEAR}#{RED}#{BOLD}/#{CLEAR}", diff --git a/test/irb/test_completion.rb b/test/irb/test_completion.rb index 52bbc7b2cc..a765bbf3a5 100644 --- a/test/irb/test_completion.rb +++ b/test/irb/test_completion.rb @@ -25,5 +25,27 @@ module TestIRB assert_include(IRB::InputCompletor.retrieve_completion_data("1r.positi", bind: binding), "1r.positive?") assert_empty(IRB::InputCompletor.retrieve_completion_data("1i.positi", bind: binding)) end + + def test_complete_symbol + _ = :aiueo + assert_include(IRB::InputCompletor.retrieve_completion_data(":a", bind: binding), ":aiueo") + assert_empty(IRB::InputCompletor.retrieve_completion_data(":irb_unknown_symbol_abcdefg", bind: binding)) + end + + def test_complete_symbol_failure + assert_nil(IRB::InputCompletor::PerfectMatchedProc.(":aiueo", bind: binding)) + end + + def test_complete_reserved_words + candidates = IRB::InputCompletor.retrieve_completion_data("de", bind: binding) + %w[def defined?].each do |word| + assert_include candidates, word + end + + candidates = IRB::InputCompletor.retrieve_completion_data("__", bind: binding) + %w[__ENCODING__ __LINE__ __FILE__].each do |word| + assert_include candidates, word + end + end end end diff --git a/test/irb/test_context.rb b/test/irb/test_context.rb index 693ebbeaea..d03cc30c78 100644 --- a/test/irb/test_context.rb +++ b/test/irb/test_context.rb @@ -63,6 +63,13 @@ module TestIRB assert_not_match(/rescue _\.class/, e.message) end + def test_evaluate_with_encoding_error_without_lineno + assert_raise_with_message(EncodingError, /invalid symbol/) { + @context.evaluate(%q[{"\xAE": 1}], 1) + # The backtrace of this invalid encoding hash doesn't contain lineno. + } + end + def test_evaluate_with_onigmo_warning assert_warning("(irb):1: warning: character class has duplicated range: /[aa]/\n") do @context.evaluate('/[aa]/', 1) @@ -216,5 +223,36 @@ module TestIRB 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") end + + def test_multiline_output_on_default_inspector + main = Object.new + def main.inspect + "abc\ndef" + end + input = TestInputMethod.new([ + "self" + ]) + irb = IRB::Irb.new(IRB::WorkSpace.new(main), input) + irb.context.return_format = "=> %s\n" + + # The default + irb.context.newline_before_multiline_output = true + out, err = capture_io do + irb.eval_input + end + assert_empty err + assert_equal("=> \nabc\ndef\n", + out) + + # No newline before multiline output + input.reset + irb.context.newline_before_multiline_output = false + out, err = capture_io do + irb.eval_input + end + assert_empty err + assert_equal("=> abc\ndef\n", + out) + end end end diff --git a/test/irb/test_ruby_lex.rb b/test/irb/test_ruby_lex.rb new file mode 100644 index 0000000000..dd5a1f7ca5 --- /dev/null +++ b/test/irb/test_ruby_lex.rb @@ -0,0 +1,130 @@ +$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__) +require 'irb/ruby-lex' +require 'test/unit' +require 'ostruct' + +module TestIRB + class TestRubyLex < Test::Unit::TestCase + Row = Struct.new(:content, :current_line_spaces, :new_line_spaces) + + class MockIO + def initialize(params, &assertion) + @params = params + @assertion = assertion + end + + def auto_indent(&block) + result = block.call(*@params) + @assertion.call(result) + end + end + + def assert_indenting(lines, correct_space_count, add_new_line) + lines = lines + [""] if add_new_line + last_line_index = lines.length - 1 + byte_pointer = lines.last.length + + ruby_lex = RubyLex.new() + io = MockIO.new([lines, last_line_index, byte_pointer, add_new_line]) do |auto_indent| + error_message = "Calculated the wrong number of spaces for:\n #{lines.join("\n")}" + assert_equal(correct_space_count, auto_indent, error_message) + end + ruby_lex.set_input(io) + context = OpenStruct.new(auto_indent_mode: true) + ruby_lex.set_auto_indent(context) + end + + def test_auto_indent + input_with_correct_indents = [ + Row.new(%q(def each_top_level_statement), nil, 2), + Row.new(%q( initialize_input), nil, 2), + Row.new(%q( catch(:TERM_INPUT) do), nil, 4), + Row.new(%q( loop do), nil, 6), + Row.new(%q( begin), nil, 8), + Row.new(%q( prompt), nil, 8), + Row.new(%q( unless l = lex), nil, 10), + Row.new(%q( throw :TERM_INPUT if @line == ''), nil, 10), + Row.new(%q( else), 8, 10), + Row.new(%q( @line_no += l.count("\n")), nil, 10), + Row.new(%q( next if l == "\n"), nil, 10), + Row.new(%q( @line.concat l), nil, 10), + Row.new(%q( if @code_block_open or @ltype or @continue or @indent > 0), nil, 12), + Row.new(%q( next), nil, 12), + Row.new(%q( end), 10, 10), + Row.new(%q( end), 8, 8), + Row.new(%q( if @line != "\n"), nil, 10), + Row.new(%q( @line.force_encoding(@io.encoding)), nil, 10), + Row.new(%q( yield @line, @exp_line_no), nil, 10), + Row.new(%q( end), 8, 8), + Row.new(%q( break if @io.eof?), nil, 8), + Row.new(%q( @line = ''), nil, 8), + Row.new(%q( @exp_line_no = @line_no), nil, 8), + Row.new(%q( ), nil, 8), + Row.new(%q( @indent = 0), nil, 8), + Row.new(%q( rescue TerminateLineInput), 6, 8), + Row.new(%q( initialize_input), nil, 8), + Row.new(%q( prompt), nil, 8), + Row.new(%q( end), 6, 6), + Row.new(%q( end), 4, 4), + Row.new(%q( end), 2, 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_braces_on_their_own_line + input_with_correct_indents = [ + Row.new(%q(if true), nil, 2), + Row.new(%q( [), nil, 4), + Row.new(%q( ]), 2, 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_multiple_braces_in_a_line + input_with_correct_indents = [ + Row.new(%q([[[), nil, 6), + Row.new(%q( ]), 4, 4), + Row.new(%q( ]), 2, 2), + Row.new(%q(]), 0, 0), + Row.new(%q([<<FOO]), nil, 0), + Row.new(%q(hello), nil, 0), + Row.new(%q(FOO), 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_a_closed_brace_and_not_closed_brace_in_a_line + input_with_correct_indents = [ + Row.new(%q(p() {), nil, 2), + Row.new(%q(}), 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 + end +end diff --git a/test/readline/helper.rb b/test/readline/helper.rb index ee157722f1..1b80327b57 100644 --- a/test/readline/helper.rb +++ b/test/readline/helper.rb @@ -3,18 +3,22 @@ begin ReadlineSo = Readline rescue LoadError end -require "reline" def use_ext_readline # Use ext/readline as Readline Object.send(:remove_const, :Readline) if Object.const_defined?(:Readline) Object.const_set(:Readline, ReadlineSo) end -def use_lib_reline # Use lib/reline as Readline - Reline.send(:remove_const, 'IOGate') if Reline.const_defined?('IOGate') - Reline.const_set('IOGate', Reline::GeneralIO) - Reline.send(:core).config.instance_variable_set(:@test_mode, true) - Reline.send(:core).config.reset - Object.send(:remove_const, :Readline) if Object.const_defined?(:Readline) - Object.const_set(:Readline, Reline) +begin + require "reline" +rescue LoadError +else + def use_lib_reline # Use lib/reline as Readline + Reline.send(:remove_const, 'IOGate') if Reline.const_defined?('IOGate') + Reline.const_set('IOGate', Reline::GeneralIO) + Reline.send(:core).config.instance_variable_set(:@test_mode, true) + Reline.send(:core).config.reset + Object.send(:remove_const, :Readline) if Object.const_defined?(:Readline) + Object.const_set(:Readline, Reline) + end end diff --git a/test/readline/test_readline.rb b/test/readline/test_readline.rb index be9ac24d25..7ac2212719 100644 --- a/test/readline/test_readline.rb +++ b/test/readline/test_readline.rb @@ -21,13 +21,16 @@ module BasetestReadline Readline.point = 0 rescue NotImplementedError end + Readline.special_prefixes = "" + Readline.completion_append_character = nil Readline.input = nil Readline.output = nil SAVED_ENV.each_with_index {|k, i| ENV[k] = @saved_env[i] } end def test_readline - skip "Skip Editline" if /EditLine/n.match(Readline::VERSION) + Readline::HISTORY.clear + omit "Skip Editline" if /EditLine/n.match(Readline::VERSION) with_temp_stdio do |stdin, stdout| stdin.write("hello\n") stdin.close @@ -45,7 +48,7 @@ module BasetestReadline # Work around lack of SecurityError in Reline # test mode with tainted prompt. # Also skip test on Ruby 2.7+, where $SAFE/taint is deprecated. - if RUBY_VERSION < '2.7' && !kind_of?(TestRelineAsReadline) + if RUBY_VERSION < '2.7' && defined?(TestRelineAsReadline) && !kind_of?(TestRelineAsReadline) begin Thread.start { $SAFE = 1 @@ -65,8 +68,8 @@ module BasetestReadline # line_buffer # point def test_line_buffer__point - skip "Skip Editline" if /EditLine/n.match(Readline::VERSION) - skip "GNU Readline has special behaviors" if defined?(Reline) and Readline == Reline + omit "Skip Editline" if /EditLine/n.match(Readline::VERSION) + omit "GNU Readline has special behaviors" if defined?(Reline) and Readline == Reline begin Readline.line_buffer Readline.point @@ -154,7 +157,7 @@ module BasetestReadline end def test_completion_proc_empty_result - skip "Skip Editline" if /EditLine/n.match(Readline::VERSION) + omit "Skip Editline" if /EditLine/n.match(Readline::VERSION) with_temp_stdio do |stdin, stdout| stdin.write("first\t") stdin.flush @@ -233,12 +236,12 @@ module BasetestReadline end def test_completion_encoding - skip "Skip Editline" if /EditLine/n.match(Readline::VERSION) + omit "Skip Editline" if /EditLine/n.match(Readline::VERSION) bug5941 = '[Bug #5941]' append_character = Readline.completion_append_character Readline.completion_append_character = "" completion_case_fold = Readline.completion_case_fold - locale = Encoding.find("locale") + locale = get_default_internal_encoding if locale == Encoding::UTF_8 enc1 = Encoding::EUC_JP else @@ -261,7 +264,7 @@ module BasetestReadline end or begin return if assert_under_utf8 - skip("missing test for locale #{locale.name}") + omit("missing test for locale #{locale.name}") end expected = results[0][0...1] Readline.completion_case_fold = false @@ -285,32 +288,6 @@ module BasetestReadline # filename_quote_characters # special_prefixes def test_some_characters_methods - method_names = [ - "basic_word_break_characters", - "completer_word_break_characters", - "basic_quote_characters", - "completer_quote_characters", - "filename_quote_characters", - "special_prefixes", - ] - method_names.each do |method_name| - begin - begin - enc = get_default_internal_encoding - saved = Readline.send(method_name.to_sym) - expecteds = [" ", " .,|\t", ""] - expecteds.each do |e| - Readline.send((method_name + "=").to_sym, e) - res = Readline.send(method_name.to_sym) - assert_equal(e, res) - assert_equal(enc, res.encoding, "Readline.#{method_name} should be #{enc.name}") - end - ensure - Readline.send((method_name + "=").to_sym, saved) if saved - end - rescue NotImplementedError - end - end end def test_closed_outstream @@ -335,7 +312,7 @@ module BasetestReadline end def test_point - skip "Skip Editline" if /EditLine/n.match(Readline::VERSION) + omit "Skip Editline" if /EditLine/n.match(Readline::VERSION) assert_equal(0, Readline.point) Readline.insert_text('12345') assert_equal(5, Readline.point) @@ -350,7 +327,7 @@ module BasetestReadline end def test_insert_text - skip "Skip Editline" if /EditLine/n.match(Readline::VERSION) + omit "Skip Editline" if /EditLine/n.match(Readline::VERSION) str = "test_insert_text" assert_equal(0, Readline.point) assert_equal(Readline, Readline.insert_text(str)) @@ -381,7 +358,7 @@ module BasetestReadline end def test_delete_text - skip "Skip Editline" if /EditLine/n.match(Readline::VERSION) + omit "Skip Editline" if /EditLine/n.match(Readline::VERSION) str = "test_insert_text" assert_equal(0, Readline.point) assert_equal(Readline, Readline.insert_text(str)) @@ -401,7 +378,7 @@ module BasetestReadline end def test_modify_text_in_pre_input_hook - skip "Skip Editline" if /EditLine/n.match(Readline::VERSION) + omit "Skip Editline" if /EditLine/n.match(Readline::VERSION) with_temp_stdio {|stdin, stdout| begin stdin.write("world\n") @@ -432,9 +409,10 @@ module BasetestReadline end def test_input_metachar - skip "Skip Editline" if /EditLine/n.match(Readline::VERSION) - skip("Won't pass on mingw w/readline 7.0.005 [ruby-core:45682]") if mingw? - skip 'Needs GNU Readline 6 or later' if windows? and defined?(TestReadline) and kind_of?(TestReadline) and Readline::VERSION < '6.0' + omit "Skip Editline" if /EditLine/n.match(Readline::VERSION) + # test will pass on Windows reline, but not readline + omit "Won't pass on mingw readline.so using 8.0.001" if /mingw/ =~ RUBY_PLATFORM and defined?(TestReadline) and kind_of?(TestReadline) + omit 'Needs GNU Readline 6 or later' if /mswin|mingw/ =~ RUBY_PLATFORM and defined?(TestReadline) and kind_of?(TestReadline) and Readline::VERSION < '6.0' bug6601 = '[ruby-core:45682]' Readline::HISTORY << "hello" wo = nil @@ -451,10 +429,10 @@ module BasetestReadline end def test_input_metachar_multibyte - skip "Skip Editline" if /EditLine/n.match(Readline::VERSION) + omit "Skip Editline" if /EditLine/n.match(Readline::VERSION) unless Encoding.find("locale") == Encoding::UTF_8 return if assert_under_utf8 - skip 'this test needs UTF-8 locale' + omit 'this test needs UTF-8 locale' end bug6602 = '[ruby-core:45683]' Readline::HISTORY << "\u3042\u3093" @@ -481,7 +459,8 @@ module BasetestReadline end def test_refresh_line - skip "Only when refresh_line exists" unless Readline.respond_to?(:refresh_line) + omit "Only when refresh_line exists" unless Readline.respond_to?(:refresh_line) + omit unless respond_to?(:assert_ruby_status) bug6232 = '[ruby-core:43957] [Bug #6232] refresh_line after set_screen_size' with_temp_stdio do |stdin, stdout| replace_stdio(stdin.path, stdout.path) do @@ -508,83 +487,98 @@ module BasetestReadline def test_using_quoting_detection_proc saved_completer_quote_characters = Readline.completer_quote_characters saved_completer_word_break_characters = Readline.completer_word_break_characters + + # skip if previous value is nil because Readline... = nil is not allowed. + skip unless saved_completer_quote_characters + skip unless saved_completer_word_break_characters + return unless Readline.respond_to?(:quoting_detection_proc=) - passed_text = nil - line = nil + begin + passed_text = nil + line = nil - with_temp_stdio do |stdin, stdout| - replace_stdio(stdin.path, stdout.path) do - Readline.completion_proc = ->(text) do - passed_text = text - ['completion'].map { |i| - i.encode(Encoding.default_external) - } - end - Readline.completer_quote_characters = '\'"' - Readline.completer_word_break_characters = ' ' - Readline.quoting_detection_proc = ->(text, index) do - index > 0 && text[index-1] == '\\' - end + with_temp_stdio do |stdin, stdout| + replace_stdio(stdin.path, stdout.path) do + Readline.completion_proc = ->(text) do + passed_text = text + ['completion'].map { |i| + i.encode(Encoding.default_external) + } + end + Readline.completer_quote_characters = '\'"' + Readline.completer_word_break_characters = ' ' + Readline.quoting_detection_proc = ->(text, index) do + index > 0 && text[index-1] == '\\' + end - stdin.write("first second\\ third\t") - stdin.flush - line = Readline.readline('> ', false) + stdin.write("first second\\ third\t") + stdin.flush + line = Readline.readline('> ', false) + end end - end - assert_equal('second\\ third', passed_text) - assert_equal('first completion', line.chomp(' ')) - ensure - Readline.completer_quote_characters = saved_completer_quote_characters - Readline.completer_word_break_characters = saved_completer_word_break_characters + assert_equal('second\\ third', passed_text) + assert_equal('first completion', line.chomp(' ')) + ensure + Readline.completer_quote_characters = saved_completer_quote_characters + Readline.completer_word_break_characters = saved_completer_word_break_characters + end end def test_using_quoting_detection_proc_with_multibyte_input + Readline.completion_append_character = nil saved_completer_quote_characters = Readline.completer_quote_characters saved_completer_word_break_characters = Readline.completer_word_break_characters + + # skip if previous value is nil because Readline... = nil is not allowed. + skip unless saved_completer_quote_characters + skip unless saved_completer_word_break_characters + return unless Readline.respond_to?(:quoting_detection_proc=) - unless Encoding.find("locale") == Encoding::UTF_8 + unless get_default_internal_encoding == Encoding::UTF_8 return if assert_under_utf8 - skip 'this test needs UTF-8 locale' + omit 'this test needs UTF-8 locale' end - passed_text = nil - escaped_char_indexes = [] - line = nil + begin + passed_text = nil + escaped_char_indexes = [] + line = nil - with_temp_stdio do |stdin, stdout| - replace_stdio(stdin.path, stdout.path) do - Readline.completion_proc = ->(text) do - passed_text = text - ['completion'].map { |i| - i.encode(Encoding.default_external) - } - end - Readline.completer_quote_characters = '\'"' - Readline.completer_word_break_characters = ' ' - Readline.quoting_detection_proc = ->(text, index) do - escaped = index > 0 && text[index-1] == '\\' - escaped_char_indexes << index if escaped - escaped - end + with_temp_stdio do |stdin, stdout| + replace_stdio(stdin.path, stdout.path) do + Readline.completion_proc = ->(text) do + passed_text = text + ['completion'].map { |i| + i.encode(Encoding.default_external) + } + end + Readline.completer_quote_characters = '\'"' + Readline.completer_word_break_characters = ' ' + Readline.quoting_detection_proc = ->(text, index) do + escaped = index > 0 && text[index-1] == '\\' + escaped_char_indexes << index if escaped + escaped + end - stdin.write("\u3042\u3093 second\\ third\t") - stdin.flush - line = Readline.readline('> ', false) + stdin.write("\u3042\u3093 second\\ third\t") + stdin.flush + line = Readline.readline('> ', false) + end end - end - assert_equal([10], escaped_char_indexes) - assert_equal('second\\ third', passed_text) - assert_equal("\u3042\u3093 completion", line) - ensure - Readline.completer_quote_characters = saved_completer_quote_characters - Readline.completer_word_break_characters = saved_completer_word_break_characters + assert_equal([10], escaped_char_indexes) + assert_equal('second\\ third', passed_text) + assert_equal("\u3042\u3093 completion#{Readline.completion_append_character}", line) + ensure + Readline.completer_quote_characters = saved_completer_quote_characters + Readline.completer_word_break_characters = saved_completer_word_break_characters + end end def test_simple_completion - skip "Skip Editline" if /EditLine/n.match(Readline::VERSION) + omit "Skip Editline" if /EditLine/n.match(Readline::VERSION) line = nil @@ -594,7 +588,7 @@ module BasetestReadline Readline.output = null Readline.completion_proc = ->(text) do ['abcde', 'abc12'].map { |i| - i.encode(Encoding.default_external) + i.encode(get_default_internal_encoding) } end w.write("a\t\n") @@ -607,8 +601,8 @@ module BasetestReadline end def test_completion_with_completion_append_character - skip "Skip Editline" if /EditLine/n.match(Readline::VERSION) - skip "Readline.completion_append_character is not implemented" unless Readline.respond_to?(:completion_append_character=) + omit "Skip Editline" if /EditLine/n.match(Readline::VERSION) + omit "Readline.completion_append_character is not implemented" unless Readline.respond_to?(:completion_append_character=) line = nil append_character = Readline.completion_append_character @@ -619,7 +613,7 @@ module BasetestReadline Readline.completion_append_character = '!' Readline.completion_proc = ->(text) do ['abcde'].map { |i| - i.encode(Encoding.default_external) + i.encode(get_default_internal_encoding) } end w.write("a\t\n") @@ -681,9 +675,9 @@ module BasetestReadline return unless Readline.respond_to?(:completion_quote_character) if /solaris/i =~ RUBY_PLATFORM # http://rubyci.s3.amazonaws.com/solaris11s-sunc/ruby-trunk/log/20181228T102505Z.fail.html.gz - skip 'This test does not succeed on Oracle Developer Studio for now' + omit 'This test does not succeed on Oracle Developer Studio for now' end - skip 'Needs GNU Readline 6 or later' if windows? and defined?(TestReadline) and kind_of?(TestReadline) and Readline::VERSION < '6.0' + omit 'Needs GNU Readline 6 or later' if /mswin|mingw/ =~ RUBY_PLATFORM and defined?(TestReadline) and kind_of?(TestReadline) and Readline::VERSION < '6.0' Readline.completion_proc = -> (_) { [] } Readline.completer_quote_characters = "'\"" @@ -730,7 +724,7 @@ module BasetestReadline Tempfile.create("test_readline_stdin") {|stdin| Tempfile.create("test_readline_stdout") {|stdout| yield stdin, stdout - if windows? + if /mswin|mingw/ =~ RUBY_PLATFORM # needed since readline holds refs to tempfiles, can't delete on Windows Readline.input = STDIN Readline.output = STDOUT @@ -766,7 +760,7 @@ module BasetestReadline return false if ENV['LC_ALL'] == 'UTF-8' loc = caller_locations(1, 1)[0].base_label.to_s assert_separately([{"LC_ALL"=>"UTF-8"}, "-r", __FILE__], <<SRC) -#skip "test \#{ENV['LC_ALL']}" +#omit "test \#{ENV['LC_ALL']}" #{self.class.name}.new(#{loc.dump}).run(Test::Unit::Runner.new) SRC return true @@ -780,7 +774,7 @@ class TestReadline < Test::Unit::TestCase use_ext_readline super end -end if defined?(ReadlineSo) +end if defined?(ReadlineSo) && ENV["TEST_READLINE_OR_RELINE"] != "Reline" class TestRelineAsReadline < Test::Unit::TestCase include BasetestReadline @@ -789,4 +783,12 @@ class TestRelineAsReadline < Test::Unit::TestCase use_lib_reline super end -end + + def get_default_internal_encoding + if RUBY_PLATFORM =~ /mswin|mingw/ + Encoding.default_internal || Encoding::UTF_8 + else + super + end + end +end if defined?(Reline) && ENV["TEST_READLINE_OR_RELINE"] != "Readline" diff --git a/test/readline/test_readline_history.rb b/test/readline/test_readline_history.rb index 34ab745eb0..f4e93fa1b6 100644 --- a/test/readline/test_readline_history.rb +++ b/test/readline/test_readline_history.rb @@ -260,6 +260,7 @@ class TestReadlineHistory < Test::Unit::TestCase super end end if defined?(::ReadlineSo) && defined?(::ReadlineSo::HISTORY) && + ENV["TEST_READLINE_OR_RELINE"] != "Reline" && ( begin ReadlineSo::HISTORY.clear @@ -275,4 +276,12 @@ class TestRelineAsReadlineHistory < Test::Unit::TestCase use_lib_reline super end -end + + def get_default_internal_encoding + if RUBY_PLATFORM =~ /mswin|mingw/ + Encoding.default_internal || Encoding::UTF_8 + else + super + end + end +end if defined?(Reline) && ENV["TEST_READLINE_OR_RELINE"] != "Readline" diff --git a/test/reline/test_config.rb b/test/reline/test_config.rb index dd5142d587..cecb364f89 100644 --- a/test/reline/test_config.rb +++ b/test/reline/test_config.rb @@ -195,4 +195,24 @@ class Reline::Config::Test < Reline::TestCase expected = { 'ef'.bytes => 'EF'.bytes, 'gh'.bytes => 'GH'.bytes } assert_equal expected, @config.key_bindings end + + def test_history_size + @config.read_lines(<<~LINES.lines) + set history-size 5000 + LINES + + assert_equal 5000, @config.instance_variable_get(:@history_size) + history = Reline::History.new(@config) + history << "a\n" + assert_equal 1, history.size + end + + def test_empty_inputrc_env + inputrc_backup = ENV['INPUTRC'] + ENV['INPUTRC'] = '' + assert_nothing_raised do + @config.read + end + ENV['INPUTRC'] = inputrc_backup + end end diff --git a/test/reline/test_history.rb b/test/reline/test_history.rb index 260b6e8528..189f2db86d 100644 --- a/test/reline/test_history.rb +++ b/test/reline/test_history.rb @@ -2,6 +2,10 @@ require_relative 'helper' require "reline/history" class Reline::History::Test < Reline::TestCase + def setup + Reline.send(:test_mode) + end + def test_ancestors assert_equal(Reline::History.ancestors.include?(Array), true) end @@ -268,6 +272,10 @@ class Reline::History::Test < Reline::TestCase end def get_default_internal_encoding - return Encoding.default_internal || Encoding.find("locale") + if RUBY_PLATFORM =~ /mswin|mingw/ + Encoding.default_internal || Encoding::UTF_8 + else + Encoding.default_internal || Encoding.find("locale") + end end end diff --git a/test/reline/test_key_actor_emacs.rb b/test/reline/test_key_actor_emacs.rb index 97ff654506..c16212c626 100644 --- a/test/reline/test_key_actor_emacs.rb +++ b/test/reline/test_key_actor_emacs.rb @@ -8,8 +8,8 @@ class Reline::KeyActor::Emacs::Test < Reline::TestCase Reline::HISTORY.instance_variable_set(:@config, @config) Reline::HISTORY.clear @encoding = (RELINE_TEST_ENCODING rescue Encoding.default_external) - @line_editor = Reline::LineEditor.new(@config) - @line_editor.reset(@prompt, @encoding) + @line_editor = Reline::LineEditor.new(@config, @encoding) + @line_editor.reset(@prompt, encoding: @encoding) end def test_ed_insert_one @@ -1325,6 +1325,68 @@ class Reline::KeyActor::Emacs::Test < Reline::TestCase assert_line('foo_ba') end + def test_completion_with_indent + @line_editor.completion_proc = proc { |word| + %w{ + foo_foo + foo_bar + foo_baz + qux + }.map { |i| + i.encode(@encoding) + } + } + input_keys(' fo') + assert_byte_pointer_size(' fo') + assert_cursor(4) + assert_cursor_max(4) + assert_line(' fo') + assert_equal(nil, @line_editor.instance_variable_get(:@menu_info)) + input_keys("\C-i", false) + assert_byte_pointer_size(' foo_') + assert_cursor(6) + assert_cursor_max(6) + assert_line(' foo_') + assert_equal(nil, @line_editor.instance_variable_get(:@menu_info)) + input_keys("\C-i", false) + assert_byte_pointer_size(' foo_') + assert_cursor(6) + assert_cursor_max(6) + assert_line(' foo_') + assert_equal(%w{foo_foo foo_bar foo_baz}, @line_editor.instance_variable_get(:@menu_info).list) + end + + def test_completion_with_indent_and_completer_quote_characters + @line_editor.completion_proc = proc { |word| + %w{ + "".foo_foo + "".foo_bar + "".foo_baz + "".qux + }.map { |i| + i.encode(@encoding) + } + } + input_keys(' "".fo') + assert_byte_pointer_size(' "".fo') + assert_cursor(7) + assert_cursor_max(7) + assert_line(' "".fo') + assert_equal(nil, @line_editor.instance_variable_get(:@menu_info)) + input_keys("\C-i", false) + assert_byte_pointer_size(' "".foo_') + assert_cursor(9) + assert_cursor_max(9) + assert_line(' "".foo_') + assert_equal(nil, @line_editor.instance_variable_get(:@menu_info)) + input_keys("\C-i", false) + assert_byte_pointer_size(' "".foo_') + assert_cursor(9) + assert_cursor_max(9) + assert_line(' "".foo_') + assert_equal(%w{"".foo_foo "".foo_bar "".foo_baz}, @line_editor.instance_variable_get(:@menu_info).list) + end + def test_completion_with_perfect_match @line_editor.completion_proc = proc { |word| %w{ @@ -1834,6 +1896,15 @@ class Reline::KeyActor::Emacs::Test < Reline::TestCase assert_equal([0, 0], @line_editor.instance_variable_get(:@mark_pointer)) end + def test_modify_lines_with_wrong_rs + original_global_slash = $/ + $/ = 'b' + @line_editor.output_modifier_proc = proc { |output| Reline::Unicode.escape_for_print(output) } + input_keys("abcdef\n") + assert_equal(['abcdef'], @line_editor.__send__(:modify_lines, @line_editor.whole_lines)) + $/ = original_global_slash + end + =begin # TODO: move KeyStroke instance from Reline to LineEditor def test_key_delete input_keys('ab') diff --git a/test/reline/test_key_actor_vi.rb b/test/reline/test_key_actor_vi.rb index 1ea160b6b5..c6337baea7 100644 --- a/test/reline/test_key_actor_vi.rb +++ b/test/reline/test_key_actor_vi.rb @@ -9,8 +9,8 @@ class Reline::KeyActor::ViInsert::Test < Reline::TestCase set editing-mode vi LINES @encoding = (RELINE_TEST_ENCODING rescue Encoding.default_external) - @line_editor = Reline::LineEditor.new(@config) - @line_editor.reset(@prompt, @encoding) + @line_editor = Reline::LineEditor.new(@config, @encoding) + @line_editor.reset(@prompt, encoding: @encoding) end def test_vi_command_mode @@ -24,6 +24,74 @@ class Reline::KeyActor::ViInsert::Test < Reline::TestCase assert_line('abc') end + def test_vi_insert + assert_instance_of(Reline::KeyActor::ViInsert, @config.editing_mode) + input_keys('i') + assert_line('i') + assert_cursor(1) + assert_instance_of(Reline::KeyActor::ViInsert, @config.editing_mode) + input_keys("\C-[") + assert_line('i') + assert_cursor(0) + assert_instance_of(Reline::KeyActor::ViCommand, @config.editing_mode) + input_keys('i') + assert_line('i') + assert_cursor(0) + assert_instance_of(Reline::KeyActor::ViInsert, @config.editing_mode) + end + + def test_vi_add + assert_instance_of(Reline::KeyActor::ViInsert, @config.editing_mode) + input_keys('a') + assert_line('a') + assert_cursor(1) + assert_instance_of(Reline::KeyActor::ViInsert, @config.editing_mode) + input_keys("\C-[") + assert_line('a') + assert_cursor(0) + assert_instance_of(Reline::KeyActor::ViCommand, @config.editing_mode) + input_keys('a') + assert_line('a') + assert_cursor(1) + assert_instance_of(Reline::KeyActor::ViInsert, @config.editing_mode) + end + + def test_vi_insert_at_bol + input_keys('I') + assert_line('I') + assert_instance_of(Reline::KeyActor::ViInsert, @config.editing_mode) + input_keys("12345\C-[hh") + assert_line('I12345') + assert_byte_pointer_size('I12') + assert_cursor(3) + assert_cursor_max(6) + assert_instance_of(Reline::KeyActor::ViCommand, @config.editing_mode) + input_keys('I') + assert_line('I12345') + assert_byte_pointer_size('') + assert_cursor(0) + assert_cursor_max(6) + assert_instance_of(Reline::KeyActor::ViInsert, @config.editing_mode) + end + + def test_vi_add_at_eol + input_keys('A') + assert_line('A') + assert_instance_of(Reline::KeyActor::ViInsert, @config.editing_mode) + input_keys("12345\C-[hh") + assert_line('A12345') + assert_byte_pointer_size('A12') + assert_cursor(3) + assert_cursor_max(6) + assert_instance_of(Reline::KeyActor::ViCommand, @config.editing_mode) + input_keys('A') + assert_line('A12345') + assert_byte_pointer_size('A12345') + assert_cursor(6) + assert_cursor_max(6) + assert_instance_of(Reline::KeyActor::ViInsert, @config.editing_mode) + end + def test_ed_insert_one input_keys('a') assert_line('a') @@ -565,6 +633,60 @@ class Reline::KeyActor::ViInsert::Test < Reline::TestCase assert_cursor_max(6) end + def test_vi_to_next_char + input_keys("abcdef\C-[0") + assert_line('abcdef') + assert_byte_pointer_size('') + assert_cursor(0) + assert_cursor_max(6) + input_keys('tz') + assert_line('abcdef') + assert_byte_pointer_size('') + assert_cursor(0) + assert_cursor_max(6) + input_keys('te') + assert_line('abcdef') + assert_byte_pointer_size('abc') + assert_cursor(3) + assert_cursor_max(6) + end + + def test_vi_prev_char + input_keys("abcdef\C-[") + assert_line('abcdef') + assert_byte_pointer_size('abcde') + assert_cursor(5) + assert_cursor_max(6) + input_keys('Fz') + assert_line('abcdef') + assert_byte_pointer_size('abcde') + assert_cursor(5) + assert_cursor_max(6) + input_keys('Fa') + assert_line('abcdef') + assert_byte_pointer_size('') + assert_cursor(0) + assert_cursor_max(6) + end + + def test_vi_to_prev_char + input_keys("abcdef\C-[") + assert_line('abcdef') + assert_byte_pointer_size('abcde') + assert_cursor(5) + assert_cursor_max(6) + input_keys('Tz') + assert_line('abcdef') + assert_byte_pointer_size('abcde') + assert_cursor(5) + assert_cursor_max(6) + input_keys('Ta') + assert_line('abcdef') + assert_byte_pointer_size('a') + assert_cursor(1) + assert_cursor_max(6) + end + def test_vi_delete_next_char input_keys("abc\C-[h") assert_byte_pointer_size('a') @@ -1092,4 +1214,27 @@ class Reline::KeyActor::ViInsert::Test < Reline::TestCase assert_cursor_max(11) assert_line('aaa ddd eee') end + + def test_vi_change_meta + input_keys("aaa bbb ccc ddd eee\C-[02w") + assert_byte_pointer_size('aaa bbb ') + assert_cursor(8) + assert_cursor_max(19) + assert_line('aaa bbb ccc ddd eee') + input_keys('cwaiueo ') + assert_byte_pointer_size('aaa bbb aiueo ') + assert_cursor(14) + assert_cursor_max(21) + assert_line('aaa bbb aiueo ddd eee') + input_keys("\C-[") + assert_byte_pointer_size('aaa bbb aiueo') + assert_cursor(13) + assert_cursor_max(21) + assert_line('aaa bbb aiueo ddd eee') + input_keys('cb') + assert_byte_pointer_size('aaa bbb ') + assert_cursor(8) + assert_cursor_max(16) + assert_line('aaa bbb ddd eee') + end end diff --git a/test/reline/test_macro.rb b/test/reline/test_macro.rb index c69b50416a..b97de88a97 100644 --- a/test/reline/test_macro.rb +++ b/test/reline/test_macro.rb @@ -3,7 +3,8 @@ require_relative 'helper' class Reline::MacroTest < Reline::TestCase def setup @config = Reline::Config.new - @line_editor = Reline::LineEditor.new(@config) + @encoding = (RELINE_TEST_ENCODING rescue Encoding.default_external) + @line_editor = Reline::LineEditor.new(@config, @encoding) @line_editor.instance_variable_set(:@screen_size, [24, 80]) @output = @line_editor.output = File.open(IO::NULL, "w") end diff --git a/test/reline/test_reline.rb b/test/reline/test_reline.rb index 274f1aa6ba..0de2462a08 100644 --- a/test/reline/test_reline.rb +++ b/test/reline/test_reline.rb @@ -21,15 +21,15 @@ class Reline::Test < Reline::TestCase Reline.completion_append_character = "a".encode(Encoding::ASCII) assert_equal("a", Reline.completion_append_character) - assert_equal(Encoding::default_external, Reline.completion_append_character.encoding) + assert_equal(get_reline_encoding, Reline.completion_append_character.encoding) Reline.completion_append_character = "ba".encode(Encoding::ASCII) assert_equal("b", Reline.completion_append_character) - assert_equal(Encoding::default_external, Reline.completion_append_character.encoding) + assert_equal(get_reline_encoding, Reline.completion_append_character.encoding) Reline.completion_append_character = "cba".encode(Encoding::ASCII) assert_equal("c", Reline.completion_append_character) - assert_equal(Encoding::default_external, Reline.completion_append_character.encoding) + assert_equal(get_reline_encoding, Reline.completion_append_character.encoding) Reline.completion_append_character = nil assert_equal(nil, Reline.completion_append_character) @@ -40,7 +40,7 @@ class Reline::Test < Reline::TestCase Reline.basic_word_break_characters = "[".encode(Encoding::ASCII) assert_equal("[", Reline.basic_word_break_characters) - assert_equal(Encoding::default_external, Reline.basic_word_break_characters.encoding) + assert_equal(get_reline_encoding, Reline.basic_word_break_characters.encoding) end def test_completer_word_break_characters @@ -48,7 +48,7 @@ class Reline::Test < Reline::TestCase Reline.completer_word_break_characters = "[".encode(Encoding::ASCII) assert_equal("[", Reline.completer_word_break_characters) - assert_equal(Encoding::default_external, Reline.completer_word_break_characters.encoding) + assert_equal(get_reline_encoding, Reline.completer_word_break_characters.encoding) end def test_basic_quote_characters @@ -56,7 +56,7 @@ class Reline::Test < Reline::TestCase Reline.basic_quote_characters = "`".encode(Encoding::ASCII) assert_equal("`", Reline.basic_quote_characters) - assert_equal(Encoding::default_external, Reline.basic_quote_characters.encoding) + assert_equal(get_reline_encoding, Reline.basic_quote_characters.encoding) end def test_completer_quote_characters @@ -64,7 +64,7 @@ class Reline::Test < Reline::TestCase Reline.completer_quote_characters = "`".encode(Encoding::ASCII) assert_equal("`", Reline.completer_quote_characters) - assert_equal(Encoding::default_external, Reline.completer_quote_characters.encoding) + assert_equal(get_reline_encoding, Reline.completer_quote_characters.encoding) end def test_filename_quote_characters @@ -72,7 +72,7 @@ class Reline::Test < Reline::TestCase Reline.filename_quote_characters = "\'".encode(Encoding::ASCII) assert_equal("\'", Reline.filename_quote_characters) - assert_equal(Encoding::default_external, Reline.filename_quote_characters.encoding) + assert_equal(get_reline_encoding, Reline.filename_quote_characters.encoding) end def test_special_prefixes @@ -80,7 +80,7 @@ class Reline::Test < Reline::TestCase Reline.special_prefixes = "\'".encode(Encoding::ASCII) assert_equal("\'", Reline.special_prefixes) - assert_equal(Encoding::default_external, Reline.special_prefixes.encoding) + assert_equal(get_reline_encoding, Reline.special_prefixes.encoding) end def test_completion_case_fold @@ -94,7 +94,10 @@ class Reline::Test < Reline::TestCase end def test_completion_proc - assert_equal(nil, Reline.completion_proc) + skip unless Reline.completion_proc == nil + # Another test can set Reline.completion_proc + + # assert_equal(nil, Reline.completion_proc) p = proc {} Reline.completion_proc = p @@ -267,4 +270,8 @@ class Reline::Test < Reline::TestCase def test_may_req_ambiguous_char_width # TODO in Reline::Core end + + def get_reline_encoding + RUBY_PLATFORM =~ /mswin|mingw/ ? Encoding::UTF_8 : Encoding::default_external + end end diff --git a/test/reline/test_string_processing.rb b/test/reline/test_string_processing.rb index 4df0363848..e76fa384f2 100644 --- a/test/reline/test_string_processing.rb +++ b/test/reline/test_string_processing.rb @@ -7,8 +7,8 @@ class Reline::LineEditor::StringProcessingTest < Reline::TestCase @config = Reline::Config.new Reline::HISTORY.instance_variable_set(:@config, @config) @encoding = (RELINE_TEST_ENCODING rescue Encoding.default_external) - @line_editor = Reline::LineEditor.new(@config) - @line_editor.reset(@prompt, @encoding) + @line_editor = Reline::LineEditor.new(@config, @encoding) + @line_editor.reset(@prompt, encoding: @encoding) end def test_calculate_width diff --git a/test/reline/test_within_pipe.rb b/test/reline/test_within_pipe.rb index 11c3c0e86a..46b4465f32 100644 --- a/test/reline/test_within_pipe.rb +++ b/test/reline/test_within_pipe.rb @@ -13,6 +13,7 @@ class Reline::WithinPipeTest < Reline::TestCase def teardown Reline.input = STDIN Reline.output = STDOUT + Reline.point = 0 @reader.close @writer.close @output.close diff --git a/test/reline/yamatanooroti/test_rendering.rb b/test/reline/yamatanooroti/test_rendering.rb new file mode 100644 index 0000000000..4eab6661d6 --- /dev/null +++ b/test/reline/yamatanooroti/test_rendering.rb @@ -0,0 +1,41 @@ +require 'reline' + +begin + require 'yamatanooroti' + + class Reline::TestRendering < Yamatanooroti::TestCase + def setup + inputrc_backup = ENV['INPUTRC'] + ENV['INPUTRC'] = 'nonexistent_file' + start_terminal(5, 30, %w{ruby -Ilib bin/multiline_repl}) + sleep 0.5 + ENV['INPUTRC'] = inputrc_backup + end + + def test_history_back + write(":a\n") + write("\C-p") + close + assert_screen(<<~EOC) + Multiline REPL. + prompt> :a + => :a + prompt> :a + EOC + end + + def test_backspace + write(":abc\C-h\n") + close + assert_screen(<<~EOC) + Multiline REPL. + prompt> :ab + => :ab + prompt> + EOC + end + end +rescue LoadError, NameError + # On Ruby repository, this test suit doesn't run because Ruby repo doesn't + # have the yamatanooroti gem. +end diff --git a/test/ruby/test_rubyoptions.rb b/test/ruby/test_rubyoptions.rb index cac420422d..093720b1fc 100644 --- a/test/ruby/test_rubyoptions.rb +++ b/test/ruby/test_rubyoptions.rb @@ -285,7 +285,7 @@ class TestRubyOptions < Test::Unit::TestCase /unknown encoding name - test_ruby_test_rubyoptions_foobarbazqux \(RuntimeError\)/) if /mswin|mingw|aix|android/ =~ RUBY_PLATFORM && - (str = "\u3042".force_encoding(Encoding.find("locale"))).valid_encoding? + (str = "\u3042".force_encoding(Encoding.find("external"))).valid_encoding? # This result depends on locale because LANG=C doesn't affect locale # on Windows. # On AIX, the source encoding of stdin with LANG=C is ISO-8859-1, @@ -836,11 +836,11 @@ class TestRubyOptions < Test::Unit::TestCase def test_command_line_glob_nonascii bug10555 = '[ruby-dev:48752] [Bug #10555]' name = "\u{3042}.txt" - expected = name.encode("locale") rescue "?.txt" + expected = name.encode("external") rescue "?.txt" with_tmpchdir do |dir| open(name, "w") {} assert_in_out_err(["-e", "puts ARGV", "?.txt"], "", [expected], [], - bug10555, encoding: "locale") + bug10555, encoding: "external") end end @@ -875,7 +875,7 @@ class TestRubyOptions < Test::Unit::TestCase with_tmpchdir do |dir| Ougai.each {|f| open(f, "w") {}} assert_in_out_err(["-Eutf-8", "-e", "puts ARGV", "*"], "", Ougai, encoding: "utf-8") - ougai = Ougai.map {|f| f.encode("locale", replace: "?")} + ougai = Ougai.map {|f| f.encode("external", replace: "?")} assert_in_out_err(["-e", "puts ARGV", "*.txt"], "", ougai) end end |