diff --git a/lib/irb/input-method.rb b/lib/irb/input-method.rb
index df73ceec5..1854567a2 100644
--- a/lib/irb/input-method.rb
+++ b/lib/irb/input-method.rb
@@ -316,13 +316,7 @@ def gets
Reline.output = @stdout
Reline.prompt_proc = @prompt_proc
Reline.auto_indent_proc = @auto_indent_proc if @auto_indent_proc
-
- l = readmultiline(@prompt, false) do |line|
- next false if Reline::IOGate.in_pasting?
- @check_termination_proc.call(line)
- end
-
- if l
+ if l = readmultiline(@prompt, false, &@check_termination_proc)
HISTORY.push(l) if !l.empty?
@line[@line_no += 1] = l + "\n"
else
diff --git a/lib/irb/ruby-lex.rb b/lib/irb/ruby-lex.rb
index 586ac3970..82df06da2 100644
--- a/lib/irb/ruby-lex.rb
+++ b/lib/irb/ruby-lex.rb
@@ -47,12 +47,26 @@ def set_input(io, p = nil, &block)
@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
@@ -739,5 +753,50 @@ def process_literal_type(tokens = @tokens)
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/test/irb/yamatanooroti/test_rendering.rb b/test/irb/yamatanooroti/test_rendering.rb
index 171bfeaff..8f55b38a9 100644
--- a/test/irb/yamatanooroti/test_rendering.rb
+++ b/test/irb/yamatanooroti/test_rendering.rb
@@ -49,6 +49,7 @@ def test_multiline_paste
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; '#'; end
def a; self; end
def b; true; end
end
@@ -63,17 +64,92 @@ def b; true; end
assert_screen(<<~EOC)
start IRB
irb(main):001:1* class A
- irb(main):002:1* def a; self; end
- irb(main):003:1* def b; true; end
- irb(main):004:0> end
- irb(main):005:0*
- irb(main):006:0> a = A.new
- irb(main):007:0*
- irb(main):008:0> a
- irb(main):009:0> .a
- irb(main):010:0> .b
+ irb(main):002:1* def inspect; '#'; 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
+ => #
+ irb(main):008:0>
+ irb(main):009:0> a
+ irb(main):010:0> .a
+ irb(main):011:0> .b
=> true
- irb(main):011:0>
+ 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; '#'; 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; '#'; 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
+ => #
+ 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()
+ => #
+ 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
+ => #
+ 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()
+ => #
+ irb(main):026:0>
EOC
end