diff options
Diffstat (limited to 'spec/syntax_suggest')
| -rw-r--r-- | spec/syntax_suggest/integration/ruby_command_line_spec.rb | 4 | ||||
| -rw-r--r-- | spec/syntax_suggest/integration/syntax_suggest_spec.rb | 33 | ||||
| -rw-r--r-- | spec/syntax_suggest/spec_helper.rb | 10 | ||||
| -rw-r--r-- | spec/syntax_suggest/unit/api_spec.rb | 10 | ||||
| -rw-r--r-- | spec/syntax_suggest/unit/clean_document_spec.rb | 2 | ||||
| -rw-r--r-- | spec/syntax_suggest/unit/code_block_spec.rb | 2 | ||||
| -rw-r--r-- | spec/syntax_suggest/unit/code_line_spec.rb | 15 | ||||
| -rw-r--r-- | spec/syntax_suggest/unit/core_ext_spec.rb | 2 | ||||
| -rw-r--r-- | spec/syntax_suggest/unit/explain_syntax_spec.rb | 32 | ||||
| -rw-r--r-- | spec/syntax_suggest/unit/lex_all_spec.rb | 26 | ||||
| -rw-r--r-- | spec/syntax_suggest/unit/visitor_spec.rb | 119 |
11 files changed, 193 insertions, 62 deletions
diff --git a/spec/syntax_suggest/integration/ruby_command_line_spec.rb b/spec/syntax_suggest/integration/ruby_command_line_spec.rb index c1ec4be54e..02354ceff0 100644 --- a/spec/syntax_suggest/integration/ruby_command_line_spec.rb +++ b/spec/syntax_suggest/integration/ruby_command_line_spec.rb @@ -94,8 +94,6 @@ module SyntaxSuggest end it "gem can be tested when executing on Ruby with default gem included" do - skip if Gem::Version.new(RUBY_VERSION) < Gem::Version.new("3.2") - out = `#{ruby} -I#{lib_dir} -rsyntax_suggest -e "puts SyntaxError.instance_method(:detailed_message).source_location" 2>&1` expect($?.success?).to be_truthy @@ -103,8 +101,6 @@ module SyntaxSuggest end it "annotates a syntax error in Ruby 3.2+ when require is not used" do - skip if Gem::Version.new(RUBY_VERSION) < Gem::Version.new("3.2") - Dir.mktmpdir do |dir| tmpdir = Pathname(dir) script = tmpdir.join("script.rb") diff --git a/spec/syntax_suggest/integration/syntax_suggest_spec.rb b/spec/syntax_suggest/integration/syntax_suggest_spec.rb index 015d088c92..de9bc5d38e 100644 --- a/spec/syntax_suggest/integration/syntax_suggest_spec.rb +++ b/spec/syntax_suggest/integration/syntax_suggest_spec.rb @@ -4,10 +4,6 @@ require_relative "../spec_helper" module SyntaxSuggest RSpec.describe "Integration tests that don't spawn a process (like using the cli)" do - before(:each) do - skip "Benchmark is not available" unless defined?(::Benchmark) - end - it "does not timeout on massive files" do next unless ENV["SYNTAX_SUGGEST_TIMEOUT"] @@ -17,7 +13,7 @@ module SyntaxSuggest io = StringIO.new - benchmark = Benchmark.measure do + benchmark_measure do debug_perf do SyntaxSuggest.call( io: io, @@ -28,7 +24,6 @@ module SyntaxSuggest end debug_display(io.string) - debug_display(benchmark) expect(io.string).to include(<<~EOM) 6 class SyntaxTree < Ripper @@ -46,7 +41,7 @@ module SyntaxSuggest io = StringIO.new debug_perf do - benchmark = Benchmark.measure do + benchmark_measure do SyntaxSuggest.call( io: io, source: file.read, @@ -54,7 +49,6 @@ module SyntaxSuggest ) end debug_display(io.string) - debug_display(benchmark) end expect(io.string).to_not include("def ruby_install_binstub_path") @@ -209,6 +203,29 @@ module SyntaxSuggest EOM end + it "multi-line chain with missing paren" do + source = <<~EOM + class Dog + def bark + User + .where(name: "schneems" + .first + end + end + EOM + io = StringIO.new + SyntaxSuggest.call( + io: io, + source: source + ) + out = io.string + expect(out).to include(<<~EOM) + > 3 User + > 4 .where(name: "schneems" + > 5 .first + EOM + end + it "empty else" do source = <<~EOM class Foo diff --git a/spec/syntax_suggest/spec_helper.rb b/spec/syntax_suggest/spec_helper.rb index b5d2924e69..c0aaf0f4f7 100644 --- a/spec/syntax_suggest/spec_helper.rb +++ b/spec/syntax_suggest/spec_helper.rb @@ -85,6 +85,16 @@ def debug_perf end end +def benchmark_measure + raise "No block given" unless block_given? + + if defined?(::Benchmark) + debug_display(Benchmark.measure { yield }) + else + yield + end +end + def run!(cmd, raise_on_nonzero_exit: true) out = `#{cmd} 2>&1` raise "Command: #{cmd} failed: #{out}" if !$?.success? && raise_on_nonzero_exit diff --git a/spec/syntax_suggest/unit/api_spec.rb b/spec/syntax_suggest/unit/api_spec.rb index e900b9e10b..9299a17bee 100644 --- a/spec/syntax_suggest/unit/api_spec.rb +++ b/spec/syntax_suggest/unit/api_spec.rb @@ -8,12 +8,6 @@ end module SyntaxSuggest RSpec.describe "Top level SyntaxSuggest api" do - it "doesn't load prism if env var is set" do - skip("SYNTAX_SUGGEST_DISABLE_PRISM not set") unless ENV["SYNTAX_SUGGEST_DISABLE_PRISM"] - - expect(SyntaxSuggest.use_prism_parser?).to be_falsey - end - it "has a `handle_error` interface" do fake_error = Object.new def fake_error.message @@ -69,8 +63,6 @@ module SyntaxSuggest end it "respects highlight API" do - skip if Gem::Version.new(RUBY_VERSION) < Gem::Version.new("3.2") - core_ext_file = lib_dir.join("syntax_suggest").join("core_ext.rb") require_relative core_ext_file @@ -91,8 +83,6 @@ module SyntaxSuggest end it "can be disabled via falsey kwarg" do - skip if Gem::Version.new(RUBY_VERSION) < Gem::Version.new("3.2") - core_ext_file = lib_dir.join("syntax_suggest").join("core_ext.rb") require_relative core_ext_file diff --git a/spec/syntax_suggest/unit/clean_document_spec.rb b/spec/syntax_suggest/unit/clean_document_spec.rb index 5b5ca04cfd..3d83bd13c0 100644 --- a/spec/syntax_suggest/unit/clean_document_spec.rb +++ b/spec/syntax_suggest/unit/clean_document_spec.rb @@ -113,7 +113,7 @@ module SyntaxSuggest lines = CleanDocument.new(source: source).lines expect(lines[0].to_s).to eq($/) expect(lines[1].to_s).to eq('puts "what"' + $/) - expect(lines[2].to_s).to eq($/) + expect(lines[2].to_s).to eq(" " + $/) end it "trailing slash: does not join trailing do" do diff --git a/spec/syntax_suggest/unit/code_block_spec.rb b/spec/syntax_suggest/unit/code_block_spec.rb index 3ab2751b27..dfea307668 100644 --- a/spec/syntax_suggest/unit/code_block_spec.rb +++ b/spec/syntax_suggest/unit/code_block_spec.rb @@ -33,7 +33,7 @@ module SyntaxSuggest array = [block_2, block_1, block_0].sort expect(array.last).to eq(block_2) - block = CodeBlock.new(lines: CodeLine.new(line: " " * 8 + "foo", index: 4, lex: [])) + block = CodeBlock.new(lines: CodeLine.new(line: " " * 8 + "foo", index: 4, tokens: [], consecutive: false)) array.prepend(block) expect(array.max).to eq(block) end diff --git a/spec/syntax_suggest/unit/code_line_spec.rb b/spec/syntax_suggest/unit/code_line_spec.rb index 5b62cc2757..309d3d1c1e 100644 --- a/spec/syntax_suggest/unit/code_line_spec.rb +++ b/spec/syntax_suggest/unit/code_line_spec.rb @@ -17,8 +17,6 @@ module SyntaxSuggest end it "supports endless method definitions" do - skip("Unsupported ruby version") unless Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("3") - line = CodeLine.from_source(<<~EOM).first def square(x) = x * x EOM @@ -46,7 +44,7 @@ module SyntaxSuggest EOM # Indicates line 1 can join 2, 2 can join 3, but 3 won't join it's next line - expect(code_lines.map(&:ignore_newline_not_beg?)).to eq([true, true, false, false]) + expect(code_lines.map(&:consecutive?)).to eq([true, true, false, false]) end it "trailing if" do @@ -131,14 +129,15 @@ module SyntaxSuggest it "knows empty lines" do code_lines = CodeLine.from_source(<<~EOM) - # Not empty + # Comment only + foo # Inline comment - # Not empty + bar EOM - expect(code_lines.map(&:empty?)).to eq([false, true, false]) - expect(code_lines.map(&:not_empty?)).to eq([true, false, true]) - expect(code_lines.map { |l| SyntaxSuggest.valid?(l) }).to eq([true, true, true]) + expect(code_lines.map(&:empty?)).to eq([true, false, true, false]) + expect(code_lines.map(&:not_empty?)).to eq([false, true, false, true]) + expect(code_lines.map { |l| SyntaxSuggest.valid?(l) }).to eq([true, true, true, true]) end it "counts indentations" do diff --git a/spec/syntax_suggest/unit/core_ext_spec.rb b/spec/syntax_suggest/unit/core_ext_spec.rb index 499c38a240..d579cc8dc4 100644 --- a/spec/syntax_suggest/unit/core_ext_spec.rb +++ b/spec/syntax_suggest/unit/core_ext_spec.rb @@ -3,8 +3,6 @@ require_relative "../spec_helper" module SyntaxSuggest RSpec.describe "Core extension" do it "SyntaxError monkepatch ensures there is a newline to the end of the file" do - skip if Gem::Version.new(RUBY_VERSION) < Gem::Version.new("3.2") - Dir.mktmpdir do |dir| tmpdir = Pathname(dir) file = tmpdir.join("file.rb") diff --git a/spec/syntax_suggest/unit/explain_syntax_spec.rb b/spec/syntax_suggest/unit/explain_syntax_spec.rb index c62a42b925..7ddb32b8ea 100644 --- a/spec/syntax_suggest/unit/explain_syntax_spec.rb +++ b/spec/syntax_suggest/unit/explain_syntax_spec.rb @@ -17,9 +17,23 @@ module SyntaxSuggest expect(explain.errors.join.strip).to_not be_empty end - it "handles %w[]" do + %w[w W i I].each do |type| + it "handles %#{type}-style array" do + source = <<~EOM + node.is_a?(Op) && %#{type}[| ||].include?(node.value) && + EOM + + explain = ExplainSyntax.new( + code_lines: CodeLine.from_source(source) + ).call + + expect(explain.missing).to eq([]) + end + end + + it "handles %r-style regexp" do source = <<~EOM - node.is_a?(Op) && %w[| ||].include?(node.value) && + node.is_a?(Op) && %r{| ||}.include?(node.value) && EOM explain = ExplainSyntax.new( @@ -29,6 +43,20 @@ module SyntaxSuggest expect(explain.missing).to eq([]) end + ["", "q", "Q"].each do |type| + it "handles %#{type}-style string" do + source = <<~EOM + node.is_a?(Op) && %#{type}(| ||).include?(node.value) && + EOM + + explain = ExplainSyntax.new( + code_lines: CodeLine.from_source(source) + ).call + + expect(explain.missing).to eq([]) + end + end + it "doesn't falsely identify strings or symbols as critical chars" do source = <<~EOM a = ['(', '{', '[', '|'] diff --git a/spec/syntax_suggest/unit/lex_all_spec.rb b/spec/syntax_suggest/unit/lex_all_spec.rb deleted file mode 100644 index 9621c9ecec..0000000000 --- a/spec/syntax_suggest/unit/lex_all_spec.rb +++ /dev/null @@ -1,26 +0,0 @@ -# frozen_string_literal: true - -require_relative "../spec_helper" - -module SyntaxSuggest - RSpec.describe "EndBlockParse" do - it "finds blocks based on `end` keyword" do - source = <<~EOM - describe "cat" # 1 - Cat.call do # 2 - end # 3 - end # 4 - # 5 - it "dog" do # 6 - Dog.call do # 7 - end # 8 - end # 9 - EOM - - lex = LexAll.new(source: source) - expect(lex.map(&:token).to_s).to include("dog") - expect(lex.first.line).to eq(1) - expect(lex.last.line).to eq(9) - end - end -end diff --git a/spec/syntax_suggest/unit/visitor_spec.rb b/spec/syntax_suggest/unit/visitor_spec.rb new file mode 100644 index 0000000000..94eefd1e95 --- /dev/null +++ b/spec/syntax_suggest/unit/visitor_spec.rb @@ -0,0 +1,119 @@ +# frozen_string_literal: true + +require_relative "../spec_helper" + +module SyntaxSuggest + RSpec.describe Visitor do + def visit(source) + ast, _tokens = Prism.parse_lex(source).value + visitor = Visitor.new + visitor.visit(ast) + visitor + end + + describe "#consecutive_lines" do + it "detects dot-leading multi-line chains" do + visitor = visit(<<~RUBY) + User + .where(name: "Earlopain") + .first + RUBY + + expect(visitor.consecutive_lines).to eq(Set[1, 2]) + end + + it "detects dot-trailing multi-line chains" do + visitor = visit(<<~RUBY) + User. + where(name: "Earlopain"). + first + RUBY + + expect(visitor.consecutive_lines).to eq(Set[1, 2]) + end + + it "handles chains separated by comments" do + visitor = visit(<<~RUBY) + User. + # comment + where(name: "Earlopain"). + # another comment + first + RUBY + + # The AST sees through comments — every line except + # the last is consecutive regardless of interleaved comments. + expect(visitor.consecutive_lines).to eq(Set[1, 2, 3, 4]) + end + + it "returns empty for single-line calls" do + visitor = visit(<<~RUBY) + User.where(name: "Earlopain").first + RUBY + + expect(visitor.consecutive_lines).to be_empty + end + + it "returns empty when there is no method chain" do + visitor = visit(<<~RUBY) + puts "hello" + puts "world" + RUBY + + expect(visitor.consecutive_lines).to be_empty + end + + it "handles deeply nested chains" do + visitor = visit(<<~RUBY) + User + .where(name: "Earlopain") + .order(:created_at) + .limit(10) + .first + RUBY + + expect(visitor.consecutive_lines).to eq(Set[1, 2, 3, 4]) + end + end + + describe "#endless_def_keyword_offsets" do + it "records the def location for endless methods" do + visitor = visit(<<~RUBY) + def square(x) = x * x + RUBY + + expect(visitor.endless_def_keyword_offsets).to eq(Set[0]) + end + + it "does not record regular method definitions" do + visitor = visit(<<~RUBY) + def square(x) + x * x + end + RUBY + + expect(visitor.endless_def_keyword_offsets).to be_empty + end + + it "records multiple endless methods" do + visitor = visit(<<~RUBY) + def square(x) = x * x + def double(x) = x * 2 + RUBY + + expect(visitor.endless_def_keyword_offsets).to eq(Set[0, 22]) + end + + it "distinguishes endless from regular in the same source" do + visitor = visit(<<~RUBY) + def square(x) = x * x + def cube(x) + x * x * x + end + RUBY + + expect(visitor.endless_def_keyword_offsets).to eq(Set[0]) + end + end + end +end |
