mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
Backport lib/reline, and lib/irb for 3.0.1 4th (#4349)
* [ruby/irb] Update help message for next context-mode of 4 While here, fixing tab/space issues in help message, and sync rdoc for IRB class to match the help message. https://github.com/ruby/irb/commit/ef8e3901cc * [ruby/irb] Do not continue line if last expression is an endless range Fixes [Bug #14824] https://github.com/ruby/irb/commit/63414f8465 * [ruby/irb] Add a test for not continuing when endless range at eol https://github.com/ruby/irb/commit/1020ac9c65 * [ruby/irb] Make save-history extension safe for concurrent use This makes the save-history extension check for modifications to the history file before saving it. If the history file was modified after the history was loaded and before it was saved, append only the new history lines to the history file. This can result in more lines in the history file than SAVE_HISTORY allows. However, that will be fixed the next time irb is run and the history is saved. Fixes [Bug #13654] https://github.com/ruby/irb/commit/041ef53845 * Fix errors when XDG_CONFIG_HOME points to non-writable directory `$HOME/.config` is not writable on CI because I think tests should not corrupt user's data. And GitHub Actions CI sets `XDG_CONFIG_HOME` since `Version: 20210309.1`. https://github.com/ruby/actions/runs/2130811016?check_suite_focus=true#step:16:301 ``` Errno::EACCES: Permission denied @ dir_s_mkdir - /home/runner/.config/irb ``` * Try to fix errors in TestIRB::TestHistory too https://github.com/ruby/actions/runs/2137935523?check_suite_focus=true#step:9:562 ``` 1) Error: TestIRB::TestHistory#test_history_concurrent_use: Errno::EACCES: Permission denied @ dir_s_mkdir - /home/runner/.config/irb /home/runner/work/actions/actions/ruby/lib/fileutils.rb:253:in `mkdir' /home/runner/work/actions/actions/ruby/lib/fileutils.rb:253:in `fu_mkdir' /home/runner/work/actions/actions/ruby/lib/fileutils.rb:231:in `block (2 levels) in mkdir_p' /home/runner/work/actions/actions/ruby/lib/fileutils.rb:229:in `reverse_each' /home/runner/work/actions/actions/ruby/lib/fileutils.rb:229:in `block in mkdir_p' /home/runner/work/actions/actions/ruby/lib/fileutils.rb:211:in `each' /home/runner/work/actions/actions/ruby/lib/fileutils.rb:211:in `mkdir_p' /home/runner/work/actions/actions/ruby/lib/irb/init.rb:355:in `rc_file_generators' /home/runner/work/actions/actions/ruby/lib/irb/init.rb:330:in `rc_file' /home/runner/work/actions/actions/ruby/test/irb/test_history.rb:170:in `block in assert_history' /home/runner/work/actions/actions/ruby/lib/tmpdir.rb:96:in `mktmpdir' /home/runner/work/actions/actions/ruby/test/irb/test_history.rb:168:in `assert_history' /home/runner/work/actions/actions/ruby/test/irb/test_history.rb:133:in `test_history_concurrent_use' ``` * [ruby/irb] Define "measure" command without forced override https://github.com/ruby/irb/commit/9587ba13b5 * [ruby/irb] Add all lib files automatically https://github.com/ruby/irb/commit/ecc82336b7 * [ruby/irb] Don't call Ruby 2.4+'s String#pretty_print https://github.com/ruby/irb/commit/89bcf107be * [ruby/irb] Implement ls command https://github.com/ruby/irb/commit/19b6c20604 * [ruby/irb] Add whereami command https://github.com/ruby/irb/commit/bc822e4aac * [ruby/irb] Fix column overflow on ls output https://github.com/ruby/irb/commit/6115754623 * [ruby/irb] Fix step's argument cols.size was calling Integer#size, which returns 8. Fixing a bug of https://github.com/ruby/irb/pull/209 https://github.com/ruby/irb/commit/c93ae4be71 * [ruby/irb] Deal with different screen sizes e.g. http://rubyci.s3.amazonaws.com/centos8/ruby-master/log/20210321T063003Z.fail.html.gz https://github.com/ruby/irb/commit/ddb3472ba2 * [ruby/irb] Have some right padding instead of filling out an entire line https://github.com/ruby/irb/commit/6ac8f45f5f * Suppress verbose messages Get rid of warnings in the parallel test. ``` unknown command: "Switch to inspect mode." ``` * [ruby/irb] Change ripper_lex_without_warning to a class method https://github.com/ruby/irb/commit/d9f8abc17e * [ruby/irb] Complete require and require_relative https://github.com/ruby/irb/commit/1c61178b4c * [ruby/reline] Add Reline.ungetc to control buffer https://github.com/ruby/reline/commit/43ac03c624 * [ruby/reline] Reline.delete_text removes the current line in multiline https://github.com/ruby/reline/commit/da90c094a1 * [ruby/reline] Support preposing and postposing for Reline.completion_proc https://github.com/ruby/reline/commit/1f469de90c * [ruby/reline] Suppress crashing when completer_{quote,word_break}_characters is empty https://github.com/ruby/reline/commit/c6f1164942 * [ruby/irb] fix completion test when out-of-place build * [ruby/irb] Cache completion files to require https://github.com/ruby/irb/commit/612ebcb311 * [ruby/irb] Always add input method when calling Irb.new in tests When passes input method as nil to Context.new through Irb.new, ReidlineInputMethod.new is executed and the global internal state of Reline is rewritten, therefore other tests are failed in the Ruby repository. This commit changes to use TestInputMethod. https://github.com/ruby/irb/commit/010dce9210 * [ruby/irb] Prevent the completion from crashing if rdoc is missing There are cases where ruby is installed without rdoc and e.g. lib/irb/cmd/help.rb also handles the LoadError Here is how to replicate the issue: ``` $ docker run -it alpine:3.13.3 sh / # apk add ruby ruby-irb ruby-io-console / # irb irb(main):001:0> Class[TAB][TAB] ``` And you end up with something like: ``` irb(main):001:0> ClassTraceback (most recent call last): 34: from /usr/bin/irb:23:in `<main>' 33: from /usr/bin/irb:23:in `load' 32: from /usr/lib/ruby/gems/2.7.0/gems/irb-1.2.6/exe/irb:11:in `<top (required)>' 31: from /usr/lib/ruby/2.7.0/irb.rb:400:in `start' 30: from /usr/lib/ruby/2.7.0/irb.rb:471:in `run' 29: from /usr/lib/ruby/2.7.0/irb.rb:471:in `catch' 28: from /usr/lib/ruby/2.7.0/irb.rb:472:in `block in run' 27: from /usr/lib/ruby/2.7.0/irb.rb:537:in `eval_input' 26: from /usr/lib/ruby/2.7.0/irb/ruby-lex.rb:150:in `each_top_level_statement' 25: from /usr/lib/ruby/2.7.0/irb/ruby-lex.rb:150:in `catch' 24: from /usr/lib/ruby/2.7.0/irb/ruby-lex.rb:151:in `block in each_top_level_statement' 23: from /usr/lib/ruby/2.7.0/irb/ruby-lex.rb:151:in `loop' 22: from /usr/lib/ruby/2.7.0/irb/ruby-lex.rb:154:in `block (2 levels) in each_top_level_statement' 21: from /usr/lib/ruby/2.7.0/irb/ruby-lex.rb:182:in `lex' 20: from /usr/lib/ruby/2.7.0/irb.rb:518:in `block in eval_input' 19: from /usr/lib/ruby/2.7.0/irb.rb:704:in `signal_status' 18: from /usr/lib/ruby/2.7.0/irb.rb:519:in `block (2 levels) in eval_input' 17: from /usr/lib/ruby/2.7.0/irb/input-method.rb:294:in `gets' 16: from /usr/lib/ruby/2.7.0/forwardable.rb:235:in `readmultiline' 15: from /usr/lib/ruby/2.7.0/forwardable.rb:235:in `readmultiline' 14: from /usr/lib/ruby/2.7.0/reline.rb:175:in `readmultiline' 13: from /usr/lib/ruby/2.7.0/reline.rb:238:in `inner_readline' 12: from /usr/lib/ruby/2.7.0/reline.rb:238:in `loop' 11: from /usr/lib/ruby/2.7.0/reline.rb:239:in `block in inner_readline' 10: from /usr/lib/ruby/2.7.0/reline.rb:270:in `read_io' 9: from /usr/lib/ruby/2.7.0/reline.rb:270:in `loop' 8: from /usr/lib/ruby/2.7.0/reline.rb:311:in `block in read_io' 7: from /usr/lib/ruby/2.7.0/reline.rb:240:in `block (2 levels) in inner_readline' 6: from /usr/lib/ruby/2.7.0/reline.rb:240:in `each' 5: from /usr/lib/ruby/2.7.0/reline.rb:241:in `block (3 levels) in inner_readline' 4: from /usr/lib/ruby/2.7.0/reline/line_editor.rb:820:in `input_key' 3: from /usr/lib/ruby/2.7.0/reline/line_editor.rb:608:in `complete' 2: from /usr/lib/ruby/2.7.0/irb/completion.rb:269:in `block in <module:InputCompletor>' 1: from /usr/lib/ruby/2.7.0/rubygems/core_ext/kernel_require.rb:92:in `require' /usr/lib/ruby/2.7.0/rubygems/core_ext/kernel_require.rb:92:in `require': cannot load such file -- rdoc (LoadError) ``` https://github.com/ruby/irb/commit/a2d299c2ac * [ruby/irb] Suppress verbose messages in the parallel test `:VERBOSE` flag needs to be set prior to `IRB::Irb.new`. https://github.com/ruby/irb/commit/78604682d9 * [ruby/irb] SIGINT should raise Interrupt after IRB session https://github.com/ruby/irb/commit/5832cfe75b * [ruby/irb] Colorize `__END__` as keyword https://github.com/ruby/irb/commit/9b84018311 * [ruby/irb] Add show_source command https://github.com/ruby/irb/commit/108cb04352 * [ruby/reline] Reset @rest_height when clear screen https://github.com/ruby/reline/commit/3a7019b0d5 * [ruby/irb] process multi-line pastes as a single entity this allows pasting leading-dot chained methods correctly: ```ruby class A def a; self; end def b; true; end end a = A.new a .a .b ``` will properly return `true` instead of erroring on the `.a` line: ``` irb(main):001:1* class A irb(main):002:1* def a; self; end irb(main):003:0> end irb(main):004:0* irb(main):005:0> a = A.new irb(main):006:0* irb(main):007:0> a irb(main):008:0> .a irb(main):009:0> .a => #<A:0x00007f984211fbe8> ``` https://github.com/ruby/irb/commit/45aeb52575 * [ruby/irb] Add yamatanooroti test example https://github.com/ruby/irb/commit/279155fcee * [ruby/irb] Add test for multiline paste https://github.com/ruby/irb/commit/e93c9cb54d * [ruby/irb] Evaluate each toplevel statement https://github.com/ruby/irb/commit/bc1b1d8bc3 * [ruby/irb] Version 1.3.5 https://github.com/ruby/irb/commit/22e2ddf715 * [ruby/reline] Version 0.2.5 https://github.com/ruby/reline/commit/22ce5651e5 Co-authored-by: Jeremy Evans <code@jeremyevans.net> Co-authored-by: Kazuhiro NISHIYAMA <zn@mbf.nifty.com> Co-authored-by: Takashi Kokubun <takashikkbn@gmail.com> Co-authored-by: Nobuyoshi Nakada <nobu@ruby-lang.org> Co-authored-by: Aleksandar Ivanov <aivanov92@gmail.com> Co-authored-by: Koichi Sasada <ko1@atdot.net> Co-authored-by: Cody Cutrer <cody@instructure.com>
This commit is contained in:
parent
6abb8ee711
commit
cafa7904e7
30 changed files with 1009 additions and 228 deletions
22
lib/irb.rb
22
lib/irb.rb
|
@ -60,7 +60,11 @@ require_relative "irb/easter-egg"
|
|||
# -E enc Same as `ruby -E`
|
||||
# -w Same as `ruby -w`
|
||||
# -W[level=2] Same as `ruby -W`
|
||||
# --inspect Use `inspect' for output (default except for bc mode)
|
||||
# --context-mode n Set n[0-4] to method to create Binding Object,
|
||||
# when new workspace was created
|
||||
# --echo Show result(default)
|
||||
# --noecho Don't show result
|
||||
# --inspect Use `inspect' for output
|
||||
# --noinspect Don't use inspect for output
|
||||
# --multiline Use multiline editor module
|
||||
# --nomultiline Don't use multiline editor module
|
||||
|
@ -68,19 +72,24 @@ require_relative "irb/easter-egg"
|
|||
# --nosingleline Don't use singleline editor module
|
||||
# --colorize Use colorization
|
||||
# --nocolorize Don't use colorization
|
||||
# --prompt prompt-mode
|
||||
# --prompt-mode prompt-mode
|
||||
# --prompt prompt-mode/--prompt-mode prompt-mode
|
||||
# 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 --multiline and --singleline.
|
||||
# --simple-prompt Simple prompt mode
|
||||
# --sample-book-mode/--simple-prompt
|
||||
# Simple prompt mode
|
||||
# --noprompt No prompt mode
|
||||
# --single-irb Share self with sub-irb.
|
||||
# --tracer Display trace for each execution of commands.
|
||||
# --back-trace-limit n
|
||||
# Display backtrace top n and tail n. The default
|
||||
# value is 16.
|
||||
# -v, --version Print the version of irb
|
||||
# --verbose Show details
|
||||
# --noverbose Don't show details
|
||||
# -v, --version Print the version of irb
|
||||
# -h, --help Print help
|
||||
# -- Separate options of irb from the list of command-line args
|
||||
#
|
||||
# == Configuration
|
||||
#
|
||||
|
@ -463,7 +472,7 @@ module IRB
|
|||
conf[:IRB_RC].call(context) if conf[:IRB_RC]
|
||||
conf[:MAIN_CONTEXT] = context
|
||||
|
||||
trap("SIGINT") do
|
||||
prev_trap = trap("SIGINT") do
|
||||
signal_handle
|
||||
end
|
||||
|
||||
|
@ -472,6 +481,7 @@ module IRB
|
|||
eval_input
|
||||
end
|
||||
ensure
|
||||
trap("SIGINT", prev_trap)
|
||||
conf[:AT_EXIT].each{|hook| hook.call}
|
||||
end
|
||||
end
|
||||
|
|
83
lib/irb/cmd/ls.rb
Normal file
83
lib/irb/cmd/ls.rb
Normal file
|
@ -0,0 +1,83 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require "reline"
|
||||
require_relative "nop"
|
||||
require_relative "../color"
|
||||
|
||||
# :stopdoc:
|
||||
module IRB
|
||||
module ExtendCommand
|
||||
class Ls < Nop
|
||||
def execute(*arg, grep: nil)
|
||||
o = Output.new(grep: grep)
|
||||
|
||||
obj = arg.empty? ? irb_context.workspace.main : arg.first
|
||||
locals = arg.empty? ? irb_context.workspace.binding.local_variables : []
|
||||
klass = (obj.class == Class || obj.class == Module ? obj : obj.class)
|
||||
|
||||
o.dump("constants", obj.constants) if obj.respond_to?(:constants)
|
||||
o.dump("#{klass}.methods", obj.singleton_methods(false))
|
||||
o.dump("#{klass}#methods", klass.public_instance_methods(false))
|
||||
o.dump("instance variables", obj.instance_variables)
|
||||
o.dump("class variables", klass.class_variables)
|
||||
o.dump("locals", locals)
|
||||
end
|
||||
|
||||
class Output
|
||||
MARGIN = " "
|
||||
|
||||
def initialize(grep: nil)
|
||||
@grep = grep
|
||||
@line_width = screen_width - MARGIN.length # right padding
|
||||
end
|
||||
|
||||
def dump(name, strs)
|
||||
strs = strs.grep(@grep) if @grep
|
||||
strs = strs.sort
|
||||
return if strs.empty?
|
||||
|
||||
# Attempt a single line
|
||||
print "#{Color.colorize(name, [:BOLD, :BLUE])}: "
|
||||
if fits_on_line?(strs, cols: strs.size, offset: "#{name}: ".length)
|
||||
puts strs.join(MARGIN)
|
||||
return
|
||||
end
|
||||
puts
|
||||
|
||||
# Dump with the largest # of columns that fits on a line
|
||||
cols = strs.size
|
||||
until fits_on_line?(strs, cols: cols, offset: MARGIN.length) || cols == 1
|
||||
cols -= 1
|
||||
end
|
||||
widths = col_widths(strs, cols: cols)
|
||||
strs.each_slice(cols) do |ss|
|
||||
puts ss.map.with_index { |s, i| "#{MARGIN}%-#{widths[i]}s" % s }.join
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def fits_on_line?(strs, cols:, offset: 0)
|
||||
width = col_widths(strs, cols: cols).sum + MARGIN.length * (cols - 1)
|
||||
width <= @line_width - offset
|
||||
end
|
||||
|
||||
def col_widths(strs, cols:)
|
||||
cols.times.map do |col|
|
||||
(col...strs.size).step(cols).map do |i|
|
||||
strs[i].length
|
||||
end.max
|
||||
end
|
||||
end
|
||||
|
||||
def screen_width
|
||||
Reline.get_screen_size.last
|
||||
rescue Errno::EINVAL # in `winsize': Invalid argument - <STDIN>
|
||||
80
|
||||
end
|
||||
end
|
||||
private_constant :Output
|
||||
end
|
||||
end
|
||||
end
|
||||
# :startdoc:
|
|
@ -14,10 +14,16 @@ module IRB
|
|||
module ExtendCommand
|
||||
class Nop
|
||||
|
||||
|
||||
def self.execute(conf, *opts, &block)
|
||||
command = new(conf)
|
||||
command.execute(*opts, &block)
|
||||
if RUBY_ENGINE == "ruby" && RUBY_VERSION >= "2.7.0"
|
||||
def self.execute(conf, *opts, **kwargs, &block)
|
||||
command = new(conf)
|
||||
command.execute(*opts, **kwargs, &block)
|
||||
end
|
||||
else
|
||||
def self.execute(conf, *opts, &block)
|
||||
command = new(conf)
|
||||
command.execute(*opts, &block)
|
||||
end
|
||||
end
|
||||
|
||||
def initialize(conf)
|
||||
|
|
86
lib/irb/cmd/show_source.rb
Normal file
86
lib/irb/cmd/show_source.rb
Normal file
|
@ -0,0 +1,86 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require_relative "nop"
|
||||
require_relative "../color"
|
||||
require_relative "../ruby-lex"
|
||||
|
||||
# :stopdoc:
|
||||
module IRB
|
||||
module ExtendCommand
|
||||
class ShowSource < Nop
|
||||
def execute(str = nil)
|
||||
unless str.is_a?(String)
|
||||
puts "Error: Expected a string but got #{str.inspect}"
|
||||
return
|
||||
end
|
||||
source = find_source(str)
|
||||
if source && File.exist?(source.file)
|
||||
show_source(source)
|
||||
else
|
||||
puts "Error: Couldn't locate a definition for #{str}"
|
||||
end
|
||||
nil
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# @param [IRB::ExtendCommand::ShowSource::Source] source
|
||||
def show_source(source)
|
||||
puts
|
||||
puts "#{bold("From")}: #{source.file}:#{source.first_line}"
|
||||
puts
|
||||
code = IRB::Color.colorize_code(File.read(source.file))
|
||||
puts code.lines[(source.first_line - 1)...source.last_line].join
|
||||
puts
|
||||
end
|
||||
|
||||
def find_source(str)
|
||||
case str
|
||||
when /\A[A-Z]\w*(::[A-Z]\w*)*\z/ # Const::Name
|
||||
eval(str, irb_context.workspace.binding) # trigger autoload
|
||||
base = irb_context.workspace.binding.receiver.yield_self { |r| r.is_a?(Module) ? r : Object }
|
||||
file, line = base.const_source_location(str) if base.respond_to?(:const_source_location) # Ruby 2.7+
|
||||
when /\A(?<owner>[A-Z]\w*(::[A-Z]\w*)*)#(?<method>[^ :.]+)\z/ # Class#method
|
||||
owner = eval(Regexp.last_match[:owner], irb_context.workspace.binding)
|
||||
method = Regexp.last_match[:method]
|
||||
if owner.respond_to?(:instance_method) && owner.instance_methods.include?(method.to_sym)
|
||||
file, line = owner.instance_method(method).source_location
|
||||
end
|
||||
when /\A((?<receiver>.+)(\.|::))?(?<method>[^ :.]+)\z/ # method, receiver.method, receiver::method
|
||||
receiver = eval(Regexp.last_match[:receiver] || 'self', irb_context.workspace.binding)
|
||||
method = Regexp.last_match[:method]
|
||||
file, line = receiver.method(method).source_location if receiver.respond_to?(method)
|
||||
end
|
||||
if file && line
|
||||
Source.new(file: file, first_line: line, last_line: find_end(file, line))
|
||||
end
|
||||
end
|
||||
|
||||
def find_end(file, first_line)
|
||||
return first_line unless File.exist?(file)
|
||||
lex = RubyLex.new
|
||||
code = +""
|
||||
File.read(file).lines[(first_line - 1)..-1].each_with_index do |line, i|
|
||||
_ltype, _indent, continue, code_block_open = lex.check_state(code << line)
|
||||
if !continue && !code_block_open
|
||||
return first_line + i
|
||||
end
|
||||
end
|
||||
first_line
|
||||
end
|
||||
|
||||
def bold(str)
|
||||
Color.colorize(str, [:BOLD])
|
||||
end
|
||||
|
||||
Source = Struct.new(
|
||||
:file, # @param [String] - file name
|
||||
:first_line, # @param [String] - first line
|
||||
:last_line, # @param [String] - last line
|
||||
keyword_init: true,
|
||||
)
|
||||
private_constant :Source
|
||||
end
|
||||
end
|
||||
end
|
||||
# :startdoc:
|
20
lib/irb/cmd/whereami.rb
Normal file
20
lib/irb/cmd/whereami.rb
Normal file
|
@ -0,0 +1,20 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require_relative "nop"
|
||||
|
||||
# :stopdoc:
|
||||
module IRB
|
||||
module ExtendCommand
|
||||
class Whereami < Nop
|
||||
def execute(*)
|
||||
code = irb_context.workspace.code_around_binding
|
||||
if code
|
||||
puts code
|
||||
else
|
||||
puts "The current context doesn't have code."
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
# :startdoc:
|
|
@ -64,6 +64,7 @@ module IRB # :nodoc:
|
|||
on_alias_error: [[RED, REVERSE], ALL],
|
||||
on_class_name_error:[[RED, REVERSE], ALL],
|
||||
on_param_error: [[RED, REVERSE], ALL],
|
||||
on___end__: [[GREEN], ALL],
|
||||
}
|
||||
rescue NameError
|
||||
# Give up highlighting Ripper-incompatible older Ruby
|
||||
|
@ -120,6 +121,7 @@ module IRB # :nodoc:
|
|||
symbol_state = SymbolState.new
|
||||
colored = +''
|
||||
length = 0
|
||||
end_seen = false
|
||||
|
||||
scan(code, allow_last_error: !complete) do |token, str, expr|
|
||||
# IRB::ColorPrinter skips colorizing fragments with any invalid token
|
||||
|
@ -138,10 +140,11 @@ module IRB # :nodoc:
|
|||
end
|
||||
end
|
||||
length += str.bytesize
|
||||
end_seen = true if token == :on___end__
|
||||
end
|
||||
|
||||
# give up colorizing incomplete Ripper tokens
|
||||
if length != code.bytesize
|
||||
unless end_seen or length == code.bytesize
|
||||
return Reline::Unicode.escape_for_print(code)
|
||||
end
|
||||
|
||||
|
|
|
@ -21,6 +21,15 @@ module IRB
|
|||
end
|
||||
end
|
||||
|
||||
def pp(obj)
|
||||
if obj.is_a?(String)
|
||||
# Avoid calling Ruby 2.4+ String#pretty_print that splits a string by "\n"
|
||||
text(obj.inspect)
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
def text(str, width = nil)
|
||||
unless str.is_a?(String)
|
||||
str = str.inspect
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
# From Original Idea of shugo@ruby-lang.org
|
||||
#
|
||||
|
||||
autoload :RDoc, "rdoc"
|
||||
require_relative 'ruby-lex'
|
||||
|
||||
module IRB
|
||||
module InputCompletor # :nodoc:
|
||||
|
@ -38,8 +38,69 @@ module IRB
|
|||
|
||||
BASIC_WORD_BREAK_CHARACTERS = " \t\n`><=;|&{("
|
||||
|
||||
CompletionProc = proc { |input|
|
||||
retrieve_completion_data(input).compact.map{ |i| i.encode(Encoding.default_external) }
|
||||
def self.retrieve_files_to_require_from_load_path
|
||||
@@files_from_load_path ||= $LOAD_PATH.flat_map { |path|
|
||||
begin
|
||||
Dir.glob("**/*.{rb,#{RbConfig::CONFIG['DLEXT']}}", base: path)
|
||||
rescue Errno::ENOENT
|
||||
[]
|
||||
end
|
||||
}.uniq.map { |path|
|
||||
path.sub(/\.(rb|#{RbConfig::CONFIG['DLEXT']})\z/, '')
|
||||
}
|
||||
end
|
||||
|
||||
def self.retrieve_files_to_require_relative_from_current_dir
|
||||
@@files_from_current_dir ||= Dir.glob("**/*.{rb,#{RbConfig::CONFIG['DLEXT']}}", base: '.').map { |path|
|
||||
path.sub(/\.(rb|#{RbConfig::CONFIG['DLEXT']})\z/, '')
|
||||
}
|
||||
end
|
||||
|
||||
CompletionRequireProc = lambda { |target, preposing = nil, postposing = nil|
|
||||
if target =~ /\A(['"])([^'"]+)\Z/
|
||||
quote = $1
|
||||
actual_target = $2
|
||||
else
|
||||
return nil # It's not String literal
|
||||
end
|
||||
tokens = RubyLex.ripper_lex_without_warning(preposing.gsub(/\s*\z/, ''))
|
||||
tok = nil
|
||||
tokens.reverse_each do |t|
|
||||
unless [:on_lparen, :on_sp, :on_ignored_sp, :on_nl, :on_ignored_nl, :on_comment].include?(t.event)
|
||||
tok = t
|
||||
break
|
||||
end
|
||||
end
|
||||
result = []
|
||||
if tok && tok.event == :on_ident && tok.state == Ripper::EXPR_CMDARG
|
||||
case tok.tok
|
||||
when 'require'
|
||||
result = retrieve_files_to_require_from_load_path.select { |path|
|
||||
path.start_with?(actual_target)
|
||||
}.map { |path|
|
||||
quote + path
|
||||
}
|
||||
when 'require_relative'
|
||||
result = retrieve_files_to_require_relative_from_current_dir.select { |path|
|
||||
path.start_with?(actual_target)
|
||||
}.map { |path|
|
||||
quote + path
|
||||
}
|
||||
end
|
||||
end
|
||||
result
|
||||
}
|
||||
|
||||
CompletionProc = lambda { |target, preposing = nil, postposing = nil|
|
||||
if preposing && postposing
|
||||
result = CompletionRequireProc.(target, preposing, postposing)
|
||||
unless result
|
||||
result = retrieve_completion_data(target).compact.map{ |i| i.encode(Encoding.default_external) }
|
||||
end
|
||||
result
|
||||
else
|
||||
retrieve_completion_data(target).compact.map{ |i| i.encode(Encoding.default_external) }
|
||||
end
|
||||
}
|
||||
|
||||
def self.retrieve_completion_data(input, bind: IRB.conf[:MAIN_CONTEXT].workspace.binding, doc_namespace: false)
|
||||
|
@ -266,13 +327,22 @@ module IRB
|
|||
end
|
||||
|
||||
PerfectMatchedProc = ->(matched, bind: IRB.conf[:MAIN_CONTEXT].workspace.binding) {
|
||||
begin
|
||||
require 'rdoc'
|
||||
rescue LoadError
|
||||
return
|
||||
end
|
||||
|
||||
RDocRIDriver ||= RDoc::RI::Driver.new
|
||||
|
||||
if matched =~ /\A(?:::)?RubyVM/ and not ENV['RUBY_YES_I_AM_NOT_A_NORMAL_USER']
|
||||
IRB.__send__(:easter_egg)
|
||||
return
|
||||
end
|
||||
|
||||
namespace = retrieve_completion_data(matched, bind: bind, doc_namespace: true)
|
||||
return unless namespace
|
||||
|
||||
if namespace.is_a?(Array)
|
||||
out = RDoc::Markup::Document.new
|
||||
namespace.each do |m|
|
||||
|
|
|
@ -81,6 +81,8 @@ module IRB
|
|||
end
|
||||
}
|
||||
end
|
||||
@loaded_history_lines = history.size
|
||||
@loaded_history_mtime = File.mtime(history_file)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -105,12 +107,20 @@ module IRB
|
|||
raise
|
||||
end
|
||||
|
||||
open(history_file, "w:#{IRB.conf[:LC_MESSAGES].encoding}", 0600) do |f|
|
||||
if File.exist?(history_file) && @loaded_history_mtime &&
|
||||
File.mtime(history_file) != @loaded_history_mtime
|
||||
history = history[@loaded_history_lines..-1]
|
||||
append_history = true
|
||||
end
|
||||
|
||||
open(history_file, "#{append_history ? 'a' : 'w'}:#{IRB.conf[:LC_MESSAGES].encoding}", 0600) do |f|
|
||||
hist = history.map{ |l| l.split("\n").join("\\\n") }
|
||||
begin
|
||||
hist = hist.last(num) if hist.size > num and num > 0
|
||||
rescue RangeError # bignum too big to convert into `long'
|
||||
# Do nothing because the bignum should be treated as inifinity
|
||||
unless append_history
|
||||
begin
|
||||
hist = hist.last(num) if hist.size > num and num > 0
|
||||
rescue RangeError # bignum too big to convert into `long'
|
||||
# Do nothing because the bignum should be treated as inifinity
|
||||
end
|
||||
end
|
||||
f.puts(hist)
|
||||
end
|
||||
|
|
|
@ -126,7 +126,23 @@ module IRB # :nodoc:
|
|||
],
|
||||
|
||||
[
|
||||
:measure, :Measure, "irb/cmd/measure"
|
||||
:irb_ls, :Ls, "irb/cmd/ls",
|
||||
[:ls, NO_OVERRIDE],
|
||||
],
|
||||
|
||||
[
|
||||
:irb_measure, :Measure, "irb/cmd/measure",
|
||||
[:measure, NO_OVERRIDE],
|
||||
],
|
||||
|
||||
[
|
||||
:irb_show_source, :ShowSource, "irb/cmd/show_source",
|
||||
[:show_source, NO_OVERRIDE],
|
||||
],
|
||||
|
||||
[
|
||||
:irb_whereami, :Whereami, "irb/cmd/whereami",
|
||||
[:whereami, NO_OVERRIDE],
|
||||
],
|
||||
|
||||
]
|
||||
|
@ -168,12 +184,13 @@ module IRB # :nodoc:
|
|||
end
|
||||
|
||||
if load_file
|
||||
kwargs = ", **kwargs" if RUBY_ENGINE == "ruby" && RUBY_VERSION >= "2.7.0"
|
||||
line = __LINE__; eval %[
|
||||
def #{cmd_name}(*opts, &b)
|
||||
def #{cmd_name}(*opts#{kwargs}, &b)
|
||||
require "#{load_file}"
|
||||
arity = ExtendCommand::#{cmd_class}.instance_method(:execute).arity
|
||||
args = (1..(arity < 0 ? ~arity : arity)).map {|i| "arg" + i.to_s }
|
||||
args << "*opts" if arity < 0
|
||||
args << "*opts#{kwargs}" if arity < 0
|
||||
args << "&block"
|
||||
args = args.join(", ")
|
||||
line = __LINE__; eval %[
|
||||
|
@ -184,7 +201,7 @@ module IRB # :nodoc:
|
|||
end
|
||||
end
|
||||
], nil, __FILE__, line
|
||||
__send__ :#{cmd_name}_, *opts, &b
|
||||
__send__ :#{cmd_name}_, *opts#{kwargs}, &b
|
||||
end
|
||||
], nil, __FILE__, line
|
||||
else
|
||||
|
|
|
@ -280,6 +280,7 @@ module IRB
|
|||
Reline.basic_word_break_characters = IRB::InputCompletor::BASIC_WORD_BREAK_CHARACTERS
|
||||
end
|
||||
Reline.completion_append_character = nil
|
||||
Reline.completer_quote_characters = ''
|
||||
Reline.completion_proc = IRB::InputCompletor::CompletionProc
|
||||
Reline.output_modifier_proc =
|
||||
if IRB.conf[:USE_COLORIZE]
|
||||
|
|
|
@ -28,53 +28,8 @@ Gem::Specification.new do |spec|
|
|||
"doc/irb/irb.rd.ja",
|
||||
"exe/irb",
|
||||
"irb.gemspec",
|
||||
"lib/irb.rb",
|
||||
"lib/irb/cmd/chws.rb",
|
||||
"lib/irb/cmd/fork.rb",
|
||||
"lib/irb/cmd/help.rb",
|
||||
"lib/irb/cmd/info.rb",
|
||||
"lib/irb/cmd/load.rb",
|
||||
"lib/irb/cmd/measure.rb",
|
||||
"lib/irb/cmd/nop.rb",
|
||||
"lib/irb/cmd/pushws.rb",
|
||||
"lib/irb/cmd/subirb.rb",
|
||||
"lib/irb/color.rb",
|
||||
"lib/irb/color_printer.rb",
|
||||
"lib/irb/completion.rb",
|
||||
"lib/irb/context.rb",
|
||||
"lib/irb/easter-egg.rb",
|
||||
"lib/irb/ext/change-ws.rb",
|
||||
"lib/irb/ext/history.rb",
|
||||
"lib/irb/ext/loader.rb",
|
||||
"lib/irb/ext/multi-irb.rb",
|
||||
"lib/irb/ext/save-history.rb",
|
||||
"lib/irb/ext/tracer.rb",
|
||||
"lib/irb/ext/use-loader.rb",
|
||||
"lib/irb/ext/workspaces.rb",
|
||||
"lib/irb/extend-command.rb",
|
||||
"lib/irb/frame.rb",
|
||||
"lib/irb/help.rb",
|
||||
"lib/irb/init.rb",
|
||||
"lib/irb/input-method.rb",
|
||||
"lib/irb/inspector.rb",
|
||||
"lib/irb/lc/error.rb",
|
||||
"lib/irb/lc/help-message",
|
||||
"lib/irb/lc/ja/encoding_aliases.rb",
|
||||
"lib/irb/lc/ja/error.rb",
|
||||
"lib/irb/lc/ja/help-message",
|
||||
"lib/irb/locale.rb",
|
||||
"lib/irb/magic-file.rb",
|
||||
"lib/irb/notifier.rb",
|
||||
"lib/irb/output-method.rb",
|
||||
"lib/irb/ruby-lex.rb",
|
||||
"lib/irb/ruby_logo.aa",
|
||||
"lib/irb/src_encoding.rb",
|
||||
"lib/irb/version.rb",
|
||||
"lib/irb/workspace.rb",
|
||||
"lib/irb/ws-for-case-2.rb",
|
||||
"lib/irb/xmp.rb",
|
||||
"man/irb.1",
|
||||
]
|
||||
] + Dir.glob("lib/**/*")
|
||||
spec.bindir = "exe"
|
||||
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
||||
spec.require_paths = ["lib"]
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
#
|
||||
#
|
||||
Usage: irb.rb [options] [programfile] [arguments]
|
||||
-f Suppress read of ~/.irbrc
|
||||
-f Suppress read of ~/.irbrc
|
||||
-d Set $DEBUG to true (same as `ruby -d')
|
||||
-r load-module Same as `ruby -r'
|
||||
-I path Specify $LOAD_PATH directory
|
||||
|
@ -18,7 +18,7 @@ Usage: irb.rb [options] [programfile] [arguments]
|
|||
-E enc Same as `ruby -E`
|
||||
-w Same as `ruby -w`
|
||||
-W[level=2] Same as `ruby -W`
|
||||
--context-mode n Set n[0-3] to method to create Binding Object,
|
||||
--context-mode n Set n[0-4] to method to create Binding Object,
|
||||
when new workspace was created
|
||||
--echo Show result(default)
|
||||
--noecho Don't show result
|
||||
|
@ -31,8 +31,8 @@ Usage: irb.rb [options] [programfile] [arguments]
|
|||
--colorize Use colorization
|
||||
--nocolorize Don't use colorization
|
||||
--prompt prompt-mode/--prompt-mode prompt-mode
|
||||
Switch prompt mode. Pre-defined prompt modes are
|
||||
`default', `simple', `xmp' and `inf-ruby'
|
||||
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 --multiline and --singleline.
|
||||
--sample-book-mode/--simple-prompt
|
||||
|
@ -41,8 +41,8 @@ Usage: irb.rb [options] [programfile] [arguments]
|
|||
--single-irb Share self with sub-irb.
|
||||
--tracer Display trace for each execution of commands.
|
||||
--back-trace-limit n
|
||||
Display backtrace top n and tail n. The default
|
||||
value is 16.
|
||||
Display backtrace top n and tail n. The default
|
||||
value is 16.
|
||||
--verbose Show details
|
||||
--noverbose Don't show details
|
||||
-v, --version Print the version of irb
|
||||
|
|
|
@ -47,12 +47,26 @@ class RubyLex
|
|||
@io = io
|
||||
if @io.respond_to?(:check_termination)
|
||||
@io.check_termination do |code|
|
||||
code.gsub!(/\s*\z/, '').concat("\n")
|
||||
ltype, indent, continue, code_block_open = check_state(code)
|
||||
if ltype or indent > 0 or continue or code_block_open
|
||||
false
|
||||
if Reline::IOGate.in_pasting?
|
||||
lex = RubyLex.new
|
||||
rest = lex.check_termination_in_prev_line(code)
|
||||
if rest
|
||||
Reline.delete_text
|
||||
rest.bytes.reverse_each do |c|
|
||||
Reline.ungetc(c)
|
||||
end
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
else
|
||||
true
|
||||
code.gsub!(/\s*\z/, '').concat("\n")
|
||||
ltype, indent, continue, code_block_open = check_state(code)
|
||||
if ltype or indent > 0 or continue or code_block_open
|
||||
false
|
||||
else
|
||||
true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -60,7 +74,7 @@ class RubyLex
|
|||
@io.dynamic_prompt do |lines|
|
||||
lines << '' if lines.empty?
|
||||
result = []
|
||||
tokens = ripper_lex_without_warning(lines.map{ |l| l + "\n" }.join)
|
||||
tokens = self.class.ripper_lex_without_warning(lines.map{ |l| l + "\n" }.join)
|
||||
code = String.new
|
||||
partial_tokens = []
|
||||
unprocessed_tokens = []
|
||||
|
@ -115,10 +129,10 @@ class RubyLex
|
|||
:on_param_error
|
||||
]
|
||||
|
||||
def ripper_lex_without_warning(code)
|
||||
def self.ripper_lex_without_warning(code)
|
||||
verbose, $VERBOSE = $VERBOSE, nil
|
||||
tokens = nil
|
||||
self.class.compile_with_errors_suppressed(code) do |inner_code, line_no|
|
||||
compile_with_errors_suppressed(code) do |inner_code, line_no|
|
||||
lexer = Ripper::Lexer.new(inner_code, '-', line_no)
|
||||
if lexer.respond_to?(:scan) # Ruby 2.7+
|
||||
tokens = []
|
||||
|
@ -168,7 +182,7 @@ class RubyLex
|
|||
if @io.respond_to?(:auto_indent) and context.auto_indent_mode
|
||||
@io.auto_indent do |lines, line_index, byte_pointer, is_newline|
|
||||
if is_newline
|
||||
@tokens = ripper_lex_without_warning(lines[0..line_index].join("\n"))
|
||||
@tokens = self.class.ripper_lex_without_warning(lines[0..line_index].join("\n"))
|
||||
prev_spaces = find_prev_spaces(line_index)
|
||||
depth_difference = check_newline_depth_difference
|
||||
depth_difference = 0 if depth_difference < 0
|
||||
|
@ -177,7 +191,7 @@ class RubyLex
|
|||
code = line_index.zero? ? '' : lines[0..(line_index - 1)].map{ |l| l + "\n" }.join
|
||||
last_line = lines[line_index]&.byteslice(0, byte_pointer)
|
||||
code += last_line if last_line
|
||||
@tokens = ripper_lex_without_warning(code)
|
||||
@tokens = self.class.ripper_lex_without_warning(code)
|
||||
corresponding_token_depth = check_corresponding_token_depth
|
||||
if corresponding_token_depth
|
||||
corresponding_token_depth
|
||||
|
@ -190,7 +204,7 @@ class RubyLex
|
|||
end
|
||||
|
||||
def check_state(code, tokens = nil)
|
||||
tokens = ripper_lex_without_warning(code) unless tokens
|
||||
tokens = self.class.ripper_lex_without_warning(code) unless tokens
|
||||
ltype = process_literal_type(tokens)
|
||||
indent = process_nesting_level(tokens)
|
||||
continue = process_continue(tokens)
|
||||
|
@ -256,7 +270,7 @@ class RubyLex
|
|||
end
|
||||
code = @line + (line.nil? ? '' : line)
|
||||
code.gsub!(/\s*\z/, '').concat("\n")
|
||||
@tokens = ripper_lex_without_warning(code)
|
||||
@tokens = self.class.ripper_lex_without_warning(code)
|
||||
@continue = process_continue
|
||||
@code_block_open = check_code_block(code)
|
||||
@indent = process_nesting_level
|
||||
|
@ -277,8 +291,9 @@ class RubyLex
|
|||
return true
|
||||
elsif tokens.size >= 1 and tokens[-1][1] == :on_heredoc_end # "EOH\n"
|
||||
return false
|
||||
elsif tokens.size >= 2 and defined?(Ripper::EXPR_BEG) and tokens[-2][3].anybits?(Ripper::EXPR_BEG | Ripper::EXPR_FNAME)
|
||||
elsif tokens.size >= 2 and defined?(Ripper::EXPR_BEG) and tokens[-2][3].anybits?(Ripper::EXPR_BEG | Ripper::EXPR_FNAME) and tokens[-2][2] !~ /\A\.\.\.?\z/
|
||||
# end of literal except for regexp
|
||||
# endless range at end of line is not a continue
|
||||
return true
|
||||
end
|
||||
false
|
||||
|
@ -738,5 +753,50 @@ class RubyLex
|
|||
nil
|
||||
end
|
||||
end
|
||||
|
||||
def check_termination_in_prev_line(code)
|
||||
tokens = self.class.ripper_lex_without_warning(code)
|
||||
past_first_newline = false
|
||||
index = tokens.rindex do |t|
|
||||
# traverse first token before last line
|
||||
if past_first_newline
|
||||
if t.tok.include?("\n")
|
||||
true
|
||||
end
|
||||
elsif t.tok.include?("\n")
|
||||
past_first_newline = true
|
||||
false
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
if index
|
||||
first_token = nil
|
||||
last_line_tokens = tokens[(index + 1)..(tokens.size - 1)]
|
||||
last_line_tokens.each do |t|
|
||||
unless [:on_sp, :on_ignored_sp, :on_comment].include?(t.event)
|
||||
first_token = t
|
||||
break
|
||||
end
|
||||
end
|
||||
if first_token.nil?
|
||||
return false
|
||||
elsif first_token && first_token.state == Ripper::EXPR_DOT
|
||||
return false
|
||||
else
|
||||
tokens_without_last_line = tokens[0..index]
|
||||
ltype = process_literal_type(tokens_without_last_line)
|
||||
indent = process_nesting_level(tokens_without_last_line)
|
||||
continue = process_continue(tokens_without_last_line)
|
||||
code_block_open = check_code_block(tokens_without_last_line.map(&:tok).join(''), tokens_without_last_line)
|
||||
if ltype or indent > 0 or continue or code_block_open
|
||||
return false
|
||||
else
|
||||
return last_line_tokens.map(&:tok).join('')
|
||||
end
|
||||
end
|
||||
end
|
||||
false
|
||||
end
|
||||
end
|
||||
# :startdoc:
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
#
|
||||
|
||||
module IRB # :nodoc:
|
||||
VERSION = "1.3.4"
|
||||
VERSION = "1.3.5"
|
||||
@RELEASE_VERSION = VERSION
|
||||
@LAST_UPDATE_DATE = "2021-02-25"
|
||||
@LAST_UPDATE_DATE = "2021-04-03"
|
||||
end
|
||||
|
|
|
@ -446,6 +446,10 @@ module Reline
|
|||
}
|
||||
end
|
||||
|
||||
def self.ungetc(c)
|
||||
Reline::IOGate.ungetc(c)
|
||||
end
|
||||
|
||||
def self.line_editor
|
||||
core.line_editor
|
||||
end
|
||||
|
|
|
@ -813,6 +813,7 @@ class Reline::LineEditor
|
|||
end
|
||||
move_cursor_up(back)
|
||||
move_cursor_down(@first_line_started_from + @started_from)
|
||||
@rest_height = (Reline::IOGate.get_screen_size.first - 1) - Reline::IOGate.cursor_pos.y
|
||||
Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
|
||||
end
|
||||
|
||||
|
@ -1158,8 +1159,25 @@ class Reline::LineEditor
|
|||
|
||||
def call_completion_proc
|
||||
result = retrieve_completion_block(true)
|
||||
slice = result[1]
|
||||
result = @completion_proc.(slice) if @completion_proc and slice
|
||||
preposing, target, postposing = result
|
||||
if @completion_proc and target
|
||||
argnum = @completion_proc.parameters.inject(0) { |result, item|
|
||||
case item.first
|
||||
when :req, :opt
|
||||
result + 1
|
||||
when :rest
|
||||
break 3
|
||||
end
|
||||
}
|
||||
case argnum
|
||||
when 1
|
||||
result = @completion_proc.(target)
|
||||
when 2
|
||||
result = @completion_proc.(target, preposing)
|
||||
when 3..Float::INFINITY
|
||||
result = @completion_proc.(target, preposing, postposing)
|
||||
end
|
||||
end
|
||||
Reline.core.instance_variable_set(:@completion_quote_character, nil)
|
||||
result
|
||||
end
|
||||
|
@ -1207,8 +1225,16 @@ class Reline::LineEditor
|
|||
end
|
||||
|
||||
def retrieve_completion_block(set_completion_quote_character = false)
|
||||
word_break_regexp = /\A[#{Regexp.escape(Reline.completer_word_break_characters)}]/
|
||||
quote_characters_regexp = /\A[#{Regexp.escape(Reline.completer_quote_characters)}]/
|
||||
if Reline.completer_word_break_characters.empty?
|
||||
word_break_regexp = nil
|
||||
else
|
||||
word_break_regexp = /\A[#{Regexp.escape(Reline.completer_word_break_characters)}]/
|
||||
end
|
||||
if Reline.completer_quote_characters.empty?
|
||||
quote_characters_regexp = nil
|
||||
else
|
||||
quote_characters_regexp = /\A[#{Regexp.escape(Reline.completer_quote_characters)}]/
|
||||
end
|
||||
before = @line.byteslice(0, @byte_pointer)
|
||||
rest = nil
|
||||
break_pointer = nil
|
||||
|
@ -1229,14 +1255,14 @@ class Reline::LineEditor
|
|||
elsif quote and slice.start_with?(escaped_quote)
|
||||
# skip
|
||||
i += 2
|
||||
elsif slice =~ quote_characters_regexp # find new "
|
||||
elsif quote_characters_regexp and slice =~ quote_characters_regexp # find new "
|
||||
rest = $'
|
||||
quote = $&
|
||||
closing_quote = /(?!\\)#{Regexp.escape(quote)}/
|
||||
escaped_quote = /\\#{Regexp.escape(quote)}/
|
||||
i += 1
|
||||
break_pointer = i - 1
|
||||
elsif not quote and slice =~ word_break_regexp
|
||||
elsif word_break_regexp and not quote and slice =~ word_break_regexp
|
||||
rest = $'
|
||||
i += 1
|
||||
before = @line.byteslice(i, @byte_pointer - i)
|
||||
|
@ -1264,6 +1290,19 @@ class Reline::LineEditor
|
|||
end
|
||||
target = before
|
||||
end
|
||||
if @is_multiline
|
||||
if @previous_line_index
|
||||
lines = whole_lines(index: @previous_line_index, line: @line)
|
||||
else
|
||||
lines = whole_lines
|
||||
end
|
||||
if @line_index > 0
|
||||
preposing = lines[0..(@line_index - 1)].join("\n") + "\n" + preposing
|
||||
end
|
||||
if (lines.size - 1) > @line_index
|
||||
postposing = postposing + "\n" + lines[(@line_index + 1)..-1].join("\n")
|
||||
end
|
||||
end
|
||||
[preposing.encode(@encoding), target.encode(@encoding), postposing.encode(@encoding)]
|
||||
end
|
||||
|
||||
|
@ -1291,10 +1330,32 @@ class Reline::LineEditor
|
|||
|
||||
def delete_text(start = nil, length = nil)
|
||||
if start.nil? and length.nil?
|
||||
@line&.clear
|
||||
@byte_pointer = 0
|
||||
@cursor = 0
|
||||
@cursor_max = 0
|
||||
if @is_multiline
|
||||
if @buffer_of_lines.size == 1
|
||||
@line&.clear
|
||||
@byte_pointer = 0
|
||||
@cursor = 0
|
||||
@cursor_max = 0
|
||||
elsif @line_index == (@buffer_of_lines.size - 1) and @line_index > 0
|
||||
@buffer_of_lines.pop
|
||||
@line_index -= 1
|
||||
@line = @buffer_of_lines[@line_index]
|
||||
@byte_pointer = 0
|
||||
@cursor = 0
|
||||
@cursor_max = calculate_width(@line)
|
||||
elsif @line_index < (@buffer_of_lines.size - 1)
|
||||
@buffer_of_lines.delete_at(@line_index)
|
||||
@line = @buffer_of_lines[@line_index]
|
||||
@byte_pointer = 0
|
||||
@cursor = 0
|
||||
@cursor_max = calculate_width(@line)
|
||||
end
|
||||
else
|
||||
@line&.clear
|
||||
@byte_pointer = 0
|
||||
@cursor = 0
|
||||
@cursor_max = 0
|
||||
end
|
||||
elsif not start.nil? and not length.nil?
|
||||
if @line
|
||||
before = @line.byteslice(0, start)
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
module Reline
|
||||
VERSION = '0.2.4'
|
||||
VERSION = '0.2.5'
|
||||
end
|
||||
|
|
|
@ -5,129 +5,6 @@ require "irb/extend-command"
|
|||
|
||||
module TestIRB
|
||||
class ExtendCommand < Test::Unit::TestCase
|
||||
def setup
|
||||
@pwd = Dir.pwd
|
||||
@tmpdir = File.join(Dir.tmpdir, "test_reline_config_#{$$}")
|
||||
begin
|
||||
Dir.mkdir(@tmpdir)
|
||||
rescue Errno::EEXIST
|
||||
FileUtils.rm_rf(@tmpdir)
|
||||
Dir.mkdir(@tmpdir)
|
||||
end
|
||||
Dir.chdir(@tmpdir)
|
||||
@home_backup = ENV["HOME"]
|
||||
ENV["HOME"] = @tmpdir
|
||||
@default_encoding = [Encoding.default_external, Encoding.default_internal]
|
||||
@stdio_encodings = [STDIN, STDOUT, STDERR].map {|io| [io.external_encoding, io.internal_encoding] }
|
||||
IRB.instance_variable_get(:@CONF).clear
|
||||
end
|
||||
|
||||
def teardown
|
||||
ENV["HOME"] = @home_backup
|
||||
Dir.chdir(@pwd)
|
||||
FileUtils.rm_rf(@tmpdir)
|
||||
EnvUtil.suppress_warning {
|
||||
Encoding.default_external, Encoding.default_internal = *@default_encoding
|
||||
[STDIN, STDOUT, STDERR].zip(@stdio_encodings) do |io, encs|
|
||||
io.set_encoding(*encs)
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
def test_irb_info_multiline
|
||||
FileUtils.touch("#{@tmpdir}/.inputrc")
|
||||
FileUtils.touch("#{@tmpdir}/.irbrc")
|
||||
IRB.setup(__FILE__, argv: [])
|
||||
IRB.conf[:USE_MULTILINE] = true
|
||||
IRB.conf[:USE_SINGLELINE] = false
|
||||
IRB.conf[:VERBOSE] = false
|
||||
workspace = IRB::WorkSpace.new(self)
|
||||
irb = IRB::Irb.new(workspace)
|
||||
IRB.conf[:MAIN_CONTEXT] = irb.context
|
||||
expected = %r{
|
||||
Ruby\sversion: .+\n
|
||||
IRB\sversion:\sirb .+\n
|
||||
InputMethod:\sReidlineInputMethod\swith\sReline .+ and .+\n
|
||||
\.irbrc\spath: .+\n
|
||||
RUBY_PLATFORM: .+
|
||||
}x
|
||||
assert_match expected, irb.context.main.irb_info.to_s
|
||||
end
|
||||
|
||||
def test_irb_info_singleline
|
||||
FileUtils.touch("#{@tmpdir}/.inputrc")
|
||||
FileUtils.touch("#{@tmpdir}/.irbrc")
|
||||
IRB.setup(__FILE__, argv: [])
|
||||
IRB.conf[:USE_MULTILINE] = false
|
||||
IRB.conf[:USE_SINGLELINE] = true
|
||||
IRB.conf[:VERBOSE] = false
|
||||
workspace = IRB::WorkSpace.new(self)
|
||||
irb = IRB::Irb.new(workspace)
|
||||
IRB.conf[:MAIN_CONTEXT] = irb.context
|
||||
expected = %r{
|
||||
Ruby\sversion: .+\n
|
||||
IRB\sversion:\sirb .+\n
|
||||
InputMethod:\sReadlineInputMethod\swith .+ and .+\n
|
||||
\.irbrc\spath: .+\n
|
||||
RUBY_PLATFORM: .+
|
||||
}x
|
||||
assert_match expected, irb.context.main.irb_info.to_s
|
||||
end
|
||||
|
||||
def test_irb_info_multiline_without_rc_files
|
||||
inputrc_backup = ENV["INPUTRC"]
|
||||
ENV["INPUTRC"] = "unknown_inpurc"
|
||||
ext_backup = IRB::IRBRC_EXT
|
||||
IRB.__send__(:remove_const, :IRBRC_EXT)
|
||||
IRB.const_set(:IRBRC_EXT, "unknown_ext")
|
||||
IRB.setup(__FILE__, argv: [])
|
||||
IRB.conf[:USE_MULTILINE] = true
|
||||
IRB.conf[:USE_SINGLELINE] = false
|
||||
IRB.conf[:VERBOSE] = false
|
||||
workspace = IRB::WorkSpace.new(self)
|
||||
irb = IRB::Irb.new(workspace)
|
||||
IRB.conf[:MAIN_CONTEXT] = irb.context
|
||||
expected = %r{
|
||||
Ruby\sversion: .+\n
|
||||
IRB\sversion:\sirb .+\n
|
||||
InputMethod:\sReidlineInputMethod\swith\sReline\s[^ ]+(?!\sand\s.+)\n
|
||||
RUBY_PLATFORM: .+\n
|
||||
\z
|
||||
}x
|
||||
assert_match expected, irb.context.main.irb_info.to_s
|
||||
ensure
|
||||
ENV["INPUTRC"] = inputrc_backup
|
||||
IRB.__send__(:remove_const, :IRBRC_EXT)
|
||||
IRB.const_set(:IRBRC_EXT, ext_backup)
|
||||
end
|
||||
|
||||
def test_irb_info_singleline_without_rc_files
|
||||
inputrc_backup = ENV["INPUTRC"]
|
||||
ENV["INPUTRC"] = "unknown_inpurc"
|
||||
ext_backup = IRB::IRBRC_EXT
|
||||
IRB.__send__(:remove_const, :IRBRC_EXT)
|
||||
IRB.const_set(:IRBRC_EXT, "unknown_ext")
|
||||
IRB.setup(__FILE__, argv: [])
|
||||
IRB.conf[:USE_MULTILINE] = false
|
||||
IRB.conf[:USE_SINGLELINE] = true
|
||||
IRB.conf[:VERBOSE] = false
|
||||
workspace = IRB::WorkSpace.new(self)
|
||||
irb = IRB::Irb.new(workspace)
|
||||
IRB.conf[:MAIN_CONTEXT] = irb.context
|
||||
expected = %r{
|
||||
Ruby\sversion: .+\n
|
||||
IRB\sversion:\sirb .+\n
|
||||
InputMethod:\sReadlineInputMethod\swith\s(?~.*\sand\s.+)\n
|
||||
RUBY_PLATFORM: .+\n
|
||||
\z
|
||||
}x
|
||||
assert_match expected, irb.context.main.irb_info.to_s
|
||||
ensure
|
||||
ENV["INPUTRC"] = inputrc_backup
|
||||
IRB.__send__(:remove_const, :IRBRC_EXT)
|
||||
IRB.const_set(:IRBRC_EXT, ext_backup)
|
||||
end
|
||||
|
||||
class TestInputMethod < ::IRB::InputMethod
|
||||
attr_reader :list, :line_no
|
||||
|
||||
|
@ -154,6 +31,131 @@ module TestIRB
|
|||
end
|
||||
end
|
||||
|
||||
def setup
|
||||
@pwd = Dir.pwd
|
||||
@tmpdir = File.join(Dir.tmpdir, "test_reline_config_#{$$}")
|
||||
begin
|
||||
Dir.mkdir(@tmpdir)
|
||||
rescue Errno::EEXIST
|
||||
FileUtils.rm_rf(@tmpdir)
|
||||
Dir.mkdir(@tmpdir)
|
||||
end
|
||||
Dir.chdir(@tmpdir)
|
||||
@home_backup = ENV["HOME"]
|
||||
ENV["HOME"] = @tmpdir
|
||||
@xdg_config_home_backup = ENV.delete("XDG_CONFIG_HOME")
|
||||
@default_encoding = [Encoding.default_external, Encoding.default_internal]
|
||||
@stdio_encodings = [STDIN, STDOUT, STDERR].map {|io| [io.external_encoding, io.internal_encoding] }
|
||||
IRB.instance_variable_get(:@CONF).clear
|
||||
end
|
||||
|
||||
def teardown
|
||||
ENV["XDG_CONFIG_HOME"] = @xdg_config_home_backup
|
||||
ENV["HOME"] = @home_backup
|
||||
Dir.chdir(@pwd)
|
||||
FileUtils.rm_rf(@tmpdir)
|
||||
EnvUtil.suppress_warning {
|
||||
Encoding.default_external, Encoding.default_internal = *@default_encoding
|
||||
[STDIN, STDOUT, STDERR].zip(@stdio_encodings) do |io, encs|
|
||||
io.set_encoding(*encs)
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
def test_irb_info_multiline
|
||||
FileUtils.touch("#{@tmpdir}/.inputrc")
|
||||
FileUtils.touch("#{@tmpdir}/.irbrc")
|
||||
IRB.setup(__FILE__, argv: [])
|
||||
IRB.conf[:USE_MULTILINE] = true
|
||||
IRB.conf[:USE_SINGLELINE] = false
|
||||
IRB.conf[:VERBOSE] = false
|
||||
workspace = IRB::WorkSpace.new(self)
|
||||
irb = IRB::Irb.new(workspace, TestInputMethod.new([]))
|
||||
IRB.conf[:MAIN_CONTEXT] = irb.context
|
||||
expected = %r{
|
||||
Ruby\sversion: .+\n
|
||||
IRB\sversion:\sirb .+\n
|
||||
InputMethod:\sAbstract\sInputMethod\n
|
||||
\.irbrc\spath: .+\n
|
||||
RUBY_PLATFORM: .+
|
||||
}x
|
||||
assert_match expected, irb.context.main.irb_info.to_s
|
||||
end
|
||||
|
||||
def test_irb_info_singleline
|
||||
FileUtils.touch("#{@tmpdir}/.inputrc")
|
||||
FileUtils.touch("#{@tmpdir}/.irbrc")
|
||||
IRB.setup(__FILE__, argv: [])
|
||||
IRB.conf[:USE_MULTILINE] = false
|
||||
IRB.conf[:USE_SINGLELINE] = true
|
||||
IRB.conf[:VERBOSE] = false
|
||||
workspace = IRB::WorkSpace.new(self)
|
||||
irb = IRB::Irb.new(workspace, TestInputMethod.new([]))
|
||||
IRB.conf[:MAIN_CONTEXT] = irb.context
|
||||
expected = %r{
|
||||
Ruby\sversion: .+\n
|
||||
IRB\sversion:\sirb .+\n
|
||||
InputMethod:\sAbstract\sInputMethod\n
|
||||
\.irbrc\spath: .+\n
|
||||
RUBY_PLATFORM: .+
|
||||
}x
|
||||
assert_match expected, irb.context.main.irb_info.to_s
|
||||
end
|
||||
|
||||
def test_irb_info_multiline_without_rc_files
|
||||
inputrc_backup = ENV["INPUTRC"]
|
||||
ENV["INPUTRC"] = "unknown_inpurc"
|
||||
ext_backup = IRB::IRBRC_EXT
|
||||
IRB.__send__(:remove_const, :IRBRC_EXT)
|
||||
IRB.const_set(:IRBRC_EXT, "unknown_ext")
|
||||
IRB.setup(__FILE__, argv: [])
|
||||
IRB.conf[:USE_MULTILINE] = true
|
||||
IRB.conf[:USE_SINGLELINE] = false
|
||||
IRB.conf[:VERBOSE] = false
|
||||
workspace = IRB::WorkSpace.new(self)
|
||||
irb = IRB::Irb.new(workspace, TestInputMethod.new([]))
|
||||
IRB.conf[:MAIN_CONTEXT] = irb.context
|
||||
expected = %r{
|
||||
Ruby\sversion: .+\n
|
||||
IRB\sversion:\sirb .+\n
|
||||
InputMethod:\sAbstract\sInputMethod\n
|
||||
RUBY_PLATFORM: .+\n
|
||||
\z
|
||||
}x
|
||||
assert_match expected, irb.context.main.irb_info.to_s
|
||||
ensure
|
||||
ENV["INPUTRC"] = inputrc_backup
|
||||
IRB.__send__(:remove_const, :IRBRC_EXT)
|
||||
IRB.const_set(:IRBRC_EXT, ext_backup)
|
||||
end
|
||||
|
||||
def test_irb_info_singleline_without_rc_files
|
||||
inputrc_backup = ENV["INPUTRC"]
|
||||
ENV["INPUTRC"] = "unknown_inpurc"
|
||||
ext_backup = IRB::IRBRC_EXT
|
||||
IRB.__send__(:remove_const, :IRBRC_EXT)
|
||||
IRB.const_set(:IRBRC_EXT, "unknown_ext")
|
||||
IRB.setup(__FILE__, argv: [])
|
||||
IRB.conf[:USE_MULTILINE] = false
|
||||
IRB.conf[:USE_SINGLELINE] = true
|
||||
IRB.conf[:VERBOSE] = false
|
||||
workspace = IRB::WorkSpace.new(self)
|
||||
irb = IRB::Irb.new(workspace, TestInputMethod.new([]))
|
||||
IRB.conf[:MAIN_CONTEXT] = irb.context
|
||||
expected = %r{
|
||||
Ruby\sversion: .+\n
|
||||
IRB\sversion:\sirb .+\n
|
||||
InputMethod:\sAbstract\sInputMethod\n
|
||||
RUBY_PLATFORM: .+\n
|
||||
\z
|
||||
}x
|
||||
assert_match expected, irb.context.main.irb_info.to_s
|
||||
ensure
|
||||
ENV["INPUTRC"] = inputrc_backup
|
||||
IRB.__send__(:remove_const, :IRBRC_EXT)
|
||||
IRB.const_set(:IRBRC_EXT, ext_backup)
|
||||
end
|
||||
|
||||
def test_measure
|
||||
IRB.init_config(nil)
|
||||
IRB.conf[:PROMPT] = {
|
||||
|
@ -372,5 +374,56 @@ module TestIRB
|
|||
/=> "bug17564"\n/,
|
||||
], out)
|
||||
end
|
||||
|
||||
def test_ls
|
||||
input = TestInputMethod.new([
|
||||
"ls Object.new.tap { |o| o.instance_variable_set(:@a, 1) }\n",
|
||||
])
|
||||
IRB.init_config(nil)
|
||||
workspace = IRB::WorkSpace.new(self)
|
||||
IRB.conf[:VERBOSE] = false
|
||||
irb = IRB::Irb.new(workspace, input)
|
||||
IRB.conf[:MAIN_CONTEXT] = irb.context
|
||||
irb.context.return_format = "=> %s\n"
|
||||
out, err = capture_output do
|
||||
irb.eval_input
|
||||
end
|
||||
assert_empty err
|
||||
assert_match(/^instance variables:\s+@a\n/m, out)
|
||||
end
|
||||
|
||||
def test_show_source
|
||||
input = TestInputMethod.new([
|
||||
"show_source 'IRB.conf'\n",
|
||||
])
|
||||
IRB.init_config(nil)
|
||||
workspace = IRB::WorkSpace.new(self)
|
||||
irb = IRB::Irb.new(workspace, input)
|
||||
IRB.conf[:VERBOSE] = false
|
||||
IRB.conf[:MAIN_CONTEXT] = irb.context
|
||||
irb.context.return_format = "=> %s\n"
|
||||
out, err = capture_output do
|
||||
irb.eval_input
|
||||
end
|
||||
assert_empty err
|
||||
assert_match(%r[/irb\.rb], out)
|
||||
end
|
||||
|
||||
def test_whereami
|
||||
input = TestInputMethod.new([
|
||||
"whereami\n",
|
||||
])
|
||||
IRB.init_config(nil)
|
||||
workspace = IRB::WorkSpace.new(self)
|
||||
IRB.conf[:VERBOSE] = false
|
||||
irb = IRB::Irb.new(workspace, input)
|
||||
IRB.conf[:MAIN_CONTEXT] = irb.context
|
||||
irb.context.return_format = "=> %s\n"
|
||||
out, err = capture_output do
|
||||
irb.eval_input
|
||||
end
|
||||
assert_empty err
|
||||
assert_match(/^From: .+ @ line \d+ :\n/, out)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -66,6 +66,7 @@ module TestIRB
|
|||
"\t" => "\t", # not ^I
|
||||
"foo(*%W(bar))" => "foo(*#{RED}#{BOLD}%W(#{CLEAR}#{RED}bar#{CLEAR}#{RED}#{BOLD})#{CLEAR})",
|
||||
"$stdout" => "#{GREEN}#{BOLD}$stdout#{CLEAR}",
|
||||
"__END__" => "#{GREEN}__END__#{CLEAR}",
|
||||
}
|
||||
|
||||
# specific to Ruby 2.7+
|
||||
|
|
|
@ -34,6 +34,7 @@ module TestIRB
|
|||
end
|
||||
{
|
||||
1 => "#{BLUE}#{BOLD}1#{CLEAR}\n",
|
||||
"a\nb" => %[#{RED}#{BOLD}"#{CLEAR}#{RED}a\\nb#{CLEAR}#{RED}#{BOLD}"#{CLEAR}\n],
|
||||
IRBTestColorPrinter.new('test') => "#{GREEN}#<struct TestIRB::TestColorPrinter::IRBTestColorPrinter#{CLEAR} a#{GREEN}=#{CLEAR}#{RED}#{BOLD}\"#{CLEAR}#{RED}test#{CLEAR}#{RED}#{BOLD}\"#{CLEAR}#{GREEN}>#{CLEAR}\n",
|
||||
Ripper::Lexer.new('1').scan => "[#{GREEN}#<Ripper::Lexer::Elem:#{CLEAR} on_int@1:0 END token: #{RED}#{BOLD}\"#{CLEAR}#{RED}1#{CLEAR}#{RED}#{BOLD}\"#{CLEAR}#{GREEN}>#{CLEAR}]\n",
|
||||
Class.new{define_method(:pretty_print){|q| q.text("[__FILE__, __LINE__, __ENCODING__]")}}.new => "[#{CYAN}#{BOLD}__FILE__#{CLEAR}, #{CYAN}#{BOLD}__LINE__#{CLEAR}, #{CYAN}#{BOLD}__ENCODING__#{CLEAR}]\n",
|
||||
|
|
|
@ -55,5 +55,33 @@ module TestIRB
|
|||
namespace = IRB::InputCompletor.retrieve_completion_data("1.positive?", bind: binding, doc_namespace: true)
|
||||
assert_equal "Integer.positive?", namespace
|
||||
end
|
||||
|
||||
def test_complete_require
|
||||
candidates = IRB::InputCompletor::CompletionProc.("'irb", "require ", "")
|
||||
%w['irb/init 'irb/ruby-lex].each do |word|
|
||||
assert_include candidates, word
|
||||
end
|
||||
# Test cache
|
||||
candidates = IRB::InputCompletor::CompletionProc.("'irb", "require ", "")
|
||||
%w['irb/init 'irb/ruby-lex].each do |word|
|
||||
assert_include candidates, word
|
||||
end
|
||||
end
|
||||
|
||||
def test_complete_require_relative
|
||||
candidates = Dir.chdir(__dir__ + "/../..") do
|
||||
IRB::InputCompletor::CompletionProc.("'lib/irb", "require_relative ", "")
|
||||
end
|
||||
%w['lib/irb/init 'lib/irb/ruby-lex].each do |word|
|
||||
assert_include candidates, word
|
||||
end
|
||||
# Test cache
|
||||
candidates = Dir.chdir(__dir__ + "/../..") do
|
||||
IRB::InputCompletor::CompletionProc.("'lib/irb", "require_relative ", "")
|
||||
end
|
||||
%w['lib/irb/init 'lib/irb/ruby-lex].each do |word|
|
||||
assert_include candidates, word
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -127,11 +127,43 @@ module TestIRB
|
|||
INPUT
|
||||
end
|
||||
|
||||
def test_history_concurrent_use
|
||||
omit "Skip Editline" if /EditLine/n.match(Readline::VERSION)
|
||||
IRB.conf[:SAVE_HISTORY] = 1
|
||||
assert_history(<<~EXPECTED_HISTORY, <<~INITIAL_HISTORY, <<~INPUT) do |history_file|
|
||||
exit
|
||||
5
|
||||
exit
|
||||
EXPECTED_HISTORY
|
||||
1
|
||||
2
|
||||
3
|
||||
4
|
||||
INITIAL_HISTORY
|
||||
5
|
||||
exit
|
||||
INPUT
|
||||
assert_history(<<~EXPECTED_HISTORY2, <<~INITIAL_HISTORY2, <<~INPUT2)
|
||||
exit
|
||||
EXPECTED_HISTORY2
|
||||
1
|
||||
2
|
||||
3
|
||||
4
|
||||
INITIAL_HISTORY2
|
||||
5
|
||||
exit
|
||||
INPUT2
|
||||
File.utime(File.atime(history_file), File.mtime(history_file) + 2, history_file)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def assert_history(expected_history, initial_irb_history, input)
|
||||
backup_verbose, $VERBOSE = $VERBOSE, nil
|
||||
backup_home = ENV["HOME"]
|
||||
backup_xdg_config_home = ENV.delete("XDG_CONFIG_HOME")
|
||||
IRB.conf[:LC_MESSAGES] = IRB::Locale.new
|
||||
actual_history = nil
|
||||
Dir.mktmpdir("test_irb_history_#{$$}") do |tmpdir|
|
||||
|
@ -143,6 +175,11 @@ module TestIRB
|
|||
io = TestInputMethod.new
|
||||
io.class::HISTORY.clear
|
||||
io.load_history
|
||||
if block_given?
|
||||
history = io.class::HISTORY.dup
|
||||
yield IRB.rc_file("_history")
|
||||
io.class::HISTORY.replace(history)
|
||||
end
|
||||
io.class::HISTORY.concat(input.split)
|
||||
io.save_history
|
||||
|
||||
|
@ -160,6 +197,7 @@ module TestIRB
|
|||
ensure
|
||||
$VERBOSE = backup_verbose
|
||||
ENV["HOME"] = backup_home
|
||||
ENV["XDG_CONFIG_HOME"] = backup_xdg_config_home
|
||||
end
|
||||
|
||||
def with_temp_stdio
|
||||
|
|
|
@ -64,6 +64,12 @@ module TestIRB
|
|||
ENV["IRBRC"] = backup_irbrc
|
||||
end
|
||||
|
||||
def test_recovery_sigint
|
||||
bundle_exec = ENV.key?('BUNDLE_GEMFILE') ? ['-rbundler/setup'] : []
|
||||
status = assert_in_out_err(bundle_exec + %w[-W0 -rirb -e binding.irb;loop{Process.kill("SIGINT",$$)} -- -f --], "exit\n", //, //)
|
||||
Process.kill("SIGKILL", status.pid) if !status.exited? && !status.stopped? && !status.signaled?
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def with_argv(argv)
|
||||
|
|
|
@ -136,6 +136,20 @@ module TestIRB
|
|||
end
|
||||
end
|
||||
|
||||
def test_endless_range_at_end_of_line
|
||||
if Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.6.0')
|
||||
skip 'Endless range is available in 2.6.0 or later'
|
||||
end
|
||||
input_with_prompt = [
|
||||
PromptRow.new('001:0: :> ', %q(a = 3..)),
|
||||
PromptRow.new('002:0: :* ', %q()),
|
||||
]
|
||||
|
||||
lines = input_with_prompt.map(&:content)
|
||||
expected_prompt_list = input_with_prompt.map(&:prompt)
|
||||
assert_dynamic_prompt(lines, expected_prompt_list)
|
||||
end
|
||||
|
||||
def test_incomplete_coding_magic_comment
|
||||
input_with_correct_indents = [
|
||||
Row.new(%q(#coding:u), nil, 0),
|
||||
|
@ -544,8 +558,7 @@ module TestIRB
|
|||
skip 'This test needs Ripper::Lexer#scan to take broken tokens'
|
||||
end
|
||||
|
||||
ruby_lex = RubyLex.new
|
||||
tokens = ruby_lex.ripper_lex_without_warning('%wwww')
|
||||
tokens = RubyLex.ripper_lex_without_warning('%wwww')
|
||||
pos_to_index = {}
|
||||
tokens.each_with_index { |t, i|
|
||||
assert_nil(pos_to_index[t[0]], "There is already another token in the position of #{t.inspect}.")
|
||||
|
@ -558,8 +571,7 @@ module TestIRB
|
|||
skip 'This test needs Ripper::Lexer#scan to take broken tokens'
|
||||
end
|
||||
|
||||
ruby_lex = RubyLex.new
|
||||
tokens = ruby_lex.ripper_lex_without_warning(<<~EOC.chomp)
|
||||
tokens = RubyLex.ripper_lex_without_warning(<<~EOC.chomp)
|
||||
def foo
|
||||
%wwww
|
||||
end
|
||||
|
|
165
test/irb/yamatanooroti/test_rendering.rb
Normal file
165
test/irb/yamatanooroti/test_rendering.rb
Normal file
|
@ -0,0 +1,165 @@
|
|||
require 'irb'
|
||||
|
||||
begin
|
||||
require 'yamatanooroti'
|
||||
|
||||
class IRB::TestRendering < Yamatanooroti::TestCase
|
||||
def setup
|
||||
@pwd = Dir.pwd
|
||||
suffix = '%010d' % Random.rand(0..65535)
|
||||
@tmpdir = File.join(File.expand_path(Dir.tmpdir), "test_irb_#{$$}_#{suffix}")
|
||||
begin
|
||||
Dir.mkdir(@tmpdir)
|
||||
rescue Errno::EEXIST
|
||||
FileUtils.rm_rf(@tmpdir)
|
||||
Dir.mkdir(@tmpdir)
|
||||
end
|
||||
@irbrc_backup = ENV['IRBRC']
|
||||
@irbrc_file = ENV['IRBRC'] = File.join(@tmpdir, 'temporaty_irbrc')
|
||||
File.unlink(@irbrc_file) if File.exist?(@irbrc_file)
|
||||
end
|
||||
|
||||
def teardown
|
||||
FileUtils.rm_rf(@tmpdir)
|
||||
ENV['IRBRC'] = @irbrc_backup
|
||||
ENV.delete('RELINE_TEST_PROMPT') if ENV['RELINE_TEST_PROMPT']
|
||||
end
|
||||
|
||||
def test_launch
|
||||
write_irbrc <<~'LINES'
|
||||
puts 'start IRB'
|
||||
LINES
|
||||
start_terminal(25, 80, %W{ruby -I#{@pwd}/lib -I#{@pwd}/../reline/lib #{@pwd}/exe/irb}, startup_message: 'start IRB')
|
||||
write(<<~EOC)
|
||||
'Hello, World!'
|
||||
EOC
|
||||
close
|
||||
assert_screen(<<~EOC)
|
||||
start IRB
|
||||
irb(main):001:0> 'Hello, World!'
|
||||
=> "Hello, World!"
|
||||
irb(main):002:0>
|
||||
EOC
|
||||
end
|
||||
|
||||
def test_multiline_paste
|
||||
write_irbrc <<~'LINES'
|
||||
puts 'start IRB'
|
||||
LINES
|
||||
start_terminal(25, 80, %W{ruby -I#{@pwd}/lib -I#{@pwd}/../reline/lib #{@pwd}/exe/irb}, startup_message: 'start IRB')
|
||||
write(<<~EOC)
|
||||
class A
|
||||
def inspect; '#<A>'; end
|
||||
def a; self; end
|
||||
def b; true; end
|
||||
end
|
||||
|
||||
a = A.new
|
||||
|
||||
a
|
||||
.a
|
||||
.b
|
||||
EOC
|
||||
close
|
||||
assert_screen(<<~EOC)
|
||||
start IRB
|
||||
irb(main):001:1* class A
|
||||
irb(main):002:1* def inspect; '#<A>'; end
|
||||
irb(main):003:1* def a; self; end
|
||||
irb(main):004:1* def b; true; end
|
||||
irb(main):005:0> end
|
||||
=> :b
|
||||
irb(main):006:0>
|
||||
irb(main):007:0> a = A.new
|
||||
=> #<A>
|
||||
irb(main):008:0>
|
||||
irb(main):009:0> a
|
||||
irb(main):010:0> .a
|
||||
irb(main):011:0> .b
|
||||
=> true
|
||||
irb(main):012:0>
|
||||
EOC
|
||||
end
|
||||
|
||||
def test_evaluate_each_toplevel_statement_by_multiline_paste
|
||||
write_irbrc <<~'LINES'
|
||||
puts 'start IRB'
|
||||
LINES
|
||||
start_terminal(40, 80, %W{ruby -I#{@pwd}/lib -I#{@pwd}/../reline/lib #{@pwd}/exe/irb}, startup_message: 'start IRB')
|
||||
write(<<~EOC)
|
||||
class A
|
||||
def inspect; '#<A>'; end
|
||||
def b; self; end
|
||||
def c; true; end
|
||||
end
|
||||
|
||||
a = A.new
|
||||
|
||||
a
|
||||
.b
|
||||
# aaa
|
||||
.c
|
||||
|
||||
(a)
|
||||
&.b()
|
||||
|
||||
|
||||
class A def b; self; end; def c; true; end; end;
|
||||
a = A.new
|
||||
a
|
||||
.b
|
||||
# aaa
|
||||
.c
|
||||
(a)
|
||||
&.b()
|
||||
EOC
|
||||
close
|
||||
assert_screen(<<~EOC)
|
||||
start IRB
|
||||
irb(main):001:1* class A
|
||||
irb(main):002:1* def inspect; '#<A>'; end
|
||||
irb(main):003:1* def b; self; end
|
||||
irb(main):004:1* def c; true; end
|
||||
irb(main):005:0> end
|
||||
=> :c
|
||||
irb(main):006:0>
|
||||
irb(main):007:0> a = A.new
|
||||
=> #<A>
|
||||
irb(main):008:0>
|
||||
irb(main):009:0> a
|
||||
irb(main):010:0> .b
|
||||
irb(main):011:0> # aaa
|
||||
irb(main):012:0> .c
|
||||
=> true
|
||||
irb(main):013:0>
|
||||
irb(main):014:0> (a)
|
||||
irb(main):015:0> &.b()
|
||||
=> #<A>
|
||||
irb(main):016:0>
|
||||
irb(main):017:0>
|
||||
irb(main):018:0> class A def b; self; end; def c; true; end; end;
|
||||
=> :c
|
||||
irb(main):019:0> a = A.new
|
||||
=> #<A>
|
||||
irb(main):020:0> a
|
||||
irb(main):021:0> .b
|
||||
irb(main):022:0> # aaa
|
||||
irb(main):023:0> .c
|
||||
=> true
|
||||
irb(main):024:0> (a)
|
||||
irb(main):025:0> &.b()
|
||||
=> #<A>
|
||||
irb(main):026:0>
|
||||
EOC
|
||||
end
|
||||
|
||||
private def write_irbrc(content)
|
||||
File.open(@irbrc_file, 'w') do |f|
|
||||
f.write content
|
||||
end
|
||||
end
|
||||
end
|
||||
rescue LoadError, NameError
|
||||
# On Ruby repository, this test suit doesn't run because Ruby repo doesn't
|
||||
# have the yamatanooroti gem.
|
||||
end
|
|
@ -65,6 +65,8 @@ class Reline::Test < Reline::TestCase
|
|||
Reline.completer_word_break_characters = "[".encode(Encoding::ASCII)
|
||||
assert_equal("[", Reline.completer_word_break_characters)
|
||||
assert_equal(get_reline_encoding, Reline.completer_word_break_characters.encoding)
|
||||
|
||||
assert_nothing_raised { Reline.completer_word_break_characters = '' }
|
||||
ensure
|
||||
Reline.completer_word_break_characters = completer_word_break_characters
|
||||
end
|
||||
|
@ -89,6 +91,8 @@ class Reline::Test < Reline::TestCase
|
|||
Reline.completer_quote_characters = "`".encode(Encoding::ASCII)
|
||||
assert_equal("`", Reline.completer_quote_characters)
|
||||
assert_equal(get_reline_encoding, Reline.completer_quote_characters.encoding)
|
||||
|
||||
assert_nothing_raised { Reline.completer_quote_characters = '' }
|
||||
ensure
|
||||
Reline.completer_quote_characters = completer_quote_characters
|
||||
end
|
||||
|
|
|
@ -20,4 +20,58 @@ class Reline::LineEditor::StringProcessingTest < Reline::TestCase
|
|||
width = @line_editor.send(:calculate_width, "\1\e[31m\2RubyColor\1\e[34m\2 default string \1\e[m\2>", true)
|
||||
assert_equal('RubyColor default string >'.size, width)
|
||||
end
|
||||
|
||||
def test_completion_proc_with_preposing_and_postposing
|
||||
buf = ['def hoge', ' puts :aaa', 'end']
|
||||
|
||||
@line_editor.instance_variable_set(:@is_multiline, true)
|
||||
@line_editor.instance_variable_set(:@buffer_of_lines, buf)
|
||||
@line_editor.instance_variable_set(:@line, buf[1])
|
||||
@line_editor.instance_variable_set(:@byte_pointer, 3)
|
||||
@line_editor.instance_variable_set(:@cursor, 3)
|
||||
@line_editor.instance_variable_set(:@cursor_max, 11)
|
||||
@line_editor.instance_variable_set(:@line_index, 1)
|
||||
@line_editor.instance_variable_set(:@completion_proc, proc { |target|
|
||||
assert_equal('p', target)
|
||||
})
|
||||
@line_editor.__send__(:call_completion_proc)
|
||||
|
||||
@line_editor.instance_variable_set(:@is_multiline, true)
|
||||
@line_editor.instance_variable_set(:@buffer_of_lines, buf)
|
||||
@line_editor.instance_variable_set(:@line, buf[1])
|
||||
@line_editor.instance_variable_set(:@byte_pointer, 6)
|
||||
@line_editor.instance_variable_set(:@cursor, 6)
|
||||
@line_editor.instance_variable_set(:@cursor_max, 11)
|
||||
@line_editor.instance_variable_set(:@line_index, 1)
|
||||
@line_editor.instance_variable_set(:@completion_proc, proc { |target, pre, post|
|
||||
assert_equal('puts', target)
|
||||
assert_equal("def hoge\n ", pre)
|
||||
assert_equal(" :aaa\nend", post)
|
||||
})
|
||||
@line_editor.__send__(:call_completion_proc)
|
||||
|
||||
@line_editor.instance_variable_set(:@line, buf[0])
|
||||
@line_editor.instance_variable_set(:@byte_pointer, 6)
|
||||
@line_editor.instance_variable_set(:@cursor, 6)
|
||||
@line_editor.instance_variable_set(:@cursor_max, 8)
|
||||
@line_editor.instance_variable_set(:@line_index, 0)
|
||||
@line_editor.instance_variable_set(:@completion_proc, proc { |target, pre, post|
|
||||
assert_equal('ho', target)
|
||||
assert_equal('def ', pre)
|
||||
assert_equal("ge\n puts :aaa\nend", post)
|
||||
})
|
||||
@line_editor.__send__(:call_completion_proc)
|
||||
|
||||
@line_editor.instance_variable_set(:@line, buf[2])
|
||||
@line_editor.instance_variable_set(:@byte_pointer, 1)
|
||||
@line_editor.instance_variable_set(:@cursor, 1)
|
||||
@line_editor.instance_variable_set(:@cursor_max, 3)
|
||||
@line_editor.instance_variable_set(:@line_index, 2)
|
||||
@line_editor.instance_variable_set(:@completion_proc, proc { |target, pre, post|
|
||||
assert_equal('e', target)
|
||||
assert_equal("def hoge\n puts :aaa\n", pre)
|
||||
assert_equal('nd', post)
|
||||
})
|
||||
@line_editor.__send__(:call_completion_proc)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -59,4 +59,17 @@ class Reline::WithinPipeTest < Reline::TestCase
|
|||
@writer.write("abcde\C-b\C-b\C-b\C-x\C-d\C-x\C-h\C-x\C-v\C-a\C-f\C-f EF\C-x\C-t gh\C-x\M-t\C-b\C-b\C-b\C-b\C-b\C-b\C-b\C-b\C-x\M-u\C-x\M-l\C-x\M-c\n")
|
||||
assert_equal "a\C-aDE gh Fe", Reline.readmultiline(&proc{ true })
|
||||
end
|
||||
|
||||
def test_delete_text_in_multiline
|
||||
@writer.write("abc\ndef\nxyz\n")
|
||||
result = Reline.readmultiline(&proc{ |str|
|
||||
if str.include?('xyz')
|
||||
Reline.delete_text
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
})
|
||||
assert_equal "abc\ndef", result
|
||||
end
|
||||
end
|
||||
|
|
|
@ -719,6 +719,17 @@ begin
|
|||
EOC
|
||||
end
|
||||
|
||||
def test_reset_rest_height_when_clear_screen
|
||||
start_terminal(5, 20, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl}, startup_message: 'Multiline REPL.')
|
||||
write("\n\n\n\C-l3\n")
|
||||
close
|
||||
assert_screen(<<~EOC)
|
||||
prompt> 3
|
||||
=> 3
|
||||
prompt>
|
||||
EOC
|
||||
end
|
||||
|
||||
private def write_inputrc(content)
|
||||
File.open(@inputrc_file, 'w') do |f|
|
||||
f.write content
|
||||
|
|
Loading…
Reference in a new issue