slim_lint 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/bin/slim-lint +0 -1
  3. data/lib/slim_lint/atom.rb +91 -0
  4. data/lib/slim_lint/capture_map.rb +17 -0
  5. data/lib/slim_lint/cli.rb +25 -8
  6. data/lib/slim_lint/configuration.rb +38 -31
  7. data/lib/slim_lint/configuration_loader.rb +21 -5
  8. data/lib/slim_lint/document.rb +20 -4
  9. data/lib/slim_lint/engine.rb +6 -0
  10. data/lib/slim_lint/file_finder.rb +13 -4
  11. data/lib/slim_lint/filters/inject_line_numbers.rb +7 -2
  12. data/lib/slim_lint/filters/sexp_converter.rb +4 -0
  13. data/lib/slim_lint/lint.rb +17 -1
  14. data/lib/slim_lint/linter/comment_control_statement.rb +2 -2
  15. data/lib/slim_lint/linter/consecutive_control_statements.rb +1 -16
  16. data/lib/slim_lint/linter/empty_control_statement.rb +1 -1
  17. data/lib/slim_lint/linter/redundant_div.rb +8 -5
  18. data/lib/slim_lint/linter/rubocop.rb +40 -18
  19. data/lib/slim_lint/linter/tag_case.rb +1 -1
  20. data/lib/slim_lint/linter.rb +19 -6
  21. data/lib/slim_lint/linter_registry.rb +13 -2
  22. data/lib/slim_lint/linter_selector.rb +74 -0
  23. data/lib/slim_lint/logger.rb +5 -9
  24. data/lib/slim_lint/matcher/anything.rb +9 -0
  25. data/lib/slim_lint/matcher/base.rb +19 -0
  26. data/lib/slim_lint/matcher/capture.rb +30 -0
  27. data/lib/slim_lint/matcher/nothing.rb +11 -0
  28. data/lib/slim_lint/options.rb +16 -6
  29. data/lib/slim_lint/rake_task.rb +15 -0
  30. data/lib/slim_lint/report.rb +7 -0
  31. data/lib/slim_lint/reporter/default_reporter.rb +2 -2
  32. data/lib/slim_lint/reporter/json_reporter.rb +15 -10
  33. data/lib/slim_lint/reporter.rb +17 -11
  34. data/lib/slim_lint/ruby_extractor.rb +2 -0
  35. data/lib/slim_lint/runner.rb +43 -39
  36. data/lib/slim_lint/sexp.rb +43 -30
  37. data/lib/slim_lint/sexp_visitor.rb +66 -28
  38. data/lib/slim_lint/utils.rb +17 -0
  39. data/lib/slim_lint/version.rb +1 -1
  40. data/lib/slim_lint.rb +10 -1
  41. metadata +10 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 90417095e752ffc02b70c6cf2c7c578642a7aced
4
- data.tar.gz: db37d397d726e455c2d98e565c1f0a4ba876c28f
3
+ metadata.gz: 66e3790d144eb2141cb93b53835a447cfe9e1db8
4
+ data.tar.gz: 7b07131e272e1b913926061eb37703d7e0738f01
5
5
  SHA512:
6
- metadata.gz: d08877671cd10ae219e055f65f16c9dc26800b4aac6fe0be74dcd669da2da0bcdf45ec7885b24c87333c7731fe1178a7c0c45259146ff7c285da6371b7da53da
7
- data.tar.gz: 1afec4e606480b536283d722121dc662fc0d6b2da4cd5bfeb3c93e4853b113404459a6e012546cd0fb179f99888983fa7ce0880f5f02bfcb5944acb163817c19
6
+ metadata.gz: d776fde028a429d5a44ae2cdc4092705e759a96f82dca6c2b59ad6a3fc1280823e86ecee38fa642f3720d048567ab31a3dd1bd5bf72a48ef3737c2b1d300843f
7
+ data.tar.gz: e617bc89440a83feefec3beabaa186ef57fb8c5433fd7b134610deaf631a4b4ad3c0e1e70474255145d2b40fc5d19ee3f19191ab102329b4f5a5dc27d9995717
data/bin/slim-lint CHANGED
@@ -1,6 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require 'slim_lint'
4
3
  require 'slim_lint/cli'
5
4
 
6
5
  logger = SlimLint::Logger.new(STDOUT)
@@ -0,0 +1,91 @@
1
+ module SlimLint
2
+ # Represents an atomic, childless, literal value within an S-expression.
3
+ #
4
+ # This creates a light wrapper around literal values of S-expressions so we
5
+ # can make an {Atom} quack like a {Sexp} without being an {Sexp}.
6
+ class Atom
7
+ # Creates an atom from the specified value.
8
+ #
9
+ # @param value [Object]
10
+ def initialize(value)
11
+ @value = value
12
+ end
13
+
14
+ # Returns whether this atom is equivalent to another object.
15
+ #
16
+ # This defines a helper which unwraps the inner value of the atom to compare
17
+ # against a literal value, saving us having to do it ourselves everywhere
18
+ # else.
19
+ #
20
+ # @param other [Object]
21
+ # @return [Boolean]
22
+ def ==(other)
23
+ case other
24
+ when Atom
25
+ @value == other.instance_variable_get(:@value)
26
+ else
27
+ @value == other
28
+ end
29
+ end
30
+
31
+ # Returns whether this atom matches the given Sexp pattern.
32
+ #
33
+ # This exists solely to make an {Atom} quack like a {Sexp}, so we don't have
34
+ # to manually check the type when doing comparisons elsewhere.
35
+ #
36
+ # @param [Array, Object]
37
+ # @return [Boolean]
38
+ def match?(pattern)
39
+ # Delegate matching logic if we're comparing against a matcher
40
+ if pattern.is_a?(SlimLint::Matcher::Base)
41
+ return pattern.match?(@value)
42
+ end
43
+
44
+ @value == pattern
45
+ end
46
+
47
+ # Displays the string representation the value this {Atom} wraps.
48
+ #
49
+ # @return [String]
50
+ def to_s
51
+ @value.to_s
52
+ end
53
+
54
+ # Displays a string representation of this {Atom} suitable for debugging.
55
+ #
56
+ # @return [String]
57
+ def inspect
58
+ "<#Atom #{@value.inspect}>"
59
+ end
60
+
61
+ # Redirect methods to the value this {Atom} wraps.
62
+ #
63
+ # Again, this is for convenience so we don't need to manually unwrap the
64
+ # value ourselves. It's pretty magical, but results in much DRYer code.
65
+ #
66
+ # @param method_sym [Symbol] method that was called
67
+ # @param args [Array]
68
+ # @yield block that was passed to the method
69
+ def method_missing(method_sym, *args, &block)
70
+ if @value.respond_to?(method_sym)
71
+ @value.send(method_sym, *args, &block)
72
+ else
73
+ super
74
+ end
75
+ end
76
+
77
+ # Return whether this {Atom} or the value it wraps responds to the given
78
+ # message.
79
+ #
80
+ # @param method_sym [Symbol]
81
+ # @param include_private [Boolean]
82
+ # @return [Boolean]
83
+ def respond_to?(method_sym, include_private = false)
84
+ if super
85
+ true
86
+ else
87
+ @value.respond_to?(method_sym, include_private)
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,17 @@
1
+ module SlimLint
2
+ # Holds the list of captures, providing a convenient interface for accessing
3
+ # the values and unwrapping them on your behalf.
4
+ class CaptureMap < Hash
5
+ # Returns the captured value with the specified name.
6
+ #
7
+ # @param capture_name [Symbol]
8
+ # @return [Object]
9
+ def [](capture_name)
10
+ if key?(capture_name)
11
+ super.value
12
+ else
13
+ raise ArgumentError, "Capture #{capture_name.inspect} does not exist!"
14
+ end
15
+ end
16
+ end
17
+ end
data/lib/slim_lint/cli.rb CHANGED
@@ -1,3 +1,4 @@
1
+ require 'slim_lint'
1
2
  require 'slim_lint/options'
2
3
 
3
4
  require 'sysexits'
@@ -5,8 +6,8 @@ require 'sysexits'
5
6
  module SlimLint
6
7
  # Command line application interface.
7
8
  class CLI
8
- attr_accessor :options
9
-
9
+ # Create a CLI that outputs to the specified logger.
10
+ #
10
11
  # @param logger [SlimLint::Logger]
11
12
  def initialize(logger)
12
13
  @log = logger
@@ -16,7 +17,7 @@ module SlimLint
16
17
  # based on those arguments.
17
18
  #
18
19
  # @param args [Array<String>] command line arguments
19
- # @return [Fixnum] exit status returned by the application
20
+ # @return [Integer] exit status code
20
21
  def run(args)
21
22
  options = SlimLint::Options.new.parse(args)
22
23
  act_on_options(options)
@@ -28,6 +29,9 @@ module SlimLint
28
29
 
29
30
  attr_reader :log
30
31
 
32
+ # Given the provided options, execute the appropriate command.
33
+ #
34
+ # @return [Integer] exit status code
31
35
  def act_on_options(options)
32
36
  log.color_enabled = options.fetch(:color, log.tty?)
33
37
 
@@ -48,6 +52,8 @@ module SlimLint
48
52
  end
49
53
  end
50
54
 
55
+ # Outputs a message and returns an appropriate error code for the specified
56
+ # exception.
51
57
  def handle_exception(ex)
52
58
  case ex
53
59
  when SlimLint::Exceptions::ConfigurationError
@@ -69,21 +75,27 @@ module SlimLint
69
75
  end
70
76
  end
71
77
 
78
+ # Scans the files specified by the given options for lints.
79
+ #
80
+ # @return [Integer] exit status code
72
81
  def scan_for_lints(options)
73
82
  report = Runner.new.run(options)
74
83
  print_report(report, options)
75
84
  report.failed? ? Sysexits::EX_DATAERR : Sysexits::EX_OK
76
85
  end
77
86
 
87
+ # Outputs a report of the linter run using the specified reporter.
78
88
  def print_report(report, options)
79
- reporter = options.fetch(:reporter, Reporter::DefaultReporter).new(log, report)
80
- reporter.report_lints
89
+ reporter = options.fetch(:reporter,
90
+ SlimLint::Reporter::DefaultReporter).new(log)
91
+ reporter.display_report(report)
81
92
  end
82
93
 
94
+ # Outputs a list of all currently available linters.
83
95
  def print_available_linters
84
96
  log.info 'Available linters:'
85
97
 
86
- linter_names = LinterRegistry.linters.map do |linter|
98
+ linter_names = SlimLint::LinterRegistry.linters.map do |linter|
87
99
  linter.name.split('::').last
88
100
  end
89
101
 
@@ -92,10 +104,11 @@ module SlimLint
92
104
  end
93
105
  end
94
106
 
107
+ # Outputs a list of currently available reporters.
95
108
  def print_available_reporters
96
109
  log.info 'Available reporters:'
97
110
 
98
- reporter_names = Reporter.descendants.map do |reporter|
111
+ reporter_names = SlimLint::Reporter.descendants.map do |reporter|
99
112
  reporter.name.split('::').last.sub(/Reporter$/, '').downcase
100
113
  end
101
114
 
@@ -104,14 +117,18 @@ module SlimLint
104
117
  end
105
118
  end
106
119
 
120
+ # Outputs help documentation.
107
121
  def print_help(options)
108
122
  log.log options[:help]
109
123
  end
110
124
 
125
+ # Outputs the application name and version.
111
126
  def print_version
112
- log.log "#{APP_NAME} #{SlimLint::VERSION}"
127
+ log.log "#{SlimLint::APP_NAME} #{SlimLint::VERSION}"
113
128
  end
114
129
 
130
+ # Outputs the backtrace of an exception with instructions on how to report
131
+ # the issue.
115
132
  def print_unexpected_exception(ex)
116
133
  log.bold_error ex.message
117
134
  log.error ex.backtrace.join("\n")
@@ -1,6 +1,12 @@
1
1
  module SlimLint
2
2
  # Stores runtime configuration for the application.
3
+ #
4
+ # The purpose of this class is to validate and ensure all configurations
5
+ # satisfy some basic pre-conditions so other parts of the application don't
6
+ # have to check the configuration for errors. It should have no knowledge of
7
+ # how these configuration values are ultimately used.
3
8
  class Configuration
9
+ # Internal hash storing the configuration.
4
10
  attr_reader :hash
5
11
 
6
12
  # Creates a configuration from the given options hash.
@@ -11,6 +17,8 @@ module SlimLint
11
17
  validate
12
18
  end
13
19
 
20
+ # Access the configuration as if it were a hash.
21
+ #
14
22
  # @param key [String]
15
23
  # @return [Array,Hash,Number,String]
16
24
  def [](key)
@@ -36,19 +44,9 @@ module SlimLint
36
44
  linter.name.split('::').last
37
45
  when SlimLint::Linter
38
46
  linter.name
39
- else
40
- linter.to_s
41
47
  end
42
48
 
43
- smart_merge(@hash['linters']['ALL'],
44
- @hash['linters'].fetch(linter_name, {})).freeze
45
- end
46
-
47
- # Returns whether the specified linter is enabled by this configuration.
48
- #
49
- # @param linter [SlimLint::Linter,String]
50
- def linter_enabled?(linter)
51
- for_linter(linter)['enabled'] != false
49
+ @hash['linters'].fetch(linter_name, {}).dup.freeze
52
50
  end
53
51
 
54
52
  # Merges the given configuration with this one, returning a new
@@ -62,13 +60,11 @@ module SlimLint
62
60
 
63
61
  private
64
62
 
65
- # Validates the configuration for any invalid options, normalizing it where
66
- # possible.
67
- def validate
68
- @hash = convert_nils_to_empty_hashes(@hash)
69
- ensure_linter_section_exists(@hash)
70
- end
71
-
63
+ # Merge two hashes such that nested hashes are merged rather than replaced.
64
+ #
65
+ # @param parent [Hash]
66
+ # @param child [Hash]
67
+ # @return [Hash]
72
68
  def smart_merge(parent, child)
73
69
  parent.merge(child) do |_key, old, new|
74
70
  case old
@@ -80,21 +76,32 @@ module SlimLint
80
76
  end
81
77
  end
82
78
 
83
- def ensure_linter_section_exists(hash)
84
- hash['linters'] ||= {}
85
- hash['linters']['ALL'] ||= {}
79
+ # Validates the configuration for any invalid options, normalizing it where
80
+ # possible.
81
+ def validate
82
+ ensure_exclude_option_array_exists
83
+ ensure_linter_section_exists
84
+ ensure_linter_include_exclude_arrays_exist
86
85
  end
87
86
 
88
- def convert_nils_to_empty_hashes(hash)
89
- hash.each_with_object({}) do |(key, value), h|
90
- h[key] =
91
- case value
92
- when nil then {}
93
- when Hash then convert_nils_to_empty_hashes(value)
94
- else
95
- value
96
- end
97
- h
87
+ # Ensures the `exclude` global option is an array.
88
+ def ensure_exclude_option_array_exists
89
+ @hash['exclude'] = Array(@hash['exclude'])
90
+ end
91
+
92
+ # Ensures the `linters` configuration section exists.
93
+ def ensure_linter_section_exists
94
+ @hash['linters'] ||= {}
95
+ end
96
+
97
+ # Ensure `include` and `exclude` options for linters are arrays
98
+ # (since users can specify a single string glob pattern for convenience)
99
+ def ensure_linter_include_exclude_arrays_exist
100
+ @hash['linters'].keys.each do |linter_name|
101
+ %w[include exclude].each do |option|
102
+ linter_config = @hash['linters'][linter_name]
103
+ linter_config[option] = Array(linter_config[option])
104
+ end
98
105
  end
99
106
  end
100
107
  end
@@ -8,6 +8,8 @@ module SlimLint
8
8
  CONFIG_FILE_NAME = '.slim-lint.yml'
9
9
 
10
10
  class << self
11
+ # Load configuration file given the current working directory the
12
+ # application is running within.
11
13
  def load_applicable_config
12
14
  directory = File.expand_path(Dir.pwd)
13
15
  config_file = possible_config_files(directory).find(&:file?)
@@ -19,33 +21,42 @@ module SlimLint
19
21
  end
20
22
  end
21
23
 
24
+ # Loads the built-in default configuration.
22
25
  def default_configuration
23
26
  @default_config ||= load_from_file(DEFAULT_CONFIG_PATH)
24
27
  end
25
28
 
26
29
  # Loads a configuration, ensuring it extends the default configuration.
30
+ #
31
+ # @param file [String]
32
+ # @return [SlimLint::Configuration]
27
33
  def load_file(file)
28
34
  config = load_from_file(file)
29
35
 
30
36
  default_configuration.merge(config)
31
- rescue => error
37
+ rescue Psych::SyntaxError, Errno::ENOENT => error
32
38
  raise SlimLint::Exceptions::ConfigurationError,
33
39
  "Unable to load configuration from '#{file}': #{error}",
34
40
  error.backtrace
35
41
  end
36
42
 
43
+ # Creates a configuration from the specified hash, ensuring it extends the
44
+ # default configuration.
45
+ #
46
+ # @param hash [Hash]
47
+ # @return [SlimLint::Configuration]
37
48
  def load_hash(hash)
38
49
  config = SlimLint::Configuration.new(hash)
39
50
 
40
51
  default_configuration.merge(config)
41
- rescue => error
42
- raise SlimLint::Exceptions::ConfigurationError,
43
- "Unable to load configuration from '#{file}': #{error}",
44
- error.backtrace
45
52
  end
46
53
 
47
54
  private
48
55
 
56
+ # Parses and loads a configuration from the given file.
57
+ #
58
+ # @param file [String]
59
+ # @return [SlimLint::Configuration]
49
60
  def load_from_file(file)
50
61
  hash =
51
62
  if yaml = YAML.load_file(file)
@@ -57,6 +68,11 @@ module SlimLint
57
68
  SlimLint::Configuration.new(hash)
58
69
  end
59
70
 
71
+ # Returns a list of possible configuration files given the context of the
72
+ # specified directory.
73
+ #
74
+ # @param directory [String]
75
+ # @return [Array<Pathname>]
60
76
  def possible_config_files(directory)
61
77
  files = Pathname.new(directory)
62
78
  .enum_for(:ascend)
@@ -1,7 +1,23 @@
1
1
  module SlimLint
2
2
  # Represents a parsed Slim document and its associated metadata.
3
3
  class Document
4
- attr_reader :config, :file, :sexp, :source, :source_lines
4
+ # File name given to source code parsed from just a string.
5
+ STRING_SOURCE = '(string)'
6
+
7
+ # @return [SlimLint::Configuration] Configuration used to parse template
8
+ attr_reader :config
9
+
10
+ # @return [String] Slim template file path
11
+ attr_reader :file
12
+
13
+ # @return [SlimLint::Sexp] Sexpression representing the parsed document
14
+ attr_reader :sexp
15
+
16
+ # @return [String] original source code
17
+ attr_reader :source
18
+
19
+ # @return [Array<String>] original source code as an array of lines
20
+ attr_reader :source_lines
5
21
 
6
22
  # Parses the specified Slim code into a {Document}.
7
23
  #
@@ -11,7 +27,7 @@ module SlimLint
11
27
  # @raise [Slim::Parser::Error] if there was a problem parsing the document
12
28
  def initialize(source, options)
13
29
  @config = options[:config]
14
- @file = options.fetch(:file, '(string)')
30
+ @file = options.fetch(:file, STRING_SOURCE)
15
31
 
16
32
  process_source(source)
17
33
  end
@@ -24,8 +40,8 @@ module SlimLint
24
40
  @source = strip_frontmatter(source)
25
41
  @source_lines = @source.split("\n")
26
42
 
27
- @engine = SlimLint::Engine.new(file: @file)
28
- @sexp = @engine.call(source)
43
+ engine = SlimLint::Engine.new(file: @file)
44
+ @sexp = engine.parse(source)
29
45
  end
30
46
 
31
47
  # Removes YAML frontmatter
@@ -23,5 +23,11 @@ module SlimLint
23
23
 
24
24
  # Annotates Sexps with line numbers
25
25
  use SlimLint::Filters::InjectLineNumbers
26
+
27
+ # Parses the given source code into a Sexp.
28
+ #
29
+ # @param source [String] source code to parse
30
+ # @return [SlimLint::Sexp] parsed Sexp
31
+ alias_method :parse, :call
26
32
  end
27
33
  end
@@ -8,6 +8,8 @@ module SlimLint
8
8
  # is specified instead of a file.
9
9
  VALID_EXTENSIONS = %w[.slim]
10
10
 
11
+ # Create a file finder using the specified configuration.
12
+ #
11
13
  # @param config [SlimLint::Configuration]
12
14
  def initialize(config)
13
15
  @config = config
@@ -22,15 +24,18 @@ module SlimLint
22
24
  def find(patterns, excluded_patterns)
23
25
  extract_files_from(patterns).reject do |file|
24
26
  excluded_patterns.any? do |exclusion_glob|
25
- ::File.fnmatch?(exclusion_glob, file,
26
- ::File::FNM_PATHNAME | # Wildcards don't match path separators
27
- ::File::FNM_DOTMATCH) # `*` wildcard matches dotfiles
27
+ SlimLint::Utils.any_glob_matches?(exclusion_glob, file)
28
28
  end
29
29
  end
30
30
  end
31
31
 
32
32
  private
33
33
 
34
+ # Extract the list of matching files given the list of glob patterns, file
35
+ # paths, and directories.
36
+ #
37
+ # @param patterns [Array<String>]
38
+ # @return [Array<String>]
34
39
  def extract_files_from(patterns) # rubocop:disable MethodLength
35
40
  files = []
36
41
 
@@ -57,9 +62,13 @@ module SlimLint
57
62
  end
58
63
  end
59
64
 
60
- files.uniq
65
+ files.uniq.sort
61
66
  end
62
67
 
68
+ # Whether the given file should be treated as a Slim file.
69
+ #
70
+ # @param file [String]
71
+ # @return [Boolean]
63
72
  def slim_file?(file)
64
73
  return false unless ::FileTest.file?(file)
65
74
 
@@ -5,8 +5,13 @@ module SlimLint::Filters
5
5
  # This is a hack that allows us to access line information directly from the
6
6
  # S-expressions, which makes a lot of other tasks easier.
7
7
  class InjectLineNumbers < Temple::Filter
8
- NEWLINE_SEXP = [:newline]
8
+ # {Sexp} representing a newline.
9
+ NEWLINE_SEXP = SlimLint::Sexp.new([:newline])
9
10
 
11
+ # Annotates the given {SlimLint::Sexp} with line number information.
12
+ #
13
+ # @param sexp [SlimLint::Sexp]
14
+ # @return [SlimLint::Sexp]
10
15
  def call(sexp)
11
16
  @line_number = 1
12
17
  traverse(sexp)
@@ -16,7 +21,7 @@ module SlimLint::Filters
16
21
  private
17
22
 
18
23
  # Traverses an {Sexp}, annotating it with line numbers by searching for
19
- # :newline abstractions within it.
24
+ # newline abstractions within it.
20
25
  #
21
26
  # @param sexp [SlimLint::Sexp]
22
27
  def traverse(sexp)
@@ -4,6 +4,10 @@ module SlimLint::Filters
4
4
  # These {SlimLint::Sexp}s include additional helpers that makes working with
5
5
  # them more pleasant.
6
6
  class SexpConverter < Temple::Filter
7
+ # Converts the given {Array} to a {SlimLint::Sexp}.
8
+ #
9
+ # @param array_sexp [Array]
10
+ # @return [SlimLint::Sexp]
7
11
  def call(array_sexp)
8
12
  SlimLint::Sexp.new(array_sexp)
9
13
  end
@@ -1,7 +1,20 @@
1
1
  module SlimLint
2
2
  # Contains information about a problem or issue with a Slim document.
3
3
  class Lint
4
- attr_reader :filename, :line, :linter, :message, :severity
4
+ # @return [String] file path to which the lint applies
5
+ attr_reader :filename
6
+
7
+ # @return [String] line number of the file the lint corresponds to
8
+ attr_reader :line
9
+
10
+ # @return [SlimLint::Linter] linter that reported the lint
11
+ attr_reader :linter
12
+
13
+ # @return [String] error/warning message to display to user
14
+ attr_reader :message
15
+
16
+ # @return [Symbol] whether this lint is a warning or an error
17
+ attr_reader :severity
5
18
 
6
19
  # Creates a new lint.
7
20
  #
@@ -18,6 +31,9 @@ module SlimLint
18
31
  @severity = severity
19
32
  end
20
33
 
34
+ # Return whether this lint has a severity of error.
35
+ #
36
+ # @return [Boolean]
21
37
  def error?
22
38
  @severity == :error
23
39
  end
@@ -5,13 +5,13 @@ module SlimLint
5
5
 
6
6
  on [:slim, :control] do |sexp|
7
7
  _, _, code = sexp
8
- next unless code =~ /\A\s*#/
8
+ next unless code[/\A\s*#/]
9
9
 
10
10
  comment = code[/\A\s*#(.*\z)/, 1]
11
11
 
12
12
  report_lint(sexp,
13
13
  "Slim code comments (`/#{comment}`) are preferred over " \
14
- "control statement comments (`-# #{comment}`)")
14
+ "control statement comments (`-##{comment}`)")
15
15
  end
16
16
  end
17
17
  end
@@ -6,27 +6,12 @@ module SlimLint
6
6
 
7
7
  on [:multi] do |sexp|
8
8
  Utils.for_consecutive_items(sexp,
9
- method(:code_sexp?),
9
+ ->(nested_sexp) { nested_sexp.match?([:slim, :control]) },
10
10
  config['max_consecutive'] + 1) do |group|
11
11
  report_lint(group.first,
12
12
  "#{group.count} consecutive control statements can be " \
13
13
  'merged into a single `ruby:` filter')
14
14
  end
15
15
  end
16
-
17
- private
18
-
19
- # Returns whether the given Sexp is a :code abstraction.
20
- #
21
- # @param sexp [SlimLint::Sexp]
22
- # @return [Boolean]
23
- def code_sexp?(sexp)
24
- # TODO: Switch this with a built-in method on the {Sexp} object itself
25
- sexp.is_a?(Sexp) && sexp.match?([:slim, :control])
26
- end
27
-
28
- def newline_sexp?(sexp)
29
- sexp.is_a?(Sexp) && sexp.first == :newline
30
- end
31
16
  end
32
17
  end
@@ -5,7 +5,7 @@ module SlimLint
5
5
 
6
6
  on [:slim, :control] do |sexp|
7
7
  _, _, code = sexp
8
- next unless code =~ /\A\s*\Z/
8
+ next unless code[/\A\s*\Z/]
9
9
 
10
10
  report_lint(sexp, 'Empty control statement can be removed')
11
11
  end
@@ -6,12 +6,15 @@ module SlimLint
6
6
 
7
7
  MESSAGE = '`div` is redundant when %s attribute shortcut is present'
8
8
 
9
- on [:html, :tag, 'div', [:html, :attrs, [:html, :attr, 'class', [:static]]]] do |sexp|
10
- report_lint(sexp, MESSAGE % 'class')
11
- end
9
+ on [:html, :tag, 'div',
10
+ [:html, :attrs,
11
+ [:html, :attr,
12
+ capture(:attr_name, anything),
13
+ [:static]]]] do |sexp|
14
+ attr = captures[:attr_name]
15
+ next unless %w[class id].include?(attr)
12
16
 
13
- on [:html, :tag, 'div', [:html, :attrs, [:html, :attr, 'id', [:static]]]] do |sexp|
14
- report_lint(sexp, MESSAGE % 'id')
17
+ report_lint(sexp, MESSAGE % attr)
15
18
  end
16
19
  end
17
20
  end