offense_to_corrector 0.0.1 → 0.0.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e3aa29d338b6fbe90a8b026e2a5a7008e4772bfe52b988a26856b6cd4c585a1b
4
- data.tar.gz: 63a528f40daa7da31df15a872d66fc0181a72fb457b1e5c23885deb752dbe6f1
3
+ metadata.gz: 633ca47b27f11bd775d9fd27182d22586603b950b3e3c8ce6e3976162ead1fb1
4
+ data.tar.gz: 43e72f100670136da7907c3ea63d3298242974b40b2cf1f330cae73f5f366d60
5
5
  SHA512:
6
- metadata.gz: b565e890a4a449467882d5ed53787990342fa5515c38e50f71653480594f4113bb564c1f353be2bdf879ca6cb9263dd9dbf5a58bef4b048e3106febaa303026b
7
- data.tar.gz: 23de46b53c06aa98dfc0c4342cce5e4344c955187dbc7d65c5161e96271a7268a89f0fe09939a46a34e7b7f524ed37e24958eeddb647bf69e0a08170be13f41d
6
+ metadata.gz: cb3b1ebad0fb6a238d839cca290065d806ada04fbb9a03858e663befbf1994f5bb5bda222a9cd7bca5a3b0b299733f179fb1f5ce0a201be261101e3a908e0972
7
+ data.tar.gz: 7608dfcd7d6d04a8c4e53f3fc1f448ba5c89585f67a4c15ab25fe87cb0992ed731fdc3ee9590fb1f01b6145ef48f47f4c16f813d894cac91982cab1276ee6f2c
data/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  ## [Unreleased]
2
2
 
3
- ## [0.1.0] - 2022-02-20
3
+ ## [0.0.2] - 2022-02-21
4
4
 
5
- - Initial release
5
+ - Refined underlying algorithms for computing nearest nodes
6
+ - Range intersection dropped for string overlaps.
7
+ - Closest node implemented on length of overlap and percentage of node source
8
+ overlap to prevent matching higher-up parent nodes, but also avoid matching
9
+ entire small child nodes that aren't as relevant.
10
+ - Fixed errors with unfindable atom nodes.
11
+ - Broke apart mono-file into related subdirectories, added basic specs
12
+
13
+ ## [0.0.1] - 2022-02-20
14
+
15
+ - Initial release.
data/Gemfile.lock CHANGED
@@ -8,12 +8,44 @@ GEM
8
8
  remote: https://rubygems.org/
9
9
  specs:
10
10
  ast (2.4.2)
11
+ coderay (1.1.3)
11
12
  diff-lcs (1.5.0)
13
+ ffi (1.15.5)
14
+ formatador (1.1.0)
15
+ guard (2.18.0)
16
+ formatador (>= 0.2.4)
17
+ listen (>= 2.7, < 4.0)
18
+ lumberjack (>= 1.0.12, < 2.0)
19
+ nenv (~> 0.1)
20
+ notiffany (~> 0.0)
21
+ pry (>= 0.13.0)
22
+ shellany (~> 0.0)
23
+ thor (>= 0.18.1)
24
+ guard-compat (1.2.1)
25
+ guard-rspec (4.7.3)
26
+ guard (~> 2.1)
27
+ guard-compat (~> 1.1)
28
+ rspec (>= 2.99.0, < 4.0)
29
+ listen (3.7.1)
30
+ rb-fsevent (~> 0.10, >= 0.10.3)
31
+ rb-inotify (~> 0.9, >= 0.9.10)
32
+ lumberjack (1.2.8)
33
+ method_source (1.0.0)
34
+ nenv (0.3.0)
35
+ notiffany (0.1.3)
36
+ nenv (~> 0.1)
37
+ shellany (~> 0.0)
12
38
  parallel (1.21.0)
13
39
  parser (3.1.0.0)
14
40
  ast (~> 2.4.1)
41
+ pry (0.14.1)
42
+ coderay (~> 1.1)
43
+ method_source (~> 1.0)
15
44
  rainbow (3.1.1)
16
45
  rake (13.0.6)
46
+ rb-fsevent (0.11.1)
47
+ rb-inotify (0.10.1)
48
+ ffi (~> 1.0)
17
49
  regexp_parser (2.2.1)
18
50
  rexml (3.2.5)
19
51
  rspec (3.11.0)
@@ -41,12 +73,15 @@ GEM
41
73
  rubocop-ast (1.15.2)
42
74
  parser (>= 3.0.1.1)
43
75
  ruby-progressbar (1.11.0)
76
+ shellany (0.0.1)
77
+ thor (1.2.1)
44
78
  unicode-display_width (2.1.0)
45
79
 
46
80
  PLATFORMS
47
81
  x86_64-darwin-20
48
82
 
49
83
  DEPENDENCIES
84
+ guard-rspec
50
85
  offense_to_corrector!
51
86
  rake (~> 13.0)
52
87
  rspec (~> 3.0)
data/Guardfile ADDED
@@ -0,0 +1,14 @@
1
+ guard :rspec, cmd: "bundle exec rspec" do
2
+ require "guard/rspec/dsl"
3
+ dsl = Guard::RSpec::Dsl.new(self)
4
+
5
+ # RSpec files
6
+ rspec = dsl.rspec
7
+ watch(rspec.spec_helper) { rspec.spec_dir }
8
+ watch(rspec.spec_support) { rspec.spec_dir }
9
+ watch(rspec.spec_files)
10
+
11
+ # Ruby files
12
+ ruby = dsl.ruby
13
+ dsl.watch_spec_files_for(ruby.lib_files)
14
+ end
@@ -0,0 +1,93 @@
1
+ module OffenseToCorrector
2
+ module AstTools
3
+ def atom?(value)
4
+ !value.is_a?(RuboCop::AST::Node)
5
+ end
6
+
7
+ # I may have to work on getting this one later to try and
8
+ # narrow the band of `AtomNode`
9
+ # def childless?(node)
10
+ # false # TODO
11
+ # end
12
+
13
+ def string_intersection(a, b)
14
+ # Smaller string inside larger string
15
+ target, search_string = a.size < b.size ? [a, b] : [b, a]
16
+
17
+ last_char_index = target.size.downto(1).find do |i|
18
+ search_string.include?(target[0..i])
19
+ end or return ""
20
+
21
+ target[0..last_char_index]
22
+ end
23
+
24
+ # How much do two ranges overlap? Used to see how well a node
25
+ # matches with the associated underline
26
+ def range_overlap_count(a, b)
27
+ return [a.end, b.end].min - b.begin if a.cover?(b.begin)
28
+ return [a.end, b.end].min - a.begin if b.cover?(a.begin)
29
+
30
+ 0
31
+ end
32
+
33
+ # See if a range overlaps another one, bidirectional
34
+ def range_overlap?(a, b)
35
+ a.cover?(b.begin) || b.cover?(a.begin)
36
+ end
37
+
38
+ # To get an AST we need the processed source of a string
39
+ def processed_source_from(string)
40
+ RuboCop::ProcessedSource.new(string, RUBY_VERSION.to_f)
41
+ end
42
+
43
+ # So why bother with the above then? If we end up into correctors
44
+ # and tree-rewrites we need that original processed source to be
45
+ # the basis of the AST, otherwise we get object ID mismatches.
46
+ def ast_from(value)
47
+ case value
48
+ when RuboCop::ProcessedSource
49
+ value.ast
50
+ else
51
+ processed_source_from(value).ast
52
+ end
53
+ end
54
+
55
+ # Not needed quite yet, but could very potentially be used to verify
56
+ # how accurate generated cops are.
57
+ def get_corrector(value)
58
+ RuboCop::Cop::Corrector.new(value.buffer)
59
+ end
60
+
61
+ # Descendants leaves out a _lot_ of detail potentially. There has to
62
+ # be a better way to deal with this, but not thinking of one right now.
63
+ def get_children(source_node)
64
+ recurse = -> parent do
65
+ collected_children = []
66
+ parent.children.each do |child|
67
+ next if child.nil?
68
+
69
+ # If it's a regular parent carry on as you were
70
+ unless atom?(child) # || childless?(child)
71
+ collected_children.concat([child, *recurse[child]])
72
+ next
73
+ end
74
+
75
+ # Otherwise we want to find where that parent is in the source code,
76
+ # and the range it exists in, to create an `AtomNode`
77
+ source = child.to_s
78
+
79
+ parent_begin = parent.location.expression.to_range.begin
80
+ range_begin = source_node.source.index(source, parent_begin)
81
+ range_end = range_begin + source.size
82
+ range = range_begin..range_end
83
+
84
+ collected_children << AtomNode.new(parent:, source:, range:)
85
+ end
86
+
87
+ collected_children
88
+ end
89
+
90
+ [source_node, *recurse[source_node]]
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,36 @@
1
+ module OffenseToCorrector
2
+ # The annoying thing is that `RuboCop::AST::Node` 's children / descendant
3
+ # methods don't capture all the relevant data, so we have to cheat a bit
4
+ # by wrapping atoms (String, Symbol, Int, etc) in a class to get around that.
5
+ class AtomNode
6
+ attr_reader :source, :range, :parent
7
+
8
+ def initialize(source:, range:, parent:)
9
+ @source = source
10
+ @range = range
11
+ @parent = parent
12
+ end
13
+
14
+ def to_s
15
+ relevant_children = @parent.children.map do |child|
16
+ next "..." unless child.to_s == self.source.to_s # Wildcard
17
+
18
+ case child
19
+ when String then %("#{child}") # Literal string
20
+ when Symbol then ":#{child}" # Literal symbol
21
+ when nil then "nil?"
22
+ else child
23
+ end
24
+ end
25
+
26
+ # The trick here is that these aren't nodes, but we do care about
27
+ # what the "parent" is that contains it to get something we can
28
+ # work with. All other children are replaced with wildcards.
29
+ "(#{self.parent.type} #{relevant_children.join(' ')})"
30
+ end
31
+
32
+ def deconstruct_keys(keys)
33
+ { source:, range:, parent: }
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,35 @@
1
+ module OffenseToCorrector
2
+ class Offense
3
+ # How many underline carots (^), and potentially an error after
4
+ OFFENSE_MATCH = /^ *?(?<underline>\^+) *?(?<error>.*)?$/
5
+
6
+ attr_reader :error, :range, :source
7
+
8
+ def initialize(error:, range:, source:)
9
+ @error = error
10
+ @range = range
11
+ @source = source
12
+ end
13
+
14
+ def deconstruct_keys(keys)
15
+ { error:, range:, source: }
16
+ end
17
+
18
+ # See if there's an underline, if so get how long it is and
19
+ # the error message after it
20
+ #
21
+ def self.parse(line:, previous_line:)
22
+ match_data = OFFENSE_MATCH.match(line) or return nil
23
+ underline = match_data[:underline]
24
+ error = match_data[:error].lstrip
25
+
26
+ underline_start = line.index(underline)
27
+ underline_end = underline_start + underline.size
28
+ range = underline_start...underline_end
29
+
30
+ source = previous_line.slice(range).chomp
31
+
32
+ new(error:, range:, source:)
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,86 @@
1
+ module OffenseToCorrector
2
+ class OffenseParser
3
+ include AstTools
4
+
5
+ attr_reader :ast, :source, :offense, :ast_nodes
6
+
7
+ def initialize(string)
8
+ @ast_lines, @offense = parse_string(string)
9
+ @source = processed_source_from(@ast_lines.join("\n"))
10
+ @ast = ast_from(@source)
11
+ @ast_nodes = get_children(@ast)
12
+ @template = OffenseTemplate.new
13
+ end
14
+
15
+ # Render that into the cop skeleton. Perhaps having too much
16
+ # fun here with rightward assignment.
17
+ def render
18
+ call => {
19
+ offense: { error: },
20
+ node_offense_info: { offending_node:, offending_node_matcher: }
21
+ }
22
+
23
+ @template.render(
24
+ class_name: "TODO",
25
+ match_pattern: offending_node_matcher,
26
+ error_message: error,
27
+ node_type: offending_node.type,
28
+ cop_type: "Lint",
29
+ node_location: "selector",
30
+ offense_severity: "warning"
31
+ )
32
+ end
33
+
34
+ # Quick info on what all is being worked on and what info we got
35
+ def deconstruct_keys(keys)
36
+ { ast:, source:, offense:, node_offense_info: }
37
+ end
38
+
39
+ def node_offense_info
40
+ return @node_offense_info if defined?(@node_offense_info)
41
+
42
+ # Find the node, or atom, with the most overlap with the offense
43
+ # range defined by that underline. That's defined as a pair of
44
+ # the length of the intersection as well as what percentage that
45
+ # intersection makes of the full node source.
46
+ offending_node = @ast_nodes.max_by do |node|
47
+ intersection = string_intersection(node.source, @offense.source)
48
+ [intersection.size, intersection.size.fdiv(node.source.size)]
49
+ end
50
+
51
+ @node_offense_info ||= {
52
+ offending_node:,
53
+ offending_node_matcher: offending_node.to_s
54
+ }
55
+ end
56
+
57
+ private def node_range(node)
58
+ if node.is_a?(AtomNode)
59
+ node.range
60
+ else
61
+ node.location.expression.to_range
62
+ end
63
+ end
64
+
65
+ # Figure which part of the passed in code string is AST vs offense
66
+ private def parse_string(string)
67
+ ast_lines = []
68
+ offense = nil
69
+ lines = string.lines
70
+
71
+ lines.each_with_index do |line, i|
72
+ # No sense of a meta-line being above every other line
73
+ meta_info = i > 0 && Offense.parse(line:, previous_line: lines[i - 1])
74
+
75
+ if meta_info
76
+ raise "Cannot have multiple offenses" unless offense.nil?
77
+ offense = meta_info
78
+ else
79
+ ast_lines << line
80
+ end
81
+ end
82
+
83
+ [ast_lines, offense]
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,30 @@
1
+ module OffenseToCorrector
2
+ # ERB template for rendering a cop skeleton, may make this more useful
3
+ # later, but mostly quick templating for now.
4
+ class OffenseTemplate
5
+ def initialize(name: "autocorrector_template.erb")
6
+ @template = File.read(OffenseToCorrector.load_template(name))
7
+ @erb = ERB.new(@template)
8
+ end
9
+
10
+ def render(
11
+ class_name: "TODO",
12
+ match_pattern:,
13
+ error_message: "",
14
+ node_type:,
15
+ cop_type: "Lint",
16
+ node_location: "selector",
17
+ offense_severity: "warning"
18
+ )
19
+ @erb.result_with_hash(
20
+ class_name:,
21
+ match_pattern:,
22
+ error_message:,
23
+ node_type:,
24
+ cop_type:,
25
+ node_location:,
26
+ offense_severity:
27
+ )
28
+ end
29
+ end
30
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module OffenseToCorrector
4
- VERSION = "0.0.1"
4
+ VERSION = "0.0.2"
5
5
  end
@@ -5,6 +5,12 @@ require_relative "offense_to_corrector/version"
5
5
  require "rubocop"
6
6
  require "erb"
7
7
 
8
+ require "offense_to_corrector/atom_node"
9
+ require "offense_to_corrector/ast_tools"
10
+ require "offense_to_corrector/offense"
11
+ require "offense_to_corrector/offense_parser"
12
+ require "offense_to_corrector/offense_template"
13
+
8
14
  module OffenseToCorrector
9
15
  module_function def load_template(name)
10
16
  File.join(File.dirname(__FILE__), "offense_to_corrector/templates", name)
@@ -17,242 +23,4 @@ module OffenseToCorrector
17
23
  module_function def offense_to_cop(code)
18
24
  OffenseParser.new(code).render
19
25
  end
20
-
21
- # The annoying thing is that `RuboCop::AST::Node` 's children / descendant
22
- # methods don't capture all the relevant data, so we have to cheat a bit
23
- # by wrapping atoms (String, Symbol, Int, etc) in a class to get around that.
24
- AtomNode = Struct.new(:source, :range, :parent, keyword_init: true) do
25
- def type
26
- self.parent.type
27
- end
28
-
29
- def to_s
30
- relevant_children = self.parent.children.map do |c|
31
- next "..." unless c.to_s == self.source.to_s # Wildcard
32
-
33
- case c
34
- when String then %("#{c}") # Literal string
35
- when Symbol then ":#{c}" # Literal symbol
36
- else c
37
- end
38
- end
39
-
40
- # The trick here is that these aren't nodes, but we do care about
41
- # what the "parent" is that contains it to get something we can
42
- # work with. All other children are replaced with wildcards.
43
- "(#{self.parent.type} #{relevant_children.join(' ')})"
44
- end
45
- end
46
-
47
- module AstTools
48
- def atom?(value)
49
- !value.is_a?(RuboCop::AST::Node)
50
- end
51
-
52
- # I may have to work on getting this one later to try and
53
- # narrow the band of `AtomNode`
54
- # def childless?(node)
55
- # false # TODO
56
- # end
57
-
58
- # How much do two ranges overlap? Used to see how well a node
59
- # matches with the associated underline
60
- def range_overlap_count(a, b)
61
- return (b.begin...[a.end, b.end].min).size if a.cover?(b.begin)
62
- return (a.begin...[a.end, b.end].min).size if b.cover?(a.begin)
63
-
64
- 0
65
- end
66
-
67
- # See if a range overlaps another one, bidirectional
68
- def range_overlap?(a, b)
69
- a.cover?(b.begin) || b.cover?(a.begin)
70
- end
71
-
72
- # To get an AST we need the processed source of a string
73
- def processed_source_from(string)
74
- RuboCop::ProcessedSource.new(string, RUBY_VERSION.to_f)
75
- end
76
-
77
- # So why bother with the above then? If we end up into correctors
78
- # and tree-rewrites we need that original processed source to be
79
- # the basis of the AST, otherwise we get object ID mismatches.
80
- def ast_from(value)
81
- case value
82
- when RuboCop::ProcessedSource
83
- value.ast
84
- else
85
- processed_source_from(value).ast
86
- end
87
- end
88
-
89
- # Not needed quite yet, but could very potentially be used to verify
90
- # how accurate generated cops are.
91
- def get_corrector(value)
92
- RuboCop::Cop::Corrector.new(value.buffer)
93
- end
94
-
95
- # Descendants leaves out a _lot_ of detail potentially. There has to
96
- # be a better way to deal with this, but not thinking of one right now.
97
- def get_children(source_node)
98
- recurse = -> node do
99
- collected_children = []
100
- node.children.each do |child|
101
- next if child.nil?
102
-
103
- # If it's a regular node carry on as you were
104
- unless atom?(child) # || childless?(child)
105
- collected_children.concat([child, *recurse[child]])
106
- next
107
- end
108
-
109
- # Otherwise we want to find where that node is in the source code,
110
- # and the range it exists in, to create an `AtomNode`
111
- child_string = child.to_s
112
- range_begin = source_node.source.index(/\b#{child_string}\b/)
113
- range_end = range_begin + child_string.size
114
-
115
- collected_children << AtomNode.new(
116
- parent: node,
117
- source: child_string,
118
- range: range_begin..range_end
119
- )
120
- end
121
-
122
- collected_children
123
- end
124
-
125
- [source_node, *recurse[source_node]]
126
- end
127
- end
128
-
129
- # ERB template for rendering a cop skeleton, may make this more useful
130
- # later, but mostly quick templating for now.
131
- class OffenseTemplate
132
- def initialize(name: "autocorrector_template.erb")
133
- @template = File.read(OffenseToCorrector.load_template(name))
134
- @erb = ERB.new(@template)
135
- end
136
-
137
- def render(
138
- class_name: "TODO",
139
- match_pattern:,
140
- error_message: "",
141
- node_type:,
142
- cop_type: "Lint",
143
- node_location: "selector",
144
- offense_severity: "warning"
145
- )
146
- @erb.result_with_hash(
147
- class_name:,
148
- match_pattern:,
149
- error_message:,
150
- node_type:,
151
- cop_type:,
152
- node_location:,
153
- offense_severity:
154
- )
155
- end
156
- end
157
-
158
- # Bit more of a structured container for an offense (the underline)
159
- Offense = Struct.new(:line, :error, :range, keyword_init: true)
160
-
161
- class OffenseParser
162
- include AstTools
163
-
164
- # How many underline carots (^), and potentially an error after
165
- OFFENSE_MATCH = /^ *?(?<underline>\^+) *?(?<error>.*)?$/
166
-
167
- attr_reader :ast, :source, :offense
168
-
169
- def initialize(string)
170
- @ast_lines, @offense = parse_string(string)
171
- @source = processed_source_from(@ast_lines.join("\n"))
172
- @ast = ast_from(@source)
173
- @ast_nodes = get_children(@ast)
174
- @template = OffenseTemplate.new
175
- end
176
-
177
- # Render that into the cop skeleton. Perhaps having too much
178
- # fun here with rightward assignment.
179
- def render
180
- call => {
181
- offense: { error: },
182
- node_offense_info: { offending_node:, offending_node_matcher: }
183
- }
184
-
185
- @template.render(
186
- class_name: "TODO",
187
- match_pattern: offending_node_matcher,
188
- error_message: error,
189
- node_type: offending_node.type,
190
- cop_type: "Lint",
191
- node_location: "selector",
192
- offense_severity: "warning"
193
- )
194
- end
195
-
196
- # Quick info on what all is being worked on and what info we got
197
- def call
198
- { ast:, offense:, node_offense_info: }
199
- end
200
-
201
- def node_offense_info
202
- return @node_offense_info if defined?(@node_offense_info)
203
-
204
- # Find the node, or atom, with the most overlap with the offense
205
- # range defined by that underline.
206
- offending_node = @ast_nodes.max do |node|
207
- node_range = if node.is_a?(AtomNode)
208
- node.range
209
- else
210
- node.location.expression.to_range
211
- end
212
-
213
- # Except we're doing it as a percentage, otherwise parent nodes
214
- # will dominate that count potentially. The closer to 100% overlap
215
- # the better.
216
- overlap_count = range_overlap_count(node_range, @offense[:range])
217
- overlap_count.fdiv(node.source.size)
218
- end
219
-
220
- @node_offense_info ||= {
221
- offending_node:,
222
- offending_node_matcher: offending_node.to_s
223
- }
224
- end
225
-
226
- # See if there's an underline, if so get how long it is and
227
- # the error message after it
228
- private def offending_meta_from(line)
229
- match_data = OFFENSE_MATCH.match(line) or return nil
230
- underline = match_data[:underline]
231
- error = match_data[:error].lstrip
232
-
233
- underline_start = line.index(underline)
234
- underline_end = underline_start + underline.size
235
-
236
- { match_data:, error:, range: underline_start..underline_end }
237
- end
238
-
239
- # Figure which part of the passed in code string is AST vs offense
240
- private def parse_string(string)
241
- ast_lines = []
242
- offense = nil
243
-
244
- string.lines.each_with_index do |line, i|
245
- meta_info = offending_meta_from(line)
246
-
247
- if meta_info
248
- raise "Cannot have multiple offenses" unless offense.nil?
249
- offense = Offense.new(line: i, **meta_info.slice(:error, :range))
250
- else
251
- ast_lines << line
252
- end
253
- end
254
-
255
- [ast_lines, offense]
256
- end
257
- end
258
26
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: offense_to_corrector
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brandon Weaver
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: guard-rspec
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
27
41
  description:
28
42
  email:
29
43
  - keystonelemur@gmail.com
@@ -36,10 +50,16 @@ files:
36
50
  - CODE_OF_CONDUCT.md
37
51
  - Gemfile
38
52
  - Gemfile.lock
53
+ - Guardfile
39
54
  - LICENSE.txt
40
55
  - README.md
41
56
  - Rakefile
42
57
  - lib/offense_to_corrector.rb
58
+ - lib/offense_to_corrector/ast_tools.rb
59
+ - lib/offense_to_corrector/atom_node.rb
60
+ - lib/offense_to_corrector/offense.rb
61
+ - lib/offense_to_corrector/offense_parser.rb
62
+ - lib/offense_to_corrector/offense_template.rb
43
63
  - lib/offense_to_corrector/templates/autocorrector_template.erb
44
64
  - lib/offense_to_corrector/version.rb
45
65
  - sig/offense_to_corrector.rbs