From 9ae13c8d2c4c11358f667d8a048f158630a89d34 Mon Sep 17 00:00:00 2001 From: schneems Date: Fri, 12 Nov 2021 15:00:31 -0600 Subject: [PATCH] Fix windows compatibility Previously I made the assumption that the filename would be the first part of the error message for a syntax error before the colon `:` while this is false, it is especially false on windows where every path starts with a drive letter and a colon such as `c://hello/world.rb`. This commit fixes that problem by checking for the file on disk and appending more parts to the string. This is not foolproof because an error message might be possible that someone is using a colon with a file that actually exists in front of it like: ``` /lol/foo.rb:imactuallyadirectory/realfile.rb ``` In this case if `/lol/foo.rb` existed then it would quit looking. While I'm noting this edge case for completeness, I am not currently handling it. I think case that would be rare. When SyntaxError is refactored in Ruby 3.2 then we don't have to rely on parsing out filenames anymore. Close #111 --- CHANGELOG.md | 1 + lib/dead_end.rb | 27 ++++++++------ lib/dead_end/pathname_from_message.rb | 47 +++++++++++++++++++++++++ spec/unit/pathname_from_message_spec.rb | 35 ++++++++++++++++++ 4 files changed, 99 insertions(+), 11 deletions(-) create mode 100644 lib/dead_end/pathname_from_message.rb create mode 100644 spec/unit/pathname_from_message_spec.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index ea926a6..ddd0bd1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ ## HEAD (unreleased) +- Fix windows filename detection (https://github.com/zombocom/dead_end/pull/114) - Update links on readme and code of conduct (https://github.com/zombocom/dead_end/pull/107) ## 3.0.1 diff --git a/lib/dead_end.rb b/lib/dead_end.rb index 566e7d0..fcbcb8b 100644 --- a/lib/dead_end.rb +++ b/lib/dead_end.rb @@ -17,12 +17,14 @@ class Error < StandardError; end TIMEOUT_DEFAULT = ENV.fetch("DEAD_END_TIMEOUT", 1).to_i def self.handle_error(e) - filename = e.message.split(":").first + file = PathnameFromMessage.new(e.message).call.name + raise e unless file + $stderr.sync = true call( - source: Pathname(filename).read, - filename: filename + source: file.read, + filename: file ) raise e @@ -139,21 +141,24 @@ def self.valid?(source) end end -require_relative "dead_end/code_line" -require_relative "dead_end/code_block" +# Integration +require_relative "dead_end/cli" +require_relative "dead_end/auto" + +# Core logic require_relative "dead_end/code_search" require_relative "dead_end/code_frontier" +require_relative "dead_end/explain_syntax" require_relative "dead_end/clean_document" +# Helpers require_relative "dead_end/lex_all" +require_relative "dead_end/code_line" +require_relative "dead_end/code_block" require_relative "dead_end/block_expand" +require_relative "dead_end/ripper_errors" require_relative "dead_end/insertion_sort" require_relative "dead_end/around_block_scan" -require_relative "dead_end/ripper_errors" +require_relative "dead_end/pathname_from_message" require_relative "dead_end/display_invalid_blocks" require_relative "dead_end/parse_blocks_from_indent_line" - -require_relative "dead_end/explain_syntax" - -require_relative "dead_end/auto" -require_relative "dead_end/cli" diff --git a/lib/dead_end/pathname_from_message.rb b/lib/dead_end/pathname_from_message.rb new file mode 100644 index 0000000..e3141eb --- /dev/null +++ b/lib/dead_end/pathname_from_message.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +module DeadEnd + # Converts a SyntaxError message to a path + # + # Handles the case where the filename has a colon in it + # such as on a windows file system: https://github.com/zombocom/dead_end/issues/111 + # + # Example: + # + # message = "/tmp/scratch:2:in `require_relative': /private/tmp/bad.rb:1: syntax error, unexpected `end' (SyntaxError)" + # puts PathnameFromMessage.new(message).call.name + # # => "/tmp/scratch.rb" + # + class PathnameFromMessage + attr_reader :name + + def initialize(message, io: $stderr) + @line = message.lines.first + @parts = @line.split(":") + @guess = [] + @name = nil + @io = io + end + + def call + until stop? + @guess << @parts.shift + @name = Pathname(@guess.join(":")) + end + + if @parts.empty? + @io.puts "DeadEnd: could not find filename from #{@line.inspect}" + @name = nil + end + + self + end + + def stop? + return true if @parts.empty? + return false if @guess.empty? + + @name&.exist? + end + end +end diff --git a/spec/unit/pathname_from_message_spec.rb b/spec/unit/pathname_from_message_spec.rb new file mode 100644 index 0000000..e570aac --- /dev/null +++ b/spec/unit/pathname_from_message_spec.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +require_relative "../spec_helper" + +module DeadEnd + RSpec.describe "PathnameFromMessage" do + it "handles filenames with colons in them" do + Dir.mktmpdir do |dir| + dir = Pathname(dir) + + file = dir.join("scr:atch.rb").tap { |p| FileUtils.touch(p) } + + message = "#{file}:2:in `require_relative': /private/tmp/bad.rb:1: syntax error, unexpected `end' (SyntaxError)" + file = PathnameFromMessage.new(message).call.name + + expect(file).to be_truthy + end + end + + it "checks if the file exists" do + Dir.mktmpdir do |dir| + dir = Pathname(dir) + + file = dir.join("scratch.rb") + + message = "#{file}:2:in `require_relative': /private/tmp/bad.rb:1: syntax error, unexpected `end' (SyntaxError)" + io = StringIO.new + file = PathnameFromMessage.new(message, io: io).call.name + + expect(io.string).to include(file.to_s) + expect(file).to be_falsey + end + end + end +end