summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoraycabta <aycabta@gmail.com>2021-04-03 18:26:46 +0900
committerGitHub <noreply@github.com>2021-04-03 18:26:46 +0900
commitcafa7904e7233d1a4b8ae7e738f6bd35ed67d642 (patch)
treee0f7fdc0ec33c37d4de1ab9185da77dbdacc75ba
parent6abb8ee711a941b7ad31d21fdb429750f7ea4463 (diff)
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>
-rw-r--r--lib/irb.rb22
-rw-r--r--lib/irb/cmd/ls.rb83
-rw-r--r--lib/irb/cmd/nop.rb14
-rw-r--r--lib/irb/cmd/show_source.rb86
-rw-r--r--lib/irb/cmd/whereami.rb20
-rw-r--r--lib/irb/color.rb5
-rw-r--r--lib/irb/color_printer.rb9
-rw-r--r--lib/irb/completion.rb76
-rw-r--r--lib/irb/ext/save-history.rb20
-rw-r--r--lib/irb/extend-command.rb25
-rw-r--r--lib/irb/input-method.rb1
-rw-r--r--lib/irb/irb.gemspec47
-rw-r--r--lib/irb/lc/help-message12
-rw-r--r--lib/irb/ruby-lex.rb86
-rw-r--r--lib/irb/version.rb4
-rw-r--r--lib/reline.rb4
-rw-r--r--lib/reline/line_editor.rb81
-rw-r--r--lib/reline/version.rb2
-rw-r--r--test/irb/test_cmd.rb121
-rw-r--r--test/irb/test_color.rb1
-rw-r--r--test/irb/test_color_printer.rb1
-rw-r--r--test/irb/test_completion.rb28
-rw-r--r--test/irb/test_history.rb38
-rw-r--r--test/irb/test_init.rb6
-rw-r--r--test/irb/test_ruby_lex.rb20
-rw-r--r--test/irb/yamatanooroti/test_rendering.rb165
-rw-r--r--test/reline/test_reline.rb4
-rw-r--r--test/reline/test_string_processing.rb54
-rw-r--r--test/reline/test_within_pipe.rb13
-rw-r--r--test/reline/yamatanooroti/test_rendering.rb11
30 files changed, 920 insertions, 139 deletions
diff --git a/lib/irb.rb b/lib/irb.rb
index 7f99974f28..93c4d25c92 100644
--- a/lib/irb.rb
+++ b/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
diff --git a/lib/irb/cmd/ls.rb b/lib/irb/cmd/ls.rb
new file mode 100644
index 0000000000..f163f4f9e6
--- /dev/null
+++ b/lib/irb/cmd/ls.rb
@@ -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:
diff --git a/lib/irb/cmd/nop.rb b/lib/irb/cmd/nop.rb
index fa3c011b5f..d6f7a611a6 100644
--- a/lib/irb/cmd/nop.rb
+++ b/lib/irb/cmd/nop.rb
@@ -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)
diff --git a/lib/irb/cmd/show_source.rb b/lib/irb/cmd/show_source.rb
new file mode 100644
index 0000000000..0bd40b7d4e
--- /dev/null
+++ b/lib/irb/cmd/show_source.rb
@@ -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:
diff --git a/lib/irb/cmd/whereami.rb b/lib/irb/cmd/whereami.rb
new file mode 100644
index 0000000000..b3def11b93
--- /dev/null
+++ b/lib/irb/cmd/whereami.rb
@@ -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:
diff --git a/lib/irb/color.rb b/lib/irb/color.rb
index a054bb20f8..cfbb3cc668 100644
--- a/lib/irb/color.rb
+++ b/lib/irb/color.rb
@@ -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
diff --git a/lib/irb/color_printer.rb b/lib/irb/color_printer.rb
index 92afea51cd..30c6825750 100644
--- a/lib/irb/color_printer.rb
+++ b/lib/irb/color_printer.rb
@@ -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
diff --git a/lib/irb/completion.rb b/lib/irb/completion.rb
index 22a1ad1d3d..d1bb82122e 100644
--- a/lib/irb/completion.rb
+++ b/lib/irb/completion.rb
@@ -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|
diff --git a/lib/irb/ext/save-history.rb b/lib/irb/ext/save-history.rb
index ac358c8ccb..7acaebe36a 100644
--- a/lib/irb/ext/save-history.rb
+++ b/lib/irb/ext/save-history.rb
@@ -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
diff --git a/lib/irb/extend-command.rb b/lib/irb/extend-command.rb
index 347323247e..339e9e6084 100644
--- a/lib/irb/extend-command.rb
+++ b/lib/irb/extend-command.rb
@@ -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
diff --git a/lib/irb/input-method.rb b/lib/irb/input-method.rb
index e223672985..1854567a2d 100644
--- a/lib/irb/input-method.rb
+++ b/lib/irb/input-method.rb
@@ -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]
diff --git a/lib/irb/irb.gemspec b/lib/irb/irb.gemspec
index 9842b4bce1..38fee9d9fb 100644
--- a/lib/irb/irb.gemspec
+++ b/lib/irb/irb.gemspec
@@ -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"]
diff --git a/lib/irb/lc/help-message b/lib/irb/lc/help-message
index a80facc9c5..9c3ea859ad 100644
--- a/lib/irb/lc/help-message
+++ b/lib/irb/lc/help-message
@@ -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
diff --git a/lib/irb/ruby-lex.rb b/lib/irb/ruby-lex.rb
index ce94797dad..82df06da2b 100644
--- a/lib/irb/ruby-lex.rb
+++ b/lib/irb/ruby-lex.rb
@@ -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:
diff --git a/lib/irb/version.rb b/lib/irb/version.rb
index 0a4a1bb922..0014bdda74 100644
--- a/lib/irb/version.rb
+++ b/lib/irb/version.rb
@@ -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
diff --git a/lib/reline.rb b/lib/reline.rb
index 81ea9f9b58..a7bd4d9280 100644
--- a/lib/reline.rb
+++ b/lib/reline.rb
@@ -446,6 +446,10 @@ module Reline
}
end
+ def self.ungetc(c)
+ Reline::IOGate.ungetc(c)
+ end
+
def self.line_editor
core.line_editor
end
diff --git a/lib/reline/line_editor.rb b/lib/reline/line_editor.rb
index 12a2bde234..7d71e62d63 100644
--- a/lib/reline/line_editor.rb
+++ b/lib/reline/line_editor.rb
@@ -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)
diff --git a/lib/reline/version.rb b/lib/reline/version.rb
index 11e8145c7f..44db465a2f 100644
--- a/lib/reline/version.rb
+++ b/lib/reline/version.rb
@@ -1,3 +1,3 @@
module Reline
- VERSION = '0.2.4'
+ VERSION = '0.2.5'
end
diff --git a/test/irb/test_cmd.rb b/test/irb/test_cmd.rb
index 41f84f1922..044d852a32 100644
--- a/test/irb/test_cmd.rb
+++ b/test/irb/test_cmd.rb
@@ -5,6 +5,32 @@ require "irb/extend-command"
module TestIRB
class ExtendCommand < Test::Unit::TestCase
+ class TestInputMethod < ::IRB::InputMethod
+ attr_reader :list, :line_no
+
+ def initialize(list = [])
+ super("test")
+ @line_no = 0
+ @list = list
+ end
+
+ def gets
+ @list[@line_no]&.tap {@line_no += 1}
+ end
+
+ def eof?
+ @line_no >= @list.size
+ end
+
+ def encoding
+ Encoding.default_external
+ end
+
+ def reset
+ @line_no = 0
+ end
+ end
+
def setup
@pwd = Dir.pwd
@tmpdir = File.join(Dir.tmpdir, "test_reline_config_#{$$}")
@@ -17,12 +43,14 @@ module TestIRB
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)
@@ -42,12 +70,12 @@ module TestIRB
IRB.conf[:USE_SINGLELINE] = false
IRB.conf[:VERBOSE] = false
workspace = IRB::WorkSpace.new(self)
- irb = IRB::Irb.new(workspace)
+ irb = IRB::Irb.new(workspace, TestInputMethod.new([]))
IRB.conf[:MAIN_CONTEXT] = irb.context
expected = %r{
Ruby\sversion: .+\n
IRB\sversion:\sirb .+\n
- InputMethod:\sReidlineInputMethod\swith\sReline .+ and .+\n
+ InputMethod:\sAbstract\sInputMethod\n
\.irbrc\spath: .+\n
RUBY_PLATFORM: .+
}x
@@ -62,12 +90,12 @@ module TestIRB
IRB.conf[:USE_SINGLELINE] = true
IRB.conf[:VERBOSE] = false
workspace = IRB::WorkSpace.new(self)
- irb = IRB::Irb.new(workspace)
+ irb = IRB::Irb.new(workspace, TestInputMethod.new([]))
IRB.conf[:MAIN_CONTEXT] = irb.context
expected = %r{
Ruby\sversion: .+\n
IRB\sversion:\sirb .+\n
- InputMethod:\sReadlineInputMethod\swith .+ and .+\n
+ InputMethod:\sAbstract\sInputMethod\n
\.irbrc\spath: .+\n
RUBY_PLATFORM: .+
}x
@@ -85,12 +113,12 @@ module TestIRB
IRB.conf[:USE_SINGLELINE] = false
IRB.conf[:VERBOSE] = false
workspace = IRB::WorkSpace.new(self)
- irb = IRB::Irb.new(workspace)
+ irb = IRB::Irb.new(workspace, TestInputMethod.new([]))
IRB.conf[:MAIN_CONTEXT] = irb.context
expected = %r{
Ruby\sversion: .+\n
IRB\sversion:\sirb .+\n
- InputMethod:\sReidlineInputMethod\swith\sReline\s[^ ]+(?!\sand\s.+)\n
+ InputMethod:\sAbstract\sInputMethod\n
RUBY_PLATFORM: .+\n
\z
}x
@@ -112,12 +140,12 @@ module TestIRB
IRB.conf[:USE_SINGLELINE] = true
IRB.conf[:VERBOSE] = false
workspace = IRB::WorkSpace.new(self)
- irb = IRB::Irb.new(workspace)
+ irb = IRB::Irb.new(workspace, TestInputMethod.new([]))
IRB.conf[:MAIN_CONTEXT] = irb.context
expected = %r{
Ruby\sversion: .+\n
IRB\sversion:\sirb .+\n
- InputMethod:\sReadlineInputMethod\swith\s(?~.*\sand\s.+)\n
+ InputMethod:\sAbstract\sInputMethod\n
RUBY_PLATFORM: .+\n
\z
}x
@@ -128,32 +156,6 @@ module TestIRB
IRB.const_set(:IRBRC_EXT, ext_backup)
end
- class TestInputMethod < ::IRB::InputMethod
- attr_reader :list, :line_no
-
- def initialize(list = [])
- super("test")
- @line_no = 0
- @list = list
- end
-
- def gets
- @list[@line_no]&.tap {@line_no += 1}
- end
-
- def eof?
- @line_no >= @list.size
- end
-
- def encoding
- Encoding.default_external
- end
-
- def reset
- @line_no = 0
- end
- 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
diff --git a/test/irb/test_color.rb b/test/irb/test_color.rb
index 9976008124..a28ae06117 100644
--- a/test/irb/test_color.rb
+++ b/test/irb/test_color.rb
@@ -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+
diff --git a/test/irb/test_color_printer.rb b/test/irb/test_color_printer.rb
index 1b28837658..1afc7ccf55 100644
--- a/test/irb/test_color_printer.rb
+++ b/test/irb/test_color_printer.rb
@@ -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",
diff --git a/test/irb/test_completion.rb b/test/irb/test_completion.rb
index 984453d059..535690ae22 100644
--- a/test/irb/test_completion.rb
+++ b/test/irb/test_completion.rb
@@ -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
diff --git a/test/irb/test_history.rb b/test/irb/test_history.rb
index 392a6afa9a..81b7fe8679 100644
--- a/test/irb/test_history.rb
+++ b/test/irb/test_history.rb
@@ -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
diff --git a/test/irb/test_init.rb b/test/irb/test_init.rb
index 83b4b5a543..2c50b5da3a 100644
--- a/test/irb/test_init.rb
+++ b/test/irb/test_init.rb
@@ -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)
diff --git a/test/irb/test_ruby_lex.rb b/test/irb/test_ruby_lex.rb
index a45ca668b9..556afbd776 100644
--- a/test/irb/test_ruby_lex.rb
+++ b/test/irb/test_ruby_lex.rb
@@ -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
diff --git a/test/irb/yamatanooroti/test_rendering.rb b/test/irb/yamatanooroti/test_rendering.rb
new file mode 100644
index 0000000000..8f55b38a93
--- /dev/null
+++ b/test/irb/yamatanooroti/test_rendering.rb
@@ -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
diff --git a/test/reline/test_reline.rb b/test/reline/test_reline.rb
index d2de4690d5..0f32ec4421 100644
--- a/test/reline/test_reline.rb
+++ b/test/reline/test_reline.rb
@@ -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
diff --git a/test/reline/test_string_processing.rb b/test/reline/test_string_processing.rb
index e76fa384f2..0e0ee9cc04 100644
--- a/test/reline/test_string_processing.rb
+++ b/test/reline/test_string_processing.rb
@@ -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
diff --git a/test/reline/test_within_pipe.rb b/test/reline/test_within_pipe.rb
index 70a0e0a5de..e453b1902e 100644
--- a/test/reline/test_within_pipe.rb
+++ b/test/reline/test_within_pipe.rb
@@ -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
diff --git a/test/reline/yamatanooroti/test_rendering.rb b/test/reline/yamatanooroti/test_rendering.rb
index 6f9a14de67..13693e7c4d 100644
--- a/test/reline/yamatanooroti/test_rendering.rb
+++ b/test/reline/yamatanooroti/test_rendering.rb
@@ -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