slim_lint 0.35.0 → 0.36.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.
- checksums.yaml +4 -4
- data/lib/slim_lint/corrector.rb +44 -0
- data/lib/slim_lint/document.rb +9 -2
- data/lib/slim_lint/lint.rb +31 -6
- data/lib/slim_lint/linter/quote_consistency.rb +30 -2
- data/lib/slim_lint/linter/redundant_div.rb +3 -1
- data/lib/slim_lint/linter/tag_case.rb +5 -1
- data/lib/slim_lint/linter/trailing_whitespace.rb +3 -1
- data/lib/slim_lint/linter.rb +20 -2
- data/lib/slim_lint/options.rb +5 -0
- data/lib/slim_lint/report.rb +1 -1
- data/lib/slim_lint/reporter/default_reporter.rb +2 -1
- data/lib/slim_lint/runner.rb +22 -3
- data/lib/slim_lint/version.rb +1 -1
- data/lib/slim_lint.rb +1 -0
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: dd046afc687f2cd47b2766c90af0d4cee87282f818f60954530dc9e579a3a797
|
|
4
|
+
data.tar.gz: a6378966b0a438ba9cdf86f5771262f7e7eebb4a40b1b915ec95a68a7d51eb05
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 13c3f8d0aacd071c62fdd1ddea37c690a48a53df0170a32575cced14ad6e828ca6a2a1bccbcc6cdd915cbf54a4b917d02099a75f51c08417395861a53b6933d4
|
|
7
|
+
data.tar.gz: d063b8fe3e5620ecc68a1bb6b2a38ceb0b5431971ec94c6a3cb4059be77d8b87e122c2ef7fae4cc1c4b1325bc9c5ffbb59d237b19caf22974977086410cf614d
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SlimLint
|
|
4
|
+
# Applies the corrections attached to a set of lints to a document's
|
|
5
|
+
# source, producing the corrected source text.
|
|
6
|
+
class Corrector
|
|
7
|
+
# @param document [SlimLint::Document] document the lints were reported against
|
|
8
|
+
def initialize(document)
|
|
9
|
+
@document = document
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# Applies every correctable lint's correction to the document's source
|
|
13
|
+
# and marks each applied lint as corrected.
|
|
14
|
+
#
|
|
15
|
+
# @param lints [Array<SlimLint::Lint>]
|
|
16
|
+
# @return [String] the corrected source
|
|
17
|
+
def correct(lints)
|
|
18
|
+
lines = @document.source_lines.dup
|
|
19
|
+
|
|
20
|
+
lints.select(&:correctable?).group_by(&:line).each do |line_number, line_lints|
|
|
21
|
+
index = line_number - 1
|
|
22
|
+
next unless lines[index]
|
|
23
|
+
|
|
24
|
+
line_lints.each do |lint|
|
|
25
|
+
lines[index] = lint.correction.call(lines[index])
|
|
26
|
+
lint.corrected = true
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
corrected_source(lines)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
# @param lines [Array<String>]
|
|
36
|
+
# @return [String]
|
|
37
|
+
def corrected_source(lines)
|
|
38
|
+
source = lines.join("\n")
|
|
39
|
+
trailing_newlines = @document.source[/\n+\z/]
|
|
40
|
+
source += trailing_newlines if trailing_newlines
|
|
41
|
+
@document.source_prefix + source
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
data/lib/slim_lint/document.rb
CHANGED
|
@@ -15,6 +15,12 @@ module SlimLint
|
|
|
15
15
|
# @return [String] original source code
|
|
16
16
|
attr_reader :source
|
|
17
17
|
|
|
18
|
+
# @return [String] source code before optional frontmatter removal
|
|
19
|
+
attr_reader :original_source
|
|
20
|
+
|
|
21
|
+
# @return [String] source removed before linting, such as frontmatter
|
|
22
|
+
attr_reader :source_prefix
|
|
23
|
+
|
|
18
24
|
# @return [Array<String>] original source code as an array of lines
|
|
19
25
|
attr_reader :source_lines
|
|
20
26
|
|
|
@@ -36,8 +42,9 @@ module SlimLint
|
|
|
36
42
|
# @param source [String] Slim code to parse
|
|
37
43
|
# @raise [SlimLint::Exceptions::ParseError] if there was a problem parsing the document
|
|
38
44
|
def process_source(source)
|
|
39
|
-
@
|
|
40
|
-
@source = strip_frontmatter(
|
|
45
|
+
@original_source = process_encoding(source)
|
|
46
|
+
@source = strip_frontmatter(@original_source)
|
|
47
|
+
@source_prefix = @original_source[0, @original_source.length - @source.length] || ''
|
|
41
48
|
@source_lines = @source.split("\n")
|
|
42
49
|
|
|
43
50
|
engine = SlimLint::Engine.new(file: @file)
|
data/lib/slim_lint/lint.rb
CHANGED
|
@@ -18,6 +18,13 @@ module SlimLint
|
|
|
18
18
|
# @return [Symbol] whether this lint is a warning or an error
|
|
19
19
|
attr_reader :severity
|
|
20
20
|
|
|
21
|
+
# @return [Proc, nil] maps a line of source to its corrected version, or
|
|
22
|
+
# `nil` if this lint has no known automatic correction
|
|
23
|
+
attr_reader :correction
|
|
24
|
+
|
|
25
|
+
# @return [Boolean] whether this lint's correction has been applied
|
|
26
|
+
attr_accessor :corrected
|
|
27
|
+
|
|
21
28
|
# Creates a new lint.
|
|
22
29
|
#
|
|
23
30
|
# @param linter [SlimLint::Linter]
|
|
@@ -25,12 +32,16 @@ module SlimLint
|
|
|
25
32
|
# @param line [Fixnum]
|
|
26
33
|
# @param message [String]
|
|
27
34
|
# @param severity [Symbol]
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
@
|
|
32
|
-
@
|
|
33
|
-
@
|
|
35
|
+
# @param correction [Proc, nil] maps a line of source to its corrected version
|
|
36
|
+
def initialize(linter, filename, line, message, severity = :warning, # rubocop:disable Metrics/ParameterLists
|
|
37
|
+
correction: nil)
|
|
38
|
+
@linter = linter
|
|
39
|
+
@filename = filename
|
|
40
|
+
@line = line || 0
|
|
41
|
+
@message = message
|
|
42
|
+
@severity = severity
|
|
43
|
+
@correction = correction
|
|
44
|
+
@corrected = false
|
|
34
45
|
end
|
|
35
46
|
|
|
36
47
|
# Return whether this lint has a severity of error.
|
|
@@ -39,5 +50,19 @@ module SlimLint
|
|
|
39
50
|
def error?
|
|
40
51
|
@severity == :error
|
|
41
52
|
end
|
|
53
|
+
|
|
54
|
+
# Return whether this lint has a known automatic correction.
|
|
55
|
+
#
|
|
56
|
+
# @return [Boolean]
|
|
57
|
+
def correctable?
|
|
58
|
+
!@correction.nil?
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Return whether this lint's correction has been applied.
|
|
62
|
+
#
|
|
63
|
+
# @return [Boolean]
|
|
64
|
+
def corrected?
|
|
65
|
+
@corrected
|
|
66
|
+
end
|
|
42
67
|
end
|
|
43
68
|
end
|
|
@@ -4,6 +4,7 @@ module SlimLint
|
|
|
4
4
|
# Checks for consistent quote usage in HTML attributes
|
|
5
5
|
class Linter::QuoteConsistency < Linter
|
|
6
6
|
include LinterRegistry
|
|
7
|
+
support_autocorrect
|
|
7
8
|
|
|
8
9
|
MSG = 'Inconsistent quote style. %s'
|
|
9
10
|
|
|
@@ -19,15 +20,42 @@ module SlimLint
|
|
|
19
20
|
|
|
20
21
|
if enforced_style == :single_quotes && double_quotes.any?
|
|
21
22
|
report_lint(node,
|
|
22
|
-
format(MSG, "Use single quotes for attribute values (')")
|
|
23
|
+
format(MSG, "Use single quotes for attribute values (')"),
|
|
24
|
+
correction: ->(source_line) { correct_quotes(source_line, '"', "'") })
|
|
23
25
|
elsif enforced_style == :double_quotes && single_quotes.any?
|
|
24
26
|
report_lint(node,
|
|
25
|
-
format(MSG, 'Use double quotes for attribute values (")')
|
|
27
|
+
format(MSG, 'Use double quotes for attribute values (")'),
|
|
28
|
+
correction: ->(source_line) { correct_quotes(source_line, "'", '"') })
|
|
26
29
|
end
|
|
27
30
|
end
|
|
28
31
|
|
|
29
32
|
private
|
|
30
33
|
|
|
34
|
+
def correct_quotes(source_line, from, to)
|
|
35
|
+
state = { quote: nil, corrected: [] }
|
|
36
|
+
source_line.scan(/\\.|['"]|[^\\'"]+/).each do |token|
|
|
37
|
+
correct_quote_token(token, state, from, to)
|
|
38
|
+
end
|
|
39
|
+
state[:corrected].join
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def correct_quote_token(token, state, from, to)
|
|
43
|
+
return state[:corrected] << token if token.length > 1 || token.start_with?('\\')
|
|
44
|
+
|
|
45
|
+
if state[:quote]
|
|
46
|
+
close_quote_token(token, state, from, to)
|
|
47
|
+
else
|
|
48
|
+
state[:quote] = token
|
|
49
|
+
state[:corrected] << (token == from ? to : token)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def close_quote_token(token, state, from, to)
|
|
54
|
+
replacement = token == state[:quote] && token == from ? to : token
|
|
55
|
+
state[:corrected] << replacement
|
|
56
|
+
state[:quote] = nil if token == state[:quote]
|
|
57
|
+
end
|
|
58
|
+
|
|
31
59
|
def enforced_style
|
|
32
60
|
config['enforced_style']&.to_sym || :single_quotes
|
|
33
61
|
end
|
|
@@ -5,6 +5,7 @@ module SlimLint
|
|
|
5
5
|
# already implies a div.
|
|
6
6
|
class Linter::RedundantDiv < Linter
|
|
7
7
|
include LinterRegistry
|
|
8
|
+
support_autocorrect
|
|
8
9
|
|
|
9
10
|
MESSAGE = '`div` is redundant when %s attribute shortcut is present'
|
|
10
11
|
|
|
@@ -16,7 +17,8 @@ module SlimLint
|
|
|
16
17
|
attr = captures[:attr_name]
|
|
17
18
|
next unless %w[class id].include?(attr)
|
|
18
19
|
|
|
19
|
-
report_lint(sexp, MESSAGE % attr
|
|
20
|
+
report_lint(sexp, MESSAGE % attr,
|
|
21
|
+
correction: ->(line) { line.sub(/\bdiv(?=[.#])/, '') })
|
|
20
22
|
end
|
|
21
23
|
end
|
|
22
24
|
end
|
|
@@ -4,12 +4,16 @@ module SlimLint
|
|
|
4
4
|
# Searches for tags with uppercase characters.
|
|
5
5
|
class Linter::TagCase < Linter
|
|
6
6
|
include LinterRegistry
|
|
7
|
+
support_autocorrect
|
|
7
8
|
|
|
8
9
|
on [:html, :tag] do |sexp|
|
|
9
10
|
_, _, name = sexp
|
|
10
11
|
next unless name[/[A-Z]/]
|
|
11
12
|
|
|
12
|
-
report_lint(sexp, "Tag `#{name}` should be written as `#{name.downcase}`"
|
|
13
|
+
report_lint(sexp, "Tag `#{name}` should be written as `#{name.downcase}`",
|
|
14
|
+
correction: ->(line) {
|
|
15
|
+
line.sub(/\b#{Regexp.escape(name.to_s)}\b/, name.downcase)
|
|
16
|
+
})
|
|
13
17
|
end
|
|
14
18
|
end
|
|
15
19
|
end
|
|
@@ -4,6 +4,7 @@ module SlimLint
|
|
|
4
4
|
# Checks for trailing whitespace.
|
|
5
5
|
class Linter::TrailingWhitespace < Linter
|
|
6
6
|
include LinterRegistry
|
|
7
|
+
support_autocorrect
|
|
7
8
|
|
|
8
9
|
on_start do |_sexp|
|
|
9
10
|
dummy_node = Struct.new(:line)
|
|
@@ -12,7 +13,8 @@ module SlimLint
|
|
|
12
13
|
next unless line =~ /\s+$/
|
|
13
14
|
|
|
14
15
|
report_lint(dummy_node.new(index + 1),
|
|
15
|
-
'Line contains trailing whitespace'
|
|
16
|
+
'Line contains trailing whitespace',
|
|
17
|
+
correction: lambda(&:rstrip))
|
|
16
18
|
end
|
|
17
19
|
end
|
|
18
20
|
end
|
data/lib/slim_lint/linter.rb
CHANGED
|
@@ -15,6 +15,21 @@ module SlimLint
|
|
|
15
15
|
# lints for the subject instead of the linter itself.
|
|
16
16
|
attr_reader :lints
|
|
17
17
|
|
|
18
|
+
class << self
|
|
19
|
+
# Declares that this linter can automatically correct (some of) the
|
|
20
|
+
# offenses it reports.
|
|
21
|
+
def support_autocorrect
|
|
22
|
+
@supports_autocorrect = true
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Returns whether this linter declared autocorrect support.
|
|
26
|
+
#
|
|
27
|
+
# @return [Boolean]
|
|
28
|
+
def supports_autocorrect?
|
|
29
|
+
@supports_autocorrect || false
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
18
33
|
# Initializes a linter with the specified configuration.
|
|
19
34
|
#
|
|
20
35
|
# @param config [Hash] configuration for this linter
|
|
@@ -49,10 +64,13 @@ module SlimLint
|
|
|
49
64
|
#
|
|
50
65
|
# @param node [#line] node to extract the line number from
|
|
51
66
|
# @param message [String] error/warning to display to the user
|
|
52
|
-
|
|
67
|
+
# @param correction [Proc, nil] maps the offending line of source to its
|
|
68
|
+
# corrected version, for linters that declared {support_autocorrect}
|
|
69
|
+
def report_lint(node, message, correction: nil)
|
|
53
70
|
return if disabled_for_line?(node.line)
|
|
54
71
|
|
|
55
|
-
@lints << SlimLint::Lint.new(self, @document.file, node.line, message
|
|
72
|
+
@lints << SlimLint::Lint.new(self, @document.file, node.line, message,
|
|
73
|
+
correction: correction)
|
|
56
74
|
end
|
|
57
75
|
|
|
58
76
|
# Parse Ruby code into an abstract syntax tree.
|
data/lib/slim_lint/options.rb
CHANGED
|
@@ -48,6 +48,11 @@ module SlimLint
|
|
|
48
48
|
'Specify which reporter you want to use to generate the output') do |reporter|
|
|
49
49
|
@options[:reporter] = load_reporter_class(reporter.capitalize)
|
|
50
50
|
end
|
|
51
|
+
|
|
52
|
+
parser.on('-a', '--autocorrect',
|
|
53
|
+
'Automatically correct offenses that support it') do
|
|
54
|
+
@options[:autocorrect] = true
|
|
55
|
+
end
|
|
51
56
|
end
|
|
52
57
|
|
|
53
58
|
# Returns the class of the specified Reporter.
|
data/lib/slim_lint/report.rb
CHANGED
data/lib/slim_lint/runner.rb
CHANGED
|
@@ -20,7 +20,8 @@ module SlimLint
|
|
|
20
20
|
if options[:stdin_file_path].nil?
|
|
21
21
|
files = extract_applicable_files(config, options)
|
|
22
22
|
lints = files.map do |file|
|
|
23
|
-
collect_lints(File.read(file), file, linter_selector, config
|
|
23
|
+
collect_lints(File.read(file), file, linter_selector, config,
|
|
24
|
+
autocorrect: options[:autocorrect])
|
|
24
25
|
end.flatten
|
|
25
26
|
else
|
|
26
27
|
files = [options[:stdin_file_path]]
|
|
@@ -53,16 +54,34 @@ module SlimLint
|
|
|
53
54
|
# @param file [String] path to file to lint
|
|
54
55
|
# @param linter_selector [SlimLint::LinterSelector]
|
|
55
56
|
# @param config [SlimLint::Configuration]
|
|
56
|
-
|
|
57
|
+
# @param autocorrect [Boolean] whether to write corrections back to the file
|
|
58
|
+
def collect_lints(file_content, file_name, linter_selector, config, autocorrect: false)
|
|
57
59
|
begin
|
|
58
60
|
document = SlimLint::Document.new(file_content, file: file_name, config: config)
|
|
59
61
|
rescue SlimLint::Exceptions::ParseError => e
|
|
60
62
|
return [SlimLint::Lint.new(nil, file_name, e.lineno, e.error, :error)]
|
|
61
63
|
end
|
|
62
64
|
|
|
63
|
-
linter_selector.linters_for_file(file_name).map do |linter|
|
|
65
|
+
lints = linter_selector.linters_for_file(file_name).map do |linter|
|
|
64
66
|
linter.run(document)
|
|
65
67
|
end.flatten
|
|
68
|
+
|
|
69
|
+
correct_lints(document, file_name, lints) if autocorrect
|
|
70
|
+
|
|
71
|
+
lints
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Applies any available corrections to the given file's lints and writes
|
|
75
|
+
# the result back to disk if anything changed.
|
|
76
|
+
#
|
|
77
|
+
# @param document [SlimLint::Document]
|
|
78
|
+
# @param file_name [String] path to file to correct
|
|
79
|
+
# @param lints [Array<SlimLint::Lint>]
|
|
80
|
+
def correct_lints(document, file_name, lints)
|
|
81
|
+
corrected_source = SlimLint::Corrector.new(document).correct(lints)
|
|
82
|
+
return if corrected_source == document.original_source
|
|
83
|
+
|
|
84
|
+
File.write(file_name, corrected_source)
|
|
66
85
|
end
|
|
67
86
|
|
|
68
87
|
# Returns the list of files that should be linted given the specified
|
data/lib/slim_lint/version.rb
CHANGED
data/lib/slim_lint.rb
CHANGED
|
@@ -30,6 +30,7 @@ require_relative 'slim_lint/sexp_visitor'
|
|
|
30
30
|
require_relative 'slim_lint/lint'
|
|
31
31
|
require_relative 'slim_lint/ruby_parser'
|
|
32
32
|
require_relative 'slim_lint/linter'
|
|
33
|
+
require_relative 'slim_lint/corrector'
|
|
33
34
|
require_relative 'slim_lint/reporter'
|
|
34
35
|
require_relative 'slim_lint/report'
|
|
35
36
|
require_relative 'slim_lint/linter_selector'
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: slim_lint
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.36.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Shane da Silva
|
|
@@ -109,6 +109,7 @@ files:
|
|
|
109
109
|
- lib/slim_lint/configuration.rb
|
|
110
110
|
- lib/slim_lint/configuration_loader.rb
|
|
111
111
|
- lib/slim_lint/constants.rb
|
|
112
|
+
- lib/slim_lint/corrector.rb
|
|
112
113
|
- lib/slim_lint/document.rb
|
|
113
114
|
- lib/slim_lint/engine.rb
|
|
114
115
|
- lib/slim_lint/exceptions.rb
|