diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 61689a4..08ccbc6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,6 +24,7 @@ jobs: uses: ruby/actions/.github/workflows/ruby_versions.yml@master with: engine: cruby + min_version: 3.3 test: needs: ruby-versions @@ -33,7 +34,9 @@ jobs: matrix: ruby: ${{ fromJson(needs.ruby-versions.outputs.versions) }} prism_version: - - 1.2.0 # Shipped with Ruby 3.4 as default parser https://www.ruby-lang.org/en/news/2024/12/25/ruby-3-4-0-released/ + # See https://stdgems.org/prism for which ruby version shipped with which prism version + - 0.19.0 + - 1.2.0 - 1.8.0 - head env: @@ -52,22 +55,3 @@ jobs: - name: test run: bin/rake test continue-on-error: ${{ matrix.ruby == 'head' }} - - test-disable-prism: - needs: ruby-versions - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - ruby: ${{ fromJson(needs.ruby-versions.outputs.versions) }} - steps: - - name: Checkout code - uses: actions/checkout@v6 - - name: Set up Ruby - uses: ruby/setup-ruby@v1 - with: - ruby-version: ${{ matrix.ruby }} - bundler-cache: true - - name: test - run: SYNTAX_SUGGEST_DISABLE_PRISM=1 bin/rake test - continue-on-error: ${{ matrix.ruby == 'head' }} diff --git a/.standard.yml b/.standard.yml index 2547695..3a33af2 100644 --- a/.standard.yml +++ b/.standard.yml @@ -1 +1 @@ -ruby_version: 3.0.0 +ruby_version: 3.3.0 diff --git a/CHANGELOG.md b/CHANGELOG.md index dc1a0a9..6600c5b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ ## HEAD (unreleased) +- Changed: Changed: Minimum supported Ruby version is now 3.3. (https://github.com/ruby/syntax_suggest/pull/246) + ## 2.0.3 - Fix: Correctly identify trailing slashes when using Prism > 1.8.0. (https://github.com/ruby/syntax_suggest/pull/243) diff --git a/lib/syntax_suggest/api.rb b/lib/syntax_suggest/api.rb index 0f82d83..a86237f 100644 --- a/lib/syntax_suggest/api.rb +++ b/lib/syntax_suggest/api.rb @@ -7,26 +7,12 @@ require "pathname" require "timeout" +# Prism is the new parser, replacing Ripper +require "prism" # We need Ripper loaded for `Prism.lex_compat` even if we're using Prism # for lexing and parsing require "ripper" -# Prism is the new parser, replacing Ripper -# -# We need to "dual boot" both for now because syntax_suggest -# supports older rubies that do not ship with syntax suggest. -# -# We also need the ability to control loading of this library -# so we can test that both modes work correctly in CI. -if (value = ENV["SYNTAX_SUGGEST_DISABLE_PRISM"]) - warn "Skipping loading prism due to SYNTAX_SUGGEST_DISABLE_PRISM=#{value}" -else - begin - require "prism" - rescue LoadError - end -end - module SyntaxSuggest # Used to indicate a default value that cannot # be confused with another input. @@ -35,14 +21,6 @@ module SyntaxSuggest class Error < StandardError; end TIMEOUT_DEFAULT = ENV.fetch("SYNTAX_SUGGEST_TIMEOUT", 1).to_i - # SyntaxSuggest.use_prism_parser? [Private] - # - # Tells us if the prism parser is available for use - # or if we should fallback to `Ripper` - def self.use_prism_parser? - defined?(Prism) - end - # SyntaxSuggest.handle_error [Public] # # Takes a `SyntaxError` exception, uses the @@ -152,20 +130,11 @@ def self.valid_without?(without_lines:, code_lines:) # SyntaxSuggest.invalid? [Private] # # Opposite of `SyntaxSuggest.valid?` - if defined?(Prism) - def self.invalid?(source) - source = source.join if source.is_a?(Array) - source = source.to_s + def self.invalid?(source) + source = source.join if source.is_a?(Array) + source = source.to_s - Prism.parse(source).failure? - end - else - def self.invalid?(source) - source = source.join if source.is_a?(Array) - source = source.to_s - - Ripper.new(source).tap(&:parse).error? - end + Prism.parse(source).failure? end # SyntaxSuggest.valid? [Private] diff --git a/lib/syntax_suggest/explain_syntax.rb b/lib/syntax_suggest/explain_syntax.rb index 0d80c4d..5d5daa8 100644 --- a/lib/syntax_suggest/explain_syntax.rb +++ b/lib/syntax_suggest/explain_syntax.rb @@ -2,18 +2,10 @@ require_relative "left_right_lex_count" -if !SyntaxSuggest.use_prism_parser? - require_relative "ripper_errors" -end - module SyntaxSuggest class GetParseErrors def self.errors(source) - if SyntaxSuggest.use_prism_parser? - Prism.parse(source).errors.map(&:message) - else - RipperErrors.new(source).call.errors - end + Prism.parse(source).errors.map(&:message) end end diff --git a/lib/syntax_suggest/lex_all.rb b/lib/syntax_suggest/lex_all.rb index c16fbb5..c3b4024 100644 --- a/lib/syntax_suggest/lex_all.rb +++ b/lib/syntax_suggest/lex_all.rb @@ -1,13 +1,7 @@ # frozen_string_literal: true module SyntaxSuggest - # Ripper.lex is not guaranteed to lex the entire source document - # - # This class guarantees the whole document is lex-ed by iteratively - # lexing the document where ripper stopped. - # - # Prism likely doesn't have the same problem. Once ripper support is removed - # we can likely reduce the complexity here if not remove the whole concept. + # Lexes the whole source and wraps the tokens in `LexValue`. # # Example usage: # @@ -20,34 +14,14 @@ class LexAll def initialize(source:, source_lines: nil) @lex = self.class.lex(source, 1) - lineno = @lex.last[0][0] + 1 - source_lines ||= source.lines - last_lineno = source_lines.length - - until lineno >= last_lineno - lines = source_lines[lineno..] - - @lex.concat( - self.class.lex(lines.join, lineno + 1) - ) - - lineno = @lex.last[0].first + 1 - end - last_lex = nil @lex.map! { |elem| last_lex = LexValue.new(elem[0].first, elem[1], elem[2], elem[3], last_lex) } end - if SyntaxSuggest.use_prism_parser? - def self.lex(source, line_number) - Prism.lex_compat(source, line: line_number).value.sort_by { |values| values[0] } - end - else - def self.lex(source, line_number) - Ripper::Lexer.new(source, "-", line_number).parse.sort_by(&:pos) - end + def self.lex(source, line_number) + Prism.lex_compat(source, line: line_number).value.sort_by { |values| values[0] } end def to_a diff --git a/lib/syntax_suggest/ripper_errors.rb b/lib/syntax_suggest/ripper_errors.rb deleted file mode 100644 index 4e2bc90..0000000 --- a/lib/syntax_suggest/ripper_errors.rb +++ /dev/null @@ -1,39 +0,0 @@ -# frozen_string_literal: true - -module SyntaxSuggest - # Capture parse errors from Ripper - # - # Prism returns the errors with their messages, but Ripper - # does not. To get them we must make a custom subclass. - # - # Example: - # - # puts RipperErrors.new(" def foo").call.errors - # # => ["syntax error, unexpected end-of-input, expecting ';' or '\\n'"] - class RipperErrors < Ripper - attr_reader :errors - - # Comes from ripper, called - # on every parse error, msg - # is a string - def on_parse_error(msg) - @errors ||= [] - @errors << msg - end - - alias_method :on_alias_error, :on_parse_error - alias_method :on_assign_error, :on_parse_error - alias_method :on_class_name_error, :on_parse_error - alias_method :on_param_error, :on_parse_error - alias_method :compile_error, :on_parse_error - - def call - @run_once ||= begin - @errors = [] - parse - true - end - self - end - end -end diff --git a/spec/integration/ruby_command_line_spec.rb b/spec/integration/ruby_command_line_spec.rb index c1ec4be..02354ce 100644 --- a/spec/integration/ruby_command_line_spec.rb +++ b/spec/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/unit/api_spec.rb b/spec/unit/api_spec.rb index e900b9e..9299a17 100644 --- a/spec/unit/api_spec.rb +++ b/spec/unit/api_spec.rb @@ -8,12 +8,6 @@ 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 @@ def fake_error.message 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 @@ def detailed_message(**kwargs) 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/unit/code_line_spec.rb b/spec/unit/code_line_spec.rb index 5b62cc2..761c460 100644 --- a/spec/unit/code_line_spec.rb +++ b/spec/unit/code_line_spec.rb @@ -17,8 +17,6 @@ def to_json(*opts) 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 diff --git a/spec/unit/core_ext_spec.rb b/spec/unit/core_ext_spec.rb index 499c38a..d579cc8 100644 --- a/spec/unit/core_ext_spec.rb +++ b/spec/unit/core_ext_spec.rb @@ -3,8 +3,6 @@ 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/syntax_suggest.gemspec b/syntax_suggest.gemspec index 756a85b..44e458a 100644 --- a/syntax_suggest.gemspec +++ b/syntax_suggest.gemspec @@ -16,7 +16,7 @@ Gem::Specification.new do |spec| spec.description = 'When you get an "unexpected end" in your syntax this gem helps you find it' spec.homepage = "https://github.com/ruby/syntax_suggest.git" spec.license = "MIT" - spec.required_ruby_version = Gem::Requirement.new(">= 3.0.0") + spec.required_ruby_version = Gem::Requirement.new(">= 3.3.0") spec.metadata["homepage_uri"] = spec.homepage spec.metadata["source_code_uri"] = "https://github.com/ruby/syntax_suggest.git"