haml_lint 0.44.0 → 0.46.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.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/bin/haml-lint +1 -1
  3. data/config/default.yml +6 -28
  4. data/config/forced_rubocop_config.yml +156 -0
  5. data/lib/haml_lint/adapter/haml_4.rb +18 -0
  6. data/lib/haml_lint/adapter/haml_5.rb +11 -0
  7. data/lib/haml_lint/adapter/haml_6.rb +11 -0
  8. data/lib/haml_lint/cli.rb +8 -3
  9. data/lib/haml_lint/configuration_loader.rb +13 -12
  10. data/lib/haml_lint/document.rb +89 -8
  11. data/lib/haml_lint/exceptions.rb +6 -0
  12. data/lib/haml_lint/extensions/haml_util_unescape_interpolation_tracking.rb +35 -0
  13. data/lib/haml_lint/file_finder.rb +2 -2
  14. data/lib/haml_lint/lint.rb +10 -1
  15. data/lib/haml_lint/linter/final_newline.rb +4 -3
  16. data/lib/haml_lint/linter/implicit_div.rb +1 -1
  17. data/lib/haml_lint/linter/indentation.rb +3 -3
  18. data/lib/haml_lint/linter/no_placeholders.rb +18 -0
  19. data/lib/haml_lint/linter/rubocop.rb +351 -59
  20. data/lib/haml_lint/linter/space_before_script.rb +8 -10
  21. data/lib/haml_lint/linter/unnecessary_string_output.rb +1 -1
  22. data/lib/haml_lint/linter/view_length.rb +1 -1
  23. data/lib/haml_lint/linter.rb +56 -9
  24. data/lib/haml_lint/linter_registry.rb +3 -5
  25. data/lib/haml_lint/logger.rb +2 -2
  26. data/lib/haml_lint/options.rb +26 -2
  27. data/lib/haml_lint/rake_task.rb +2 -2
  28. data/lib/haml_lint/reporter/hash_reporter.rb +1 -3
  29. data/lib/haml_lint/reporter/offense_count_reporter.rb +1 -1
  30. data/lib/haml_lint/reporter/utils.rb +33 -4
  31. data/lib/haml_lint/ruby_extraction/ad_hoc_chunk.rb +20 -0
  32. data/lib/haml_lint/ruby_extraction/base_chunk.rb +113 -0
  33. data/lib/haml_lint/ruby_extraction/chunk_extractor.rb +504 -0
  34. data/lib/haml_lint/ruby_extraction/coordinator.rb +181 -0
  35. data/lib/haml_lint/ruby_extraction/haml_comment_chunk.rb +54 -0
  36. data/lib/haml_lint/ruby_extraction/implicit_end_chunk.rb +17 -0
  37. data/lib/haml_lint/ruby_extraction/interpolation_chunk.rb +26 -0
  38. data/lib/haml_lint/ruby_extraction/non_ruby_filter_chunk.rb +32 -0
  39. data/lib/haml_lint/ruby_extraction/placeholder_marker_chunk.rb +40 -0
  40. data/lib/haml_lint/ruby_extraction/ruby_filter_chunk.rb +33 -0
  41. data/lib/haml_lint/ruby_extraction/ruby_source.rb +5 -0
  42. data/lib/haml_lint/ruby_extraction/script_chunk.rb +132 -0
  43. data/lib/haml_lint/ruby_extraction/tag_attributes_chunk.rb +39 -0
  44. data/lib/haml_lint/ruby_extraction/tag_script_chunk.rb +30 -0
  45. data/lib/haml_lint/ruby_extractor.rb +11 -10
  46. data/lib/haml_lint/runner.rb +35 -3
  47. data/lib/haml_lint/spec/matchers/report_lint.rb +22 -7
  48. data/lib/haml_lint/spec/normalize_indent.rb +2 -2
  49. data/lib/haml_lint/spec/shared_linter_context.rb +9 -1
  50. data/lib/haml_lint/spec/shared_rubocop_autocorrect_context.rb +143 -0
  51. data/lib/haml_lint/spec.rb +1 -0
  52. data/lib/haml_lint/tree/filter_node.rb +10 -0
  53. data/lib/haml_lint/tree/node.rb +13 -4
  54. data/lib/haml_lint/tree/tag_node.rb +5 -9
  55. data/lib/haml_lint/utils.rb +130 -5
  56. data/lib/haml_lint/version.rb +1 -1
  57. data/lib/haml_lint/version_comparer.rb +25 -0
  58. data/lib/haml_lint.rb +12 -0
  59. metadata +25 -6
@@ -27,11 +27,9 @@ module HamlLint
27
27
  # @return [Array<Class>]
28
28
  def extract_linters_from(linter_names)
29
29
  linter_names.map do |linter_name|
30
- begin
31
- HamlLint::Linter.const_get(linter_name)
32
- rescue NameError
33
- raise NoSuchLinter, "Linter #{linter_name} does not exist"
34
- end
30
+ HamlLint::Linter.const_get(linter_name)
31
+ rescue NameError
32
+ raise NoSuchLinter, "Linter #{linter_name} does not exist"
35
33
  end
36
34
  end
37
35
  end
@@ -30,7 +30,7 @@ module HamlLint
30
30
  #
31
31
  # @param output [String] the output to send
32
32
  # @param newline [true,false] whether to append a newline
33
- def log(output, newline = true)
33
+ def log(output, newline = true) # rubocop:disable Style/OptionalBooleanParameter
34
34
  @out.print(output)
35
35
  @out.print("\n") if newline
36
36
  end
@@ -97,7 +97,7 @@ module HamlLint
97
97
 
98
98
  private
99
99
 
100
- def color(code, output, newline = true)
100
+ def color(code, output, newline = true) # rubocop:disable Style/OptionalBooleanParameter
101
101
  log(color_enabled ? "\033[#{code}m#{output}\033[0m" : output, newline)
102
102
  end
103
103
  end
@@ -32,7 +32,7 @@ module HamlLint
32
32
 
33
33
  private
34
34
 
35
- def add_linter_options(parser)
35
+ def add_linter_options(parser) # rubocop:disable Metrics/MethodLength
36
36
  parser.on('--auto-gen-config', 'Generate a configuration file acting as a TODO list') do
37
37
  @options[:auto_gen_config] = true
38
38
  end
@@ -55,6 +55,20 @@ module HamlLint
55
55
  parser.on('-p', '--parallel', 'Run linters in parallel using available CPUs') do
56
56
  @options[:parallel] = true
57
57
  end
58
+
59
+ parser.on('-a', '--auto-correct', 'Auto-correct offenses (only when it’s safe)') do
60
+ @options[:autocorrect] = :safe
61
+ end
62
+
63
+ parser.on('-A', '--auto-correct-all', 'Auto-correct offenses (safe and unsafe)') do
64
+ @options[:autocorrect] = :all
65
+ end
66
+
67
+ parser.on('--auto-correct-only', "Only do auto-correct, don't lint. " \
68
+ 'Also activates safe auto-correct if no auto-correct is selected') do
69
+ @options[:autocorrect_only] = true
70
+ @options[:autocorrect] ||= :safe
71
+ end
58
72
  end
59
73
 
60
74
  def add_report_options(parser)
@@ -99,7 +113,7 @@ module HamlLint
99
113
  end
100
114
  end
101
115
 
102
- def add_info_options(parser)
116
+ def add_info_options(parser) # rubocop:disable Metrics/MethodLength
103
117
  parser.on('--show-linters', 'Display available linters') do
104
118
  @options[:show_linters] = true
105
119
  end
@@ -108,6 +122,16 @@ module HamlLint
108
122
  @options[:show_reporters] = true
109
123
  end
110
124
 
125
+ parser.on('-d', '--debug', 'Add some debug information to messages') do
126
+ @options[:debug] = true
127
+ end
128
+
129
+ parser.on('--internal-debug', 'Add lots of (internal) debug information.' \
130
+ " Also affects some lint's line numbers to skip sourcemap") do
131
+ @options[:debug] = true
132
+ @options[:internal_debug] = true
133
+ end
134
+
111
135
  parser.on_tail('-h', '--help', 'Display help documentation') do
112
136
  @options[:help] = parser.help
113
137
  end
@@ -104,7 +104,7 @@ module HamlLint
104
104
  # @param task_args [Rake::TaskArguments]
105
105
  def run_cli(task_args)
106
106
  cli_args = parse_args
107
- logger = quiet ? HamlLint::Logger.silent : HamlLint::Logger.new(STDOUT)
107
+ logger = quiet ? HamlLint::Logger.silent : HamlLint::Logger.new($stdout)
108
108
  result = HamlLint::CLI.new(logger).run(Array(cli_args) + files_to_lint(task_args))
109
109
 
110
110
  fail "#{HamlLint::APP_NAME} failed with exit code #{result}" unless result == 0
@@ -115,7 +115,7 @@ module HamlLint
115
115
  #
116
116
  # @param task_args [Rake::TaskArguments]
117
117
  def files_to_lint(task_args)
118
- # Note: we're abusing Rake's argument handling a bit here. We call the
118
+ # NOTE: we're abusing Rake's argument handling a bit here. We call the
119
119
  # first argument `files` but it's actually only the first file--we pull
120
120
  # the rest out of the `extras` from the task arguments. This is so we
121
121
  # can specify an arbitrary list of files separated by commas on the
@@ -14,7 +14,7 @@ module HamlLint
14
14
  lints = report.lints
15
15
  grouped = lints.group_by(&:filename)
16
16
 
17
- report_hash = {
17
+ {
18
18
  metadata: metadata,
19
19
  files: grouped.map { |l| map_file(l) },
20
20
  summary: {
@@ -23,8 +23,6 @@ module HamlLint
23
23
  inspected_file_count: report.files.length,
24
24
  },
25
25
  }
26
-
27
- report_hash
28
26
  end
29
27
 
30
28
  private
@@ -10,7 +10,7 @@ module HamlLint
10
10
  return if total_count.zero?
11
11
 
12
12
  lints.group_by { |l| lint_type_group(l) }
13
- .map { |linter, lints_for_this_linter| [linter, lints_for_this_linter.size] }.to_h
13
+ .transform_values(&:size)
14
14
  .sort_by { |_linter, lint_count| -lint_count }
15
15
  .each do |linter, lint_count|
16
16
  log.log "#{lint_count.to_s.ljust(total_count.to_s.length + 2)} #{linter}"
@@ -56,6 +56,10 @@ module HamlLint
56
56
  # @param lint [HamlLint::Lint] the lint to print
57
57
  # @return [void]
58
58
  def print_message(lint)
59
+ if lint.corrected
60
+ log.success('[Corrected] ', false)
61
+ end
62
+
59
63
  if lint.linter
60
64
  log.success("#{lint.linter.name}: ", false)
61
65
  end
@@ -70,10 +74,15 @@ module HamlLint
70
74
  def print_summary(report)
71
75
  return unless log.summary_enabled
72
76
 
77
+ log.log('')
73
78
  print_summary_files(report)
74
- print_summary_lints(report)
75
79
 
76
- log.log ' detected'
80
+ print_summary_lints(report, is_append: true)
81
+
82
+ log.log ' detected', false
83
+
84
+ print_summary_corrected_lints(report, is_append: true)
85
+ log.log ''
77
86
  end
78
87
 
79
88
  # Prints a summary of the number of files linted in a report.
@@ -81,14 +90,17 @@ module HamlLint
81
90
  # @param report [HamlLint::Report] the report to print
82
91
  # @return [void]
83
92
  def print_summary_files(report)
84
- log.log "\n#{pluralize('file', count: report.files.count)} inspected, ", false
93
+ log.log "#{pluralize('file', count: report.files.count)} inspected", false
85
94
  end
86
95
 
87
96
  # Prints a summary of the number of lints found in a report.
88
97
  #
89
98
  # @param report [HamlLint::Report] the report to print
99
+ # @param is_append [Boolean] if this is appending to a line. Will preffix with ", ".
90
100
  # @return [void]
91
- def print_summary_lints(report)
101
+ def print_summary_lints(report, is_append:)
102
+ log.log ', ', false if is_append
103
+
92
104
  lint_count = report.lints.size
93
105
  lint_message = pluralize('lint', count: lint_count)
94
106
 
@@ -98,6 +110,23 @@ module HamlLint
98
110
  log.error lint_message, false
99
111
  end
100
112
  end
113
+
114
+ # Prints a summary of the number of lints corrected in a report.
115
+ #
116
+ # @param report [HamlLint::Report] the report to print
117
+ # @param is_append [Boolean] if this is appending to a line. Will preffix with ", ".
118
+ # @return [void]
119
+ def print_summary_corrected_lints(report, is_append:)
120
+ lint_count = report.lints.count(&:corrected)
121
+ return if lint_count == 0
122
+
123
+ log.log ', ', false if is_append
124
+
125
+ lint_message = pluralize('lint', count: lint_count)
126
+
127
+ log.info lint_message, false
128
+ log.log ' corrected', false
129
+ end
101
130
  end
102
131
  end
103
132
  end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HamlLint::RubyExtraction
4
+ # This chunk just adds its code to the ruby, but does not attempt to transfer their correction
5
+ # in any way.
6
+ #
7
+ # Used for piece of code that just need to be in the generated ruby for reasons specific to
8
+ # the use cases, such as needing a `begin` to do add indentation.
9
+ class AdHocChunk < BaseChunk
10
+ def initialize(*args, **kwargs)
11
+ super(*args, **kwargs.merge(end_marker_indent: nil))
12
+ end
13
+
14
+ def wrap_in_markers
15
+ false
16
+ end
17
+
18
+ def transfer_correction(coordinator, all_corrected_ruby_lines, haml_lines); end
19
+ end
20
+ end
@@ -0,0 +1,113 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HamlLint::RubyExtraction
4
+ # This is the base class for all of the Chunks of HamlLint::RubyExtraction.
5
+ # A Chunk represents a part of the HAML file that HamlLint::Linter::RuboCop
6
+ # is processing and will insert some Ruby code in a file passed to RuboCop.
7
+ #
8
+ # There are chunks for most HAML concepts, even if they don't represent Ruby
9
+ # code. For example, there is a chunk that represents a `%div` tag, which
10
+ # uses a `begin` in the generated Ruby to add indentation for the children
11
+ # of the %div in the Ruby file just like there is in the HAML file.
12
+ class BaseChunk
13
+ COMMA_CHANGES_LINES = true
14
+
15
+ # @return [HamlLint::Tree::Node] Haml node that this comes from
16
+ attr_reader :node
17
+
18
+ # @return [Integer] First line index of the auto-correctable code in the Haml source
19
+ # Usually same as node.line - 1, but some cases, such as interpolation in a filter will
20
+ # will be different.
21
+ attr_reader :haml_line_index
22
+
23
+ # @return [Integer] Line number of the line marker in the ruby source placed before
24
+ # this auto-correctable code
25
+ attr_reader :start_marker_line_number
26
+
27
+ # @return [Integer] The indentation (number of spaces) to use to index the marker
28
+ # that follows this chunk. Unlike the marker before, this one can vary.
29
+ attr_reader :end_marker_indent
30
+
31
+ # @return [Array<String>] The ruby lines that this chunk will insert
32
+ attr_reader :ruby_lines
33
+
34
+ def initialize(node,
35
+ ruby_lines,
36
+ end_marker_indent:, haml_line_index: node.line - 1)
37
+ ruby_lines = [ruby_lines] if ruby_lines.is_a?(String)
38
+ @node = node
39
+ @ruby_lines = ruby_lines
40
+ @haml_line_index = haml_line_index
41
+ @end_marker_indent = end_marker_indent
42
+ end
43
+
44
+ # To be overridden in subclasses.
45
+ # Return a new chunk which is the result of fusing self with the given following chunk.
46
+ # If no fusion is possible, returns nil
47
+ def fuse(_following_chunk)
48
+ nil
49
+ end
50
+
51
+ # Overwrites haml_lines to match the Ruby code that was corrected by RuboCop which is in
52
+ # all_corrected_ruby_lines. This can change non-ruby parts to, especially for
53
+ # indentation.
54
+ #
55
+ # This will be called on ruby chunks in the reverse order they were created. Two benefits
56
+ # of this approach:
57
+ # * No need to track when lines in haml_lines are moved to apply changes later in the file
58
+ # * When fixing indentation of lines that follow a corrected line, those following lines will
59
+ # already have been corrected and so require nothing.
60
+ # Can be overridden by subclasses to make it do nothing
61
+ def transfer_correction(coordinator, _all_corrected_ruby_lines, haml_lines)
62
+ to_ruby_lines = coordinator.extract_from_corrected_lines(@start_marker_line_number, @ruby_lines.size)
63
+ transfer_correction_logic(coordinator, to_ruby_lines, haml_lines)
64
+ end
65
+
66
+ # To be overriden by subclasses.
67
+ #
68
+ # Logic to transfer the corrections that turned from_ruby_lines into to_ruby_lines.
69
+ #
70
+ # This method only received the ruby code that belongs to this chunk. (It was
71
+ # extracted using #extract_from by #transfer_correction)
72
+ def transfer_correction_logic(_coordinator, _to_ruby_lines, _haml_lines)
73
+ raise "Implement #transfer_correction_logic in #{self.class.name}"
74
+ end
75
+
76
+ def start_marker_indent
77
+ ruby_lines.first[/ */].size
78
+ end
79
+
80
+ def haml_end_line_index
81
+ @haml_line_index + nb_haml_lines - 1
82
+ end
83
+
84
+ def nb_haml_lines
85
+ @ruby_lines.size - skip_line_indexes_in_source_map.size
86
+ end
87
+
88
+ def full_assemble(coordinator)
89
+ if wrap_in_markers
90
+ @start_marker_line_number = coordinator.add_marker(start_marker_indent,
91
+ haml_line_index: haml_line_index)
92
+ assemble_in(coordinator)
93
+ coordinator.add_marker(@end_marker_indent, haml_line_index: haml_end_line_index)
94
+ else
95
+ assemble_in(coordinator)
96
+ end
97
+ end
98
+
99
+ def assemble_in(coordinator)
100
+ coordinator.add_lines(@ruby_lines,
101
+ haml_line_index: haml_line_index,
102
+ skip_indexes_in_source_map: skip_line_indexes_in_source_map)
103
+ end
104
+
105
+ def skip_line_indexes_in_source_map
106
+ []
107
+ end
108
+
109
+ def wrap_in_markers
110
+ true
111
+ end
112
+ end
113
+ end