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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 91a09272a46d3519c0ca78dcd3fd38199898fea1d6261c18576ff832e55351bd
4
- data.tar.gz: f50e76a749fe0ecdf871fdaf6aead25132cb26a5d03068cbc611476c6eb06a8c
3
+ metadata.gz: 125719d74f88dcc5d196a1570508bd36d83d7ece6058b029a579da90a70cc099
4
+ data.tar.gz: 2eea6be22997101b46ff2820dfabaef4e473424a552e7f9c2835e43471c4431b
5
5
  SHA512:
6
- metadata.gz: 89e7c7734a7a74c735479d35418ffda7d53557ce7d0c8a47fb2a67f5581d87d1be8117ff62d6590ed2cbf633fb566b797a6f3c3033e6c6a50b543cfbba9f4718
7
- data.tar.gz: 2804e68394e928f36803859eb0e193bf96dc088138c4fea87510d6bc0d70e9c5485b6ca4c402762174c1ba3b139b9a4d1af28a6b31e87ec4e36859f659afc42d
6
+ metadata.gz: 674e35c603ec9ed2a8662e6ee3472381c6108a8734f455d39ef30472e38d13fd08a23cb107333f9ff8353ace614cb6c1f5bcb21e6aeb5384911ad1a61eb1ff58
7
+ data.tar.gz: 4ed5351764e96b5c95b2c4f68403bd81a3739163ad83d9e473829a1f6571f7e3614b855f56f21f5e8f15434817f7b7e8a947146044d4ac4c4f9314a15cd6b9bf
@@ -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)
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- dead_end (1.0.1)
4
+ dead_end (1.0.2)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
@@ -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
- around_lines = AroundBlockScan.new(code_lines: @code_lines, block: block)
39
- .start_at_next_line
40
- .capture_neighbor_context
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
@@ -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
- DeadEnd.valid?(self.to_s)
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 an "indent hash" which is exposed via `next_indent_line`
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 via and then when that code block
22
- # is added back to the frontier, then the lines in the code block are removed from the
23
- # indent hash so we don't double-create the same block.
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
- @indent_hash = {}
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
- indent = @indent_hash.keys.sort.last
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 @indent_hash.empty?
78
+ return true if @unvisited_lines.empty?
90
79
 
91
80
  frontier_indent = @frontier.last.current_indent
92
- hash_indent = @indent_hash.keys.sort.last
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 " @hash indent: #{hash_indent}"
88
+ puts " @unvisited indent: #{unvisited_indent}"
100
89
  end
101
90
 
102
- frontier_indent >= hash_indent
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.each do |line|
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
 
@@ -36,9 +36,14 @@ module DeadEnd
36
36
  def initialize(line: , index:)
37
37
  @original_line = line.freeze
38
38
  @line = @original_line
39
- @empty = line.strip.empty?
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
@@ -75,7 +75,7 @@ module DeadEnd
75
75
  record(block: block, name: name)
76
76
 
77
77
  if block.valid?
78
- block.lines.each(&:mark_invisible)
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 add_invalid_blocks
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
- add_invalid_blocks
148
+ visit_new_blocks
149
149
  end
150
150
  end
151
151
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DeadEnd
4
- VERSION = "1.0.1"
4
+ VERSION = "1.0.2"
5
5
  end
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.1
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 00:00:00.000000000 Z
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.0.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