haml_lint 0.44.0 → 0.46.0

Sign up to get free protection for your applications and to get access to all the features.
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