dead_end 3.0.0 → 3.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -0
- data/Gemfile +1 -0
- data/Gemfile.lock +4 -2
- data/README.md +21 -1
- data/lib/dead_end/clean_document.rb +19 -28
- data/lib/dead_end/cli.rb +16 -5
- data/lib/dead_end/code_frontier.rb +11 -11
- data/lib/dead_end/code_search.rb +1 -2
- data/lib/dead_end/insertion_sort.rb +46 -0
- data/lib/dead_end/version.rb +1 -1
- data/lib/dead_end.rb +11 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c232ccfce002bd3a79d6a5508ea46b27684a9a0ae2bb5d6f843d49e5ba59c0ac
|
4
|
+
data.tar.gz: 745847314c5c3ff09cb85b525a5d583f89c1c597e1f5b785ed54e01299bb72c4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c49f5b58a7a2f222606a01efc3256a2a0eceade66ff6e46f7c15172cf972a48aa4b6d55212eb714ad69dde1b6d80e91cf8589a9b7026ad2b3e9baafabb8c2e6c
|
7
|
+
data.tar.gz: f70fc9947a92d8de2d8f05e08ab803a791c9fc396f03a0bed812b0a88bdb74af33658b23ebdf148adbc716cc41870bee8fabe57c3ae62bd6c9e9b063717f0bbc
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,9 @@
|
|
1
1
|
## HEAD (unreleased)
|
2
2
|
|
3
|
+
## 3.0.1
|
4
|
+
|
5
|
+
- Fix CLI parsing when flags come before filename (https://github.com/zombocom/dead_end/pull/102)
|
6
|
+
|
3
7
|
## 3.0.0
|
4
8
|
|
5
9
|
- [Breaking] Remove previously deprecated `require "dead_end/fyi"` interface (https://github.com/zombocom/dead_end/pull/94)
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
dead_end (3.0.
|
4
|
+
dead_end (3.0.1)
|
5
5
|
|
6
6
|
GEM
|
7
7
|
remote: https://rubygems.org/
|
@@ -42,6 +42,7 @@ GEM
|
|
42
42
|
rubocop-performance (1.11.5)
|
43
43
|
rubocop (>= 1.7.0, < 2.0)
|
44
44
|
rubocop-ast (>= 0.4.0)
|
45
|
+
ruby-prof (1.4.3)
|
45
46
|
ruby-progressbar (1.11.0)
|
46
47
|
stackprof (0.2.16)
|
47
48
|
standard (1.3.0)
|
@@ -56,8 +57,9 @@ DEPENDENCIES
|
|
56
57
|
dead_end!
|
57
58
|
rake (~> 12.0)
|
58
59
|
rspec (~> 3.0)
|
60
|
+
ruby-prof
|
59
61
|
stackprof
|
60
62
|
standard
|
61
63
|
|
62
64
|
BUNDLED WITH
|
63
|
-
2.2.
|
65
|
+
2.2.30
|
data/README.md
CHANGED
@@ -170,7 +170,27 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
|
|
170
170
|
You can see changes to output against a variety of invalid code by running specs and using the `DEBUG_DISPLAY=1` environment variable. For example:
|
171
171
|
|
172
172
|
```
|
173
|
-
$ DEBUG_DISPLAY=1
|
173
|
+
$ DEBUG_DISPLAY=1 bundle exec rspec spec/ --format=failures
|
174
|
+
```
|
175
|
+
|
176
|
+
### Run profiler
|
177
|
+
|
178
|
+
You can output profiler data to the `tmp` directory by running:
|
179
|
+
|
180
|
+
```
|
181
|
+
$ DEBUG_PERF=1 bundle exec rspec spec/integration/dead_end_spec.rb
|
182
|
+
```
|
183
|
+
|
184
|
+
Some outputs are in text format, some are html, the raw marshaled data is available in `raw.rb.marshal`. See https://ruby-prof.github.io/#reports for more info. One interesting one, is the "kcachegrind" interface. To view this on mac:
|
185
|
+
|
186
|
+
```
|
187
|
+
$ brew install qcachegrind
|
188
|
+
```
|
189
|
+
|
190
|
+
Open:
|
191
|
+
|
192
|
+
```
|
193
|
+
$ qcachegrind tmp/last/profile.callgrind.out.<numbers>
|
174
194
|
```
|
175
195
|
|
176
196
|
## Contributing
|
@@ -85,17 +85,16 @@ module DeadEnd
|
|
85
85
|
#
|
86
86
|
class CleanDocument
|
87
87
|
def initialize(source:)
|
88
|
-
@source = source
|
88
|
+
@source = clean_sweep(source: source)
|
89
89
|
@document = CodeLine.from_source(@source)
|
90
90
|
end
|
91
91
|
|
92
92
|
# Call all of the document "cleaners"
|
93
93
|
# and return self
|
94
94
|
def call
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
.join_heredoc!
|
95
|
+
join_trailing_slash!
|
96
|
+
join_consecutive!
|
97
|
+
join_heredoc!
|
99
98
|
|
100
99
|
self
|
101
100
|
end
|
@@ -122,17 +121,15 @@ module DeadEnd
|
|
122
121
|
# puts "world"
|
123
122
|
# EOM
|
124
123
|
#
|
125
|
-
# lines = CleanDocument.new(source: source).
|
124
|
+
# lines = CleanDocument.new(source: source).lines
|
126
125
|
# expect(lines[0].to_s).to eq("\n")
|
127
126
|
# expect(lines[1].to_s).to eq("puts "hello")
|
128
127
|
# expect(lines[2].to_s).to eq("\n")
|
129
128
|
# expect(lines[3].to_s).to eq("puts "world")
|
130
129
|
#
|
131
|
-
#
|
132
|
-
# If you run this after any of the "join" commands, they
|
133
|
-
# will be un-joined.
|
130
|
+
# Important: This must be done before lexing.
|
134
131
|
#
|
135
|
-
# After this change is made, we
|
132
|
+
# After this change is made, we lex the document because
|
136
133
|
# removing comments can change how the doc is parsed.
|
137
134
|
#
|
138
135
|
# For example:
|
@@ -142,7 +139,9 @@ module DeadEnd
|
|
142
139
|
# # comment
|
143
140
|
# where(name: 'schneems')
|
144
141
|
# EOM
|
145
|
-
# expect(
|
142
|
+
# expect(
|
143
|
+
# values.count {|v| v.type == :on_ignored_nl}
|
144
|
+
# ).to eq(1)
|
146
145
|
#
|
147
146
|
# After the comment is removed:
|
148
147
|
#
|
@@ -151,26 +150,18 @@ module DeadEnd
|
|
151
150
|
#
|
152
151
|
# where(name: 'schneems')
|
153
152
|
# EOM
|
154
|
-
# expect(
|
153
|
+
# expect(
|
154
|
+
# values.count {|v| v.type == :on_ignored_nl}
|
155
|
+
# ).to eq(2)
|
155
156
|
#
|
156
|
-
def clean_sweep
|
157
|
-
source
|
158
|
-
#
|
159
|
-
|
160
|
-
|
157
|
+
def clean_sweep(source:)
|
158
|
+
source.lines.map do |line|
|
159
|
+
if line.match?(/^\s*(#[^{].*)?$/) # https://rubular.com/r/LLE10D8HKMkJvs
|
160
|
+
$/
|
161
|
+
else
|
162
|
+
line
|
161
163
|
end
|
162
|
-
|
163
|
-
# Remove comments
|
164
|
-
if code_line.lex.detect { |lex| lex.type != :on_sp }&.type == :on_comment
|
165
|
-
next CodeLine.new(line: "\n", index: code_line.index, lex: [])
|
166
|
-
end
|
167
|
-
|
168
|
-
code_line
|
169
164
|
end.join
|
170
|
-
|
171
|
-
@source = source
|
172
|
-
@document = CodeLine.from_source(source)
|
173
|
-
self
|
174
165
|
end
|
175
166
|
|
176
167
|
# Smushes all heredoc lines into one line
|
data/lib/dead_end/cli.rb
CHANGED
@@ -12,7 +12,7 @@ module DeadEnd
|
|
12
12
|
# Cli.new(argv: ["<path/to/file>.rb", "--terminal"]).call
|
13
13
|
#
|
14
14
|
class Cli
|
15
|
-
attr_accessor :options
|
15
|
+
attr_accessor :options
|
16
16
|
|
17
17
|
# ARGV is Everything passed to the executable, does not include executable name
|
18
18
|
#
|
@@ -26,22 +26,33 @@ module DeadEnd
|
|
26
26
|
|
27
27
|
@io = io
|
28
28
|
@argv = argv
|
29
|
-
@file_name = argv[0]
|
30
29
|
@exit_obj = exit_obj
|
31
30
|
end
|
32
31
|
|
33
32
|
def call
|
34
|
-
if
|
33
|
+
if @argv.empty?
|
35
34
|
# Display help if raw command
|
36
35
|
parser.parse! %w[--help]
|
36
|
+
return
|
37
37
|
else
|
38
|
+
# Mutates @argv
|
38
39
|
parse
|
40
|
+
return if options[:exit]
|
39
41
|
end
|
40
42
|
|
41
|
-
|
42
|
-
|
43
|
+
file_name = @argv.first
|
44
|
+
if file_name.nil?
|
45
|
+
@io.puts "No file given"
|
46
|
+
@exit_obj.exit(1)
|
47
|
+
return
|
48
|
+
end
|
43
49
|
|
44
50
|
file = Pathname(file_name)
|
51
|
+
if !file.exist?
|
52
|
+
@io.puts "file not found: #{file.expand_path} "
|
53
|
+
@exit_obj.exit(1)
|
54
|
+
return
|
55
|
+
end
|
45
56
|
|
46
57
|
@io.puts "Record dir: #{options[:record_dir]}" if options[:record_dir]
|
47
58
|
|
@@ -52,14 +52,14 @@ module DeadEnd
|
|
52
52
|
class CodeFrontier
|
53
53
|
def initialize(code_lines:)
|
54
54
|
@code_lines = code_lines
|
55
|
-
@frontier =
|
55
|
+
@frontier = InsertionSort.new
|
56
56
|
@unvisited_lines = @code_lines.sort_by(&:indent_index)
|
57
57
|
@has_run = false
|
58
58
|
@check_next = true
|
59
59
|
end
|
60
60
|
|
61
61
|
def count
|
62
|
-
@frontier.
|
62
|
+
@frontier.to_a.length
|
63
63
|
end
|
64
64
|
|
65
65
|
# Performance optimization
|
@@ -89,7 +89,7 @@ module DeadEnd
|
|
89
89
|
def holds_all_syntax_errors?(block_array = @frontier, can_cache: true)
|
90
90
|
return false if can_cache && can_skip_check?
|
91
91
|
|
92
|
-
without_lines = block_array.flat_map do |block|
|
92
|
+
without_lines = block_array.to_a.flat_map do |block|
|
93
93
|
block.lines
|
94
94
|
end
|
95
95
|
|
@@ -101,7 +101,7 @@ module DeadEnd
|
|
101
101
|
|
102
102
|
# Returns a code block with the largest indentation possible
|
103
103
|
def pop
|
104
|
-
@frontier.pop
|
104
|
+
@frontier.to_a.pop
|
105
105
|
end
|
106
106
|
|
107
107
|
def next_indent_line
|
@@ -109,15 +109,15 @@ module DeadEnd
|
|
109
109
|
end
|
110
110
|
|
111
111
|
def expand?
|
112
|
-
return false if @frontier.empty?
|
113
|
-
return true if @unvisited_lines.empty?
|
112
|
+
return false if @frontier.to_a.empty?
|
113
|
+
return true if @unvisited_lines.to_a.empty?
|
114
114
|
|
115
|
-
frontier_indent = @frontier.last.current_indent
|
115
|
+
frontier_indent = @frontier.to_a.last.current_indent
|
116
116
|
unvisited_indent = next_indent_line.indent
|
117
117
|
|
118
118
|
if ENV["DEBUG"]
|
119
119
|
puts "```"
|
120
|
-
puts @frontier.last.to_s
|
120
|
+
puts @frontier.to_a.last.to_s
|
121
121
|
puts "```"
|
122
122
|
puts " @frontier indent: #{frontier_indent}"
|
123
123
|
puts " @unvisited indent: #{unvisited_indent}"
|
@@ -141,13 +141,13 @@ module DeadEnd
|
|
141
141
|
register_indent_block(block)
|
142
142
|
|
143
143
|
# Make sure we don't double expand, if a code block fully engulfs another code block, keep the bigger one
|
144
|
-
@frontier.reject! { |b|
|
144
|
+
@frontier.to_a.reject! { |b|
|
145
145
|
b.starts_at >= block.starts_at && b.ends_at <= block.ends_at
|
146
146
|
}
|
147
147
|
|
148
148
|
@check_next = true if block.invalid?
|
149
149
|
@frontier << block
|
150
|
-
@frontier.sort!
|
150
|
+
# @frontier.sort!
|
151
151
|
|
152
152
|
self
|
153
153
|
end
|
@@ -167,7 +167,7 @@ module DeadEnd
|
|
167
167
|
# Given that we know our syntax error exists somewhere in our frontier, we want to find
|
168
168
|
# the smallest possible set of blocks that contain all the syntax errors
|
169
169
|
def detect_invalid_blocks
|
170
|
-
self.class.combination(@frontier.select(&:invalid?)).detect do |block_array|
|
170
|
+
self.class.combination(@frontier.to_a.select(&:invalid?)).detect do |block_array|
|
171
171
|
holds_all_syntax_errors?(block_array, can_cache: false)
|
172
172
|
end || []
|
173
173
|
end
|
data/lib/dead_end/code_search.rb
CHANGED
@@ -43,8 +43,7 @@ module DeadEnd
|
|
43
43
|
|
44
44
|
def initialize(source, record_dir: ENV["DEAD_END_RECORD_DIR"] || ENV["DEBUG"] ? "tmp" : nil)
|
45
45
|
if record_dir
|
46
|
-
@
|
47
|
-
@record_dir = Pathname(record_dir).join(@time).tap { |p| p.mkpath }
|
46
|
+
@record_dir = DeadEnd.record_dir(record_dir)
|
48
47
|
@write_count = 0
|
49
48
|
end
|
50
49
|
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DeadEnd
|
4
|
+
# Sort elements on insert
|
5
|
+
#
|
6
|
+
# Instead of constantly calling `sort!`, put
|
7
|
+
# the element where it belongs the first time
|
8
|
+
# around
|
9
|
+
#
|
10
|
+
# Example:
|
11
|
+
#
|
12
|
+
# sorted = InsertionSort.new
|
13
|
+
# sorted << 33
|
14
|
+
# sorted << 44
|
15
|
+
# sorted << 1
|
16
|
+
# puts sorted.to_a
|
17
|
+
# # => [1, 44, 33]
|
18
|
+
#
|
19
|
+
class InsertionSort
|
20
|
+
def initialize
|
21
|
+
@array = []
|
22
|
+
end
|
23
|
+
|
24
|
+
def <<(value)
|
25
|
+
insert_in = @array.length
|
26
|
+
@array.each.with_index do |existing, index|
|
27
|
+
case value <=> existing
|
28
|
+
when -1
|
29
|
+
insert_in = index
|
30
|
+
break
|
31
|
+
when 0
|
32
|
+
insert_in = index
|
33
|
+
break
|
34
|
+
when 1
|
35
|
+
# Keep going
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
@array.insert(insert_in, value)
|
40
|
+
end
|
41
|
+
|
42
|
+
def to_a
|
43
|
+
@array
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
data/lib/dead_end/version.rb
CHANGED
data/lib/dead_end.rb
CHANGED
@@ -28,6 +28,16 @@ module DeadEnd
|
|
28
28
|
raise e
|
29
29
|
end
|
30
30
|
|
31
|
+
def self.record_dir(dir)
|
32
|
+
time = Time.now.strftime("%Y-%m-%d-%H-%M-%s-%N")
|
33
|
+
dir = Pathname(dir)
|
34
|
+
symlink = dir.join("last").tap { |path| path.delete if path.exist? }
|
35
|
+
dir.join(time).tap { |path|
|
36
|
+
path.mkpath
|
37
|
+
FileUtils.symlink(path.basename, symlink)
|
38
|
+
}
|
39
|
+
end
|
40
|
+
|
31
41
|
def self.call(source:, filename: DEFAULT_VALUE, terminal: DEFAULT_VALUE, record_dir: nil, timeout: TIMEOUT_DEFAULT, io: $stderr)
|
32
42
|
search = nil
|
33
43
|
filename = nil if filename == DEFAULT_VALUE
|
@@ -137,6 +147,7 @@ require_relative "dead_end/clean_document"
|
|
137
147
|
|
138
148
|
require_relative "dead_end/lex_all"
|
139
149
|
require_relative "dead_end/block_expand"
|
150
|
+
require_relative "dead_end/insertion_sort"
|
140
151
|
require_relative "dead_end/around_block_scan"
|
141
152
|
require_relative "dead_end/ripper_errors"
|
142
153
|
require_relative "dead_end/display_invalid_blocks"
|
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: 3.0.
|
4
|
+
version: 3.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-
|
11
|
+
date: 2021-11-04 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
|
@@ -49,6 +49,7 @@ files:
|
|
49
49
|
- lib/dead_end/display_code_with_line_numbers.rb
|
50
50
|
- lib/dead_end/display_invalid_blocks.rb
|
51
51
|
- lib/dead_end/explain_syntax.rb
|
52
|
+
- lib/dead_end/insertion_sort.rb
|
52
53
|
- lib/dead_end/left_right_lex_count.rb
|
53
54
|
- lib/dead_end/lex_all.rb
|
54
55
|
- lib/dead_end/lex_value.rb
|