haml_lint 0.13.0 → 0.14.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/config/default.yml +1 -0
  3. data/lib/haml_lint/cli.rb +25 -8
  4. data/lib/haml_lint/configuration.rb +56 -34
  5. data/lib/haml_lint/configuration_loader.rb +22 -6
  6. data/lib/haml_lint/constants.rb +1 -1
  7. data/lib/haml_lint/document.rb +102 -0
  8. data/lib/haml_lint/file_finder.rb +14 -5
  9. data/lib/haml_lint/lint.rb +17 -1
  10. data/lib/haml_lint/linter/alt_text.rb +1 -1
  11. data/lib/haml_lint/linter/class_attribute_with_static_value.rb +2 -2
  12. data/lib/haml_lint/linter/classes_before_ids.rb +2 -2
  13. data/lib/haml_lint/linter/consecutive_comments.rb +3 -5
  14. data/lib/haml_lint/linter/consecutive_silent_scripts.rb +5 -5
  15. data/lib/haml_lint/linter/empty_script.rb +1 -1
  16. data/lib/haml_lint/linter/html_attributes.rb +2 -2
  17. data/lib/haml_lint/linter/implicit_div.rb +3 -3
  18. data/lib/haml_lint/linter/leading_comment_space.rb +1 -1
  19. data/lib/haml_lint/linter/line_length.rb +2 -2
  20. data/lib/haml_lint/linter/multiline_pipe.rb +3 -3
  21. data/lib/haml_lint/linter/multiline_script.rb +3 -3
  22. data/lib/haml_lint/linter/object_reference_attributes.rb +1 -1
  23. data/lib/haml_lint/linter/rubocop.rb +60 -34
  24. data/lib/haml_lint/linter/ruby_comments.rb +1 -1
  25. data/lib/haml_lint/linter/space_before_script.rb +4 -4
  26. data/lib/haml_lint/linter/space_inside_hash_attributes.rb +2 -2
  27. data/lib/haml_lint/linter/tag_name.rb +1 -1
  28. data/lib/haml_lint/linter/trailing_whitespace.rb +2 -2
  29. data/lib/haml_lint/linter/unnecessary_interpolation.rb +3 -3
  30. data/lib/haml_lint/linter/unnecessary_string_output.rb +10 -3
  31. data/lib/haml_lint/linter.rb +30 -10
  32. data/lib/haml_lint/linter_registry.rb +13 -2
  33. data/lib/haml_lint/linter_selector.rb +77 -0
  34. data/lib/haml_lint/node_transformer.rb +5 -5
  35. data/lib/haml_lint/options.rb +13 -6
  36. data/lib/haml_lint/rake_task.rb +18 -3
  37. data/lib/haml_lint/report.rb +7 -0
  38. data/lib/haml_lint/reporter/default_reporter.rb +2 -2
  39. data/lib/haml_lint/reporter/json_reporter.rb +15 -10
  40. data/lib/haml_lint/reporter.rb +17 -11
  41. data/lib/haml_lint/{script_extractor.rb → ruby_extractor.rb} +26 -17
  42. data/lib/haml_lint/runner.rb +44 -44
  43. data/lib/haml_lint/tree/node.rb +7 -8
  44. data/lib/haml_lint/utils.rb +88 -21
  45. data/lib/haml_lint/version.rb +1 -1
  46. data/lib/haml_lint.rb +2 -1
  47. metadata +5 -4
  48. data/lib/haml_lint/parser.rb +0 -87
@@ -24,7 +24,7 @@ module HamlLint
24
24
  # Plain text nodes are allowed to consist of a single pipe
25
25
  return if line.strip == '|'
26
26
 
27
- add_lint(node, MESSAGE) if line.match(MULTILINE_PIPE_REGEX)
27
+ record_lint(node, MESSAGE) if line.match(MULTILINE_PIPE_REGEX)
28
28
  end
29
29
 
30
30
  private
@@ -32,12 +32,12 @@ module HamlLint
32
32
  MULTILINE_PIPE_REGEX = /\s+\|\s*$/
33
33
 
34
34
  def line_text_for_node(node)
35
- parser.lines[node.line - 1]
35
+ document.source_lines[node.line - 1]
36
36
  end
37
37
 
38
38
  def check(node)
39
39
  line = line_text_for_node(node)
40
- add_lint(node, MESSAGE) if line.match(MULTILINE_PIPE_REGEX)
40
+ record_lint(node, MESSAGE) if line.match(MULTILINE_PIPE_REGEX)
41
41
  end
42
42
  end
43
43
  end
@@ -34,9 +34,9 @@ module HamlLint
34
34
  def check(node)
35
35
  operator = node.script[/\s+(\S+)\z/, 1]
36
36
  if SPLIT_OPERATORS.include?(operator)
37
- add_lint(node,
38
- "Script with trailing operator `#{operator}` should be " \
39
- 'merged with the script on the following line')
37
+ record_lint(node,
38
+ "Script with trailing operator `#{operator}` should be " \
39
+ 'merged with the script on the following line')
40
40
  end
41
41
  end
42
42
  end
@@ -7,7 +7,7 @@ module HamlLint
7
7
  def visit_tag(node)
8
8
  return unless node.object_reference?
9
9
 
10
- add_lint(node, 'Avoid using object reference syntax to assign class/id ' \
10
+ record_lint(node, 'Avoid using object reference syntax to assign class/id ' \
11
11
  'attributes for tags')
12
12
  end
13
13
  end
@@ -1,4 +1,4 @@
1
- require 'haml_lint/script_extractor'
1
+ require 'haml_lint/ruby_extractor'
2
2
  require 'rubocop'
3
3
  require 'tempfile'
4
4
 
@@ -7,33 +7,35 @@ module HamlLint
7
7
  class Linter::RuboCop < Linter
8
8
  include LinterRegistry
9
9
 
10
- def initialize(config)
11
- super
12
- @rubocop = ::RuboCop::CLI.new
13
- @ignored_cops = Array(config['ignored_cops']).flatten
14
- end
10
+ def visit_root(_node)
11
+ extractor = HamlLint::RubyExtractor.new
12
+ extracted_source = extractor.extract(document)
15
13
 
16
- def run(parser)
17
- @parser = parser
18
- @extractor = ScriptExtractor.new(parser)
19
- extracted_code = @extractor.extract.strip
14
+ return if extracted_source.source.empty?
20
15
 
21
- # Ensure a final newline in the code we feed to RuboCop
22
- find_lints(extracted_code + "\n") unless extracted_code.empty?
16
+ find_lints(extracted_source.source, extracted_source.source_map)
23
17
  end
24
18
 
25
19
  private
26
20
 
27
- def find_lints(code)
28
- original_filename = @parser.filename || 'ruby_script'
21
+ # Executes RuboCop against the given Ruby code and records the offenses as
22
+ # lints.
23
+ #
24
+ # @param ruby [String] Ruby code
25
+ # @param source_map [Hash] map of Ruby code line numbers to original line
26
+ # numbers in the template
27
+ def find_lints(ruby, source_map)
28
+ rubocop = ::RuboCop::CLI.new
29
+
30
+ original_filename = document.file || 'ruby_script'
29
31
  filename = "#{File.basename(original_filename)}.haml_lint.tmp"
30
32
  directory = File.dirname(original_filename)
31
33
 
32
34
  Tempfile.open(filename, directory) do |f|
33
35
  begin
34
- f.write(code)
36
+ f.write(ruby)
35
37
  f.close
36
- extract_lints_from_offences(lint_file(f.path))
38
+ extract_lints_from_offenses(lint_file(rubocop, f.path), source_map)
37
39
  ensure
38
40
  f.unlink
39
41
  end
@@ -41,36 +43,60 @@ module HamlLint
41
43
  end
42
44
 
43
45
  # Defined so we can stub the results in tests
44
- def lint_file(file)
45
- @rubocop.run(%w[--format HamlLint::OffenceCollector] << file)
46
- OffenceCollector.offences
46
+ #
47
+ # @param rubocop [RuboCop::CLI]
48
+ # @param file [String]
49
+ # @return [Array<RuboCop::Cop::Offense>]
50
+ def lint_file(rubocop, file)
51
+ rubocop.run(rubocop_flags << file)
52
+ OffenseCollector.offenses
47
53
  end
48
54
 
49
- def extract_lints_from_offences(offences)
50
- offences.select { |offence| !@ignored_cops.include?(offence.cop_name) }
51
- .each do |offence|
52
- @lints << Lint.new(self,
53
- @parser.filename,
54
- @extractor.source_map[offence.line],
55
- "#{offence.cop_name}: #{offence.message}")
55
+ # Aggregates RuboCop offenses and converts them to {HamlLint::Lint}s
56
+ # suitable for reporting.
57
+ #
58
+ # @param offenses [Array<RuboCop::Cop::Offense>]
59
+ # @param source_map [Hash]
60
+ def extract_lints_from_offenses(offenses, source_map)
61
+ dummy_node = Struct.new(:line)
62
+
63
+ offenses.select { |offense| !config['ignored_cops'].include?(offense.cop_name) }
64
+ .each do |offense|
65
+ record_lint(dummy_node.new(source_map[offense.line]), offense.message)
56
66
  end
57
67
  end
58
- end
59
68
 
60
- # Collects offences detected by RuboCop.
61
- class OffenceCollector < ::RuboCop::Formatter::BaseFormatter
62
- attr_accessor :offences
69
+ # Returns flags that will be passed to RuboCop CLI.
70
+ #
71
+ # @return [Array<String>]
72
+ def rubocop_flags
73
+ flags = %w[--format HamlLint::OffenseCollector]
74
+ flags += ['--config', ENV['HAML_LINT_RUBOCOP_CONF']] if ENV['HAML_LINT_RUBOCOP_CONF']
75
+ flags
76
+ end
77
+ end
63
78
 
79
+ # Collects offenses detected by RuboCop.
80
+ class OffenseCollector < ::RuboCop::Formatter::BaseFormatter
64
81
  class << self
65
- attr_accessor :offences
82
+ # List of offenses reported by RuboCop.
83
+ attr_accessor :offenses
66
84
  end
67
85
 
86
+ # Executed when RuboCop begins linting.
87
+ #
88
+ # @param _target_files [Array<String>]
68
89
  def started(_target_files)
69
- self.class.offences = []
90
+ self.class.offenses = []
70
91
  end
71
92
 
72
- def file_finished(_file, offences)
73
- self.class.offences += offences
93
+ # Executed when a file has been scanned by RuboCop, adding the reported
94
+ # offenses to our collection.
95
+ #
96
+ # @param _file [String]
97
+ # @param offenses [Array<RuboCop::Cop::Offense>]
98
+ def file_finished(_file, offenses)
99
+ self.class.offenses += offenses
74
100
  end
75
101
  end
76
102
  end
@@ -5,7 +5,7 @@ module HamlLint
5
5
 
6
6
  def visit_silent_script(node)
7
7
  if code_comment?(node)
8
- add_lint(node, 'Use `-#` for comments instead of `- #`')
8
+ record_lint(node, 'Use `-#` for comments instead of `- #`')
9
9
  end
10
10
  end
11
11
 
@@ -28,18 +28,18 @@ module HamlLint
28
28
  # (need to do it this way as the parser strips whitespace from node)
29
29
  return unless tag_with_text[index - 1] != ' '
30
30
 
31
- add_lint(node, MESSAGE_FORMAT % '=')
31
+ record_lint(node, MESSAGE_FORMAT % '=')
32
32
  end
33
33
 
34
34
  def visit_script(node)
35
35
  # Plain text nodes with interpolation are converted to script nodes, so we
36
36
  # need to ignore them here.
37
- return unless parser.lines[node.line - 1].lstrip.start_with?('=')
38
- add_lint(node, MESSAGE_FORMAT % '=') if missing_space?(node)
37
+ return unless document.source_lines[node.line - 1].lstrip.start_with?('=')
38
+ record_lint(node, MESSAGE_FORMAT % '=') if missing_space?(node)
39
39
  end
40
40
 
41
41
  def visit_silent_script(node)
42
- add_lint(node, MESSAGE_FORMAT % '-') if missing_space?(node)
42
+ record_lint(node, MESSAGE_FORMAT % '-') if missing_space?(node)
43
43
  end
44
44
 
45
45
  private
@@ -25,8 +25,8 @@ module HamlLint
25
25
  style = STYLE[config['style'] == 'no_space' ? 'no_space' : 'space']
26
26
  source = node.hash_attributes_source
27
27
 
28
- add_lint(node, style[:start_message]) unless source =~ style[:start_regex]
29
- add_lint(node, style[:end_message]) unless source =~ style[:end_regex]
28
+ record_lint(node, style[:start_message]) unless source =~ style[:start_regex]
29
+ record_lint(node, style[:end_message]) unless source =~ style[:end_regex]
30
30
  end
31
31
  end
32
32
  end
@@ -7,7 +7,7 @@ module HamlLint
7
7
  tag = node.tag_name
8
8
  return unless tag.match(/[A-Z]/)
9
9
 
10
- add_lint(node, "`#{tag}` should be written in lowercase as `#{tag.downcase}`")
10
+ record_lint(node, "`#{tag}` should be written in lowercase as `#{tag.downcase}`")
11
11
  end
12
12
  end
13
13
  end
@@ -6,10 +6,10 @@ module HamlLint
6
6
  def visit_root(_node)
7
7
  dummy_node = Struct.new(:line)
8
8
 
9
- parser.lines.each_with_index do |line, index|
9
+ document.source_lines.each_with_index do |line, index|
10
10
  next unless line =~ /\s+$/
11
11
 
12
- add_lint dummy_node.new(index + 1), 'Line contains trailing whitespace'
12
+ record_lint dummy_node.new(index + 1), 'Line contains trailing whitespace'
13
13
  end
14
14
  end
15
15
  end
@@ -14,15 +14,15 @@ module HamlLint
14
14
 
15
15
  count = 0
16
16
  chars = 2 # Include surrounding quote chars
17
- HamlLint::Utils.extract_interpolated_values(node.script) do |interpolated_code|
17
+ HamlLint::Utils.extract_interpolated_values(node.script) do |interpolated_code, _line|
18
18
  count += 1
19
19
  return if count > 1 # rubocop:disable Lint/NonLocalExitFromIterator
20
20
  chars += interpolated_code.length + 3
21
21
  end
22
22
 
23
23
  if chars == node.script.length
24
- add_lint(node, '`%... \#{expression}` can be written without ' \
25
- 'interpolation as `%...= expression`')
24
+ record_lint(node, '`%... \#{expression}` can be written without ' \
25
+ 'interpolation as `%...= expression`')
26
26
  end
27
27
  end
28
28
  end
@@ -13,7 +13,7 @@ module HamlLint
13
13
 
14
14
  def visit_tag(node)
15
15
  if tag_has_inline_script?(node) && inline_content_is_string?(node)
16
- add_lint(node, MESSAGE)
16
+ record_lint(node, MESSAGE)
17
17
  end
18
18
  end
19
19
 
@@ -23,7 +23,7 @@ module HamlLint
23
23
  return if node.source_code !~ /\s*=/
24
24
 
25
25
  if outputs_string_literal?(node)
26
- add_lint(node, MESSAGE)
26
+ record_lint(node, MESSAGE)
27
27
  end
28
28
  end
29
29
 
@@ -31,9 +31,16 @@ module HamlLint
31
31
 
32
32
  def outputs_string_literal?(script_node)
33
33
  tree = parse_ruby(script_node.script)
34
- [:str, :dstr].include?(tree.type)
34
+ [:str, :dstr].include?(tree.type) &&
35
+ starts_with_reserved_character?(tree.children.first)
35
36
  rescue ::Parser::SyntaxError # rubocop:disable Lint/HandleExceptions
36
37
  # Gracefully ignore syntax errors, as that's managed by a different linter
37
38
  end
39
+
40
+ # Returns whether a string starts with a character that would otherwise be
41
+ # given special treatment, thus making enclosing it in a string necessary.
42
+ def starts_with_reserved_character?(string)
43
+ string !~ %r{\A\s*[/#-=%~]}
44
+ end
38
45
  end
39
46
  end
@@ -1,33 +1,52 @@
1
1
  module HamlLint
2
2
  # Base implementation for all lint checks.
3
+ #
4
+ # @abstract
3
5
  class Linter
4
6
  include HamlVisitor
5
7
 
6
- attr_reader :parser, :lints
8
+ # List of lints reported by this linter.
9
+ #
10
+ # @todo Remove once spec/support/shared_linter_context returns an array of
11
+ # lints for the subject instead of the linter itself.
12
+ attr_reader :lints
7
13
 
14
+ # Initializes a linter with the specified configuration.
15
+ #
8
16
  # @param config [Hash] configuration for this linter
9
17
  def initialize(config)
10
18
  @config = config
11
19
  @lints = []
12
- @ruby_parser = nil
13
20
  end
14
21
 
15
- def run(parser)
16
- @parser = parser
17
- visit(parser.tree)
22
+ # Runs the linter against the given Haml document.
23
+ #
24
+ # @param document [HamlLint::Document]
25
+ def run(document)
26
+ @document = document
27
+ @lints = []
28
+ visit(document.tree)
29
+ @lints
18
30
  end
19
31
 
20
32
  # Returns the simple name for this linter.
33
+ #
34
+ # @return [String]
21
35
  def name
22
36
  self.class.name.split('::').last
23
37
  end
24
38
 
25
39
  private
26
40
 
27
- attr_reader :config
41
+ attr_reader :config, :document
28
42
 
29
- def add_lint(node, message)
30
- @lints << Lint.new(self, parser.filename, node.line, message)
43
+ # Record a lint for reporting back to the user.
44
+ #
45
+ # @param node [#line] node to extract the line number from
46
+ # @param message [String] error/warning to display to the user
47
+ def record_lint(node, message)
48
+ @lints << HamlLint::Lint.new(self, @document.file, node.line, message,
49
+ config.fetch('severity', :warning).to_sym)
31
50
  end
32
51
 
33
52
  # Parse Ruby code into an abstract syntax tree.
@@ -136,7 +155,7 @@ module HamlLint
136
155
  def following_node_line(node)
137
156
  [
138
157
  [node.children.first, next_node(node)].compact.map(&:line),
139
- parser.lines.count + 1,
158
+ @document.source_lines.count + 1,
140
159
  ].flatten.min
141
160
  end
142
161
 
@@ -148,7 +167,8 @@ module HamlLint
148
167
  def tag_with_inline_text(tag_node)
149
168
  # Normalize each of the lines to ignore the multiline bar (|) and
150
169
  # excess whitespace
151
- parser.lines[(tag_node.line - 1)...(following_node_line(tag_node) - 1)].map do |line|
170
+ @document.source_lines[(tag_node.line - 1)...(following_node_line(tag_node) - 1)]
171
+ .map do |line|
152
172
  line.strip.gsub(/\|\z/, '').rstrip
153
173
  end.join(' ')
154
174
  end
@@ -6,12 +6,23 @@ module HamlLint
6
6
  @linters = []
7
7
 
8
8
  class << self
9
+ # List of all registered linters.
9
10
  attr_reader :linters
10
11
 
11
- def included(base)
12
- @linters << base
12
+ # Executed when a linter includes the {LinterRegistry} module.
13
+ #
14
+ # This results in the linter being registered with the registry.
15
+ #
16
+ # @param subclass [Class]
17
+ def included(subclass)
18
+ @linters << subclass
13
19
  end
14
20
 
21
+ # Return a list of {HamlLint::Linter} {Class}es corresponding to the
22
+ # specified list of names.
23
+ #
24
+ # @param linter_names [Array<String>]
25
+ # @return [Array<Class>]
15
26
  def extract_linters_from(linter_names)
16
27
  linter_names.map do |linter_name|
17
28
  begin
@@ -0,0 +1,77 @@
1
+ module HamlLint
2
+ # Chooses the appropriate linters to run given the specified configuration.
3
+ class LinterSelector
4
+ # Creates a selector using the given configuration and additional options.
5
+ #
6
+ # @param config [HamlLint::Configuration]
7
+ # @param options [Hash]
8
+ def initialize(config, options)
9
+ @config = config
10
+ @options = options
11
+ end
12
+
13
+ # Returns the set of linters to run against the given file.
14
+ #
15
+ # @param file [String]
16
+ # @raise [HamlLint::Exceptions::NoLintersError] when no linters are enabled
17
+ # @return [Array<HamlLint::Linter>]
18
+ def linters_for_file(file)
19
+ @linters ||= extract_enabled_linters(@config, @options)
20
+ @linters.select { |linter| run_linter_on_file?(@config, linter, file) }
21
+ end
22
+
23
+ private
24
+
25
+ # Returns a list of linters that are enabled given the specified
26
+ # configuration and additional options.
27
+ #
28
+ # @param config [HamlLint::Configuration]
29
+ # @param options [Hash]
30
+ # @return [Array<HamlLint::Linter>]
31
+ def extract_enabled_linters(config, options)
32
+ included_linters = LinterRegistry
33
+ .extract_linters_from(options.fetch(:included_linters, []))
34
+
35
+ included_linters = LinterRegistry.linters if included_linters.empty?
36
+
37
+ excluded_linters = LinterRegistry
38
+ .extract_linters_from(options.fetch(:excluded_linters, []))
39
+
40
+ # After filtering out explicitly included/excluded linters, only include
41
+ # linters which are enabled in the configuration
42
+ linters = (included_linters - excluded_linters).map do |linter_class|
43
+ linter_config = config.for_linter(linter_class)
44
+ linter_class.new(linter_config) if linter_config['enabled']
45
+ end.compact
46
+
47
+ # Highlight condition where all linters were filtered out, as this was
48
+ # likely a mistake on the user's part
49
+ if linters.empty?
50
+ raise HamlLint::Exceptions::NoLintersError, 'No linters specified'
51
+ end
52
+
53
+ linters
54
+ end
55
+
56
+ # Whether to run the given linter against the specified file.
57
+ #
58
+ # @param config [HamlLint::Configuration]
59
+ # @param linter [HamlLint::Linter]
60
+ # @param file [String]
61
+ # @return [Boolean]
62
+ def run_linter_on_file?(config, linter, file)
63
+ linter_config = config.for_linter(linter)
64
+
65
+ if linter_config['include'].any? &&
66
+ !HamlLint::Utils.any_glob_matches?(linter_config['include'], file)
67
+ return false
68
+ end
69
+
70
+ if HamlLint::Utils.any_glob_matches?(linter_config['exclude'], file)
71
+ return false
72
+ end
73
+
74
+ true
75
+ end
76
+ end
77
+ end
@@ -7,11 +7,11 @@ module HamlLint
7
7
  # would expect. This class is intended to isolate and handle these cases so
8
8
  # that linters don't have to deal with them.
9
9
  class NodeTransformer
10
- # Creates a node transformer for the given parser context.
10
+ # Creates a node transformer for the given Haml document.
11
11
  #
12
- # @param parser [HamlLint::Parser]
13
- def initialize(parser)
14
- @parser = parser
12
+ # @param document [HamlLint::Document]
13
+ def initialize(document)
14
+ @document = document
15
15
  end
16
16
 
17
17
  # Transforms the given {Haml::Parser::ParseNode} into its corresponding
@@ -19,7 +19,7 @@ module HamlLint
19
19
  def transform(haml_node)
20
20
  node_class = "#{HamlLint::Utils.camel_case(haml_node.type.to_s)}Node"
21
21
 
22
- HamlLint::Tree.const_get(node_class).new(@parser, haml_node)
22
+ HamlLint::Tree.const_get(node_class).new(@document, haml_node)
23
23
  rescue NameError
24
24
  # TODO: Wrap in parser error?
25
25
  raise
@@ -31,11 +31,6 @@ module HamlLint
31
31
  private
32
32
 
33
33
  def add_linter_options(parser)
34
- parser.on('-e', '--exclude file,...', Array,
35
- 'List of file names to exclude') do |files|
36
- @options[:excluded_files] = files
37
- end
38
-
39
34
  parser.on('-i', '--include-linter linter,...', Array,
40
35
  'Specify which linters you want to run') do |linters|
41
36
  @options[:included_linters] = linters
@@ -48,10 +43,22 @@ module HamlLint
48
43
 
49
44
  parser.on('-r', '--reporter reporter', String,
50
45
  'Specify which reporter you want to use to generate the output') do |reporter|
51
- @options[:reporter] = HamlLint::Reporter.const_get("#{reporter.capitalize}Reporter")
46
+ @options[:reporter] = load_reporter_class(reporter.capitalize)
52
47
  end
53
48
  end
54
49
 
50
+ # Returns the class of the specified Reporter.
51
+ #
52
+ # @param reporter_name [String]
53
+ # @raise [HamlLint::Exceptions::InvalidCLIOption] if reporter doesn't exist
54
+ # @return [Class]
55
+ def load_reporter_class(reporter_name)
56
+ HamlLint::Reporter.const_get("#{reporter_name}Reporter")
57
+ rescue NameError
58
+ raise HamlLint::Exceptions::InvalidCLIOption,
59
+ "#{reporter_name}Reporter does not exist"
60
+ end
61
+
55
62
  def add_file_options(parser)
56
63
  parser.on('-c', '--config config-file', String,
57
64
  'Specify which configuration file you want to use') do |conf_file|
@@ -1,5 +1,6 @@
1
1
  require 'rake'
2
2
  require 'rake/tasklib'
3
+ require 'haml_lint/constants'
3
4
 
4
5
  module HamlLint
5
6
  # Rake task interface for haml-lint command line interface.
@@ -51,6 +52,8 @@ module HamlLint
51
52
  attr_accessor :quiet
52
53
 
53
54
  # Create the task so it exists in the current namespace.
55
+ #
56
+ # @param name [Symbol] task name
54
57
  def initialize(name = :haml_lint)
55
58
  @name = name
56
59
  @files = ['.'] # Search for everything under current directory by default
@@ -63,27 +66,34 @@ module HamlLint
63
66
 
64
67
  private
65
68
 
69
+ # Defines the Rake task.
66
70
  def define
67
71
  desc default_description unless ::Rake.application.last_description
68
72
 
69
73
  task(name, [:files]) do |_task, task_args|
70
74
  # Lazy-load so task doesn't affect Rakefile load time
71
- require 'haml_lint'
72
75
  require 'haml_lint/cli'
73
76
 
74
77
  run_cli(task_args)
75
78
  end
76
79
  end
77
80
 
81
+ # Executes the CLI given the specified task arguments.
82
+ #
83
+ # @param task_args [Rake::TaskArguments]
78
84
  def run_cli(task_args)
79
85
  cli_args = ['--config', config] if config
80
86
 
81
87
  logger = quiet ? HamlLint::Logger.silent : HamlLint::Logger.new(STDOUT)
82
88
  result = HamlLint::CLI.new(logger).run(Array(cli_args) + files_to_lint(task_args))
83
89
 
84
- fail "haml-lint failed with exit code #{result}" unless result == 0
90
+ fail "#{HamlLint::APP_NAME} failed with exit code #{result}" unless result == 0
85
91
  end
86
92
 
93
+ # Returns the list of files that should be linted given the specified task
94
+ # arguments.
95
+ #
96
+ # @param task_args [Rake::TaskArguments]
87
97
  def files_to_lint(task_args)
88
98
  # Note: we're abusing Rake's argument handling a bit here. We call the
89
99
  # first argument `files` but it's actually only the first file--we pull
@@ -96,8 +106,13 @@ module HamlLint
96
106
  end
97
107
 
98
108
  # Friendly description that shows the full command that will be executed.
109
+ #
110
+ # This allows us to change the information displayed by `rake --tasks` based
111
+ # on the options passed to the constructor which defined the task.
112
+ #
113
+ # @return [String]
99
114
  def default_description
100
- description = 'Run `haml-lint'
115
+ description = "Run `#{HamlLint::APP_NAME}"
101
116
  description += " --config #{config}" if config
102
117
  description += " #{files.join(' ')}" if files.any?
103
118
  description += ' [files...]`'
@@ -1,9 +1,16 @@
1
1
  module HamlLint
2
2
  # Contains information about all lints detected during a scan.
3
3
  class Report
4
+ # List of lints that were found.
4
5
  attr_accessor :lints
6
+
7
+ # List of files that were linted.
5
8
  attr_reader :files
6
9
 
10
+ # Creates a report.
11
+ #
12
+ # @param lints [Array<HamlLint::Lint>] lints that were found
13
+ # @param files [Array<String>] files that were linted
7
14
  def initialize(lints, files)
8
15
  @lints = lints.sort_by { |l| [l.filename, l.line] }
9
16
  @files = files
@@ -2,8 +2,8 @@ module HamlLint
2
2
  # Outputs lints in a simple format with the filename, line number, and lint
3
3
  # message.
4
4
  class Reporter::DefaultReporter < Reporter
5
- def report_lints
6
- sorted_lints = lints.sort_by { |l| [l.filename, l.line] }
5
+ def display_report(report)
6
+ sorted_lints = report.lints.sort_by { |l| [l.filename, l.line] }
7
7
 
8
8
  sorted_lints.each do |lint|
9
9
  print_location(lint)