dead_end 1.1.5 → 2.0.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 +21 -1
- 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 +17 -19
- data/lib/dead_end/auto.rb +3 -52
- data/lib/dead_end/block_expand.rb +6 -5
- data/lib/dead_end/capture_code_context.rb +167 -50
- data/lib/dead_end/clean_document.rb +313 -0
- data/lib/dead_end/code_block.rb +3 -3
- data/lib/dead_end/code_frontier.rb +28 -17
- data/lib/dead_end/code_line.rb +160 -77
- data/lib/dead_end/code_search.rb +37 -48
- data/lib/dead_end/display_code_with_line_numbers.rb +7 -8
- data/lib/dead_end/display_invalid_blocks.rb +10 -9
- data/lib/dead_end/fyi.rb +2 -1
- data/lib/dead_end/internals.rb +23 -27
- data/lib/dead_end/lex_all.rb +16 -32
- data/lib/dead_end/lex_value.rb +62 -0
- data/lib/dead_end/parse_blocks_from_indent_line.rb +3 -4
- data/lib/dead_end/version.rb +1 -1
- data/lib/dead_end/who_dis_syntax_error.rb +14 -9
- metadata +6 -6
- data/.travis.yml +0 -6
- data/lib/dead_end/heredoc_block_parse.rb +0 -30
- data/lib/dead_end/trailing_slash_join.rb +0 -53
data/lib/dead_end/internals.rb
CHANGED
@@ -1,43 +1,41 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
|
2
|
+
|
3
3
|
# This is the top level file, but is moved to `internals`
|
4
|
-
# so the top level
|
4
|
+
# so the top level require can instead enable the "automatic" behavior
|
5
5
|
|
6
6
|
require_relative "version"
|
7
7
|
|
8
|
-
require
|
9
|
-
require
|
10
|
-
require
|
11
|
-
require
|
12
|
-
require
|
8
|
+
require "tmpdir"
|
9
|
+
require "stringio"
|
10
|
+
require "pathname"
|
11
|
+
require "ripper"
|
12
|
+
require "timeout"
|
13
13
|
|
14
14
|
module DeadEnd
|
15
15
|
class Error < StandardError; end
|
16
16
|
SEARCH_SOURCE_ON_ERROR_DEFAULT = true
|
17
|
-
TIMEOUT_DEFAULT = ENV.fetch("DEAD_END_TIMEOUT",
|
17
|
+
TIMEOUT_DEFAULT = ENV.fetch("DEAD_END_TIMEOUT", 1).to_i
|
18
18
|
|
19
19
|
def self.handle_error(e, search_source_on_error: SEARCH_SOURCE_ON_ERROR_DEFAULT)
|
20
|
-
raise e
|
20
|
+
raise e unless e.message.include?("end-of-input")
|
21
21
|
|
22
22
|
filename = e.message.split(":").first
|
23
23
|
|
24
24
|
$stderr.sync = true
|
25
|
-
|
25
|
+
warn "Run `$ dead_end #{filename}` for more options\n"
|
26
26
|
|
27
27
|
if search_source_on_error
|
28
|
-
|
28
|
+
call(
|
29
29
|
source: Pathname(filename).read,
|
30
30
|
filename: filename,
|
31
|
-
terminal: true
|
31
|
+
terminal: true
|
32
32
|
)
|
33
33
|
end
|
34
34
|
|
35
|
-
$stderr.puts ""
|
36
|
-
$stderr.puts ""
|
37
35
|
raise e
|
38
36
|
end
|
39
37
|
|
40
|
-
def self.call(source
|
38
|
+
def self.call(source:, filename:, terminal: false, record_dir: nil, timeout: TIMEOUT_DEFAULT, io: $stderr)
|
41
39
|
search = nil
|
42
40
|
Timeout.timeout(timeout) do
|
43
41
|
record_dir ||= ENV["DEBUG"] ? "tmp" : nil
|
@@ -82,13 +80,13 @@ module DeadEnd
|
|
82
80
|
# ) # => true
|
83
81
|
#
|
84
82
|
# DeadEnd.valid?(code_lines) # => false
|
85
|
-
def self.valid_without?(without_lines
|
83
|
+
def self.valid_without?(without_lines:, code_lines:)
|
86
84
|
lines = code_lines - Array(without_lines).flatten
|
87
85
|
|
88
86
|
if lines.empty?
|
89
|
-
|
87
|
+
true
|
90
88
|
else
|
91
|
-
|
89
|
+
valid?(lines)
|
92
90
|
end
|
93
91
|
end
|
94
92
|
|
@@ -137,7 +135,6 @@ module DeadEnd
|
|
137
135
|
!invalid?(source)
|
138
136
|
end
|
139
137
|
|
140
|
-
|
141
138
|
def self.invalid_type(source)
|
142
139
|
WhoDisSyntaxError.new(source).call
|
143
140
|
end
|
@@ -145,14 +142,13 @@ end
|
|
145
142
|
|
146
143
|
require_relative "code_line"
|
147
144
|
require_relative "code_block"
|
145
|
+
require_relative "code_search"
|
148
146
|
require_relative "code_frontier"
|
149
|
-
require_relative "
|
150
|
-
require_relative "around_block_scan"
|
151
|
-
require_relative "block_expand"
|
152
|
-
require_relative "parse_blocks_from_indent_line"
|
147
|
+
require_relative "clean_document"
|
153
148
|
|
154
|
-
require_relative "code_search"
|
155
|
-
require_relative "who_dis_syntax_error"
|
156
|
-
require_relative "heredoc_block_parse"
|
157
149
|
require_relative "lex_all"
|
158
|
-
require_relative "
|
150
|
+
require_relative "block_expand"
|
151
|
+
require_relative "around_block_scan"
|
152
|
+
require_relative "who_dis_syntax_error"
|
153
|
+
require_relative "display_invalid_blocks"
|
154
|
+
require_relative "parse_blocks_from_indent_line"
|
data/lib/dead_end/lex_all.rb
CHANGED
@@ -8,20 +8,24 @@ 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
|
+
end
|
26
|
+
|
27
|
+
def to_a
|
28
|
+
@lex
|
25
29
|
end
|
26
30
|
|
27
31
|
def each
|
@@ -31,34 +35,14 @@ module DeadEnd
|
|
31
35
|
end
|
32
36
|
end
|
33
37
|
|
34
|
-
def
|
35
|
-
@lex
|
38
|
+
def [](index)
|
39
|
+
@lex[index]
|
36
40
|
end
|
37
41
|
|
38
|
-
|
39
|
-
|
40
|
-
# This lex:
|
41
|
-
#
|
42
|
-
# [1, 0], :on_ident, "describe", CMDARG
|
43
|
-
#
|
44
|
-
# Would translate into:
|
45
|
-
#
|
46
|
-
# lex.line # => 1
|
47
|
-
# lex.type # => :on_indent
|
48
|
-
# lex.token # => "describe"
|
49
|
-
class LexValue
|
50
|
-
attr_reader :line, :type, :token, :state
|
51
|
-
|
52
|
-
def initialize(line, _, type, token, state)
|
53
|
-
@line = line
|
54
|
-
@type = type
|
55
|
-
@token = token
|
56
|
-
@state = state
|
57
|
-
end
|
58
|
-
|
59
|
-
def expr_label?
|
60
|
-
state.allbits?(Ripper::EXPR_LABEL)
|
61
|
-
end
|
42
|
+
def last
|
43
|
+
@lex.last
|
62
44
|
end
|
63
45
|
end
|
64
46
|
end
|
47
|
+
|
48
|
+
require_relative "lex_value"
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module DeadEnd
|
2
|
+
# Value object for accessing lex values
|
3
|
+
#
|
4
|
+
# This lex:
|
5
|
+
#
|
6
|
+
# [1, 0], :on_ident, "describe", CMDARG
|
7
|
+
#
|
8
|
+
# Would translate into:
|
9
|
+
#
|
10
|
+
# lex.line # => 1
|
11
|
+
# lex.type # => :on_indent
|
12
|
+
# lex.token # => "describe"
|
13
|
+
class LexValue
|
14
|
+
attr_reader :line, :type, :token, :state
|
15
|
+
|
16
|
+
def initialize(line, type, token, state)
|
17
|
+
@line = line
|
18
|
+
@type = type
|
19
|
+
@token = token
|
20
|
+
@state = state
|
21
|
+
|
22
|
+
set_kw_end
|
23
|
+
end
|
24
|
+
|
25
|
+
private def set_kw_end
|
26
|
+
@is_end = false
|
27
|
+
@is_kw = false
|
28
|
+
return if type != :on_kw
|
29
|
+
|
30
|
+
case token
|
31
|
+
when "if", "unless", "while", "until"
|
32
|
+
# Only count if/unless when it's not a "trailing" if/unless
|
33
|
+
# https://github.com/ruby/ruby/blob/06b44f819eb7b5ede1ff69cecb25682b56a1d60c/lib/irb/ruby-lex.rb#L374-L375
|
34
|
+
@is_kw = true unless expr_label?
|
35
|
+
when "def", "case", "for", "begin", "class", "module", "do"
|
36
|
+
@is_kw = true
|
37
|
+
when "end"
|
38
|
+
@is_end = true
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def ignore_newline?
|
43
|
+
type == :on_ignored_nl
|
44
|
+
end
|
45
|
+
|
46
|
+
def is_end?
|
47
|
+
@is_end
|
48
|
+
end
|
49
|
+
|
50
|
+
def is_kw?
|
51
|
+
@is_kw
|
52
|
+
end
|
53
|
+
|
54
|
+
def expr_beg?
|
55
|
+
state.anybits?(Ripper::EXPR_BEG)
|
56
|
+
end
|
57
|
+
|
58
|
+
def expr_label?
|
59
|
+
state.allbits?(Ripper::EXPR_LABEL)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -4,7 +4,7 @@ module DeadEnd
|
|
4
4
|
# This class is responsible for generating initial code blocks
|
5
5
|
# that will then later be expanded.
|
6
6
|
#
|
7
|
-
# The biggest concern when guessing
|
7
|
+
# The biggest concern when guessing code blocks, is accidentally
|
8
8
|
# grabbing one that contains only an "end". In this example:
|
9
9
|
#
|
10
10
|
# def dog
|
@@ -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
|
-
|
data/lib/dead_end/version.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module DeadEnd
|
4
|
-
# Determines what type of syntax error is in the source
|
4
|
+
# Determines what type of syntax error that is in the source
|
5
5
|
#
|
6
6
|
# Example:
|
7
7
|
#
|
@@ -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
|
|
@@ -51,15 +56,15 @@ module DeadEnd
|
|
51
56
|
when /unexpected end-of-input/
|
52
57
|
@error_symbol = :missing_end
|
53
58
|
when /expecting end-of-input/
|
54
|
-
@error_symbol = :unmatched_syntax
|
55
59
|
@unmatched_symbol = :end
|
56
|
-
|
57
|
-
|
60
|
+
@error_symbol = :unmatched_syntax
|
61
|
+
when /unexpected .* expecting '(?<unmatched_symbol>.*)'/
|
62
|
+
@unmatched_symbol = $1.to_sym if $1
|
63
|
+
@error_symbol = :unmatched_syntax
|
64
|
+
when /unexpected `end'/, # Ruby 2.7 and 3.0
|
65
|
+
/unexpected end/, # Ruby 2.6
|
58
66
|
/unexpected keyword_end/i # Ruby 2.5
|
59
67
|
|
60
|
-
match = @error.match(/expecting '(?<unmatched_symbol>.*)'/)
|
61
|
-
@unmatched_symbol = match[:unmatched_symbol].to_sym if match
|
62
|
-
|
63
68
|
@error_symbol = :unmatched_syntax
|
64
69
|
else
|
65
70
|
@error_symbol = :unknown
|
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:
|
4
|
+
version: 2.0.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-11 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
|
@@ -40,6 +40,7 @@ files:
|
|
40
40
|
- lib/dead_end/auto.rb
|
41
41
|
- lib/dead_end/block_expand.rb
|
42
42
|
- lib/dead_end/capture_code_context.rb
|
43
|
+
- lib/dead_end/clean_document.rb
|
43
44
|
- lib/dead_end/code_block.rb
|
44
45
|
- lib/dead_end/code_frontier.rb
|
45
46
|
- lib/dead_end/code_line.rb
|
@@ -47,11 +48,10 @@ files:
|
|
47
48
|
- lib/dead_end/display_code_with_line_numbers.rb
|
48
49
|
- lib/dead_end/display_invalid_blocks.rb
|
49
50
|
- lib/dead_end/fyi.rb
|
50
|
-
- lib/dead_end/heredoc_block_parse.rb
|
51
51
|
- lib/dead_end/internals.rb
|
52
52
|
- lib/dead_end/lex_all.rb
|
53
|
+
- lib/dead_end/lex_value.rb
|
53
54
|
- lib/dead_end/parse_blocks_from_indent_line.rb
|
54
|
-
- lib/dead_end/trailing_slash_join.rb
|
55
55
|
- lib/dead_end/version.rb
|
56
56
|
- lib/dead_end/who_dis_syntax_error.rb
|
57
57
|
homepage: https://github.com/zombocom/dead_end.git
|
@@ -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
|
data/.travis.yml
DELETED
@@ -1,30 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module DeadEnd
|
4
|
-
# Takes in a source, and returns blocks containing each heredoc
|
5
|
-
class HeredocBlockParse
|
6
|
-
private; attr_reader :code_lines, :lex; public
|
7
|
-
|
8
|
-
def initialize(source:, code_lines: )
|
9
|
-
@code_lines = code_lines
|
10
|
-
@lex = LexAll.new(source: source)
|
11
|
-
end
|
12
|
-
|
13
|
-
def call
|
14
|
-
blocks = []
|
15
|
-
beginning = []
|
16
|
-
@lex.each do |lex|
|
17
|
-
case lex.type
|
18
|
-
when :on_heredoc_beg
|
19
|
-
beginning << lex.line
|
20
|
-
when :on_heredoc_end
|
21
|
-
start_index = beginning.pop - 1
|
22
|
-
end_index = lex.line - 1
|
23
|
-
blocks << CodeBlock.new(lines: code_lines[start_index..end_index])
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
blocks
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
@@ -1,53 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module DeadEnd
|
4
|
-
# Handles code that contains trailing slashes
|
5
|
-
# by turning multiple lines with trailing slash(es) into
|
6
|
-
# a single code line
|
7
|
-
#
|
8
|
-
# expect(code_lines.join).to eq(<<~EOM)
|
9
|
-
# it "trailing \
|
10
|
-
# "slash" do
|
11
|
-
# end
|
12
|
-
# EOM
|
13
|
-
#
|
14
|
-
# lines = TrailngSlashJoin(code_lines: code_lines).call
|
15
|
-
# expect(lines.first.to_s).to eq(<<~EOM)
|
16
|
-
# it "trailing \
|
17
|
-
# "slash" do
|
18
|
-
# EOM
|
19
|
-
#
|
20
|
-
class TrailingSlashJoin
|
21
|
-
def initialize(code_lines:)
|
22
|
-
@code_lines = code_lines
|
23
|
-
@code_lines_dup = code_lines.dup
|
24
|
-
end
|
25
|
-
|
26
|
-
def call
|
27
|
-
@trailing_lines = []
|
28
|
-
@code_lines.select(&:trailing_slash?).each do |trailing|
|
29
|
-
stop_next = false
|
30
|
-
lines = @code_lines[trailing.index..-1].take_while do |line|
|
31
|
-
next false if stop_next
|
32
|
-
|
33
|
-
if !line.trailing_slash?
|
34
|
-
stop_next = true
|
35
|
-
end
|
36
|
-
|
37
|
-
true
|
38
|
-
end
|
39
|
-
|
40
|
-
joined_line = CodeLine.new(line: lines.map(&:original_line).join, index: trailing.index)
|
41
|
-
|
42
|
-
@code_lines_dup[trailing.index] = joined_line
|
43
|
-
|
44
|
-
@trailing_lines << joined_line
|
45
|
-
|
46
|
-
lines.shift # Don't hide first trailing slash line
|
47
|
-
lines.each(&:mark_invisible)
|
48
|
-
end
|
49
|
-
|
50
|
-
return @code_lines_dup
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|