dead_end 1.0.1 → 1.0.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|