summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authoraycabta <aycabta@gmail.com>2019-04-23 18:09:46 +0900
committerNobuyoshi Nakada <nobu@ruby-lang.org>2019-04-23 20:08:02 +0900
commitf2cd4f4cd0a1e352fcc48a216127beaeda2b2399 (patch)
tree8e02f39a8df8fa5708e446aeb8d42d45d8a28abe /lib
parent87cf45a512a7803f266e4782c49e0a99c06a4039 (diff)
IRB is improved with Reline and RDoc, take 2
Diffstat (limited to 'lib')
-rw-r--r--lib/irb.rb9
-rw-r--r--lib/irb/completion.rb19
-rw-r--r--lib/irb/context.rb81
-rw-r--r--lib/irb/init.rb8
-rw-r--r--lib/irb/input-method.rb85
-rw-r--r--lib/irb/lc/help-message1
-rw-r--r--lib/irb/lc/ja/help-message2
-rw-r--r--lib/irb/ruby-lex.rb1259
-rw-r--r--lib/rdoc/ri/paths.rb2
-rw-r--r--lib/readline.rb6
-rw-r--r--lib/reline.rb196
-rw-r--r--lib/reline/ansi.rb87
-rw-r--r--lib/reline/config.rb235
-rw-r--r--lib/reline/key_actor.rb7
-rw-r--r--lib/reline/key_actor/base.rb7
-rw-r--r--lib/reline/key_actor/emacs.rb518
-rw-r--r--lib/reline/key_actor/vi_command.rb519
-rw-r--r--lib/reline/key_actor/vi_insert.rb518
-rw-r--r--lib/reline/key_stroke.rb74
-rw-r--r--lib/reline/kill_ring.rb113
-rw-r--r--lib/reline/line_editor.rb1357
-rw-r--r--lib/reline/reline.gemspec25
-rw-r--r--lib/reline/unicode.rb415
-rw-r--r--lib/reline/unicode/east_asian_width.rb1145
-rw-r--r--lib/reline/version.rb3
-rw-r--r--lib/reline/windows.rb132
-rw-r--r--lib/rubygems.rb1
27 files changed, 5701 insertions, 1123 deletions
diff --git a/lib/irb.rb b/lib/irb.rb
index 78d0b7c8cf..897cfae957 100644
--- a/lib/irb.rb
+++ b/lib/irb.rb
@@ -59,6 +59,8 @@ require "irb/version"
# -W[level=2] Same as `ruby -W`
# --inspect Use `inspect' for output (default except for bc mode)
# --noinspect Don't use inspect for output
+# --reidline Use Reidline extension module
+# --noreidline Don't use Reidline extension module
# --readline Use Readline extension module
# --noreadline Don't use Readline extension module
# --prompt prompt-mode
@@ -66,14 +68,13 @@ require "irb/version"
# Switch prompt mode. Pre-defined prompt modes are
# `default', `simple', `xmp' and `inf-ruby'
# --inf-ruby-mode Use prompt appropriate for inf-ruby-mode on emacs.
-# Suppresses --readline.
+# Suppresses --reidline and --readline.
# --simple-prompt Simple prompt mode
# --noprompt No prompt mode
# --tracer Display trace for each execution of commands.
# --back-trace-limit n
# Display backtrace top n and tail n. The default
# value is 16.
-# --irb_debug n Set internal debug level to n (not for popular use)
# -v, --version Print the version of irb
#
# == Configuration
@@ -95,13 +96,13 @@ require "irb/version"
# IRB.conf[:IRB_RC] = nil
# IRB.conf[:BACK_TRACE_LIMIT]=16
# IRB.conf[:USE_LOADER] = false
+# IRB.conf[:USE_REIDLINE] = nil
# IRB.conf[:USE_READLINE] = nil
# IRB.conf[:USE_TRACER] = false
# IRB.conf[:IGNORE_SIGINT] = true
# IRB.conf[:IGNORE_EOF] = false
# IRB.conf[:PROMPT_MODE] = :DEFAULT
# IRB.conf[:PROMPT] = {...}
-# IRB.conf[:DEBUG_LEVEL]=0
#
# === Auto indentation
#
@@ -410,9 +411,7 @@ module IRB
@context = Context.new(self, workspace, input_method, output_method)
@context.main.extend ExtendCommandBundle
@signal_status = :IN_IRB
-
@scanner = RubyLex.new
- @scanner.exception_on_syntax_error = false
end
def run(conf = IRB.conf)
diff --git a/lib/irb/completion.rb b/lib/irb/completion.rb
index 390e7254dd..4cd9427743 100644
--- a/lib/irb/completion.rb
+++ b/lib/irb/completion.rb
@@ -8,11 +8,10 @@
#
require "readline"
+require "rdoc"
module IRB
module InputCompletor # :nodoc:
-
-
# Set of reserved words used by Ruby, you should not use these for
# constants or variables
ReservedWords = %w[
@@ -35,6 +34,8 @@ module IRB
yield
]
+ BASIC_WORD_BREAK_CHARACTERS = " \t\n`><=;|&{("
+
CompletionProc = proc { |input|
bind = IRB.conf[:MAIN_CONTEXT].workspace.binding
@@ -195,6 +196,14 @@ module IRB
end
}
+ RDocRIDriver = RDoc::RI::Driver.new
+ PerfectMatchedProc = proc { |matched|
+ begin
+ RDocRIDriver.display_name(matched)
+ rescue RDoc::RI::Driver::NotFoundError
+ end
+ }
+
# Set of available operators in Ruby
Operators = %w[% & * ** + - / < << <= <=> == === =~ > >= >> [] []= ^ ! != !~]
@@ -236,9 +245,3 @@ module IRB
end
end
end
-
-if Readline.respond_to?("basic_word_break_characters=")
- Readline.basic_word_break_characters= " \t\n`><=;|&{("
-end
-Readline.completion_append_character = nil
-Readline.completion_proc = IRB::InputCompletor::CompletionProc
diff --git a/lib/irb/context.rb b/lib/irb/context.rb
index e8e6a118e6..866eb1de9d 100644
--- a/lib/irb/context.rb
+++ b/lib/irb/context.rb
@@ -22,7 +22,7 @@ module IRB
#
# The optional +input_method+ argument:
#
- # +nil+:: uses stdin or Readline
+ # +nil+:: uses stdin or Reidline or Readline
# +String+:: uses a File
# +other+:: uses this as InputMethod
def initialize(irb, workspace = nil, input_method = nil, output_method = nil)
@@ -40,6 +40,7 @@ module IRB
@load_modules = IRB.conf[:LOAD_MODULES]
@use_readline = IRB.conf[:USE_READLINE]
+ @use_reidline = IRB.conf[:USE_REIDLINE]
@verbose = IRB.conf[:VERBOSE]
@io = nil
@@ -64,23 +65,41 @@ module IRB
case input_method
when nil
- case use_readline?
+ @io = nil
+ case use_reidline?
when nil
- if (defined?(ReadlineInputMethod) && STDIN.tty? &&
- IRB.conf[:PROMPT_MODE] != :INF_RUBY)
- @io = ReadlineInputMethod.new
+ if STDIN.tty? && IRB.conf[:PROMPT_MODE] != :INF_RUBY && !use_readline?
+ @io = ReidlineInputMethod.new
else
- @io = StdioInputMethod.new
+ @io = nil
end
when false
- @io = StdioInputMethod.new
+ @io = nil
when true
- if defined?(ReadlineInputMethod)
- @io = ReadlineInputMethod.new
+ @io = ReidlineInputMethod.new
+ end
+ unless @io
+ case use_readline?
+ when nil
+ if (defined?(ReadlineInputMethod) && STDIN.tty? &&
+ IRB.conf[:PROMPT_MODE] != :INF_RUBY)
+ @io = ReadlineInputMethod.new
+ else
+ @io = nil
+ end
+ when false
+ @io = nil
+ when true
+ if defined?(ReadlineInputMethod)
+ @io = ReadlineInputMethod.new
+ else
+ @io = nil
+ end
else
- @io = StdioInputMethod.new
+ @io = nil
end
end
+ @io = StdioInputMethod.new unless @io
when String
@io = FileInputMethod.new(input_method)
@@ -101,7 +120,6 @@ module IRB
if @echo.nil?
@echo = true
end
- self.debug_level = IRB.conf[:DEBUG_LEVEL]
end
# The top-level workspace, see WorkSpace#main
@@ -117,9 +135,9 @@ module IRB
attr_reader :thread
# The current input method
#
- # Can be either StdioInputMethod, ReadlineInputMethod, FileInputMethod or
- # other specified when the context is created. See ::new for more
- # information on +input_method+.
+ # Can be either StdioInputMethod, ReadlineInputMethod,
+ # ReidlineInputMethod, FileInputMethod or other specified when the
+ # context is created. See ::new for more # information on +input_method+.
attr_accessor :io
# Current irb session
@@ -137,6 +155,12 @@ module IRB
# +input_method+ passed to Context.new
attr_accessor :irb_path
+ # Whether +Reidline+ is enabled or not.
+ #
+ # A copy of the default <code>IRB.conf[:USE_REIDLINE]</code>
+ #
+ # See #use_reidline= for more information.
+ attr_reader :use_reidline
# Whether +Readline+ is enabled or not.
#
# A copy of the default <code>IRB.conf[:USE_READLINE]</code>
@@ -211,10 +235,6 @@ module IRB
#
# A copy of the default <code>IRB.conf[:VERBOSE]</code>
attr_accessor :verbose
- # The debug level of irb
- #
- # See #debug_level= for more information.
- attr_reader :debug_level
# The limit of backtrace lines displayed as top +n+ and tail +n+.
#
@@ -225,6 +245,8 @@ module IRB
# See IRB@Command+line+options for more command line options.
attr_accessor :back_trace_limit
+ # Alias for #use_reidline
+ alias use_reidline? use_reidline
# Alias for #use_readline
alias use_readline? use_readline
# Alias for #rc
@@ -236,7 +258,9 @@ module IRB
# Returns whether messages are displayed or not.
def verbose?
if @verbose.nil?
- if defined?(ReadlineInputMethod) && @io.kind_of?(ReadlineInputMethod)
+ if @io.kind_of?(ReidlineInputMethod)
+ false
+ elsif defined?(ReadlineInputMethod) && @io.kind_of?(ReadlineInputMethod)
false
elsif !STDIN.tty? or @io.kind_of?(FileInputMethod)
true
@@ -249,9 +273,11 @@ module IRB
end
# Whether #verbose? is +true+, and +input_method+ is either
- # StdioInputMethod or ReadlineInputMethod, see #io for more information.
+ # StdioInputMethod or ReidlineInputMethod or ReadlineInputMethod, see #io
+ # for more information.
def prompting?
verbose? || (STDIN.tty? && @io.kind_of?(StdioInputMethod) ||
+ @io.kind_of?(ReidlineInputMethod) ||
(defined?(ReadlineInputMethod) && @io.kind_of?(ReadlineInputMethod)))
end
@@ -361,21 +387,6 @@ module IRB
print "Do nothing."
end
- # Sets the debug level of irb
- #
- # Can also be set using the +--irb_debug+ command line option.
- #
- # See IRB@Command+line+options for more command line options.
- def debug_level=(value)
- @debug_level = value
- RubyLex.debug_level = value
- end
-
- # Whether or not debug mode is enabled, see #debug_level=.
- def debug?
- @debug_level > 0
- end
-
def evaluate(line, line_no, exception: nil) # :nodoc:
@line_no = line_no
if exception
diff --git a/lib/irb/init.rb b/lib/irb/init.rb
index 2066d8cb64..ec413679df 100644
--- a/lib/irb/init.rb
+++ b/lib/irb/init.rb
@@ -112,8 +112,6 @@ module IRB # :nodoc:
@CONF[:LC_MESSAGES] = Locale.new
@CONF[:AT_EXIT] = []
-
- @CONF[:DEBUG_LEVEL] = 0
end
def IRB.init_error
@@ -165,6 +163,10 @@ module IRB # :nodoc:
@CONF[:USE_READLINE] = true
when "--noreadline"
@CONF[:USE_READLINE] = false
+ when "--reidline"
+ @CONF[:USE_REIDLINE] = true
+ when "--noreidline"
+ @CONF[:USE_REIDLINE] = false
when "--echo"
@CONF[:ECHO] = true
when "--noecho"
@@ -191,8 +193,6 @@ module IRB # :nodoc:
@CONF[:CONTEXT_MODE] = ($1 || argv.shift).to_i
when "--single-irb"
@CONF[:SINGLE_IRB] = true
- when /^--irb_debug(?:=(.+))?/
- @CONF[:DEBUG_LEVEL] = ($1 || argv.shift).to_i
when "-v", "--version"
print IRB.version, "\n"
exit 0
diff --git a/lib/irb/input-method.rb b/lib/irb/input-method.rb
index f491d5a760..e29d282d2c 100644
--- a/lib/irb/input-method.rb
+++ b/lib/irb/input-method.rb
@@ -11,6 +11,8 @@
#
require_relative 'src_encoding'
require_relative 'magic-file'
+require_relative "completion"
+require 'reline'
module IRB
STDIN_FILE_NAME = "(line)" # :nodoc:
@@ -140,6 +142,12 @@ module IRB
@stdin = IO.open(STDIN.to_i, :external_encoding => IRB.conf[:LC_MESSAGES].encoding, :internal_encoding => "-")
@stdout = IO.open(STDOUT.to_i, 'w', :external_encoding => IRB.conf[:LC_MESSAGES].encoding, :internal_encoding => "-")
+
+ if Readline.respond_to?("basic_word_break_characters=")
+ Readline.basic_word_break_characters = IRB::InputCompletor::BASIC_WORD_BREAK_CHARACTERS
+ end
+ Readline.completion_append_character = nil
+ Readline.completion_proc = IRB::InputCompletor::CompletionProc
end
# Reads the next line from this input method.
@@ -186,7 +194,84 @@ module IRB
def encoding
@stdin.external_encoding
end
+
+ if Readline.respond_to?("basic_word_break_characters=")
+ Readline.basic_word_break_characters = IRB::InputCompletor::BASIC_WORD_BREAK_CHARACTERS
+ end
+ Readline.completion_append_character = nil
+ Readline.completion_proc = IRB::InputCompletor::CompletionProc
end
rescue LoadError
end
+
+ class ReidlineInputMethod < InputMethod
+ include Reline
+ # Creates a new input method object using Readline
+ def initialize
+ super
+
+ @line_no = 0
+ @line = []
+ @eof = false
+
+ @stdin = IO.open(STDIN.to_i, :external_encoding => IRB.conf[:LC_MESSAGES].encoding, :internal_encoding => "-")
+ @stdout = IO.open(STDOUT.to_i, 'w', :external_encoding => IRB.conf[:LC_MESSAGES].encoding, :internal_encoding => "-")
+
+ if Reline.respond_to?("basic_word_break_characters=")
+ Reline.basic_word_break_characters = IRB::InputCompletor::BASIC_WORD_BREAK_CHARACTERS
+ end
+ Reline.completion_append_character = nil
+ Reline.completion_proc = IRB::InputCompletor::CompletionProc
+ Reline.dig_perfect_match_proc = IRB::InputCompletor::PerfectMatchedProc
+ end
+
+ def check_termination(&block)
+ @check_termination_proc = block
+ end
+
+ # Reads the next line from this input method.
+ #
+ # See IO#gets for more information.
+ def gets
+ Reline.input = @stdin
+ Reline.output = @stdout
+ if l = readmultiline(@prompt, false, &@check_termination_proc)
+ HISTORY.push(l) if !l.empty?
+ @line[@line_no += 1] = l + "\n"
+ else
+ @eof = true
+ l
+ end
+ end
+
+ # Whether the end of this input method has been reached, returns +true+
+ # if there is no more data to read.
+ #
+ # See IO#eof? for more information.
+ def eof?
+ @eof
+ end
+
+ # Whether this input method is still readable when there is no more data to
+ # read.
+ #
+ # See IO#eof for more information.
+ def readable_after_eof?
+ true
+ end
+
+ # Returns the current line number for #io.
+ #
+ # #line counts the number of times #gets is called.
+ #
+ # See IO#lineno for more information.
+ def line(line_no)
+ @line[line_no]
+ end
+
+ # The external encoding for standard input.
+ def encoding
+ @stdin.external_encoding
+ end
+ end
end
diff --git a/lib/irb/lc/help-message b/lib/irb/lc/help-message
index d43c6a1695..d1a66dddda 100644
--- a/lib/irb/lc/help-message
+++ b/lib/irb/lc/help-message
@@ -39,7 +39,6 @@ Usage: irb.rb [options] [programfile] [arguments]
--back-trace-limit n
Display backtrace top n and tail n. The default
value is 16.
- --irb_debug n Set internal debug level to n (not for popular use)
--verbose Show details
--noverbose Don't show details
-v, --version Print the version of irb
diff --git a/lib/irb/lc/ja/help-message b/lib/irb/lc/ja/help-message
index 1b24d14d28..7a15f973c6 100644
--- a/lib/irb/lc/ja/help-message
+++ b/lib/irb/lc/ja/help-message
@@ -41,8 +41,6 @@ Usage: irb.rb [options] [programfile] [arguments]
バックトレース表示をバックトレースの頭から n, 後ろ
からnだけ行なう. デフォルトは16
- --irb_debug n irbのデバッグレベルをnに設定する(非推奨).
-
--verbose 詳細なメッセージを出力する.
--noverbose 詳細なメッセージを出力しない(デフォルト).
-v, --version irbのバージョンを表示する.
diff --git a/lib/irb/ruby-lex.rb b/lib/irb/ruby-lex.rb
index 555d1f024f..c4bec4a854 100644
--- a/lib/irb/ruby-lex.rb
+++ b/lib/irb/ruby-lex.rb
@@ -11,73 +11,39 @@
#
require "e2mmap"
-require_relative "slex"
-require_relative "ruby-token"
+require "ripper"
# :stopdoc:
class RubyLex
extend Exception2MessageMapper
- def_exception(:AlreadyDefinedToken, "Already defined token(%s)")
- def_exception(:TkReading2TokenNoKey, "key nothing(key='%s')")
- def_exception(:TkSymbol2TokenNoKey, "key nothing(key='%s')")
- def_exception(:TkReading2TokenDuplicateError,
- "key duplicate(token_n='%s', key='%s')")
- def_exception(:SyntaxError, "%s")
-
def_exception(:TerminateLineInput, "Terminate Line Input")
- include RubyToken
-
- class << self
- attr_accessor :debug_level
- def debug?
- @debug_level > 0
- end
- end
- @debug_level = 0
-
def initialize
- lex_init
- set_input(STDIN)
-
- @seek = 0
@exp_line_no = @line_no = 1
- @base_char_no = 0
- @char_no = 0
- @rests = []
- @readed = []
- @here_readed = []
-
@indent = 0
- @indent_stack = []
- @lex_state = EXPR_BEG
- @space_seen = false
- @here_header = false
- @post_symbeg = false
-
@continue = false
@line = ""
-
- @skip_space = false
- @readed_auto_clean_up = false
- @exception_on_syntax_error = true
-
@prompt = nil
end
- attr_accessor :skip_space
- attr_accessor :readed_auto_clean_up
- attr_accessor :exception_on_syntax_error
-
- attr_reader :seek
- attr_reader :char_no
- attr_reader :line_no
- attr_reader :indent
-
# io functions
def set_input(io, p = nil, &block)
@io = io
+ if @io.respond_to?(:check_termination)
+ @io.check_termination do |code|
+ @tokens = Ripper.lex(code)
+ continue = process_continue
+ code_block_open = check_code_block(code)
+ indent = process_nesting_level
+ ltype = process_literal_type
+ if code_block_open or ltype or continue or indent > 0
+ false
+ else
+ true
+ end
+ end
+ end
if p.respond_to?(:call)
@input = p
elsif block_given?
@@ -87,112 +53,6 @@ class RubyLex
end
end
- def get_readed
- if idx = @readed.rindex("\n")
- @base_char_no = @readed.size - (idx + 1)
- else
- @base_char_no += @readed.size
- end
-
- readed = @readed.join("")
- @readed = []
- readed
- end
-
- def getc
- while @rests.empty?
- @rests.push nil unless buf_input
- end
- c = @rests.shift
- if @here_header
- @here_readed.push c
- else
- @readed.push c
- end
- @seek += 1
- if c == "\n"
- @line_no += 1
- @char_no = 0
- else
- @char_no += 1
- end
- c
- end
-
- def gets
- l = ""
- while c = getc
- l.concat(c)
- break if c == "\n"
- end
- return nil if l == "" and c.nil?
- l
- end
-
- def eof?
- @io.eof?
- end
-
- def getc_of_rests
- if @rests.empty?
- nil
- else
- getc
- end
- end
-
- def ungetc(c = nil)
- if @here_readed.empty?
- c2 = @readed.pop
- else
- c2 = @here_readed.pop
- end
- c = c2 unless c
- @rests.unshift c #c =
- @seek -= 1
- if c == "\n"
- @line_no -= 1
- if idx = @readed.rindex("\n")
- @char_no = idx + 1
- else
- @char_no = @base_char_no + @readed.size
- end
- else
- @char_no -= 1
- end
- end
-
- def peek_equal?(str)
- chrs = str.split(//)
- until @rests.size >= chrs.size
- return false unless buf_input
- end
- @rests[0, chrs.size] == chrs
- end
-
- def peek_match?(regexp)
- while @rests.empty?
- return false unless buf_input
- end
- regexp =~ @rests.join("")
- end
-
- def peek(i = 0)
- while @rests.size <= i
- return nil unless buf_input
- end
- @rests[i]
- end
-
- def buf_input
- prompt
- line = @input.call
- return nil unless line
- @rests.concat line.chars.to_a
- true
- end
- private :buf_input
-
def set_prompt(p = nil, &block)
p = block if block_given?
if p.respond_to?(:call)
@@ -210,20 +70,11 @@ class RubyLex
def initialize_input
@ltype = nil
- @quoted = nil
@indent = 0
- @indent_stack = []
- @lex_state = EXPR_BEG
- @space_seen = false
- @here_header = false
-
@continue = false
- @post_symbeg = false
-
- prompt
-
@line = ""
@exp_line_no = @line_no
+ @code_block_open = false
end
def each_top_level_statement
@@ -231,13 +82,14 @@ class RubyLex
catch(:TERM_INPUT) do
loop do
begin
- @continue = false
prompt
unless l = lex
throw :TERM_INPUT if @line == ''
else
+ @line_no += 1
+ next if l == "\n"
@line.concat l
- if @ltype or @continue or @indent > 0
+ if @code_block_open or @ltype or @continue or @indent > 0
next
end
end
@@ -250,930 +102,203 @@ class RubyLex
@exp_line_no = @line_no
@indent = 0
- @indent_stack = []
- prompt
rescue TerminateLineInput
initialize_input
prompt
- get_readed
end
end
end
end
def lex
- continue = @continue
- while tk = token
- case tk
- when TkNL, TkEND_OF_SCRIPT
- @continue = continue unless continue.nil?
- break unless @continue
- when TkSPACE, TkCOMMENT
- when TkSEMICOLON, TkBEGIN, TkELSE
- @continue = continue = false
- else
- continue = nil
- end
- end
- line = get_readed
- if line == "" and tk.kind_of?(TkEND_OF_SCRIPT) || tk.nil?
- nil
- else
- line
- end
- end
-
- def token
- @prev_seek = @seek
- @prev_line_no = @line_no
- @prev_char_no = @char_no
- begin
- begin
- tk = @OP.match(self)
- @space_seen = tk.kind_of?(TkSPACE)
- @lex_state = EXPR_END if @post_symbeg && tk.kind_of?(TkOp)
- @post_symbeg = tk.kind_of?(TkSYMBEG)
- rescue SyntaxError
- raise if @exception_on_syntax_error
- tk = TkError.new(@seek, @line_no, @char_no)
- end
- end while @skip_space and tk.kind_of?(TkSPACE)
- if @readed_auto_clean_up
- get_readed
- end
- tk
- end
-
- ENINDENT_CLAUSE = [
- "case", "class", "def", "do", "for", "if",
- "module", "unless", "until", "while", "begin"
- ]
- DEINDENT_CLAUSE = ["end"
- ]
-
- PERCENT_LTYPE = {
- "q" => "\'",
- "Q" => "\"",
- "x" => "\`",
- "r" => "/",
- "w" => "]",
- "W" => "]",
- "i" => "]",
- "I" => "]",
- "s" => ":"
- }
-
- PERCENT_PAREN = {
- "{" => "}",
- "[" => "]",
- "<" => ">",
- "(" => ")"
- }
-
- Ltype2Token = {
- "\'" => TkSTRING,
- "\"" => TkSTRING,
- "\`" => TkXSTRING,
- "/" => TkREGEXP,
- "]" => TkDSTRING,
- ":" => TkSYMBOL
- }
- DLtype2Token = {
- "\"" => TkDSTRING,
- "\`" => TkDXSTRING,
- "/" => TkDREGEXP,
- }
-
- def lex_init()
- @OP = IRB::SLex.new
- @OP.def_rules("\0", "\004", "\032") do |op, io|
- Token(TkEND_OF_SCRIPT)
- end
-
- @OP.def_rules(" ", "\t", "\f", "\r", "\13") do |op, io|
- @space_seen = true
- while getc =~ /[ \t\f\r\13]/; end
- ungetc
- Token(TkSPACE)
- end
-
- @OP.def_rule("#") do |op, io|
- identify_comment
- end
-
- @OP.def_rule("=begin",
- proc{|op, io| @prev_char_no == 0 && peek(0) =~ /\s/}) do
- |op, io|
- @ltype = "="
- until getc == "\n"; end
- until peek_equal?("=end") && peek(4) =~ /\s/
- until getc == "\n"; end
- end
- gets
- @ltype = nil
- Token(TkRD_COMMENT)
- end
-
- @OP.def_rule("\n") do |op, io|
- print "\\n\n" if RubyLex.debug?
- case @lex_state
- when EXPR_BEG, EXPR_FNAME, EXPR_DOT
- @continue = true
- else
- @continue = false
- @lex_state = EXPR_BEG
- until (@indent_stack.empty? ||
- [TkLPAREN, TkLBRACK, TkLBRACE,
- TkfLPAREN, TkfLBRACK, TkfLBRACE].include?(@indent_stack.last))
- @indent_stack.pop
- end
- end
- @here_header = false
- @here_readed = []
- Token(TkNL)
- end
-
- @OP.def_rules("*", "**",
- "=", "==", "===",
- "=~", "<=>",
- "<", "<=",
- ">", ">=", ">>",
- "!", "!=", "!~") do
- |op, io|
- case @lex_state
- when EXPR_FNAME, EXPR_DOT
- @lex_state = EXPR_ARG
- else
- @lex_state = EXPR_BEG
- end
- Token(op)
- end
-
- @OP.def_rules("<<") do
- |op, io|
- tk = nil
- if @lex_state != EXPR_END && @lex_state != EXPR_CLASS &&
- (@lex_state != EXPR_ARG || @space_seen)
- c = peek(0)
- if /[-~"'`\w]/ =~ c
- tk = identify_here_document
- end
- end
- unless tk
- tk = Token(op)
- case @lex_state
- when EXPR_FNAME, EXPR_DOT
- @lex_state = EXPR_ARG
- else
- @lex_state = EXPR_BEG
- end
- end
- tk
- end
-
- @OP.def_rules("'", '"') do
- |op, io|
- identify_string(op)
- end
-
- @OP.def_rules("`") do
- |op, io|
- if @lex_state == EXPR_FNAME
- @lex_state = EXPR_END
- Token(op)
- else
- identify_string(op)
- end
- end
-
- @OP.def_rules('?') do
- |op, io|
- if @lex_state == EXPR_END
- @lex_state = EXPR_BEG
- Token(TkQUESTION)
- else
- ch = getc
- if @lex_state == EXPR_ARG && ch =~ /\s/
- ungetc
- @lex_state = EXPR_BEG;
- Token(TkQUESTION)
- else
- if (ch == '\\')
- read_escape
- end
- @lex_state = EXPR_END
- Token(TkINTEGER)
- end
- end
- end
-
- @OP.def_rules("&", "&&", "|", "||") do
- |op, io|
- @lex_state = EXPR_BEG
- Token(op)
- end
-
- @OP.def_rules("+=", "-=", "*=", "**=",
- "&=", "|=", "^=", "<<=", ">>=", "||=", "&&=") do
- |op, io|
- @lex_state = EXPR_BEG
- op =~ /^(.*)=$/
- Token(TkOPASGN, $1)
- end
-
- @OP.def_rule("+@", proc{|op, io| @lex_state == EXPR_FNAME}) do
- |op, io|
- @lex_state = EXPR_ARG
- Token(op)
- end
-
- @OP.def_rule("-@", proc{|op, io| @lex_state == EXPR_FNAME}) do
- |op, io|
- @lex_state = EXPR_ARG
- Token(op)
- end
-
- @OP.def_rules("+", "-") do
- |op, io|
- catch(:RET) do
- if @lex_state == EXPR_ARG
- if @space_seen and peek(0) =~ /[0-9]/
- throw :RET, identify_number
- else
- @lex_state = EXPR_BEG
- end
- elsif @lex_state != EXPR_END and peek(0) =~ /[0-9]/
- throw :RET, identify_number
- else
- @lex_state = EXPR_BEG
- end
- Token(op)
- end
- end
-
- @OP.def_rule(".") do
- |op, io|
- @lex_state = EXPR_BEG
- if peek(0) =~ /[0-9]/
- ungetc
- identify_number
- else
- # for "obj.if" etc.
- @lex_state = EXPR_DOT
- Token(TkDOT)
- end
- end
-
- @OP.def_rules("..", "...") do
- |op, io|
- @lex_state = EXPR_BEG
- Token(op)
- end
-
- lex_int2
- end
-
- def lex_int2
- @OP.def_rules("]", "}", ")") do
- |op, io|
- @lex_state = EXPR_END
- @indent -= 1
- @indent_stack.pop
- Token(op)
- end
-
- @OP.def_rule(":") do
- |op, io|
- if @lex_state == EXPR_END || peek(0) =~ /\s/
- @lex_state = EXPR_BEG
- Token(TkCOLON)
- else
- @lex_state = EXPR_FNAME
- Token(TkSYMBEG)
- end
- end
-
- @OP.def_rule("::") do
- |op, io|
- if @lex_state == EXPR_BEG or @lex_state == EXPR_ARG && @space_seen
- @lex_state = EXPR_BEG
- Token(TkCOLON3)
- else
- @lex_state = EXPR_DOT
- Token(TkCOLON2)
- end
- end
-
- @OP.def_rule("/") do
- |op, io|
- if @lex_state == EXPR_BEG || @lex_state == EXPR_MID
- identify_string(op)
- elsif peek(0) == '='
- getc
- @lex_state = EXPR_BEG
- Token(TkOPASGN, "/") #/)
- elsif @lex_state == EXPR_ARG and @space_seen and peek(0) !~ /\s/
- identify_string(op)
- else
- @lex_state = EXPR_BEG
- Token("/") #/)
- end
- end
-
- @OP.def_rules("^") do
- |op, io|
- @lex_state = EXPR_BEG
- Token("^")
- end
-
- @OP.def_rules(",") do
- |op, io|
- @lex_state = EXPR_BEG
- Token(op)
- end
-
- @OP.def_rules(";") do
- |op, io|
- @lex_state = EXPR_BEG
- until (@indent_stack.empty? ||
- [TkLPAREN, TkLBRACK, TkLBRACE,
- TkfLPAREN, TkfLBRACK, TkfLBRACE].include?(@indent_stack.last))
- @indent_stack.pop
- end
- Token(op)
- end
-
- @OP.def_rule("~") do
- |op, io|
- @lex_state = EXPR_BEG
- Token("~")
- end
-
- @OP.def_rule("~@", proc{|op, io| @lex_state == EXPR_FNAME}) do
- |op, io|
- @lex_state = EXPR_BEG
- Token("~")
- end
-
- @OP.def_rule("(") do
- |op, io|
- @indent += 1
- if @lex_state == EXPR_BEG || @lex_state == EXPR_MID
- @lex_state = EXPR_BEG
- tk_c = TkfLPAREN
- else
- @lex_state = EXPR_BEG
- tk_c = TkLPAREN
- end
- @indent_stack.push tk_c
- Token(tk_c)
- end
-
- @OP.def_rule("[]", proc{|op, io| @lex_state == EXPR_FNAME}) do
- |op, io|
- @lex_state = EXPR_ARG
- Token("[]")
- end
-
- @OP.def_rule("[]=", proc{|op, io| @lex_state == EXPR_FNAME}) do
- |op, io|
- @lex_state = EXPR_ARG
- Token("[]=")
- end
-
- @OP.def_rule("[") do
- |op, io|
- @indent += 1
- if @lex_state == EXPR_FNAME
- tk_c = TkfLBRACK
- else
- if @lex_state == EXPR_BEG || @lex_state == EXPR_MID
- tk_c = TkLBRACK
- elsif @lex_state == EXPR_ARG && @space_seen
- tk_c = TkLBRACK
- else
- tk_c = TkfLBRACK
- end
- @lex_state = EXPR_BEG
- end
- @indent_stack.push tk_c
- Token(tk_c)
- end
-
- @OP.def_rule("{") do
- |op, io|
- @indent += 1
- if @lex_state != EXPR_END && @lex_state != EXPR_ARG
- tk_c = TkLBRACE
- else
- tk_c = TkfLBRACE
- end
- @lex_state = EXPR_BEG
- @indent_stack.push tk_c
- Token(tk_c)
- end
-
- @OP.def_rule('\\') do
- |op, io|
- if getc == "\n"
- @space_seen = true
- @continue = true
- Token(TkSPACE)
- else
- read_escape
- Token("\\")
- end
- end
-
- @OP.def_rule('%') do
- |op, io|
- if @lex_state == EXPR_BEG || @lex_state == EXPR_MID
- identify_quotation
- elsif peek(0) == '='
- getc
- Token(TkOPASGN, :%)
- elsif @lex_state == EXPR_ARG and @space_seen and peek(0) !~ /\s/
- identify_quotation
- else
- @lex_state = EXPR_BEG
- Token("%") #))
- end
- end
-
- @OP.def_rule('$') do
- |op, io|
- identify_gvar
- end
-
- @OP.def_rule('@') do
- |op, io|
- if peek(0) =~ /[\w@]/
- ungetc
- identify_identifier
- else
- Token("@")
- end
- end
-
- @OP.def_rule("") do
- |op, io|
- printf "MATCH: start %s: %s\n", op, io.inspect if RubyLex.debug?
- if peek(0) =~ /[0-9]/
- t = identify_number
- elsif peek(0) =~ /[^\x00-\/:-@\[-^`{-\x7F]/
- t = identify_identifier
- end
- printf "MATCH: end %s: %s\n", op, io.inspect if RubyLex.debug?
- t
- end
-
- p @OP if RubyLex.debug?
- end
-
- def identify_gvar
- @lex_state = EXPR_END
-
- case ch = getc
- when /[~_*$?!@\/\\;,=:<>".]/ #"
- Token(TkGVAR, "$" + ch)
- when "-"
- Token(TkGVAR, "$-" + getc)
- when "&", "`", "'", "+"
- Token(TkBACK_REF, "$"+ch)
- when /[1-9]/
- while getc =~ /[0-9]/; end
- ungetc
- Token(TkNTH_REF)
- when /\w/
- ungetc
- ungetc
- identify_identifier
- else
- ungetc
- Token("$")
- end
- end
-
- def identify_identifier
- token = ""
- if peek(0) =~ /[$@]/
- token.concat(c = getc)
- if c == "@" and peek(0) == "@"
- token.concat getc
- end
- end
-
- while (ch = getc) =~ /[^\x00-\/:-@\[-^`{-\x7F]/
- print ":", ch, ":" if RubyLex.debug?
- token.concat ch
- end
- ungetc
-
- if (ch == "!" || ch == "?") && token[0,1] =~ /\w/ && peek(0) != "="
- token.concat getc
- end
-
- # almost fix token
-
- case token
- when /^\$/
- return Token(TkGVAR, token)
- when /^\@\@/
- @lex_state = EXPR_END
- # p Token(TkCVAR, token)
- return Token(TkCVAR, token)
- when /^\@/
- @lex_state = EXPR_END
- return Token(TkIVAR, token)
- end
-
- if @lex_state != EXPR_DOT
- print token, "\n" if RubyLex.debug?
-
- token_c, *trans = TkReading2Token[token]
- if token_c
- # reserved word?
-
- if (@lex_state != EXPR_BEG &&
- @lex_state != EXPR_FNAME &&
- trans[1])
- # modifiers
- token_c = TkSymbol2Token[trans[1]]
- @lex_state = trans[0]
- else
- if @lex_state != EXPR_FNAME and peek(0) != ':'
- if ENINDENT_CLAUSE.include?(token)
- # check for ``class = val'' etc.
- valid = true
- case token
- when "class"
- valid = false unless peek_match?(/^\s*(<<|\w|::)/)
- when "def"
- valid = false if peek_match?(/^\s*(([+\-\/*&\|^]|<<|>>|\|\||\&\&)=|\&\&|\|\|)/)
- when "do"
- valid = false if peek_match?(/^\s*([+\-\/*]?=|\*|<|>|\&)/)
- when *ENINDENT_CLAUSE
- valid = false if peek_match?(/^\s*([+\-\/*]?=|\*|<|>|\&|\|)/)
- else
- # no nothing
- end
- if valid
- if token == "do"
- if ![TkFOR, TkWHILE, TkUNTIL].include?(@indent_stack.last)
- @indent += 1
- @indent_stack.push token_c
- end
- else
- @indent += 1
- @indent_stack.push token_c
- end
- end
-
- elsif DEINDENT_CLAUSE.include?(token)
- @indent -= 1
- @indent_stack.pop
- end
- @lex_state = trans[0]
- else
- @lex_state = EXPR_END
- end
- end
- return Token(token_c, token)
- end
- end
-
- if @lex_state == EXPR_FNAME
- @lex_state = EXPR_END
- if peek(0) == '='
- token.concat getc
- end
- elsif @lex_state == EXPR_BEG || @lex_state == EXPR_DOT
- @lex_state = EXPR_ARG
- else
- @lex_state = EXPR_END
- end
-
- if token[0, 1] =~ /[A-Z]/
- return Token(TkCONSTANT, token)
- elsif token[token.size - 1, 1] =~ /[!?]/
- return Token(TkFID, token)
- else
- return Token(TkIDENTIFIER, token)
- end
- end
-
- def identify_here_document
- ch = getc
- if ch == "-" || ch == "~"
- ch = getc
- indent = true
- end
- if /['"`]/ =~ ch
- lt = ch
- quoted = ""
- while (c = getc) && c != lt
- quoted.concat c
- end
- else
- lt = '"'
- quoted = ch.dup
- while (c = getc) && c =~ /\w/
- quoted.concat c
- end
- ungetc
- end
-
- ltback, @ltype = @ltype, lt
- reserve = []
- while ch = getc
- reserve.push ch
- if ch == "\\"
- reserve.push ch = getc
- elsif ch == "\n"
- break
- end
- end
-
- @here_header = false
-
- line = ""
- while ch = getc
- if ch == "\n"
- if line == quoted
- break
- end
- line = ""
- else
- line.concat ch unless indent && line == "" && /\s/ =~ ch
- if @ltype != "'" && ch == "#" && peek(0) == "{"
- identify_string_dvar
- end
- end
- end
-
- @here_header = true
- @here_readed.concat reserve
- while ch = reserve.pop
- ungetc ch
- end
-
- @ltype = ltback
- @lex_state = EXPR_END
- Token(Ltype2Token[lt])
+ line = @input.call
+ if @io.respond_to?(:check_termination)
+ return line # multiline
+ end
+ code = @line + (line.nil? ? '' : line)
+ code.gsub!(/\n*$/, '').concat("\n")
+ @tokens = Ripper.lex(code)
+ @continue = process_continue
+ @code_block_open = check_code_block(code)
+ @indent = process_nesting_level
+ @ltype = process_literal_type
+ line
end
- def identify_quotation
- ch = getc
- if lt = PERCENT_LTYPE[ch]
- ch = getc
- elsif ch =~ /\W/
- lt = "\""
- else
- RubyLex.fail SyntaxError, "unknown type of %string"
- end
- @quoted = ch unless @quoted = PERCENT_PAREN[ch]
- identify_string(lt, @quoted)
+ def process_continue
+ continued_bits = Ripper::EXPR_BEG | Ripper::EXPR_FNAME | Ripper::EXPR_DOT
+ # last token is always newline
+ if @tokens.size >= 2 and @tokens[-2][1] == :on_regexp_end
+ # end of regexp literal
+ return false
+ elsif @tokens.size >= 2 and @tokens[-2][1] == :on_semicolon
+ return false
+ elsif @tokens.size >= 2 and @tokens[-2][1] == :on_kw and (@tokens[-2][2] == 'begin' or @tokens[-2][2] == 'else')
+ return false
+ elsif !@tokens.empty? and @tokens.last[2] == "\\\n"
+ return true
+ elsif @tokens.size >= 2 and @tokens[-2][3].anybits?(continued_bits)
+ # end of literal except for regexp
+ return true
+ end
+ false
end
- def identify_number
- @lex_state = EXPR_END
-
- if peek(0) == "0" && peek(1) !~ /[.eE]/
- getc
- case peek(0)
- when /[xX]/
- ch = getc
- match = /[0-9a-fA-F_]/
- when /[bB]/
- ch = getc
- match = /[01_]/
- when /[oO]/
- ch = getc
- match = /[0-7_]/
- when /[dD]/
- ch = getc
- match = /[0-9_]/
- when /[0-7]/
- match = /[0-7_]/
- when /[89]/
- RubyLex.fail SyntaxError, "Invalid octal digit"
- else
- return Token(TkINTEGER)
- end
-
- len0 = true
- non_digit = false
- while ch = getc
- if match =~ ch
- if ch == "_"
- if non_digit
- RubyLex.fail SyntaxError, "trailing `#{ch}' in number"
- else
- non_digit = ch
- end
- else
- non_digit = false
- len0 = false
- end
- else
- ungetc
- if len0
- RubyLex.fail SyntaxError, "numeric literal without digits"
- end
- if non_digit
- RubyLex.fail SyntaxError, "trailing `#{non_digit}' in number"
- end
- break
- end
- end
- return Token(TkINTEGER)
- end
-
- type = TkINTEGER
- allow_point = true
- allow_e = true
- non_digit = false
- while ch = getc
- case ch
- when /[0-9]/
- non_digit = false
- when "_"
- non_digit = ch
- when allow_point && "."
- if non_digit
- RubyLex.fail SyntaxError, "trailing `#{non_digit}' in number"
- end
- type = TkFLOAT
- if peek(0) !~ /[0-9]/
- type = TkINTEGER
- ungetc
- break
- end
- allow_point = false
- when allow_e && "e", allow_e && "E"
- if non_digit
- RubyLex.fail SyntaxError, "trailing `#{non_digit}' in number"
- end
- type = TkFLOAT
- if peek(0) =~ /[+-]/
- getc
- end
- allow_e = false
- allow_point = false
- non_digit = ch
- else
- if non_digit
- RubyLex.fail SyntaxError, "trailing `#{non_digit}' in number"
- end
- ungetc
- break
- end
- end
- Token(type)
+ def check_code_block(code)
+ return true if @tokens.empty?
+ if @tokens.last[1] == :on_heredoc_beg
+ return true
+ end
+
+ begin # check if parser error are available
+ RubyVM::InstructionSequence.compile(code)
+ rescue SyntaxError => e
+ case e.message
+ when /unterminated (?:string|regexp) meets end of file/
+ # "unterminated regexp meets end of file"
+ #
+ # example:
+ # /
+ #
+ # "unterminated string meets end of file"
+ #
+ # example:
+ # '
+ return true
+ when /syntax error, unexpected end-of-input/
+ # "syntax error, unexpected end-of-input, expecting keyword_end"
+ #
+ # example:
+ # if ture
+ # hoge
+ # if false
+ # fuga
+ # end
+ return true
+ when /syntax error, unexpected keyword_end/
+ # "syntax error, unexpected keyword_end"
+ #
+ # example:
+ # if (
+ # end
+ #
+ # example:
+ # end
+ return false
+ when /unexpected tREGEXP_BEG/
+ # "syntax error, unexpected tREGEXP_BEG, expecting keyword_do or '{' or '('"
+ #
+ # example:
+ # method / f /
+ return false
+ end
+ end
+
+ last_lex_state = @tokens.last[3]
+ if last_lex_state.allbits?(Ripper::EXPR_BEG)
+ return false
+ elsif last_lex_state.allbits?(Ripper::EXPR_DOT)
+ return true
+ elsif last_lex_state.allbits?(Ripper::EXPR_CLASS)
+ return true
+ elsif last_lex_state.allbits?(Ripper::EXPR_FNAME)
+ return true
+ elsif last_lex_state.allbits?(Ripper::EXPR_VALUE)
+ return true
+ elsif last_lex_state.allbits?(Ripper::EXPR_ARG)
+ return false
+ end
+
+ false
end
- def identify_string(ltype, quoted = ltype)
- @ltype = ltype
- @quoted = quoted
- subtype = nil
- begin
- nest = 0
- while ch = getc
- if @quoted == ch and nest == 0
- break
- elsif @ltype != "'" && ch == "#" && peek(0) == "{"
- identify_string_dvar
- elsif @ltype != "'" && @ltype != "]" && @ltype != ":" and ch == "#"
- subtype = true
- elsif ch == '\\' and @ltype == "'" #'
- case ch = getc
- when "\\", "\n", "'"
- else
- ungetc
- end
- elsif ch == '\\' #'
- read_escape
- end
- if PERCENT_PAREN.values.include?(@quoted)
- if PERCENT_PAREN[ch] == @quoted
- nest += 1
- elsif ch == @quoted
- nest -= 1
- end
+ def process_nesting_level
+ @tokens.inject(0) { |indent, t|
+ case t[1]
+ when :on_lbracket, :on_lbrace, :on_lparen
+ indent += 1
+ when :on_rbracket, :on_rbrace, :on_rparen
+ indent -= 1
+ when :on_kw
+ case t[2]
+ when 'def', 'do', 'case', 'for', 'begin', 'class', 'module'
+ indent += 1
+ when 'if', 'unless', 'while', 'until', 'rescue'
+ # postfix if/unless/while/until/rescue must be Ripper::EXPR_LABEL
+ indent += 1 unless t[3].allbits?(Ripper::EXPR_LABEL)
+ when 'end'
+ indent -= 1
end
end
- if @ltype == "/"
- while /[imxoesun]/ =~ peek(0)
- getc
- end
- end
- if subtype
- Token(DLtype2Token[ltype])
- else
- Token(Ltype2Token[ltype])
- end
- ensure
- @ltype = nil
- @quoted = nil
- @lex_state = EXPR_END
- end
+ # percent literals are not indented
+ indent
+ }
end
- def identify_string_dvar
- begin
- getc
-
- reserve_continue = @continue
- reserve_ltype = @ltype
- reserve_indent = @indent
- reserve_indent_stack = @indent_stack
- reserve_state = @lex_state
- reserve_quoted = @quoted
-
- @ltype = nil
- @quoted = nil
- @indent = 0
- @indent_stack = []
- @lex_state = EXPR_BEG
-
- loop do
- @continue = false
- prompt
- tk = token
- if @ltype or @continue or @indent >= 0
- next
+ def check_string_literal
+ i = 0
+ start_token = []
+ end_type = []
+ while i < @tokens.size
+ t = @tokens[i]
+ case t[1]
+ when :on_tstring_beg
+ start_token << t
+ end_type << :on_tstring_end
+ when :on_regexp_beg
+ start_token << t
+ end_type << :on_regexp_end
+ when :on_symbeg
+ if (i + 1) < @tokens.size and @tokens[i + 1][1] != :on_ident
+ start_token << t
+ end_type << :on_tstring_end
end
- break if tk.kind_of?(TkRBRACE)
- end
- ensure
- @continue = reserve_continue
- @ltype = reserve_ltype
- @indent = reserve_indent
- @indent_stack = reserve_indent_stack
- @lex_state = reserve_state
- @quoted = reserve_quoted
- end
+ when :on_backtick
+ start_token << t
+ end_type << :on_tstring_end
+ when :on_qwords_beg, :on_words_beg, :on_qsymbols_beg, :on_symbols_beg
+ start_token << t
+ end_type << :on_tstring_end
+ when :on_heredoc_beg
+ start_token << t
+ end_type << :on_heredoc_end
+ when end_type.last
+ start_token.pop
+ end_type.pop
+ end
+ i += 1
+ end
+ start_token.last.nil? ? '' : start_token.last
end
- def identify_comment
- @ltype = "#"
-
- while ch = getc
- if ch == "\n"
- @ltype = nil
- ungetc
- break
- end
- end
- return Token(TkCOMMENT)
- end
-
- def read_escape
- case ch = getc
- when "\n", "\r", "\f"
- when "\\", "n", "t", "r", "f", "v", "a", "e", "b", "s" #"
- when /[0-7]/
- ungetc ch
- 3.times do
- case ch = getc
- when /[0-7]/
- when nil
- break
- else
- ungetc
- break
- end
- end
-
- when "x"
- 2.times do
- case ch = getc
- when /[0-9a-fA-F]/
- when nil
- break
- else
- ungetc
- break
- end
- end
-
- when "M"
- if (ch = getc) != '-'
- ungetc
- else
- if (ch = getc) == "\\" #"
- read_escape
- end
- end
-
- when "C", "c" #, "^"
- if ch == "C" and (ch = getc) != "-"
- ungetc
- elsif (ch = getc) == "\\" #"
- read_escape
+ def process_literal_type
+ start_token = check_string_literal
+ case start_token[1]
+ when :on_tstring_beg
+ case start_token[2]
+ when ?" then ?"
+ when /^%.$/ then ?"
+ when /^%Q.$/ then ?"
+ when ?' then ?'
+ when /^%q.$/ then ?'
+ end
+ when :on_regexp_beg then ?/
+ when :on_symbeg then ?:
+ when :on_backtick then ?`
+ when :on_qwords_beg then ?]
+ when :on_words_beg then ?]
+ when :on_qsymbols_beg then ?]
+ when :on_symbols_beg then ?]
+ when :on_heredoc_beg
+ start_token[2] =~ /<<[-~]?(['"`])[_a-zA-Z0-9]+\1/
+ case $1
+ when ?" then ?"
+ when ?' then ?'
+ when ?` then ?`
+ else ?"
end
else
- # other characters
+ nil
end
end
end
diff --git a/lib/rdoc/ri/paths.rb b/lib/rdoc/ri/paths.rb
index d41e610591..f76721d318 100644
--- a/lib/rdoc/ri/paths.rb
+++ b/lib/rdoc/ri/paths.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rdoc/ri'
+require 'rdoc/rdoc'
##
# The directories where ri data lives. Paths can be enumerated via ::each, or
diff --git a/lib/readline.rb b/lib/readline.rb
new file mode 100644
index 0000000000..690441e05c
--- /dev/null
+++ b/lib/readline.rb
@@ -0,0 +1,6 @@
+begin
+ require 'readline.so'
+rescue LoadError
+ require 'reline'
+ Readline = Reline
+end
diff --git a/lib/reline.rb b/lib/reline.rb
new file mode 100644
index 0000000000..b1d7718b7e
--- /dev/null
+++ b/lib/reline.rb
@@ -0,0 +1,196 @@
+require 'io/console'
+require 'reline/version'
+require 'reline/config'
+require 'reline/key_actor'
+require 'reline/key_stroke'
+require 'reline/line_editor'
+
+module Reline
+ extend self
+ FILENAME_COMPLETION_PROC = nil
+ USERNAME_COMPLETION_PROC = nil
+ HISTORY = Array.new
+
+ if RUBY_PLATFORM =~ /mswin|mingw/
+ require 'Win32API'
+ IS_WINDOWS = true
+ else
+ IS_WINDOWS = false
+ end
+
+ CursorPos = Struct.new(:x, :y)
+
+ class << self
+ attr_accessor :basic_quote_characters
+ attr_accessor :completer_quote_characters
+ attr_accessor :completer_word_break_characters
+ attr_reader :completion_append_character
+ attr_accessor :completion_case_fold
+ attr_accessor :filename_quote_characters
+ attr_writer :input
+ attr_writer :output
+ end
+
+ @@ambiguous_width = nil
+ @@config = nil
+
+ @basic_quote_characters = '"\''
+ @completer_quote_characters
+ @completer_word_break_characters = @basic_word_break_characters.dup
+ @completion_append_character
+ def self.completion_append_character=(val)
+ if val.nil?
+ @completion_append_character = nil
+ elsif val.size == 1
+ @completion_append_character = val
+ elsif val.size > 1
+ @completion_append_character = val[0]
+ else
+ @completion_append_character = val
+ end
+ end
+ @completion_case_fold
+ @filename_quote_characters
+
+ @@basic_word_break_characters = " \t\n`><=;|&{("
+ def self.basic_word_break_characters
+ @@basic_word_break_characters
+ end
+ def self.basic_word_break_characters=(v)
+ @@basic_word_break_characters = v
+ end
+
+ @@completion_proc = nil
+ def self.completion_proc
+ @@completion_proc
+ end
+ def self.completion_proc=(p)
+ @@completion_proc = p
+ end
+
+ @@dig_perfect_match_proc = nil
+ def self.dig_perfect_match_proc
+ @@dig_perfect_match_proc
+ end
+ def self.dig_perfect_match_proc=(p)
+ @@dig_perfect_match_proc = p
+ end
+
+ if IS_WINDOWS
+ require 'reline/windows'
+ else
+ require 'reline/ansi'
+ end
+
+ def retrieve_completion_block(line, byte_pointer)
+ break_regexp = /[#{Regexp.escape(@@basic_word_break_characters)}]/
+ before_pointer = line.byteslice(0, byte_pointer)
+ break_point = before_pointer.rindex(break_regexp)
+ if break_point
+ preposing = before_pointer[0..(break_point)]
+ block = before_pointer[(break_point + 1)..-1]
+ else
+ preposing = ''
+ block = before_pointer
+ end
+ postposing = line.byteslice(byte_pointer, line.bytesize)
+ [preposing, block, postposing]
+ end
+
+ def readmultiline(prompt = '', add_hist = false, &confirm_multiline_termination)
+ if block_given?
+ inner_readline(prompt, add_hist, true, &confirm_multiline_termination)
+ else
+ inner_readline(prompt, add_hist, true)
+ end
+
+ if add_hist and @line_editor.whole_buffer and @line_editor.whole_buffer.chomp.size > 0
+ Reline::HISTORY << @line_editor.whole_buffer
+ end
+
+ @line_editor.whole_buffer
+ end
+
+ def readline(prompt = '', add_hist = false)
+ inner_readline(prompt, add_hist, false)
+
+ if add_hist and @line_editor.line and @line_editor.line.chomp.size > 0
+ Reline::HISTORY << @line_editor.line.chomp
+ end
+
+ @line_editor.line
+ end
+
+ def inner_readline(prompt, add_hist, multiline, &confirm_multiline_termination)
+ if @@config.nil?
+ @@config = Reline::Config.new
+ @@config.read
+ end
+ otio = prep
+
+ may_req_ambiguous_char_width
+ @line_editor = Reline::LineEditor.new(@@config, prompt)
+ if multiline
+ @line_editor.multiline_on
+ if block_given?
+ @line_editor.confirm_multiline_termination_proc = confirm_multiline_termination
+ end
+ end
+ @line_editor.completion_proc = @@completion_proc
+ @line_editor.dig_perfect_match_proc = @@dig_perfect_match_proc
+ @line_editor.retrieve_completion_block = method(:retrieve_completion_block)
+ @line_editor.rerender
+
+ if IS_WINDOWS
+ config = {
+ key_mapping: {
+ [224, 72] => :ed_prev_history, # ↑
+ [224, 80] => :ed_next_history, # ↓
+ [224, 77] => :ed_next_char, # →
+ [224, 75] => :ed_prev_char # ←
+ }
+ }
+ else
+ config = {
+ key_mapping: {
+ [27, 91, 65] => :ed_prev_history, # ↑
+ [27, 91, 66] => :ed_next_history, # ↓
+ [27, 91, 67] => :ed_next_char, # →
+ [27, 91, 68] => :ed_prev_char # ←
+ }
+ }
+ end
+
+ key_stroke = Reline::KeyStroke.new(config)
+ begin
+ while c = getc
+ key_stroke.input_to!(c)&.then { |inputs|
+ inputs.each { |c|
+ @line_editor.input_key(c)
+ @line_editor.rerender
+ }
+ }
+ break if @line_editor.finished?
+ end
+ Reline.move_cursor_column(0)
+ rescue StandardError => e
+ deprep(otio)
+ raise e
+ end
+
+ deprep(otio)
+ end
+
+ def may_req_ambiguous_char_width
+ return if @@ambiguous_width
+ Reline.move_cursor_column(0)
+ print "\u{25bd}"
+ @@ambiguous_width = Reline.cursor_pos.x
+ Reline.move_cursor_column(0)
+ Reline.erase_after_cursor
+ end
+
+ def self.ambiguous_width
+ @@ambiguous_width
+ end
+end
diff --git a/lib/reline/ansi.rb b/lib/reline/ansi.rb
new file mode 100644
index 0000000000..f34c4207e5
--- /dev/null
+++ b/lib/reline/ansi.rb
@@ -0,0 +1,87 @@
+module Reline
+ def getc
+ c = nil
+ until c
+ return nil if @line_editor.finished?
+ result = select([$stdin], [], [], 0.1)
+ next if result.nil?
+ c = $stdin.read(1)
+ end
+ c.ord
+ end
+
+ def self.get_screen_size
+ $stdin.winsize
+ end
+
+ def self.set_screen_size(rows, columns)
+ $stdin.winsize = [rows, columns]
+ self
+ end
+
+ def self.cursor_pos
+ res = ''
+ $stdin.raw do |stdin|
+ $stdout << "\e[6n"
+ $stdout.flush
+ while (c = stdin.getc) != 'R'
+ res << c if c
+ end
+ end
+ m = res.match(/(?<row>\d+);(?<column>\d+)/)
+ CursorPos.new(m[:column].to_i - 1, m[:row].to_i - 1)
+ end
+
+ def self.move_cursor_column(x)
+ print "\e[#{x + 1}G"
+ end
+
+ def self.move_cursor_up(x)
+ if x > 0
+ print "\e[#{x}A" if x > 0
+ elsif x < 0
+ move_cursor_down(-x)
+ end
+ end
+
+ def self.move_cursor_down(x)
+ if x > 0
+ print "\e[#{x}B" if x > 0
+ elsif x < 0
+ move_cursor_up(-x)
+ end
+ end
+
+ def self.erase_after_cursor
+ print "\e[K"
+ end
+
+ def self.scroll_down(x)
+ return if x.zero?
+ print "\e[#{x}S"
+ end
+
+ def self.clear_screen
+ print "\e[2J"
+ print "\e[1;1H"
+ end
+
+ def prep
+ int_handle = Signal.trap('INT', 'IGNORE')
+ otio = `stty -g`.chomp
+ setting = ' -echo -icrnl cbreak'
+ if (`stty -a`.scan(/-parenb\b/).first == '-parenb')
+ setting << ' pass8'
+ end
+ setting << ' -ixoff'
+ `stty #{setting}`
+ Signal.trap('INT', int_handle)
+ otio
+ end
+
+ def deprep(otio)
+ int_handle = Signal.trap('INT', 'IGNORE')
+ `stty #{otio}`
+ Signal.trap('INT', int_handle)
+ end
+end
diff --git a/lib/reline/config.rb b/lib/reline/config.rb
new file mode 100644
index 0000000000..0800dfd30f
--- /dev/null
+++ b/lib/reline/config.rb
@@ -0,0 +1,235 @@
+require 'pathname'
+
+class Reline::Config
+ DEFAULT_PATH = Pathname.new(Dir.home).join('.inputrc')
+
+ def initialize
+ @skip_section = nil
+ @if_stack = []
+ @editing_mode_label = :emacs
+ @keymap_label = :emacs
+ @key_actors = {}
+ @key_actors[:emacs] = Reline::KeyActor::Emacs.new
+ @key_actors[:vi_insert] = Reline::KeyActor::ViInsert.new
+ @key_actors[:vi_command] = Reline::KeyActor::ViCommand.new
+ end
+
+ def reset
+ if editing_mode_is?(:vi_command)
+ @editing_mode_label = :vi_insert
+ end
+ end
+
+ def editing_mode
+ @key_actors[@editing_mode_label]
+ end
+
+ def editing_mode=(val)
+ @editing_mode_label = val
+ end
+
+ def editing_mode_is?(*val)
+ (val.respond_to?(:any?) ? val : [val]).any?(@editing_mode_label)
+ end
+
+ def keymap
+ @key_actors[@keymap_label]
+ end
+
+ def read(file = DEFAULT_PATH)
+ begin
+ if file.respond_to?(:readlines)
+ lines = file.readlines
+ else
+ File.open(file, 'rt') do |f|
+ lines = f.readlines
+ end
+ end
+ rescue Errno::ENOENT
+ $stderr.puts "no such file #{file}"
+ return nil
+ end
+
+ read_lines(lines)
+ self
+ end
+
+ def read_lines(lines)
+ lines.each do |line|
+ line = line.chomp.gsub(/^\s*/, '')
+ if line[0, 1] == '$'
+ handle_directive(line[1..-1])
+ next
+ end
+
+ next if @skip_section
+
+ if line.match(/^set +([^ ]+) +([^ ]+)/i)
+ var, value = $1.downcase, $2.downcase
+ bind_variable(var, value)
+ next
+ end
+
+ if line =~ /\s*(.*)\s*:\s*(.*)\s*$/
+ key, func_name = $1, $2
+ bind_key(key, func_name)
+ end
+ end
+ end
+
+ def handle_directive(directive)
+ directive, args = directive.split(' ')
+ case directive
+ when 'if'
+ condition = false
+ case args # TODO: variables
+ when 'mode'
+ when 'term'
+ when 'version'
+ else # application name
+ condition = true if args == 'Ruby'
+ end
+ unless @skip_section.nil?
+ @if_stack << @skip_section
+ end
+ @skip_section = !condition
+ when 'else'
+ @skip_section = !@skip_section
+ when 'endif'
+ @skip_section = nil
+ unless @if_stack.empty?
+ @skip_section = @if_stack.pop
+ end
+ when 'include'
+ read(args)
+ end
+ end
+
+ def bind_variable(name, value)
+ case name
+ when %w{
+ bind-tty-special-chars
+ blink-matching-paren
+ byte-oriented
+ completion-ignore-case
+ convert-meta
+ disable-completion
+ enable-keypad
+ expand-tilde
+ history-preserve-point
+ horizontal-scroll-mode
+ input-meta
+ mark-directories
+ mark-modified-lines
+ mark-symlinked-directories
+ match-hidden-files
+ meta-flag
+ output-meta
+ page-completions
+ prefer-visible-bell
+ print-completions-horizontally
+ show-all-if-ambiguous
+ show-all-if-unmodified
+ visible-stats
+ } then
+ variable_name = :"@#{name.tr(?-, ?_)}"
+ instance_variable_set(variable_name, value.nil? || value == '1' || value == 'on')
+ when 'bell-style'
+ @bell_style =
+ case value
+ when 'none', 'off'
+ :none
+ when 'audible', 'on'
+ :audible
+ when 'visible'
+ :visible
+ else
+ :audible
+ end
+ when 'comment-begin'
+ @comment_begin = value.dup
+ when 'completion-query-items'
+ @completion_query_items = value.to_i
+ when 'isearch-terminators'
+ @isearch_terminators = instance_eval(value)
+ when 'editing-mode'
+ case value
+ when 'emacs'
+ @editing_mode_label = :emacs
+ @keymap_label = :emacs
+ when 'vi'
+ @editing_mode_label = :vi_insert
+ @keymap_label = :vi_insert
+ end
+ when 'keymap'
+ case value
+ when 'emacs', 'emacs-standard', 'emacs-meta', 'emacs-ctlx'
+ @keymap_label = :emacs
+ when 'vi', 'vi-move', 'vi-command'
+ @keymap_label = :vi_command
+ when 'vi-insert'
+ @keymap_label = :vi_insert
+ end
+ end
+ end
+
+ def bind_key(key, func_name)
+ if key =~ /"(.*)"/
+ keyseq = parse_keyseq($1).force_encoding('ASCII-8BIT')
+ else
+ keyseq = nil
+ end
+ if func_name =~ /"(.*)"/
+ func = parse_keyseq($1).force_encoding('ASCII-8BIT')
+ else
+ func = func_name.to_sym # It must be macro.
+ end
+ [keyseq, func]
+ end
+
+ def key_notation_to_char(notation)
+ case notation
+ when /\\C-([A-Za-z_])/
+ (1 + $1.downcase.ord - ?a.ord).chr('ASCII-8BIT')
+ when /\\M-([0-9A-Za-z_])/
+ modified_key = $1
+ code =
+ case $1
+ when /[0-9]/
+ ?\M-0.bytes.first + (modified_key.ord - ?0.ord)
+ when /[A-Z]/
+ ?\M-A.bytes.first + (modified_key.ord - ?A.ord)
+ when /[a-z]/
+ ?\M-a.bytes.first + (modified_key.ord - ?a.ord)
+ end
+ code.chr('ASCII-8BIT')
+ when /\\C-M-[A-Za-z_]/, /\\M-C-[A-Za-z_]/
+ # 129 M-^A
+ when /\\(\d{1,3})/ then $1.to_i(8).chr # octal
+ when /\\x(\h{1,2})/ then $1.to_i(16).chr # hexadecimal
+ when "\\e" then ?\e
+ when "\\\\" then ?\\
+ when "\\\"" then ?"
+ when "\\'" then ?'
+ when "\\a" then ?\a
+ when "\\b" then ?\b
+ when "\\d" then ?\d
+ when "\\f" then ?\f
+ when "\\n" then ?\n
+ when "\\r" then ?\r
+ when "\\t" then ?\t
+ when "\\v" then ?\v
+ else notation
+ end
+ end
+
+ def parse_keyseq(str)
+ # TODO: Control- and Meta-
+ ret = String.new(encoding: 'ASCII-8BIT')
+ while str =~ /(\\C-[A-Za-z_]|\\M-[0-9A-Za-z_]|\\C-M-[A-Za-z_]|\\M-C-[A-Za-z_]|\\e|\\\\|\\"|\\'|\\a|\\b|\\d|\\f|\\n|\\r|\\t|\\v|\\\d{1,3}|\\x\h{1,2}|.)/
+ ret << key_notation_to_char($&)
+ str = $'
+ end
+ ret
+ end
+end
diff --git a/lib/reline/key_actor.rb b/lib/reline/key_actor.rb
new file mode 100644
index 0000000000..ebe09d2009
--- /dev/null
+++ b/lib/reline/key_actor.rb
@@ -0,0 +1,7 @@
+module Reline::KeyActor
+end
+
+require 'reline/key_actor/base'
+require 'reline/key_actor/emacs'
+require 'reline/key_actor/vi_command'
+require 'reline/key_actor/vi_insert'
diff --git a/lib/reline/key_actor/base.rb b/lib/reline/key_actor/base.rb
new file mode 100644
index 0000000000..f4abac55d4
--- /dev/null
+++ b/lib/reline/key_actor/base.rb
@@ -0,0 +1,7 @@
+class Reline::KeyActor::Base
+ MAPPING = Array.new(256)
+
+ def get_method(key)
+ self.class::MAPPING[key]
+ end
+end
diff --git a/lib/reline/key_actor/emacs.rb b/lib/reline/key_actor/emacs.rb
new file mode 100644
index 0000000000..0836cd340c
--- /dev/null
+++ b/lib/reline/key_actor/emacs.rb
@@ -0,0 +1,518 @@
+class Reline::KeyActor::Emacs < Reline::KeyActor::Base
+ MAPPING = [
+ # 0 ^@
+ :em_set_mark,
+ # 1 ^A
+ :ed_move_to_beg,
+ # 2 ^B
+ :ed_prev_char,
+ # 3 ^C
+ :ed_ignore,
+ # 4 ^D
+ :em_delete_or_list,
+ # 5 ^E
+ :ed_move_to_end,
+ # 6 ^F
+ :ed_next_char,
+ # 7 ^G
+ :ed_unassigned,
+ # 8 ^H
+ :em_delete_prev_char,
+ # 9 ^I
+ :ed_unassigned,
+ # 10 ^J
+ :ed_newline,
+ # 11 ^K
+ :ed_kill_line,
+ # 12 ^L
+ :ed_clear_screen,
+ # 13 ^M
+ :ed_newline,
+ # 14 ^N
+ :ed_next_history,
+ # 15 ^O
+ :ed_ignore,
+ # 16 ^P
+ :ed_prev_history,
+ # 17 ^Q
+ :ed_ignore,
+ # 18 ^R
+ :ed_redisplay,
+ # 19 ^S
+ :ed_ignore,
+ # 20 ^T
+ :ed_transpose_chars,
+ # 21 ^U
+ :em_kill_line,
+ # 22 ^V
+ :ed_quoted_insert,
+ # 23 ^W
+ :em_kill_region,
+ # 24 ^X
+ :ed_sequence_lead_in,
+ # 25 ^Y
+ :em_yank,
+ # 26 ^Z
+ :ed_ignore,
+ # 27 ^[
+ :em_meta_next,
+ # 28 ^\
+ :ed_ignore,
+ # 29 ^]
+ :ed_ignore,
+ # 30 ^^
+ :ed_unassigned,
+ # 31 ^_
+ :ed_unassigned,
+ # 32 SPACE
+ :ed_insert,
+ # 33 !
+ :ed_insert,
+ # 34 "
+ :ed_insert,
+ # 35 #
+ :ed_insert,
+ # 36 $
+ :ed_insert,
+ # 37 %
+ :ed_insert,
+ # 38 &
+ :ed_insert,
+ # 39 '
+ :ed_insert,
+ # 40 (
+ :ed_insert,
+ # 41 )
+ :ed_insert,
+ # 42 *
+ :ed_insert,
+ # 43 +
+ :ed_insert,
+ # 44 ,
+ :ed_insert,
+ # 45 -
+ :ed_insert,
+ # 46 .
+ :ed_insert,
+ # 47 /
+ :ed_insert,
+ # 48 0
+ :ed_digit,
+ # 49 1
+ :ed_digit,
+ # 50 2
+ :ed_digit,
+ # 51 3
+ :ed_digit,
+ # 52 4
+ :ed_digit,
+ # 53 5
+ :ed_digit,
+ # 54 6
+ :ed_digit,
+ # 55 7
+ :ed_digit,
+ # 56 8
+ :ed_digit,
+ # 57 9
+ :ed_digit,
+ # 58 :
+ :ed_insert,
+ # 59 ;
+ :ed_insert,
+ # 60 <
+ :ed_insert,
+ # 61 =
+ :ed_insert,
+ # 62 >
+ :ed_insert,
+ # 63 ?
+ :ed_insert,
+ # 64 @
+ :ed_insert,
+ # 65 A
+ :ed_insert,
+ # 66 B
+ :ed_insert,
+ # 67 C
+ :ed_insert,
+ # 68 D
+ :ed_insert,
+ # 69 E
+ :ed_insert,
+ # 70 F
+ :ed_insert,
+ # 71 G
+ :ed_insert,
+ # 72 H
+ :ed_insert,
+ # 73 I
+ :ed_insert,
+ # 74 J
+ :ed_insert,
+ # 75 K
+ :ed_insert,
+ # 76 L
+ :ed_insert,
+ # 77 M
+ :ed_insert,
+ # 78 N
+ :ed_insert,
+ # 79 O
+ :ed_insert,
+ # 80 P
+ :ed_insert,
+ # 81 Q
+ :ed_insert,
+ # 82 R
+ :ed_insert,
+ # 83 S
+ :ed_insert,
+ # 84 T
+ :ed_insert,
+ # 85 U
+ :ed_insert,
+ # 86 V
+ :ed_insert,
+ # 87 W
+ :ed_insert,
+ # 88 X
+ :ed_insert,
+ # 89 Y
+ :ed_insert,
+ # 90 Z
+ :ed_insert,
+ # 91 [
+ :ed_insert,
+ # 92 \
+ :ed_insert,
+ # 93 ]
+ :ed_insert,
+ # 94 ^
+ :ed_insert,
+ # 95 _
+ :ed_insert,
+ # 96 `
+ :ed_insert,
+ # 97 a
+ :ed_insert,
+ # 98 b
+ :ed_insert,
+ # 99 c
+ :ed_insert,
+ # 100 d
+ :ed_insert,
+ # 101 e
+ :ed_insert,
+ # 102 f
+ :ed_insert,
+ # 103 g
+ :ed_insert,
+ # 104 h
+ :ed_insert,
+ # 105 i
+ :ed_insert,
+ # 106 j
+ :ed_insert,
+ # 107 k
+ :ed_insert,
+ # 108 l
+ :ed_insert,
+ # 109 m
+ :ed_insert,
+ # 110 n
+ :ed_insert,
+ # 111 o
+ :ed_insert,
+ # 112 p
+ :ed_insert,
+ # 113 q
+ :ed_insert,
+ # 114 r
+ :ed_insert,
+ # 115 s
+ :ed_insert,
+ # 116 t
+ :ed_insert,
+ # 117 u
+ :ed_insert,
+ # 118 v
+ :ed_insert,
+ # 119 w
+ :ed_insert,
+ # 120 x
+ :ed_insert,
+ # 121 y
+ :ed_insert,
+ # 122 z
+ :ed_insert,
+ # 123 {
+ :ed_insert,
+ # 124 |
+ :ed_insert,
+ # 125 }
+ :ed_insert,
+ # 126 ~
+ :ed_insert,
+ # 127 ^?
+ :em_delete_prev_char,
+ # 128 M-^@
+ :ed_unassigned,
+ # 129 M-^A
+ :ed_unassigned,
+ # 130 M-^B
+ :ed_unassigned,
+ # 131 M-^C
+ :ed_unassigned,
+ # 132 M-^D
+ :ed_unassigned,
+ # 133 M-^E
+ :ed_unassigned,
+ # 134 M-^F
+ :ed_unassigned,
+ # 135 M-^G
+ :ed_unassigned,
+ # 136 M-^H
+ :ed_delete_prev_word,
+ # 137 M-^I
+ :ed_unassigned,
+ # 138 M-^J
+ :ed_unassigned,
+ # 139 M-^K
+ :ed_unassigned,
+ # 140 M-^L
+ :ed_clear_screen,
+ # 141 M-^M
+ :ed_unassigned,
+ # 142 M-^N
+ :ed_unassigned,
+ # 143 M-^O
+ :ed_unassigned,
+ # 144 M-^P
+ :ed_unassigned,
+ # 145 M-^Q
+ :ed_unassigned,
+ # 146 M-^R
+ :ed_unassigned,
+ # 147 M-^S
+ :ed_unassigned,
+ # 148 M-^T
+ :ed_unassigned,
+ # 149 M-^U
+ :ed_unassigned,
+ # 150 M-^V
+ :ed_unassigned,
+ # 151 M-^W
+ :ed_unassigned,
+ # 152 M-^X
+ :ed_unassigned,
+ # 153 M-^Y
+ :ed_unassigned,
+ # 154 M-^Z
+ :ed_unassigned,
+ # 155 M-^[
+ :ed_unassigned,
+ # 156 M-^\
+ :ed_unassigned,
+ # 157 M-^]
+ :ed_unassigned,
+ # 158 M-^^
+ :ed_unassigned,
+ # 159 M-^_
+ :em_copy_prev_word,
+ # 160 M-SPACE
+ :ed_unassigned,
+ # 161 M-!
+ :ed_unassigned,
+ # 162 M-"
+ :ed_unassigned,
+ # 163 M-#
+ :ed_unassigned,
+ # 164 M-$
+ :ed_unassigned,
+ # 165 M-%
+ :ed_unassigned,
+ # 166 M-&
+ :ed_unassigned,
+ # 167 M-'
+ :ed_unassigned,
+ # 168 M-(
+ :ed_unassigned,
+ # 169 M-)
+ :ed_unassigned,
+ # 170 M-*
+ :ed_unassigned,
+ # 171 M-+
+ :ed_unassigned,
+ # 172 M-,
+ :ed_unassigned,
+ # 173 M--
+ :ed_unassigned,
+ # 174 M-.
+ :ed_unassigned,
+ # 175 M-/
+ :ed_unassigned,
+ # 176 M-0
+ :ed_argument_digit,
+ # 177 M-1
+ :ed_argument_digit,
+ # 178 M-2
+ :ed_argument_digit,
+ # 179 M-3
+ :ed_argument_digit,
+ # 180 M-4
+ :ed_argument_digit,
+ # 181 M-5
+ :ed_argument_digit,
+ # 182 M-6
+ :ed_argument_digit,
+ # 183 M-7
+ :ed_argument_digit,
+ # 184 M-8
+ :ed_argument_digit,
+ # 185 M-9
+ :ed_argument_digit,
+ # 186 M-:
+ :ed_unassigned,
+ # 187 M-;
+ :ed_unassigned,
+ # 188 M-<
+ :ed_unassigned,
+ # 189 M-=
+ :ed_unassigned,
+ # 190 M->
+ :ed_unassigned,
+ # 191 M-?
+ :ed_unassigned,
+ # 192 M-@
+ :ed_unassigned,
+ # 193 M-A
+ :ed_unassigned,
+ # 194 M-B
+ :ed_prev_word,
+ # 195 M-C
+ :em_capitol_case,
+ # 196 M-D
+ :em_delete_next_word,
+ # 197 M-E
+ :ed_unassigned,
+ # 198 M-F
+ :em_next_word,
+ # 199 M-G
+ :ed_unassigned,
+ # 200 M-H
+ :ed_unassigned,
+ # 201 M-I
+ :ed_unassigned,
+ # 202 M-J
+ :ed_unassigned,
+ # 203 M-K
+ :ed_unassigned,
+ # 204 M-L
+ :em_lower_case,
+ # 205 M-M
+ :ed_unassigned,
+ # 206 M-N
+ :ed_search_next_history,
+ # 207 M-O
+ :ed_sequence_lead_in,
+ # 208 M-P
+ :ed_search_prev_history,
+ # 209 M-Q
+ :ed_unassigned,
+ # 210 M-R
+ :ed_unassigned,
+ # 211 M-S
+ :ed_unassigned,
+ # 212 M-T
+ :ed_unassigned,
+ # 213 M-U
+ :em_upper_case,
+ # 214 M-V
+ :ed_unassigned,
+ # 215 M-W
+ :em_copy_region,
+ # 216 M-X
+ :ed_command,
+ # 217 M-Y
+ :ed_unassigned,
+ # 218 M-Z
+ :ed_unassigned,
+ # 219 M-[
+ :ed_sequence_lead_in,
+ # 220 M-\
+ :ed_unassigned,
+ # 221 M-]
+ :ed_unassigned,
+ # 222 M-^
+ :ed_unassigned,
+ # 223 M-_
+ :ed_unassigned,
+ # 223 M-`
+ :ed_unassigned,
+ # 224 M-a
+ :ed_unassigned,
+ # 225 M-b
+ :ed_prev_word,
+ # 226 M-c
+ :em_capitol_case,
+ # 227 M-d
+ :em_delete_next_word,
+ # 228 M-e
+ :ed_unassigned,
+ # 229 M-f
+ :em_next_word,
+ # 230 M-g
+ :ed_unassigned,
+ # 231 M-h
+ :ed_unassigned,
+ # 232 M-i
+ :ed_unassigned,
+ # 233 M-j
+ :ed_unassigned,
+ # 234 M-k
+ :ed_unassigned,
+ # 235 M-l
+ :em_lower_case,
+ # 236 M-m
+ :ed_unassigned,
+ # 237 M-n
+ :ed_search_next_history,
+ # 238 M-o
+ :ed_unassigned,
+ # 239 M-p
+ :ed_search_prev_history,
+ # 240 M-q
+ :ed_unassigned,
+ # 241 M-r
+ :ed_unassigned,
+ # 242 M-s
+ :ed_unassigned,
+ # 243 M-t
+ :ed_unassigned,
+ # 244 M-u
+ :em_upper_case,
+ # 245 M-v
+ :ed_unassigned,
+ # 246 M-w
+ :em_copy_region,
+ # 247 M-x
+ :ed_command,
+ # 248 M-y
+ :ed_unassigned,
+ # 249 M-z
+ :ed_unassigned,
+ # 250 M-{
+ :ed_unassigned,
+ # 251 M-|
+ :ed_unassigned,
+ # 252 M-}
+ :ed_unassigned,
+ # 253 M-~
+ :ed_unassigned,
+ # 254 M-^?
+ :ed_delete_prev_word
+ # 255
+ # EOF
+ ]
+end
diff --git a/lib/reline/key_actor/vi_command.rb b/lib/reline/key_actor/vi_command.rb
new file mode 100644
index 0000000000..724f459011
--- /dev/null
+++ b/lib/reline/key_actor/vi_command.rb
@@ -0,0 +1,519 @@
+class Reline::KeyActor::ViCommand < Reline::KeyActor::Base
+ MAPPING = [
+ # 0 ^@
+ :ed_unassigned,
+ # 1 ^A
+ :ed_move_to_beg,
+ # 2 ^B
+ :ed_unassigned,
+ # 3 ^C
+ :ed_ignore,
+ # 4 ^D
+ :vi_end_of_transmission,
+ # 5 ^E
+ :ed_move_to_end,
+ # 6 ^F
+ :ed_unassigned,
+ # 7 ^G
+ :ed_unassigned,
+ # 8 ^H
+ :ed_delete_prev_char,
+ # 9 ^I
+ :ed_unassigned,
+ # 10 ^J
+ :ed_newline,
+ # 11 ^K
+ :ed_kill_line,
+ # 12 ^L
+ :ed_clear_screen,
+ # 13 ^M
+ :ed_newline,
+ # 14 ^N
+ :ed_next_history,
+ # 15 ^O
+ :ed_ignore,
+ # 16 ^P
+ :ed_prev_history,
+ # 17 ^Q
+ :ed_ignore,
+ # 18 ^R
+ :ed_redisplay,
+ # 19 ^S
+ :ed_ignore,
+ # 20 ^T
+ :ed_unassigned,
+ # 21 ^U
+ :vi_kill_line_prev,
+ # 22 ^V
+ :ed_quoted_insert,
+ # 23 ^W
+ :ed_delete_prev_word,
+ # 24 ^X
+ :ed_unassigned,
+ # 25 ^Y
+ :ed_unassigned,
+ # 26 ^Z
+ :ed_unassigned,
+ # 27 ^[
+ :em_meta_next,
+ # 28 ^\
+ :ed_ignore,
+ # 29 ^]
+ :ed_unassigned,
+ # 30 ^^
+ :ed_unassigned,
+ # 31 ^_
+ :ed_unassigned,
+ # 32 SPACE
+ :ed_next_char,
+ # 33 !
+ :ed_unassigned,
+ # 34 "
+ :ed_unassigned,
+ # 35 #
+ :vi_comment_out,
+ # 36 $
+ :ed_move_to_end,
+ # 37 %
+ :vi_match,
+ # 38 &
+ :ed_unassigned,
+ # 39 '
+ :ed_unassigned,
+ # 40 (
+ :ed_unassigned,
+ # 41 )
+ :ed_unassigned,
+ # 42 *
+ :ed_unassigned,
+ # 43 +
+ :ed_next_history,
+ # 44 ,
+ :vi_repeat_prev_char,
+ # 45 -
+ :ed_prev_history,
+ # 46 .
+ :vi_redo,
+ # 47 /
+ :vi_search_prev,
+ # 48 0
+ :vi_zero,
+ # 49 1
+ :ed_argument_digit,
+ # 50 2
+ :ed_argument_digit,
+ # 51 3
+ :ed_argument_digit,
+ # 52 4
+ :ed_argument_digit,
+ # 53 5
+ :ed_argument_digit,
+ # 54 6
+ :ed_argument_digit,
+ # 55 7
+ :ed_argument_digit,
+ # 56 8
+ :ed_argument_digit,
+ # 57 9
+ :ed_argument_digit,
+ # 58 :
+ :ed_command,
+ # 59 ;
+ :vi_repeat_next_char,
+ # 60 <
+ :ed_unassigned,
+ # 61 =
+ :ed_unassigned,
+ # 62 >
+ :ed_unassigned,
+ # 63 ?
+ :vi_search_next,
+ # 64 @
+ :vi_alias,
+ # 65 A
+ :vi_add_at_eol,
+ # 66 B
+ :vi_prev_big_word,
+ # 67 C
+ :vi_change_to_eol,
+ # 68 D
+ :ed_kill_line,
+ # 69 E
+ :vi_end_big_word,
+ # 70 F
+ :vi_prev_char,
+ # 71 G
+ :vi_to_history_line,
+ # 72 H
+ :ed_unassigned,
+ # 73 I
+ :vi_insert_at_bol,
+ # 74 J
+ :ed_search_next_history,
+ # 75 K
+ :ed_search_prev_history,
+ # 76 L
+ :ed_unassigned,
+ # 77 M
+ :ed_unassigned,
+ # 78 N
+ :vi_repeat_search_prev,
+ # 79 O
+ :ed_sequence_lead_in,
+ # 80 P
+ :vi_paste_prev,
+ # 81 Q
+ :ed_unassigned,
+ # 82 R
+ :vi_replace_mode,
+ # 83 S
+ :vi_substitute_line,
+ # 84 T
+ :vi_to_prev_char,
+ # 85 U
+ :vi_undo_line,
+ # 86 V
+ :ed_unassigned,
+ # 87 W
+ :vi_next_big_word,
+ # 88 X
+ :ed_delete_prev_char,
+ # 89 Y
+ :vi_yank_end,
+ # 90 Z
+ :ed_unassigned,
+ # 91 [
+ :ed_sequence_lead_in,
+ # 92 \
+ :ed_unassigned,
+ # 93 ]
+ :ed_unassigned,
+ # 94 ^
+ :ed_move_to_beg,
+ # 95 _
+ :vi_history_word,
+ # 96 `
+ :ed_unassigned,
+ # 97 a
+ :vi_add,
+ # 98 b
+ :vi_prev_word,
+ # 99 c
+ :vi_change_meta,
+ # 100 d
+ :vi_delete_meta,
+ # 101 e
+ :vi_end_word,
+ # 102 f
+ :vi_next_char,
+ # 103 g
+ :ed_unassigned,
+ # 104 h
+ :ed_prev_char,
+ # 105 i
+ :vi_insert,
+ # 106 j
+ :ed_next_history,
+ # 107 k
+ :ed_prev_history,
+ # 108 l
+ :ed_next_char,
+ # 109 m
+ :ed_unassigned,
+ # 110 n
+ :vi_repeat_search_next,
+ # 111 o
+ :ed_unassigned,
+ # 112 p
+ :vi_paste_next,
+ # 113 q
+ :ed_unassigned,
+ # 114 r
+ :vi_replace_char,
+ # 115 s
+ :vi_substitute_char,
+ # 116 t
+ :vi_to_next_char,
+ # 117 u
+ :vi_undo,
+ # 118 v
+ :vi_histedit,
+ # 119 w
+ :vi_next_word,
+ # 120 x
+ :ed_delete_next_char,
+ # 121 y
+ :vi_yank,
+ # 122 z
+ :ed_unassigned,
+ # 123 {
+ :ed_unassigned,
+ # 124 |
+ :vi_to_column,
+ # 125 }
+ :ed_unassigned,
+ # 126 ~
+ :vi_change_case,
+ # 127 ^?
+ :ed_delete_prev_char,
+ # 128 M-^@
+ :ed_unassigned,
+ # 129 M-^A
+ :ed_unassigned,
+ # 130 M-^B
+ :ed_unassigned,
+ # 131 M-^C
+ :ed_unassigned,
+ # 132 M-^D
+ :ed_unassigned,
+ # 133 M-^E
+ :ed_unassigned,
+ # 134 M-^F
+ :ed_unassigned,
+ # 135 M-^G
+ :ed_unassigned,
+ # 136 M-^H
+ :ed_unassigned,
+ # 137 M-^I
+ :ed_unassigned,
+ # 138 M-^J
+ :ed_unassigned,
+ # 139 M-^K
+ :ed_unassigned,
+ # 140 M-^L
+ :ed_unassigned,
+ # 141 M-^M
+ :ed_unassigned,
+ # 142 M-^N
+ :ed_unassigned,
+ # 143 M-^O
+ :ed_unassigned,
+ # 144 M-^P
+ :ed_unassigned,
+ # 145 M-^Q
+ :ed_unassigned,
+ # 146 M-^R
+ :ed_unassigned,
+ # 147 M-^S
+ :ed_unassigned,
+ # 148 M-^T
+ :ed_unassigned,
+ # 149 M-^U
+ :ed_unassigned,
+ # 150 M-^V
+ :ed_unassigned,
+ # 151 M-^W
+ :ed_unassigned,
+ # 152 M-^X
+ :ed_unassigned,
+ # 153 M-^Y
+ :ed_unassigned,
+ # 154 M-^Z
+ :ed_unassigned,
+ # 155 M-^[
+ :ed_unassigned,
+ # 156 M-^\
+ :ed_unassigned,
+ # 157 M-^]
+ :ed_unassigned,
+ # 158 M-^^
+ :ed_unassigned,
+ # 159 M-^_
+ :ed_unassigned,
+ # 160 M-SPACE
+ :ed_unassigned,
+ # 161 M-!
+ :ed_unassigned,
+ # 162 M-"
+ :ed_unassigned,
+ # 163 M-#
+ :ed_unassigned,
+ # 164 M-$
+ :ed_unassigned,
+ # 165 M-%
+ :ed_unassigned,
+ # 166 M-&
+ :ed_unassigned,
+ # 167 M-'
+ :ed_unassigned,
+ # 168 M-(
+ :ed_unassigned,
+ # 169 M-)
+ :ed_unassigned,
+ # 170 M-*
+ :ed_unassigned,
+ # 171 M-+
+ :ed_unassigned,
+ # 172 M-,
+ :ed_unassigned,
+ # 173 M--
+ :ed_unassigned,
+ # 174 M-.
+ :ed_unassigned,
+ # 175 M-/
+ :ed_unassigned,
+ # 176 M-0
+ :ed_unassigned,
+ # 177 M-1
+ :ed_unassigned,
+ # 178 M-2
+ :ed_unassigned,
+ # 179 M-3
+ :ed_unassigned,
+ # 180 M-4
+ :ed_unassigned,
+ # 181 M-5
+ :ed_unassigned,
+ # 182 M-6
+ :ed_unassigned,
+ # 183 M-7
+ :ed_unassigned,
+ # 184 M-8
+ :ed_unassigned,
+ # 185 M-9
+ :ed_unassigned,
+ # 186 M-:
+ :ed_unassigned,
+ # 187 M-;
+ :ed_unassigned,
+ # 188 M-<
+ :ed_unassigned,
+ # 189 M-=
+ :ed_unassigned,
+ # 190 M->
+ :ed_unassigned,
+ # 191 M-?
+ :ed_unassigned,
+ # 192 M-@
+ :ed_unassigned,
+ # 193 M-A
+ :ed_unassigned,
+ # 194 M-B
+ :ed_unassigned,
+ # 195 M-C
+ :ed_unassigned,
+ # 196 M-D
+ :ed_unassigned,
+ # 197 M-E
+ :ed_unassigned,
+ # 198 M-F
+ :ed_unassigned,
+ # 199 M-G
+ :ed_unassigned,
+ # 200 M-H
+ :ed_unassigned,
+ # 201 M-I
+ :ed_unassigned,
+ # 202 M-J
+ :ed_unassigned,
+ # 203 M-K
+ :ed_unassigned,
+ # 204 M-L
+ :ed_unassigned,
+ # 205 M-M
+ :ed_unassigned,
+ # 206 M-N
+ :ed_unassigned,
+ # 207 M-O
+ :ed_sequence_lead_in,
+ # 208 M-P
+ :ed_unassigned,
+ # 209 M-Q
+ :ed_unassigned,
+ # 210 M-R
+ :ed_unassigned,
+ # 211 M-S
+ :ed_unassigned,
+ # 212 M-T
+ :ed_unassigned,
+ # 213 M-U
+ :ed_unassigned,
+ # 214 M-V
+ :ed_unassigned,
+ # 215 M-W
+ :ed_unassigned,
+ # 216 M-X
+ :ed_unassigned,
+ # 217 M-Y
+ :ed_unassigned,
+ # 218 M-Z
+ :ed_unassigned,
+ # 219 M-[
+ :ed_sequence_lead_in,
+ # 220 M-\
+ :ed_unassigned,
+ # 221 M-]
+ :ed_unassigned,
+ # 222 M-^
+ :ed_unassigned,
+ # 223 M-_
+ :ed_unassigned,
+ # 223 M-`
+ :ed_unassigned,
+ # 224 M-a
+ :ed_unassigned,
+ # 225 M-b
+ :ed_unassigned,
+ # 226 M-c
+ :ed_unassigned,
+ # 227 M-d
+ :ed_unassigned,
+ # 228 M-e
+ :ed_unassigned,
+ # 229 M-f
+ :ed_unassigned,
+ # 230 M-g
+ :ed_unassigned,
+ # 231 M-h
+ :ed_unassigned,
+ # 232 M-i
+ :ed_unassigned,
+ # 233 M-j
+ :ed_unassigned,
+ # 234 M-k
+ :ed_unassigned,
+ # 235 M-l
+ :ed_unassigned,
+ # 236 M-m
+ :ed_unassigned,
+ # 237 M-n
+ :ed_unassigned,
+ # 238 M-o
+ :ed_unassigned,
+ # 239 M-p
+ :ed_unassigned,
+ # 240 M-q
+ :ed_unassigned,
+ # 241 M-r
+ :ed_unassigned,
+ # 242 M-s
+ :ed_unassigned,
+ # 243 M-t
+ :ed_unassigned,
+ # 244 M-u
+ :ed_unassigned,
+ # 245 M-v
+ :ed_unassigned,
+ # 246 M-w
+ :ed_unassigned,
+ # 247 M-x
+ :ed_unassigned,
+ # 248 M-y
+ :ed_unassigned,
+ # 249 M-z
+ :ed_unassigned,
+ # 250 M-{
+ :ed_unassigned,
+ # 251 M-|
+ :ed_unassigned,
+ # 252 M-}
+ :ed_unassigned,
+ # 253 M-~
+ :ed_unassigned,
+ # 254 M-^?
+ :ed_unassigned
+ # 255
+ # EOF
+ ]
+end
+
diff --git a/lib/reline/key_actor/vi_insert.rb b/lib/reline/key_actor/vi_insert.rb
new file mode 100644
index 0000000000..8585a642ab
--- /dev/null
+++ b/lib/reline/key_actor/vi_insert.rb
@@ -0,0 +1,518 @@
+class Reline::KeyActor::ViInsert < Reline::KeyActor::Base
+ MAPPING = [
+ # 0 ^@
+ :ed_unassigned,
+ # 1 ^A
+ :ed_insert,
+ # 2 ^B
+ :ed_insert,
+ # 3 ^C
+ :ed_insert,
+ # 4 ^D
+ :vi_list_or_eof,
+ # 5 ^E
+ :ed_insert,
+ # 6 ^F
+ :ed_insert,
+ # 7 ^G
+ :ed_insert,
+ # 8 ^H
+ :vi_delete_prev_char,
+ # 9 ^I
+ :ed_insert,
+ # 10 ^J
+ :ed_newline,
+ # 11 ^K
+ :ed_insert,
+ # 12 ^L
+ :ed_insert,
+ # 13 ^M
+ :ed_newline,
+ # 14 ^N
+ :ed_insert,
+ # 15 ^O
+ :ed_insert,
+ # 16 ^P
+ :ed_insert,
+ # 17 ^Q
+ :ed_ignore,
+ # 18 ^R
+ :ed_insert,
+ # 19 ^S
+ :ed_ignore,
+ # 20 ^T
+ :ed_insert,
+ # 21 ^U
+ :vi_kill_line_prev,
+ # 22 ^V
+ :ed_quoted_insert,
+ # 23 ^W
+ :ed_delete_prev_word,
+ # 24 ^X
+ :ed_insert,
+ # 25 ^Y
+ :ed_insert,
+ # 26 ^Z
+ :ed_insert,
+ # 27 ^[
+ :vi_command_mode,
+ # 28 ^\
+ :ed_ignore,
+ # 29 ^]
+ :ed_insert,
+ # 30 ^^
+ :ed_insert,
+ # 31 ^_
+ :ed_insert,
+ # 32 SPACE
+ :ed_insert,
+ # 33 !
+ :ed_insert,
+ # 34 "
+ :ed_insert,
+ # 35 #
+ :ed_insert,
+ # 36 $
+ :ed_insert,
+ # 37 %
+ :ed_insert,
+ # 38 &
+ :ed_insert,
+ # 39 '
+ :ed_insert,
+ # 40 (
+ :ed_insert,
+ # 41 )
+ :ed_insert,
+ # 42 *
+ :ed_insert,
+ # 43 +
+ :ed_insert,
+ # 44 ,
+ :ed_insert,
+ # 45 -
+ :ed_insert,
+ # 46 .
+ :ed_insert,
+ # 47 /
+ :ed_insert,
+ # 48 0
+ :ed_insert,
+ # 49 1
+ :ed_insert,
+ # 50 2
+ :ed_insert,
+ # 51 3
+ :ed_insert,
+ # 52 4
+ :ed_insert,
+ # 53 5
+ :ed_insert,
+ # 54 6
+ :ed_insert,
+ # 55 7
+ :ed_insert,
+ # 56 8
+ :ed_insert,
+ # 57 9
+ :ed_insert,
+ # 58 :
+ :ed_insert,
+ # 59 ;
+ :ed_insert,
+ # 60 <
+ :ed_insert,
+ # 61 =
+ :ed_insert,
+ # 62 >
+ :ed_insert,
+ # 63 ?
+ :ed_insert,
+ # 64 @
+ :ed_insert,
+ # 65 A
+ :ed_insert,
+ # 66 B
+ :ed_insert,
+ # 67 C
+ :ed_insert,
+ # 68 D
+ :ed_insert,
+ # 69 E
+ :ed_insert,
+ # 70 F
+ :ed_insert,
+ # 71 G
+ :ed_insert,
+ # 72 H
+ :ed_insert,
+ # 73 I
+ :ed_insert,
+ # 74 J
+ :ed_insert,
+ # 75 K
+ :ed_insert,
+ # 76 L
+ :ed_insert,
+ # 77 M
+ :ed_insert,
+ # 78 N
+ :ed_insert,
+ # 79 O
+ :ed_insert,
+ # 80 P
+ :ed_insert,
+ # 81 Q
+ :ed_insert,
+ # 82 R
+ :ed_insert,
+ # 83 S
+ :ed_insert,
+ # 84 T
+ :ed_insert,
+ # 85 U
+ :ed_insert,
+ # 86 V
+ :ed_insert,
+ # 87 W
+ :ed_insert,
+ # 88 X
+ :ed_insert,
+ # 89 Y
+ :ed_insert,
+ # 90 Z
+ :ed_insert,
+ # 91 [
+ :ed_insert,
+ # 92 \
+ :ed_insert,
+ # 93 ]
+ :ed_insert,
+ # 94 ^
+ :ed_insert,
+ # 95 _
+ :ed_insert,
+ # 96 `
+ :ed_insert,
+ # 97 a
+ :ed_insert,
+ # 98 b
+ :ed_insert,
+ # 99 c
+ :ed_insert,
+ # 100 d
+ :ed_insert,
+ # 101 e
+ :ed_insert,
+ # 102 f
+ :ed_insert,
+ # 103 g
+ :ed_insert,
+ # 104 h
+ :ed_insert,
+ # 105 i
+ :ed_insert,
+ # 106 j
+ :ed_insert,
+ # 107 k
+ :ed_insert,
+ # 108 l
+ :ed_insert,
+ # 109 m
+ :ed_insert,
+ # 110 n
+ :ed_insert,
+ # 111 o
+ :ed_insert,
+ # 112 p
+ :ed_insert,
+ # 113 q
+ :ed_insert,
+ # 114 r
+ :ed_insert,
+ # 115 s
+ :ed_insert,
+ # 116 t
+ :ed_insert,
+ # 117 u
+ :ed_insert,
+ # 118 v
+ :ed_insert,
+ # 119 w
+ :ed_insert,
+ # 120 x
+ :ed_insert,
+ # 121 y
+ :ed_insert,
+ # 122 z
+ :ed_insert,
+ # 123 {
+ :ed_insert,
+ # 124 |
+ :ed_insert,
+ # 125 }
+ :ed_insert,
+ # 126 ~
+ :ed_insert,
+ # 127 ^?
+ :vi_delete_prev_char,
+ # 128 M-^@
+ :ed_insert,
+ # 129 M-^A
+ :ed_insert,
+ # 130 M-^B
+ :ed_insert,
+ # 131 M-^C
+ :ed_insert,
+ # 132 M-^D
+ :ed_insert,
+ # 133 M-^E
+ :ed_insert,
+ # 134 M-^F
+ :ed_insert,
+ # 135 M-^G
+ :ed_insert,
+ # 136 M-^H
+ :ed_insert,
+ # 137 M-^I
+ :ed_insert,
+ # 138 M-^J
+ :ed_insert,
+ # 139 M-^K
+ :ed_insert,
+ # 140 M-^L
+ :ed_insert,
+ # 141 M-^M
+ :ed_insert,
+ # 142 M-^N
+ :ed_insert,
+ # 143 M-^O
+ :ed_insert,
+ # 144 M-^P
+ :ed_insert,
+ # 145 M-^Q
+ :ed_insert,
+ # 146 M-^R
+ :ed_insert,
+ # 147 M-^S
+ :ed_insert,
+ # 148 M-^T
+ :ed_insert,
+ # 149 M-^U
+ :ed_insert,
+ # 150 M-^V
+ :ed_insert,
+ # 151 M-^W
+ :ed_insert,
+ # 152 M-^X
+ :ed_insert,
+ # 153 M-^Y
+ :ed_insert,
+ # 154 M-^Z
+ :ed_insert,
+ # 155 M-^[
+ :ed_insert,
+ # 156 M-^\
+ :ed_insert,
+ # 157 M-^]
+ :ed_insert,
+ # 158 M-^^
+ :ed_insert,
+ # 159 M-^_
+ :ed_insert,
+ # 160 M-SPACE
+ :ed_insert,
+ # 161 M-!
+ :ed_insert,
+ # 162 M-"
+ :ed_insert,
+ # 163 M-#
+ :ed_insert,
+ # 164 M-$
+ :ed_insert,
+ # 165 M-%
+ :ed_insert,
+ # 166 M-&
+ :ed_insert,
+ # 167 M-'
+ :ed_insert,
+ # 168 M-(
+ :ed_insert,
+ # 169 M-)
+ :ed_insert,
+ # 170 M-*
+ :ed_insert,
+ # 171 M-+
+ :ed_insert,
+ # 172 M-,
+ :ed_insert,
+ # 173 M--
+ :ed_insert,
+ # 174 M-.
+ :ed_insert,
+ # 175 M-/
+ :ed_insert,
+ # 176 M-0
+ :ed_insert,
+ # 177 M-1
+ :ed_insert,
+ # 178 M-2
+ :ed_insert,
+ # 179 M-3
+ :ed_insert,
+ # 180 M-4
+ :ed_insert,
+ # 181 M-5
+ :ed_insert,
+ # 182 M-6
+ :ed_insert,
+ # 183 M-7
+ :ed_insert,
+ # 184 M-8
+ :ed_insert,
+ # 185 M-9
+ :ed_insert,
+ # 186 M-:
+ :ed_insert,
+ # 187 M-;
+ :ed_insert,
+ # 188 M-<
+ :ed_insert,
+ # 189 M-=
+ :ed_insert,
+ # 190 M->
+ :ed_insert,
+ # 191 M-?
+ :ed_insert,
+ # 192 M-@
+ :ed_insert,
+ # 193 M-A
+ :ed_insert,
+ # 194 M-B
+ :ed_insert,
+ # 195 M-C
+ :ed_insert,
+ # 196 M-D
+ :ed_insert,
+ # 197 M-E
+ :ed_insert,
+ # 198 M-F
+ :ed_insert,
+ # 199 M-G
+ :ed_insert,
+ # 200 M-H
+ :ed_insert,
+ # 201 M-I
+ :ed_insert,
+ # 202 M-J
+ :ed_insert,
+ # 203 M-K
+ :ed_insert,
+ # 204 M-L
+ :ed_insert,
+ # 205 M-M
+ :ed_insert,
+ # 206 M-N
+ :ed_insert,
+ # 207 M-O
+ :ed_insert,
+ # 208 M-P
+ :ed_insert,
+ # 209 M-Q
+ :ed_insert,
+ # 210 M-R
+ :ed_insert,
+ # 211 M-S
+ :ed_insert,
+ # 212 M-T
+ :ed_insert,
+ # 213 M-U
+ :ed_insert,
+ # 214 M-V
+ :ed_insert,
+ # 215 M-W
+ :ed_insert,
+ # 216 M-X
+ :ed_insert,
+ # 217 M-Y
+ :ed_insert,
+ # 218 M-Z
+ :ed_insert,
+ # 219 M-[
+ :ed_insert,
+ # 220 M-\
+ :ed_insert,
+ # 221 M-]
+ :ed_insert,
+ # 222 M-^
+ :ed_insert,
+ # 223 M-_
+ :ed_insert,
+ # 223 M-`
+ :ed_insert,
+ # 224 M-a
+ :ed_insert,
+ # 225 M-b
+ :ed_insert,
+ # 226 M-c
+ :ed_insert,
+ # 227 M-d
+ :ed_insert,
+ # 228 M-e
+ :ed_insert,
+ # 229 M-f
+ :ed_insert,
+ # 230 M-g
+ :ed_insert,
+ # 231 M-h
+ :ed_insert,
+ # 232 M-i
+ :ed_insert,
+ # 233 M-j
+ :ed_insert,
+ # 234 M-k
+ :ed_insert,
+ # 235 M-l
+ :ed_insert,
+ # 236 M-m
+ :ed_insert,
+ # 237 M-n
+ :ed_insert,
+ # 238 M-o
+ :ed_insert,
+ # 239 M-p
+ :ed_insert,
+ # 240 M-q
+ :ed_insert,
+ # 241 M-r
+ :ed_insert,
+ # 242 M-s
+ :ed_insert,
+ # 243 M-t
+ :ed_insert,
+ # 244 M-u
+ :ed_insert,
+ # 245 M-v
+ :ed_insert,
+ # 246 M-w
+ :ed_insert,
+ # 247 M-x
+ :ed_insert,
+ # 248 M-y
+ :ed_insert,
+ # 249 M-z
+ :ed_insert,
+ # 250 M-{
+ :ed_insert,
+ # 251 M-|
+ :ed_insert,
+ # 252 M-}
+ :ed_insert,
+ # 253 M-~
+ :ed_insert,
+ # 254 M-^?
+ :ed_insert
+ # 255
+ # EOF
+ ]
+end
diff --git a/lib/reline/key_stroke.rb b/lib/reline/key_stroke.rb
new file mode 100644
index 0000000000..ac0a820759
--- /dev/null
+++ b/lib/reline/key_stroke.rb
@@ -0,0 +1,74 @@
+class Reline::KeyStroke
+ using Module.new {
+ refine Array do
+ def start_with?(other)
+ other.size <= size && other == self.take(other.size)
+ end
+
+ def bytes
+ self
+ end
+ end
+ }
+
+ def initialize(config)
+ @config = config
+ @buffer = []
+ end
+
+ def input_to(bytes)
+ case match_status(bytes)
+ when :matching
+ nil
+ when :matched
+ expand(bytes)
+ when :unmatched
+ bytes
+ end
+ end
+
+ def input_to!(bytes)
+ @buffer.concat Array(bytes)
+ input_to(@buffer)&.tap { clear }
+ end
+
+ private
+
+ def match_status(input)
+ key_mapping.keys.select { |lhs|
+ lhs.start_with? input
+ }.tap { |it|
+ return :matched if it.size == 1 && (it.max_by(&:size)&.size&.== input.size)
+ return :matching if it.size == 1 && (it.max_by(&:size)&.size&.!= input.size)
+ return :matched if it.max_by(&:size)&.size&.< input.size
+ return :matching if it.size > 1
+ }
+ key_mapping.keys.select { |lhs|
+ input.start_with? lhs
+ }.tap { |it|
+ return it.size > 0 ? :matched : :unmatched
+ }
+ end
+
+ def expand(input)
+ lhs = key_mapping.keys.select { |lhs| input.start_with? lhs }.sort_by(&:size).reverse.first
+ return input unless lhs
+ rhs = key_mapping[lhs]
+
+ case rhs
+ when String
+ rhs_bytes = rhs.bytes
+ expand(expand(rhs_bytes) + expand(input.drop(lhs.size)))
+ when Symbol
+ [rhs] + expand(input.drop(lhs.size))
+ end
+ end
+
+ def key_mapping
+ @config[:key_mapping].transform_keys(&:bytes)
+ end
+
+ def clear
+ @buffer = []
+ end
+end
diff --git a/lib/reline/kill_ring.rb b/lib/reline/kill_ring.rb
new file mode 100644
index 0000000000..842fd04697
--- /dev/null
+++ b/lib/reline/kill_ring.rb
@@ -0,0 +1,113 @@
+class Reline::KillRing
+ module State
+ FRESH = :fresh
+ CONTINUED = :continued
+ PROCESSED = :processed
+ YANK = :yank
+ end
+
+ RingPoint = Struct.new(:backward, :forward, :str) do
+ def initialize(str)
+ super(nil, nil, str)
+ end
+
+ def ==(other)
+ object_id == other.object_id
+ end
+ end
+
+ class RingBuffer
+ attr_reader :size
+ attr_reader :head
+
+ def initialize(max = 1024)
+ @max = max
+ @size = 0
+ @head = nil # reading head of ring-shaped tape
+ end
+
+ def <<(point)
+ if @size.zero?
+ @head = point
+ @head.backward = @head
+ @head.forward = @head
+ @size = 1
+ elsif @size >= @max
+ tail = @head.forward
+ new_tail = tail.forward
+ @head.forward = point
+ point.backward = @head
+ new_tail.backward = point
+ point.forward = new_tail
+ @head = point
+ else
+ tail = @head.forward
+ @head.forward = point
+ point.backward = @head
+ tail.backward = point
+ point.forward = tail
+ @head = point
+ @size += 1
+ end
+ end
+
+ def empty?
+ @size.zero?
+ end
+ end
+
+ def initialize(max = 1024)
+ @ring = RingBuffer.new(max)
+ @ring_pointer = nil
+ @buffer = nil
+ @state = State::FRESH
+ end
+
+ def append(string, before_p = false)
+ case @state
+ when State::FRESH, State::YANK
+ @ring << RingPoint.new(string)
+ @state = State::CONTINUED
+ when State::CONTINUED, State::PROCESSED
+ if before_p
+ @ring.head.str.prepend(string)
+ else
+ @ring.head.str.concat(string)
+ end
+ @state = State::CONTINUED
+ end
+ end
+
+ def process
+ case @state
+ when State::FRESH
+ # nothing to do
+ when State::CONTINUED
+ @state = State::PROCESSED
+ when State::PROCESSED
+ @state = State::FRESH
+ when State::YANK
+ # nothing to do
+ end
+ end
+
+ def yank
+ unless @ring.empty?
+ @state = State::YANK
+ @ring_pointer = @ring.head
+ @ring_pointer.str
+ else
+ nil
+ end
+ end
+
+ def yank_pop
+ if @state == State::YANK
+ prev_yank = @ring_pointer.str
+ @ring_pointer = @ring_pointer.backward
+ [@ring_pointer.str, prev_yank]
+ else
+ nil
+ end
+ end
+end
diff --git a/lib/reline/line_editor.rb b/lib/reline/line_editor.rb
new file mode 100644
index 0000000000..7d1030ea0e
--- /dev/null
+++ b/lib/reline/line_editor.rb
@@ -0,0 +1,1357 @@
+require 'reline/kill_ring'
+require 'reline/unicode'
+
+require 'tempfile'
+require 'pathname'
+
+class Reline::LineEditor
+ # TODO: undo
+ attr_reader :line
+ attr_accessor :confirm_multiline_termination_proc
+ attr_accessor :completion_proc
+ attr_accessor :dig_perfect_match_proc
+ attr_writer :retrieve_completion_block
+
+ ARGUMENTABLE = %i{
+ ed_delete_next_char
+ ed_delete_prev_char
+ ed_delete_prev_word
+ ed_next_char
+ ed_next_history
+ ed_next_line#
+ ed_prev_char
+ ed_prev_history
+ ed_prev_line#
+ ed_prev_word
+ ed_quoted_insert
+ vi_to_column
+ vi_next_word
+ vi_prev_word
+ vi_end_word
+ vi_next_big_word
+ vi_prev_big_word
+ vi_end_big_word
+ vi_next_char
+ vi_delete_meta
+ vi_paste_prev
+ vi_paste_next
+ vi_replace_char
+ }
+
+ VI_OPERATORS = %i{
+ vi_change_meta
+ vi_delete_meta
+ vi_yank
+ }
+
+ VI_MOTIONS = %i{
+ ed_prev_char
+ ed_next_char
+ vi_zero
+ ed_move_to_beg
+ ed_move_to_end
+ vi_to_column
+ vi_next_char
+ vi_prev_char
+ vi_next_word
+ vi_prev_word
+ vi_to_next_char
+ vi_to_prev_char
+ vi_end_word
+ vi_next_big_word
+ vi_prev_big_word
+ vi_end_big_word
+ vi_repeat_next_char
+ vi_repeat_prev_char
+ }
+
+ module CompletionState
+ NORMAL = :normal
+ COMPLETION = :completion
+ MENU = :menu
+ JOURNEY = :journey
+ PERFECT_MATCH = :perfect_match
+ end
+
+ CompletionJourneyData = Struct.new('CompletionJourneyData', :preposing, :postposing, :list, :pointer)
+ MenuInfo = Struct.new('MenuInfo', :target, :list)
+
+ def initialize(config, prompt, encoding = Encoding.default_external)
+ @config = config
+ @prompt = prompt
+ @prompt_width = calculate_width(@prompt)
+ @cursor = 0
+ @cursor_max = 0
+ @byte_pointer = 0
+ @encoding = encoding
+ @buffer_of_lines = [String.new(encoding: @encoding)]
+ @line_index = 0
+ @previous_line_index = nil
+ @line = @buffer_of_lines[0]
+ @is_multiline = false
+ @finished = false
+ @cleared = false
+ @rerender_all = false
+ @is_confirm_multiline_termination = false
+ @history_pointer = nil
+ @line_backup_in_history = nil
+ @kill_ring = Reline::KillRing.new
+ @vi_clipboard = ''
+ @vi_arg = nil
+ @multibyte_buffer = String.new(encoding: 'ASCII-8BIT')
+ @meta_prefix = false
+ @waiting_proc = nil
+ @waiting_operator_proc = nil
+ @completion_journey_data = nil
+ @completion_state = CompletionState::NORMAL
+ @perfect_matched = nil
+ @screen_size = Reline.get_screen_size
+ @first_line_started_from = 0
+ @move_up = 0
+ @started_from = 0
+ @highest_in_this = 1
+ @highest_in_all = 1
+ @menu_info = nil
+ end
+
+ def multiline_on
+ @is_multiline = true
+ end
+
+ def multiline_off
+ @is_multiline = false
+ end
+
+ private def insert_new_line(cursor_line, next_line)
+ @line = cursor_line
+ @buffer_of_lines.insert(@line_index + 1, String.new(next_line, encoding: @encoding))
+ @previous_line_index = @line_index
+ @line_index += 1
+ end
+
+ private def calculate_height_by_width(width)
+ return 1 if width.zero?
+ height = 1
+ max_width = @screen_size.last
+ while width > max_width * height
+ height += 1
+ end
+ height += 1 if (width % max_width).zero?
+ height
+ end
+
+ private def split_by_width(str, max_width)
+ lines = [String.new(encoding: @encoding)]
+ width = 0
+ str.encode(Encoding::UTF_8).grapheme_clusters.each do |gc|
+ mbchar_width = Reline::Unicode.get_mbchar_width(gc)
+ width += mbchar_width
+ if width > max_width
+ width = mbchar_width
+ lines << String.new(encoding: @encoding)
+ end
+ lines.last << gc
+ end
+ # The cursor moves to next line in first
+ lines << String.new(encoding: @encoding) if width == max_width
+ lines
+ end
+
+ private def scroll_down(val)
+ if val <= @rest_height
+ Reline.move_cursor_down(val)
+ @rest_height -= val
+ else
+ Reline.move_cursor_down(@rest_height)
+ Reline.scroll_down(val - @rest_height)
+ @rest_height = 0
+ end
+ end
+
+ private def move_cursor_up(val)
+ if val > 0
+ Reline.move_cursor_up(val)
+ @rest_height += val
+ elsif val < 0
+ move_cursor_down(-val)
+ end
+ end
+
+ private def move_cursor_down(val)
+ if val > 0
+ Reline.move_cursor_down(val)
+ @rest_height -= val
+ @rest_height = 0 if @rest_height < 0
+ elsif val < 0
+ move_cursor_up(-val)
+ end
+ end
+
+ private def calculate_nearest_cursor
+ @cursor_max = calculate_width(line)
+ new_cursor = 0
+ new_byte_pointer = 0
+ height = 1
+ max_width = @screen_size.last
+ @line.encode(Encoding::UTF_8).grapheme_clusters.each do |gc|
+ mbchar_width = Reline::Unicode.get_mbchar_width(gc)
+ now = new_cursor + mbchar_width
+ if now > @cursor_max or now > @cursor
+ break
+ end
+ new_cursor += mbchar_width
+ if new_cursor > max_width * height
+ height += 1
+ end
+ new_byte_pointer += gc.bytesize
+ end
+ @started_from = height - 1
+ @cursor = new_cursor
+ @byte_pointer = new_byte_pointer
+ end
+
+ def rerender # TODO: support physical and logical lines
+ @rest_height ||= (Reline.get_screen_size.first - 1) - Reline.cursor_pos.y
+ if @menu_info
+ puts
+ @menu_info.list.each do |item|
+ puts item
+ end
+ @menu_info = nil
+ end
+ return if @line.nil?
+ if @vi_arg
+ prompt = "(arg: #{@vi_arg}) "
+ prompt_width = calculate_width(prompt)
+ else
+ prompt = @prompt
+ prompt_width = @prompt_width
+ end
+ if @cleared
+ Reline.clear_screen
+ @cleared = false
+ back = 0
+ @buffer_of_lines.each_with_index do |line, index|
+ line = @line if index == @line_index
+ height = render_partial(prompt, prompt_width, line, false)
+ if index < (@buffer_of_lines.size - 1)
+ move_cursor_down(height)
+ back += height
+ end
+ end
+ move_cursor_up(back)
+ move_cursor_down(@first_line_started_from + @started_from)
+ Reline.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
+ return
+ end
+ # FIXME: end of logical line sometimes breaks
+ if @previous_line_index
+ previous_line = @line
+ all_height = @buffer_of_lines.inject(0) { |result, line|
+ result + calculate_height_by_width(@prompt_width + calculate_width(line))
+ }
+ diff = all_height - @highest_in_all
+ if diff > 0
+ @highest_in_all = all_height
+ scroll_down(diff)
+ move_cursor_up(@first_line_started_from + @started_from + diff)
+ back = 0
+ @buffer_of_lines.each_with_index do |line, index|
+ line = @line if index == @previous_line_index
+ height = render_partial(prompt, prompt_width, line, false)
+ if index < (@buffer_of_lines.size - 1)
+ move_cursor_down(height)
+ back += height
+ end
+ end
+ move_cursor_up(back)
+ else
+ render_partial(prompt, prompt_width, previous_line)
+ move_cursor_up(@first_line_started_from + @started_from)
+ end
+ @buffer_of_lines[@previous_line_index] = @line
+ @line = @buffer_of_lines[@line_index]
+ @first_line_started_from =
+ if @line_index.zero?
+ 0
+ else
+ @buffer_of_lines[0..(@line_index - 1)].inject(0) { |result, line|
+ result + calculate_height_by_width(@prompt_width + calculate_width(line))
+ }
+ end
+ move_cursor_down(@first_line_started_from)
+ calculate_nearest_cursor
+ @highest_in_this = calculate_height_by_width(@prompt_width + @cursor_max)
+ @previous_line_index = nil
+ elsif @rerender_all
+ move_cursor_up(@first_line_started_from + @started_from)
+ Reline.move_cursor_column(0)
+ back = 0
+ @buffer_of_lines.each do |line|
+ width = prompt_width + calculate_width(line)
+ height = calculate_height_by_width(width)
+ back += height
+ end
+ if back > @highest_in_all
+ scroll_down(back)
+ move_cursor_up(back)
+ elsif back < @highest_in_all
+ scroll_down(back)
+ Reline.erase_after_cursor
+ (@highest_in_all - back).times do
+ scroll_down(1)
+ Reline.erase_after_cursor
+ end
+ move_cursor_up(@highest_in_all)
+ end
+ @buffer_of_lines.each_with_index do |line, index|
+ height = render_partial(prompt, prompt_width, line, false)
+ if index < (@buffer_of_lines.size - 1)
+ move_cursor_down(1)
+ end
+ end
+ move_cursor_up(back - 1)
+ @highest_in_all = back
+ @highest_in_this = calculate_height_by_width(@prompt_width + @cursor_max)
+ @first_line_started_from =
+ if @line_index.zero?
+ 0
+ else
+ @buffer_of_lines[0..(@line_index - 1)].inject(0) { |result, line|
+ result + calculate_height_by_width(@prompt_width + calculate_width(line))
+ }
+ end
+ move_cursor_down(@first_line_started_from)
+ @rerender_all = false
+ end
+ render_partial(prompt, prompt_width, @line) if !@is_multiline or !finished?
+ if @is_multiline and finished?
+ scroll_down(1) unless @buffer_of_lines.last.empty?
+ Reline.move_cursor_column(0)
+ Reline.erase_after_cursor
+ end
+ end
+
+ private def render_partial(prompt, prompt_width, line_to_render, with_control = true)
+ whole_line = prompt + (line_to_render.nil? ? '' : line_to_render)
+ visual_lines = split_by_width(whole_line, @screen_size.last)
+ if with_control
+ if visual_lines.size > @highest_in_this
+ diff = visual_lines.size - @highest_in_this
+ scroll_down(diff)
+ @highest_in_all += diff
+ @highest_in_this = visual_lines.size
+ move_cursor_up(1)
+ end
+ move_cursor_up(@started_from)
+ @started_from = calculate_height_by_width(prompt_width + @cursor) - 1
+ end
+ visual_lines.each_with_index do |line, index|
+ Reline.move_cursor_column(0)
+ escaped_print line
+ Reline.erase_after_cursor
+ move_cursor_down(1) if index < (visual_lines.size - 1)
+ end
+ if with_control
+ if finished?
+ puts
+ else
+ move_cursor_up((visual_lines.size - 1) - @started_from)
+ Reline.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
+ end
+ end
+ visual_lines.size
+ end
+
+ def editing_mode
+ @config.editing_mode
+ end
+
+ private def escaped_print(str)
+ print str.chars.map { |gr|
+ escaped = Reline::Unicode::EscapedPairs[gr.ord]
+ if escaped
+ escaped
+ else
+ gr
+ end
+ }.join
+ end
+
+ private def menu(target, list)
+ @menu_info = MenuInfo.new(target, list)
+ end
+
+ private def complete_internal_proc(list, is_menu)
+ preposing, target, postposing = @retrieve_completion_block.(@line, @byte_pointer)
+ list = list.select { |i| i&.start_with?(target) }
+ if is_menu
+ menu(target, list)
+ return nil
+ end
+ completed = list.inject { |memo, item|
+ memo_mbchars = memo.unicode_normalize.grapheme_clusters
+ item_mbchars = item.unicode_normalize.grapheme_clusters
+ size = [memo_mbchars.size, item_mbchars.size].min
+ result = ''
+ size.times do |i|
+ if memo_mbchars[i] == item_mbchars[i]
+ result << memo_mbchars[i]
+ else
+ break
+ end
+ end
+ result
+ }
+ [target, preposing, completed, postposing]
+ end
+
+ private def complete(list)
+ case @completion_state
+ when CompletionState::NORMAL, CompletionState::JOURNEY
+ @completion_state = CompletionState::COMPLETION
+ when CompletionState::PERFECT_MATCH
+ @dig_perfect_match_proc&.(@perfect_matched)
+ end
+ is_menu = (@completion_state == CompletionState::MENU)
+ result = complete_internal_proc(list, is_menu)
+ return if result.nil?
+ target, preposing, completed, postposing = result
+ return if completed.nil?
+ if target <= completed and (@completion_state == CompletionState::COMPLETION or @completion_state == CompletionState::PERFECT_MATCH)
+ @completion_state = CompletionState::MENU
+ if list.include?(completed)
+ @completion_state = CompletionState::PERFECT_MATCH
+ @perfect_matched = completed
+ end
+ if target < completed
+ @line = preposing + completed + postposing
+ line_to_pointer = preposing + completed
+ @cursor_max = calculate_width(@line)
+ @cursor = calculate_width(line_to_pointer)
+ @byte_pointer = line_to_pointer.bytesize
+ end
+ end
+ end
+
+ private def move_completed_list(list, direction)
+ case @completion_state
+ when CompletionState::NORMAL, CompletionState::COMPLETION, CompletionState::MENU
+ @completion_state = CompletionState::JOURNEY
+ result = @retrieve_completion_block.(@line, @byte_pointer)
+ return if result.nil?
+ preposing, target, postposing = result
+ @completion_journey_data = CompletionJourneyData.new(
+ preposing, postposing,
+ [target] + list.select{ |item| item.start_with?(target) }, 0)
+ @completion_state = CompletionState::JOURNEY
+ else
+ case direction
+ when :up
+ @completion_journey_data.pointer -= 1
+ if @completion_journey_data.pointer < 0
+ @completion_journey_data.pointer = @completion_journey_data.list.size - 1
+ end
+ when :down
+ @completion_journey_data.pointer += 1
+ if @completion_journey_data.pointer >= @completion_journey_data.list.size
+ @completion_journey_data.pointer = 0
+ end
+ end
+ completed = @completion_journey_data.list[@completion_journey_data.pointer]
+ @line = @completion_journey_data.preposing + completed + @completion_journey_data.postposing
+ line_to_pointer = @completion_journey_data.preposing + completed
+ @cursor_max = calculate_width(@line)
+ @cursor = calculate_width(line_to_pointer)
+ @byte_pointer = line_to_pointer.bytesize
+ end
+ end
+
+ private def run_for_operators(key, method_symbol, &block)
+ if @waiting_operator_proc
+ if VI_MOTIONS.include?(method_symbol)
+ old_cursor, old_byte_pointer = @cursor, @byte_pointer
+ block.()
+ unless @waiting_proc
+ cursor_diff, byte_pointer_diff = @cursor - old_cursor, @byte_pointer - old_byte_pointer
+ @cursor, @byte_pointer = old_cursor, old_byte_pointer
+ @waiting_operator_proc.(cursor_diff, byte_pointer_diff)
+ else
+ old_waiting_proc = @waiting_proc
+ old_waiting_operator_proc = @waiting_operator_proc
+ @waiting_proc = proc { |key|
+ old_cursor, old_byte_pointer = @cursor, @byte_pointer
+ old_waiting_proc.(key)
+ cursor_diff, byte_pointer_diff = @cursor - old_cursor, @byte_pointer - old_byte_pointer
+ @cursor, @byte_pointer = old_cursor, old_byte_pointer
+ @waiting_operator_proc.(cursor_diff, byte_pointer_diff)
+ }
+ end
+ else
+ # Ignores operator when not motion is given.
+ block.()
+ end
+ @waiting_operator_proc = nil
+ else
+ block.()
+ end
+ end
+
+ private def process_key(key, method_symbol, method_obj)
+ if @vi_arg
+ if key.chr =~ /[0-9]/
+ ed_argument_digit(key)
+ else
+ if ARGUMENTABLE.include?(method_symbol) and method_obj
+ run_for_operators(key, method_symbol) do
+ method_obj.(key, arg: @vi_arg)
+ end
+ elsif @waiting_proc
+ @waiting_proc.(key)
+ elsif method_obj
+ method_obj.(key)
+ else
+ ed_insert(key)
+ end
+ @kill_ring.process
+ @vi_arg = nil
+ end
+ elsif @waiting_proc
+ @waiting_proc.(key)
+ @kill_ring.process
+ elsif method_obj
+ if method_symbol == :ed_argument_digit
+ method_obj.(key)
+ else
+ run_for_operators(key, method_symbol) do
+ method_obj.(key)
+ end
+ end
+ @kill_ring.process
+ else
+ ed_insert(key)
+ end
+ end
+
+ private def normal_char(key)
+ method_symbol = method_obj = nil
+ @multibyte_buffer << key
+ if @multibyte_buffer.size > 1
+ if @multibyte_buffer.dup.force_encoding(@encoding).valid_encoding?
+ key = @multibyte_buffer.dup.force_encoding(@encoding)
+ @multibyte_buffer.clear
+ else
+ # invalid
+ return
+ end
+ else # single byte
+ return if key >= 128 # maybe, first byte of multi byte
+ if @meta_prefix
+ key |= 0b10000000 if key.nobits?(0b10000000)
+ @meta_prefix = false
+ end
+ method_symbol = @config.editing_mode.get_method(key)
+ if key.allbits?(0b10000000) and method_symbol == :ed_unassigned
+ return # This is unknown input
+ end
+ if method_symbol and respond_to?(method_symbol, true)
+ method_obj = method(method_symbol)
+ end
+ @multibyte_buffer.clear
+ end
+ process_key(key, method_symbol, method_obj)
+ if @config.editing_mode_is?(:vi_command) and @cursor > 0 and @cursor == @cursor_max
+ byte_size = Reline::Unicode.get_prev_mbchar_size(@line, @byte_pointer)
+ @byte_pointer -= byte_size
+ mbchar = @line.byteslice(@byte_pointer, byte_size)
+ width = Reline::Unicode.get_mbchar_width(mbchar)
+ @cursor -= width
+ end
+ end
+
+ def input_key(key)
+ completion_occurs = false
+ if @config.editing_mode_is?(:emacs, :vi_insert) and key == "\C-i".ord
+ result = @completion_proc&.(@line)
+ if result.is_a?(Array)
+ completion_occurs = true
+ complete(result)
+ end
+ elsif @config.editing_mode_is?(:vi_insert) and ["\C-p".ord, "\C-n".ord].include?(key)
+ result = @completion_proc&.(@line)
+ if result.is_a?(Array)
+ completion_occurs = true
+ move_completed_list(result, "\C-p".ord == key ? :up : :down)
+ end
+ elsif @config.editing_mode_is?(:emacs) and key == "\e".ord # meta key
+ if @meta_prefix
+ # escape twice
+ @meta_prefix = false
+ @kill_ring.process
+ else
+ @meta_prefix = true
+ end
+ elsif @config.editing_mode_is?(:vi_command) and key == "\e".ord
+ # suppress ^[ when command_mode
+ elsif Symbol === key and respond_to?(key, true)
+ process_key(key, key, method(key))
+ else
+ normal_char(key)
+ end
+ unless completion_occurs
+ @completion_state = CompletionState::NORMAL
+ end
+ if @is_confirm_multiline_termination and @confirm_multiline_termination_proc
+ @is_confirm_multiline_termination = false
+ temp_buffer = @buffer_of_lines.dup
+ if @previous_line_index and @line_index == (@buffer_of_lines.size - 1)
+ temp_buffer[@previous_line_index] = @line
+ end
+ finish if @confirm_multiline_termination_proc.(temp_buffer.join("\n"))
+ end
+ end
+
+ def whole_buffer
+ temp_lines = @buffer_of_lines.dup
+ temp_lines[@line_index] = @line
+ if @buffer_of_lines.size == 1 and @line.nil?
+ nil
+ else
+ temp_lines.join("\n")
+ end
+ end
+
+ def finished?
+ @finished
+ end
+
+ def finish
+ @finished = true
+ @config.reset
+ end
+
+ private def byteslice!(str, byte_pointer, size)
+ new_str = str.byteslice(0, byte_pointer)
+ new_str << str.byteslice(byte_pointer + size, str.bytesize)
+ [new_str, str.byteslice(byte_pointer, size)]
+ end
+
+ private def byteinsert(str, byte_pointer, other)
+ new_str = str.byteslice(0, byte_pointer)
+ new_str << other
+ new_str << str.byteslice(byte_pointer, str.bytesize)
+ new_str
+ end
+
+ private def calculate_width(str)
+ str.encode(Encoding::UTF_8).grapheme_clusters.inject(0) { |width, gc|
+ width + Reline::Unicode.get_mbchar_width(gc)
+ }
+ end
+
+ private def ed_insert(key)
+ if key.instance_of?(String)
+ width = Reline::Unicode.get_mbchar_width(key)
+ if @cursor == @cursor_max
+ @line += key
+ else
+ @line = byteinsert(@line, @byte_pointer, key)
+ end
+ @byte_pointer += key.bytesize
+ @cursor += width
+ @cursor_max += width
+ else
+ if @cursor == @cursor_max
+ @line += key.chr
+ else
+ @line = byteinsert(@line, @byte_pointer, key.chr)
+ end
+ width = Reline::Unicode.get_mbchar_width(key.chr)
+ @byte_pointer += 1
+ @cursor += width
+ @cursor_max += width
+ end
+ end
+ alias_method :ed_digit, :ed_insert
+
+ private def ed_quoted_insert(str, arg: 1)
+ @waiting_proc = proc { |key|
+ arg.times do
+ ed_insert(key)
+ end
+ @waiting_proc = nil
+ }
+ end
+
+ private def ed_next_char(key, arg: 1)
+ byte_size = Reline::Unicode.get_next_mbchar_size(@line, @byte_pointer)
+ if (@byte_pointer < @line.bytesize)
+ mbchar = @line.byteslice(@byte_pointer, byte_size)
+ width = Reline::Unicode.get_mbchar_width(mbchar)
+ @cursor += width if width
+ @byte_pointer += byte_size
+ end
+ arg -= 1
+ ed_next_char(key, arg: arg) if arg > 0
+ end
+
+ private def ed_prev_char(key, arg: 1)
+ if @cursor > 0
+ byte_size = Reline::Unicode.get_prev_mbchar_size(@line, @byte_pointer)
+ @byte_pointer -= byte_size
+ mbchar = @line.byteslice(@byte_pointer, byte_size)
+ width = Reline::Unicode.get_mbchar_width(mbchar)
+ @cursor -= width
+ end
+ arg -= 1
+ ed_prev_char(key, arg: arg) if arg > 0
+ end
+
+ private def ed_move_to_beg(key)
+ @byte_pointer, @cursor = Reline::Unicode.ed_move_to_begin(@line)
+ end
+
+ private def ed_move_to_end(key)
+ @byte_pointer = 0
+ @cursor = 0
+ byte_size = 0
+ while @byte_pointer < @line.bytesize
+ byte_size = Reline::Unicode.get_next_mbchar_size(@line, @byte_pointer)
+ if byte_size > 0
+ mbchar = @line.byteslice(@byte_pointer, byte_size)
+ @cursor += Reline::Unicode.get_mbchar_width(mbchar)
+ end
+ @byte_pointer += byte_size
+ end
+ end
+
+ private def ed_prev_history(key, arg: 1)
+ if @is_multiline and @line_index > 0
+ @previous_line_index = @line_index
+ @line_index -= 1
+ return
+ end
+ if Reline::HISTORY.empty?
+ return
+ end
+ if @history_pointer.nil?
+ @history_pointer = Reline::HISTORY.size - 1
+ if @is_multiline
+ @line_backup_in_history = whole_buffer
+ @buffer_of_lines = Reline::HISTORY[@history_pointer].split("\n")
+ @line_index = @buffer_of_lines.size - 1
+ @line = @buffer_of_lines.last
+ @rerender_all = true
+ else
+ @line_backup_in_history = @line
+ @line = Reline::HISTORY[@history_pointer]
+ end
+ elsif @history_pointer.zero?
+ return
+ else
+ if @is_multiline
+ Reline::HISTORY[@history_pointer] = whole_buffer
+ @history_pointer -= 1
+ @buffer_of_lines = Reline::HISTORY[@history_pointer].split("\n")
+ @line_index = @buffer_of_lines.size - 1
+ @line = @buffer_of_lines.last
+ @rerender_all = true
+ else
+ Reline::HISTORY[@history_pointer] = @line
+ @history_pointer -= 1
+ @line = Reline::HISTORY[@history_pointer]
+ end
+ end
+ if @config.editing_mode_is?(:emacs)
+ @cursor_max = @cursor = calculate_width(@line)
+ @byte_pointer = @line.bytesize
+ elsif @config.editing_mode_is?(:vi_command)
+ @byte_pointer = @cursor = 0
+ @cursor_max = calculate_width(@line)
+ end
+ arg -= 1
+ ed_prev_history(key, arg: arg) if arg > 0
+ end
+
+ private def ed_next_history(key, arg: 1)
+ if @is_multiline and @line_index < (@buffer_of_lines.size - 1)
+ @previous_line_index = @line_index
+ @line_index += 1
+ return
+ end
+ if @history_pointer.nil?
+ return
+ elsif @history_pointer == (Reline::HISTORY.size - 1)
+ if @is_multiline
+ @history_pointer = nil
+ @buffer_of_lines = @line_backup_in_history.split("\n")
+ @buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
+ @line_index = 0
+ @line = @buffer_of_lines.first
+ @rerender_all = true
+ else
+ @history_pointer = nil
+ @line = @line_backup_in_history
+ end
+ else
+ if @is_multiline
+ Reline::HISTORY[@history_pointer] = whole_buffer
+ @history_pointer += 1
+ @buffer_of_lines = Reline::HISTORY[@history_pointer].split("\n")
+ @line_index = 0
+ @line = @buffer_of_lines.first
+ @rerender_all = true
+ else
+ Reline::HISTORY[@history_pointer] = @line
+ @history_pointer += 1
+ @line = Reline::HISTORY[@history_pointer]
+ end
+ end
+ @line = '' unless @line
+ if @config.editing_mode_is?(:emacs)
+ @cursor_max = @cursor = calculate_width(@line)
+ @byte_pointer = @line.bytesize
+ elsif @config.editing_mode_is?(:vi_command)
+ @byte_pointer = @cursor = 0
+ @cursor_max = calculate_width(@line)
+ end
+ arg -= 1
+ ed_next_history(key, arg: arg) if arg > 0
+ end
+
+ private def ed_newline(key)
+ if @is_multiline
+ if @config.editing_mode_is?(:vi_command)
+ if @line_index < (@buffer_of_lines.size - 1)
+ ed_next_history(key)
+ else
+ @is_confirm_multiline_termination = true
+ end
+ else
+ next_line = @line.byteslice(@byte_pointer, @line.bytesize - @byte_pointer)
+ cursor_line = @line.byteslice(0, @byte_pointer)
+ insert_new_line(cursor_line, next_line)
+ if @line_index == (@buffer_of_lines.size - 1)
+ @is_confirm_multiline_termination = true
+ end
+ end
+ return
+ end
+ if @history_pointer
+ Reline::HISTORY[@history_pointer] = @line
+ @history_pointer = nil
+ end
+ finish
+ end
+
+ private def em_delete_prev_char(key)
+ if @is_multiline and @cursor == 0 and @line_index > 0
+ @buffer_of_lines[@line_index] = @line
+ @cursor = calculate_width(@buffer_of_lines[@line_index - 1])
+ @byte_pointer = @buffer_of_lines[@line_index - 1].bytesize
+ @buffer_of_lines[@line_index - 1] += @buffer_of_lines.delete_at(@line_index)
+ @line_index -= 1
+ @line = @buffer_of_lines[@line_index]
+ @cursor_max = calculate_width(@line)
+ @rerender_all = true
+ elsif @cursor > 0
+ byte_size = Reline::Unicode.get_prev_mbchar_size(@line, @byte_pointer)
+ @byte_pointer -= byte_size
+ @line, mbchar = byteslice!(@line, @byte_pointer, byte_size)
+ width = Reline::Unicode.get_mbchar_width(mbchar)
+ @cursor -= width
+ @cursor_max -= width
+ end
+ end
+
+ private def ed_kill_line(key)
+ if @line.bytesize > @byte_pointer
+ @line, deleted = byteslice!(@line, @byte_pointer, @line.bytesize - @byte_pointer)
+ @byte_pointer = @line.bytesize
+ @cursor = @cursor_max = calculate_width(@line)
+ @kill_ring.append(deleted)
+ end
+ end
+
+ private def em_kill_line(key)
+ if @byte_pointer > 0
+ @line, deleted = byteslice!(@line, 0, @byte_pointer)
+ @byte_pointer = 0
+ @kill_ring.append(deleted, true)
+ @cursor_max = calculate_width(@line)
+ @cursor = 0
+ end
+ end
+
+ private def em_delete_or_list(key)
+ if @line.empty?
+ @line = nil
+ finish
+ elsif @byte_pointer < @line.bytesize
+ splitted_last = @line.byteslice(@byte_pointer, @line.bytesize)
+ mbchar = splitted_last.grapheme_clusters.first
+ width = Reline::Unicode.get_mbchar_width(mbchar)
+ @cursor_max -= width
+ @line, = byteslice!(@line, @byte_pointer, mbchar.bytesize)
+ end
+ end
+
+ private def em_yank(key)
+ yanked = @kill_ring.yank
+ if yanked
+ @line = byteinsert(@line, @byte_pointer, yanked)
+ yanked_width = calculate_width(yanked)
+ @cursor += yanked_width
+ @cursor_max += yanked_width
+ @byte_pointer += yanked.bytesize
+ end
+ end
+
+ private def em_yank_pop(key)
+ yanked, prev_yank = @kill_ring.yank_pop
+ if yanked
+ prev_yank_width = calculate_width(prev_yank)
+ @cursor -= prev_yank_width
+ @cursor_max -= prev_yank_width
+ @byte_pointer -= prev_yank.bytesize
+ @line, = byteslice!(@line, @byte_pointer, prev_yank.bytesize)
+ @line = byteinsert(@line, @byte_pointer, yanked)
+ yanked_width = calculate_width(yanked)
+ @cursor += yanked_width
+ @cursor_max += yanked_width
+ @byte_pointer += yanked.bytesize
+ end
+ end
+
+ private def ed_clear_screen(key)
+ @cleared = true
+ end
+
+ private def em_next_word(key)
+ if @line.bytesize > @byte_pointer
+ byte_size, width = Reline::Unicode.em_forward_word(@line, @byte_pointer)
+ @byte_pointer += byte_size
+ @cursor += width
+ end
+ end
+
+ private def ed_prev_word(key)
+ if @byte_pointer > 0
+ byte_size, width = Reline::Unicode.em_backward_word(@line, @byte_pointer)
+ @byte_pointer -= byte_size
+ @cursor -= width
+ end
+ end
+
+ private def em_delete_next_word(key)
+ if @line.bytesize > @byte_pointer
+ byte_size, width = Reline::Unicode.em_forward_word(@line, @byte_pointer)
+ @line, word = byteslice!(@line, @byte_pointer, byte_size)
+ @kill_ring.append(word)
+ @cursor_max -= width
+ end
+ end
+
+ private def ed_delete_prev_word(key)
+ if @byte_pointer > 0
+ byte_size, width = Reline::Unicode.em_backward_word(@line, @byte_pointer)
+ @line, word = byteslice!(@line, @byte_pointer - byte_size, byte_size)
+ @kill_ring.append(word, true)
+ @byte_pointer -= byte_size
+ @cursor -= width
+ @cursor_max -= width
+ end
+ end
+
+ private def ed_transpose_chars(key)
+ if @byte_pointer > 0
+ if @cursor_max > @cursor
+ byte_size = Reline::Unicode.get_next_mbchar_size(@line, @byte_pointer)
+ mbchar = @line.byteslice(@byte_pointer, byte_size)
+ width = Reline::Unicode.get_mbchar_width(mbchar)
+ @cursor += width
+ @byte_pointer += byte_size
+ end
+ back1_byte_size = Reline::Unicode.get_prev_mbchar_size(@line, @byte_pointer)
+ if (@byte_pointer - back1_byte_size) > 0
+ back2_byte_size = Reline::Unicode.get_prev_mbchar_size(@line, @byte_pointer - back1_byte_size)
+ back2_pointer = @byte_pointer - back1_byte_size - back2_byte_size
+ @line, back2_mbchar = byteslice!(@line, back2_pointer, back2_byte_size)
+ @line = byteinsert(@line, @byte_pointer - back2_byte_size, back2_mbchar)
+ end
+ end
+ end
+
+ private def em_capitol_case(key)
+ if @line.bytesize > @byte_pointer
+ byte_size, _, new_str = Reline::Unicode.em_forward_word_with_capitalization(@line, @byte_pointer)
+ before = @line.byteslice(0, @byte_pointer)
+ after = @line.byteslice((@byte_pointer + byte_size)..-1)
+ @line = before + new_str + after
+ @byte_pointer += new_str.bytesize
+ @cursor += calculate_width(new_str)
+ end
+ end
+
+ private def em_lower_case(key)
+ if @line.bytesize > @byte_pointer
+ byte_size, = Reline::Unicode.em_forward_word(@line, @byte_pointer)
+ part = @line.byteslice(@byte_pointer, byte_size).grapheme_clusters.map { |mbchar|
+ mbchar =~ /[A-Z]/ ? mbchar.downcase : mbchar
+ }.join
+ rest = @line.byteslice((@byte_pointer + byte_size)..-1)
+ @line = @line.byteslice(0, @byte_pointer) + part
+ @byte_pointer = @line.bytesize
+ @cursor = calculate_width(@line)
+ @cursor_max = @cursor + calculate_width(rest)
+ @line += rest
+ end
+ end
+
+ private def em_upper_case(key)
+ if @line.bytesize > @byte_pointer
+ byte_size, = Reline::Unicode.em_forward_word(@line, @byte_pointer)
+ part = @line.byteslice(@byte_pointer, byte_size).grapheme_clusters.map { |mbchar|
+ mbchar =~ /[a-z]/ ? mbchar.upcase : mbchar
+ }.join
+ rest = @line.byteslice((@byte_pointer + byte_size)..-1)
+ @line = @line.byteslice(0, @byte_pointer) + part
+ @byte_pointer = @line.bytesize
+ @cursor = calculate_width(@line)
+ @cursor_max = @cursor + calculate_width(rest)
+ @line += rest
+ end
+ end
+
+ private def em_kill_region(key)
+ if @byte_pointer > 0
+ byte_size, width = Reline::Unicode.em_big_backward_word(@line, @byte_pointer)
+ @line, deleted = byteslice!(@line, @byte_pointer - byte_size, byte_size)
+ @byte_pointer -= byte_size
+ @cursor -= width
+ @cursor_max -= width
+ @kill_ring.append(deleted)
+ end
+ end
+
+ private def copy_for_vi(text)
+ if @config.editing_mode_is?(:vi_insert) or @config.editing_mode_is?(:vi_command)
+ @vi_clipboard = text
+ end
+ end
+
+ private def vi_insert(key)
+ @config.editing_mode = :vi_insert
+ end
+
+ private def vi_add(key)
+ @config.editing_mode = :vi_insert
+ ed_next_char(key)
+ end
+
+ private def vi_command_mode(key)
+ ed_prev_char(key)
+ @config.editing_mode = :vi_command
+ end
+
+ private def vi_next_word(key, arg: 1)
+ if @line.bytesize > @byte_pointer
+ byte_size, width = Reline::Unicode.vi_forward_word(@line, @byte_pointer)
+ @byte_pointer += byte_size
+ @cursor += width
+ end
+ arg -= 1
+ vi_next_word(key, arg: arg) if arg > 0
+ end
+
+ private def vi_prev_word(key, arg: 1)
+ if @byte_pointer > 0
+ byte_size, width = Reline::Unicode.vi_backward_word(@line, @byte_pointer)
+ @byte_pointer -= byte_size
+ @cursor -= width
+ end
+ arg -= 1
+ vi_prev_word(key, arg: arg) if arg > 0
+ end
+
+ private def vi_end_word(key, arg: 1)
+ if @line.bytesize > @byte_pointer
+ byte_size, width = Reline::Unicode.vi_forward_end_word(@line, @byte_pointer)
+ @byte_pointer += byte_size
+ @cursor += width
+ end
+ arg -= 1
+ vi_end_word(key, arg: arg) if arg > 0
+ end
+
+ private def vi_next_big_word(key, arg: 1)
+ if @line.bytesize > @byte_pointer
+ byte_size, width = Reline::Unicode.vi_big_forward_word(@line, @byte_pointer)
+ @byte_pointer += byte_size
+ @cursor += width
+ end
+ arg -= 1
+ vi_next_big_word(key, arg: arg) if arg > 0
+ end
+
+ private def vi_prev_big_word(key, arg: 1)
+ if @byte_pointer > 0
+ byte_size, width = Reline::Unicode.vi_big_backward_word(@line, @byte_pointer)
+ @byte_pointer -= byte_size
+ @cursor -= width
+ end
+ arg -= 1
+ vi_prev_big_word(key, arg: arg) if arg > 0
+ end
+
+ private def vi_end_big_word(key, arg: 1)
+ if @line.bytesize > @byte_pointer
+ byte_size, width = Reline::Unicode.vi_big_forward_end_word(@line, @byte_pointer)
+ @byte_pointer += byte_size
+ @cursor += width
+ end
+ arg -= 1
+ vi_end_big_word(key, arg: arg) if arg > 0
+ end
+
+ private def vi_delete_prev_char(key)
+ if @is_multiline and @cursor == 0 and @line_index > 0
+ @buffer_of_lines[@line_index] = @line
+ @cursor = calculate_width(@buffer_of_lines[@line_index - 1])
+ @byte_pointer = @buffer_of_lines[@line_index - 1].bytesize
+ @buffer_of_lines[@line_index - 1] += @buffer_of_lines.delete_at(@line_index)
+ @line_index -= 1
+ @line = @buffer_of_lines[@line_index]
+ @cursor_max = calculate_width(@line)
+ @rerender_all = true
+ elsif @cursor > 0
+ byte_size = Reline::Unicode.get_prev_mbchar_size(@line, @byte_pointer)
+ @byte_pointer -= byte_size
+ @line, mbchar = byteslice!(@line, @byte_pointer, byte_size)
+ width = Reline::Unicode.get_mbchar_width(mbchar)
+ @cursor -= width
+ @cursor_max -= width
+ end
+ end
+
+ private def ed_delete_prev_char(key, arg: 1)
+ deleted = ''
+ arg.times do
+ if @cursor > 0
+ byte_size = Reline::Unicode.get_prev_mbchar_size(@line, @byte_pointer)
+ @byte_pointer -= byte_size
+ @line, mbchar = byteslice!(@line, @byte_pointer, byte_size)
+ deleted.prepend(mbchar)
+ width = Reline::Unicode.get_mbchar_width(mbchar)
+ @cursor -= width
+ @cursor_max -= width
+ end
+ end
+ copy_for_vi(deleted)
+ end
+
+ private def vi_zero(key)
+ @byte_pointer = 0
+ @cursor = 0
+ end
+
+ private def vi_change_meta(key)
+ end
+
+ private def vi_delete_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
+ }
+ end
+
+ private def vi_yank(key)
+ end
+
+ private def vi_end_of_transmission(key)
+ if @line.empty?
+ @line = nil
+ finish
+ end
+ end
+
+ private def vi_list_or_eof(key)
+ if @line.empty?
+ @line = nil
+ finish
+ else
+ # TODO: list
+ end
+ end
+
+ private def ed_delete_next_char(key, arg: 1)
+ unless @line.empty?
+ byte_size = Reline::Unicode.get_next_mbchar_size(@line, @byte_pointer)
+ @line, mbchar = byteslice!(@line, @byte_pointer, byte_size)
+ copy_for_vi(mbchar)
+ width = Reline::Unicode.get_mbchar_width(mbchar)
+ @cursor_max -= width # FIXME
+ if @cursor > 0 and @cursor >= @cursor_max
+ @byte_pointer -= byte_size
+ @cursor -= width
+ end
+ end
+ arg -= 1
+ ed_delete_next_char(key, arg: arg) if arg > 0
+ end
+
+ private def vi_to_history_line(key)
+ if Reline::HISTORY.empty?
+ return
+ end
+ if @history_pointer.nil?
+ @history_pointer = 0
+ @line_backup_in_history = @line
+ @line = Reline::HISTORY[@history_pointer]
+ @cursor_max = calculate_width(@line)
+ @cursor = 0
+ @byte_pointer = 0
+ elsif @history_pointer.zero?
+ return
+ else
+ Reline::HISTORY[@history_pointer] = @line
+ @history_pointer = 0
+ @line = Reline::HISTORY[@history_pointer]
+ @cursor_max = calculate_width(@line)
+ @cursor = 0
+ @byte_pointer = 0
+ end
+ end
+
+ private def vi_histedit(key)
+ path = Tempfile.open { |fp|
+ fp.write @line
+ fp.path
+ }
+ system("#{ENV['EDITOR']} #{path}")
+ @line = Pathname.new(path).read
+ finish
+ end
+
+ private def vi_paste_prev(key, arg: 1)
+ if @vi_clipboard.size > 0
+ @line = byteinsert(@line, @byte_pointer, @vi_clipboard)
+ @cursor_max += calculate_width(@vi_clipboard)
+ cursor_point = @vi_clipboard.grapheme_clusters[0..-2].join
+ @cursor += calculate_width(cursor_point)
+ @byte_pointer += cursor_point.bytesize
+ end
+ arg -= 1
+ vi_paste_prev(key, arg: arg) if arg > 0
+ end
+
+ private def vi_paste_next(key, arg: 1)
+ if @vi_clipboard.size > 0
+ byte_size = Reline::Unicode.get_next_mbchar_size(@line, @byte_pointer)
+ @line = byteinsert(@line, @byte_pointer + byte_size, @vi_clipboard)
+ @cursor_max += calculate_width(@vi_clipboard)
+ @cursor += calculate_width(@vi_clipboard)
+ @byte_pointer += @vi_clipboard.bytesize
+ end
+ arg -= 1
+ vi_paste_next(key, arg: arg) if arg > 0
+ end
+
+ private def ed_argument_digit(key)
+ if @vi_arg.nil?
+ unless key.chr.to_i.zero?
+ @vi_arg = key.chr.to_i
+ end
+ else
+ @vi_arg = @vi_arg * 10 + key.chr.to_i
+ end
+ end
+
+ private def vi_to_column(key, arg: 0)
+ @byte_pointer, @cursor = @line.grapheme_clusters.inject([0, 0]) { |total, gc|
+ # total has [byte_size, cursor]
+ mbchar_width = Reline::Unicode.get_mbchar_width(gc)
+ if (total.last + mbchar_width) >= arg
+ break total
+ elsif (total.last + mbchar_width) >= @cursor_max
+ break total
+ else
+ total = [total.first + gc.bytesize, total.last + mbchar_width]
+ total
+ end
+ }
+ end
+
+ private def vi_replace_char(key, arg: 1)
+ @waiting_proc = ->(key) {
+ if arg == 1
+ byte_size = Reline::Unicode.get_next_mbchar_size(@line, @byte_pointer)
+ before = @line.byteslice(0, @byte_pointer)
+ remaining_point = @byte_pointer + byte_size
+ after = @line.byteslice(remaining_point, @line.size - remaining_point)
+ @line = before + key.chr + after
+ @cursor_max = calculate_width(@line)
+ @waiting_proc = nil
+ elsif arg > 1
+ byte_size = 0
+ arg.times do
+ byte_size += Reline::Unicode.get_next_mbchar_size(@line, @byte_pointer + byte_size)
+ end
+ before = @line.byteslice(0, @byte_pointer)
+ remaining_point = @byte_pointer + byte_size
+ after = @line.byteslice(remaining_point, @line.size - remaining_point)
+ replaced = key.chr * arg
+ @line = before + replaced + after
+ @byte_pointer += replaced.bytesize
+ @cursor += calculate_width(replaced)
+ @cursor_max = calculate_width(@line)
+ @waiting_proc = nil
+ end
+ }
+ end
+
+ private def vi_next_char(key, arg: 1)
+ @waiting_proc = ->(key_for_proc) { search_next_char(key_for_proc, arg) }
+ end
+
+ private def search_next_char(key, arg)
+ if key.instance_of?(String)
+ inputed_char = key
+ else
+ inputed_char = key.chr
+ end
+ total = nil
+ found = false
+ @line.byteslice(@byte_pointer..-1).grapheme_clusters.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)
+ total = [total.first + mbchar.bytesize, total.last + width]
+ end
+ end
+ if found and total
+ byte_size, width = total
+ @byte_pointer += byte_size
+ @cursor += width
+ end
+ @waiting_proc = nil
+ end
+end
diff --git a/lib/reline/reline.gemspec b/lib/reline/reline.gemspec
new file mode 100644
index 0000000000..9ff9ca2128
--- /dev/null
+++ b/lib/reline/reline.gemspec
@@ -0,0 +1,25 @@
+
+lib = File.expand_path('../lib', __FILE__)
+$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
+require 'reline/version'
+
+Gem::Specification.new do |spec|
+ spec.name = 'reline'
+ spec.version = Reline::VERSION
+ spec.authors = ['aycabta']
+ spec.email = ['aycabta@gmail.com']
+
+ spec.summary = %q{Alternative GNU Readline or Editline implementation by pure Ruby.}
+ spec.description = %q{Alternative GNU Readline or Editline implementation by pure Ruby.}
+ spec.homepage = 'https://github.com/aycabta/reline'
+ spec.license = 'Ruby License'
+
+ spec.files = Dir['BSDL', 'COPYING', 'README.md', 'lib/**/*']
+ 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 'test-unit'
+end
diff --git a/lib/reline/unicode.rb b/lib/reline/unicode.rb
new file mode 100644
index 0000000000..bdb182f59c
--- /dev/null
+++ b/lib/reline/unicode.rb
@@ -0,0 +1,415 @@
+class Reline::Unicode
+ EscapedPairs = {
+ 0x00 => '^@',
+ 0x01 => '^A', # C-a
+ 0x02 => '^B',
+ 0x03 => '^C',
+ 0x04 => '^D',
+ 0x05 => '^E',
+ 0x06 => '^F',
+ 0x07 => '^G',
+ 0x08 => '^H', # Backspace
+ 0x09 => '^I',
+ 0x0A => '^J',
+ 0x0B => '^K',
+ 0x0C => '^L',
+ 0x0D => '^M', # Enter
+ 0x0E => '^N',
+ 0x0F => '^O',
+ 0x10 => '^P',
+ 0x11 => '^Q',
+ 0x12 => '^R',
+ 0x13 => '^S',
+ 0x14 => '^T',
+ 0x15 => '^U',
+ 0x16 => '^V',
+ 0x17 => '^W',
+ 0x18 => '^X',
+ 0x19 => '^Y',
+ 0x1A => '^Z', # C-z
+ 0x1B => '^[', # C-[ C-3
+ 0x1D => '^]', # C-]
+ 0x1E => '^^', # C-~ C-6
+ 0x1F => '^_', # C-_ C-7
+ 0x7F => '^?', # C-? C-8
+ }
+ EscapedChars = EscapedPairs.keys.map(&:chr)
+
+ def self.get_mbchar_byte_size_by_first_char(c)
+ # Checks UTF-8 character byte size
+ case c.ord
+ # 0b0xxxxxxx
+ when ->(code) { (code ^ 0b10000000).allbits?(0b10000000) } then 1
+ # 0b110xxxxx
+ when ->(code) { (code ^ 0b00100000).allbits?(0b11100000) } then 2
+ # 0b1110xxxx
+ when ->(code) { (code ^ 0b00010000).allbits?(0b11110000) } then 3
+ # 0b11110xxx
+ when ->(code) { (code ^ 0b00001000).allbits?(0b11111000) } then 4
+ # 0b111110xx
+ when ->(code) { (code ^ 0b00000100).allbits?(0b11111100) } then 5
+ # 0b1111110x
+ when ->(code) { (code ^ 0b00000010).allbits?(0b11111110) } then 6
+ # successor of mbchar
+ else 0
+ end
+ end
+
+ def self.get_mbchar_width(mbchar)
+ case mbchar.encode(Encoding::UTF_8)
+ when *EscapedChars # ^ + char, such as ^M, ^H, ^[, ...
+ 2
+ when /^\u{2E3B}/ # THREE-EM DASH
+ 3
+ when /^\p{M}/
+ 0
+ when EastAsianWidth::TYPE_A
+ Reline.ambiguous_width
+ when EastAsianWidth::TYPE_F, EastAsianWidth::TYPE_W
+ 2
+ when EastAsianWidth::TYPE_H, EastAsianWidth::TYPE_NA, EastAsianWidth::TYPE_N
+ 1
+ else
+ nil
+ end
+ end
+
+ def self.get_next_mbchar_size(line, byte_pointer)
+ grapheme = line.byteslice(byte_pointer..-1).grapheme_clusters.first
+ grapheme ? grapheme.bytesize : 0
+ end
+
+ def self.get_prev_mbchar_size(line, byte_pointer)
+ if byte_pointer.zero?
+ 0
+ else
+ grapheme = line.byteslice(0..(byte_pointer - 1)).grapheme_clusters.last
+ grapheme ? grapheme.bytesize : 0
+ end
+ end
+
+ def self.em_forward_word(line, byte_pointer)
+ width = 0
+ byte_size = 0
+ while line.bytesize > (byte_pointer + byte_size)
+ size = get_next_mbchar_size(line, byte_pointer + byte_size)
+ mbchar = line.byteslice(byte_pointer + byte_size, size)
+ break if mbchar.encode(Encoding::UTF_8) =~ /\p{Word}/
+ width += get_mbchar_width(mbchar)
+ byte_size += size
+ end
+ while line.bytesize > (byte_pointer + byte_size)
+ size = get_next_mbchar_size(line, byte_pointer + byte_size)
+ mbchar = line.byteslice(byte_pointer + byte_size, size)
+ break if mbchar.encode(Encoding::UTF_8) =~ /\P{Word}/
+ width += get_mbchar_width(mbchar)
+ byte_size += size
+ end
+ [byte_size, width]
+ end
+
+ def self.em_forward_word_with_capitalization(line, byte_pointer)
+ width = 0
+ byte_size = 0
+ new_str = String.new
+ while line.bytesize > (byte_pointer + byte_size)
+ size = get_next_mbchar_size(line, byte_pointer + byte_size)
+ mbchar = line.byteslice(byte_pointer + byte_size, size)
+ break if mbchar.encode(Encoding::UTF_8) =~ /\p{Word}/
+ new_str += mbchar
+ width += get_mbchar_width(mbchar)
+ byte_size += size
+ end
+ first = true
+ while line.bytesize > (byte_pointer + byte_size)
+ size = get_next_mbchar_size(line, byte_pointer + byte_size)
+ mbchar = line.byteslice(byte_pointer + byte_size, size)
+ break if mbchar.encode(Encoding::UTF_8) =~ /\P{Word}/
+ if first
+ new_str += mbchar.upcase
+ first = false
+ else
+ new_str += mbchar.downcase
+ end
+ width += get_mbchar_width(mbchar)
+ byte_size += size
+ end
+ [byte_size, width, new_str]
+ end
+
+ def self.em_backward_word(line, byte_pointer)
+ width = 0
+ byte_size = 0
+ while 0 < (byte_pointer - byte_size)
+ size = get_prev_mbchar_size(line, byte_pointer - byte_size)
+ mbchar = line.byteslice(byte_pointer - byte_size - size, size)
+ break if mbchar.encode(Encoding::UTF_8) =~ /\p{Word}/
+ width += get_mbchar_width(mbchar)
+ byte_size += size
+ end
+ while 0 < (byte_pointer - byte_size)
+ size = get_prev_mbchar_size(line, byte_pointer - byte_size)
+ mbchar = line.byteslice(byte_pointer - byte_size - size, size)
+ break if mbchar.encode(Encoding::UTF_8) =~ /\P{Word}/
+ width += get_mbchar_width(mbchar)
+ byte_size += size
+ end
+ [byte_size, width]
+ end
+
+ def self.em_big_backward_word(line, byte_pointer)
+ width = 0
+ byte_size = 0
+ while 0 < (byte_pointer - byte_size)
+ size = get_prev_mbchar_size(line, byte_pointer - byte_size)
+ mbchar = line.byteslice(byte_pointer - byte_size - size, size)
+ break if mbchar =~ /\S/
+ width += get_mbchar_width(mbchar)
+ byte_size += size
+ end
+ while 0 < (byte_pointer - byte_size)
+ size = get_prev_mbchar_size(line, byte_pointer - byte_size)
+ mbchar = line.byteslice(byte_pointer - byte_size - size, size)
+ break if mbchar =~ /\s/
+ width += get_mbchar_width(mbchar)
+ byte_size += size
+ end
+ [byte_size, width]
+ end
+
+ def self.vi_big_forward_word(line, byte_pointer)
+ width = 0
+ byte_size = 0
+ while (line.bytesize - 1) > (byte_pointer + byte_size)
+ size = get_next_mbchar_size(line, byte_pointer + byte_size)
+ mbchar = line.byteslice(byte_pointer + byte_size, size)
+ break if mbchar =~ /\s/
+ width += get_mbchar_width(mbchar)
+ byte_size += size
+ end
+ while (line.bytesize - 1) > (byte_pointer + byte_size)
+ size = get_next_mbchar_size(line, byte_pointer + byte_size)
+ mbchar = line.byteslice(byte_pointer + byte_size, size)
+ break if mbchar =~ /\S/
+ width += get_mbchar_width(mbchar)
+ byte_size += size
+ end
+ [byte_size, width]
+ end
+
+ def self.vi_big_forward_end_word(line, byte_pointer)
+ if (line.bytesize - 1) > byte_pointer
+ size = get_next_mbchar_size(line, byte_pointer)
+ mbchar = line.byteslice(byte_pointer, size)
+ width = get_mbchar_width(mbchar)
+ byte_size = size
+ else
+ return [0, 0]
+ end
+ while (line.bytesize - 1) > (byte_pointer + byte_size)
+ size = get_next_mbchar_size(line, byte_pointer + byte_size)
+ mbchar = line.byteslice(byte_pointer + byte_size, size)
+ break if mbchar =~ /\S/
+ width += get_mbchar_width(mbchar)
+ byte_size += size
+ end
+ prev_width = width
+ prev_byte_size = byte_size
+ while line.bytesize > (byte_pointer + byte_size)
+ size = get_next_mbchar_size(line, byte_pointer + byte_size)
+ mbchar = line.byteslice(byte_pointer + byte_size, size)
+ break if mbchar =~ /\s/
+ prev_width = width
+ prev_byte_size = byte_size
+ width += get_mbchar_width(mbchar)
+ byte_size += size
+ end
+ [prev_byte_size, prev_width]
+ end
+
+ def self.vi_big_backward_word(line, byte_pointer)
+ width = 0
+ byte_size = 0
+ while 0 < (byte_pointer - byte_size)
+ size = get_prev_mbchar_size(line, byte_pointer - byte_size)
+ mbchar = line.byteslice(byte_pointer - byte_size - size, size)
+ break if mbchar =~ /\S/
+ width += get_mbchar_width(mbchar)
+ byte_size += size
+ end
+ while 0 < (byte_pointer - byte_size)
+ size = get_prev_mbchar_size(line, byte_pointer - byte_size)
+ mbchar = line.byteslice(byte_pointer - byte_size - size, size)
+ break if mbchar =~ /\s/
+ width += get_mbchar_width(mbchar)
+ byte_size += size
+ end
+ [byte_size, width]
+ end
+
+ def self.vi_forward_word(line, byte_pointer)
+ if (line.bytesize - 1) > byte_pointer
+ size = get_next_mbchar_size(line, byte_pointer)
+ mbchar = line.byteslice(byte_pointer, size)
+ if mbchar =~ /\w/
+ started_by = :word
+ elsif mbchar =~ /\s/
+ started_by = :space
+ else
+ started_by = :non_word_printable
+ end
+ width = get_mbchar_width(mbchar)
+ byte_size = size
+ else
+ return [0, 0]
+ end
+ while (line.bytesize - 1) > (byte_pointer + byte_size)
+ size = get_next_mbchar_size(line, byte_pointer + byte_size)
+ mbchar = line.byteslice(byte_pointer + byte_size, size)
+ case started_by
+ when :word
+ break if mbchar =~ /\W/
+ when :space
+ break if mbchar =~ /\S/
+ when :non_word_printable
+ break if mbchar =~ /\w|\s/
+ end
+ width += get_mbchar_width(mbchar)
+ byte_size += size
+ end
+ while (line.bytesize - 1) > (byte_pointer + byte_size)
+ size = get_next_mbchar_size(line, byte_pointer + byte_size)
+ mbchar = line.byteslice(byte_pointer + byte_size, size)
+ break if mbchar =~ /\S/
+ width += get_mbchar_width(mbchar)
+ byte_size += size
+ end
+ [byte_size, width]
+ end
+
+ def self.vi_forward_end_word(line, byte_pointer)
+ if (line.bytesize - 1) > byte_pointer
+ size = get_next_mbchar_size(line, byte_pointer)
+ mbchar = line.byteslice(byte_pointer, size)
+ if mbchar =~ /\w/
+ started_by = :word
+ elsif mbchar =~ /\s/
+ started_by = :space
+ else
+ started_by = :non_word_printable
+ end
+ width = get_mbchar_width(mbchar)
+ byte_size = size
+ else
+ return [0, 0]
+ end
+ if (line.bytesize - 1) > (byte_pointer + byte_size)
+ size = get_next_mbchar_size(line, byte_pointer + byte_size)
+ mbchar = line.byteslice(byte_pointer + byte_size, size)
+ if mbchar =~ /\w/
+ second = :word
+ elsif mbchar =~ /\s/
+ second = :space
+ else
+ second = :non_word_printable
+ end
+ second_width = get_mbchar_width(mbchar)
+ second_byte_size = size
+ else
+ return [byte_size, width]
+ end
+ if second == :space
+ width += second_width
+ byte_size += second_byte_size
+ while (line.bytesize - 1) > (byte_pointer + byte_size)
+ size = get_next_mbchar_size(line, byte_pointer + byte_size)
+ mbchar = line.byteslice(byte_pointer + byte_size, size)
+ if mbchar =~ /\S/
+ if mbchar =~ /\w/
+ started_by = :word
+ else
+ started_by = :non_word_printable
+ end
+ break
+ end
+ width += get_mbchar_width(mbchar)
+ byte_size += size
+ end
+ else
+ case [started_by, second]
+ when [:word, :non_word_printable], [:non_word_printable, :word]
+ started_by = second
+ else
+ width += second_width
+ byte_size += second_byte_size
+ started_by = second
+ end
+ end
+ prev_width = width
+ prev_byte_size = byte_size
+ while line.bytesize > (byte_pointer + byte_size)
+ size = get_next_mbchar_size(line, byte_pointer + byte_size)
+ mbchar = line.byteslice(byte_pointer + byte_size, size)
+ case started_by
+ when :word
+ break if mbchar =~ /\W/
+ when :non_word_printable
+ break if mbchar =~ /[\w\s]/
+ end
+ prev_width = width
+ prev_byte_size = byte_size
+ width += get_mbchar_width(mbchar)
+ byte_size += size
+ end
+ [prev_byte_size, prev_width]
+ end
+
+ def self.vi_backward_word(line, byte_pointer)
+ width = 0
+ byte_size = 0
+ while 0 < (byte_pointer - byte_size)
+ size = get_prev_mbchar_size(line, byte_pointer - byte_size)
+ mbchar = line.byteslice(byte_pointer - byte_size - size, size)
+ if mbchar =~ /\S/
+ if mbchar =~ /\w/
+ started_by = :word
+ else
+ started_by = :non_word_printable
+ end
+ break
+ end
+ width += get_mbchar_width(mbchar)
+ byte_size += size
+ end
+ while 0 < (byte_pointer - byte_size)
+ size = get_prev_mbchar_size(line, byte_pointer - byte_size)
+ mbchar = line.byteslice(byte_pointer - byte_size - size, size)
+ case started_by
+ when :word
+ break if mbchar =~ /\W/
+ when :non_word_printable
+ break if mbchar =~ /[\w\s]/
+ end
+ width += get_mbchar_width(mbchar)
+ byte_size += size
+ end
+ [byte_size, width]
+ end
+
+ def self.ed_move_to_begin(line)
+ width = 0
+ byte_size = 0
+ while (line.bytesize - 1) > byte_size
+ size = get_next_mbchar_size(line, byte_size)
+ mbchar = line.byteslice(byte_size, size)
+ if mbchar =~ /\S/
+ break
+ end
+ width += get_mbchar_width(mbchar)
+ byte_size += size
+ end
+ [byte_size, width]
+ end
+end
+
+require 'reline/unicode/east_asian_width'
diff --git a/lib/reline/unicode/east_asian_width.rb b/lib/reline/unicode/east_asian_width.rb
new file mode 100644
index 0000000000..4eea3a7cdf
--- /dev/null
+++ b/lib/reline/unicode/east_asian_width.rb
@@ -0,0 +1,1145 @@
+class Reline::Unicode::EastAsianWidth
+ # This is based on EastAsianWidth.txt
+ # http://www.unicode.org/Public/10.0.0/ucd/EastAsianWidth.txt
+
+ # Fullwidth
+ TYPE_F = /^(
+ \u{3000} |
+ [\u{FF01}-\u{FF60}] |
+ [\u{FFE0}-\u{FFE6}]
+ )/x
+
+ # Halfwidth
+ TYPE_H = /^(
+ \u{20A9} |
+ [\u{FF61}-\u{FFBE}] |
+ [\u{FFC2}-\u{FFC7}] |
+ [\u{FFCA}-\u{FFCF}] |
+ [\u{FFD2}-\u{FFD7}] |
+ [\u{FFDA}-\u{FFDC}] |
+ [\u{FFE8}-\u{FFEE}]
+ )/x
+
+ # Wide
+ TYPE_W = /^(
+ [\u{1100}-\u{115F}] |
+ [\u{231A}-\u{231B}] |
+ [\u{2329}-\u{232A}] |
+ [\u{23E9}-\u{23EC}] |
+ \u{23F0} |
+ \u{23F3} |
+ [\u{25FD}-\u{25FE}] |
+ [\u{2614}-\u{2615}] |
+ [\u{2648}-\u{2653}] |
+ \u{267F} |
+ \u{2693} |
+ \u{26A1} |
+ [\u{26AA}-\u{26AB}] |
+ [\u{26BD}-\u{26BE}] |
+ [\u{26C4}-\u{26C5}] |
+ \u{26CE} |
+ \u{26D4} |
+ \u{26EA} |
+ [\u{26F2}-\u{26F3}] |
+ \u{26F5} |
+ \u{26FA} |
+ \u{26FD} |
+ \u{2705} |
+ [\u{270A}-\u{270B}] |
+ \u{2728} |
+ \u{274C} |
+ \u{274E} |
+ [\u{2753}-\u{2755}] |
+ \u{2757} |
+ [\u{2795}-\u{2797}] |
+ \u{27B0} |
+ \u{27BF} |
+ [\u{2B1B}-\u{2B1C}] |
+ \u{2B50} |
+ \u{2B55} |
+ [\u{2E80}-\u{2E99}] |
+ [\u{2E9B}-\u{2EF3}] |
+ [\u{2F00}-\u{2FD5}] |
+ [\u{2FF0}-\u{2FFB}] |
+ [\u{3001}-\u{303E}] |
+ [\u{3041}-\u{3096}] |
+ [\u{3099}-\u{30FF}] |
+ [\u{3105}-\u{312F}] |
+ [\u{3131}-\u{318E}] |
+ [\u{3190}-\u{31BA}] |
+ [\u{31C0}-\u{31E3}] |
+ [\u{31F0}-\u{321E}] |
+ [\u{3220}-\u{3247}] |
+ [\u{3250}-\u{4DBF}] |
+ [\u{4E00}-\u{A48C}] |
+ [\u{A490}-\u{A4C6}] |
+ [\u{A960}-\u{A97C}] |
+ [\u{AC00}-\u{D7A3}] |
+ [\u{F900}-\u{FAFF}] |
+ [\u{FE10}-\u{FE19}] |
+ [\u{FE30}-\u{FE52}] |
+ [\u{FE54}-\u{FE66}] |
+ [\u{FE68}-\u{FE6B}] |
+ [\u{16FE0}-\u{16FE3}] |
+ [\u{17000}-\u{187F7}] |
+ [\u{18800}-\u{18AF2}] |
+ [\u{1B000}-\u{1B11E}] |
+ [\u{1B150}-\u{1B152}] |
+ [\u{1B164}-\u{1B167}] |
+ [\u{1B170}-\u{1B2FB}] |
+ \u{1F004} |
+ \u{1F0CF} |
+ \u{1F18E} |
+ [\u{1F191}-\u{1F19A}] |
+ [\u{1F200}-\u{1F202}] |
+ [\u{1F210}-\u{1F23B}] |
+ [\u{1F240}-\u{1F248}] |
+ [\u{1F250}-\u{1F251}] |
+ [\u{1F260}-\u{1F265}] |
+ [\u{1F300}-\u{1F320}] |
+ [\u{1F32D}-\u{1F335}] |
+ [\u{1F337}-\u{1F37C}] |
+ [\u{1F37E}-\u{1F393}] |
+ [\u{1F3A0}-\u{1F3CA}] |
+ [\u{1F3CF}-\u{1F3D3}] |
+ [\u{1F3E0}-\u{1F3F0}] |
+ \u{1F3F4} |
+ [\u{1F3F8}-\u{1F43E}] |
+ \u{1F440} |
+ [\u{1F442}-\u{1F4FC}] |
+ [\u{1F4FF}-\u{1F53D}] |
+ [\u{1F54B}-\u{1F54E}] |
+ [\u{1F550}-\u{1F567}] |
+ \u{1F57A} |
+ [\u{1F595}-\u{1F596}] |
+ \u{1F5A4} |
+ [\u{1F5FB}-\u{1F64F}] |
+ [\u{1F680}-\u{1F6C5}] |
+ \u{1F6CC} |
+ [\u{1F6D0}-\u{1F6D2}] |
+ \u{1F6D5} |
+ [\u{1F6EB}-\u{1F6EC}] |
+ [\u{1F6F4}-\u{1F6FA}] |
+ [\u{1F7E0}-\u{1F7EB}] |
+ [\u{1F90D}-\u{1F971}] |
+ [\u{1F973}-\u{1F976}] |
+ [\u{1F97A}-\u{1F9A2}] |
+ [\u{1F9A5}-\u{1F9AA}] |
+ [\u{1F9AE}-\u{1F9CA}] |
+ [\u{1F9CD}-\u{1F9FF}] |
+ [\u{1FA70}-\u{1FA73}] |
+ [\u{1FA78}-\u{1FA7A}] |
+ [\u{1FA80}-\u{1FA82}] |
+ [\u{1FA90}-\u{1FA95}] |
+ [\u{20000}-\u{2FFFD}] |
+ [\u{30000}-\u{3FFFD}]
+ )/x
+
+ # Narrow
+ TYPE_NA = /^(
+ [\u{0020}-\u{007E}] |
+ [\u{00A2}-\u{00A3}] |
+ [\u{00A5}-\u{00A6}] |
+ \u{00AC} |
+ \u{00AF} |
+ [\u{27E6}-\u{27ED}] |
+ [\u{2985}-\u{2986}]
+ )/x
+
+ # Ambiguous
+ TYPE_A = /^(
+ \u{00A1} |
+ \u{00A4} |
+ [\u{00A7}-\u{00A8}] |
+ \u{00AA} |
+ [\u{00AD}-\u{00AE}] |
+ [\u{00B0}-\u{00B4}] |
+ [\u{00B6}-\u{00BA}] |
+ [\u{00BC}-\u{00BF}] |
+ \u{00C6} |
+ \u{00D0} |
+ [\u{00D7}-\u{00D8}] |
+ [\u{00DE}-\u{00E1}] |
+ \u{00E6} |
+ [\u{00E8}-\u{00EA}] |
+ [\u{00EC}-\u{00ED}] |
+ \u{00F0} |
+ [\u{00F2}-\u{00F3}] |
+ [\u{00F7}-\u{00FA}] |
+ \u{00FC} |
+ \u{00FE} |
+ \u{0101} |
+ \u{0111} |
+ \u{0113} |
+ \u{011B} |
+ [\u{0126}-\u{0127}] |
+ \u{012B} |
+ [\u{0131}-\u{0133}] |
+ \u{0138} |
+ [\u{013F}-\u{0142}] |
+ \u{0144} |
+ [\u{0148}-\u{014B}] |
+ \u{014D} |
+ [\u{0152}-\u{0153}] |
+ [\u{0166}-\u{0167}] |
+ \u{016B} |
+ \u{01CE} |
+ \u{01D0} |
+ \u{01D2} |
+ \u{01D4} |
+ \u{01D6} |
+ \u{01D8} |
+ \u{01DA} |
+ \u{01DC} |
+ \u{0251} |
+ \u{0261} |
+ \u{02C4} |
+ \u{02C7} |
+ [\u{02C9}-\u{02CB}] |
+ \u{02CD} |
+ \u{02D0} |
+ [\u{02D8}-\u{02DB}] |
+ \u{02DD} |
+ \u{02DF} |
+ [\u{0300}-\u{036F}] |
+ [\u{0391}-\u{03A1}] |
+ [\u{03A3}-\u{03A9}] |
+ [\u{03B1}-\u{03C1}] |
+ [\u{03C3}-\u{03C9}] |
+ \u{0401} |
+ [\u{0410}-\u{044F}] |
+ \u{0451} |
+ \u{2010} |
+ [\u{2013}-\u{2016}] |
+ [\u{2018}-\u{2019}] |
+ [\u{201C}-\u{201D}] |
+ [\u{2020}-\u{2022}] |
+ [\u{2024}-\u{2027}] |
+ \u{2030} |
+ [\u{2032}-\u{2033}] |
+ \u{2035} |
+ \u{203B} |
+ \u{203E} |
+ \u{2074} |
+ \u{207F} |
+ [\u{2081}-\u{2084}] |
+ \u{20AC} |
+ \u{2103} |
+ \u{2105} |
+ \u{2109} |
+ \u{2113} |
+ \u{2116} |
+ [\u{2121}-\u{2122}] |
+ \u{2126} |
+ \u{212B} |
+ [\u{2153}-\u{2154}] |
+ [\u{215B}-\u{215E}] |
+ [\u{2160}-\u{216B}] |
+ [\u{2170}-\u{2179}] |
+ \u{2189} |
+ [\u{2190}-\u{2199}] |
+ [\u{21B8}-\u{21B9}] |
+ \u{21D2} |
+ \u{21D4} |
+ \u{21E7} |
+ \u{2200} |
+ [\u{2202}-\u{2203}] |
+ [\u{2207}-\u{2208}] |
+ \u{220B} |
+ \u{220F} |
+ \u{2211} |
+ \u{2215} |
+ \u{221A} |
+ [\u{221D}-\u{2220}] |
+ \u{2223} |
+ \u{2225} |
+ [\u{2227}-\u{222C}] |
+ \u{222E} |
+ [\u{2234}-\u{2237}] |
+ [\u{223C}-\u{223D}] |
+ \u{2248} |
+ \u{224C} |
+ \u{2252} |
+ [\u{2260}-\u{2261}] |
+ [\u{2264}-\u{2267}] |
+ [\u{226A}-\u{226B}] |
+ [\u{226E}-\u{226F}] |
+ [\u{2282}-\u{2283}] |
+ [\u{2286}-\u{2287}] |
+ \u{2295} |
+ \u{2299} |
+ \u{22A5} |
+ \u{22BF} |
+ \u{2312} |
+ [\u{2460}-\u{24E9}] |
+ [\u{24EB}-\u{254B}] |
+ [\u{2550}-\u{2573}] |
+ [\u{2580}-\u{258F}] |
+ [\u{2592}-\u{2595}] |
+ [\u{25A0}-\u{25A1}] |
+ [\u{25A3}-\u{25A9}] |
+ [\u{25B2}-\u{25B3}] |
+ [\u{25B6}-\u{25B7}] |
+ [\u{25BC}-\u{25BD}] |
+ [\u{25C0}-\u{25C1}] |
+ [\u{25C6}-\u{25C8}] |
+ \u{25CB} |
+ [\u{25CE}-\u{25D1}] |
+ [\u{25E2}-\u{25E5}] |
+ \u{25EF} |
+ [\u{2605}-\u{2606}] |
+ \u{2609} |
+ [\u{260E}-\u{260F}] |
+ \u{261C} |
+ \u{261E} |
+ \u{2640} |
+ \u{2642} |
+ [\u{2660}-\u{2661}] |
+ [\u{2663}-\u{2665}] |
+ [\u{2667}-\u{266A}] |
+ [\u{266C}-\u{266D}] |
+ \u{266F} |
+ [\u{269E}-\u{269F}] |
+ \u{26BF} |
+ [\u{26C6}-\u{26CD}] |
+ [\u{26CF}-\u{26D3}] |
+ [\u{26D5}-\u{26E1}] |
+ \u{26E3} |
+ [\u{26E8}-\u{26E9}] |
+ [\u{26EB}-\u{26F1}] |
+ \u{26F4} |
+ [\u{26F6}-\u{26F9}] |
+ [\u{26FB}-\u{26FC}] |
+ [\u{26FE}-\u{26FF}] |
+ \u{273D} |
+ [\u{2776}-\u{277F}] |
+ [\u{2B56}-\u{2B59}] |
+ [\u{3248}-\u{324F}] |
+ [\u{E000}-\u{F8FF}] |
+ [\u{FE00}-\u{FE0F}] |
+ \u{FFFD} |
+ [\u{1F100}-\u{1F10A}] |
+ [\u{1F110}-\u{1F12D}] |
+ [\u{1F130}-\u{1F169}] |
+ [\u{1F170}-\u{1F18D}] |
+ [\u{1F18F}-\u{1F190}] |
+ [\u{1F19B}-\u{1F1AC}] |
+ [\u{E0100}-\u{E01EF}] |
+ [\u{F0000}-\u{FFFFD}] |
+ [\u{100000}-\u{10FFFD}]
+ )/x
+
+ # Neutral
+ TYPE_N = /^(
+ [\u{0000}-\u{001F}] |
+ [\u{007F}-\u{00A0}] |
+ \u{00A9} |
+ \u{00AB} |
+ \u{00B5} |
+ \u{00BB} |
+ [\u{00C0}-\u{00C5}] |
+ [\u{00C7}-\u{00CF}] |
+ [\u{00D1}-\u{00D6}] |
+ [\u{00D9}-\u{00DD}] |
+ [\u{00E2}-\u{00E5}] |
+ \u{00E7} |
+ \u{00EB} |
+ [\u{00EE}-\u{00EF}] |
+ \u{00F1} |
+ [\u{00F4}-\u{00F6}] |
+ \u{00FB} |
+ \u{00FD} |
+ [\u{00FF}-\u{0100}] |
+ [\u{0102}-\u{0110}] |
+ \u{0112} |
+ [\u{0114}-\u{011A}] |
+ [\u{011C}-\u{0125}] |
+ [\u{0128}-\u{012A}] |
+ [\u{012C}-\u{0130}] |
+ [\u{0134}-\u{0137}] |
+ [\u{0139}-\u{013E}] |
+ \u{0143} |
+ [\u{0145}-\u{0147}] |
+ \u{014C} |
+ [\u{014E}-\u{0151}] |
+ [\u{0154}-\u{0165}] |
+ [\u{0168}-\u{016A}] |
+ [\u{016C}-\u{01CD}] |
+ \u{01CF} |
+ \u{01D1} |
+ \u{01D3} |
+ \u{01D5} |
+ \u{01D7} |
+ \u{01D9} |
+ \u{01DB} |
+ [\u{01DD}-\u{0250}] |
+ [\u{0252}-\u{0260}] |
+ [\u{0262}-\u{02C3}] |
+ [\u{02C5}-\u{02C6}] |
+ \u{02C8} |
+ \u{02CC} |
+ [\u{02CE}-\u{02CF}] |
+ [\u{02D1}-\u{02D7}] |
+ \u{02DC} |
+ \u{02DE} |
+ [\u{02E0}-\u{02FF}] |
+ [\u{0370}-\u{0377}] |
+ [\u{037A}-\u{037F}] |
+ [\u{0384}-\u{038A}] |
+ \u{038C} |
+ [\u{038E}-\u{0390}] |
+ [\u{03AA}-\u{03B0}] |
+ \u{03C2} |
+ [\u{03CA}-\u{0400}] |
+ [\u{0402}-\u{040F}] |
+ \u{0450} |
+ [\u{0452}-\u{052F}] |
+ [\u{0531}-\u{0556}] |
+ [\u{0559}-\u{058A}] |
+ [\u{058D}-\u{058F}] |
+ [\u{0591}-\u{05C7}] |
+ [\u{05D0}-\u{05EA}] |
+ [\u{05EF}-\u{05F4}] |
+ [\u{0600}-\u{061C}] |
+ [\u{061E}-\u{070D}] |
+ [\u{070F}-\u{074A}] |
+ [\u{074D}-\u{07B1}] |
+ [\u{07C0}-\u{07FA}] |
+ [\u{07FD}-\u{082D}] |
+ [\u{0830}-\u{083E}] |
+ [\u{0840}-\u{085B}] |
+ \u{085E} |
+ [\u{0860}-\u{086A}] |
+ [\u{08A0}-\u{08B4}] |
+ [\u{08B6}-\u{08BD}] |
+ [\u{08D3}-\u{0983}] |
+ [\u{0985}-\u{098C}] |
+ [\u{098F}-\u{0990}] |
+ [\u{0993}-\u{09A8}] |
+ [\u{09AA}-\u{09B0}] |
+ \u{09B2} |
+ [\u{09B6}-\u{09B9}] |
+ [\u{09BC}-\u{09C4}] |
+ [\u{09C7}-\u{09C8}] |
+ [\u{09CB}-\u{09CE}] |
+ \u{09D7} |
+ [\u{09DC}-\u{09DD}] |
+ [\u{09DF}-\u{09E3}] |
+ [\u{09E6}-\u{09FE}] |
+ [\u{0A01}-\u{0A03}] |
+ [\u{0A05}-\u{0A0A}] |
+ [\u{0A0F}-\u{0A10}] |
+ [\u{0A13}-\u{0A28}] |
+ [\u{0A2A}-\u{0A30}] |
+ [\u{0A32}-\u{0A33}] |
+ [\u{0A35}-\u{0A36}] |
+ [\u{0A38}-\u{0A39}] |
+ \u{0A3C} |
+ [\u{0A3E}-\u{0A42}] |
+ [\u{0A47}-\u{0A48}] |
+ [\u{0A4B}-\u{0A4D}] |
+ \u{0A51} |
+ [\u{0A59}-\u{0A5C}] |
+ \u{0A5E} |
+ [\u{0A66}-\u{0A76}] |
+ [\u{0A81}-\u{0A83}] |
+ [\u{0A85}-\u{0A8D}] |
+ [\u{0A8F}-\u{0A91}] |
+ [\u{0A93}-\u{0AA8}] |
+ [\u{0AAA}-\u{0AB0}] |
+ [\u{0AB2}-\u{0AB3}] |
+ [\u{0AB5}-\u{0AB9}] |
+ [\u{0ABC}-\u{0AC5}] |
+ [\u{0AC7}-\u{0AC9}] |
+ [\u{0ACB}-\u{0ACD}] |
+ \u{0AD0} |
+ [\u{0AE0}-\u{0AE3}] |
+ [\u{0AE6}-\u{0AF1}] |
+ [\u{0AF9}-\u{0AFF}] |
+ [\u{0B01}-\u{0B03}] |
+ [\u{0B05}-\u{0B0C}] |
+ [\u{0B0F}-\u{0B10}] |
+ [\u{0B13}-\u{0B28}] |
+ [\u{0B2A}-\u{0B30}] |
+ [\u{0B32}-\u{0B33}] |
+ [\u{0B35}-\u{0B39}] |
+ [\u{0B3C}-\u{0B44}] |
+ [\u{0B47}-\u{0B48}] |
+ [\u{0B4B}-\u{0B4D}] |
+ [\u{0B56}-\u{0B57}] |
+ [\u{0B5C}-\u{0B5D}] |
+ [\u{0B5F}-\u{0B63}] |
+ [\u{0B66}-\u{0B77}] |
+ [\u{0B82}-\u{0B83}] |
+ [\u{0B85}-\u{0B8A}] |
+ [\u{0B8E}-\u{0B90}] |
+ [\u{0B92}-\u{0B95}] |
+ [\u{0B99}-\u{0B9A}] |
+ \u{0B9C} |
+ [\u{0B9E}-\u{0B9F}] |
+ [\u{0BA3}-\u{0BA4}] |
+ [\u{0BA8}-\u{0BAA}] |
+ [\u{0BAE}-\u{0BB9}] |
+ [\u{0BBE}-\u{0BC2}] |
+ [\u{0BC6}-\u{0BC8}] |
+ [\u{0BCA}-\u{0BCD}] |
+ \u{0BD0} |
+ \u{0BD7} |
+ [\u{0BE6}-\u{0BFA}] |
+ [\u{0C00}-\u{0C0C}] |
+ [\u{0C0E}-\u{0C10}] |
+ [\u{0C12}-\u{0C28}] |
+ [\u{0C2A}-\u{0C39}] |
+ [\u{0C3D}-\u{0C44}] |
+ [\u{0C46}-\u{0C48}] |
+ [\u{0C4A}-\u{0C4D}] |
+ [\u{0C55}-\u{0C56}] |
+ [\u{0C58}-\u{0C5A}] |
+ [\u{0C60}-\u{0C63}] |
+ [\u{0C66}-\u{0C6F}] |
+ [\u{0C77}-\u{0C8C}] |
+ [\u{0C8E}-\u{0C90}] |
+ [\u{0C92}-\u{0CA8}] |
+ [\u{0CAA}-\u{0CB3}] |
+ [\u{0CB5}-\u{0CB9}] |
+ [\u{0CBC}-\u{0CC4}] |
+ [\u{0CC6}-\u{0CC8}] |
+ [\u{0CCA}-\u{0CCD}] |
+ [\u{0CD5}-\u{0CD6}] |
+ \u{0CDE} |
+ [\u{0CE0}-\u{0CE3}] |
+ [\u{0CE6}-\u{0CEF}] |
+ [\u{0CF1}-\u{0CF2}] |
+ [\u{0D00}-\u{0D03}] |
+ [\u{0D05}-\u{0D0C}] |
+ [\u{0D0E}-\u{0D10}] |
+ [\u{0D12}-\u{0D44}] |
+ [\u{0D46}-\u{0D48}] |
+ [\u{0D4A}-\u{0D4F}] |
+ [\u{0D54}-\u{0D63}] |
+ [\u{0D66}-\u{0D7F}] |
+ [\u{0D82}-\u{0D83}] |
+ [\u{0D85}-\u{0D96}] |
+ [\u{0D9A}-\u{0DB1}] |
+ [\u{0DB3}-\u{0DBB}] |
+ \u{0DBD} |
+ [\u{0DC0}-\u{0DC6}] |
+ \u{0DCA} |
+ [\u{0DCF}-\u{0DD4}] |
+ \u{0DD6} |
+ [\u{0DD8}-\u{0DDF}] |
+ [\u{0DE6}-\u{0DEF}] |
+ [\u{0DF2}-\u{0DF4}] |
+ [\u{0E01}-\u{0E3A}] |
+ [\u{0E3F}-\u{0E5B}] |
+ [\u{0E81}-\u{0E82}] |
+ \u{0E84} |
+ [\u{0E86}-\u{0E8A}] |
+ [\u{0E8C}-\u{0EA3}] |
+ \u{0EA5} |
+ [\u{0EA7}-\u{0EBD}] |
+ [\u{0EC0}-\u{0EC4}] |
+ \u{0EC6} |
+ [\u{0EC8}-\u{0ECD}] |
+ [\u{0ED0}-\u{0ED9}] |
+ [\u{0EDC}-\u{0EDF}] |
+ [\u{0F00}-\u{0F47}] |
+ [\u{0F49}-\u{0F6C}] |
+ [\u{0F71}-\u{0F97}] |
+ [\u{0F99}-\u{0FBC}] |
+ [\u{0FBE}-\u{0FCC}] |
+ [\u{0FCE}-\u{0FDA}] |
+ [\u{1000}-\u{10C5}] |
+ \u{10C7} |
+ \u{10CD} |
+ [\u{10D0}-\u{10FF}] |
+ [\u{1160}-\u{1248}] |
+ [\u{124A}-\u{124D}] |
+ [\u{1250}-\u{1256}] |
+ \u{1258} |
+ [\u{125A}-\u{125D}] |
+ [\u{1260}-\u{1288}] |
+ [\u{128A}-\u{128D}] |
+ [\u{1290}-\u{12B0}] |
+ [\u{12B2}-\u{12B5}] |
+ [\u{12B8}-\u{12BE}] |
+ \u{12C0} |
+ [\u{12C2}-\u{12C5}] |
+ [\u{12C8}-\u{12D6}] |
+ [\u{12D8}-\u{1310}] |
+ [\u{1312}-\u{1315}] |
+ [\u{1318}-\u{135A}] |
+ [\u{135D}-\u{137C}] |
+ [\u{1380}-\u{1399}] |
+ [\u{13A0}-\u{13F5}] |
+ [\u{13F8}-\u{13FD}] |
+ [\u{1400}-\u{169C}] |
+ [\u{16A0}-\u{16F8}] |
+ [\u{1700}-\u{170C}] |
+ [\u{170E}-\u{1714}] |
+ [\u{1720}-\u{1736}] |
+ [\u{1740}-\u{1753}] |
+ [\u{1760}-\u{176C}] |
+ [\u{176E}-\u{1770}] |
+ [\u{1772}-\u{1773}] |
+ [\u{1780}-\u{17DD}] |
+ [\u{17E0}-\u{17E9}] |
+ [\u{17F0}-\u{17F9}] |
+ [\u{1800}-\u{180E}] |
+ [\u{1810}-\u{1819}] |
+ [\u{1820}-\u{1878}] |
+ [\u{1880}-\u{18AA}] |
+ [\u{18B0}-\u{18F5}] |
+ [\u{1900}-\u{191E}] |
+ [\u{1920}-\u{192B}] |
+ [\u{1930}-\u{193B}] |
+ \u{1940} |
+ [\u{1944}-\u{196D}] |
+ [\u{1970}-\u{1974}] |
+ [\u{1980}-\u{19AB}] |
+ [\u{19B0}-\u{19C9}] |
+ [\u{19D0}-\u{19DA}] |
+ [\u{19DE}-\u{1A1B}] |
+ [\u{1A1E}-\u{1A5E}] |
+ [\u{1A60}-\u{1A7C}] |
+ [\u{1A7F}-\u{1A89}] |
+ [\u{1A90}-\u{1A99}] |
+ [\u{1AA0}-\u{1AAD}] |
+ [\u{1AB0}-\u{1ABE}] |
+ [\u{1B00}-\u{1B4B}] |
+ [\u{1B50}-\u{1B7C}] |
+ [\u{1B80}-\u{1BF3}] |
+ [\u{1BFC}-\u{1C37}] |
+ [\u{1C3B}-\u{1C49}] |
+ [\u{1C4D}-\u{1C88}] |
+ [\u{1C90}-\u{1CBA}] |
+ [\u{1CBD}-\u{1CC7}] |
+ [\u{1CD0}-\u{1CFA}] |
+ [\u{1D00}-\u{1DF9}] |
+ [\u{1DFB}-\u{1F15}] |
+ [\u{1F18}-\u{1F1D}] |
+ [\u{1F20}-\u{1F45}] |
+ [\u{1F48}-\u{1F4D}] |
+ [\u{1F50}-\u{1F57}] |
+ \u{1F59} |
+ \u{1F5B} |
+ \u{1F5D} |
+ [\u{1F5F}-\u{1F7D}] |
+ [\u{1F80}-\u{1FB4}] |
+ [\u{1FB6}-\u{1FC4}] |
+ [\u{1FC6}-\u{1FD3}] |
+ [\u{1FD6}-\u{1FDB}] |
+ [\u{1FDD}-\u{1FEF}] |
+ [\u{1FF2}-\u{1FF4}] |
+ [\u{1FF6}-\u{1FFE}] |
+ [\u{2000}-\u{200F}] |
+ [\u{2011}-\u{2012}] |
+ \u{2017} |
+ [\u{201A}-\u{201B}] |
+ [\u{201E}-\u{201F}] |
+ \u{2023} |
+ [\u{2028}-\u{202F}] |
+ \u{2031} |
+ \u{2034} |
+ [\u{2036}-\u{203A}] |
+ [\u{203C}-\u{203D}] |
+ [\u{203F}-\u{2064}] |
+ [\u{2066}-\u{2071}] |
+ [\u{2075}-\u{207E}] |
+ \u{2080} |
+ [\u{2085}-\u{208E}] |
+ [\u{2090}-\u{209C}] |
+ [\u{20A0}-\u{20A8}] |
+ [\u{20AA}-\u{20AB}] |
+ [\u{20AD}-\u{20BF}] |
+ [\u{20D0}-\u{20F0}] |
+ [\u{2100}-\u{2102}] |
+ \u{2104} |
+ [\u{2106}-\u{2108}] |
+ [\u{210A}-\u{2112}] |
+ [\u{2114}-\u{2115}] |
+ [\u{2117}-\u{2120}] |
+ [\u{2123}-\u{2125}] |
+ [\u{2127}-\u{212A}] |
+ [\u{212C}-\u{2152}] |
+ [\u{2155}-\u{215A}] |
+ \u{215F} |
+ [\u{216C}-\u{216F}] |
+ [\u{217A}-\u{2188}] |
+ [\u{218A}-\u{218B}] |
+ [\u{219A}-\u{21B7}] |
+ [\u{21BA}-\u{21D1}] |
+ \u{21D3} |
+ [\u{21D5}-\u{21E6}] |
+ [\u{21E8}-\u{21FF}] |
+ \u{2201} |
+ [\u{2204}-\u{2206}] |
+ [\u{2209}-\u{220A}] |
+ [\u{220C}-\u{220E}] |
+ \u{2210} |
+ [\u{2212}-\u{2214}] |
+ [\u{2216}-\u{2219}] |
+ [\u{221B}-\u{221C}] |
+ [\u{2221}-\u{2222}] |
+ \u{2224} |
+ \u{2226} |
+ \u{222D} |
+ [\u{222F}-\u{2233}] |
+ [\u{2238}-\u{223B}] |
+ [\u{223E}-\u{2247}] |
+ [\u{2249}-\u{224B}] |
+ [\u{224D}-\u{2251}] |
+ [\u{2253}-\u{225F}] |
+ [\u{2262}-\u{2263}] |
+ [\u{2268}-\u{2269}] |
+ [\u{226C}-\u{226D}] |
+ [\u{2270}-\u{2281}] |
+ [\u{2284}-\u{2285}] |
+ [\u{2288}-\u{2294}] |
+ [\u{2296}-\u{2298}] |
+ [\u{229A}-\u{22A4}] |
+ [\u{22A6}-\u{22BE}] |
+ [\u{22C0}-\u{2311}] |
+ [\u{2313}-\u{2319}] |
+ [\u{231C}-\u{2328}] |
+ [\u{232B}-\u{23E8}] |
+ [\u{23ED}-\u{23EF}] |
+ [\u{23F1}-\u{23F2}] |
+ [\u{23F4}-\u{2426}] |
+ [\u{2440}-\u{244A}] |
+ \u{24EA} |
+ [\u{254C}-\u{254F}] |
+ [\u{2574}-\u{257F}] |
+ [\u{2590}-\u{2591}] |
+ [\u{2596}-\u{259F}] |
+ \u{25A2} |
+ [\u{25AA}-\u{25B1}] |
+ [\u{25B4}-\u{25B5}] |
+ [\u{25B8}-\u{25BB}] |
+ [\u{25BE}-\u{25BF}] |
+ [\u{25C2}-\u{25C5}] |
+ [\u{25C9}-\u{25CA}] |
+ [\u{25CC}-\u{25CD}] |
+ [\u{25D2}-\u{25E1}] |
+ [\u{25E6}-\u{25EE}] |
+ [\u{25F0}-\u{25FC}] |
+ [\u{25FF}-\u{2604}] |
+ [\u{2607}-\u{2608}] |
+ [\u{260A}-\u{260D}] |
+ [\u{2610}-\u{2613}] |
+ [\u{2616}-\u{261B}] |
+ \u{261D} |
+ [\u{261F}-\u{263F}] |
+ \u{2641} |
+ [\u{2643}-\u{2647}] |
+ [\u{2654}-\u{265F}] |
+ \u{2662} |
+ \u{2666} |
+ \u{266B} |
+ \u{266E} |
+ [\u{2670}-\u{267E}] |
+ [\u{2680}-\u{2692}] |
+ [\u{2694}-\u{269D}] |
+ \u{26A0} |
+ [\u{26A2}-\u{26A9}] |
+ [\u{26AC}-\u{26BC}] |
+ [\u{26C0}-\u{26C3}] |
+ \u{26E2} |
+ [\u{26E4}-\u{26E7}] |
+ [\u{2700}-\u{2704}] |
+ [\u{2706}-\u{2709}] |
+ [\u{270C}-\u{2727}] |
+ [\u{2729}-\u{273C}] |
+ [\u{273E}-\u{274B}] |
+ \u{274D} |
+ [\u{274F}-\u{2752}] |
+ \u{2756} |
+ [\u{2758}-\u{2775}] |
+ [\u{2780}-\u{2794}] |
+ [\u{2798}-\u{27AF}] |
+ [\u{27B1}-\u{27BE}] |
+ [\u{27C0}-\u{27E5}] |
+ [\u{27EE}-\u{2984}] |
+ [\u{2987}-\u{2B1A}] |
+ [\u{2B1D}-\u{2B4F}] |
+ [\u{2B51}-\u{2B54}] |
+ [\u{2B5A}-\u{2B73}] |
+ [\u{2B76}-\u{2B95}] |
+ [\u{2B98}-\u{2C2E}] |
+ [\u{2C30}-\u{2C5E}] |
+ [\u{2C60}-\u{2CF3}] |
+ [\u{2CF9}-\u{2D25}] |
+ \u{2D27} |
+ \u{2D2D} |
+ [\u{2D30}-\u{2D67}] |
+ [\u{2D6F}-\u{2D70}] |
+ [\u{2D7F}-\u{2D96}] |
+ [\u{2DA0}-\u{2DA6}] |
+ [\u{2DA8}-\u{2DAE}] |
+ [\u{2DB0}-\u{2DB6}] |
+ [\u{2DB8}-\u{2DBE}] |
+ [\u{2DC0}-\u{2DC6}] |
+ [\u{2DC8}-\u{2DCE}] |
+ [\u{2DD0}-\u{2DD6}] |
+ [\u{2DD8}-\u{2DDE}] |
+ [\u{2DE0}-\u{2E4F}] |
+ \u{303F} |
+ [\u{4DC0}-\u{4DFF}] |
+ [\u{A4D0}-\u{A62B}] |
+ [\u{A640}-\u{A6F7}] |
+ [\u{A700}-\u{A7BF}] |
+ [\u{A7C2}-\u{A7C6}] |
+ [\u{A7F7}-\u{A82B}] |
+ [\u{A830}-\u{A839}] |
+ [\u{A840}-\u{A877}] |
+ [\u{A880}-\u{A8C5}] |
+ [\u{A8CE}-\u{A8D9}] |
+ [\u{A8E0}-\u{A953}] |
+ \u{A95F} |
+ [\u{A980}-\u{A9CD}] |
+ [\u{A9CF}-\u{A9D9}] |
+ [\u{A9DE}-\u{A9FE}] |
+ [\u{AA00}-\u{AA36}] |
+ [\u{AA40}-\u{AA4D}] |
+ [\u{AA50}-\u{AA59}] |
+ [\u{AA5C}-\u{AAC2}] |
+ [\u{AADB}-\u{AAF6}] |
+ [\u{AB01}-\u{AB06}] |
+ [\u{AB09}-\u{AB0E}] |
+ [\u{AB11}-\u{AB16}] |
+ [\u{AB20}-\u{AB26}] |
+ [\u{AB28}-\u{AB2E}] |
+ [\u{AB30}-\u{AB67}] |
+ [\u{AB70}-\u{ABED}] |
+ [\u{ABF0}-\u{ABF9}] |
+ [\u{D7B0}-\u{D7C6}] |
+ [\u{D7CB}-\u{D7FB}] |
+ [\u{FB00}-\u{FB06}] |
+ [\u{FB13}-\u{FB17}] |
+ [\u{FB1D}-\u{FB36}] |
+ [\u{FB38}-\u{FB3C}] |
+ \u{FB3E} |
+ [\u{FB40}-\u{FB41}] |
+ [\u{FB43}-\u{FB44}] |
+ [\u{FB46}-\u{FBC1}] |
+ [\u{FBD3}-\u{FD3F}] |
+ [\u{FD50}-\u{FD8F}] |
+ [\u{FD92}-\u{FDC7}] |
+ [\u{FDF0}-\u{FDFD}] |
+ [\u{FE20}-\u{FE2F}] |
+ [\u{FE70}-\u{FE74}] |
+ [\u{FE76}-\u{FEFC}] |
+ \u{FEFF} |
+ [\u{FFF9}-\u{FFFC}] |
+ [\u{10000}-\u{1000B}] |
+ [\u{1000D}-\u{10026}] |
+ [\u{10028}-\u{1003A}] |
+ [\u{1003C}-\u{1003D}] |
+ [\u{1003F}-\u{1004D}] |
+ [\u{10050}-\u{1005D}] |
+ [\u{10080}-\u{100FA}] |
+ [\u{10100}-\u{10102}] |
+ [\u{10107}-\u{10133}] |
+ [\u{10137}-\u{1018E}] |
+ [\u{10190}-\u{1019B}] |
+ \u{101A0} |
+ [\u{101D0}-\u{101FD}] |
+ [\u{10280}-\u{1029C}] |
+ [\u{102A0}-\u{102D0}] |
+ [\u{102E0}-\u{102FB}] |
+ [\u{10300}-\u{10323}] |
+ [\u{1032D}-\u{1034A}] |
+ [\u{10350}-\u{1037A}] |
+ [\u{10380}-\u{1039D}] |
+ [\u{1039F}-\u{103C3}] |
+ [\u{103C8}-\u{103D5}] |
+ [\u{10400}-\u{1049D}] |
+ [\u{104A0}-\u{104A9}] |
+ [\u{104B0}-\u{104D3}] |
+ [\u{104D8}-\u{104FB}] |
+ [\u{10500}-\u{10527}] |
+ [\u{10530}-\u{10563}] |
+ \u{1056F} |
+ [\u{10600}-\u{10736}] |
+ [\u{10740}-\u{10755}] |
+ [\u{10760}-\u{10767}] |
+ [\u{10800}-\u{10805}] |
+ \u{10808} |
+ [\u{1080A}-\u{10835}] |
+ [\u{10837}-\u{10838}] |
+ \u{1083C} |
+ [\u{1083F}-\u{10855}] |
+ [\u{10857}-\u{1089E}] |
+ [\u{108A7}-\u{108AF}] |
+ [\u{108E0}-\u{108F2}] |
+ [\u{108F4}-\u{108F5}] |
+ [\u{108FB}-\u{1091B}] |
+ [\u{1091F}-\u{10939}] |
+ \u{1093F} |
+ [\u{10980}-\u{109B7}] |
+ [\u{109BC}-\u{109CF}] |
+ [\u{109D2}-\u{10A03}] |
+ [\u{10A05}-\u{10A06}] |
+ [\u{10A0C}-\u{10A13}] |
+ [\u{10A15}-\u{10A17}] |
+ [\u{10A19}-\u{10A35}] |
+ [\u{10A38}-\u{10A3A}] |
+ [\u{10A3F}-\u{10A48}] |
+ [\u{10A50}-\u{10A58}] |
+ [\u{10A60}-\u{10A9F}] |
+ [\u{10AC0}-\u{10AE6}] |
+ [\u{10AEB}-\u{10AF6}] |
+ [\u{10B00}-\u{10B35}] |
+ [\u{10B39}-\u{10B55}] |
+ [\u{10B58}-\u{10B72}] |
+ [\u{10B78}-\u{10B91}] |
+ [\u{10B99}-\u{10B9C}] |
+ [\u{10BA9}-\u{10BAF}] |
+ [\u{10C00}-\u{10C48}] |
+ [\u{10C80}-\u{10CB2}] |
+ [\u{10CC0}-\u{10CF2}] |
+ [\u{10CFA}-\u{10D27}] |
+ [\u{10D30}-\u{10D39}] |
+ [\u{10E60}-\u{10E7E}] |
+ [\u{10F00}-\u{10F27}] |
+ [\u{10F30}-\u{10F59}] |
+ [\u{10FE0}-\u{10FF6}] |
+ [\u{11000}-\u{1104D}] |
+ [\u{11052}-\u{1106F}] |
+ [\u{1107F}-\u{110C1}] |
+ \u{110CD} |
+ [\u{110D0}-\u{110E8}] |
+ [\u{110F0}-\u{110F9}] |
+ [\u{11100}-\u{11134}] |
+ [\u{11136}-\u{11146}] |
+ [\u{11150}-\u{11176}] |
+ [\u{11180}-\u{111CD}] |
+ [\u{111D0}-\u{111DF}] |
+ [\u{111E1}-\u{111F4}] |
+ [\u{11200}-\u{11211}] |
+ [\u{11213}-\u{1123E}] |
+ [\u{11280}-\u{11286}] |
+ \u{11288} |
+ [\u{1128A}-\u{1128D}] |
+ [\u{1128F}-\u{1129D}] |
+ [\u{1129F}-\u{112A9}] |
+ [\u{112B0}-\u{112EA}] |
+ [\u{112F0}-\u{112F9}] |
+ [\u{11300}-\u{11303}] |
+ [\u{11305}-\u{1130C}] |
+ [\u{1130F}-\u{11310}] |
+ [\u{11313}-\u{11328}] |
+ [\u{1132A}-\u{11330}] |
+ [\u{11332}-\u{11333}] |
+ [\u{11335}-\u{11339}] |
+ [\u{1133B}-\u{11344}] |
+ [\u{11347}-\u{11348}] |
+ [\u{1134B}-\u{1134D}] |
+ \u{11350} |
+ \u{11357} |
+ [\u{1135D}-\u{11363}] |
+ [\u{11366}-\u{1136C}] |
+ [\u{11370}-\u{11374}] |
+ [\u{11400}-\u{11459}] |
+ \u{1145B} |
+ [\u{1145D}-\u{1145F}] |
+ [\u{11480}-\u{114C7}] |
+ [\u{114D0}-\u{114D9}] |
+ [\u{11580}-\u{115B5}] |
+ [\u{115B8}-\u{115DD}] |
+ [\u{11600}-\u{11644}] |
+ [\u{11650}-\u{11659}] |
+ [\u{11660}-\u{1166C}] |
+ [\u{11680}-\u{116B8}] |
+ [\u{116C0}-\u{116C9}] |
+ [\u{11700}-\u{1171A}] |
+ [\u{1171D}-\u{1172B}] |
+ [\u{11730}-\u{1173F}] |
+ [\u{11800}-\u{1183B}] |
+ [\u{118A0}-\u{118F2}] |
+ \u{118FF} |
+ [\u{119A0}-\u{119A7}] |
+ [\u{119AA}-\u{119D7}] |
+ [\u{119DA}-\u{119E4}] |
+ [\u{11A00}-\u{11A47}] |
+ [\u{11A50}-\u{11AA2}] |
+ [\u{11AC0}-\u{11AF8}] |
+ [\u{11C00}-\u{11C08}] |
+ [\u{11C0A}-\u{11C36}] |
+ [\u{11C38}-\u{11C45}] |
+ [\u{11C50}-\u{11C6C}] |
+ [\u{11C70}-\u{11C8F}] |
+ [\u{11C92}-\u{11CA7}] |
+ [\u{11CA9}-\u{11CB6}] |
+ [\u{11D00}-\u{11D06}] |
+ [\u{11D08}-\u{11D09}] |
+ [\u{11D0B}-\u{11D36}] |
+ \u{11D3A} |
+ [\u{11D3C}-\u{11D3D}] |
+ [\u{11D3F}-\u{11D47}] |
+ [\u{11D50}-\u{11D59}] |
+ [\u{11D60}-\u{11D65}] |
+ [\u{11D67}-\u{11D68}] |
+ [\u{11D6A}-\u{11D8E}] |
+ [\u{11D90}-\u{11D91}] |
+ [\u{11D93}-\u{11D98}] |
+ [\u{11DA0}-\u{11DA9}] |
+ [\u{11EE0}-\u{11EF8}] |
+ [\u{11FC0}-\u{11FF1}] |
+ [\u{11FFF}-\u{12399}] |
+ [\u{12400}-\u{1246E}] |
+ [\u{12470}-\u{12474}] |
+ [\u{12480}-\u{12543}] |
+ [\u{13000}-\u{1342E}] |
+ [\u{13430}-\u{13438}] |
+ [\u{14400}-\u{14646}] |
+ [\u{16800}-\u{16A38}] |
+ [\u{16A40}-\u{16A5E}] |
+ [\u{16A60}-\u{16A69}] |
+ [\u{16A6E}-\u{16A6F}] |
+ [\u{16AD0}-\u{16AED}] |
+ [\u{16AF0}-\u{16AF5}] |
+ [\u{16B00}-\u{16B45}] |
+ [\u{16B50}-\u{16B59}] |
+ [\u{16B5B}-\u{16B61}] |
+ [\u{16B63}-\u{16B77}] |
+ [\u{16B7D}-\u{16B8F}] |
+ [\u{16E40}-\u{16E9A}] |
+ [\u{16F00}-\u{16F4A}] |
+ [\u{16F4F}-\u{16F87}] |
+ [\u{16F8F}-\u{16F9F}] |
+ [\u{1BC00}-\u{1BC6A}] |
+ [\u{1BC70}-\u{1BC7C}] |
+ [\u{1BC80}-\u{1BC88}] |
+ [\u{1BC90}-\u{1BC99}] |
+ [\u{1BC9C}-\u{1BCA3}] |
+ [\u{1D000}-\u{1D0F5}] |
+ [\u{1D100}-\u{1D126}] |
+ [\u{1D129}-\u{1D1E8}] |
+ [\u{1D200}-\u{1D245}] |
+ [\u{1D2E0}-\u{1D2F3}] |
+ [\u{1D300}-\u{1D356}] |
+ [\u{1D360}-\u{1D378}] |
+ [\u{1D400}-\u{1D454}] |
+ [\u{1D456}-\u{1D49C}] |
+ [\u{1D49E}-\u{1D49F}] |
+ \u{1D4A2} |
+ [\u{1D4A5}-\u{1D4A6}] |
+ [\u{1D4A9}-\u{1D4AC}] |
+ [\u{1D4AE}-\u{1D4B9}] |
+ \u{1D4BB} |
+ [\u{1D4BD}-\u{1D4C3}] |
+ [\u{1D4C5}-\u{1D505}] |
+ [\u{1D507}-\u{1D50A}] |
+ [\u{1D50D}-\u{1D514}] |
+ [\u{1D516}-\u{1D51C}] |
+ [\u{1D51E}-\u{1D539}] |
+ [\u{1D53B}-\u{1D53E}] |
+ [\u{1D540}-\u{1D544}] |
+ \u{1D546} |
+ [\u{1D54A}-\u{1D550}] |
+ [\u{1D552}-\u{1D6A5}] |
+ [\u{1D6A8}-\u{1D7CB}] |
+ [\u{1D7CE}-\u{1DA8B}] |
+ [\u{1DA9B}-\u{1DA9F}] |
+ [\u{1DAA1}-\u{1DAAF}] |
+ [\u{1E000}-\u{1E006}] |
+ [\u{1E008}-\u{1E018}] |
+ [\u{1E01B}-\u{1E021}] |
+ [\u{1E023}-\u{1E024}] |
+ [\u{1E026}-\u{1E02A}] |
+ [\u{1E100}-\u{1E12C}] |
+ [\u{1E130}-\u{1E13D}] |
+ [\u{1E140}-\u{1E149}] |
+ [\u{1E14E}-\u{1E14F}] |
+ [\u{1E2C0}-\u{1E2F9}] |
+ \u{1E2FF} |
+ [\u{1E800}-\u{1E8C4}] |
+ [\u{1E8C7}-\u{1E8D6}] |
+ [\u{1E900}-\u{1E94B}] |
+ [\u{1E950}-\u{1E959}] |
+ [\u{1E95E}-\u{1E95F}] |
+ [\u{1EC71}-\u{1ECB4}] |
+ [\u{1ED01}-\u{1ED3D}] |
+ [\u{1EE00}-\u{1EE03}] |
+ [\u{1EE05}-\u{1EE1F}] |
+ [\u{1EE21}-\u{1EE22}] |
+ \u{1EE24} |
+ \u{1EE27} |
+ [\u{1EE29}-\u{1EE32}] |
+ [\u{1EE34}-\u{1EE37}] |
+ \u{1EE39} |
+ \u{1EE3B} |
+ \u{1EE42} |
+ \u{1EE47} |
+ \u{1EE49} |
+ \u{1EE4B} |
+ [\u{1EE4D}-\u{1EE4F}] |
+ [\u{1EE51}-\u{1EE52}] |
+ \u{1EE54} |
+ \u{1EE57} |
+ \u{1EE59} |
+ \u{1EE5B} |
+ \u{1EE5D} |
+ \u{1EE5F} |
+ [\u{1EE61}-\u{1EE62}] |
+ \u{1EE64} |
+ [\u{1EE67}-\u{1EE6A}] |
+ [\u{1EE6C}-\u{1EE72}] |
+ [\u{1EE74}-\u{1EE77}] |
+ [\u{1EE79}-\u{1EE7C}] |
+ \u{1EE7E} |
+ [\u{1EE80}-\u{1EE89}] |
+ [\u{1EE8B}-\u{1EE9B}] |
+ [\u{1EEA1}-\u{1EEA3}] |
+ [\u{1EEA5}-\u{1EEA9}] |
+ [\u{1EEAB}-\u{1EEBB}] |
+ [\u{1EEF0}-\u{1EEF1}] |
+ [\u{1F000}-\u{1F003}] |
+ [\u{1F005}-\u{1F02B}] |
+ [\u{1F030}-\u{1F093}] |
+ [\u{1F0A0}-\u{1F0AE}] |
+ [\u{1F0B1}-\u{1F0BF}] |
+ [\u{1F0C1}-\u{1F0CE}] |
+ [\u{1F0D1}-\u{1F0F5}] |
+ [\u{1F10B}-\u{1F10C}] |
+ [\u{1F12E}-\u{1F12F}] |
+ [\u{1F16A}-\u{1F16C}] |
+ [\u{1F1E6}-\u{1F1FF}] |
+ [\u{1F321}-\u{1F32C}] |
+ \u{1F336} |
+ \u{1F37D} |
+ [\u{1F394}-\u{1F39F}] |
+ [\u{1F3CB}-\u{1F3CE}] |
+ [\u{1F3D4}-\u{1F3DF}] |
+ [\u{1F3F1}-\u{1F3F3}] |
+ [\u{1F3F5}-\u{1F3F7}] |
+ \u{1F43F} |
+ \u{1F441} |
+ [\u{1F4FD}-\u{1F4FE}] |
+ [\u{1F53E}-\u{1F54A}] |
+ \u{1F54F} |
+ [\u{1F568}-\u{1F579}] |
+ [\u{1F57B}-\u{1F594}] |
+ [\u{1F597}-\u{1F5A3}] |
+ [\u{1F5A5}-\u{1F5FA}] |
+ [\u{1F650}-\u{1F67F}] |
+ [\u{1F6C6}-\u{1F6CB}] |
+ [\u{1F6CD}-\u{1F6CF}] |
+ [\u{1F6D3}-\u{1F6D4}] |
+ [\u{1F6E0}-\u{1F6EA}] |
+ [\u{1F6F0}-\u{1F6F3}] |
+ [\u{1F700}-\u{1F773}] |
+ [\u{1F780}-\u{1F7D8}] |
+ [\u{1F800}-\u{1F80B}] |
+ [\u{1F810}-\u{1F847}] |
+ [\u{1F850}-\u{1F859}] |
+ [\u{1F860}-\u{1F887}] |
+ [\u{1F890}-\u{1F8AD}] |
+ [\u{1F900}-\u{1F90B}] |
+ [\u{1FA00}-\u{1FA53}] |
+ [\u{1FA60}-\u{1FA6D}] |
+ \u{E0001} |
+ [\u{E0020}-\u{E007F}]
+ )/x
+end
diff --git a/lib/reline/version.rb b/lib/reline/version.rb
new file mode 100644
index 0000000000..58a69a09a0
--- /dev/null
+++ b/lib/reline/version.rb
@@ -0,0 +1,3 @@
+module Reline
+ VERSION = '0.0.0'
+end
diff --git a/lib/reline/windows.rb b/lib/reline/windows.rb
new file mode 100644
index 0000000000..f41ec17489
--- /dev/null
+++ b/lib/reline/windows.rb
@@ -0,0 +1,132 @@
+module Reline
+ VK_LMENU = 0xA4
+ STD_OUTPUT_HANDLE = -11
+ @@getwch = Win32API.new('msvcrt', '_getwch', [], 'I')
+ @@kbhit = Win32API.new('msvcrt', '_kbhit', [], 'I')
+ @@GetKeyState = Win32API.new('user32', 'GetKeyState', ['L'], 'L')
+ @@GetConsoleScreenBufferInfo = Win32API.new('kernel32', 'GetConsoleScreenBufferInfo', ['L', 'P'], 'L')
+ @@SetConsoleCursorPosition = Win32API.new('kernel32', 'SetConsoleCursorPosition', ['L', 'L'], 'L')
+ @@GetStdHandle = Win32API.new('kernel32', 'GetStdHandle', ['L'], 'L')
+ @@FillConsoleOutputCharacter = Win32API.new('kernel32', 'FillConsoleOutputCharacter', ['L', 'L', 'L', 'L', 'P'], 'L')
+ @@ScrollConsoleScreenBuffer = Win32API.new('kernel32', 'ScrollConsoleScreenBuffer', ['L', 'P', 'P', 'L', 'P'], 'L')
+ @@hConsoleHandle = @@GetStdHandle.call(STD_OUTPUT_HANDLE)
+ @@buf = []
+
+ def getwch
+ while @@kbhit.call == 0
+ sleep(0.001)
+ end
+ result = []
+ until @@kbhit.call == 0
+ ret = @@getwch.call
+ begin
+ result.concat(ret.chr(Encoding::UTF_8).encode(Encoding.default_external).bytes)
+ rescue Encoding::UndefinedConversionError
+ result << ret
+ result << @@getwch.call if ret == 224
+ end
+ end
+ result
+ end
+
+ def getc
+ unless @@buf.empty?
+ return @@buf.shift
+ end
+ input = getwch
+ alt = (@@GetKeyState.call(VK_LMENU) & 0x80) != 0
+ if input.size > 1
+ @@buf.concat(input)
+ else # single byte
+ case input[0]
+ when 0x00
+ getwch
+ alt = false
+ input = getwch
+ @@buf.concat(input)
+ when 0xE0
+ @@buf.concat(input)
+ input = getwch
+ @@buf.concat(input)
+ when 0x03
+ @@buf.concat(input)
+ else
+ @@buf.concat(input)
+ end
+ end
+ if alt
+ "\e".ord
+ else
+ @@buf.shift
+ end
+ end
+
+ def self.get_screen_size
+ csbi = 0.chr * 24
+ @@GetConsoleScreenBufferInfo.call(@@hConsoleHandle, csbi)
+ csbi[0, 4].unpack('SS')
+ end
+
+ def self.cursor_pos
+ csbi = 0.chr * 24
+ @@GetConsoleScreenBufferInfo.call(@@hConsoleHandle, csbi)
+ x = csbi[4, 2].unpack('s*').first
+ y = csbi[6, 4].unpack('s*').first
+ CursorPos.new(x, y)
+ end
+
+ def self.move_cursor_column(val)
+ @@SetConsoleCursorPosition.call(@@hConsoleHandle, cursor_pos.y * 65536 + val)
+ end
+
+ def self.move_cursor_up(val)
+ if val > 0
+ @@SetConsoleCursorPosition.call(@@hConsoleHandle, (cursor_pos.y - val) * 65536 + cursor_pos.x)
+ elsif val < 0
+ move_cursor_down(-val)
+ end
+ end
+
+ def self.move_cursor_down(val)
+ if val > 0
+ @@SetConsoleCursorPosition.call(@@hConsoleHandle, (cursor_pos.y + val) * 65536 + cursor_pos.x)
+ elsif val < 0
+ move_cursor_up(-val)
+ end
+ end
+
+ def self.erase_after_cursor
+ csbi = 0.chr * 24
+ @@GetConsoleScreenBufferInfo.call(@@hConsoleHandle, csbi)
+ cursor = csbi[4, 4].unpack('L').first
+ written = 0.chr * 4
+ @@FillConsoleOutputCharacter.call(@@hConsoleHandle, 0x20, get_screen_size.first - cursor_pos.x, cursor, written)
+ end
+
+ def self.scroll_down(val)
+ return if val.zero?
+ 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)
+ end
+
+ def self.clear_screen
+ # TODO: Use FillConsoleOutputCharacter and FillConsoleOutputAttribute
+ print "\e[2J"
+ print "\e[1;1H"
+ end
+
+ def self.set_screen_size(rows, columns)
+ raise NotImplementedError
+ end
+
+ def prep
+ # do nothing
+ nil
+ end
+
+ def deprep(otio)
+ # do nothing
+ end
+end
diff --git a/lib/rubygems.rb b/lib/rubygems.rb
index 54bd995b83..16cdb7cb04 100644
--- a/lib/rubygems.rb
+++ b/lib/rubygems.rb
@@ -18,6 +18,7 @@ require 'rubygems/compatibility'
require 'rubygems/defaults'
require 'rubygems/deprecate'
require 'rubygems/errors'
+require 'rubygems/path_support'
##
# RubyGems is the Ruby standard for publishing and managing third party