dead_end 2.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/.circleci/config.yml +9 -0
- data/.github/workflows/check_changelog.yml +14 -7
- data/.standard.yml +1 -1
- data/CHANGELOG.md +26 -0
- data/Gemfile +1 -0
- data/Gemfile.lock +4 -2
- data/README.md +109 -21
- data/exe/dead_end +3 -66
- data/lib/dead_end/around_block_scan.rb +1 -1
- data/lib/dead_end/auto.rb +1 -21
- data/lib/dead_end/clean_document.rb +23 -32
- data/lib/dead_end/cli.rb +129 -0
- data/lib/dead_end/code_block.rb +18 -2
- data/lib/dead_end/code_frontier.rb +39 -13
- data/lib/dead_end/code_search.rb +7 -7
- data/lib/dead_end/display_invalid_blocks.rb +39 -78
- data/lib/dead_end/explain_syntax.rb +103 -0
- data/lib/dead_end/insertion_sort.rb +46 -0
- data/lib/dead_end/left_right_lex_count.rb +157 -0
- data/lib/dead_end/lex_all.rb +1 -1
- data/lib/dead_end/ripper_errors.rb +30 -0
- data/lib/dead_end/version.rb +1 -1
- data/lib/dead_end.rb +156 -1
- metadata +7 -5
- data/lib/dead_end/fyi.rb +0 -8
- data/lib/dead_end/internals.rb +0 -154
- data/lib/dead_end/who_dis_syntax_error.rb +0 -74
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/.circleci/config.yml
CHANGED
@@ -13,6 +13,14 @@ references:
|
|
13
13
|
command: bundle exec standardrb
|
14
14
|
|
15
15
|
jobs:
|
16
|
+
"ruby-2-5":
|
17
|
+
docker:
|
18
|
+
- image: circleci/ruby:2.5
|
19
|
+
steps:
|
20
|
+
- checkout
|
21
|
+
- ruby/install-deps
|
22
|
+
- <<: *unit
|
23
|
+
|
16
24
|
"ruby-2-6":
|
17
25
|
docker:
|
18
26
|
- image: circleci/ruby:2.6
|
@@ -49,6 +57,7 @@ workflows:
|
|
49
57
|
version: 2
|
50
58
|
build:
|
51
59
|
jobs:
|
60
|
+
- "ruby-2-5"
|
52
61
|
- "ruby-2-6"
|
53
62
|
- "ruby-2-7"
|
54
63
|
- "ruby-3-0"
|
@@ -1,13 +1,20 @@
|
|
1
1
|
name: Check Changelog
|
2
2
|
|
3
3
|
on:
|
4
|
-
|
5
|
-
|
4
|
+
pull_request:
|
5
|
+
types: [opened, reopened, edited, labeled, unlabeled, synchronize]
|
6
|
+
|
6
7
|
jobs:
|
7
|
-
|
8
|
+
check-changelog:
|
8
9
|
runs-on: ubuntu-latest
|
10
|
+
if: |
|
11
|
+
!contains(github.event.pull_request.body, '[skip changelog]') &&
|
12
|
+
!contains(github.event.pull_request.body, '[changelog skip]') &&
|
13
|
+
!contains(github.event.pull_request.body, '[skip ci]') &&
|
14
|
+
!contains(github.event.pull_request.labels.*.name, 'skip changelog')
|
9
15
|
steps:
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
16
|
+
- uses: actions/checkout@v2.3.5
|
17
|
+
- name: Check that CHANGELOG is touched
|
18
|
+
run: |
|
19
|
+
git fetch origin ${{ github.base_ref }} --depth 1 && \
|
20
|
+
git diff remotes/origin/${{ github.base_ref }} --name-only | grep CHANGELOG.md
|
data/.standard.yml
CHANGED
@@ -1 +1 @@
|
|
1
|
-
ruby_version: 2.
|
1
|
+
ruby_version: 2.5.9
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,31 @@
|
|
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
|
+
|
7
|
+
## 3.0.0
|
8
|
+
|
9
|
+
- [Breaking] Remove previously deprecated `require "dead_end/fyi"` interface (https://github.com/zombocom/dead_end/pull/94)
|
10
|
+
- Fix double output bug (https://github.com/zombocom/dead_end/pull/99)
|
11
|
+
- Fix bug causing poor results (fix #95, fix #88) (https://github.com/zombocom/dead_end/pull/96)
|
12
|
+
- DeadEnd is now fired on EVERY syntax error (https://github.com/zombocom/dead_end/pull/94)
|
13
|
+
- Output format changes:
|
14
|
+
- Parse errors emitted per-block rather than for the whole document (https://github.com/zombocom/dead_end/pull/94)
|
15
|
+
- The "banner" is now based on lexical analysis rather than parser regex (fix #68, fix #87) (https://github.com/zombocom/dead_end/pull/96)
|
16
|
+
|
17
|
+
## 2.0.2
|
18
|
+
|
19
|
+
- Don't print terminal color codes when output is not tty (https://github.com/zombocom/dead_end/pull/91)
|
20
|
+
|
21
|
+
## 2.0.1
|
22
|
+
|
23
|
+
- Reintroduce Ruby 2.5 support (https://github.com/zombocom/dead_end/pull/90)
|
24
|
+
- Support naked braces/brackets/parens, invert labels on banner (https://github.com/zombocom/dead_end/pull/89)
|
25
|
+
- Handle mismatched end when using rescue without begin (https://github.com/zombocom/dead_end/pull/83)
|
26
|
+
- CLI returns non-zero exit code when syntax error is found (https://github.com/zombocom/dead_end/pull/86)
|
27
|
+
- Let -v respond with gem version instead of 'unknown' (https://github.com/zombocom/dead_end/pull/82)
|
28
|
+
|
3
29
|
## 2.0.0
|
4
30
|
|
5
31
|
- Support "endless" oneline method definitions for Ruby 3+ (https://github.com/zombocom/dead_end/pull/80)
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
dead_end (
|
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
@@ -2,19 +2,14 @@
|
|
2
2
|
|
3
3
|
An error in your code forces you to stop. DeadEnd helps you find those errors to get you back on your way faster.
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
This code has an unmatched `end`. Ensure that all `end` lines
|
8
|
-
in your code have a matching syntax keyword (`def`, `do`, etc.)
|
9
|
-
and that you don't have any extra `end` lines.
|
10
|
-
|
11
|
-
file: path/to/dog.rb
|
12
|
-
simplified:
|
5
|
+
```
|
6
|
+
Unmatched `end', missing keyword (`do', `def`, `if`, etc.) ?
|
13
7
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
8
|
+
1 class Dog
|
9
|
+
❯ 2 defbark
|
10
|
+
❯ 4 end
|
11
|
+
5 end
|
12
|
+
```
|
18
13
|
|
19
14
|
## Installation in your codebase
|
20
15
|
|
@@ -52,34 +47,99 @@ This gives you the CLI command `$ dead_end` for more info run `$ dead_end --help
|
|
52
47
|
|
53
48
|
## What syntax errors does it handle?
|
54
49
|
|
50
|
+
Dead end will fire against all syntax errors and can isolate any syntax error. In addition, dead_end attempts to produce human readable descriptions of what needs to be done to resolve the issue. For example:
|
51
|
+
|
55
52
|
- Missing `end`:
|
56
53
|
|
54
|
+
<!--
|
57
55
|
```ruby
|
58
56
|
class Dog
|
59
57
|
def bark
|
60
58
|
puts "bark"
|
61
|
-
|
62
|
-
def woof
|
63
|
-
puts "woof"
|
64
|
-
end
|
65
59
|
end
|
66
|
-
# => scratch.rb:8: syntax error, unexpected end-of-input, expecting `end'
|
67
60
|
```
|
61
|
+
-->
|
68
62
|
|
69
|
-
|
63
|
+
```
|
64
|
+
Unmatched keyword, missing `end' ?
|
65
|
+
|
66
|
+
❯ 1 class Dog
|
67
|
+
❯ 2 def bark
|
68
|
+
❯ 4 end
|
69
|
+
```
|
70
70
|
|
71
|
+
- Missing keyword
|
72
|
+
<!--
|
71
73
|
```ruby
|
72
74
|
class Dog
|
73
75
|
def speak
|
74
|
-
@sounds.each |sound|
|
76
|
+
@sounds.each |sound|
|
75
77
|
puts sound
|
76
78
|
end
|
77
79
|
end
|
78
80
|
end
|
79
|
-
# => scratch.rb:7: syntax error, unexpected `end', expecting end-of-input
|
80
81
|
```
|
82
|
+
-->
|
83
|
+
|
84
|
+
```
|
85
|
+
Unmatched `end', missing keyword (`do', `def`, `if`, etc.) ?
|
86
|
+
|
87
|
+
1 class Dog
|
88
|
+
2 def speak
|
89
|
+
❯ 3 @sounds.each |sound|
|
90
|
+
❯ 5 end
|
91
|
+
6 end
|
92
|
+
7 end
|
93
|
+
```
|
94
|
+
|
95
|
+
- Missing pair characters (like `{}`, `[]`, `()` , or `|<var>|`)
|
96
|
+
<!--
|
97
|
+
|
98
|
+
```ruby
|
99
|
+
class Dog
|
100
|
+
def speak(sound
|
101
|
+
puts sound
|
102
|
+
end
|
103
|
+
end
|
104
|
+
```
|
105
|
+
-->
|
106
|
+
|
107
|
+
```
|
108
|
+
Unmatched `(', missing `)' ?
|
109
|
+
|
110
|
+
1 class Dog
|
111
|
+
❯ 2 def speak(sound
|
112
|
+
❯ 4 end
|
113
|
+
5 end
|
114
|
+
```
|
115
|
+
|
116
|
+
- Any ambiguous or unknown errors will be annotated by the original ripper error output:
|
117
|
+
|
118
|
+
<!--
|
119
|
+
class Dog
|
120
|
+
def meals_last_month
|
121
|
+
puts 3 *
|
122
|
+
end
|
123
|
+
end
|
124
|
+
-->
|
125
|
+
|
126
|
+
```
|
127
|
+
syntax error, unexpected end-of-input
|
128
|
+
|
129
|
+
1 class Dog
|
130
|
+
2 def meals_last_month
|
131
|
+
❯ 3 puts 3 *
|
132
|
+
4 end
|
133
|
+
5 end
|
134
|
+
```
|
135
|
+
|
136
|
+
## How is it better than `ruby -wc`?
|
81
137
|
|
82
|
-
|
138
|
+
Ruby allows you to syntax check a file with warnings using `ruby -wc`. This emits a parser error instead of a human focused error. Ruby's parse errors attempt to narrow down the location and can tell you if there is a glaring indentation error involving `end`.
|
139
|
+
|
140
|
+
The `dead_end` algorithm doesn't just guess at the location of syntax errors, it re-parses the document to prove that it captured them.
|
141
|
+
|
142
|
+
This library focuses on the human side of syntax errors. It cares less about why the document could not be parsed (computer problem) and more on what the programmer needs (human problem) to fix the problem.
|
83
143
|
|
84
144
|
## Sounds cool, but why isn't this baked into Ruby directly?
|
85
145
|
|
@@ -105,6 +165,34 @@ After checking out the repo, run `bin/setup` to install dependencies. Then, run
|
|
105
165
|
|
106
166
|
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
107
167
|
|
168
|
+
### How to debug changes to output display
|
169
|
+
|
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
|
+
|
172
|
+
```
|
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>
|
194
|
+
```
|
195
|
+
|
108
196
|
## Contributing
|
109
197
|
|
110
198
|
Bug reports and pull requests are welcome on GitHub at https://github.com/zombocom/dead_end. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/zombocom/dead_end/blob/master/CODE_OF_CONDUCT.md).
|
data/exe/dead_end
CHANGED
@@ -1,70 +1,7 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
require "pathname"
|
4
|
-
require "optparse"
|
5
3
|
require_relative "../lib/dead_end"
|
6
4
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
parser = OptionParser.new do |opts|
|
12
|
-
opts.banner = <<~EOM
|
13
|
-
Usage: dead_end <file> [options]
|
14
|
-
|
15
|
-
Parses a ruby source file and searches for syntax error(s) such as
|
16
|
-
unexpected `end', expecting end-of-input.
|
17
|
-
|
18
|
-
Example:
|
19
|
-
|
20
|
-
$ dead_end dog.rb
|
21
|
-
|
22
|
-
# ...
|
23
|
-
|
24
|
-
❯ 10 defdog
|
25
|
-
❯ 15 end
|
26
|
-
❯ 16
|
27
|
-
|
28
|
-
Env options:
|
29
|
-
|
30
|
-
DEAD_END_RECORD_DIR=<dir>
|
31
|
-
|
32
|
-
When enabled, records the steps used to search for a syntax error to the
|
33
|
-
given directory
|
34
|
-
|
35
|
-
Options:
|
36
|
-
EOM
|
37
|
-
|
38
|
-
opts.on("--help", "Help - displays this message") do |v|
|
39
|
-
puts opts
|
40
|
-
exit
|
41
|
-
end
|
42
|
-
|
43
|
-
opts.on("--record <dir>", "When enabled, records the steps used to search for a syntax error to the given directory") do |v|
|
44
|
-
options[:record_dir] = v
|
45
|
-
end
|
46
|
-
|
47
|
-
opts.on("--no-terminal", "Disable terminal highlighting") do |v|
|
48
|
-
options[:terminal] = false
|
49
|
-
end
|
50
|
-
end
|
51
|
-
parser.parse!
|
52
|
-
|
53
|
-
file = ARGV[0]
|
54
|
-
|
55
|
-
if file.nil? || file.empty?
|
56
|
-
# Display help if raw command
|
57
|
-
parser.parse! %w[--help]
|
58
|
-
end
|
59
|
-
|
60
|
-
file = Pathname(file)
|
61
|
-
options[:record_dir] = "tmp" if ENV["DEBUG"]
|
62
|
-
|
63
|
-
warn "Record dir: #{options[:record_dir]}" if options[:record_dir]
|
64
|
-
|
65
|
-
DeadEnd.call(
|
66
|
-
source: file.read,
|
67
|
-
filename: file.expand_path,
|
68
|
-
terminal: options[:terminal],
|
69
|
-
record_dir: options[:record_dir]
|
70
|
-
)
|
5
|
+
DeadEnd::Cli.new(
|
6
|
+
argv: ARGV
|
7
|
+
).call
|
data/lib/dead_end/auto.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative "../dead_end
|
3
|
+
require_relative "../dead_end"
|
4
4
|
|
5
5
|
# Monkey patch kernel to ensure that all `require` calls call the same
|
6
6
|
# method
|
@@ -33,23 +33,3 @@ module Kernel
|
|
33
33
|
DeadEnd.handle_error(e)
|
34
34
|
end
|
35
35
|
end
|
36
|
-
|
37
|
-
# I honestly have no idea why this Object delegation is needed
|
38
|
-
# I keep staring at bootsnap and it doesn't have to do this
|
39
|
-
# is there a bug in their implementation they haven't caught or
|
40
|
-
# am I doing something different?
|
41
|
-
class Object
|
42
|
-
private
|
43
|
-
|
44
|
-
def load(path, wrap = false)
|
45
|
-
Kernel.load(path, wrap)
|
46
|
-
rescue SyntaxError => e
|
47
|
-
DeadEnd.handle_error(e)
|
48
|
-
end
|
49
|
-
|
50
|
-
def require(path)
|
51
|
-
Kernel.require(path)
|
52
|
-
rescue SyntaxError => e
|
53
|
-
DeadEnd.handle_error(e)
|
54
|
-
end
|
55
|
-
end
|
@@ -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
|
@@ -231,7 +222,7 @@ module DeadEnd
|
|
231
222
|
#
|
232
223
|
def join_consecutive!
|
233
224
|
consecutive_groups = @document.select(&:ignore_newline_not_beg?).map do |code_line|
|
234
|
-
take_while_including(code_line.index
|
225
|
+
take_while_including(code_line.index..-1) do |line|
|
235
226
|
line.ignore_newline_not_beg?
|
236
227
|
end
|
237
228
|
end
|
@@ -252,7 +243,7 @@ module DeadEnd
|
|
252
243
|
# expect(lines[1].to_s).to eq("")
|
253
244
|
def join_trailing_slash!
|
254
245
|
trailing_groups = @document.select(&:trailing_slash?).map do |code_line|
|
255
|
-
take_while_including(code_line.index
|
246
|
+
take_while_including(code_line.index..-1) { |x| x.trailing_slash? }
|
256
247
|
end
|
257
248
|
join_groups(trailing_groups)
|
258
249
|
self
|
@@ -286,7 +277,7 @@ module DeadEnd
|
|
286
277
|
)
|
287
278
|
|
288
279
|
# Hide the rest of the lines
|
289
|
-
lines[1
|
280
|
+
lines[1..-1].each do |line|
|
290
281
|
# The above lines already have newlines in them, if add more
|
291
282
|
# then there will be double newline, use an empty line instead
|
292
283
|
@document[line.index] = CodeLine.new(line: "", index: line.index, lex: [])
|
@@ -300,7 +291,7 @@ module DeadEnd
|
|
300
291
|
# Like `take_while` except when it stops
|
301
292
|
# iterating, it also returns the line
|
302
293
|
# that caused it to stop
|
303
|
-
def take_while_including(range = 0
|
294
|
+
def take_while_including(range = 0..-1)
|
304
295
|
take_next_and_stop = false
|
305
296
|
@document[range].take_while do |line|
|
306
297
|
next if take_next_and_stop
|
data/lib/dead_end/cli.rb
ADDED
@@ -0,0 +1,129 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "pathname"
|
4
|
+
require "optparse"
|
5
|
+
|
6
|
+
module DeadEnd
|
7
|
+
# All the logic of the exe/dead_end CLI in one handy spot
|
8
|
+
#
|
9
|
+
# Cli.new(argv: ["--help"]).call
|
10
|
+
# Cli.new(argv: ["<path/to/file>.rb"]).call
|
11
|
+
# Cli.new(argv: ["<path/to/file>.rb", "--record=tmp"]).call
|
12
|
+
# Cli.new(argv: ["<path/to/file>.rb", "--terminal"]).call
|
13
|
+
#
|
14
|
+
class Cli
|
15
|
+
attr_accessor :options
|
16
|
+
|
17
|
+
# ARGV is Everything passed to the executable, does not include executable name
|
18
|
+
#
|
19
|
+
# All other intputs are dependency injection for testing
|
20
|
+
def initialize(argv:, exit_obj: Kernel, io: $stdout, env: ENV)
|
21
|
+
@options = {}
|
22
|
+
@parser = nil
|
23
|
+
options[:record_dir] = env["DEAD_END_RECORD_DIR"]
|
24
|
+
options[:record_dir] = "tmp" if env["DEBUG"]
|
25
|
+
options[:terminal] = DeadEnd::DEFAULT_VALUE
|
26
|
+
|
27
|
+
@io = io
|
28
|
+
@argv = argv
|
29
|
+
@exit_obj = exit_obj
|
30
|
+
end
|
31
|
+
|
32
|
+
def call
|
33
|
+
if @argv.empty?
|
34
|
+
# Display help if raw command
|
35
|
+
parser.parse! %w[--help]
|
36
|
+
return
|
37
|
+
else
|
38
|
+
# Mutates @argv
|
39
|
+
parse
|
40
|
+
return if options[:exit]
|
41
|
+
end
|
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
|
49
|
+
|
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
|
56
|
+
|
57
|
+
@io.puts "Record dir: #{options[:record_dir]}" if options[:record_dir]
|
58
|
+
|
59
|
+
display = DeadEnd.call(
|
60
|
+
io: @io,
|
61
|
+
source: file.read,
|
62
|
+
filename: file.expand_path,
|
63
|
+
terminal: options.fetch(:terminal, DeadEnd::DEFAULT_VALUE),
|
64
|
+
record_dir: options[:record_dir]
|
65
|
+
)
|
66
|
+
|
67
|
+
if display.document_ok?
|
68
|
+
@exit_obj.exit(0)
|
69
|
+
else
|
70
|
+
@exit_obj.exit(1)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def parse
|
75
|
+
parser.parse!(@argv)
|
76
|
+
|
77
|
+
self
|
78
|
+
end
|
79
|
+
|
80
|
+
def parser
|
81
|
+
@parser ||= OptionParser.new do |opts|
|
82
|
+
opts.banner = <<~EOM
|
83
|
+
Usage: dead_end <file> [options]
|
84
|
+
|
85
|
+
Parses a ruby source file and searches for syntax error(s) such as
|
86
|
+
unexpected `end', expecting end-of-input.
|
87
|
+
|
88
|
+
Example:
|
89
|
+
|
90
|
+
$ dead_end dog.rb
|
91
|
+
|
92
|
+
# ...
|
93
|
+
|
94
|
+
❯ 10 defdog
|
95
|
+
❯ 15 end
|
96
|
+
|
97
|
+
ENV options:
|
98
|
+
|
99
|
+
DEAD_END_RECORD_DIR=<dir>
|
100
|
+
|
101
|
+
Records the steps used to search for a syntax error
|
102
|
+
to the given directory
|
103
|
+
|
104
|
+
Options:
|
105
|
+
EOM
|
106
|
+
|
107
|
+
opts.version = DeadEnd::VERSION
|
108
|
+
|
109
|
+
opts.on("--help", "Help - displays this message") do |v|
|
110
|
+
@io.puts opts
|
111
|
+
options[:exit] = true
|
112
|
+
@exit_obj.exit
|
113
|
+
end
|
114
|
+
|
115
|
+
opts.on("--record <dir>", "Records the steps used to search for a syntax error to the given directory") do |v|
|
116
|
+
options[:record_dir] = v
|
117
|
+
end
|
118
|
+
|
119
|
+
opts.on("--terminal", "Enable terminal highlighting") do |v|
|
120
|
+
options[:terminal] = true
|
121
|
+
end
|
122
|
+
|
123
|
+
opts.on("--no-terminal", "Disable terminal highlighting") do |v|
|
124
|
+
options[:terminal] = false
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|