diff options
-rw-r--r-- | lib/syntax_suggest/core_ext.rb | 89 | ||||
-rw-r--r-- | spec/syntax_suggest/integration/ruby_command_line_spec.rb | 12 | ||||
-rw-r--r-- | spec/syntax_suggest/unit/api_spec.rb | 30 |
3 files changed, 79 insertions, 52 deletions
diff --git a/lib/syntax_suggest/core_ext.rb b/lib/syntax_suggest/core_ext.rb index 59e13a149a..aed93e129c 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,52 +20,49 @@ if SyntaxError.method_defined?(:detailed_message) 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 - - 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) - 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/syntax_suggest/integration/ruby_command_line_spec.rb b/spec/syntax_suggest/integration/ruby_command_line_spec.rb index 9e488df93a..5c7ee06d83 100644 --- a/spec/syntax_suggest/integration/ruby_command_line_spec.rb +++ b/spec/syntax_suggest/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/syntax_suggest/unit/api_spec.rb b/spec/syntax_suggest/unit/api_spec.rb index a89d9f6a33..079a91e46d 100644 --- a/spec/syntax_suggest/unit/api_spec.rb +++ b/spec/syntax_suggest/unit/api_spec.rb @@ -65,11 +65,20 @@ module SyntaxSuggest 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 - skip if error.respond_to?(:path) + error_klass = Class.new do + def path + fixtures_dir.join("this_project_extra_def.rb.txt") + end - require "syntax_suggest/core_ext" + 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) @@ -78,11 +87,20 @@ module SyntaxSuggest 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 - skip if error.respond_to?(:path) + error_klass = Class.new do + def path + fixtures_dir.join("this_project_extra_def.rb.txt") + end - require "syntax_suggest/core_ext" + 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 |