From 45342fa31923a5f187da11e252d8765a3d84103b Mon Sep 17 00:00:00 2001 From: schneems Date: Wed, 7 Dec 2022 11:20:59 -0600 Subject: [PATCH 1/2] Drop support for old 3.2.0 preview versions Ruby 3.2.0rc1 introduced SyntaxError#path in addition to the ability to monkey patch SyntaxError. That means all code inside of this syntax error is guaranteed to be on Ruby 3.2. As the contents of the preview release were unstable, we do not have to continue to support older preview releases that did not contain SyntaxError#path --- .github/workflows/ci.yml | 2 +- CHANGELOG.md | 3 ++- lib/syntax_suggest/core_ext.rb | 14 ++------------ 3 files changed, 5 insertions(+), 14 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index da2e2b7..4f145a5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,7 +29,7 @@ jobs: - 2.7 - '3.0' - 3.1 - - "3.2.0-preview1" + - "3.2.0-rc1" - head steps: - name: Checkout code diff --git a/CHANGELOG.md b/CHANGELOG.md index 884a1c1..bec1c3a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ ## HEAD (unreleased) -- Native support of `SyntaxError#path`, support 3.2.0-preview3 will be dropped with the release of 3.2.0-preview4 () +- Drop support or Ruby 3.2.0 preview, now that 3.2.0-rc1 is available (https://github.com/ruby/syntax_suggest/pull/165) +- Native support of `SyntaxError#path`, support 3.2.0-preview3 will be dropped with the release of 3.2.0-preview4 (https://github.com/ruby/syntax_suggest/pull/164) - Added dependabot for GitHub Actions (https://github.com/ruby/syntax_suggest/pull/160) ## 1.0.1 diff --git a/lib/syntax_suggest/core_ext.rb b/lib/syntax_suggest/core_ext.rb index 59e13a1..1f32da5 100644 --- a/lib/syntax_suggest/core_ext.rb +++ b/lib/syntax_suggest/core_ext.rb @@ -26,18 +26,8 @@ def detailed_message(highlight: true, syntax_suggest: true, **kwargs) message = super - file = if respond_to?(:path) - path - elsif highlight - # This branch will be removed when the next Ruby 3.2 preview is released with - # support for SyntaxError#path - SyntaxSuggest::PathnameFromMessage.new(super(highlight: false, **kwargs)).call.name - else - SyntaxSuggest::PathnameFromMessage.new(message).call.name - end - - if file - file = Pathname.new(file) + if path + file = Pathname.new(path) io = SyntaxSuggest::MiniStringIO.new SyntaxSuggest.call( From d6fc460eaebd13fdb98377445c9e75dfa3ff7eff Mon Sep 17 00:00:00 2001 From: schneems Date: Wed, 7 Dec 2022 16:17:31 -0600 Subject: [PATCH 2/2] Allow testing SyntaxError#detailed_message Since syntax_suggest is now integrated with Ruby as a default gem it is now a bit tougher to test. The default gem monkeypatches SyntaxError so when it's monkey patched again via the gem the result will be different. This change moves the module into a method that can be used to prepend any class for testing so we can assert behavior against our code in isolation while integration tests assert with the full stack. --- lib/syntax_suggest/core_ext.rb | 79 ++++++++++++---------- spec/integration/ruby_command_line_spec.rb | 12 +++- spec/unit/api_spec.rb | 30 ++++++-- 3 files changed, 81 insertions(+), 40 deletions(-) diff --git a/lib/syntax_suggest/core_ext.rb b/lib/syntax_suggest/core_ext.rb index 1f32da5..aed93e1 100644 --- a/lib/syntax_suggest/core_ext.rb +++ b/lib/syntax_suggest/core_ext.rb @@ -3,6 +3,10 @@ # Ruby 3.2+ has a cleaner way to hook into Ruby that doesn't use `require` if SyntaxError.method_defined?(:detailed_message) module SyntaxSuggest + # Mini String IO [Private] + # + # Acts like a StringIO with reduced API, but without having to require that + # class. class MiniStringIO def initialize(isatty: $stderr.isatty) @string = +"" @@ -16,42 +20,49 @@ def puts(value = $/, **) attr_reader :string end - end - - SyntaxError.prepend Module.new { - def detailed_message(highlight: true, syntax_suggest: true, **kwargs) - return super unless syntax_suggest - - require "syntax_suggest/api" unless defined?(SyntaxSuggest::DEFAULT_VALUE) - - message = super - - if path - file = Pathname.new(path) - io = SyntaxSuggest::MiniStringIO.new - - SyntaxSuggest.call( - io: io, - source: file.read, - filename: file, - terminal: highlight - ) - annotation = io.string - annotation + message - else - message - end - rescue => e - if ENV["SYNTAX_SUGGEST_DEBUG"] - $stderr.warn(e.message) - $stderr.warn(e.backtrace) - end - - # Ignore internal errors - message + # SyntaxSuggest.record_dir [Private] + # + # Used to monkeypatch SyntaxError via Module.prepend + def self.module_for_detailed_message + Module.new { + def detailed_message(highlight: true, syntax_suggest: true, **kwargs) + return super unless syntax_suggest + + require "syntax_suggest/api" unless defined?(SyntaxSuggest::DEFAULT_VALUE) + + message = super + + if path + file = Pathname.new(path) + io = SyntaxSuggest::MiniStringIO.new + + SyntaxSuggest.call( + io: io, + source: file.read, + filename: file, + terminal: highlight + ) + annotation = io.string + + annotation + message + else + message + end + rescue => e + if ENV["SYNTAX_SUGGEST_DEBUG"] + $stderr.warn(e.message) + $stderr.warn(e.backtrace) + end + + # Ignore internal errors + message + end + } end - } + end + + SyntaxError.prepend(SyntaxSuggest.module_for_detailed_message) else autoload :Pathname, "pathname" diff --git a/spec/integration/ruby_command_line_spec.rb b/spec/integration/ruby_command_line_spec.rb index 9e488df..5c7ee06 100644 --- a/spec/integration/ruby_command_line_spec.rb +++ b/spec/integration/ruby_command_line_spec.rb @@ -76,9 +76,17 @@ module SyntaxSuggest end end - it "annotates a syntax error in Ruby 3.2+ when require is not used" do - pending("Support for SyntaxError#detailed_message monkeypatch needed https://gist.github.com/schneems/09f45cc23b9a8c46e9af6acbb6e6840d?permalink_comment_id=4172585#gistcomment-4172585") + it "gem can be tested when executing on Ruby with default gem included" do + skip if ruby_core? + 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 + expect(out).to include(lib_dir.join("syntax_suggest").join("core_ext.rb").to_s).once + end + + it "annotates a syntax error in Ruby 3.2+ when require is not used" do skip if ruby_core? skip if Gem::Version.new(RUBY_VERSION) < Gem::Version.new("3.2") diff --git a/spec/unit/api_spec.rb b/spec/unit/api_spec.rb index 006e26f..079a91e 100644 --- a/spec/unit/api_spec.rb +++ b/spec/unit/api_spec.rb @@ -65,9 +65,20 @@ def fake_error.message it "respects highlight API" do skip if Gem::Version.new(RUBY_VERSION) < Gem::Version.new("3.2") - error = SyntaxError.new("#{fixtures_dir.join("this_project_extra_def.rb.txt")}:1 ") + core_ext_file = lib_dir.join("syntax_suggest").join("core_ext.rb") + require_relative core_ext_file - require "syntax_suggest/core_ext" + error_klass = Class.new do + def path + fixtures_dir.join("this_project_extra_def.rb.txt") + end + + def detailed_message(**kwargs) + "error" + end + end + error_klass.prepend(SyntaxSuggest.module_for_detailed_message) + error = error_klass.new expect(error.detailed_message(highlight: true)).to include(SyntaxSuggest::DisplayCodeWithLineNumbers::TERMINAL_HIGHLIGHT) expect(error.detailed_message(highlight: false)).to_not include(SyntaxSuggest::DisplayCodeWithLineNumbers::TERMINAL_HIGHLIGHT) @@ -76,9 +87,20 @@ def fake_error.message it "can be disabled via falsey kwarg" do skip if Gem::Version.new(RUBY_VERSION) < Gem::Version.new("3.2") - error = SyntaxError.new("#{fixtures_dir.join("this_project_extra_def.rb.txt")}:1 ") + core_ext_file = lib_dir.join("syntax_suggest").join("core_ext.rb") + require_relative core_ext_file - require "syntax_suggest/core_ext" + error_klass = Class.new do + def path + fixtures_dir.join("this_project_extra_def.rb.txt") + end + + def detailed_message(**kwargs) + "error" + end + end + error_klass.prepend(SyntaxSuggest.module_for_detailed_message) + error = error_klass.new expect(error.detailed_message(syntax_suggest: true)).to_not eq(error.detailed_message(syntax_suggest: false)) end