dead_end 1.1.7 → 1.2.0
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 +14 -9
- data/.standard.yml +1 -0
- data/CHANGELOG.md +5 -0
- data/Gemfile +1 -0
- data/Gemfile.lock +29 -2
- data/README.md +1 -19
- data/Rakefile +1 -1
- data/dead_end.gemspec +12 -12
- data/exe/dead_end +3 -3
- data/lib/dead_end/around_block_scan.rb +13 -12
- data/lib/dead_end/auto.rb +2 -51
- data/lib/dead_end/block_expand.rb +6 -5
- data/lib/dead_end/capture_code_context.rb +49 -39
- data/lib/dead_end/code_block.rb +3 -3
- data/lib/dead_end/code_frontier.rb +4 -4
- data/lib/dead_end/code_line.rb +11 -11
- data/lib/dead_end/code_search.rb +22 -19
- data/lib/dead_end/display_code_with_line_numbers.rb +7 -7
- data/lib/dead_end/display_invalid_blocks.rb +6 -7
- data/lib/dead_end/fyi.rb +1 -2
- data/lib/dead_end/heredoc_block_parse.rb +6 -2
- data/lib/dead_end/internals.rb +16 -16
- data/lib/dead_end/lex_all.rb +7 -7
- data/lib/dead_end/parse_blocks_from_indent_line.rb +2 -3
- data/lib/dead_end/trailing_slash_join.rb +2 -2
- data/lib/dead_end/version.rb +1 -1
- data/lib/dead_end/who_dis_syntax_error.rb +10 -5
- metadata +4 -4
- data/.travis.yml +0 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c7db29aed59a901a58a0b1ed50873b6f2c17692f7724ffad2736b99694b78ba0
|
4
|
+
data.tar.gz: e1cf2a11fa38af85df30d89a559b0c6e5a74beec2c0add8b6f67dd142d793de1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 444cfdfd7df93038d1714729a21a3b7a20f9000e9cf0541521d51a125d00be4968e83bbc8a51bf4c4689e2b99706cf5eb0032a318647ecb3fd4452a343798e7a
|
7
|
+
data.tar.gz: c532b87160ae6231776b72ff68f3c8f940064051e14c27da28fa22a15c1be0060d9b2afd600bb66dd4571cd39b9372b1956782565040a5e159776f9dac9f9e62
|
data/.circleci/config.yml
CHANGED
@@ -7,15 +7,12 @@ references:
|
|
7
7
|
name: Run test suite
|
8
8
|
command: bundle exec rspec spec/
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
steps:
|
15
|
-
- checkout
|
16
|
-
- ruby/install-deps
|
17
|
-
- <<: *unit
|
10
|
+
lint: &lint
|
11
|
+
run:
|
12
|
+
name: Run linter, fix with `standardrb --fix` locally
|
13
|
+
command: bundle exec standardrb
|
18
14
|
|
15
|
+
jobs:
|
19
16
|
"ruby-2-6":
|
20
17
|
docker:
|
21
18
|
- image: circleci/ruby:2.6
|
@@ -40,11 +37,19 @@ jobs:
|
|
40
37
|
- ruby/install-deps
|
41
38
|
- <<: *unit
|
42
39
|
|
40
|
+
"lint":
|
41
|
+
docker:
|
42
|
+
- image: circleci/ruby:3.0
|
43
|
+
steps:
|
44
|
+
- checkout
|
45
|
+
- ruby/install-deps
|
46
|
+
- <<: *lint
|
47
|
+
|
43
48
|
workflows:
|
44
49
|
version: 2
|
45
50
|
build:
|
46
51
|
jobs:
|
47
|
-
- "ruby-2-5"
|
48
52
|
- "ruby-2-6"
|
49
53
|
- "ruby-2-7"
|
50
54
|
- "ruby-3-0"
|
55
|
+
- "lint"
|
data/.standard.yml
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
ruby_version: 2.6.6
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,10 @@
|
|
1
1
|
## HEAD (unreleased)
|
2
2
|
|
3
|
+
## 1.2.0
|
4
|
+
|
5
|
+
- Output improvements via less greedy unmatched kw capture https://github.com/zombocom/dead_end/pull/73
|
6
|
+
- Remove NoMethodError patching instead use https://github.com/ruby/error_highlight/ (https://github.com/zombocom/dead_end/pull/71)
|
7
|
+
|
3
8
|
## 1.1.7
|
4
9
|
|
5
10
|
- Fix sinatra support for `require_relative` (https://github.com/zombocom/dead_end/pull/63)
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,13 +1,20 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
dead_end (1.
|
4
|
+
dead_end (1.2.0)
|
5
5
|
|
6
6
|
GEM
|
7
7
|
remote: https://rubygems.org/
|
8
8
|
specs:
|
9
|
+
ast (2.4.2)
|
9
10
|
diff-lcs (1.4.4)
|
11
|
+
parallel (1.21.0)
|
12
|
+
parser (3.0.2.0)
|
13
|
+
ast (~> 2.4.1)
|
14
|
+
rainbow (3.0.0)
|
10
15
|
rake (12.3.3)
|
16
|
+
regexp_parser (2.1.1)
|
17
|
+
rexml (3.2.5)
|
11
18
|
rspec (3.10.0)
|
12
19
|
rspec-core (~> 3.10.0)
|
13
20
|
rspec-expectations (~> 3.10.0)
|
@@ -21,7 +28,26 @@ GEM
|
|
21
28
|
diff-lcs (>= 1.2.0, < 2.0)
|
22
29
|
rspec-support (~> 3.10.0)
|
23
30
|
rspec-support (3.10.0)
|
31
|
+
rubocop (1.20.0)
|
32
|
+
parallel (~> 1.10)
|
33
|
+
parser (>= 3.0.0.0)
|
34
|
+
rainbow (>= 2.2.2, < 4.0)
|
35
|
+
regexp_parser (>= 1.8, < 3.0)
|
36
|
+
rexml
|
37
|
+
rubocop-ast (>= 1.9.1, < 2.0)
|
38
|
+
ruby-progressbar (~> 1.7)
|
39
|
+
unicode-display_width (>= 1.4.0, < 3.0)
|
40
|
+
rubocop-ast (1.12.0)
|
41
|
+
parser (>= 3.0.1.1)
|
42
|
+
rubocop-performance (1.11.5)
|
43
|
+
rubocop (>= 1.7.0, < 2.0)
|
44
|
+
rubocop-ast (>= 0.4.0)
|
45
|
+
ruby-progressbar (1.11.0)
|
24
46
|
stackprof (0.2.16)
|
47
|
+
standard (1.3.0)
|
48
|
+
rubocop (= 1.20.0)
|
49
|
+
rubocop-performance (= 1.11.5)
|
50
|
+
unicode-display_width (2.1.0)
|
25
51
|
|
26
52
|
PLATFORMS
|
27
53
|
ruby
|
@@ -31,6 +57,7 @@ DEPENDENCIES
|
|
31
57
|
rake (~> 12.0)
|
32
58
|
rspec (~> 3.0)
|
33
59
|
stackprof
|
60
|
+
standard
|
34
61
|
|
35
62
|
BUNDLED WITH
|
36
|
-
2.2.
|
63
|
+
2.2.27
|
data/README.md
CHANGED
@@ -81,27 +81,9 @@ end
|
|
81
81
|
|
82
82
|
As well as unmatched `|` and unmatched `}`. These errors can be time consuming to debug because Ruby often only tells you the last line in the file. The command `ruby -wc path/to/file.rb` can narrow it down a little bit, but this library does a better job.
|
83
83
|
|
84
|
-
## What other errors does it handle?
|
85
|
-
|
86
|
-
In addition to syntax errors, the NoMethodError is annotated to show the line where the error occured, and the surrounding context:
|
87
|
-
|
88
|
-
```
|
89
|
-
scratch.rb:7:in `call': undefined method `upcase' for nil:NilClass (NoMethodError)
|
90
|
-
|
91
|
-
|
92
|
-
1 class Pet
|
93
|
-
6 def call
|
94
|
-
❯ 7 puts "Come here #{@neam.upcase}"
|
95
|
-
8 end
|
96
|
-
9 end
|
97
|
-
```
|
98
|
-
|
99
84
|
## Sounds cool, but why isn't this baked into Ruby directly?
|
100
85
|
|
101
|
-
|
102
|
-
|
103
|
-
1. Get real world useage and feedback. If we gave you an awful suggestion, let us know! We try to handle lots of cases well, but maybe we could be better.
|
104
|
-
2. Prove out demand. If you like this idea, then vote for it by putting it in your Gemfile.
|
86
|
+
We are now talking about it https://bugs.ruby-lang.org/issues/18159#change-93682.
|
105
87
|
|
106
88
|
## Artificial Inteligence?
|
107
89
|
|
data/Rakefile
CHANGED
data/dead_end.gemspec
CHANGED
@@ -1,17 +1,17 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative
|
3
|
+
require_relative "lib/dead_end/version"
|
4
4
|
|
5
5
|
Gem::Specification.new do |spec|
|
6
|
-
spec.name
|
7
|
-
spec.version
|
8
|
-
spec.authors
|
9
|
-
spec.email
|
6
|
+
spec.name = "dead_end"
|
7
|
+
spec.version = DeadEnd::VERSION
|
8
|
+
spec.authors = ["schneems"]
|
9
|
+
spec.email = ["richard.schneeman+foo@gmail.com"]
|
10
10
|
|
11
|
-
spec.summary
|
12
|
-
spec.description
|
13
|
-
spec.homepage
|
14
|
-
spec.license
|
11
|
+
spec.summary = "Find syntax errors in your source in a snap"
|
12
|
+
spec.description = 'When you get an "unexpected end" in your syntax this gem helps you find it'
|
13
|
+
spec.homepage = "https://github.com/zombocom/dead_end.git"
|
14
|
+
spec.license = "MIT"
|
15
15
|
spec.required_ruby_version = Gem::Requirement.new(">= 2.5.0")
|
16
16
|
|
17
17
|
spec.metadata["homepage_uri"] = spec.homepage
|
@@ -19,10 +19,10 @@ Gem::Specification.new do |spec|
|
|
19
19
|
|
20
20
|
# Specify which files should be added to the gem when it is released.
|
21
21
|
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
22
|
-
spec.files
|
22
|
+
spec.files = Dir.chdir(File.expand_path("..", __FILE__)) do
|
23
23
|
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features|assets)/}) }
|
24
24
|
end
|
25
|
-
spec.bindir
|
26
|
-
spec.executables
|
25
|
+
spec.bindir = "exe"
|
26
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
27
27
|
spec.require_paths = ["lib"]
|
28
28
|
end
|
data/exe/dead_end
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
require
|
3
|
+
require "pathname"
|
4
4
|
require "optparse"
|
5
|
-
require_relative "../lib/dead_end
|
5
|
+
require_relative "../lib/dead_end"
|
6
6
|
|
7
7
|
options = {}
|
8
8
|
options[:terminal] = true
|
@@ -60,7 +60,7 @@ end
|
|
60
60
|
file = Pathname(file)
|
61
61
|
options[:record_dir] = "tmp" if ENV["DEBUG"]
|
62
62
|
|
63
|
-
|
63
|
+
warn "Record dir: #{options[:record_dir]}" if options[:record_dir]
|
64
64
|
|
65
65
|
DeadEnd.call(
|
66
66
|
source: file.read,
|
@@ -1,5 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
|
2
|
+
|
3
3
|
module DeadEnd
|
4
4
|
# This class is useful for exploring contents before and after
|
5
5
|
# a block
|
@@ -28,7 +28,7 @@ module DeadEnd
|
|
28
28
|
#
|
29
29
|
# To grab the next surrounding indentation use AroundBlockScan#scan_adjacent_indent
|
30
30
|
class AroundBlockScan
|
31
|
-
def initialize(code_lines
|
31
|
+
def initialize(code_lines:, block:)
|
32
32
|
@code_lines = code_lines
|
33
33
|
@orig_before_index = block.lines.first.index
|
34
34
|
@orig_after_index = block.lines.last.index
|
@@ -56,7 +56,7 @@ module DeadEnd
|
|
56
56
|
end_count = 0
|
57
57
|
@before_index = before_lines.reverse_each.take_while do |line|
|
58
58
|
next false if stop_next
|
59
|
-
next true if @skip_array.detect {|meth| line.send(meth) }
|
59
|
+
next true if @skip_array.detect { |meth| line.send(meth) }
|
60
60
|
|
61
61
|
kw_count += 1 if line.is_kw?
|
62
62
|
end_count += 1 if line.is_end?
|
@@ -65,14 +65,14 @@ module DeadEnd
|
|
65
65
|
end
|
66
66
|
|
67
67
|
block.call(line)
|
68
|
-
end.
|
68
|
+
end.last&.index
|
69
69
|
|
70
70
|
stop_next = false
|
71
71
|
kw_count = 0
|
72
72
|
end_count = 0
|
73
73
|
@after_index = after_lines.take_while do |line|
|
74
74
|
next false if stop_next
|
75
|
-
next true if @skip_array.detect {|meth| line.send(meth) }
|
75
|
+
next true if @skip_array.detect { |meth| line.send(meth) }
|
76
76
|
|
77
77
|
kw_count += 1 if line.is_kw?
|
78
78
|
end_count += 1 if line.is_end?
|
@@ -89,7 +89,7 @@ module DeadEnd
|
|
89
89
|
lines = []
|
90
90
|
kw_count = 0
|
91
91
|
end_count = 0
|
92
|
-
before_lines.
|
92
|
+
before_lines.reverse_each do |line|
|
93
93
|
next if line.empty?
|
94
94
|
break if line.indent < @orig_indent
|
95
95
|
next if line.indent != @orig_indent
|
@@ -124,14 +124,14 @@ module DeadEnd
|
|
124
124
|
|
125
125
|
lines << line
|
126
126
|
end
|
127
|
-
lines.select! {|line| !line.is_comment? }
|
127
|
+
lines.select! { |line| !line.is_comment? }
|
128
128
|
|
129
129
|
lines
|
130
130
|
end
|
131
131
|
|
132
132
|
def on_falling_indent
|
133
133
|
last_indent = @orig_indent
|
134
|
-
before_lines.
|
134
|
+
before_lines.reverse_each do |line|
|
135
135
|
next if line.empty?
|
136
136
|
if line.indent < last_indent
|
137
137
|
yield line
|
@@ -150,7 +150,7 @@ module DeadEnd
|
|
150
150
|
end
|
151
151
|
|
152
152
|
def scan_neighbors
|
153
|
-
|
153
|
+
scan_while { |line| line.not_empty? && line.indent >= @orig_indent }
|
154
154
|
end
|
155
155
|
|
156
156
|
def next_up
|
@@ -167,13 +167,14 @@ module DeadEnd
|
|
167
167
|
before_after_indent << (next_down&.indent || 0)
|
168
168
|
|
169
169
|
indent = before_after_indent.min
|
170
|
-
|
170
|
+
scan_while { |line| line.not_empty? && line.indent >= indent }
|
171
171
|
|
172
172
|
self
|
173
173
|
end
|
174
174
|
|
175
175
|
def start_at_next_line
|
176
|
-
before_index
|
176
|
+
before_index
|
177
|
+
after_index
|
177
178
|
@before_index -= 1
|
178
179
|
@after_index += 1
|
179
180
|
self
|
@@ -196,7 +197,7 @@ module DeadEnd
|
|
196
197
|
end
|
197
198
|
|
198
199
|
private def after_lines
|
199
|
-
@code_lines[after_index.next
|
200
|
+
@code_lines[after_index.next..] || []
|
200
201
|
end
|
201
202
|
end
|
202
203
|
end
|
data/lib/dead_end/auto.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
|
2
|
+
|
3
3
|
require_relative "../dead_end/internals"
|
4
4
|
|
5
5
|
# Monkey patch kernel to ensure that all `require` calls call the same
|
@@ -40,6 +40,7 @@ end
|
|
40
40
|
# am I doing something different?
|
41
41
|
class Object
|
42
42
|
private
|
43
|
+
|
43
44
|
def load(path, wrap = false)
|
44
45
|
Kernel.load(path, wrap)
|
45
46
|
rescue SyntaxError => e
|
@@ -52,53 +53,3 @@ class Object
|
|
52
53
|
DeadEnd.handle_error(e)
|
53
54
|
end
|
54
55
|
end
|
55
|
-
|
56
|
-
module DeadEnd
|
57
|
-
IsProduction = -> {
|
58
|
-
ENV["RAILS_ENV"] == "production" || ENV["RACK_ENV"] == "production"
|
59
|
-
}
|
60
|
-
end
|
61
|
-
|
62
|
-
# Unlike a syntax error, a NoMethodError can occur hundreds or thousands of times and
|
63
|
-
# chew up CPU and other resources. Since this is primarilly a "development" optimization
|
64
|
-
# we can attempt to disable this behavior in a production context.
|
65
|
-
if !DeadEnd::IsProduction.call
|
66
|
-
class NoMethodError
|
67
|
-
alias :dead_end_original_to_s :to_s
|
68
|
-
|
69
|
-
def to_s
|
70
|
-
return super if DeadEnd::IsProduction.call
|
71
|
-
|
72
|
-
file, line, _ = backtrace[0].split(":")
|
73
|
-
return super if !File.exist?(file)
|
74
|
-
|
75
|
-
index = line.to_i - 1
|
76
|
-
source = File.read(file)
|
77
|
-
code_lines = DeadEnd::CodeLine.parse(source)
|
78
|
-
|
79
|
-
block = DeadEnd::CodeBlock.new(lines: code_lines[index])
|
80
|
-
lines = DeadEnd::CaptureCodeContext.new(
|
81
|
-
blocks: block,
|
82
|
-
code_lines: code_lines
|
83
|
-
).call
|
84
|
-
|
85
|
-
message = super.dup
|
86
|
-
message << $/
|
87
|
-
message << $/
|
88
|
-
|
89
|
-
message << DeadEnd::DisplayCodeWithLineNumbers.new(
|
90
|
-
lines: lines,
|
91
|
-
highlight_lines: block.lines,
|
92
|
-
terminal: self.class.to_tty?
|
93
|
-
).call
|
94
|
-
|
95
|
-
message << $/
|
96
|
-
message
|
97
|
-
rescue => e
|
98
|
-
puts "DeadEnd Internal error: #{e.dead_end_original_to_s}"
|
99
|
-
puts "DeadEnd Internal backtrace:"
|
100
|
-
puts backtrace.map {|l| " " + l }.join($/)
|
101
|
-
super
|
102
|
-
end
|
103
|
-
end
|
104
|
-
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module DeadEnd
|
3
4
|
# This class is responsible for taking a code block that exists
|
4
5
|
# at a far indentaion and then iteratively increasing the block
|
@@ -30,7 +31,7 @@ module DeadEnd
|
|
30
31
|
# end
|
31
32
|
#
|
32
33
|
class BlockExpand
|
33
|
-
def initialize(code_lines:
|
34
|
+
def initialize(code_lines:)
|
34
35
|
@code_lines = code_lines
|
35
36
|
end
|
36
37
|
|
@@ -43,7 +44,7 @@ module DeadEnd
|
|
43
44
|
end
|
44
45
|
|
45
46
|
def expand_indent(block)
|
46
|
-
|
47
|
+
AroundBlockScan.new(code_lines: @code_lines, block: block)
|
47
48
|
.skip(:hidden?)
|
48
49
|
.stop_after_kw
|
49
50
|
.scan_adjacent_indent
|
@@ -59,15 +60,15 @@ module DeadEnd
|
|
59
60
|
# Slurp up empties
|
60
61
|
if grab_empty
|
61
62
|
scan = AroundBlockScan.new(code_lines: @code_lines, block: scan.code_block)
|
62
|
-
.scan_while {|line| line.empty? || line.hidden? }
|
63
|
+
.scan_while { |line| line.empty? || line.hidden? }
|
63
64
|
end
|
64
65
|
|
65
66
|
new_block = scan.code_block
|
66
67
|
|
67
68
|
if block.lines == new_block.lines
|
68
|
-
|
69
|
+
nil
|
69
70
|
else
|
70
|
-
|
71
|
+
new_block
|
71
72
|
end
|
72
73
|
end
|
73
74
|
end
|
@@ -1,7 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module DeadEnd
|
4
|
-
|
5
4
|
# Given a block, this method will capture surrounding
|
6
5
|
# code to give the user more context for the location of
|
7
6
|
# the problem.
|
@@ -26,7 +25,7 @@ module DeadEnd
|
|
26
25
|
class CaptureCodeContext
|
27
26
|
attr_reader :code_lines
|
28
27
|
|
29
|
-
def initialize(blocks
|
28
|
+
def initialize(blocks:, code_lines:)
|
30
29
|
@blocks = Array(blocks)
|
31
30
|
@code_lines = code_lines
|
32
31
|
@visible_lines = @blocks.map(&:visible_lines).flatten
|
@@ -45,13 +44,13 @@ module DeadEnd
|
|
45
44
|
@lines_to_output.uniq!
|
46
45
|
@lines_to_output.sort!
|
47
46
|
|
48
|
-
|
47
|
+
@lines_to_output
|
49
48
|
end
|
50
49
|
|
51
50
|
def capture_falling_indent(block)
|
52
51
|
AroundBlockScan.new(
|
53
52
|
block: block,
|
54
|
-
code_lines: @code_lines
|
53
|
+
code_lines: @code_lines
|
55
54
|
).on_falling_indent do |line|
|
56
55
|
@lines_to_output << line
|
57
56
|
end
|
@@ -67,50 +66,61 @@ module DeadEnd
|
|
67
66
|
@lines_to_output.concat(around_lines)
|
68
67
|
end
|
69
68
|
|
70
|
-
#
|
69
|
+
# When there is an invalid with a keyword
|
70
|
+
# right before an end, it's unclear where
|
71
|
+
# the correct code should be.
|
72
|
+
#
|
73
|
+
# Take this example:
|
74
|
+
#
|
75
|
+
# class Dog # 1
|
76
|
+
# def bark # 2
|
77
|
+
# puts "woof" # 3
|
78
|
+
# end # 4
|
79
|
+
#
|
80
|
+
# However due to https://github.com/zombocom/dead_end/issues/32
|
81
|
+
# the problem line will be identified as:
|
82
|
+
#
|
83
|
+
# ❯ class Dog # 1
|
84
|
+
#
|
85
|
+
# Because lines 2, 3, and 4 are technically valid code and are expanded
|
86
|
+
# first, deemed valid, and hidden. We need to un-hide the matching end
|
87
|
+
# line 4. Also work backwards and if there's a mis-matched keyword, show it
|
88
|
+
# too
|
71
89
|
def capture_last_end_same_indent(block)
|
72
90
|
start_index = block.visible_lines.first.index
|
73
91
|
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
92
|
|
77
|
-
#
|
93
|
+
# Find first end with same indent
|
94
|
+
# (this would return line 4)
|
78
95
|
#
|
79
|
-
#
|
80
|
-
|
81
|
-
|
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?
|
96
|
+
# end # 4
|
97
|
+
matching_end = lines.find { |line| line.indent == block.current_indent && line.is_end? }
|
98
|
+
return unless matching_end
|
99
99
|
|
100
|
-
|
101
|
-
true
|
102
|
-
end.reverse
|
100
|
+
@lines_to_output << matching_end
|
103
101
|
|
104
|
-
|
102
|
+
lines = @code_lines[start_index..matching_end.index]
|
105
103
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
104
|
+
# Work backwards from the end to
|
105
|
+
# see if there are mis-matched
|
106
|
+
# keyword/end pairs
|
107
|
+
#
|
108
|
+
# Return the first mis-matched keyword
|
109
|
+
# this would find line 2
|
110
|
+
#
|
111
|
+
# def bark # 2
|
112
|
+
# puts "woof" # 3
|
113
|
+
# end # 4
|
114
|
+
end_count = 0
|
115
|
+
kw_count = 0
|
116
|
+
kw_line = lines.reverse.detect do |line|
|
117
|
+
end_count += 1 if line.is_end?
|
118
|
+
kw_count += 1 if line.is_kw?
|
119
|
+
|
120
|
+
!kw_count.zero? && kw_count >= end_count
|
113
121
|
end
|
122
|
+
return unless kw_line
|
123
|
+
@lines_to_output << kw_line
|
114
124
|
end
|
115
125
|
end
|
116
126
|
end
|
data/lib/dead_end/code_block.rb
CHANGED
@@ -54,11 +54,11 @@ module DeadEnd
|
|
54
54
|
# populate an array with multiple code blocks then call `sort!`
|
55
55
|
# on it without having to specify the sorting criteria
|
56
56
|
def <=>(other)
|
57
|
-
out =
|
57
|
+
out = current_indent <=> other.current_indent
|
58
58
|
return out if out != 0
|
59
59
|
|
60
60
|
# Stable sort
|
61
|
-
|
61
|
+
starts_at <=> other.starts_at
|
62
62
|
end
|
63
63
|
|
64
64
|
def current_indent
|
@@ -71,7 +71,7 @@ module DeadEnd
|
|
71
71
|
|
72
72
|
def valid?
|
73
73
|
return @valid if @valid != UNSET
|
74
|
-
@valid = DeadEnd.valid?(
|
74
|
+
@valid = DeadEnd.valid?(to_s)
|
75
75
|
end
|
76
76
|
|
77
77
|
def to_s
|
@@ -39,7 +39,7 @@ module DeadEnd
|
|
39
39
|
#
|
40
40
|
# CodeFrontier#detect_invalid_blocks
|
41
41
|
class CodeFrontier
|
42
|
-
def initialize(code_lines:
|
42
|
+
def initialize(code_lines:)
|
43
43
|
@code_lines = code_lines
|
44
44
|
@frontier = []
|
45
45
|
@unvisited_lines = @code_lines.sort_by(&:indent_index)
|
@@ -66,7 +66,7 @@ module DeadEnd
|
|
66
66
|
|
67
67
|
# Returns a code block with the largest indentation possible
|
68
68
|
def pop
|
69
|
-
|
69
|
+
@frontier.pop
|
70
70
|
end
|
71
71
|
|
72
72
|
def next_indent_line
|
@@ -78,7 +78,7 @@ module DeadEnd
|
|
78
78
|
return true if @unvisited_lines.empty?
|
79
79
|
|
80
80
|
frontier_indent = @frontier.last.current_indent
|
81
|
-
unvisited_indent= next_indent_line.indent
|
81
|
+
unvisited_indent = next_indent_line.indent
|
82
82
|
|
83
83
|
if ENV["DEBUG"]
|
84
84
|
puts "```"
|
@@ -106,7 +106,7 @@ module DeadEnd
|
|
106
106
|
register_indent_block(block)
|
107
107
|
|
108
108
|
# Make sure we don't double expand, if a code block fully engulfs another code block, keep the bigger one
|
109
|
-
@frontier.reject! {|b|
|
109
|
+
@frontier.reject! { |b|
|
110
110
|
b.starts_at >= block.starts_at && b.ends_at <= block.ends_at
|
111
111
|
}
|
112
112
|
@frontier << block
|
data/lib/dead_end/code_line.rb
CHANGED
@@ -39,7 +39,7 @@ module DeadEnd
|
|
39
39
|
|
40
40
|
attr_reader :line, :index, :indent, :original_line
|
41
41
|
|
42
|
-
def initialize(line
|
42
|
+
def initialize(line:, index:)
|
43
43
|
@original_line = line.freeze
|
44
44
|
@line = @original_line
|
45
45
|
if line.strip.empty?
|
@@ -64,25 +64,25 @@ module DeadEnd
|
|
64
64
|
next unless lex.type == :on_kw
|
65
65
|
|
66
66
|
case lex.token
|
67
|
-
when
|
67
|
+
when "if", "unless", "while", "until"
|
68
68
|
# Only count if/unless when it's not a "trailing" if/unless
|
69
69
|
# https://github.com/ruby/ruby/blob/06b44f819eb7b5ede1ff69cecb25682b56a1d60c/lib/irb/ruby-lex.rb#L374-L375
|
70
|
-
kw_count += 1
|
71
|
-
when
|
70
|
+
kw_count += 1 unless lex.expr_label?
|
71
|
+
when "def", "case", "for", "begin", "class", "module", "do"
|
72
72
|
kw_count += 1
|
73
|
-
when
|
73
|
+
when "end"
|
74
74
|
end_count += 1
|
75
75
|
end
|
76
76
|
end
|
77
77
|
|
78
|
-
@is_comment = lex_array.detect {|lex| lex.type != :on_sp}&.type == :on_comment
|
78
|
+
@is_comment = lex_array.detect { |lex| lex.type != :on_sp }&.type == :on_comment
|
79
79
|
return if @is_comment
|
80
80
|
@is_kw = (kw_count - end_count) > 0
|
81
81
|
@is_end = (end_count - kw_count) > 0
|
82
82
|
@is_trailing_slash = lex_array.last.token == TRAILING_SLASH
|
83
83
|
end
|
84
84
|
|
85
|
-
|
85
|
+
alias_method :original, :original_line
|
86
86
|
|
87
87
|
def trailing_slash?
|
88
88
|
@is_trailing_slash
|
@@ -92,8 +92,8 @@ module DeadEnd
|
|
92
92
|
@indent_index ||= [indent, index]
|
93
93
|
end
|
94
94
|
|
95
|
-
def <=>(
|
96
|
-
|
95
|
+
def <=>(other)
|
96
|
+
index <=> other.index
|
97
97
|
end
|
98
98
|
|
99
99
|
def is_comment?
|
@@ -133,7 +133,7 @@ module DeadEnd
|
|
133
133
|
def line_number
|
134
134
|
index + 1
|
135
135
|
end
|
136
|
-
|
136
|
+
alias_method :number, :line_number
|
137
137
|
|
138
138
|
def not_empty?
|
139
139
|
!empty?
|
@@ -144,7 +144,7 @@ module DeadEnd
|
|
144
144
|
end
|
145
145
|
|
146
146
|
def to_s
|
147
|
-
|
147
|
+
line
|
148
148
|
end
|
149
149
|
end
|
150
150
|
end
|
data/lib/dead_end/code_search.rb
CHANGED
@@ -25,14 +25,21 @@ module DeadEnd
|
|
25
25
|
# # => ["def lol\n"]
|
26
26
|
#
|
27
27
|
class CodeSearch
|
28
|
-
private
|
29
|
-
|
28
|
+
private
|
29
|
+
|
30
|
+
attr_reader :frontier
|
31
|
+
|
32
|
+
public
|
33
|
+
|
34
|
+
public
|
35
|
+
|
36
|
+
attr_reader :invalid_blocks, :record_dir, :code_lines
|
30
37
|
|
31
38
|
def initialize(source, record_dir: ENV["DEAD_END_RECORD_DIR"] || ENV["DEBUG"] ? "tmp" : nil)
|
32
39
|
@source = source
|
33
40
|
if record_dir
|
34
|
-
@time = Time.now.strftime(
|
35
|
-
@record_dir = Pathname(record_dir).join(@time).tap {|p| p.mkpath }
|
41
|
+
@time = Time.now.strftime("%Y-%m-%d-%H-%M-%s-%N")
|
42
|
+
@record_dir = Pathname(record_dir).join(@time).tap { |p| p.mkpath }
|
36
43
|
@write_count = 0
|
37
44
|
end
|
38
45
|
code_lines = source.lines.map.with_index do |line, i|
|
@@ -43,7 +50,7 @@ module DeadEnd
|
|
43
50
|
|
44
51
|
@frontier = CodeFrontier.new(code_lines: @code_lines)
|
45
52
|
@invalid_blocks = []
|
46
|
-
@name_tick = Hash.new {|hash, k| hash[k] = 0 }
|
53
|
+
@name_tick = Hash.new { |hash, k| hash[k] = 0 }
|
47
54
|
@tick = 0
|
48
55
|
@block_expand = BlockExpand.new(code_lines: code_lines)
|
49
56
|
@parse_blocks_from_indent_line = ParseBlocksFromIndentLine.new(code_lines: @code_lines)
|
@@ -51,13 +58,13 @@ module DeadEnd
|
|
51
58
|
|
52
59
|
# Used for debugging
|
53
60
|
def record(block:, name: "record")
|
54
|
-
return
|
61
|
+
return unless @record_dir
|
55
62
|
@name_tick[name] += 1
|
56
63
|
filename = "#{@write_count += 1}-#{name}-#{@name_tick[name]}.txt"
|
57
64
|
if ENV["DEBUG"]
|
58
65
|
puts "\n\n==== #{filename} ===="
|
59
66
|
puts "\n```#{block.starts_at}:#{block.ends_at}"
|
60
|
-
puts
|
67
|
+
puts block.to_s
|
61
68
|
puts "```"
|
62
69
|
puts " block indent: #{block.current_indent}"
|
63
70
|
end
|
@@ -65,25 +72,21 @@ module DeadEnd
|
|
65
72
|
display = DisplayInvalidBlocks.new(
|
66
73
|
blocks: block,
|
67
74
|
terminal: false,
|
68
|
-
code_lines: @code_lines
|
75
|
+
code_lines: @code_lines
|
69
76
|
)
|
70
|
-
f.write(display.indent
|
77
|
+
f.write(display.indent(display.code_with_lines))
|
71
78
|
end
|
72
79
|
end
|
73
80
|
|
74
|
-
def push(block, name:
|
81
|
+
def push(block, name:)
|
75
82
|
record(block: block, name: name)
|
76
83
|
|
77
|
-
if block.valid?
|
78
|
-
|
79
|
-
frontier << block
|
80
|
-
else
|
81
|
-
frontier << block
|
82
|
-
end
|
84
|
+
block.mark_invisible if block.valid?
|
85
|
+
frontier << block
|
83
86
|
end
|
84
87
|
|
85
88
|
# Removes the block without putting it back in the frontier
|
86
|
-
def sweep(block:, name:
|
89
|
+
def sweep(block:, name:)
|
87
90
|
record(block: block, name: name)
|
88
91
|
|
89
92
|
block.lines.each(&:mark_invisible)
|
@@ -149,8 +152,8 @@ module DeadEnd
|
|
149
152
|
end
|
150
153
|
end
|
151
154
|
|
152
|
-
@invalid_blocks.concat(frontier.detect_invalid_blocks
|
153
|
-
@invalid_blocks.sort_by! {|block| block.starts_at }
|
155
|
+
@invalid_blocks.concat(frontier.detect_invalid_blocks)
|
156
|
+
@invalid_blocks.sort_by! { |block| block.starts_at }
|
154
157
|
self
|
155
158
|
end
|
156
159
|
end
|
@@ -23,10 +23,10 @@ module DeadEnd
|
|
23
23
|
TERMINAL_HIGHLIGHT = "\e[1;3m" # Bold, italics
|
24
24
|
TERMINAL_END = "\e[0m"
|
25
25
|
|
26
|
-
def initialize(lines
|
26
|
+
def initialize(lines:, highlight_lines: [], terminal: false)
|
27
27
|
@lines = Array(lines).sort
|
28
28
|
@terminal = terminal
|
29
|
-
@highlight_line_hash = Array(highlight_lines).each_with_object({}) {|line, h| h[line] = true
|
29
|
+
@highlight_line_hash = Array(highlight_lines).each_with_object({}) { |line, h| h[line] = true }
|
30
30
|
@digit_count = @lines.last&.line_number.to_s.length
|
31
31
|
end
|
32
32
|
|
@@ -48,12 +48,12 @@ module DeadEnd
|
|
48
48
|
end.join
|
49
49
|
end
|
50
50
|
|
51
|
-
private def format(contents
|
52
|
-
string =
|
53
|
-
if highlight
|
54
|
-
|
51
|
+
private def format(contents:, number:, empty:, highlight: false)
|
52
|
+
string = +""
|
53
|
+
string << if highlight
|
54
|
+
"❯ "
|
55
55
|
else
|
56
|
-
|
56
|
+
" "
|
57
57
|
end
|
58
58
|
|
59
59
|
string << number.rjust(@digit_count).to_s
|
@@ -8,7 +8,7 @@ module DeadEnd
|
|
8
8
|
class DisplayInvalidBlocks
|
9
9
|
attr_reader :filename
|
10
10
|
|
11
|
-
def initialize(code_lines
|
11
|
+
def initialize(code_lines:, blocks:, io: $stderr, filename: nil, terminal: false, invalid_obj: WhoDisSyntaxError::Null.new)
|
12
12
|
@terminal = terminal
|
13
13
|
@filename = filename
|
14
14
|
@io = io
|
@@ -78,22 +78,21 @@ module DeadEnd
|
|
78
78
|
<<~EOM
|
79
79
|
DeadEnd: Unmatched `}` character detected
|
80
80
|
|
81
|
-
This code has an unmatched `}`. Ensure that opening
|
81
|
+
This code has an unmatched `}`. Ensure that opening curly braces are
|
82
82
|
closed: `{ }`.
|
83
83
|
EOM
|
84
84
|
else
|
85
85
|
"DeadEnd: Unmatched `#{@invalid_obj.unmatched_symbol}` detected"
|
86
86
|
end
|
87
87
|
end
|
88
|
-
|
89
88
|
end
|
90
89
|
|
91
90
|
def indent(string, with: " ")
|
92
|
-
string.each_line.map {|l| with
|
91
|
+
string.each_line.map { |l| with + l }.join
|
93
92
|
end
|
94
93
|
|
95
94
|
def code_block
|
96
|
-
string =
|
95
|
+
string = +""
|
97
96
|
string << code_with_context
|
98
97
|
string
|
99
98
|
end
|
@@ -107,7 +106,7 @@ module DeadEnd
|
|
107
106
|
DisplayCodeWithLineNumbers.new(
|
108
107
|
lines: lines,
|
109
108
|
terminal: @terminal,
|
110
|
-
highlight_lines: @invalid_lines
|
109
|
+
highlight_lines: @invalid_lines
|
111
110
|
).call
|
112
111
|
end
|
113
112
|
|
@@ -115,7 +114,7 @@ module DeadEnd
|
|
115
114
|
DisplayCodeWithLineNumbers.new(
|
116
115
|
lines: @code_lines.select(&:visible?),
|
117
116
|
terminal: @terminal,
|
118
|
-
highlight_lines: @invalid_lines
|
117
|
+
highlight_lines: @invalid_lines
|
119
118
|
).call
|
120
119
|
end
|
121
120
|
end
|
data/lib/dead_end/fyi.rb
CHANGED
@@ -3,9 +3,13 @@
|
|
3
3
|
module DeadEnd
|
4
4
|
# Takes in a source, and returns blocks containing each heredoc
|
5
5
|
class HeredocBlockParse
|
6
|
-
private
|
6
|
+
private
|
7
7
|
|
8
|
-
|
8
|
+
attr_reader :code_lines, :lex
|
9
|
+
|
10
|
+
public
|
11
|
+
|
12
|
+
def initialize(source:, code_lines:)
|
9
13
|
@code_lines = code_lines
|
10
14
|
@lex = LexAll.new(source: source)
|
11
15
|
end
|
data/lib/dead_end/internals.rb
CHANGED
@@ -1,15 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
#
|
3
4
|
# This is the top level file, but is moved to `internals`
|
4
5
|
# so the top level file can instead enable the "automatic" behavior
|
5
6
|
|
6
7
|
require_relative "version"
|
7
8
|
|
8
|
-
require
|
9
|
-
require
|
10
|
-
require
|
11
|
-
require
|
12
|
-
require
|
9
|
+
require "tmpdir"
|
10
|
+
require "stringio"
|
11
|
+
require "pathname"
|
12
|
+
require "ripper"
|
13
|
+
require "timeout"
|
13
14
|
|
14
15
|
module DeadEnd
|
15
16
|
class Error < StandardError; end
|
@@ -17,27 +18,27 @@ module DeadEnd
|
|
17
18
|
TIMEOUT_DEFAULT = ENV.fetch("DEAD_END_TIMEOUT", 5).to_i
|
18
19
|
|
19
20
|
def self.handle_error(e, search_source_on_error: SEARCH_SOURCE_ON_ERROR_DEFAULT)
|
20
|
-
raise e
|
21
|
+
raise e unless e.message.include?("end-of-input")
|
21
22
|
|
22
23
|
filename = e.message.split(":").first
|
23
24
|
|
24
25
|
$stderr.sync = true
|
25
|
-
|
26
|
+
warn "Run `$ dead_end #{filename}` for more options\n"
|
26
27
|
|
27
28
|
if search_source_on_error
|
28
|
-
|
29
|
+
call(
|
29
30
|
source: Pathname(filename).read,
|
30
31
|
filename: filename,
|
31
|
-
terminal: true
|
32
|
+
terminal: true
|
32
33
|
)
|
33
34
|
end
|
34
35
|
|
35
|
-
|
36
|
-
|
36
|
+
warn ""
|
37
|
+
warn ""
|
37
38
|
raise e
|
38
39
|
end
|
39
40
|
|
40
|
-
def self.call(source
|
41
|
+
def self.call(source:, filename:, terminal: false, record_dir: nil, timeout: TIMEOUT_DEFAULT, io: $stderr)
|
41
42
|
search = nil
|
42
43
|
Timeout.timeout(timeout) do
|
43
44
|
record_dir ||= ENV["DEBUG"] ? "tmp" : nil
|
@@ -82,13 +83,13 @@ module DeadEnd
|
|
82
83
|
# ) # => true
|
83
84
|
#
|
84
85
|
# DeadEnd.valid?(code_lines) # => false
|
85
|
-
def self.valid_without?(without_lines
|
86
|
+
def self.valid_without?(without_lines:, code_lines:)
|
86
87
|
lines = code_lines - Array(without_lines).flatten
|
87
88
|
|
88
89
|
if lines.empty?
|
89
|
-
|
90
|
+
true
|
90
91
|
else
|
91
|
-
|
92
|
+
valid?(lines)
|
92
93
|
end
|
93
94
|
end
|
94
95
|
|
@@ -137,7 +138,6 @@ module DeadEnd
|
|
137
138
|
!invalid?(source)
|
138
139
|
end
|
139
140
|
|
140
|
-
|
141
141
|
def self.invalid_type(source)
|
142
142
|
WhoDisSyntaxError.new(source).call
|
143
143
|
end
|
data/lib/dead_end/lex_all.rb
CHANGED
@@ -8,20 +8,20 @@ module DeadEnd
|
|
8
8
|
class LexAll
|
9
9
|
include Enumerable
|
10
10
|
|
11
|
-
def initialize(source:
|
11
|
+
def initialize(source:)
|
12
12
|
@lex = Ripper.lex(source)
|
13
|
-
lineno = @lex.last
|
13
|
+
lineno = @lex.last.first.first + 1
|
14
14
|
source_lines = source.lines
|
15
15
|
last_lineno = source_lines.count
|
16
16
|
|
17
17
|
until lineno >= last_lineno
|
18
|
-
lines = source_lines[lineno
|
18
|
+
lines = source_lines[lineno..]
|
19
19
|
|
20
|
-
@lex.concat(Ripper.lex(lines.join,
|
21
|
-
lineno = @lex.last
|
20
|
+
@lex.concat(Ripper.lex(lines.join, "-", lineno + 1))
|
21
|
+
lineno = @lex.last.first.first + 1
|
22
22
|
end
|
23
23
|
|
24
|
-
@lex.map! {|(line, _), type, token, state| LexValue.new(line,
|
24
|
+
@lex.map! { |(line, _), type, token, state| LexValue.new(line, type, token, state) }
|
25
25
|
end
|
26
26
|
|
27
27
|
def each
|
@@ -49,7 +49,7 @@ module DeadEnd
|
|
49
49
|
class LexValue
|
50
50
|
attr_reader :line, :type, :token, :state
|
51
51
|
|
52
|
-
def initialize(line,
|
52
|
+
def initialize(line, type, token, state)
|
53
53
|
@line = line
|
54
54
|
@type = type
|
55
55
|
@token = token
|
@@ -29,7 +29,7 @@ module DeadEnd
|
|
29
29
|
class ParseBlocksFromIndentLine
|
30
30
|
attr_reader :code_lines
|
31
31
|
|
32
|
-
def initialize(code_lines:
|
32
|
+
def initialize(code_lines:)
|
33
33
|
@code_lines = code_lines
|
34
34
|
end
|
35
35
|
|
@@ -38,7 +38,7 @@ module DeadEnd
|
|
38
38
|
scan = AroundBlockScan.new(code_lines: code_lines, block: CodeBlock.new(lines: target_line))
|
39
39
|
.skip(:empty?)
|
40
40
|
.skip(:hidden?)
|
41
|
-
.scan_while {|line| line.indent >= target_line.indent }
|
41
|
+
.scan_while { |line| line.indent >= target_line.indent }
|
42
42
|
|
43
43
|
neighbors = scan.code_block.lines
|
44
44
|
|
@@ -53,4 +53,3 @@ module DeadEnd
|
|
53
53
|
end
|
54
54
|
end
|
55
55
|
end
|
56
|
-
|
@@ -27,7 +27,7 @@ module DeadEnd
|
|
27
27
|
@trailing_lines = []
|
28
28
|
@code_lines.select(&:trailing_slash?).each do |trailing|
|
29
29
|
stop_next = false
|
30
|
-
lines = @code_lines[trailing.index
|
30
|
+
lines = @code_lines[trailing.index..].take_while do |line|
|
31
31
|
next false if stop_next
|
32
32
|
|
33
33
|
if !line.trailing_slash?
|
@@ -47,7 +47,7 @@ module DeadEnd
|
|
47
47
|
lines.each(&:mark_invisible)
|
48
48
|
end
|
49
49
|
|
50
|
-
|
50
|
+
@code_lines_dup
|
51
51
|
end
|
52
52
|
end
|
53
53
|
end
|
data/lib/dead_end/version.rb
CHANGED
@@ -9,8 +9,13 @@ module DeadEnd
|
|
9
9
|
# # => :missing_end
|
10
10
|
class WhoDisSyntaxError < Ripper
|
11
11
|
class Null
|
12
|
-
def error_symbol
|
13
|
-
|
12
|
+
def error_symbol
|
13
|
+
:missing_end
|
14
|
+
end
|
15
|
+
|
16
|
+
def unmatched_symbol
|
17
|
+
:end
|
18
|
+
end
|
14
19
|
end
|
15
20
|
attr_reader :error, :run_once
|
16
21
|
|
@@ -56,9 +61,9 @@ module DeadEnd
|
|
56
61
|
when /unexpected .* expecting '(?<unmatched_symbol>.*)'/
|
57
62
|
@unmatched_symbol = $1.to_sym if $1
|
58
63
|
@error_symbol = :unmatched_syntax
|
59
|
-
when /unexpected `end'/,
|
60
|
-
/unexpected end/,
|
61
|
-
/unexpected keyword_end/i
|
64
|
+
when /unexpected `end'/, # Ruby 2.7 and 3.0
|
65
|
+
/unexpected end/, # Ruby 2.6
|
66
|
+
/unexpected keyword_end/i # Ruby 2.5
|
62
67
|
|
63
68
|
@error_symbol = :unmatched_syntax
|
64
69
|
else
|
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.
|
4
|
+
version: 1.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- schneems
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-10-08 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
|
@@ -23,7 +23,7 @@ files:
|
|
23
23
|
- ".github/workflows/check_changelog.yml"
|
24
24
|
- ".gitignore"
|
25
25
|
- ".rspec"
|
26
|
-
- ".
|
26
|
+
- ".standard.yml"
|
27
27
|
- CHANGELOG.md
|
28
28
|
- CODE_OF_CONDUCT.md
|
29
29
|
- Gemfile
|
@@ -75,7 +75,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
75
75
|
- !ruby/object:Gem::Version
|
76
76
|
version: '0'
|
77
77
|
requirements: []
|
78
|
-
rubygems_version: 3.2.
|
78
|
+
rubygems_version: 3.2.22
|
79
79
|
signing_key:
|
80
80
|
specification_version: 4
|
81
81
|
summary: Find syntax errors in your source in a snap
|