slim_lint 0.36.0 → 0.37.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dd046afc687f2cd47b2766c90af0d4cee87282f818f60954530dc9e579a3a797
4
- data.tar.gz: a6378966b0a438ba9cdf86f5771262f7e7eebb4a40b1b915ec95a68a7d51eb05
3
+ metadata.gz: 1ab51b789afce474d3a969052cf00dc0760a2d98706aa74d2d5f2daf2c58ad44
4
+ data.tar.gz: ad86883e7bb179a3f7f7c01c2aad5e07d57c359f790ca3541d3da78f3af17516
5
5
  SHA512:
6
- metadata.gz: 13c3f8d0aacd071c62fdd1ddea37c690a48a53df0170a32575cced14ad6e828ca6a2a1bccbcc6cdd915cbf54a4b917d02099a75f51c08417395861a53b6933d4
7
- data.tar.gz: d063b8fe3e5620ecc68a1bb6b2a38ceb0b5431971ec94c6a3cb4059be77d8b87e122c2ef7fae4cc1c4b1325bc9c5ffbb59d237b19caf22974977086410cf614d
6
+ metadata.gz: 7223d666bd6ae4e0c266431a5ab8edd34a89a7862ca6a7b7aca9f7171fcea57016a622ea95794372f11579ad62136e97279d54988f4c5e70f7bc3305f71c916e
7
+ data.tar.gz: 5de9a96b2055eeeae3f5af80053bf0af37f85d7821c874de7584da6a8a0837811dfe84aca1461c1b7c27c541bef306d08adb73d516a7efb3400790858b1b6d17
@@ -12,33 +12,55 @@ module SlimLint
12
12
  # Applies every correctable lint's correction to the document's source
13
13
  # and marks each applied lint as corrected.
14
14
  #
15
+ # A correction maps the current text of its line to the corrected text:
16
+ # returning `nil` removes the line entirely, and returning a string
17
+ # containing embedded newlines replaces the line with multiple lines.
18
+ # Corrections are applied from the last line to the first so that a
19
+ # correction which adds or removes lines never invalidates the line
20
+ # numbers of corrections still waiting to be applied.
21
+ #
15
22
  # @param lints [Array<SlimLint::Lint>]
16
23
  # @return [String] the corrected source
17
24
  def correct(lints)
18
- lines = @document.source_lines.dup
25
+ # `-1` keeps a trailing empty element when the source ends with a
26
+ # newline, so the array can be joined back with "\n" to exactly
27
+ # reproduce the source (including its trailing newline, or lack of one).
28
+ lines = @document.source.split("\n", -1)
19
29
 
20
- lints.select(&:correctable?).group_by(&:line).each do |line_number, line_lints|
30
+ correction_groups(lints).each do |line_number, line_lints|
21
31
  index = line_number - 1
22
- next unless lines[index]
32
+ next unless index.between?(0, lines.length - 1)
23
33
 
24
- line_lints.each do |lint|
25
- lines[index] = lint.correction.call(lines[index])
26
- lint.corrected = true
27
- end
34
+ lines[index, 1] = apply_corrections(lines[index], line_lints)
28
35
  end
29
36
 
30
- corrected_source(lines)
37
+ @document.source_prefix + lines.join("\n")
31
38
  end
32
39
 
33
40
  private
34
41
 
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
+ # Groups correctable lints by line number, ordered from the last line to
43
+ # the first, so that a correction which adds or removes lines never
44
+ # invalidates the line numbers of corrections still waiting to be applied.
45
+ #
46
+ # @param lints [Array<SlimLint::Lint>]
47
+ # @return [Array<(Integer, Array<SlimLint::Lint>)>]
48
+ def correction_groups(lints)
49
+ lints.select(&:correctable?).group_by(&:line).sort_by { |line_number, _| -line_number }
50
+ end
51
+
52
+ # @param line [String] current text of the line
53
+ # @param line_lints [Array<SlimLint::Lint>] lints reported on this line
54
+ # @return [Array<String>] replacement line(s) for this line, possibly empty
55
+ def apply_corrections(line, line_lints)
56
+ line_lints.each do |lint|
57
+ break if line.nil?
58
+
59
+ line = lint.correction.call(line)
60
+ lint.corrected = true
61
+ end
62
+
63
+ line.nil? ? [] : line.split("\n", -1)
42
64
  end
43
65
  end
44
66
  end
@@ -18,8 +18,10 @@ 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
21
+ # @return [Proc, nil] maps a line of source to its corrected version
22
+ # (`nil` to remove the line, or a string with embedded newlines to
23
+ # replace it with multiple lines), or `nil` if this lint has no known
24
+ # automatic correction
23
25
  attr_reader :correction
24
26
 
25
27
  # @return [Boolean] whether this lint's correction has been applied
@@ -4,6 +4,7 @@ module SlimLint
4
4
  # Searches for control statements with only comments.
5
5
  class Linter::CommentControlStatement < Linter
6
6
  include LinterRegistry
7
+ support_autocorrect
7
8
 
8
9
  on [:slim, :control] do |sexp|
9
10
  _, _, code = sexp
@@ -16,7 +17,10 @@ module SlimLint
16
17
 
17
18
  report_lint(sexp,
18
19
  "Slim code comments (`/#{comment}`) are preferred over " \
19
- "control statement comments (`-##{comment}`)")
20
+ "control statement comments (`-##{comment}`)",
21
+ correction: ->(line) {
22
+ line.sub(/-\s*#{Regexp.escape(code.to_s)}/, "/#{comment}")
23
+ })
20
24
  end
21
25
  end
22
26
  end
@@ -4,10 +4,17 @@ module SlimLint
4
4
  # Checks for missing or superfluous spacing before and after control statements.
5
5
  class Linter::ControlStatementSpacing < Linter
6
6
  include LinterRegistry
7
+ support_autocorrect
7
8
 
8
9
  MESSAGE_OUTPUT = 'Please add a space before and after the `=`'
9
10
  MESSAGE_CONTROL = 'Please add a space after the `-`'
10
11
 
12
+ # Matches the tag/attribute-shortcut selector, the `=`-based operator
13
+ # (`=`, `==`, `=<`, `=>`, `=<>`, `==<`, `==>`, `==<>`), and the whitespace
14
+ # surrounding the operator, so it can be normalized to a single space on
15
+ # either side without touching the Ruby code that follows.
16
+ OUTPUT_SPACING = /^(\s*)(\S+?)(\s*)(=[=<>]*)(\s*)/
17
+
11
18
  on [:html, :tag, anything, [],
12
19
  [:slim, :output, anything, capture(:ruby, anything)]] do |sexp|
13
20
  # Fetch original Slim code that contains an element with a control statement.
@@ -20,7 +27,8 @@ module SlimLint
20
27
 
21
28
  next if line =~ /[^ ] ==?<?>? [^ ]/
22
29
 
23
- report_lint(sexp, MESSAGE_OUTPUT)
30
+ report_lint(sexp, MESSAGE_OUTPUT,
31
+ correction: ->(source_line) { source_line.sub(OUTPUT_SPACING, '\1\2 \4 ') })
24
32
  end
25
33
 
26
34
  on [:slim, :control] do |sexp|
@@ -30,7 +38,8 @@ module SlimLint
30
38
 
31
39
  next if line =~ /^ *- [^ ]/
32
40
 
33
- report_lint(sexp, MESSAGE_CONTROL)
41
+ report_lint(sexp, MESSAGE_CONTROL,
42
+ correction: ->(source_line) { source_line.sub(/^(\s*)-\s*/, '\1- ') })
34
43
  end
35
44
 
36
45
  private
@@ -4,12 +4,14 @@ module SlimLint
4
4
  # Searches for control statements with no code.
5
5
  class Linter::EmptyControlStatement < Linter
6
6
  include LinterRegistry
7
+ support_autocorrect
7
8
 
8
9
  on [:slim, :control] do |sexp|
9
10
  _, _, code = sexp
10
11
  next unless code[/\A\s*\Z/]
11
12
 
12
- report_lint(sexp, 'Empty control statement can be removed')
13
+ report_lint(sexp, 'Empty control statement can be removed',
14
+ correction: ->(_line) { nil })
13
15
  end
14
16
  end
15
17
  end
@@ -5,6 +5,7 @@ module SlimLint
5
5
  # and for the first blank line in file.
6
6
  class Linter::EmptyLines < Linter
7
7
  include LinterRegistry
8
+ support_autocorrect
8
9
 
9
10
  on_start do |_sexp|
10
11
  dummy_node = Struct.new(:line)
@@ -14,7 +15,8 @@ module SlimLint
14
15
  if line.blank?
15
16
  if was_empty
16
17
  report_lint(dummy_node.new(i + 1),
17
- 'Extra empty line detected')
18
+ 'Extra empty line detected',
19
+ correction: ->(_line) { nil })
18
20
  end
19
21
  was_empty = true
20
22
  else
@@ -4,6 +4,7 @@ module SlimLint
4
4
  # This linter looks for trailing blank lines and a final newline.
5
5
  class Linter::TrailingBlankLines < Linter
6
6
  include LinterRegistry
7
+ support_autocorrect
7
8
 
8
9
  on_start do |_sexp|
9
10
  dummy_node = Struct.new(:line)
@@ -11,10 +12,12 @@ module SlimLint
11
12
 
12
13
  if !document.source.end_with?("\n")
13
14
  report_lint(dummy_node.new(document.source_lines.size),
14
- 'No blank line in the end of file')
15
+ 'No blank line in the end of file',
16
+ correction: ->(line) { "#{line}\n" })
15
17
  elsif document.source.lines.last.blank?
16
18
  report_lint(dummy_node.new(document.source.lines.size),
17
- 'Multiple empty lines in the end of file')
19
+ 'Multiple empty lines in the end of file',
20
+ correction: ->(_line) { nil })
18
21
  end
19
22
  end
20
23
  end
@@ -3,6 +3,7 @@
3
3
  module SlimLint
4
4
  class Linter::Zwsp < Linter
5
5
  include LinterRegistry
6
+ support_autocorrect
6
7
 
7
8
  MSG = 'Remove zero-width space'
8
9
 
@@ -11,7 +12,8 @@ module SlimLint
11
12
  document.source_lines.each_with_index do |line, index|
12
13
  next unless line.include?("\u200b")
13
14
 
14
- report_lint(dummy_node.new(index + 1), MSG)
15
+ report_lint(dummy_node.new(index + 1), MSG,
16
+ correction: ->(source_line) { source_line.delete("\u200b") })
15
17
  end
16
18
  end
17
19
  end
@@ -65,7 +65,9 @@ module SlimLint
65
65
  # @param node [#line] node to extract the line number from
66
66
  # @param message [String] error/warning to display to the user
67
67
  # @param correction [Proc, nil] maps the offending line of source to its
68
- # corrected version, for linters that declared {support_autocorrect}
68
+ # corrected version, for linters that declared {support_autocorrect}.
69
+ # Return `nil` to remove the line entirely, or a string containing
70
+ # embedded newlines to replace it with multiple lines.
69
71
  def report_lint(node, message, correction: nil)
70
72
  return if disabled_for_line?(node.line)
71
73
 
@@ -5,6 +5,10 @@ require 'optparse'
5
5
  module SlimLint
6
6
  # Handles option parsing for the command line application.
7
7
  class Options
8
+ # Path scanned by default when no files or directories are given on the
9
+ # command line.
10
+ DEFAULT_FILES = ['.'].freeze
11
+
8
12
  # Parses command line options into an options hash.
9
13
  #
10
14
  # @param args [Array<String>] arguments passed via the command line
@@ -13,15 +17,18 @@ module SlimLint
13
17
  @options = {}
14
18
 
15
19
  OptionParser.new do |parser|
16
- parser.banner = "Usage: #{APP_NAME} [options] [file1, file2, ...]"
20
+ parser.banner = "Usage: #{APP_NAME} [options] [file1, file2, ...]\n\n" \
21
+ 'If no files or directories are given, the current ' \
22
+ 'directory is scanned by default.'
17
23
 
18
24
  add_linter_options parser
19
25
  add_file_options parser
20
26
  add_info_options parser
21
27
  end.parse!(args)
22
28
 
23
- # Any remaining arguments are assumed to be files
24
- @options[:files] = args
29
+ # Any remaining arguments are assumed to be files; fall back to
30
+ # DEFAULT_FILES when none are given
31
+ @options[:files] = args.empty? ? DEFAULT_FILES : args
25
32
 
26
33
  @options
27
34
  rescue OptionParser::InvalidOption => e
@@ -2,5 +2,5 @@
2
2
 
3
3
  # Defines the gem version.
4
4
  module SlimLint
5
- VERSION = '0.36.0'
5
+ VERSION = '0.37.0'
6
6
  end
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.36.0
4
+ version: 0.37.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shane da Silva
@@ -168,7 +168,11 @@ files:
168
168
  homepage: https://github.com/sds/slim-lint
169
169
  licenses:
170
170
  - MIT
171
- metadata: {}
171
+ metadata:
172
+ bug_tracker_uri: https://github.com/sds/slim-lint/issues
173
+ changelog_uri: https://github.com/sds/slim-lint/blob/main/CHANGELOG.md
174
+ source_code_uri: https://github.com/sds/slim-lint
175
+ rubygems_mfa_required: 'true'
172
176
  rdoc_options: []
173
177
  require_paths:
174
178
  - lib