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 +4 -4
- data/.circleci/config.yml +25 -1
- data/CHANGELOG.md +15 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +4 -2
- data/README.md +10 -0
- data/dead_end.gemspec +5 -1
- data/lib/dead_end/api.rb +198 -0
- data/lib/dead_end/around_block_scan.rb +34 -10
- data/lib/dead_end/auto.rb +2 -31
- data/lib/dead_end/block_expand.rb +12 -13
- data/lib/dead_end/code_block.rb +12 -9
- data/lib/dead_end/code_frontier.rb +23 -28
- data/lib/dead_end/code_search.rb +14 -21
- data/lib/dead_end/core_ext.rb +35 -0
- data/lib/dead_end/lex_all.rb +4 -1
- data/lib/dead_end/lex_value.rb +9 -3
- data/lib/dead_end/parse_blocks_from_indent_line.rb +11 -6
- data/lib/dead_end/pathname_from_message.rb +1 -1
- data/lib/dead_end/priority_engulf_queue.rb +63 -0
- data/lib/dead_end/priority_queue.rb +105 -0
- data/lib/dead_end/unvisited_lines.rb +36 -0
- data/lib/dead_end/version.rb +1 -1
- data/lib/dead_end.rb +2 -162
- metadata +8 -4
- data/lib/dead_end/insertion_sort.rb +0 -46
data/lib/dead_end/code_search.rb
CHANGED
@@ -41,7 +41,13 @@ module DeadEnd
|
|
41
41
|
|
42
42
|
attr_reader :invalid_blocks, :record_dir, :code_lines
|
43
43
|
|
44
|
-
def initialize(source, record_dir:
|
44
|
+
def initialize(source, record_dir: DEFAULT_VALUE)
|
45
|
+
record_dir = if record_dir == DEFAULT_VALUE
|
46
|
+
ENV["DEAD_END_RECORD_DIR"] || ENV["DEBUG"] ? "tmp" : nil
|
47
|
+
else
|
48
|
+
record_dir
|
49
|
+
end
|
50
|
+
|
45
51
|
if record_dir
|
46
52
|
@record_dir = DeadEnd.record_dir(record_dir)
|
47
53
|
@write_count = 0
|
@@ -63,7 +69,7 @@ module DeadEnd
|
|
63
69
|
def record(block:, name: "record")
|
64
70
|
return unless @record_dir
|
65
71
|
@name_tick[name] += 1
|
66
|
-
filename = "#{@write_count += 1}-#{name}-#{@name_tick[name]}.txt"
|
72
|
+
filename = "#{@write_count += 1}-#{name}-#{@name_tick[name]}-(#{block.starts_at}__#{block.ends_at}).txt"
|
67
73
|
if ENV["DEBUG"]
|
68
74
|
puts "\n\n==== #{filename} ===="
|
69
75
|
puts "\n```#{block.starts_at}..#{block.ends_at}"
|
@@ -78,7 +84,7 @@ module DeadEnd
|
|
78
84
|
highlight_lines: block.lines
|
79
85
|
).call
|
80
86
|
|
81
|
-
f.write(document)
|
87
|
+
f.write(" Block lines: #{block.starts_at..block.ends_at} (#{name}) \n\n#{document}")
|
82
88
|
end
|
83
89
|
end
|
84
90
|
|
@@ -89,25 +95,13 @@ module DeadEnd
|
|
89
95
|
frontier << block
|
90
96
|
end
|
91
97
|
|
92
|
-
# Removes the block without putting it back in the frontier
|
93
|
-
def sweep(block:, name:)
|
94
|
-
record(block: block, name: name)
|
95
|
-
|
96
|
-
block.lines.each(&:mark_invisible)
|
97
|
-
frontier.register_indent_block(block)
|
98
|
-
end
|
99
|
-
|
100
98
|
# Parses the most indented lines into blocks that are marked
|
101
99
|
# and added to the frontier
|
102
|
-
def
|
100
|
+
def create_blocks_from_untracked_lines
|
103
101
|
max_indent = frontier.next_indent_line&.indent
|
104
102
|
|
105
103
|
while (line = frontier.next_indent_line) && (line.indent == max_indent)
|
106
|
-
|
107
104
|
@parse_blocks_from_indent_line.each_neighbor_block(frontier.next_indent_line) do |block|
|
108
|
-
record(block: block, name: "add")
|
109
|
-
|
110
|
-
block.mark_invisible if block.valid?
|
111
105
|
push(block, name: "add")
|
112
106
|
end
|
113
107
|
end
|
@@ -115,13 +109,12 @@ module DeadEnd
|
|
115
109
|
|
116
110
|
# Given an already existing block in the frontier, expand it to see
|
117
111
|
# if it contains our invalid syntax
|
118
|
-
def
|
112
|
+
def expand_existing
|
119
113
|
block = frontier.pop
|
120
114
|
return unless block
|
121
115
|
|
122
|
-
record(block: block, name: "
|
116
|
+
record(block: block, name: "before-expand")
|
123
117
|
|
124
|
-
# block = block.expand_until_next_boundry
|
125
118
|
block = @block_expand.call(block)
|
126
119
|
push(block, name: "expand")
|
127
120
|
end
|
@@ -132,9 +125,9 @@ module DeadEnd
|
|
132
125
|
@tick += 1
|
133
126
|
|
134
127
|
if frontier.expand?
|
135
|
-
|
128
|
+
expand_existing
|
136
129
|
else
|
137
|
-
|
130
|
+
create_blocks_from_untracked_lines
|
138
131
|
end
|
139
132
|
end
|
140
133
|
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Monkey patch kernel to ensure that all `require` calls call the same
|
4
|
+
# method
|
5
|
+
module Kernel
|
6
|
+
module_function
|
7
|
+
|
8
|
+
alias_method :dead_end_original_require, :require
|
9
|
+
alias_method :dead_end_original_require_relative, :require_relative
|
10
|
+
alias_method :dead_end_original_load, :load
|
11
|
+
|
12
|
+
def load(file, wrap = false)
|
13
|
+
dead_end_original_load(file)
|
14
|
+
rescue SyntaxError => e
|
15
|
+
DeadEnd.handle_error(e)
|
16
|
+
end
|
17
|
+
|
18
|
+
def require(file)
|
19
|
+
dead_end_original_require(file)
|
20
|
+
rescue SyntaxError => e
|
21
|
+
DeadEnd.handle_error(e)
|
22
|
+
end
|
23
|
+
|
24
|
+
def require_relative(file)
|
25
|
+
if Pathname.new(file).absolute?
|
26
|
+
dead_end_original_require file
|
27
|
+
else
|
28
|
+
relative_from = caller_locations(1..1).first
|
29
|
+
relative_from_path = relative_from.absolute_path || relative_from.path
|
30
|
+
dead_end_original_require File.expand_path("../#{file}", relative_from_path)
|
31
|
+
end
|
32
|
+
rescue SyntaxError => e
|
33
|
+
DeadEnd.handle_error(e)
|
34
|
+
end
|
35
|
+
end
|
data/lib/dead_end/lex_all.rb
CHANGED
@@ -25,7 +25,10 @@ module DeadEnd
|
|
25
25
|
lineno = @lex.last.pos.first + 1
|
26
26
|
end
|
27
27
|
|
28
|
-
|
28
|
+
last_lex = nil
|
29
|
+
@lex.map! { |elem|
|
30
|
+
last_lex = LexValue.new(elem.pos.first, elem.event, elem.tok, elem.state, last_lex)
|
31
|
+
}
|
29
32
|
end
|
30
33
|
|
31
34
|
def to_a
|
data/lib/dead_end/lex_value.rb
CHANGED
@@ -15,19 +15,21 @@ module DeadEnd
|
|
15
15
|
class LexValue
|
16
16
|
attr_reader :line, :type, :token, :state
|
17
17
|
|
18
|
-
def initialize(line, type, token, state)
|
18
|
+
def initialize(line, type, token, state, last_lex = nil)
|
19
19
|
@line = line
|
20
20
|
@type = type
|
21
21
|
@token = token
|
22
22
|
@state = state
|
23
23
|
|
24
|
-
set_kw_end
|
24
|
+
set_kw_end(last_lex)
|
25
25
|
end
|
26
26
|
|
27
|
-
private def set_kw_end
|
27
|
+
private def set_kw_end(last_lex)
|
28
28
|
@is_end = false
|
29
29
|
@is_kw = false
|
30
30
|
return if type != :on_kw
|
31
|
+
#
|
32
|
+
return if last_lex && last_lex.fname? # https://github.com/ruby/ruby/commit/776759e300e4659bb7468e2b97c8c2d4359a2953
|
31
33
|
|
32
34
|
case token
|
33
35
|
when "if", "unless", "while", "until"
|
@@ -41,6 +43,10 @@ module DeadEnd
|
|
41
43
|
end
|
42
44
|
end
|
43
45
|
|
46
|
+
def fname?
|
47
|
+
state.allbits?(Ripper::EXPR_FNAME)
|
48
|
+
end
|
49
|
+
|
44
50
|
def ignore_newline?
|
45
51
|
type == :on_ignored_nl
|
46
52
|
end
|
@@ -42,13 +42,18 @@ module DeadEnd
|
|
42
42
|
|
43
43
|
neighbors = scan.code_block.lines
|
44
44
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
45
|
+
block = CodeBlock.new(lines: neighbors)
|
46
|
+
if neighbors.length <= 2 || block.valid?
|
47
|
+
yield block
|
48
|
+
else
|
49
|
+
until neighbors.empty?
|
50
|
+
lines = [neighbors.pop]
|
51
|
+
while (block = CodeBlock.new(lines: lines)) && block.invalid? && neighbors.any?
|
52
|
+
lines.prepend neighbors.pop
|
53
|
+
end
|
50
54
|
|
51
|
-
|
55
|
+
yield block if block
|
56
|
+
end
|
52
57
|
end
|
53
58
|
end
|
54
59
|
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DeadEnd
|
4
|
+
# Keeps track of what elements are in the queue in
|
5
|
+
# priority and also ensures that when one element
|
6
|
+
# engulfs/covers/eats another that the larger element
|
7
|
+
# evicts the smaller element
|
8
|
+
class PriorityEngulfQueue
|
9
|
+
def initialize
|
10
|
+
@queue = PriorityQueue.new
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_a
|
14
|
+
@queue.to_a
|
15
|
+
end
|
16
|
+
|
17
|
+
def empty?
|
18
|
+
@queue.empty?
|
19
|
+
end
|
20
|
+
|
21
|
+
def length
|
22
|
+
@queue.length
|
23
|
+
end
|
24
|
+
|
25
|
+
def peek
|
26
|
+
@queue.peek
|
27
|
+
end
|
28
|
+
|
29
|
+
def pop
|
30
|
+
@queue.pop
|
31
|
+
end
|
32
|
+
|
33
|
+
def push(block)
|
34
|
+
prune_engulf(block)
|
35
|
+
@queue << block
|
36
|
+
flush_deleted
|
37
|
+
|
38
|
+
self
|
39
|
+
end
|
40
|
+
|
41
|
+
private def flush_deleted
|
42
|
+
while @queue&.peek&.deleted?
|
43
|
+
@queue.pop
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
private def prune_engulf(block)
|
48
|
+
# If we're about to pop off the same block, we can skip deleting
|
49
|
+
# things from the frontier this iteration since we'll get it
|
50
|
+
# on the next iteration
|
51
|
+
return if @queue.peek && (block <=> @queue.peek) == 1
|
52
|
+
|
53
|
+
if block.starts_at != block.ends_at # A block of size 1 cannot engulf another
|
54
|
+
@queue.to_a.each { |b|
|
55
|
+
if b.starts_at >= block.starts_at && b.ends_at <= block.ends_at
|
56
|
+
b.delete
|
57
|
+
true
|
58
|
+
end
|
59
|
+
}
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DeadEnd
|
4
|
+
# Holds elements in a priority heap on insert
|
5
|
+
#
|
6
|
+
# Instead of constantly calling `sort!`, put
|
7
|
+
# the element where it belongs the first time
|
8
|
+
# around
|
9
|
+
#
|
10
|
+
# Example:
|
11
|
+
#
|
12
|
+
# queue = PriorityQueue.new
|
13
|
+
# queue << 33
|
14
|
+
# queue << 44
|
15
|
+
# queue << 1
|
16
|
+
#
|
17
|
+
# puts queue.peek # => 44
|
18
|
+
#
|
19
|
+
class PriorityQueue
|
20
|
+
attr_reader :elements
|
21
|
+
|
22
|
+
def initialize
|
23
|
+
@elements = []
|
24
|
+
end
|
25
|
+
|
26
|
+
def <<(element)
|
27
|
+
@elements << element
|
28
|
+
bubble_up(last_index, element)
|
29
|
+
end
|
30
|
+
|
31
|
+
def pop
|
32
|
+
exchange(0, last_index)
|
33
|
+
max = @elements.pop
|
34
|
+
bubble_down(0)
|
35
|
+
max
|
36
|
+
end
|
37
|
+
|
38
|
+
def length
|
39
|
+
@elements.length
|
40
|
+
end
|
41
|
+
|
42
|
+
def empty?
|
43
|
+
@elements.empty?
|
44
|
+
end
|
45
|
+
|
46
|
+
def peek
|
47
|
+
@elements.first
|
48
|
+
end
|
49
|
+
|
50
|
+
def to_a
|
51
|
+
@elements
|
52
|
+
end
|
53
|
+
|
54
|
+
# Used for testing, extremely not performant
|
55
|
+
def sorted
|
56
|
+
out = []
|
57
|
+
elements = @elements.dup
|
58
|
+
while (element = pop)
|
59
|
+
out << element
|
60
|
+
end
|
61
|
+
@elements = elements
|
62
|
+
out.reverse
|
63
|
+
end
|
64
|
+
|
65
|
+
private def last_index
|
66
|
+
@elements.size - 1
|
67
|
+
end
|
68
|
+
|
69
|
+
private def bubble_up(index, element)
|
70
|
+
return if index <= 0
|
71
|
+
|
72
|
+
parent_index = (index - 1) / 2
|
73
|
+
parent = @elements[parent_index]
|
74
|
+
|
75
|
+
return if (parent <=> element) >= 0
|
76
|
+
|
77
|
+
exchange(index, parent_index)
|
78
|
+
bubble_up(parent_index, element)
|
79
|
+
end
|
80
|
+
|
81
|
+
private def bubble_down(index)
|
82
|
+
child_index = (index * 2) + 1
|
83
|
+
|
84
|
+
return if child_index > last_index
|
85
|
+
|
86
|
+
not_the_last_element = child_index < last_index
|
87
|
+
left_element = @elements[child_index]
|
88
|
+
right_element = @elements[child_index + 1]
|
89
|
+
|
90
|
+
child_index += 1 if not_the_last_element && (right_element <=> left_element) == 1
|
91
|
+
|
92
|
+
return if (@elements[index] <=> @elements[child_index]) >= 0
|
93
|
+
|
94
|
+
exchange(index, child_index)
|
95
|
+
bubble_down(child_index)
|
96
|
+
end
|
97
|
+
|
98
|
+
def exchange(source, target)
|
99
|
+
a = @elements[source]
|
100
|
+
b = @elements[target]
|
101
|
+
@elements[source] = b
|
102
|
+
@elements[target] = a
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DeadEnd
|
4
|
+
# Tracks which lines various code blocks have expanded to
|
5
|
+
# and which are still unexplored
|
6
|
+
class UnvisitedLines
|
7
|
+
def initialize(code_lines:)
|
8
|
+
@unvisited = code_lines.sort_by(&:indent_index)
|
9
|
+
@visited_lines = {}
|
10
|
+
@visited_lines.compare_by_identity
|
11
|
+
end
|
12
|
+
|
13
|
+
def empty?
|
14
|
+
@unvisited.empty?
|
15
|
+
end
|
16
|
+
|
17
|
+
def peek
|
18
|
+
@unvisited.last
|
19
|
+
end
|
20
|
+
|
21
|
+
def pop
|
22
|
+
@unvisited.pop
|
23
|
+
end
|
24
|
+
|
25
|
+
def visit_block(block)
|
26
|
+
block.lines.each do |line|
|
27
|
+
next if @visited_lines[line]
|
28
|
+
@visited_lines[line] = true
|
29
|
+
end
|
30
|
+
|
31
|
+
while @visited_lines[@unvisited.last]
|
32
|
+
@unvisited.pop
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
data/lib/dead_end/version.rb
CHANGED
data/lib/dead_end.rb
CHANGED
@@ -1,164 +1,4 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative "dead_end/
|
4
|
-
|
5
|
-
require "tmpdir"
|
6
|
-
require "stringio"
|
7
|
-
require "pathname"
|
8
|
-
require "ripper"
|
9
|
-
require "timeout"
|
10
|
-
|
11
|
-
module DeadEnd
|
12
|
-
# Used to indicate a default value that cannot
|
13
|
-
# be confused with another input
|
14
|
-
DEFAULT_VALUE = Object.new.freeze
|
15
|
-
|
16
|
-
class Error < StandardError; end
|
17
|
-
TIMEOUT_DEFAULT = ENV.fetch("DEAD_END_TIMEOUT", 1).to_i
|
18
|
-
|
19
|
-
def self.handle_error(e)
|
20
|
-
file = PathnameFromMessage.new(e.message).call.name
|
21
|
-
raise e unless file
|
22
|
-
|
23
|
-
$stderr.sync = true
|
24
|
-
|
25
|
-
call(
|
26
|
-
source: file.read,
|
27
|
-
filename: file
|
28
|
-
)
|
29
|
-
|
30
|
-
raise e
|
31
|
-
end
|
32
|
-
|
33
|
-
def self.record_dir(dir)
|
34
|
-
time = Time.now.strftime("%Y-%m-%d-%H-%M-%s-%N")
|
35
|
-
dir = Pathname(dir)
|
36
|
-
symlink = dir.join("last").tap { |path| path.delete if path.exist? }
|
37
|
-
dir.join(time).tap { |path|
|
38
|
-
path.mkpath
|
39
|
-
FileUtils.symlink(path.basename, symlink)
|
40
|
-
}
|
41
|
-
end
|
42
|
-
|
43
|
-
def self.call(source:, filename: DEFAULT_VALUE, terminal: DEFAULT_VALUE, record_dir: nil, timeout: TIMEOUT_DEFAULT, io: $stderr)
|
44
|
-
search = nil
|
45
|
-
filename = nil if filename == DEFAULT_VALUE
|
46
|
-
Timeout.timeout(timeout) do
|
47
|
-
record_dir ||= ENV["DEBUG"] ? "tmp" : nil
|
48
|
-
search = CodeSearch.new(source, record_dir: record_dir).call
|
49
|
-
end
|
50
|
-
|
51
|
-
blocks = search.invalid_blocks
|
52
|
-
DisplayInvalidBlocks.new(
|
53
|
-
io: io,
|
54
|
-
blocks: blocks,
|
55
|
-
filename: filename,
|
56
|
-
terminal: terminal,
|
57
|
-
code_lines: search.code_lines
|
58
|
-
).call
|
59
|
-
rescue Timeout::Error => e
|
60
|
-
io.puts "Search timed out DEAD_END_TIMEOUT=#{timeout}, run with DEBUG=1 for more info"
|
61
|
-
io.puts e.backtrace.first(3).join($/)
|
62
|
-
end
|
63
|
-
|
64
|
-
# Used for counting spaces
|
65
|
-
module SpaceCount
|
66
|
-
def self.indent(string)
|
67
|
-
string.split(/\S/).first&.length || 0
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
# This will tell you if the `code_lines` would be valid
|
72
|
-
# if you removed the `without_lines`. In short it's a
|
73
|
-
# way to detect if we've found the lines with syntax errors
|
74
|
-
# in our document yet.
|
75
|
-
#
|
76
|
-
# code_lines = [
|
77
|
-
# CodeLine.new(line: "def foo\n", index: 0)
|
78
|
-
# CodeLine.new(line: " def bar\n", index: 1)
|
79
|
-
# CodeLine.new(line: "end\n", index: 2)
|
80
|
-
# ]
|
81
|
-
#
|
82
|
-
# DeadEnd.valid_without?(
|
83
|
-
# without_lines: code_lines[1],
|
84
|
-
# code_lines: code_lines
|
85
|
-
# ) # => true
|
86
|
-
#
|
87
|
-
# DeadEnd.valid?(code_lines) # => false
|
88
|
-
def self.valid_without?(without_lines:, code_lines:)
|
89
|
-
lines = code_lines - Array(without_lines).flatten
|
90
|
-
|
91
|
-
if lines.empty?
|
92
|
-
true
|
93
|
-
else
|
94
|
-
valid?(lines)
|
95
|
-
end
|
96
|
-
end
|
97
|
-
|
98
|
-
def self.invalid?(source)
|
99
|
-
source = source.join if source.is_a?(Array)
|
100
|
-
source = source.to_s
|
101
|
-
|
102
|
-
Ripper.new(source).tap(&:parse).error?
|
103
|
-
end
|
104
|
-
|
105
|
-
# Returns truthy if a given input source is valid syntax
|
106
|
-
#
|
107
|
-
# DeadEnd.valid?(<<~EOM) # => true
|
108
|
-
# def foo
|
109
|
-
# end
|
110
|
-
# EOM
|
111
|
-
#
|
112
|
-
# DeadEnd.valid?(<<~EOM) # => false
|
113
|
-
# def foo
|
114
|
-
# def bar # Syntax error here
|
115
|
-
# end
|
116
|
-
# EOM
|
117
|
-
#
|
118
|
-
# You can also pass in an array of lines and they'll be
|
119
|
-
# joined before evaluating
|
120
|
-
#
|
121
|
-
# DeadEnd.valid?(
|
122
|
-
# [
|
123
|
-
# "def foo\n",
|
124
|
-
# "end\n"
|
125
|
-
# ]
|
126
|
-
# ) # => true
|
127
|
-
#
|
128
|
-
# DeadEnd.valid?(
|
129
|
-
# [
|
130
|
-
# "def foo\n",
|
131
|
-
# " def bar\n", # Syntax error here
|
132
|
-
# "end\n"
|
133
|
-
# ]
|
134
|
-
# ) # => false
|
135
|
-
#
|
136
|
-
# As an FYI the CodeLine class instances respond to `to_s`
|
137
|
-
# so passing a CodeLine in as an object or as an array
|
138
|
-
# will convert it to it's code representation.
|
139
|
-
def self.valid?(source)
|
140
|
-
!invalid?(source)
|
141
|
-
end
|
142
|
-
end
|
143
|
-
|
144
|
-
# Integration
|
145
|
-
require_relative "dead_end/cli"
|
146
|
-
require_relative "dead_end/auto"
|
147
|
-
|
148
|
-
# Core logic
|
149
|
-
require_relative "dead_end/code_search"
|
150
|
-
require_relative "dead_end/code_frontier"
|
151
|
-
require_relative "dead_end/explain_syntax"
|
152
|
-
require_relative "dead_end/clean_document"
|
153
|
-
|
154
|
-
# Helpers
|
155
|
-
require_relative "dead_end/lex_all"
|
156
|
-
require_relative "dead_end/code_line"
|
157
|
-
require_relative "dead_end/code_block"
|
158
|
-
require_relative "dead_end/block_expand"
|
159
|
-
require_relative "dead_end/ripper_errors"
|
160
|
-
require_relative "dead_end/insertion_sort"
|
161
|
-
require_relative "dead_end/around_block_scan"
|
162
|
-
require_relative "dead_end/pathname_from_message"
|
163
|
-
require_relative "dead_end/display_invalid_blocks"
|
164
|
-
require_relative "dead_end/parse_blocks_from_indent_line"
|
3
|
+
require_relative "dead_end/api"
|
4
|
+
require_relative "dead_end/core_ext"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dead_end
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.
|
4
|
+
version: 3.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- schneems
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-05-18 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: When you get an "unexpected end" in your syntax this gem helps you find
|
14
14
|
it
|
@@ -36,6 +36,7 @@ files:
|
|
36
36
|
- dead_end.gemspec
|
37
37
|
- exe/dead_end
|
38
38
|
- lib/dead_end.rb
|
39
|
+
- lib/dead_end/api.rb
|
39
40
|
- lib/dead_end/around_block_scan.rb
|
40
41
|
- lib/dead_end/auto.rb
|
41
42
|
- lib/dead_end/block_expand.rb
|
@@ -46,16 +47,19 @@ files:
|
|
46
47
|
- lib/dead_end/code_frontier.rb
|
47
48
|
- lib/dead_end/code_line.rb
|
48
49
|
- lib/dead_end/code_search.rb
|
50
|
+
- lib/dead_end/core_ext.rb
|
49
51
|
- lib/dead_end/display_code_with_line_numbers.rb
|
50
52
|
- lib/dead_end/display_invalid_blocks.rb
|
51
53
|
- lib/dead_end/explain_syntax.rb
|
52
|
-
- lib/dead_end/insertion_sort.rb
|
53
54
|
- lib/dead_end/left_right_lex_count.rb
|
54
55
|
- lib/dead_end/lex_all.rb
|
55
56
|
- lib/dead_end/lex_value.rb
|
56
57
|
- lib/dead_end/parse_blocks_from_indent_line.rb
|
57
58
|
- lib/dead_end/pathname_from_message.rb
|
59
|
+
- lib/dead_end/priority_engulf_queue.rb
|
60
|
+
- lib/dead_end/priority_queue.rb
|
58
61
|
- lib/dead_end/ripper_errors.rb
|
62
|
+
- lib/dead_end/unvisited_lines.rb
|
59
63
|
- lib/dead_end/version.rb
|
60
64
|
homepage: https://github.com/zombocom/dead_end.git
|
61
65
|
licenses:
|
@@ -78,7 +82,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
78
82
|
- !ruby/object:Gem::Version
|
79
83
|
version: '0'
|
80
84
|
requirements: []
|
81
|
-
rubygems_version: 3.
|
85
|
+
rubygems_version: 3.3.14
|
82
86
|
signing_key:
|
83
87
|
specification_version: 4
|
84
88
|
summary: Find syntax errors in your source in a snap
|
@@ -1,46 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module DeadEnd
|
4
|
-
# Sort elements on insert
|
5
|
-
#
|
6
|
-
# Instead of constantly calling `sort!`, put
|
7
|
-
# the element where it belongs the first time
|
8
|
-
# around
|
9
|
-
#
|
10
|
-
# Example:
|
11
|
-
#
|
12
|
-
# sorted = InsertionSort.new
|
13
|
-
# sorted << 33
|
14
|
-
# sorted << 44
|
15
|
-
# sorted << 1
|
16
|
-
# puts sorted.to_a
|
17
|
-
# # => [1, 44, 33]
|
18
|
-
#
|
19
|
-
class InsertionSort
|
20
|
-
def initialize
|
21
|
-
@array = []
|
22
|
-
end
|
23
|
-
|
24
|
-
def <<(value)
|
25
|
-
insert_in = @array.length
|
26
|
-
@array.each.with_index do |existing, index|
|
27
|
-
case value <=> existing
|
28
|
-
when -1
|
29
|
-
insert_in = index
|
30
|
-
break
|
31
|
-
when 0
|
32
|
-
insert_in = index
|
33
|
-
break
|
34
|
-
when 1
|
35
|
-
# Keep going
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
@array.insert(insert_in, value)
|
40
|
-
end
|
41
|
-
|
42
|
-
def to_a
|
43
|
-
@array
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|