dead_end 2.0.2 → 3.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.
@@ -1,157 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # This is the top level file, but is moved to `internals`
4
- # so the top level require can instead enable the "automatic" behavior
5
-
6
- require_relative "version"
7
-
8
- require "tmpdir"
9
- require "stringio"
10
- require "pathname"
11
- require "ripper"
12
- require "timeout"
13
-
14
- module DeadEnd
15
- # Used to indicate a default value that cannot
16
- # be confused with another input
17
- DEFAULT_VALUE = Object.new.freeze
18
-
19
- class Error < StandardError; end
20
- SEARCH_SOURCE_ON_ERROR_DEFAULT = true
21
- TIMEOUT_DEFAULT = ENV.fetch("DEAD_END_TIMEOUT", 1).to_i
22
-
23
- def self.handle_error(e, search_source_on_error: SEARCH_SOURCE_ON_ERROR_DEFAULT)
24
- raise e unless e.message.include?("end-of-input")
25
-
26
- filename = e.message.split(":").first
27
-
28
- $stderr.sync = true
29
- warn "Run `$ dead_end #{filename}` for more options\n"
30
-
31
- if search_source_on_error
32
- call(
33
- source: Pathname(filename).read,
34
- filename: filename
35
- )
36
- end
37
-
38
- raise e
39
- end
40
-
41
- def self.call(source:, filename:, terminal: DEFAULT_VALUE, record_dir: nil, timeout: TIMEOUT_DEFAULT, io: $stderr)
42
- search = nil
43
- Timeout.timeout(timeout) do
44
- record_dir ||= ENV["DEBUG"] ? "tmp" : nil
45
- search = CodeSearch.new(source, record_dir: record_dir).call
46
- end
47
-
48
- blocks = search.invalid_blocks
49
- DisplayInvalidBlocks.new(
50
- blocks: blocks,
51
- filename: filename,
52
- terminal: terminal,
53
- code_lines: search.code_lines,
54
- invalid_obj: invalid_type(source),
55
- io: io
56
- ).call
57
- rescue Timeout::Error => e
58
- io.puts "Search timed out DEAD_END_TIMEOUT=#{timeout}, run with DEBUG=1 for more info"
59
- io.puts e.backtrace.first(3).join($/)
60
- end
61
-
62
- # Used for counting spaces
63
- module SpaceCount
64
- def self.indent(string)
65
- string.split(/\S/).first&.length || 0
66
- end
67
- end
68
-
69
- # This will tell you if the `code_lines` would be valid
70
- # if you removed the `without_lines`. In short it's a
71
- # way to detect if we've found the lines with syntax errors
72
- # in our document yet.
73
- #
74
- # code_lines = [
75
- # CodeLine.new(line: "def foo\n", index: 0)
76
- # CodeLine.new(line: " def bar\n", index: 1)
77
- # CodeLine.new(line: "end\n", index: 2)
78
- # ]
79
- #
80
- # DeadEnd.valid_without?(
81
- # without_lines: code_lines[1],
82
- # code_lines: code_lines
83
- # ) # => true
84
- #
85
- # DeadEnd.valid?(code_lines) # => false
86
- def self.valid_without?(without_lines:, code_lines:)
87
- lines = code_lines - Array(without_lines).flatten
88
-
89
- if lines.empty?
90
- true
91
- else
92
- valid?(lines)
93
- end
94
- end
95
-
96
- def self.invalid?(source)
97
- source = source.join if source.is_a?(Array)
98
- source = source.to_s
99
-
100
- Ripper.new(source).tap(&:parse).error?
101
- end
102
-
103
- # Returns truthy if a given input source is valid syntax
104
- #
105
- # DeadEnd.valid?(<<~EOM) # => true
106
- # def foo
107
- # end
108
- # EOM
109
- #
110
- # DeadEnd.valid?(<<~EOM) # => false
111
- # def foo
112
- # def bar # Syntax error here
113
- # end
114
- # EOM
115
- #
116
- # You can also pass in an array of lines and they'll be
117
- # joined before evaluating
118
- #
119
- # DeadEnd.valid?(
120
- # [
121
- # "def foo\n",
122
- # "end\n"
123
- # ]
124
- # ) # => true
125
- #
126
- # DeadEnd.valid?(
127
- # [
128
- # "def foo\n",
129
- # " def bar\n", # Syntax error here
130
- # "end\n"
131
- # ]
132
- # ) # => false
133
- #
134
- # As an FYI the CodeLine class instances respond to `to_s`
135
- # so passing a CodeLine in as an object or as an array
136
- # will convert it to it's code representation.
137
- def self.valid?(source)
138
- !invalid?(source)
139
- end
140
-
141
- def self.invalid_type(source)
142
- WhoDisSyntaxError.new(source).call
143
- end
144
- end
145
-
146
- require_relative "code_line"
147
- require_relative "code_block"
148
- require_relative "code_search"
149
- require_relative "code_frontier"
150
- require_relative "clean_document"
151
-
152
- require_relative "lex_all"
153
- require_relative "block_expand"
154
- require_relative "around_block_scan"
155
- require_relative "who_dis_syntax_error"
156
- require_relative "display_invalid_blocks"
157
- require_relative "parse_blocks_from_indent_line"
@@ -1,83 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module DeadEnd
4
- # Determines what type of syntax error that is in the source
5
- #
6
- # Example:
7
- #
8
- # puts WhoDisSyntaxError.new("def foo;").call.error_symbol
9
- # # => :missing_end
10
- class WhoDisSyntaxError < Ripper
11
- CHARACTERS = {"{": :"}", "}": :"{", "[": :"]", "]": :"[", "(": :")", ")": :"("}
12
- class Null
13
- def error_symbol
14
- :missing_end
15
- end
16
-
17
- def unmatched_symbol
18
- :end
19
- end
20
- end
21
- attr_reader :error, :run_once
22
-
23
- # Return options:
24
- # - :missing_end
25
- # - :unmatched_syntax
26
- # - :unknown
27
- def error_symbol
28
- call
29
- @error_symbol
30
- end
31
-
32
- # Return options:
33
- # - :end
34
- # - :|
35
- # - :}
36
- # - :unknown
37
- def unmatched_symbol
38
- call
39
- @unmatched_symbol
40
- end
41
-
42
- def call
43
- @run_once ||= begin
44
- parse
45
- true
46
- end
47
- self
48
- end
49
-
50
- def on_parse_error(msg)
51
- return if @error_symbol && @unmatched_symbol
52
-
53
- @error = msg
54
- @unmatched_symbol = :unknown
55
-
56
- case @error
57
- when /unexpected end-of-input/
58
- @error_symbol = :missing_end
59
- when /expecting end-of-input/
60
- @unmatched_symbol = :end
61
- @error_symbol = :unmatched_syntax
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
72
- @error_symbol = :unmatched_syntax
73
- when /unexpected `end'/, # Ruby 2.7 and 3.0
74
- /unexpected end/, # Ruby 2.6
75
- /unexpected keyword_end/i # Ruby 2.5
76
-
77
- @error_symbol = :unmatched_syntax
78
- else
79
- @error_symbol = :unknown
80
- end
81
- end
82
- end
83
- end