dead_end 1.0.1 → 1.0.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/CHANGELOG.md +5 -0
- data/Gemfile.lock +1 -1
- data/lib/dead_end/around_block_scan.rb +2 -2
- data/lib/dead_end/capture_code_context.rb +68 -14
- data/lib/dead_end/code_block.rb +4 -1
- data/lib/dead_end/code_frontier.rb +16 -29
- data/lib/dead_end/code_line.rb +13 -12
- data/lib/dead_end/code_search.rb +3 -3
- data/lib/dead_end/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 125719d74f88dcc5d196a1570508bd36d83d7ece6058b029a579da90a70cc099
|
4
|
+
data.tar.gz: 2eea6be22997101b46ff2820dfabaef4e473424a552e7f9c2835e43471c4431b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 674e35c603ec9ed2a8662e6ee3472381c6108a8734f455d39ef30472e38d13fd08a23cb107333f9ff8353ace614cb6c1f5bcb21e6aeb5384911ad1a61eb1ff58
|
7
|
+
data.tar.gz: 4ed5351764e96b5c95b2c4f68403bd81a3739163ad83d9e473829a1f6571f7e3614b855f56f21f5e8f15434817f7b7e8a947146044d4ac4c4f9314a15cd6b9bf
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,10 @@
|
|
1
1
|
## HEAD (unreleased)
|
2
2
|
|
3
|
+
## 1.0.2
|
4
|
+
|
5
|
+
- Fix bug where empty lines were interpreted to have a zero indentation (https://github.com/zombocom/dead_end/pull/39)
|
6
|
+
- Better results when missing "end" comes at the end of a capturing block (such as a class or module definition) (https://github.com/zombocom/dead_end/issues/32)
|
7
|
+
|
3
8
|
## 1.0.1
|
4
9
|
|
5
10
|
- Fix performance issue when evaluating multiple block combinations (https://github.com/zombocom/dead_end/pull/35)
|
data/Gemfile.lock
CHANGED
@@ -163,8 +163,8 @@ module DeadEnd
|
|
163
163
|
|
164
164
|
def scan_adjacent_indent
|
165
165
|
before_after_indent = []
|
166
|
-
before_after_indent << next_up&.indent || 0
|
167
|
-
before_after_indent << next_down&.indent || 0
|
166
|
+
before_after_indent << (next_up&.indent || 0)
|
167
|
+
before_after_indent << (next_down&.indent || 0)
|
168
168
|
|
169
169
|
indent = before_after_indent.min
|
170
170
|
self.scan_while {|line| line.not_empty? && line.indent >= indent }
|
@@ -35,20 +35,9 @@ module DeadEnd
|
|
35
35
|
|
36
36
|
def call
|
37
37
|
@blocks.each do |block|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
around_lines -= block.lines
|
43
|
-
|
44
|
-
@lines_to_output.concat(around_lines)
|
45
|
-
|
46
|
-
AroundBlockScan.new(
|
47
|
-
block: block,
|
48
|
-
code_lines: @code_lines,
|
49
|
-
).on_falling_indent do |line|
|
50
|
-
@lines_to_output << line
|
51
|
-
end
|
38
|
+
capture_last_end_same_indent(block)
|
39
|
+
capture_before_after_kws(block)
|
40
|
+
capture_falling_indent(block)
|
52
41
|
end
|
53
42
|
|
54
43
|
@lines_to_output.select!(&:not_empty?)
|
@@ -58,5 +47,70 @@ module DeadEnd
|
|
58
47
|
|
59
48
|
return @lines_to_output
|
60
49
|
end
|
50
|
+
|
51
|
+
def capture_falling_indent(block)
|
52
|
+
AroundBlockScan.new(
|
53
|
+
block: block,
|
54
|
+
code_lines: @code_lines,
|
55
|
+
).on_falling_indent do |line|
|
56
|
+
@lines_to_output << line
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def capture_before_after_kws(block)
|
61
|
+
around_lines = AroundBlockScan.new(code_lines: @code_lines, block: block)
|
62
|
+
.start_at_next_line
|
63
|
+
.capture_neighbor_context
|
64
|
+
|
65
|
+
around_lines -= block.lines
|
66
|
+
|
67
|
+
@lines_to_output.concat(around_lines)
|
68
|
+
end
|
69
|
+
|
70
|
+
# Problems heredocs are back in play
|
71
|
+
def capture_last_end_same_indent(block)
|
72
|
+
start_index = block.visible_lines.first.index
|
73
|
+
lines = @code_lines[start_index..block.lines.last.index]
|
74
|
+
kw_end_lines = lines.select {|line| line.indent == block.current_indent && (line.is_end? || line.is_kw?) }
|
75
|
+
|
76
|
+
|
77
|
+
# TODO handle case of heredocs showing up here
|
78
|
+
#
|
79
|
+
# Due to https://github.com/zombocom/dead_end/issues/32
|
80
|
+
# There's a special case where a keyword right before the last
|
81
|
+
# end of a valid block accidentally ends up identifying that the problem
|
82
|
+
# was with the block instead of before it. To handle that
|
83
|
+
# special case, we can re-parse back through the internals of blocks
|
84
|
+
# and if they have mis-matched keywords and ends show the last one
|
85
|
+
end_lines = kw_end_lines.select(&:is_end?)
|
86
|
+
end_lines.each_with_index do |end_line, i|
|
87
|
+
start_index = i.zero? ? 0 : end_lines[i-1].index
|
88
|
+
end_index = end_line.index - 1
|
89
|
+
lines = @code_lines[start_index..end_index]
|
90
|
+
|
91
|
+
stop_next = false
|
92
|
+
kw_count = 0
|
93
|
+
end_count = 0
|
94
|
+
lines = lines.reverse.take_while do |line|
|
95
|
+
next false if stop_next
|
96
|
+
|
97
|
+
end_count += 1 if line.is_end?
|
98
|
+
kw_count += 1 if line.is_kw?
|
99
|
+
|
100
|
+
stop_next = true if !kw_count.zero? && kw_count >= end_count
|
101
|
+
true
|
102
|
+
end.reverse
|
103
|
+
|
104
|
+
next unless kw_count > end_count
|
105
|
+
|
106
|
+
lines = lines.select {|line| line.is_kw? || line.is_end? }
|
107
|
+
|
108
|
+
next if lines.empty?
|
109
|
+
|
110
|
+
@lines_to_output << end_line
|
111
|
+
@lines_to_output << lines.first
|
112
|
+
@lines_to_output << lines.last
|
113
|
+
end
|
114
|
+
end
|
61
115
|
end
|
62
116
|
end
|
data/lib/dead_end/code_block.rb
CHANGED
@@ -17,10 +17,12 @@ module DeadEnd
|
|
17
17
|
#
|
18
18
|
#
|
19
19
|
class CodeBlock
|
20
|
+
UNSET = Object.new.freeze
|
20
21
|
attr_reader :lines
|
21
22
|
|
22
23
|
def initialize(lines: [])
|
23
24
|
@lines = Array(lines)
|
25
|
+
@valid = UNSET
|
24
26
|
end
|
25
27
|
|
26
28
|
def visible_lines
|
@@ -68,7 +70,8 @@ module DeadEnd
|
|
68
70
|
end
|
69
71
|
|
70
72
|
def valid?
|
71
|
-
|
73
|
+
return @valid if @valid != UNSET
|
74
|
+
@valid = DeadEnd.valid?(self.to_s)
|
72
75
|
end
|
73
76
|
|
74
77
|
def to_s
|
@@ -10,20 +10,20 @@ module DeadEnd
|
|
10
10
|
# sorted and then the frontier can be filtered. Large blocks that totally contain a
|
11
11
|
# smaller block will cause the smaller block to be evicted.
|
12
12
|
#
|
13
|
-
# CodeFrontier#<<
|
14
|
-
# CodeFrontier#pop
|
13
|
+
# CodeFrontier#<<(block) # Adds block to frontier
|
14
|
+
# CodeFrontier#pop # Removes block from frontier
|
15
15
|
#
|
16
16
|
# ## Knowing where we can go
|
17
17
|
#
|
18
|
-
# Internally it keeps track of
|
18
|
+
# Internally it keeps track of "unvisited" lines which is exposed via `next_indent_line`
|
19
19
|
# when called this will return a line of code with the most indentation.
|
20
20
|
#
|
21
|
-
# This line of code can be used to build a CodeBlock
|
22
|
-
# is added back to the frontier, then the lines
|
23
|
-
#
|
21
|
+
# This line of code can be used to build a CodeBlock and then when that code block
|
22
|
+
# is added back to the frontier, then the lines are removed from the
|
23
|
+
# "unvisited" so we don't double-create the same block.
|
24
24
|
#
|
25
|
-
# CodeFrontier#next_indent_line
|
26
|
-
# CodeFrontier#register_indent_block
|
25
|
+
# CodeFrontier#next_indent_line # Shows next line
|
26
|
+
# CodeFrontier#register_indent_block(block) # Removes lines from unvisited
|
27
27
|
#
|
28
28
|
# ## Knowing when to stop
|
29
29
|
#
|
@@ -42,13 +42,7 @@ module DeadEnd
|
|
42
42
|
def initialize(code_lines: )
|
43
43
|
@code_lines = code_lines
|
44
44
|
@frontier = []
|
45
|
-
@
|
46
|
-
code_lines.each do |line|
|
47
|
-
next if line.empty?
|
48
|
-
|
49
|
-
@indent_hash[line.indent] ||= []
|
50
|
-
@indent_hash[line.indent] << line
|
51
|
-
end
|
45
|
+
@unvisited_lines = @code_lines.sort_by(&:indent_index)
|
52
46
|
end
|
53
47
|
|
54
48
|
def count
|
@@ -75,38 +69,31 @@ module DeadEnd
|
|
75
69
|
return @frontier.pop
|
76
70
|
end
|
77
71
|
|
78
|
-
def indent_hash_indent
|
79
|
-
@indent_hash.keys.sort.last
|
80
|
-
end
|
81
|
-
|
82
72
|
def next_indent_line
|
83
|
-
|
84
|
-
@indent_hash[indent]&.first
|
73
|
+
@unvisited_lines.last
|
85
74
|
end
|
86
75
|
|
87
76
|
def expand?
|
88
77
|
return false if @frontier.empty?
|
89
|
-
return true if @
|
78
|
+
return true if @unvisited_lines.empty?
|
90
79
|
|
91
80
|
frontier_indent = @frontier.last.current_indent
|
92
|
-
|
81
|
+
unvisited_indent= next_indent_line.indent
|
93
82
|
|
94
83
|
if ENV["DEBUG"]
|
95
84
|
puts "```"
|
96
85
|
puts @frontier.last.to_s
|
97
86
|
puts "```"
|
98
87
|
puts " @frontier indent: #{frontier_indent}"
|
99
|
-
puts " @
|
88
|
+
puts " @unvisited indent: #{unvisited_indent}"
|
100
89
|
end
|
101
90
|
|
102
|
-
|
91
|
+
# Expand all blocks before moving to unvisited lines
|
92
|
+
frontier_indent >= unvisited_indent
|
103
93
|
end
|
104
94
|
|
105
95
|
def register_indent_block(block)
|
106
|
-
block.lines
|
107
|
-
@indent_hash[line.indent]&.delete(line)
|
108
|
-
end
|
109
|
-
@indent_hash.select! {|k, v| !v.empty?}
|
96
|
+
@unvisited_lines -= block.lines
|
110
97
|
self
|
111
98
|
end
|
112
99
|
|
data/lib/dead_end/code_line.rb
CHANGED
@@ -36,9 +36,14 @@ module DeadEnd
|
|
36
36
|
def initialize(line: , index:)
|
37
37
|
@original_line = line.freeze
|
38
38
|
@line = @original_line
|
39
|
-
|
39
|
+
if line.strip.empty?
|
40
|
+
@empty = true
|
41
|
+
@indent = 0
|
42
|
+
else
|
43
|
+
@empty = false
|
44
|
+
@indent = SpaceCount.indent(line)
|
45
|
+
end
|
40
46
|
@index = index
|
41
|
-
@indent = SpaceCount.indent(line)
|
42
47
|
@status = nil # valid, invalid, unknown
|
43
48
|
@invalid = false
|
44
49
|
|
@@ -60,9 +65,10 @@ module DeadEnd
|
|
60
65
|
end
|
61
66
|
end
|
62
67
|
|
68
|
+
@is_comment = lex.detect {|lex| lex.type != :on_sp}&.type == :on_comment
|
69
|
+
return if @is_comment
|
63
70
|
@is_kw = (kw_count - end_count) > 0
|
64
71
|
@is_end = (end_count - kw_count) > 0
|
65
|
-
@is_comment = lex.detect {|lex| lex.type != :on_sp}&.type == :on_comment
|
66
72
|
@is_trailing_slash = lex.last.token == TRAILING_SLASH
|
67
73
|
end
|
68
74
|
|
@@ -72,6 +78,10 @@ module DeadEnd
|
|
72
78
|
@is_trailing_slash
|
73
79
|
end
|
74
80
|
|
81
|
+
def indent_index
|
82
|
+
@indent_index ||= [indent, index]
|
83
|
+
end
|
84
|
+
|
75
85
|
def <=>(b)
|
76
86
|
self.index <=> b.index
|
77
87
|
end
|
@@ -92,15 +102,6 @@ module DeadEnd
|
|
92
102
|
@is_end
|
93
103
|
end
|
94
104
|
|
95
|
-
def mark_invalid
|
96
|
-
@invalid = true
|
97
|
-
self
|
98
|
-
end
|
99
|
-
|
100
|
-
def marked_invalid?
|
101
|
-
@invalid
|
102
|
-
end
|
103
|
-
|
104
105
|
def mark_invisible
|
105
106
|
@line = ""
|
106
107
|
self
|
data/lib/dead_end/code_search.rb
CHANGED
@@ -75,7 +75,7 @@ module DeadEnd
|
|
75
75
|
record(block: block, name: name)
|
76
76
|
|
77
77
|
if block.valid?
|
78
|
-
block.
|
78
|
+
block.mark_invisible
|
79
79
|
frontier << block
|
80
80
|
else
|
81
81
|
frontier << block
|
@@ -92,7 +92,7 @@ module DeadEnd
|
|
92
92
|
|
93
93
|
# Parses the most indented lines into blocks that are marked
|
94
94
|
# and added to the frontier
|
95
|
-
def
|
95
|
+
def visit_new_blocks
|
96
96
|
max_indent = frontier.next_indent_line&.indent
|
97
97
|
|
98
98
|
while (line = frontier.next_indent_line) && (line.indent == max_indent)
|
@@ -145,7 +145,7 @@ module DeadEnd
|
|
145
145
|
if frontier.expand?
|
146
146
|
expand_invalid_block
|
147
147
|
else
|
148
|
-
|
148
|
+
visit_new_blocks
|
149
149
|
end
|
150
150
|
end
|
151
151
|
|
data/lib/dead_end/version.rb
CHANGED
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: 1.0.
|
4
|
+
version: 1.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- schneems
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-12-
|
11
|
+
date: 2020-12-28 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
|
@@ -76,7 +76,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
76
76
|
- !ruby/object:Gem::Version
|
77
77
|
version: '0'
|
78
78
|
requirements: []
|
79
|
-
rubygems_version: 3.
|
79
|
+
rubygems_version: 3.1.4
|
80
80
|
signing_key:
|
81
81
|
specification_version: 4
|
82
82
|
summary: Find syntax errors in your source in a snap
|