dead_end 3.0.3 → 3.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 88cc140d421e1df16009993e7d7438054dad7a6b6fec740496e92c06c0bc2f6d
4
- data.tar.gz: ba736b1459f6c18aed62b566fa8e48ca6a4dd8379bc2dc2d99594c0daf136fb6
3
+ metadata.gz: f35aa207ae1ff3904d39f5c0ddca5b8a0f68ae157f5c0a999cd3f2f1530e461b
4
+ data.tar.gz: b08307eb152fc12b84493409bfd8a9b8f23905180ad5ed9168a58d152913bc6e
5
5
  SHA512:
6
- metadata.gz: 3cf585c9f073344d6259478c239fd45b77a5fb7fd1c37c18d3d2a7cf91323b00750cccb18d666935ea20ce2c209137216304429a26aac0bf26fec7aa5440ff14
7
- data.tar.gz: 8a818ac4001ce7ec01ae36c4c223f47c7686e111226718cf22e8e2ca9f00a0bb3f3ef6409ad703cb698a67dc62035225973f37ea5f527207c26e705acd4c8f33
6
+ metadata.gz: 02b4ec64732eea9dbb55594c130d2fade4838a855bf3366408a5846683c3d2866e21844c7d3a2797adc865e177e7edf3bffaad0b87f555627f75fcac3d2aa219
7
+ data.tar.gz: '029add7304c46c97a8a04b564f5798a2e5d43afe24472ae51268226e2dd5e71c56b9c48bf889232d4c3f2759b6edb7ea4c4f332688d6995ddaac0f7ec8ae5803'
data/.circleci/config.yml CHANGED
@@ -1,6 +1,6 @@
1
1
  version: 2.1
2
2
  orbs:
3
- ruby: circleci/ruby@1.1.2
3
+ ruby: circleci/ruby@1.2.0
4
4
  references:
5
5
  unit: &unit
6
6
  run:
@@ -45,6 +45,28 @@ jobs:
45
45
  - ruby/install-deps
46
46
  - <<: *unit
47
47
 
48
+ "ruby-3-1":
49
+ docker:
50
+ - image: 'cimg/base:stable'
51
+ steps:
52
+ - checkout
53
+ - ruby/install:
54
+ version: '3.1.2'
55
+ - run: ruby -v
56
+ - ruby/install-deps
57
+ - <<: *unit
58
+
59
+ "ruby-3-2":
60
+ docker:
61
+ - image: 'cimg/base:stable'
62
+ steps:
63
+ - checkout
64
+ - ruby/install:
65
+ version: '3.2.0-preview1'
66
+ - run: ruby -v
67
+ - ruby/install-deps
68
+ - <<: *unit
69
+
48
70
  "lint":
49
71
  docker:
50
72
  - image: circleci/ruby:3.0
@@ -61,4 +83,6 @@ workflows:
61
83
  - "ruby-2-6"
62
84
  - "ruby-2-7"
63
85
  - "ruby-3-0"
86
+ - "ruby-3-1"
87
+ - "ruby-3-2"
64
88
  - "lint"
data/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  ## HEAD (unreleased)
2
2
 
3
+ ## 3.1.2
4
+
5
+ - Fixed internal class AroundBlockScan, minor changes in outputs (https://github.com/zombocom/dead_end/pull/131)
6
+
7
+ ## 3.1.1
8
+
9
+ - Fix case where Ripper lexing identified incorrect code as a keyword (https://github.com/zombocom/dead_end/pull/122)
10
+
11
+ ## 3.1.0
12
+
13
+ - Add support for Ruby 3.1 by updating `require_relative` logic (https://github.com/zombocom/dead_end/pull/120)
14
+ - Requiring `dead_end/auto` is now deprecated please require `dead_end` instead (https://github.com/zombocom/dead_end/pull/119)
15
+ - Requiring `dead_end/api` now loads code without monkeypatching core extensions (https://github.com/zombocom/dead_end/pull/119)
16
+ - The interface `DeadEnd.handle_error` is declared public and stable (https://github.com/zombocom/dead_end/pull/119)
17
+
3
18
  ## 3.0.3
4
19
 
5
20
  - Expand explanations coming from additional Ripper errors (https://github.com/zombocom/dead_end/pull/117)
data/Gemfile CHANGED
@@ -10,3 +10,5 @@ gem "rspec", "~> 3.0"
10
10
  gem "stackprof"
11
11
  gem "standard"
12
12
  gem "ruby-prof"
13
+
14
+ gem "benchmark-ips"
data/Gemfile.lock CHANGED
@@ -1,12 +1,13 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- dead_end (3.0.3)
4
+ dead_end (3.1.2)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
8
8
  specs:
9
9
  ast (2.4.2)
10
+ benchmark-ips (2.9.2)
10
11
  diff-lcs (1.4.4)
11
12
  parallel (1.21.0)
12
13
  parser (3.0.2.0)
@@ -54,6 +55,7 @@ PLATFORMS
54
55
  ruby
55
56
 
56
57
  DEPENDENCIES
58
+ benchmark-ips
57
59
  dead_end!
58
60
  rake (~> 12.0)
59
61
  rspec (~> 3.0)
@@ -62,4 +64,4 @@ DEPENDENCIES
62
64
  standard
63
65
 
64
66
  BUNDLED WITH
65
- 2.2.30
67
+ 2.3.14
data/README.md CHANGED
@@ -166,6 +166,16 @@ Here's an example:
166
166
 
167
167
  ![](assets/syntax_search.gif)
168
168
 
169
+ ## Use internals
170
+
171
+ To use the `dead_end` gem without monkeypatching you can `require 'dead_end/api'`. This will allow you to load `dead_end` and use its internals without mutating `require`.
172
+
173
+ Stable internal interface(s):
174
+
175
+ - `DeadEnd.handle_error(e)`
176
+
177
+ Any other entrypoints are subject to change without warning. If you want to use an internal interface from `dead_end` not on this list, open an issue to explain your use case.
178
+
169
179
  ## Development
170
180
 
171
181
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
data/dead_end.gemspec CHANGED
@@ -1,6 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "lib/dead_end/version"
3
+ begin
4
+ require_relative "lib/dead_end/version"
5
+ rescue LoadError # Fallback to load version file in ruby core repository
6
+ require_relative "version"
7
+ end
4
8
 
5
9
  Gem::Specification.new do |spec|
6
10
  spec.name = "dead_end"
@@ -0,0 +1,198 @@
1
+ require_relative "version"
2
+
3
+ require "tmpdir"
4
+ require "stringio"
5
+ require "pathname"
6
+ require "ripper"
7
+ require "timeout"
8
+
9
+ module DeadEnd
10
+ # Used to indicate a default value that cannot
11
+ # be confused with another input.
12
+ DEFAULT_VALUE = Object.new.freeze
13
+
14
+ class Error < StandardError; end
15
+ TIMEOUT_DEFAULT = ENV.fetch("DEAD_END_TIMEOUT", 1).to_i
16
+
17
+ # DeadEnd.handle_error [Public]
18
+ #
19
+ # Takes a `SyntaxError`` exception, uses the
20
+ # error message to locate the file. Then the file
21
+ # will be analyzed to find the location of the syntax
22
+ # error and emit that location to stderr.
23
+ #
24
+ # Example:
25
+ #
26
+ # begin
27
+ # require 'bad_file'
28
+ # rescue => e
29
+ # DeadEnd.handle_error(e)
30
+ # end
31
+ #
32
+ # By default it will re-raise the exception unless
33
+ # `re_raise: false`. The message output location
34
+ # can be configured using the `io: $stderr` input.
35
+ #
36
+ # If a valid filename cannot be determined, the original
37
+ # exception will be re-raised (even with
38
+ # `re_raise: false`).
39
+ def self.handle_error(e, re_raise: true, io: $stderr)
40
+ unless e.is_a?(SyntaxError)
41
+ io.puts("DeadEnd: Must pass a SyntaxError, got: #{e.class}")
42
+ raise e
43
+ end
44
+
45
+ file = PathnameFromMessage.new(e.message, io: io).call.name
46
+ raise e unless file
47
+
48
+ io.sync = true
49
+
50
+ call(
51
+ io: io,
52
+ source: file.read,
53
+ filename: file
54
+ )
55
+
56
+ raise e if re_raise
57
+ end
58
+
59
+ # DeadEnd.call [Private]
60
+ #
61
+ # Main private interface
62
+ def self.call(source:, filename: DEFAULT_VALUE, terminal: DEFAULT_VALUE, record_dir: DEFAULT_VALUE, timeout: TIMEOUT_DEFAULT, io: $stderr)
63
+ search = nil
64
+ filename = nil if filename == DEFAULT_VALUE
65
+ Timeout.timeout(timeout) do
66
+ record_dir ||= ENV["DEBUG"] ? "tmp" : nil
67
+ search = CodeSearch.new(source, record_dir: record_dir).call
68
+ end
69
+
70
+ blocks = search.invalid_blocks
71
+ DisplayInvalidBlocks.new(
72
+ io: io,
73
+ blocks: blocks,
74
+ filename: filename,
75
+ terminal: terminal,
76
+ code_lines: search.code_lines
77
+ ).call
78
+ rescue Timeout::Error => e
79
+ io.puts "Search timed out DEAD_END_TIMEOUT=#{timeout}, run with DEBUG=1 for more info"
80
+ io.puts e.backtrace.first(3).join($/)
81
+ end
82
+
83
+ # DeadEnd.record_dir [Private]
84
+ #
85
+ # Used to generate a unique directory to record
86
+ # search steps for debugging
87
+ def self.record_dir(dir)
88
+ time = Time.now.strftime("%Y-%m-%d-%H-%M-%s-%N")
89
+ dir = Pathname(dir)
90
+ symlink = dir.join("last").tap { |path| path.delete if path.exist? }
91
+ dir.join(time).tap { |path|
92
+ path.mkpath
93
+ FileUtils.symlink(path.basename, symlink)
94
+ }
95
+ end
96
+
97
+ # DeadEnd.valid_without? [Private]
98
+ #
99
+ # This will tell you if the `code_lines` would be valid
100
+ # if you removed the `without_lines`. In short it's a
101
+ # way to detect if we've found the lines with syntax errors
102
+ # in our document yet.
103
+ #
104
+ # code_lines = [
105
+ # CodeLine.new(line: "def foo\n", index: 0)
106
+ # CodeLine.new(line: " def bar\n", index: 1)
107
+ # CodeLine.new(line: "end\n", index: 2)
108
+ # ]
109
+ #
110
+ # DeadEnd.valid_without?(
111
+ # without_lines: code_lines[1],
112
+ # code_lines: code_lines
113
+ # ) # => true
114
+ #
115
+ # DeadEnd.valid?(code_lines) # => false
116
+ def self.valid_without?(without_lines:, code_lines:)
117
+ lines = code_lines - Array(without_lines).flatten
118
+
119
+ if lines.empty?
120
+ true
121
+ else
122
+ valid?(lines)
123
+ end
124
+ end
125
+
126
+ # DeadEnd.invalid? [Private]
127
+ #
128
+ # Opposite of `DeadEnd.valid?`
129
+ def self.invalid?(source)
130
+ source = source.join if source.is_a?(Array)
131
+ source = source.to_s
132
+
133
+ Ripper.new(source).tap(&:parse).error?
134
+ end
135
+
136
+ # DeadEnd.valid? [Private]
137
+ #
138
+ # Returns truthy if a given input source is valid syntax
139
+ #
140
+ # DeadEnd.valid?(<<~EOM) # => true
141
+ # def foo
142
+ # end
143
+ # EOM
144
+ #
145
+ # DeadEnd.valid?(<<~EOM) # => false
146
+ # def foo
147
+ # def bar # Syntax error here
148
+ # end
149
+ # EOM
150
+ #
151
+ # You can also pass in an array of lines and they'll be
152
+ # joined before evaluating
153
+ #
154
+ # DeadEnd.valid?(
155
+ # [
156
+ # "def foo\n",
157
+ # "end\n"
158
+ # ]
159
+ # ) # => true
160
+ #
161
+ # DeadEnd.valid?(
162
+ # [
163
+ # "def foo\n",
164
+ # " def bar\n", # Syntax error here
165
+ # "end\n"
166
+ # ]
167
+ # ) # => false
168
+ #
169
+ # As an FYI the CodeLine class instances respond to `to_s`
170
+ # so passing a CodeLine in as an object or as an array
171
+ # will convert it to it's code representation.
172
+ def self.valid?(source)
173
+ !invalid?(source)
174
+ end
175
+ end
176
+
177
+ # Integration
178
+ require_relative "cli"
179
+
180
+ # Core logic
181
+ require_relative "code_search"
182
+ require_relative "code_frontier"
183
+ require_relative "explain_syntax"
184
+ require_relative "clean_document"
185
+
186
+ # Helpers
187
+ require_relative "lex_all"
188
+ require_relative "code_line"
189
+ require_relative "code_block"
190
+ require_relative "block_expand"
191
+ require_relative "ripper_errors"
192
+ require_relative "priority_queue"
193
+ require_relative "unvisited_lines"
194
+ require_relative "around_block_scan"
195
+ require_relative "priority_engulf_queue"
196
+ require_relative "pathname_from_message"
197
+ require_relative "display_invalid_blocks"
198
+ require_relative "parse_blocks_from_indent_line"
@@ -37,10 +37,20 @@ module DeadEnd
37
37
  @after_array = []
38
38
  @before_array = []
39
39
  @stop_after_kw = false
40
+
41
+ @skip_hidden = false
42
+ @skip_empty = false
40
43
  end
41
44
 
42
45
  def skip(name)
43
- @skip_array << name
46
+ case name
47
+ when :hidden?
48
+ @skip_hidden = true
49
+ when :empty?
50
+ @skip_empty = true
51
+ else
52
+ raise "Unsupported skip #{name}"
53
+ end
44
54
  self
45
55
  end
46
56
 
@@ -49,14 +59,15 @@ module DeadEnd
49
59
  self
50
60
  end
51
61
 
52
- def scan_while(&block)
62
+ def scan_while
53
63
  stop_next = false
54
64
 
55
65
  kw_count = 0
56
66
  end_count = 0
57
- @before_index = before_lines.reverse_each.take_while do |line|
67
+ index = before_lines.reverse_each.take_while do |line|
58
68
  next false if stop_next
59
- next true if @skip_array.detect { |meth| line.send(meth) }
69
+ next true if @skip_hidden && line.hidden?
70
+ next true if @skip_empty && line.empty?
60
71
 
61
72
  kw_count += 1 if line.is_kw?
62
73
  end_count += 1 if line.is_end?
@@ -64,15 +75,20 @@ module DeadEnd
64
75
  stop_next = true
65
76
  end
66
77
 
67
- block.call(line)
78
+ yield line
68
79
  end.last&.index
69
80
 
81
+ if index && index < before_index
82
+ @before_index = index
83
+ end
84
+
70
85
  stop_next = false
71
86
  kw_count = 0
72
87
  end_count = 0
73
- @after_index = after_lines.take_while do |line|
88
+ index = after_lines.take_while do |line|
74
89
  next false if stop_next
75
- next true if @skip_array.detect { |meth| line.send(meth) }
90
+ next true if @skip_hidden && line.hidden?
91
+ next true if @skip_empty && line.empty?
76
92
 
77
93
  kw_count += 1 if line.is_kw?
78
94
  end_count += 1 if line.is_end?
@@ -80,8 +96,12 @@ module DeadEnd
80
96
  stop_next = true
81
97
  end
82
98
 
83
- block.call(line)
99
+ yield line
84
100
  end.last&.index
101
+
102
+ if index && index > after_index
103
+ @after_index = index
104
+ end
85
105
  self
86
106
  end
87
107
 
@@ -178,7 +198,11 @@ module DeadEnd
178
198
  end
179
199
 
180
200
  def code_block
181
- CodeBlock.new(lines: @code_lines[before_index..after_index])
201
+ CodeBlock.new(lines: lines)
202
+ end
203
+
204
+ def lines
205
+ @code_lines[before_index..after_index]
182
206
  end
183
207
 
184
208
  def before_index
@@ -190,7 +214,7 @@ module DeadEnd
190
214
  end
191
215
 
192
216
  private def before_lines
193
- @code_lines[0...@orig_before_index] || []
217
+ @code_lines[0...before_index] || []
194
218
  end
195
219
 
196
220
  private def after_lines
data/lib/dead_end/auto.rb CHANGED
@@ -1,35 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "../dead_end"
4
+ require_relative "core_ext"
4
5
 
5
- # Monkey patch kernel to ensure that all `require` calls call the same
6
- # method
7
- module Kernel
8
- module_function
9
-
10
- alias_method :dead_end_original_require, :require
11
- alias_method :dead_end_original_require_relative, :require_relative
12
- alias_method :dead_end_original_load, :load
13
-
14
- def load(file, wrap = false)
15
- dead_end_original_load(file)
16
- rescue SyntaxError => e
17
- DeadEnd.handle_error(e)
18
- end
19
-
20
- def require(file)
21
- dead_end_original_require(file)
22
- rescue SyntaxError => e
23
- DeadEnd.handle_error(e)
24
- end
25
-
26
- def require_relative(file)
27
- if Pathname.new(file).absolute?
28
- dead_end_original_require file
29
- else
30
- dead_end_original_require File.expand_path("../#{file}", Kernel.caller_locations(1, 1)[0].absolute_path)
31
- end
32
- rescue SyntaxError => e
33
- DeadEnd.handle_error(e)
34
- end
35
- end
6
+ warn "Calling `require 'dead_end/auto'` is deprecated, please `require 'dead_end'` instead."
@@ -36,7 +36,7 @@ module DeadEnd
36
36
  end
37
37
 
38
38
  def call(block)
39
- if (next_block = expand_neighbors(block, grab_empty: true))
39
+ if (next_block = expand_neighbors(block))
40
40
  return next_block
41
41
  end
42
42
 
@@ -51,25 +51,24 @@ module DeadEnd
51
51
  .code_block
52
52
  end
53
53
 
54
- def expand_neighbors(block, grab_empty: true)
55
- scan = AroundBlockScan.new(code_lines: @code_lines, block: block)
54
+ def expand_neighbors(block)
55
+ expanded_lines = AroundBlockScan.new(code_lines: @code_lines, block: block)
56
56
  .skip(:hidden?)
57
57
  .stop_after_kw
58
58
  .scan_neighbors
59
+ .scan_while { |line| line.empty? } # Slurp up empties
60
+ .lines
59
61
 
60
- # Slurp up empties
61
- if grab_empty
62
- scan = AroundBlockScan.new(code_lines: @code_lines, block: scan.code_block)
63
- .scan_while { |line| line.empty? || line.hidden? }
64
- end
65
-
66
- new_block = scan.code_block
67
-
68
- if block.lines == new_block.lines
62
+ if block.lines == expanded_lines
69
63
  nil
70
64
  else
71
- new_block
65
+ CodeBlock.new(lines: expanded_lines)
72
66
  end
73
67
  end
68
+
69
+ # Managable rspec errors
70
+ def inspect
71
+ "#<DeadEnd::CodeBlock:0x0000123843lol >"
72
+ end
74
73
  end
75
74
  end
@@ -18,11 +18,22 @@ module DeadEnd
18
18
  #
19
19
  class CodeBlock
20
20
  UNSET = Object.new.freeze
21
- attr_reader :lines
21
+ attr_reader :lines, :starts_at, :ends_at
22
22
 
23
23
  def initialize(lines: [])
24
24
  @lines = Array(lines)
25
25
  @valid = UNSET
26
+ @deleted = false
27
+ @starts_at = @lines.first.number
28
+ @ends_at = @lines.last.number
29
+ end
30
+
31
+ def delete
32
+ @deleted = true
33
+ end
34
+
35
+ def deleted?
36
+ @deleted
26
37
  end
27
38
 
28
39
  def visible_lines
@@ -41,14 +52,6 @@ module DeadEnd
41
52
  @lines.all?(&:hidden?)
42
53
  end
43
54
 
44
- def starts_at
45
- @starts_at ||= @lines.first&.line_number
46
- end
47
-
48
- def ends_at
49
- @ends_at ||= @lines.last&.line_number
50
- end
51
-
52
55
  # This is used for frontier ordering, we are searching from
53
56
  # the largest indentation to the smallest. This allows us to
54
57
  # populate an array with multiple code blocks then call `sort!`
@@ -50,18 +50,16 @@ module DeadEnd
50
50
  # CodeFrontier#detect_invalid_blocks
51
51
  #
52
52
  class CodeFrontier
53
- def initialize(code_lines:)
53
+ def initialize(code_lines:, unvisited: UnvisitedLines.new(code_lines: code_lines))
54
54
  @code_lines = code_lines
55
- @frontier = InsertionSort.new
56
- @unvisited_lines = @code_lines.sort_by(&:indent_index)
57
- @visited_lines = {}
55
+ @unvisited = unvisited
56
+ @queue = PriorityEngulfQueue.new
58
57
 
59
- @has_run = false
60
58
  @check_next = true
61
59
  end
62
60
 
63
61
  def count
64
- @frontier.to_a.length
62
+ @queue.length
65
63
  end
66
64
 
67
65
  # Performance optimization
@@ -88,7 +86,7 @@ module DeadEnd
88
86
  # removed. By default it checks all blocks in present in
89
87
  # the frontier array, but can be used for arbitrary arrays
90
88
  # of codeblocks as well
91
- def holds_all_syntax_errors?(block_array = @frontier, can_cache: true)
89
+ def holds_all_syntax_errors?(block_array = @queue, can_cache: true)
92
90
  return false if can_cache && can_skip_check?
93
91
 
94
92
  without_lines = block_array.to_a.flat_map do |block|
@@ -103,23 +101,23 @@ module DeadEnd
103
101
 
104
102
  # Returns a code block with the largest indentation possible
105
103
  def pop
106
- @frontier.to_a.pop
104
+ @queue.pop
107
105
  end
108
106
 
109
107
  def next_indent_line
110
- @unvisited_lines.last
108
+ @unvisited.peek
111
109
  end
112
110
 
113
111
  def expand?
114
- return false if @frontier.to_a.empty?
115
- return true if @unvisited_lines.to_a.empty?
112
+ return false if @queue.empty?
113
+ return true if @unvisited.empty?
116
114
 
117
- frontier_indent = @frontier.to_a.last.current_indent
115
+ frontier_indent = @queue.peek.current_indent
118
116
  unvisited_indent = next_indent_line.indent
119
117
 
120
118
  if ENV["DEBUG"]
121
119
  puts "```"
122
- puts @frontier.to_a.last.to_s
120
+ puts @queue.peek.to_s
123
121
  puts "```"
124
122
  puts " @frontier indent: #{frontier_indent}"
125
123
  puts " @unvisited indent: #{unvisited_indent}"
@@ -129,33 +127,30 @@ module DeadEnd
129
127
  frontier_indent >= unvisited_indent
130
128
  end
131
129
 
130
+ # Keeps track of what lines have been added to blocks and which are not yet
131
+ # visited.
132
132
  def register_indent_block(block)
133
- block.lines.each do |line|
134
- next if @visited_lines[line]
135
- @visited_lines[line] = true
136
-
137
- index = @unvisited_lines.bsearch_index { |l| line.indent_index <=> l.indent_index }
138
- @unvisited_lines.delete_at(index)
139
- end
133
+ @unvisited.visit_block(block)
140
134
  self
141
135
  end
142
136
 
137
+ # When one element fully encapsulates another we remove the smaller
138
+ # block from the frontier. This prevents double expansions and all-around
139
+ # weird behavior. However this guarantee is quite expensive to maintain
140
+ def register_engulf_block(block)
141
+ end
142
+
143
143
  # Add a block to the frontier
144
144
  #
145
145
  # This method ensures the frontier always remains sorted (in indentation order)
146
146
  # and that each code block's lines are removed from the indentation hash so we
147
147
  # don't re-evaluate the same line multiple times.
148
148
  def <<(block)
149
- register_indent_block(block)
149
+ @unvisited.visit_block(block)
150
150
 
151
- # Make sure we don't double expand, if a code block fully engulfs another code block, keep the bigger one
152
- @frontier.to_a.reject! { |b|
153
- b.starts_at >= block.starts_at && b.ends_at <= block.ends_at
154
- }
151
+ @queue.push(block)
155
152
 
156
153
  @check_next = true if block.invalid?
157
- @frontier << block
158
- # @frontier.sort!
159
154
 
160
155
  self
161
156
  end
@@ -175,7 +170,7 @@ module DeadEnd
175
170
  # Given that we know our syntax error exists somewhere in our frontier, we want to find
176
171
  # the smallest possible set of blocks that contain all the syntax errors
177
172
  def detect_invalid_blocks
178
- self.class.combination(@frontier.to_a.select(&:invalid?)).detect do |block_array|
173
+ self.class.combination(@queue.to_a.select(&:invalid?)).detect do |block_array|
179
174
  holds_all_syntax_errors?(block_array, can_cache: false)
180
175
  end || []
181
176
  end