From 01cf0570bd5a93726a95f914fffe78faadc9fc79 Mon Sep 17 00:00:00 2001 From: schneems Date: Sat, 30 Oct 2021 13:42:22 -0500 Subject: [PATCH 01/10] Remove unused method --- lib/dead_end/display_invalid_blocks.rb | 5 ----- 1 file changed, 5 deletions(-) diff --git a/lib/dead_end/display_invalid_blocks.rb b/lib/dead_end/display_invalid_blocks.rb index c0b0959..4f4af45 100644 --- a/lib/dead_end/display_invalid_blocks.rb +++ b/lib/dead_end/display_invalid_blocks.rb @@ -36,11 +36,6 @@ def call self end - private def no_invalid_blocks - @io.puts <<~EOM - EOM - end - private def found_invalid_blocks @io.puts if banner From 1890135d4619c64cb3b87ee9dd952e667df97286 Mon Sep 17 00:00:00 2001 From: schneems Date: Sat, 30 Oct 2021 13:47:57 -0500 Subject: [PATCH 02/10] Refactor and remove DisplayInvalidBlocks#code_with_lines Make methods private --- lib/dead_end/code_search.rb | 11 ++++--- lib/dead_end/display_invalid_blocks.rb | 26 ++++----------- spec/unit/code_search_spec.rb | 40 +++++++++--------------- spec/unit/display_invalid_blocks_spec.rb | 40 +++++++++++++----------- 4 files changed, 48 insertions(+), 69 deletions(-) diff --git a/lib/dead_end/code_search.rb b/lib/dead_end/code_search.rb index 79818f6..400cf32 100644 --- a/lib/dead_end/code_search.rb +++ b/lib/dead_end/code_search.rb @@ -73,12 +73,13 @@ def record(block:, name: "record") puts " block indent: #{block.current_indent}" end @record_dir.join(filename).open(mode: "a") do |f| - display = DisplayInvalidBlocks.new( - blocks: block, + document = DisplayCodeWithLineNumbers.new( + lines: @code_lines.select(&:visible?), terminal: false, - code_lines: @code_lines - ) - f.write(display.indent(display.code_with_lines)) + highlight_lines: block.lines + ).call + + f.write(document) end end diff --git a/lib/dead_end/display_invalid_blocks.rb b/lib/dead_end/display_invalid_blocks.rb index 4f4af45..ab31143 100644 --- a/lib/dead_end/display_invalid_blocks.rb +++ b/lib/dead_end/display_invalid_blocks.rb @@ -28,15 +28,8 @@ def document_ok? end def call - if document_ok? - @io.puts "Syntax OK" - else - found_invalid_blocks - end - self - end + @io.puts "Syntax OK" and return if document_ok? - private def found_invalid_blocks @io.puts if banner @io.puts banner @@ -48,23 +41,24 @@ def call #{indent(code_block)} EOM + self end - def banner + private def banner Banner.new(invalid_obj: @invalid_obj).call end - def indent(string, with: " ") + private def indent(string, with: " ") string.each_line.map { |l| with + l }.join end - def code_block + private def code_block string = +"" string << code_with_context string end - def code_with_context + private def code_with_context lines = CaptureCodeContext.new( blocks: @blocks, code_lines: @code_lines @@ -76,13 +70,5 @@ def code_with_context highlight_lines: @invalid_lines ).call end - - def code_with_lines - DisplayCodeWithLineNumbers.new( - lines: @code_lines.select(&:visible?), - terminal: @terminal, - highlight_lines: @invalid_lines - ).call - end end end diff --git a/spec/unit/code_search_spec.rb b/spec/unit/code_search_spec.rb index 2341aed..4343adc 100644 --- a/spec/unit/code_search_spec.rb +++ b/spec/unit/code_search_spec.rb @@ -100,7 +100,7 @@ def hai search.call expect(search.record_dir.entries.map(&:to_s)).to include("1-add-1.txt") - expect(search.record_dir.join("1-add-1.txt").read).to eq(<<~EOM.indent(4)) + expect(search.record_dir.join("1-add-1.txt").read).to eq(<<~EOM) 1 class OH 2 def hello ❯ 3 def hai @@ -154,20 +154,15 @@ def hello it "finds hanging def in this project" do source_string = fixtures_dir.join("this_project_extra_def.rb.txt").read search = CodeSearch.new(source_string) - search.call - blocks = search.invalid_blocks - io = StringIO.new - display = DisplayInvalidBlocks.new( - code_lines: search.code_lines, - blocks: blocks, - io: io - ) - display.call - # puts io.string - - expect(display.code_with_lines.strip_control_codes).to include(<<~EOM) + document = DisplayCodeWithLineNumbers.new( + lines: search.code_lines.select(&:visible?), + terminal: false, + highlight_lines: search.invalid_blocks.flat_map(&:lines) + ).call + + expect(document).to include(<<~EOM) ❯ 36 def filename EOM end @@ -208,18 +203,13 @@ def hello EOM search.call - blocks = search.invalid_blocks - io = StringIO.new - display = DisplayInvalidBlocks.new( - io: io, - blocks: blocks, - code_lines: search.code_lines, - filename: "fake/spec/lol.rb" - ) - display.call - # io.string - - expect(display.code_with_lines).to include(<<~EOM) + document = DisplayCodeWithLineNumbers.new( + lines: search.code_lines.select(&:visible?), + terminal: false, + highlight_lines: search.invalid_blocks.flat_map(&:lines) + ).call + + expect(document).to include(<<~EOM) 1 require 'rails_helper' 2 3 RSpec.describe AclassNameHere, type: :worker do diff --git a/spec/unit/display_invalid_blocks_spec.rb b/spec/unit/display_invalid_blocks_spec.rb index 96da06b..026afb7 100644 --- a/spec/unit/display_invalid_blocks_spec.rb +++ b/spec/unit/display_invalid_blocks_spec.rb @@ -107,12 +107,14 @@ def hai code_lines = CleanDocument.new(source: source).call.lines block = CodeBlock.new(lines: code_lines[1]) - display = DisplayInvalidBlocks.new( + io = StringIO.new + DisplayInvalidBlocks.new( + io: io, blocks: block, terminal: false, code_lines: code_lines - ) - expect(display.code_block).to eq(<<~EOM) + ).call + expect(io.string).to include(<<~EOM.indent(4)) 1 class OH ❯ 2 def hello 4 def hai @@ -130,40 +132,40 @@ def hai end EOM + io = StringIO.new block = CodeBlock.new(lines: code_lines[1]) - display = DisplayInvalidBlocks.new( + DisplayInvalidBlocks.new( + io: io, blocks: block, terminal: false, code_lines: code_lines - ) + ).call - expect(display.code_with_lines).to eq( - [ - " 1 class OH", - "❯ 2 def hello", - " 3 def hai", - " 4 end", - " 5 end", + expect(io.string).to include([ + " 1 class OH", + " ❯ 2 def hello", + " 4 end", + " 5 end", "" - ].join($/) - ) + ].join($/)) block = CodeBlock.new(lines: code_lines[1]) - display = DisplayInvalidBlocks.new( + io = StringIO.new + DisplayInvalidBlocks.new( + io: io, blocks: block, terminal: true, code_lines: code_lines - ) + ).call - expect(display.code_with_lines).to eq( + expect(io.string).to include( [ " 1 class OH", ["❯ 2 ", DisplayCodeWithLineNumbers::TERMINAL_HIGHLIGHT, " def hello"].join, - " 3 def hai", " 4 end", " 5 end", "" - ].join($/ + DisplayCodeWithLineNumbers::TERMINAL_END) + ].join($/ + DisplayCodeWithLineNumbers::TERMINAL_END).indent(4) ) end end From 5b7fe5bcea00761ac2f8df883f21e48fd709470f Mon Sep 17 00:00:00 2001 From: schneems Date: Sat, 30 Oct 2021 14:42:29 -0500 Subject: [PATCH 03/10] Remove deprecated "fyi" interface --- lib/dead_end/fyi.rb | 8 -------- lib/dead_end/internals.rb | 13 +++++-------- 2 files changed, 5 insertions(+), 16 deletions(-) delete mode 100644 lib/dead_end/fyi.rb diff --git a/lib/dead_end/fyi.rb b/lib/dead_end/fyi.rb deleted file mode 100644 index 57b08da..0000000 --- a/lib/dead_end/fyi.rb +++ /dev/null @@ -1,8 +0,0 @@ -require_relative "../dead_end/internals" - -require_relative "auto" - -DeadEnd.send(:remove_const, :SEARCH_SOURCE_ON_ERROR_DEFAULT) -DeadEnd::SEARCH_SOURCE_ON_ERROR_DEFAULT = false - -warn "DEPRECATED: calling `require 'dead_end/fyi'` is deprecated, `require 'dead_end'` instead" diff --git a/lib/dead_end/internals.rb b/lib/dead_end/internals.rb index 9dc4a76..9277f9c 100644 --- a/lib/dead_end/internals.rb +++ b/lib/dead_end/internals.rb @@ -17,10 +17,9 @@ module DeadEnd DEFAULT_VALUE = Object.new.freeze class Error < StandardError; end - SEARCH_SOURCE_ON_ERROR_DEFAULT = true TIMEOUT_DEFAULT = ENV.fetch("DEAD_END_TIMEOUT", 1).to_i - def self.handle_error(e, search_source_on_error: SEARCH_SOURCE_ON_ERROR_DEFAULT) + def self.handle_error(e) raise e unless e.message.include?("end-of-input") filename = e.message.split(":").first @@ -28,12 +27,10 @@ def self.handle_error(e, search_source_on_error: SEARCH_SOURCE_ON_ERROR_DEFAULT) $stderr.sync = true warn "Run `$ dead_end #{filename}` for more options\n" - if search_source_on_error - call( - source: Pathname(filename).read, - filename: filename - ) - end + call( + source: Pathname(filename).read, + filename: filename + ) raise e end From 9e97d4d1a4f39f890f50b9344ef4c35e95f20bb7 Mon Sep 17 00:00:00 2001 From: schneems Date: Sat, 30 Oct 2021 14:50:51 -0500 Subject: [PATCH 04/10] Fire dead end on every syntax error --- lib/dead_end/internals.rb | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/lib/dead_end/internals.rb b/lib/dead_end/internals.rb index 9277f9c..59499aa 100644 --- a/lib/dead_end/internals.rb +++ b/lib/dead_end/internals.rb @@ -20,12 +20,8 @@ class Error < StandardError; end TIMEOUT_DEFAULT = ENV.fetch("DEAD_END_TIMEOUT", 1).to_i def self.handle_error(e) - raise e unless e.message.include?("end-of-input") - filename = e.message.split(":").first - $stderr.sync = true - warn "Run `$ dead_end #{filename}` for more options\n" call( source: Pathname(filename).read, @@ -44,12 +40,12 @@ def self.call(source:, filename:, terminal: DEFAULT_VALUE, record_dir: nil, time blocks = search.invalid_blocks DisplayInvalidBlocks.new( + io: io, blocks: blocks, filename: filename, terminal: terminal, code_lines: search.code_lines, invalid_obj: invalid_type(source), - io: io ).call rescue Timeout::Error => e io.puts "Search timed out DEAD_END_TIMEOUT=#{timeout}, run with DEBUG=1 for more info" From 3f49cab9e2e8aada7f4c187e812b7e0338ffc6d2 Mon Sep 17 00:00:00 2001 From: schneems Date: Sat, 30 Oct 2021 14:55:34 -0500 Subject: [PATCH 05/10] Display raw errors from Ruby's parser MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently we're trying to make sense of the errors by the whole document and delivering one "banner" stating the cause of the problem. This is based on the "who dis" ripper subclass which only captures one error. With this move, we're going to multi errors per output and not attempting to re-explain the existing ripper error. The main focus is showing the problem rather than explaining it. Example: ``` $ ./exe/dead_end spec/fixtures/rexe.rb.txt --> /Users/rschneeman/Documents/projects/dead_end/spec/fixtures/rexe.rb.txt syntax error, unexpected `def', expecting ')' syntax error, unexpected `end', expecting end-of-input 16 class Rexe ❯ 40 class Options < Struct.new( ❯ 41 :input_filespec, ❯ 42 :input_format, ❯ 43 :input_mode, ❯ 44 :loads, ❯ 45 :output_format, ❯ 46 :output_format_tty, ❯ 47 :output_format_block, ❯ 48 :requires, ❯ 49 :log_format, ❯ 71 end 551 end syntax error, unexpected end-of-input, expecting `end' 16 class Rexe 40 class Options < Struct.new( 71 end ❯ 77 class Lookups ❯ 140 def format_requires ❯ 148 end 551 end ``` Eventually We can re-work this logic to attempt to do a better job at explaining errors based on lex values in each captured block rather than using regex values on the parser errors. --- lib/dead_end/display_invalid_blocks.rb | 54 ++++++++++--------- lib/dead_end/internals.rb | 2 +- lib/dead_end/ripper_errors.rb | 31 +++++++++++ spec/integration/exe_cli_spec.rb | 6 +-- .../improvement_regression_spec.rb | 6 +-- spec/integration/ruby_command_line_spec.rb | 38 ------------- spec/spec_helper.rb | 4 -- spec/unit/code_search_spec.rb | 11 ++-- spec/unit/display_invalid_blocks_spec.rb | 14 ++--- 9 files changed, 78 insertions(+), 88 deletions(-) create mode 100644 lib/dead_end/ripper_errors.rb diff --git a/lib/dead_end/display_invalid_blocks.rb b/lib/dead_end/display_invalid_blocks.rb index ab31143..63369be 100644 --- a/lib/dead_end/display_invalid_blocks.rb +++ b/lib/dead_end/display_invalid_blocks.rb @@ -10,52 +10,56 @@ class DisplayInvalidBlocks attr_reader :filename def initialize(code_lines:, blocks:, io: $stderr, filename: nil, terminal: DEFAULT_VALUE, invalid_obj: WhoDisSyntaxError::Null.new) - @terminal = terminal == DEFAULT_VALUE ? io.isatty : terminal - - @filename = filename @io = io - @blocks = Array(blocks) - - @invalid_lines = @blocks.map(&:lines).flatten + @filename = filename @code_lines = code_lines - @invalid_obj = invalid_obj + @terminal = terminal == DEFAULT_VALUE ? io.isatty : terminal end def document_ok? @blocks.none? { |b| !b.hidden? } end + def call - @io.puts "Syntax OK" and return if document_ok? + if document_ok? + @io.puts "Syntax OK" + return self + end + @io.puts("--> #{filename}") if filename @io.puts - if banner - @io.puts banner - @io.puts + @blocks.each do |block| + display_block(block) end - @io.puts("file: #{filename}") if filename - @io.puts <<~EOM - simplified: - #{indent(code_block)} - EOM self end - private def banner - Banner.new(invalid_obj: @invalid_obj).call - end + def display_block(block) + lines = CaptureCodeContext.new( + blocks: block, + code_lines: @code_lines + ).call + + document = DisplayCodeWithLineNumbers.new( + lines: lines, + terminal: @terminal, + highlight_lines: block.lines + ).call + + RipperErrors.new(block.lines.map(&:original).join).call.errors.each do |e| + @io.puts e + end + @io.puts - private def indent(string, with: " ") - string.each_line.map { |l| with + l }.join + @io.puts(document) end - private def code_block - string = +"" - string << code_with_context - string + private def banner + Banner.new(invalid_obj: @invalid_obj).call end private def code_with_context diff --git a/lib/dead_end/internals.rb b/lib/dead_end/internals.rb index 59499aa..94b3add 100644 --- a/lib/dead_end/internals.rb +++ b/lib/dead_end/internals.rb @@ -45,7 +45,6 @@ def self.call(source:, filename:, terminal: DEFAULT_VALUE, record_dir: nil, time filename: filename, terminal: terminal, code_lines: search.code_lines, - invalid_obj: invalid_type(source), ).call rescue Timeout::Error => e io.puts "Search timed out DEAD_END_TIMEOUT=#{timeout}, run with DEBUG=1 for more info" @@ -146,5 +145,6 @@ def self.invalid_type(source) require_relative "block_expand" require_relative "around_block_scan" require_relative "who_dis_syntax_error" +require_relative "ripper_errors" require_relative "display_invalid_blocks" require_relative "parse_blocks_from_indent_line" diff --git a/lib/dead_end/ripper_errors.rb b/lib/dead_end/ripper_errors.rb new file mode 100644 index 0000000..039b9cb --- /dev/null +++ b/lib/dead_end/ripper_errors.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +module DeadEnd + # Capture parse errors from ripper + # + # 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 + + def call + @run_once ||= begin + @errors = [] + parse + true + end + self + end + end +end diff --git a/spec/integration/exe_cli_spec.rb b/spec/integration/exe_cli_spec.rb index 7e26d8f..e2e9e0c 100644 --- a/spec/integration/exe_cli_spec.rb +++ b/spec/integration/exe_cli_spec.rb @@ -25,9 +25,7 @@ def exe(cmd) ruby_file = fixtures_dir.join("this_project_extra_def.rb.txt") out = exe(ruby_file) - expect(out.strip).to include("Missing `end` detected") expect(out.strip).to include("❯ 36 def filename") - expect($?.success?).to be_falsey end @@ -40,7 +38,7 @@ def exe(cmd) out = exe(file.path) - expect(out).to include(<<~EOM.indent(4)) + expect(out).to include(<<~EOM) 16 class Rexe ❯ 77 class Lookups ❯ 78 def input_modes @@ -57,7 +55,6 @@ def exe(cmd) ruby_file = fixtures_dir.join("this_project_extra_def.rb.txt") out = exe("#{ruby_file} --terminal") - expect(out.strip).to include("Missing `end` detected") expect(out.strip).to include("\e[0m❯ 36 \e[1;3m def filename") end @@ -72,7 +69,6 @@ def exe(cmd) out = exe("#{ruby_file} --record #{tmp_dir}") - expect(out.strip).to include("Unmatched `end` detected") expect(tmp_dir).to_not be_empty end end diff --git a/spec/integration/improvement_regression_spec.rb b/spec/integration/improvement_regression_spec.rb index 07423b5..fc7c796 100644 --- a/spec/integration/improvement_regression_spec.rb +++ b/spec/integration/improvement_regression_spec.rb @@ -14,7 +14,7 @@ module DeadEnd filename: "none" ) - expect(io.string).to include(<<~'EOM'.indent(4)) + expect(io.string).to include(<<~'EOM') 1 Rails.application.routes.draw do ❯ 113 namespace :admin do ❯ 116 match "/foobar(*path)", via: :all, to: redirect { |_params, req| @@ -33,7 +33,7 @@ module DeadEnd filename: "none" ) - expect(io.string).to include(<<~'EOM'.indent(4)) + expect(io.string).to include(<<~'EOM') 1 describe "webmock tests" do 22 it "body" do 27 query = Cutlass::FunctionQuery.new( @@ -55,7 +55,7 @@ module DeadEnd filename: "none" ) - expect(io.string).to include(<<~'EOM'.indent(4)) + expect(io.string).to include(<<~'EOM') 5 module DerailedBenchmarks 6 class RequireTree 7 REQUIRED_BY = {} diff --git a/spec/integration/ruby_command_line_spec.rb b/spec/integration/ruby_command_line_spec.rb index 429f017..e4bb52d 100644 --- a/spec/integration/ruby_command_line_spec.rb +++ b/spec/integration/ruby_command_line_spec.rb @@ -50,46 +50,8 @@ module DeadEnd require_relative "./script.rb" EOM - out = `ruby -I#{lib_dir} -rdead_end/auto #{require_rb} 2>&1` - - expect(out).to include("Unmatched `end` detected") - expect(out).to include("Run `$ dead_end") - expect($?.success?).to be_falsey - out = `ruby -I#{lib_dir} -rdead_end #{require_rb} 2>&1` - expect(out).to include("Unmatched `end` detected") - expect(out).to include("Run `$ dead_end") - expect($?.success?).to be_falsey - end - end - - it "detects require error and adds a message with fyi mode" do - Dir.mktmpdir do |dir| - @tmpdir = Pathname(dir) - @script = @tmpdir.join("script.rb") - @script.write <<~EOM - describe "things" do - it "blerg" do - end - - it "flerg" - end - - it "zlerg" do - end - end - EOM - - require_rb = @tmpdir.join("require.rb") - require_rb.write <<~EOM - require_relative "./script.rb" - EOM - - out = `ruby -I#{lib_dir} -rdead_end/fyi #{require_rb} 2>&1` - - expect(out).to_not include("This code has an unmatched") - expect(out).to include("Run `$ dead_end") expect($?.success?).to be_falsey end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index c63a84c..46cf1da 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -56,8 +56,4 @@ def indent(number) end end.join end - - def strip_control_codes - gsub(/\e\[[^\x40-\x7E]*[\x40-\x7E]/, "") - end end diff --git a/spec/unit/code_search_spec.rb b/spec/unit/code_search_spec.rb index 4343adc..f3d391a 100644 --- a/spec/unit/code_search_spec.rb +++ b/spec/unit/code_search_spec.rb @@ -42,8 +42,8 @@ class Foo EOM end - it "handles no spaces between blocks" do - search = CodeSearch.new(<<~'EOM') + it "handles no spaces between blocks and trailing slash" do + source = <<~'EOM' require "rails_helper" RSpec.describe Foo, type: :model do describe "#bar" do @@ -64,13 +64,14 @@ class Foo end EOM + search = CodeSearch.new(source) search.call expect(search.invalid_blocks.join.strip).to eq('it "returns true" do # <== HERE') end it "handles no spaces between blocks" do - search = CodeSearch.new(<<~EOM) + source = <<~EOM context "foo bar" do it "bars the foo" do travel_to DateTime.new(2020, 10, 1, 10, 0, 0) do @@ -81,13 +82,13 @@ class Foo it "should" do end EOM - + search = CodeSearch.new(source) search.call expect(search.invalid_blocks.join.strip).to eq('it "should" do') end - it "recording" do + it "records debugging steps to a directory" do Dir.mktmpdir do |dir| dir = Pathname(dir) search = CodeSearch.new(<<~EOM, record_dir: dir) diff --git a/spec/unit/display_invalid_blocks_spec.rb b/spec/unit/display_invalid_blocks_spec.rb index 026afb7..45073d9 100644 --- a/spec/unit/display_invalid_blocks_spec.rb +++ b/spec/unit/display_invalid_blocks_spec.rb @@ -16,6 +16,7 @@ def hai search = CodeSearch.new(syntax_string) search.call + io = StringIO.new display = DisplayInvalidBlocks.new( io: io, @@ -92,7 +93,6 @@ def hai ) display.call expect(io.string).to include("❯ 2 def hello") - expect(io.string).to include("DeadEnd") end it " wraps code with github style codeblocks" do @@ -114,7 +114,7 @@ def hai terminal: false, code_lines: code_lines ).call - expect(io.string).to include(<<~EOM.indent(4)) + expect(io.string).to include(<<~EOM) 1 class OH ❯ 2 def hello 4 def hai @@ -142,10 +142,10 @@ def hai ).call expect(io.string).to include([ - " 1 class OH", - " ❯ 2 def hello", - " 4 end", - " 5 end", + " 1 class OH", + "❯ 2 def hello", + " 4 end", + " 5 end", "" ].join($/)) @@ -165,7 +165,7 @@ def hai " 4 end", " 5 end", "" - ].join($/ + DisplayCodeWithLineNumbers::TERMINAL_END).indent(4) + ].join($/ + DisplayCodeWithLineNumbers::TERMINAL_END) ) end end From 64c724ae523ef7aceb69b3948bdb848917bb0215 Mon Sep 17 00:00:00 2001 From: schneems Date: Sat, 30 Oct 2021 20:28:41 -0500 Subject: [PATCH 06/10] Move `dead_end/internals` to `dead_end` This file was created so that there could be an "fyi" interface that told people to run the CLI command, as well as a "auto" interface that fired the search functionality. I did this for safety reasons originally. Since then, the reliability has increased. We have deprecated and then removed the `dead_end/fyi` interface and can now also get rid of this internals file. That allows us to have the core functionality where you would expect it. --- lib/dead_end.rb | 147 ++++++++++++++++++++++++++++++++++++- lib/dead_end/auto.rb | 2 +- lib/dead_end/internals.rb | 150 -------------------------------------- spec/spec_helper.rb | 2 +- 4 files changed, 148 insertions(+), 153 deletions(-) delete mode 100644 lib/dead_end/internals.rb diff --git a/lib/dead_end.rb b/lib/dead_end.rb index cd55c53..96b56a7 100644 --- a/lib/dead_end.rb +++ b/lib/dead_end.rb @@ -1,4 +1,149 @@ # frozen_string_literal: true -require_relative "dead_end/internals" +require_relative "dead_end/version" + +require "tmpdir" +require "stringio" +require "pathname" +require "ripper" +require "timeout" + +module DeadEnd + # Used to indicate a default value that cannot + # be confused with another input + DEFAULT_VALUE = Object.new.freeze + + class Error < StandardError; end + TIMEOUT_DEFAULT = ENV.fetch("DEAD_END_TIMEOUT", 1).to_i + + def self.handle_error(e) + filename = e.message.split(":").first + $stderr.sync = true + + call( + source: Pathname(filename).read, + filename: filename + ) + + raise e + end + + def self.call(source:, filename:, terminal: DEFAULT_VALUE, record_dir: nil, timeout: TIMEOUT_DEFAULT, io: $stderr) + search = nil + Timeout.timeout(timeout) do + record_dir ||= ENV["DEBUG"] ? "tmp" : nil + search = CodeSearch.new(source, record_dir: record_dir).call + end + + blocks = search.invalid_blocks + DisplayInvalidBlocks.new( + io: io, + blocks: blocks, + filename: filename, + terminal: terminal, + code_lines: search.code_lines, + ).call + rescue Timeout::Error => e + io.puts "Search timed out DEAD_END_TIMEOUT=#{timeout}, run with DEBUG=1 for more info" + io.puts e.backtrace.first(3).join($/) + end + + # Used for counting spaces + module SpaceCount + def self.indent(string) + string.split(/\S/).first&.length || 0 + end + end + + # This will tell you if the `code_lines` would be valid + # if you removed the `without_lines`. In short it's a + # way to detect if we've found the lines with syntax errors + # in our document yet. + # + # code_lines = [ + # CodeLine.new(line: "def foo\n", index: 0) + # CodeLine.new(line: " def bar\n", index: 1) + # CodeLine.new(line: "end\n", index: 2) + # ] + # + # DeadEnd.valid_without?( + # without_lines: code_lines[1], + # code_lines: code_lines + # ) # => true + # + # DeadEnd.valid?(code_lines) # => false + def self.valid_without?(without_lines:, code_lines:) + lines = code_lines - Array(without_lines).flatten + + if lines.empty? + true + else + valid?(lines) + end + end + + def self.invalid?(source) + source = source.join if source.is_a?(Array) + source = source.to_s + + Ripper.new(source).tap(&:parse).error? + end + + # Returns truthy if a given input source is valid syntax + # + # DeadEnd.valid?(<<~EOM) # => true + # def foo + # end + # EOM + # + # DeadEnd.valid?(<<~EOM) # => false + # def foo + # def bar # Syntax error here + # end + # EOM + # + # You can also pass in an array of lines and they'll be + # joined before evaluating + # + # DeadEnd.valid?( + # [ + # "def foo\n", + # "end\n" + # ] + # ) # => true + # + # DeadEnd.valid?( + # [ + # "def foo\n", + # " def bar\n", # Syntax error here + # "end\n" + # ] + # ) # => false + # + # As an FYI the CodeLine class instances respond to `to_s` + # so passing a CodeLine in as an object or as an array + # will convert it to it's code representation. + def self.valid?(source) + !invalid?(source) + end + + def self.invalid_type(source) + WhoDisSyntaxError.new(source).call + end +end + +require_relative "dead_end/code_line" +require_relative "dead_end/code_block" +require_relative "dead_end/code_search" +require_relative "dead_end/code_frontier" +require_relative "dead_end/clean_document" + +require_relative "dead_end/lex_all" +require_relative "dead_end/block_expand" +require_relative "dead_end/around_block_scan" +require_relative "dead_end/who_dis_syntax_error" +require_relative "dead_end/ripper_errors" +require_relative "dead_end/display_invalid_blocks" +require_relative "dead_end/parse_blocks_from_indent_line" + require_relative "dead_end/auto" diff --git a/lib/dead_end/auto.rb b/lib/dead_end/auto.rb index 00c113c..c99059b 100644 --- a/lib/dead_end/auto.rb +++ b/lib/dead_end/auto.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "../dead_end/internals" +require_relative "../dead_end" # Monkey patch kernel to ensure that all `require` calls call the same # method diff --git a/lib/dead_end/internals.rb b/lib/dead_end/internals.rb deleted file mode 100644 index 94b3add..0000000 --- a/lib/dead_end/internals.rb +++ /dev/null @@ -1,150 +0,0 @@ -# frozen_string_literal: true - -# This is the top level file, but is moved to `internals` -# so the top level require can instead enable the "automatic" behavior - -require_relative "version" - -require "tmpdir" -require "stringio" -require "pathname" -require "ripper" -require "timeout" - -module DeadEnd - # Used to indicate a default value that cannot - # be confused with another input - DEFAULT_VALUE = Object.new.freeze - - class Error < StandardError; end - TIMEOUT_DEFAULT = ENV.fetch("DEAD_END_TIMEOUT", 1).to_i - - def self.handle_error(e) - filename = e.message.split(":").first - $stderr.sync = true - - call( - source: Pathname(filename).read, - filename: filename - ) - - raise e - end - - def self.call(source:, filename:, terminal: DEFAULT_VALUE, record_dir: nil, timeout: TIMEOUT_DEFAULT, io: $stderr) - search = nil - Timeout.timeout(timeout) do - record_dir ||= ENV["DEBUG"] ? "tmp" : nil - search = CodeSearch.new(source, record_dir: record_dir).call - end - - blocks = search.invalid_blocks - DisplayInvalidBlocks.new( - io: io, - blocks: blocks, - filename: filename, - terminal: terminal, - code_lines: search.code_lines, - ).call - rescue Timeout::Error => e - io.puts "Search timed out DEAD_END_TIMEOUT=#{timeout}, run with DEBUG=1 for more info" - io.puts e.backtrace.first(3).join($/) - end - - # Used for counting spaces - module SpaceCount - def self.indent(string) - string.split(/\S/).first&.length || 0 - end - end - - # This will tell you if the `code_lines` would be valid - # if you removed the `without_lines`. In short it's a - # way to detect if we've found the lines with syntax errors - # in our document yet. - # - # code_lines = [ - # CodeLine.new(line: "def foo\n", index: 0) - # CodeLine.new(line: " def bar\n", index: 1) - # CodeLine.new(line: "end\n", index: 2) - # ] - # - # DeadEnd.valid_without?( - # without_lines: code_lines[1], - # code_lines: code_lines - # ) # => true - # - # DeadEnd.valid?(code_lines) # => false - def self.valid_without?(without_lines:, code_lines:) - lines = code_lines - Array(without_lines).flatten - - if lines.empty? - true - else - valid?(lines) - end - end - - def self.invalid?(source) - source = source.join if source.is_a?(Array) - source = source.to_s - - Ripper.new(source).tap(&:parse).error? - end - - # Returns truthy if a given input source is valid syntax - # - # DeadEnd.valid?(<<~EOM) # => true - # def foo - # end - # EOM - # - # DeadEnd.valid?(<<~EOM) # => false - # def foo - # def bar # Syntax error here - # end - # EOM - # - # You can also pass in an array of lines and they'll be - # joined before evaluating - # - # DeadEnd.valid?( - # [ - # "def foo\n", - # "end\n" - # ] - # ) # => true - # - # DeadEnd.valid?( - # [ - # "def foo\n", - # " def bar\n", # Syntax error here - # "end\n" - # ] - # ) # => false - # - # As an FYI the CodeLine class instances respond to `to_s` - # so passing a CodeLine in as an object or as an array - # will convert it to it's code representation. - def self.valid?(source) - !invalid?(source) - end - - def self.invalid_type(source) - WhoDisSyntaxError.new(source).call - end -end - -require_relative "code_line" -require_relative "code_block" -require_relative "code_search" -require_relative "code_frontier" -require_relative "clean_document" - -require_relative "lex_all" -require_relative "block_expand" -require_relative "around_block_scan" -require_relative "who_dis_syntax_error" -require_relative "ripper_errors" -require_relative "display_invalid_blocks" -require_relative "parse_blocks_from_indent_line" diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 46cf1da..39f3b9e 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require "bundler/setup" -require "dead_end/internals" # Don't auto load code to +require "dead_end" require "tempfile" From aaec2c0d77950e1da022c7f9741a51a98a759bfc Mon Sep 17 00:00:00 2001 From: schneems Date: Sat, 30 Oct 2021 20:31:33 -0500 Subject: [PATCH 07/10] Remove WhoDisSyntaxError This class was used for detecting the syntax error type and emitting a "better" error than the parser. It made several incorrect assumptions, mainly that the document would only have a single syntax error. Over time this has proven to be an ineffective if not outright confusing annotation. With all the usage removed, these classes can also be removed. Ideally we will come back and add a better, more robust way to improve the errors, at which time we can revisit the banner code which will continue to live on his git history. --- lib/dead_end.rb | 5 - lib/dead_end/banner.rb | 58 ------------ lib/dead_end/display_invalid_blocks.rb | 3 +- lib/dead_end/who_dis_syntax_error.rb | 83 ---------------- spec/unit/banner_spec.rb | 122 ------------------------ spec/unit/who_dis_syntax_error_spec.rb | 126 ------------------------- 6 files changed, 1 insertion(+), 396 deletions(-) delete mode 100644 lib/dead_end/banner.rb delete mode 100644 lib/dead_end/who_dis_syntax_error.rb delete mode 100644 spec/unit/banner_spec.rb delete mode 100644 spec/unit/who_dis_syntax_error_spec.rb diff --git a/lib/dead_end.rb b/lib/dead_end.rb index 96b56a7..bffcd89 100644 --- a/lib/dead_end.rb +++ b/lib/dead_end.rb @@ -126,10 +126,6 @@ def self.invalid?(source) def self.valid?(source) !invalid?(source) end - - def self.invalid_type(source) - WhoDisSyntaxError.new(source).call - end end require_relative "dead_end/code_line" @@ -141,7 +137,6 @@ def self.invalid_type(source) require_relative "dead_end/lex_all" require_relative "dead_end/block_expand" require_relative "dead_end/around_block_scan" -require_relative "dead_end/who_dis_syntax_error" require_relative "dead_end/ripper_errors" require_relative "dead_end/display_invalid_blocks" require_relative "dead_end/parse_blocks_from_indent_line" diff --git a/lib/dead_end/banner.rb b/lib/dead_end/banner.rb deleted file mode 100644 index b04ad35..0000000 --- a/lib/dead_end/banner.rb +++ /dev/null @@ -1,58 +0,0 @@ -# frozen_string_literal: true - -module DeadEnd - class Banner - attr_reader :invalid_obj - - def initialize(invalid_obj:) - @invalid_obj = invalid_obj - end - - def call - case invalid_obj.error_symbol - when :missing_end - <<~EOM - DeadEnd: Missing `end` detected - - This code has a missing `end`. Ensure that all - syntax keywords (`def`, `do`, etc.) have a matching `end`. - EOM - when :unmatched_syntax - case unmatched_symbol - when :end - <<~EOM - DeadEnd: Unmatched `end` detected - - This code has an unmatched `end`. Ensure that all `end` lines - in your code have a matching syntax keyword (`def`, `do`, etc.) - and that you don't have any extra `end` lines. - EOM - when :| - <<~EOM - DeadEnd: Unmatched `|` character detected - - Example: - - `do |x` should be `do |x|` - EOM - when *WhoDisSyntaxError::CHARACTERS.keys - <<~EOM - DeadEnd: Unmatched `#{unmatched_symbol}` character detected - - It appears a `#{missing_character}` might be missing. - EOM - else - "DeadEnd: Unmatched `#{unmatched_symbol}` detected" - end - end - end - - private def unmatched_symbol - invalid_obj.unmatched_symbol - end - - private def missing_character - WhoDisSyntaxError::CHARACTERS[unmatched_symbol] - end - end -end diff --git a/lib/dead_end/display_invalid_blocks.rb b/lib/dead_end/display_invalid_blocks.rb index 63369be..0894f64 100644 --- a/lib/dead_end/display_invalid_blocks.rb +++ b/lib/dead_end/display_invalid_blocks.rb @@ -1,6 +1,5 @@ # frozen_string_literal: true -require_relative "banner" require_relative "capture_code_context" require_relative "display_code_with_line_numbers" @@ -9,7 +8,7 @@ module DeadEnd class DisplayInvalidBlocks attr_reader :filename - def initialize(code_lines:, blocks:, io: $stderr, filename: nil, terminal: DEFAULT_VALUE, invalid_obj: WhoDisSyntaxError::Null.new) + def initialize(code_lines:, blocks:, io: $stderr, filename: nil, terminal: DEFAULT_VALUE) @io = io @blocks = Array(blocks) @filename = filename diff --git a/lib/dead_end/who_dis_syntax_error.rb b/lib/dead_end/who_dis_syntax_error.rb deleted file mode 100644 index cd43c49..0000000 --- a/lib/dead_end/who_dis_syntax_error.rb +++ /dev/null @@ -1,83 +0,0 @@ -# frozen_string_literal: true - -module DeadEnd - # Determines what type of syntax error that is in the source - # - # Example: - # - # puts WhoDisSyntaxError.new("def foo;").call.error_symbol - # # => :missing_end - class WhoDisSyntaxError < Ripper - CHARACTERS = {"{": :"}", "}": :"{", "[": :"]", "]": :"[", "(": :")", ")": :"("} - class Null - def error_symbol - :missing_end - end - - def unmatched_symbol - :end - end - end - attr_reader :error, :run_once - - # Return options: - # - :missing_end - # - :unmatched_syntax - # - :unknown - def error_symbol - call - @error_symbol - end - - # Return options: - # - :end - # - :| - # - :} - # - :unknown - def unmatched_symbol - call - @unmatched_symbol - end - - def call - @run_once ||= begin - parse - true - end - self - end - - def on_parse_error(msg) - return if @error_symbol && @unmatched_symbol - - @error = msg - @unmatched_symbol = :unknown - - case @error - when /unexpected end-of-input/ - @error_symbol = :missing_end - when /expecting end-of-input/ - @unmatched_symbol = :end - @error_symbol = :unmatched_syntax - when /unexpected .* expecting ['`]?(?[^']*)/ - if $1 - character = $1.to_sym - @unmatched_symbol = CHARACTERS[character] || character - @unmatched_symbol = :end if @unmatched_symbol == :keyword_end - end - @error_symbol = :unmatched_syntax - when /unexpected '(?.*)'/ - @unmatched_symbol = $1.to_sym - @unmatched_symbol = :end if @unmatched_symbol == :keyword_end - @error_symbol = :unmatched_syntax - when /unexpected `end'/, # Ruby 2.7 and 3.0 - /unexpected end/, # Ruby 2.6 - /unexpected keyword_end/i # Ruby 2.5 - - @error_symbol = :unmatched_syntax - else - @error_symbol = :unknown - end - end - end -end diff --git a/spec/unit/banner_spec.rb b/spec/unit/banner_spec.rb deleted file mode 100644 index 86f86a5..0000000 --- a/spec/unit/banner_spec.rb +++ /dev/null @@ -1,122 +0,0 @@ -# frozen_string_literal: true - -require_relative "../spec_helper" - -module DeadEnd - RSpec.describe Banner do - it "Unmatched | banner" do - source = <<~EOM - Foo.call do | - end - EOM - - invalid_obj = WhoDisSyntaxError.new(source) - banner = Banner.new(invalid_obj: invalid_obj) - expect(banner.call).to include("Unmatched `|` character detected") - end - - it "Unmatched { banner" do - source = <<~EOM - class Cat - lol = { - end - EOM - - invalid_obj = WhoDisSyntaxError.new(source) - banner = Banner.new(invalid_obj: invalid_obj) - expect(banner.call).to include("Unmatched `{` character detected") - end - - it "Unmatched } banner" do - skip("Unsupported ruby version") unless Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("2.7") - - source = <<~EOM - def foo - lol = } - end - EOM - - invalid_obj = WhoDisSyntaxError.new(source) - banner = Banner.new(invalid_obj: invalid_obj) - expect(banner.call).to include("Unmatched `}` character detected") - end - - it "Unmatched [ banner" do - source = <<~EOM - class Cat - lol = [ - end - EOM - - invalid_obj = WhoDisSyntaxError.new(source) - banner = Banner.new(invalid_obj: invalid_obj) - expect(banner.call).to include("Unmatched `[` character detected") - end - - it "Unmatched ] banner" do - source = <<~EOM - def foo - lol = ] - end - EOM - - invalid_obj = WhoDisSyntaxError.new(source) - banner = Banner.new(invalid_obj: invalid_obj) - expect(banner.call).to include("Unmatched `]` character detected") - end - - it "Unmatched end banner" do - source = <<~EOM - class Cat - end - end - EOM - - invalid_obj = WhoDisSyntaxError.new(source) - banner = Banner.new(invalid_obj: invalid_obj) - expect(banner.call).to include("DeadEnd: Unmatched `end` detected") - end - - it "Unmatched unknown banner" do - source = <<~EOM - class Cat - def meow - 1 * - end - end - EOM - - invalid_obj = WhoDisSyntaxError.new(source) - banner = Banner.new(invalid_obj: invalid_obj) - expect(banner.call).to include("DeadEnd: Unmatched `unknown` detected") - end - - it "missing end banner" do - source = <<~EOM - class Cat - def meow - end - EOM - - invalid_obj = WhoDisSyntaxError.new(source) - banner = Banner.new(invalid_obj: invalid_obj) - expect(banner.call).to include("DeadEnd: Missing `end` detected") - end - - it "naked (closing) parenthesis" do - invalid_obj = WhoDisSyntaxError.new("def initialize; ); end").call - - expect( - Banner.new(invalid_obj: invalid_obj).call - ).to include("Unmatched `)` character detected") - end - - it "naked (opening) parenthesis" do - invalid_obj = WhoDisSyntaxError.new("def initialize; (; end").call - - expect( - Banner.new(invalid_obj: invalid_obj).call - ).to include("Unmatched `(` character detected") - end - end -end diff --git a/spec/unit/who_dis_syntax_error_spec.rb b/spec/unit/who_dis_syntax_error_spec.rb deleted file mode 100644 index cdfb41c..0000000 --- a/spec/unit/who_dis_syntax_error_spec.rb +++ /dev/null @@ -1,126 +0,0 @@ -# frozen_string_literal: true - -require_relative "../spec_helper" - -module DeadEnd - RSpec.describe WhoDisSyntaxError do - context "determines the type of syntax error to be an unmatched end" do - it "with missing or extra end's" do - expect( - WhoDisSyntaxError.new("def foo;").call.error_symbol - ).to eq(:missing_end) - - expect( - WhoDisSyntaxError.new("def foo; end; end").call.error_symbol - ).to eq(:unmatched_syntax) - - expect( - WhoDisSyntaxError.new("def foo; end; end").call.unmatched_symbol - ).to eq(:end) - end - - it "with unexpected rescue" do - source = <<~EOM - def foo - if bar - "baz" - else - "foo" - rescue FooBar - nil - end - EOM - - expect( - WhoDisSyntaxError.new(source).call.error_symbol - ).to eq(:unmatched_syntax) - - expect( - WhoDisSyntaxError.new(source).call.unmatched_symbol - ).to eq(:end) - end - end - - context "determines the type of syntax error to be an unmatched pipe" do - it "with unexpected 'end'" do - source = <<~EOM - class Blerg - Foo.call do |a - end # one - - puts lol - class Foo - end # two - end # three - EOM - - expect( - DeadEnd.invalid_type(source).error_symbol - ).to eq(:unmatched_syntax) - - expect( - DeadEnd.invalid_type(source).unmatched_symbol - ).to eq(:|) - end - - it "with unexpected local variable or method" do - source = <<~EOM - class Blerg - [].each do |a - puts a - end - end - EOM - - expect( - DeadEnd.invalid_type(source).error_symbol - ).to eq(:unmatched_syntax) - - expect( - DeadEnd.invalid_type(source).unmatched_symbol - ).to eq(:|) - end - end - - context "determines the type of syntax error to be an unmatched bracket" do - it "with missing bracket" do - source = <<~EOM - module Hey - class Foo - def initialize - [1,2,3 - end - - def call - end - end - end - EOM - - expect( - DeadEnd.invalid_type(source).error_symbol - ).to eq(:unmatched_syntax) - - expect( - DeadEnd.invalid_type(source).unmatched_symbol - ).to eq(:"[") - end - - it "with naked bracket" do - source = <<~EOM - def initialize - ] - end - EOM - - expect( - DeadEnd.invalid_type(source).error_symbol - ).to eq(:unmatched_syntax) - - expect( - DeadEnd.invalid_type(source).unmatched_symbol - ).to eq(:"]") - end - end - end -end From 9f5914f30f55afe004a0f73a60ec0f1ddfe791a0 Mon Sep 17 00:00:00 2001 From: schneems Date: Sat, 30 Oct 2021 20:35:33 -0500 Subject: [PATCH 08/10] Prefer private methods when possible --- lib/dead_end/display_invalid_blocks.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/dead_end/display_invalid_blocks.rb b/lib/dead_end/display_invalid_blocks.rb index 0894f64..c840f63 100644 --- a/lib/dead_end/display_invalid_blocks.rb +++ b/lib/dead_end/display_invalid_blocks.rb @@ -21,7 +21,6 @@ def document_ok? @blocks.none? { |b| !b.hidden? } end - def call if document_ok? @io.puts "Syntax OK" @@ -37,7 +36,7 @@ def call self end - def display_block(block) + private def display_block(block) lines = CaptureCodeContext.new( blocks: block, code_lines: @code_lines From 4f75fe1ccb535e816cb6767bdc48fccf60fa6626 Mon Sep 17 00:00:00 2001 From: schneems Date: Sat, 30 Oct 2021 20:42:28 -0500 Subject: [PATCH 09/10] Standardrb --fix --- lib/dead_end.rb | 2 +- lib/dead_end/ripper_errors.rb | 1 - spec/integration/exe_cli_spec.rb | 2 +- spec/integration/ruby_command_line_spec.rb | 2 +- spec/unit/display_invalid_blocks_spec.rb | 12 ++++++------ 5 files changed, 9 insertions(+), 10 deletions(-) diff --git a/lib/dead_end.rb b/lib/dead_end.rb index bffcd89..b30aa4a 100644 --- a/lib/dead_end.rb +++ b/lib/dead_end.rb @@ -41,7 +41,7 @@ def self.call(source:, filename:, terminal: DEFAULT_VALUE, record_dir: nil, time blocks: blocks, filename: filename, terminal: terminal, - code_lines: search.code_lines, + code_lines: search.code_lines ).call rescue Timeout::Error => e io.puts "Search timed out DEAD_END_TIMEOUT=#{timeout}, run with DEBUG=1 for more info" diff --git a/lib/dead_end/ripper_errors.rb b/lib/dead_end/ripper_errors.rb index 039b9cb..c8d7c42 100644 --- a/lib/dead_end/ripper_errors.rb +++ b/lib/dead_end/ripper_errors.rb @@ -8,7 +8,6 @@ module DeadEnd # 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 diff --git a/spec/integration/exe_cli_spec.rb b/spec/integration/exe_cli_spec.rb index e2e9e0c..a6a891f 100644 --- a/spec/integration/exe_cli_spec.rb +++ b/spec/integration/exe_cli_spec.rb @@ -67,7 +67,7 @@ def exe(cmd) expect(tmp_dir).to be_empty - out = exe("#{ruby_file} --record #{tmp_dir}") + exe("#{ruby_file} --record #{tmp_dir}") expect(tmp_dir).to_not be_empty end diff --git a/spec/integration/ruby_command_line_spec.rb b/spec/integration/ruby_command_line_spec.rb index e4bb52d..f79a048 100644 --- a/spec/integration/ruby_command_line_spec.rb +++ b/spec/integration/ruby_command_line_spec.rb @@ -50,7 +50,7 @@ module DeadEnd require_relative "./script.rb" EOM - out = `ruby -I#{lib_dir} -rdead_end #{require_rb} 2>&1` + `ruby -I#{lib_dir} -rdead_end #{require_rb} 2>&1` expect($?.success?).to be_falsey end diff --git a/spec/unit/display_invalid_blocks_spec.rb b/spec/unit/display_invalid_blocks_spec.rb index 45073d9..887f30e 100644 --- a/spec/unit/display_invalid_blocks_spec.rb +++ b/spec/unit/display_invalid_blocks_spec.rb @@ -142,12 +142,12 @@ def hai ).call expect(io.string).to include([ - " 1 class OH", - "❯ 2 def hello", - " 4 end", - " 5 end", - "" - ].join($/)) + " 1 class OH", + "❯ 2 def hello", + " 4 end", + " 5 end", + "" + ].join($/)) block = CodeBlock.new(lines: code_lines[1]) io = StringIO.new From 3ca6da82ae30bc79afeed9d9413209c7df39793a Mon Sep 17 00:00:00 2001 From: schneems Date: Sat, 30 Oct 2021 20:40:27 -0500 Subject: [PATCH 10/10] Update changelog --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6af0cff..78dc793 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ ## HEAD (unreleased) +- [Breaking] Remove previously deprecated `require "dead_end/fyi"` interface (https://github.com/zombocom/dead_end/pull/94) +- DeadEnd is now fired on EVERY syntax error (https://github.com/zombocom/dead_end/pull/94) +- Output format changes + - The "banner" is removed in favor of original parse error messages (https://github.com/zombocom/dead_end/pull/94) + - Parse errors emitted per-block rather than for the whole document (https://github.com/zombocom/dead_end/pull/94) + ## 2.0.2 - Don't print terminal color codes when output is not tty (https://github.com/zombocom/dead_end/pull/91)