dead_end 1.0.2 → 1.1.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 125719d74f88dcc5d196a1570508bd36d83d7ece6058b029a579da90a70cc099
4
- data.tar.gz: 2eea6be22997101b46ff2820dfabaef4e473424a552e7f9c2835e43471c4431b
3
+ metadata.gz: d1e8a394d94d7d8f70138a9d80d7e660cca8dc06e3d3a5eb9b74334585f284aa
4
+ data.tar.gz: 75296d7faaf4b117ad45bed40252566ee08752b622b2d90e4afa3be8b4a9b774
5
5
  SHA512:
6
- metadata.gz: 674e35c603ec9ed2a8662e6ee3472381c6108a8734f455d39ef30472e38d13fd08a23cb107333f9ff8353ace614cb6c1f5bcb21e6aeb5384911ad1a61eb1ff58
7
- data.tar.gz: 4ed5351764e96b5c95b2c4f68403bd81a3739163ad83d9e473829a1f6571f7e3614b855f56f21f5e8f15434817f7b7e8a947146044d4ac4c4f9314a15cd6b9bf
6
+ metadata.gz: f970086da7e3f662d2dabda9a59d89af73d503dc986a9d469d45b83f25184590892e5bdb0a4c174318b2d1bb201f112c81a0a281d0a530feb49dd7f0eb1fb144
7
+ data.tar.gz: cc85e81a1d996320128d7656c0cea0727f9672060d7d2747e30e77d2aeb59d3549f4dd230835c5042988c53b1252f975c472774eb2ef7f258caebea5cd5eee40
@@ -32,6 +32,14 @@ jobs:
32
32
  - ruby/install-deps
33
33
  - <<: *unit
34
34
 
35
+ "ruby-3-0":
36
+ docker:
37
+ - image: circleci/ruby:3.0
38
+ steps:
39
+ - checkout
40
+ - ruby/install-deps
41
+ - <<: *unit
42
+
35
43
  workflows:
36
44
  version: 2
37
45
  build:
@@ -39,3 +47,4 @@ workflows:
39
47
  - "ruby-2-5"
40
48
  - "ruby-2-6"
41
49
  - "ruby-2-7"
50
+ - "ruby-3-0"
@@ -1,5 +1,26 @@
1
1
  ## HEAD (unreleased)
2
2
 
3
+ ## 1.1.4
4
+
5
+ - Avoid including demo gif in built gem (https://github.com/zombocom/dead_end/pull/53)
6
+
7
+ ## 1.1.3
8
+
9
+ - Add compatibility with zeitwerk (https://github.com/zombocom/dead_end/pull/52)
10
+
11
+ ## 1.1.2
12
+
13
+ - Namespace Kernel method aliases (https://github.com/zombocom/dead_end/pull/51)
14
+
15
+ ## 1.1.1
16
+
17
+ - Safer NoMethodError annotation (https://github.com/zombocom/dead_end/pull/48)
18
+
19
+ ## 1.1.0
20
+
21
+ - Annotate NoMethodError in non-production environments (https://github.com/zombocom/dead_end/pull/46)
22
+ - Do not count trailing if/unless as a keyword (https://github.com/zombocom/dead_end/pull/44)
23
+
3
24
  ## 1.0.2
4
25
 
5
26
  - Fix bug where empty lines were interpreted to have a zero indentation (https://github.com/zombocom/dead_end/pull/39)
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- dead_end (1.0.2)
4
+ dead_end (1.1.4)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
@@ -33,4 +33,4 @@ DEPENDENCIES
33
33
  stackprof
34
34
 
35
35
  BUNDLED WITH
36
- 2.1.4
36
+ 2.2.3
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # DeadEnd
2
2
 
3
- An AI powered library to find syntax errors in your source code:
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
5
  DeadEnd: Unmatched `end` detected
6
6
 
@@ -18,7 +18,7 @@ An AI powered library to find syntax errors in your source code:
18
18
 
19
19
  ## Installation in your codebase
20
20
 
21
- To automatically search syntax errors when they happen, add this to your Gemfile:
21
+ To automatically annotate errors when they happen, add this to your Gemfile:
22
22
 
23
23
  ```ruby
24
24
  gem 'dead_end'
@@ -44,7 +44,7 @@ If you're using rspec add this to your `.rspec` file:
44
44
 
45
45
  ## Install the CLI
46
46
 
47
- To get the CLI and manually search for syntax errors, install the gem:
47
+ To get the CLI and manually search for syntax errors (but not automatically annotate them), you can manually install the gem:
48
48
 
49
49
  $ gem install dead_end
50
50
 
@@ -54,7 +54,7 @@ This gives you the CLI command `$ dead_end` for more info run `$ dead_end --help
54
54
 
55
55
  - Missing `end`:
56
56
 
57
- ```
57
+ ```ruby
58
58
  class Dog
59
59
  def bark
60
60
  puts "bark"
@@ -68,7 +68,7 @@ end
68
68
 
69
69
  - Unexpected `end`
70
70
 
71
- ```
71
+ ```ruby
72
72
  class Dog
73
73
  def speak
74
74
  @sounds.each |sound| # Note the missing `do` here
@@ -81,6 +81,21 @@ 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
+
84
99
  ## Sounds cool, but why isn't this baked into Ruby directly?
85
100
 
86
101
  I would love to get something like this directly in Ruby, but I first need to prove it's useful. The `did_you_mean` functionality started as a gem that was eventually adopted by a bunch of people and then Ruby core liked it enough that they included it in the source. The goal of this gem is to:
@@ -90,7 +105,7 @@ I would love to get something like this directly in Ruby, but I first need to pr
90
105
 
91
106
  ## Artificial Inteligence?
92
107
 
93
- This library uses a goal-seeking algorithm similar to that of a path-finding search. For more information [read the blog post about how it works under the hood](https://schneems.com/2020/12/01/squash-unexpectedend-errors-with-syntaxsearch/).
108
+ This library uses a goal-seeking algorithm for syntax error detection similar to that of a path-finding search. For more information [read the blog post about how it works under the hood](https://schneems.com/2020/12/01/squash-unexpectedend-errors-with-syntaxsearch/).
94
109
 
95
110
  ## How does it detect syntax error locations?
96
111
 
@@ -20,7 +20,7 @@ Gem::Specification.new do |spec|
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
22
  spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
23
- `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
23
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features|assets)/}) }
24
24
  end
25
25
  spec.bindir = "exe"
26
26
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
@@ -1,29 +1,33 @@
1
+ # frozen_string_literal: true
2
+ #
1
3
  require_relative "../dead_end/internals"
2
4
 
3
5
  # Monkey patch kernel to ensure that all `require` calls call the same
4
6
  # method
5
7
  module Kernel
6
- alias_method :original_require, :require
7
- alias_method :original_require_relative, :require_relative
8
- alias_method :original_load, :load
8
+ module_function
9
+
10
+ alias_method :dead_end_original_require, :require
11
+ alias_method :dead_end_original_require_relative, :require_relative
12
+ alias_method :dead_end_original_load, :load
9
13
 
10
14
  def load(file, wrap = false)
11
- original_load(file)
15
+ dead_end_original_load(file)
12
16
  rescue SyntaxError => e
13
17
  DeadEnd.handle_error(e)
14
18
  end
15
19
 
16
20
  def require(file)
17
- original_require(file)
21
+ dead_end_original_require(file)
18
22
  rescue SyntaxError => e
19
23
  DeadEnd.handle_error(e)
20
24
  end
21
25
 
22
26
  def require_relative(file)
23
27
  if Pathname.new(file).absolute?
24
- original_require file
28
+ dead_end_original_require file
25
29
  else
26
- original_require File.expand_path("../#{file}", caller_locations(1, 1)[0].absolute_path)
30
+ dead_end_original_require File.expand_path("../#{file}", caller_locations(1, 1)[0].absolute_path)
27
31
  end
28
32
  rescue SyntaxError => e
29
33
  DeadEnd.handle_error(e)
@@ -49,3 +53,52 @@ class Object
49
53
  end
50
54
  end
51
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
@@ -31,6 +31,12 @@ module DeadEnd
31
31
  class CodeLine
32
32
  TRAILING_SLASH = ("\\" + $/).freeze
33
33
 
34
+ def self.parse(source)
35
+ source.lines.map.with_index do |line, index|
36
+ CodeLine.new(line: line, index: index)
37
+ end
38
+ end
39
+
34
40
  attr_reader :line, :index, :indent, :original_line
35
41
 
36
42
  def initialize(line: , index:)
@@ -51,25 +57,29 @@ module DeadEnd
51
57
  end
52
58
 
53
59
  private def lex_detect!
54
- lex = LexAll.new(source: line)
60
+ lex_array = LexAll.new(source: line)
55
61
  kw_count = 0
56
62
  end_count = 0
57
- lex.each do |lex|
63
+ lex_array.each_with_index do |lex, index|
58
64
  next unless lex.type == :on_kw
59
65
 
60
66
  case lex.token
61
- when 'def', 'case', 'for', 'begin', 'class', 'module', 'if', 'unless', 'while', 'until' , 'do'
67
+ when 'if', 'unless', 'while', 'until'
68
+ # Only count if/unless when it's not a "trailing" if/unless
69
+ # https://github.com/ruby/ruby/blob/06b44f819eb7b5ede1ff69cecb25682b56a1d60c/lib/irb/ruby-lex.rb#L374-L375
70
+ kw_count += 1 if !lex.expr_label?
71
+ when 'def', 'case', 'for', 'begin', 'class', 'module', 'do'
62
72
  kw_count += 1
63
73
  when 'end'
64
74
  end_count += 1
65
75
  end
66
76
  end
67
77
 
68
- @is_comment = lex.detect {|lex| lex.type != :on_sp}&.type == :on_comment
78
+ @is_comment = lex_array.detect {|lex| lex.type != :on_sp}&.type == :on_comment
69
79
  return if @is_comment
70
80
  @is_kw = (kw_count - end_count) > 0
71
81
  @is_end = (end_count - kw_count) > 0
72
- @is_trailing_slash = lex.last.token == TRAILING_SLASH
82
+ @is_trailing_slash = lex_array.last.token == TRAILING_SLASH
73
83
  end
74
84
 
75
85
  alias :original :original_line
@@ -21,7 +21,7 @@ module DeadEnd
21
21
  lineno = @lex.last&.first&.first + 1
22
22
  end
23
23
 
24
- @lex.map! {|(line, _), type, token| LexValue.new(line, _, type, token) }
24
+ @lex.map! {|(line, _), type, token, state| LexValue.new(line, _, type, token, state) }
25
25
  end
26
26
 
27
27
  def each
@@ -47,11 +47,17 @@ module DeadEnd
47
47
  # lex.type # => :on_indent
48
48
  # lex.token # => "describe"
49
49
  class LexValue
50
- attr_reader :line, :type, :token
51
- def initialize(line, _, type, token)
50
+ attr_reader :line, :type, :token, :state
51
+
52
+ def initialize(line, _, type, token, state)
52
53
  @line = line
53
54
  @type = type
54
55
  @token = token
56
+ @state = state
57
+ end
58
+
59
+ def expr_label?
60
+ state.allbits?(Ripper::EXPR_LABEL)
55
61
  end
56
62
  end
57
63
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DeadEnd
4
- VERSION = "1.0.2"
4
+ VERSION = "1.1.4"
5
5
  end
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.0.2
4
+ version: 1.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - schneems
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-12-28 00:00:00.000000000 Z
11
+ date: 2021-01-26 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
@@ -31,7 +31,6 @@ files:
31
31
  - LICENSE.txt
32
32
  - README.md
33
33
  - Rakefile
34
- - assets/syntax_search.gif
35
34
  - bin/console
36
35
  - bin/setup
37
36
  - dead_end.gemspec
@@ -76,7 +75,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
76
75
  - !ruby/object:Gem::Version
77
76
  version: '0'
78
77
  requirements: []
79
- rubygems_version: 3.1.4
78
+ rubygems_version: 3.0.3
80
79
  signing_key:
81
80
  specification_version: 4
82
81
  summary: Find syntax errors in your source in a snap
Binary file