dead_end 1.1.6 → 2.0.1
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 -0
- data/.standard.yml +1 -0
- data/CHANGELOG.md +24 -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 +12 -4
- data/lib/dead_end/around_block_scan.rb +16 -18
- data/lib/dead_end/auto.rb +3 -52
- data/lib/dead_end/banner.rb +58 -0
- 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 +18 -48
- data/lib/dead_end/fyi.rb +2 -1
- data/lib/dead_end/internals.rb +23 -27
- data/lib/dead_end/lex_all.rb +15 -31
- 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 +22 -8
- metadata +7 -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
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative "banner"
|
3
4
|
require_relative "capture_code_context"
|
4
5
|
require_relative "display_code_with_line_numbers"
|
5
6
|
|
@@ -8,7 +9,7 @@ module DeadEnd
|
|
8
9
|
class DisplayInvalidBlocks
|
9
10
|
attr_reader :filename
|
10
11
|
|
11
|
-
def initialize(code_lines
|
12
|
+
def initialize(code_lines:, blocks:, io: $stderr, filename: nil, terminal: false, invalid_obj: WhoDisSyntaxError::Null.new)
|
12
13
|
@terminal = terminal
|
13
14
|
@filename = filename
|
14
15
|
@io = io
|
@@ -21,11 +22,15 @@ module DeadEnd
|
|
21
22
|
@invalid_obj = invalid_obj
|
22
23
|
end
|
23
24
|
|
25
|
+
def document_ok?
|
26
|
+
@blocks.none? { |b| !b.hidden? }
|
27
|
+
end
|
28
|
+
|
24
29
|
def call
|
25
|
-
if
|
26
|
-
found_invalid_blocks
|
27
|
-
else
|
30
|
+
if document_ok?
|
28
31
|
@io.puts "Syntax OK"
|
32
|
+
else
|
33
|
+
found_invalid_blocks
|
29
34
|
end
|
30
35
|
self
|
31
36
|
end
|
@@ -37,8 +42,10 @@ module DeadEnd
|
|
37
42
|
|
38
43
|
private def found_invalid_blocks
|
39
44
|
@io.puts
|
40
|
-
|
41
|
-
|
45
|
+
if banner
|
46
|
+
@io.puts banner
|
47
|
+
@io.puts
|
48
|
+
end
|
42
49
|
@io.puts("file: #{filename}") if filename
|
43
50
|
@io.puts <<~EOM
|
44
51
|
simplified:
|
@@ -48,52 +55,15 @@ module DeadEnd
|
|
48
55
|
end
|
49
56
|
|
50
57
|
def banner
|
51
|
-
|
52
|
-
when :missing_end
|
53
|
-
<<~EOM
|
54
|
-
DeadEnd: Missing `end` detected
|
55
|
-
|
56
|
-
This code has a missing `end`. Ensure that all
|
57
|
-
syntax keywords (`def`, `do`, etc.) have a matching `end`.
|
58
|
-
EOM
|
59
|
-
when :unmatched_syntax
|
60
|
-
case @invalid_obj.unmatched_symbol
|
61
|
-
when :end
|
62
|
-
<<~EOM
|
63
|
-
DeadEnd: Unmatched `end` detected
|
64
|
-
|
65
|
-
This code has an unmatched `end`. Ensure that all `end` lines
|
66
|
-
in your code have a matching syntax keyword (`def`, `do`, etc.)
|
67
|
-
and that you don't have any extra `end` lines.
|
68
|
-
EOM
|
69
|
-
when :|
|
70
|
-
<<~EOM
|
71
|
-
DeadEnd: Unmatched `|` character detected
|
72
|
-
|
73
|
-
Example:
|
74
|
-
|
75
|
-
`do |x` should be `do |x|`
|
76
|
-
EOM
|
77
|
-
when :"}"
|
78
|
-
<<~EOM
|
79
|
-
DeadEnd: Unmatched `}` character detected
|
80
|
-
|
81
|
-
This code has an unmatched `}`. Ensure that opening curl braces are
|
82
|
-
closed: `{ }`.
|
83
|
-
EOM
|
84
|
-
else
|
85
|
-
"DeadEnd: Unmatched `#{@invalid_obj.unmatched_symbol}` detected"
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
58
|
+
Banner.new(invalid_obj: @invalid_obj).call
|
89
59
|
end
|
90
60
|
|
91
61
|
def indent(string, with: " ")
|
92
|
-
string.each_line.map {|l| with
|
62
|
+
string.each_line.map { |l| with + l }.join
|
93
63
|
end
|
94
64
|
|
95
65
|
def code_block
|
96
|
-
string =
|
66
|
+
string = +""
|
97
67
|
string << code_with_context
|
98
68
|
string
|
99
69
|
end
|
@@ -107,7 +77,7 @@ module DeadEnd
|
|
107
77
|
DisplayCodeWithLineNumbers.new(
|
108
78
|
lines: lines,
|
109
79
|
terminal: @terminal,
|
110
|
-
highlight_lines: @invalid_lines
|
80
|
+
highlight_lines: @invalid_lines
|
111
81
|
).call
|
112
82
|
end
|
113
83
|
|
@@ -115,7 +85,7 @@ module DeadEnd
|
|
115
85
|
DisplayCodeWithLineNumbers.new(
|
116
86
|
lines: @code_lines.select(&:visible?),
|
117
87
|
terminal: @terminal,
|
118
|
-
highlight_lines: @invalid_lines
|
88
|
+
highlight_lines: @invalid_lines
|
119
89
|
).call
|
120
90
|
end
|
121
91
|
end
|
data/lib/dead_end/fyi.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
require_relative "../dead_end/internals"
|
2
2
|
|
3
|
-
require_relative "auto
|
3
|
+
require_relative "auto"
|
4
4
|
|
5
5
|
DeadEnd.send(:remove_const, :SEARCH_SOURCE_ON_ERROR_DEFAULT)
|
6
6
|
DeadEnd::SEARCH_SOURCE_ON_ERROR_DEFAULT = false
|
7
7
|
|
8
|
+
warn "DEPRECATED: calling `require 'dead_end/fyi'` is deprecated, `require 'dead_end'` instead"
|
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
18
|
lines = source_lines[lineno..-1]
|
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,16 +1,22 @@
|
|
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
|
#
|
8
8
|
# puts WhoDisSyntaxError.new("def foo;").call.error_symbol
|
9
9
|
# # => :missing_end
|
10
10
|
class WhoDisSyntaxError < Ripper
|
11
|
+
CHARACTERS = {"{": :"}", "}": :"{", "[": :"]", "]": :"[", "(": :")", ")": :"("}
|
11
12
|
class Null
|
12
|
-
def error_symbol
|
13
|
-
|
13
|
+
def error_symbol
|
14
|
+
:missing_end
|
15
|
+
end
|
16
|
+
|
17
|
+
def unmatched_symbol
|
18
|
+
:end
|
19
|
+
end
|
14
20
|
end
|
15
21
|
attr_reader :error, :run_once
|
16
22
|
|
@@ -53,12 +59,20 @@ module DeadEnd
|
|
53
59
|
when /expecting end-of-input/
|
54
60
|
@unmatched_symbol = :end
|
55
61
|
@error_symbol = :unmatched_syntax
|
56
|
-
when /unexpected .* expecting '(?<unmatched_symbol
|
57
|
-
|
62
|
+
when /unexpected .* expecting ['`]?(?<unmatched_symbol>[^']*)/
|
63
|
+
if $1
|
64
|
+
character = $1.to_sym
|
65
|
+
@unmatched_symbol = CHARACTERS[character] || character
|
66
|
+
@unmatched_symbol = :end if @unmatched_symbol == :keyword_end
|
67
|
+
end
|
68
|
+
@error_symbol = :unmatched_syntax
|
69
|
+
when /unexpected '(?<unmatched_symbol>.*)'/
|
70
|
+
@unmatched_symbol = $1.to_sym
|
71
|
+
@unmatched_symbol = :end if @unmatched_symbol == :keyword_end
|
58
72
|
@error_symbol = :unmatched_syntax
|
59
|
-
when /unexpected `end'/,
|
60
|
-
/unexpected end/,
|
61
|
-
/unexpected keyword_end/i
|
73
|
+
when /unexpected `end'/, # Ruby 2.7 and 3.0
|
74
|
+
/unexpected end/, # Ruby 2.6
|
75
|
+
/unexpected keyword_end/i # Ruby 2.5
|
62
76
|
|
63
77
|
@error_symbol = :unmatched_syntax
|
64
78
|
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:
|
4
|
+
version: 2.0.1
|
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-19 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
|
@@ -38,8 +38,10 @@ files:
|
|
38
38
|
- lib/dead_end.rb
|
39
39
|
- lib/dead_end/around_block_scan.rb
|
40
40
|
- lib/dead_end/auto.rb
|
41
|
+
- lib/dead_end/banner.rb
|
41
42
|
- lib/dead_end/block_expand.rb
|
42
43
|
- lib/dead_end/capture_code_context.rb
|
44
|
+
- lib/dead_end/clean_document.rb
|
43
45
|
- lib/dead_end/code_block.rb
|
44
46
|
- lib/dead_end/code_frontier.rb
|
45
47
|
- lib/dead_end/code_line.rb
|
@@ -47,11 +49,10 @@ files:
|
|
47
49
|
- lib/dead_end/display_code_with_line_numbers.rb
|
48
50
|
- lib/dead_end/display_invalid_blocks.rb
|
49
51
|
- lib/dead_end/fyi.rb
|
50
|
-
- lib/dead_end/heredoc_block_parse.rb
|
51
52
|
- lib/dead_end/internals.rb
|
52
53
|
- lib/dead_end/lex_all.rb
|
54
|
+
- lib/dead_end/lex_value.rb
|
53
55
|
- lib/dead_end/parse_blocks_from_indent_line.rb
|
54
|
-
- lib/dead_end/trailing_slash_join.rb
|
55
56
|
- lib/dead_end/version.rb
|
56
57
|
- lib/dead_end/who_dis_syntax_error.rb
|
57
58
|
homepage: https://github.com/zombocom/dead_end.git
|
@@ -75,7 +76,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
75
76
|
- !ruby/object:Gem::Version
|
76
77
|
version: '0'
|
77
78
|
requirements: []
|
78
|
-
rubygems_version: 3.2.
|
79
|
+
rubygems_version: 3.2.22
|
79
80
|
signing_key:
|
80
81
|
specification_version: 4
|
81
82
|
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
|