summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/benchmark/benchmark.gemspec2
-rw-r--r--lib/cgi/cgi.gemspec2
-rw-r--r--lib/delegate/delegate.gemspec2
-rw-r--r--lib/getoptlong/getoptlong.gemspec2
-rw-r--r--lib/irb.rb12
-rw-r--r--lib/irb/.document1
-rw-r--r--lib/irb/completion.rb27
-rw-r--r--lib/irb/context.rb20
-rw-r--r--lib/irb/easter-egg.rb137
-rw-r--r--lib/irb/ext/save-history.rb4
-rw-r--r--lib/irb/init.rb9
-rw-r--r--lib/irb/input-method.rb4
-rw-r--r--lib/irb/irb.gemspec3
-rw-r--r--lib/irb/lc/.document4
-rw-r--r--lib/irb/locale.rb11
-rw-r--r--lib/irb/ruby-lex.rb13
-rw-r--r--lib/irb/version.rb4
-rw-r--r--lib/net/pop/net-pop.gemspec2
-rw-r--r--lib/net/smtp/net-smtp.gemspec2
-rw-r--r--lib/observer/observer.gemspec2
-rw-r--r--lib/open3/open3.gemspec2
-rw-r--r--lib/pstore/pstore.gemspec2
-rw-r--r--lib/reline.rb50
-rw-r--r--lib/reline/ansi.rb58
-rw-r--r--lib/reline/config.rb19
-rw-r--r--lib/reline/general_io.rb8
-rw-r--r--lib/reline/history.rb6
-rw-r--r--lib/reline/line_editor.rb134
-rw-r--r--lib/reline/version.rb2
-rw-r--r--lib/reline/windows.rb45
-rw-r--r--lib/singleton/singleton.gemspec2
-rw-r--r--lib/timeout/timeout.gemspec2
-rw-r--r--lib/uri/uri.gemspec2
-rw-r--r--lib/yaml/yaml.gemspec2
34 files changed, 494 insertions, 103 deletions
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) }