# frozen_string_literal: false require 'irb' require_relative "helper" module TestIRB class NestingParserTest < TestCase def setup save_encodings end def teardown restore_encodings end def parse_by_line(code) IRB::NestingParser.parse_by_line(IRB::RubyLex.ripper_lex_without_warning(code)) end def test_open_tokens code = <<~'EOS' class A def f if true tap do { x: " #{p(1, 2, 3 EOS opens = IRB::NestingParser.open_tokens(IRB::RubyLex.ripper_lex_without_warning(code)) assert_equal(%w[class def if do { " #{ (], opens.map(&:tok)) end def test_parse_by_line code = <<~EOS (((((1+2 ).to_s())).tap do ((( EOS _tokens, prev_opens, next_opens, min_depth = parse_by_line(code).last assert_equal(%w[( ( ( ( (], prev_opens.map(&:tok)) assert_equal(%w[( ( do ( ( (], next_opens.map(&:tok)) assert_equal(2, min_depth) end def test_ruby_syntax code = <<~'EOS' class A 1 if 2 1 while 2 1 until 2 1 unless 2 1 rescue 2 begin; rescue; ensure; end tap do; rescue; ensure; end class B; end module C; end def f; end def `; end def f() = 1 %(); %w[]; %q(); %r{}; %i[] "#{1}"; ''; /#{1}/; `#{1}` :sym; :"sym"; :+; :`; :if [1, 2, 3] { x: 1, y: 2 } (a, (*b, c), d), e = 1, 2, 3 ->(a){}; ->(a) do end -> a = -> b = :do do end do end if 1; elsif 2; else; end unless 1; end while 1; end until 1; end for i in j; end case 1; when 2; end puts(1, 2, 3) loop{|i|} loop do |i| end end EOS line_results = parse_by_line(code) assert_equal(code.lines.size, line_results.size) class_open, *inner_line_results, class_close = line_results assert_equal(['class'], class_open[2].map(&:tok)) inner_line_results.each {|result| assert_equal(['class'], result[2].map(&:tok)) } assert_equal([], class_close[2].map(&:tok)) end def test_multiline_string code = <<~EOS " aaa bbb " < do end do here end EOS line_results = parse_by_line(code).select { |tokens,| tokens.map(&:last).include?('here') } assert_equal(7, line_results.size) line_results.each do |_tokens, _prev_opens, next_opens, _min_depth| assert_equal(['for'], next_opens.map(&:tok)) end end def test_while_until base_code = <<~'EOS' while_or_until true here end while_or_until a < c here end while_or_until true do here end while_or_until # comment (a + b) < # comment c do here end while_or_until :\ do do here end while_or_until def do; end == :do do here end while_or_until -> do end do here end EOS %w[while until].each do |keyword| code = base_code.gsub('while_or_until', keyword) line_results = parse_by_line(code).select { |tokens,| tokens.map(&:last).include?('here') } assert_equal(7, line_results.size) line_results.each do |_tokens, _prev_opens, next_opens, _min_depth| assert_equal([keyword], next_opens.map(&:tok) ) end end end def test_case_in if Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.7.0') pend 'This test requires ruby version that supports case-in syntax' end code = <<~EOS case 1 in 1 here in 2 here end EOS line_results = parse_by_line(code).select { |tokens,| tokens.map(&:last).include?('here') } assert_equal(2, line_results.size) line_results.each do |_tokens, _prev_opens, next_opens, _min_depth| assert_equal(['in'], next_opens.map(&:tok)) end end end end