haml_lint 0.53.0 → 0.76.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 +4 -4
- data/config/default.yml +17 -5
- data/config/forced_rubocop_config.yml +25 -13
- data/lib/haml_lint/adapter.rb +5 -4
- data/lib/haml_lint/cli.rb +7 -3
- data/lib/haml_lint/constants.rb +1 -1
- data/lib/haml_lint/directive.rb +2 -2
- data/lib/haml_lint/document.rb +14 -5
- data/lib/haml_lint/exceptions.rb +2 -2
- data/lib/haml_lint/extensions/haml_util_unescape_interpolation_tracking.rb +2 -2
- data/lib/haml_lint/haml_visitor.rb +1 -1
- data/lib/haml_lint/lint.rb +8 -3
- data/lib/haml_lint/linter/alignment_tabs.rb +21 -4
- data/lib/haml_lint/linter/class_attribute_with_static_value.rb +52 -12
- data/lib/haml_lint/linter/classes_before_ids.rb +15 -6
- data/lib/haml_lint/linter/consecutive_comments.rb +20 -1
- data/lib/haml_lint/linter/consecutive_silent_scripts.rb +68 -1
- data/lib/haml_lint/linter/empty_object_reference.rb +16 -1
- data/lib/haml_lint/linter/empty_script.rb +32 -2
- data/lib/haml_lint/linter/final_newline.rb +31 -7
- data/lib/haml_lint/linter/html_attributes.rb +71 -2
- data/lib/haml_lint/linter/id_names.rb +1 -1
- data/lib/haml_lint/linter/implicit_div.rb +14 -1
- data/lib/haml_lint/linter/indentation.rb +2 -2
- data/lib/haml_lint/linter/instance_variables.rb +28 -3
- data/lib/haml_lint/linter/leading_comment_space.rb +14 -2
- data/lib/haml_lint/linter/multiline_pipe.rb +3 -3
- data/lib/haml_lint/linter/multiline_script.rb +65 -5
- data/lib/haml_lint/linter/no_placeholders.rb +2 -2
- data/lib/haml_lint/linter/rubocop.rb +87 -76
- data/lib/haml_lint/linter/ruby_comments.rb +14 -4
- data/lib/haml_lint/linter/space_before_script.rb +37 -4
- data/lib/haml_lint/linter/space_inside_hash_attributes.rb +41 -3
- data/lib/haml_lint/linter/strict_locals.rb +69 -0
- data/lib/haml_lint/linter/tag_name.rb +15 -2
- data/lib/haml_lint/linter/trailing_empty_lines.rb +13 -1
- data/lib/haml_lint/linter/trailing_whitespace.rb +16 -3
- data/lib/haml_lint/linter/unescaped_html.rb +27 -0
- data/lib/haml_lint/linter/unnecessary_interpolation.rb +30 -3
- data/lib/haml_lint/linter/unnecessary_string_output.rb +108 -12
- data/lib/haml_lint/linter.rb +119 -5
- data/lib/haml_lint/node_transformer.rb +2 -2
- data/lib/haml_lint/options.rb +17 -3
- data/lib/haml_lint/rake_task.rb +3 -2
- data/lib/haml_lint/reporter/default_reporter.rb +1 -1
- data/lib/haml_lint/reporter/disabled_config_reporter.rb +29 -8
- data/lib/haml_lint/reporter/github_reporter.rb +50 -0
- data/lib/haml_lint/reporter/hash_reporter.rb +2 -0
- data/lib/haml_lint/reporter/json_reporter.rb +1 -1
- data/lib/haml_lint/reporter/progress_reporter.rb +2 -1
- data/lib/haml_lint/reporter/utils.rb +4 -2
- data/lib/haml_lint/reporter.rb +1 -1
- data/lib/haml_lint/ruby_extraction/base_chunk.rb +4 -4
- data/lib/haml_lint/ruby_extraction/chunk_extractor.rb +73 -32
- data/lib/haml_lint/ruby_extraction/coordinator.rb +9 -4
- data/lib/haml_lint/ruby_extraction/implicit_end_chunk.rb +1 -1
- data/lib/haml_lint/ruby_extraction/non_ruby_filter_chunk.rb +2 -2
- data/lib/haml_lint/ruby_extraction/placeholder_marker_chunk.rb +1 -1
- data/lib/haml_lint/ruby_extraction/ruby_filter_chunk.rb +1 -1
- data/lib/haml_lint/ruby_extraction/script_chunk.rb +2 -2
- data/lib/haml_lint/runner.rb +70 -30
- data/lib/haml_lint/source.rb +25 -0
- data/lib/haml_lint/spec/matchers/report_lint.rb +17 -6
- data/lib/haml_lint/spec/shared_rubocop_autocorrect_context.rb +34 -4
- data/lib/haml_lint/spec.rb +4 -4
- data/lib/haml_lint/tree/comment_node.rb +1 -1
- data/lib/haml_lint/tree/doctype_node.rb +1 -1
- data/lib/haml_lint/tree/filter_node.rb +14 -1
- data/lib/haml_lint/tree/haml_comment_node.rb +9 -2
- data/lib/haml_lint/tree/node.rb +4 -4
- data/lib/haml_lint/tree/root_node.rb +4 -4
- data/lib/haml_lint/tree/script_node.rb +1 -1
- data/lib/haml_lint/tree/silent_script_node.rb +1 -1
- data/lib/haml_lint/tree/tag_node.rb +69 -25
- data/lib/haml_lint/utils.rb +4 -31
- data/lib/haml_lint/version.rb +1 -1
- data/lib/haml_lint.rb +23 -23
- metadata +14 -10
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 188328ae96f24b3a739665a5d3ccc7271a753afbe0ae63a1fbe3f33897492caa
|
|
4
|
+
data.tar.gz: 52b39c0992433545f610c7c18af3d06b224027edea3a9cdb97f71c7c1cf4f8c5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 31c53c1177acc7679c9f3c5b633a2b14278252f447ddea3b03c9d49e82d6e3d4587d44803e7b2b5ca677b726702c1a716cb02c848b8d2b71ed7bd402b78d8519
|
|
7
|
+
data.tar.gz: 253e971535ea2d8c38340e2e13a882fbe215cded72f5faa96b26dd88bb39024efe5b03d8334cc7692de4edbab2a7e49db9d4bb55a6a55e10319b52e3770a9ea4
|
data/config/default.yml
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
# This is an opinionated list of which hooks are valuable to run and what their
|
|
4
4
|
# out of the box settings should be.
|
|
5
5
|
|
|
6
|
-
# Whether to ignore frontmatter at the beginning of
|
|
6
|
+
# Whether to ignore frontmatter at the beginning of Haml documents for
|
|
7
7
|
# frameworks such as Jekyll/Middleman
|
|
8
8
|
skip_frontmatter: false
|
|
9
9
|
|
|
@@ -50,8 +50,8 @@ linters:
|
|
|
50
50
|
|
|
51
51
|
Indentation:
|
|
52
52
|
enabled: true
|
|
53
|
-
character: space
|
|
54
|
-
width: 2
|
|
53
|
+
character: space # or tab
|
|
54
|
+
width: 2 # ignored if character == tab
|
|
55
55
|
|
|
56
56
|
InlineStyles:
|
|
57
57
|
enabled: true
|
|
@@ -88,8 +88,10 @@ linters:
|
|
|
88
88
|
|
|
89
89
|
RuboCop:
|
|
90
90
|
enabled: true
|
|
91
|
-
#
|
|
92
|
-
#
|
|
91
|
+
# Cops listed here are skipped entirely (passed to RuboCop via `--except`), both when
|
|
92
|
+
# reporting and when auto-correcting. This is the way to disable a cop only for Haml files,
|
|
93
|
+
# and the only way to disable a forced cop (see config/forced_rubocop_config.yml), since the
|
|
94
|
+
# forced configuration always overrides `Enabled: false` set in your own .rubocop.yml.
|
|
93
95
|
ignored_cops: []
|
|
94
96
|
|
|
95
97
|
RubyComments:
|
|
@@ -102,6 +104,13 @@ linters:
|
|
|
102
104
|
enabled: true
|
|
103
105
|
style: space
|
|
104
106
|
|
|
107
|
+
StrictLocals:
|
|
108
|
+
enabled: false
|
|
109
|
+
file_types: partials
|
|
110
|
+
matchers:
|
|
111
|
+
all: .*
|
|
112
|
+
partials: \A_.*\.haml\z
|
|
113
|
+
|
|
105
114
|
TagName:
|
|
106
115
|
enabled: true
|
|
107
116
|
|
|
@@ -111,6 +120,9 @@ linters:
|
|
|
111
120
|
TrailingWhitespace:
|
|
112
121
|
enabled: true
|
|
113
122
|
|
|
123
|
+
UnescapedHtml:
|
|
124
|
+
enabled: true
|
|
125
|
+
|
|
114
126
|
UnnecessaryInterpolation:
|
|
115
127
|
enabled: true
|
|
116
128
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# These are some configurations that are required for RuboCop because:
|
|
2
|
-
# *
|
|
3
|
-
#
|
|
4
|
-
# *
|
|
2
|
+
# * Haml-Lint compiles ruby code with a particular format. If the rules mis-match that format,
|
|
3
|
+
# Haml-Lint would generate lints that the user cannot fix.
|
|
4
|
+
# * Haml-Lint can autocorrect code only if the result matches some specific format
|
|
5
5
|
#
|
|
6
6
|
# So these configuration should not be overwritable by users.
|
|
7
7
|
|
|
@@ -31,20 +31,32 @@ Layout/CaseIndentation:
|
|
|
31
31
|
IndentOneStep: false # Need to force the `false`
|
|
32
32
|
|
|
33
33
|
# Need this cop so that code gets formatted similarly to Haml's indentation,
|
|
34
|
-
# since
|
|
34
|
+
# since Haml-Lint relies on Ruby's indentation being the same as Haml's.
|
|
35
35
|
Layout/ElseAlignment:
|
|
36
36
|
Enabled: true
|
|
37
37
|
|
|
38
38
|
# Need this cop so that code gets formatted similarly to Haml's indentation,
|
|
39
|
-
# since
|
|
39
|
+
# since Haml-Lint relies on Ruby's indentation being the same as Haml's.
|
|
40
40
|
Layout/EndAlignment:
|
|
41
41
|
EnforcedStyleAlignWith: start_of_line
|
|
42
42
|
Enabled: true
|
|
43
43
|
|
|
44
|
-
# We generate the ruby content, this is basically useless and should be a lint in
|
|
44
|
+
# We generate the ruby content, this is basically useless and should be a lint in Haml-Lint
|
|
45
45
|
Layout/EndOfLine:
|
|
46
46
|
Enabled: false
|
|
47
47
|
|
|
48
|
+
# Complying with this lint requires the use of pipes which causes a conflict with MultilinePipe.
|
|
49
|
+
Layout/FirstArrayElementLineBreak:
|
|
50
|
+
Enabled: false
|
|
51
|
+
|
|
52
|
+
# Complying with this lint requires the use of pipes which causes a conflict with MultilinePipe.
|
|
53
|
+
Layout/FirstHashElementLineBreak:
|
|
54
|
+
Enabled: false
|
|
55
|
+
|
|
56
|
+
# Complying with this lint requires the use of pipes which causes a conflict with MultilinePipe.
|
|
57
|
+
Layout/FirstMethodArgumentLineBreak:
|
|
58
|
+
Enabled: false
|
|
59
|
+
|
|
48
60
|
# Turning this cop on can turn
|
|
49
61
|
# = content_tag(:span) do
|
|
50
62
|
# - foo
|
|
@@ -78,14 +90,14 @@ Metrics/BlockNesting:
|
|
|
78
90
|
Naming/FileName:
|
|
79
91
|
Enabled: false
|
|
80
92
|
|
|
81
|
-
#
|
|
93
|
+
# Haml doesn't properly support multiline blocks using { }, only using do/end.
|
|
82
94
|
# If you don't consider the { } block for indentation, things "works", but the indentation is misleading.
|
|
83
95
|
# For example, this works:
|
|
84
96
|
# - a = lambda {
|
|
85
97
|
# - if abc
|
|
86
98
|
# - something
|
|
87
99
|
# - }
|
|
88
|
-
# But if you indented the 2 lines within { }, then
|
|
100
|
+
# But if you indented the 2 lines within { }, then Haml would add an extra `end` and the generated
|
|
89
101
|
# ruby would be invalid.
|
|
90
102
|
Style/BlockDelimiters:
|
|
91
103
|
# So we need this cop to cleanup those cases and turn them to `end`.
|
|
@@ -94,7 +106,7 @@ Style/BlockDelimiters:
|
|
|
94
106
|
# We don't allow the default "Can be anything" exception for lambda/proc
|
|
95
107
|
<%= rubocop_version < '1.33' ? 'IgnoredMethods' : 'AllowedMethods' %>: []
|
|
96
108
|
|
|
97
|
-
# We don't support correcting
|
|
109
|
+
# We don't support correcting Haml comments
|
|
98
110
|
Style/CommentAnnotation:
|
|
99
111
|
AutoCorrect: false
|
|
100
112
|
|
|
@@ -132,7 +144,7 @@ Style/RedundantStringEscape:
|
|
|
132
144
|
<% end %>
|
|
133
145
|
|
|
134
146
|
# In some case, this cop can cause a change in the spacing.
|
|
135
|
-
# In
|
|
147
|
+
# In Haml 5.2, going from (absurd example for clarity):
|
|
136
148
|
# = 'abc' rescue nil
|
|
137
149
|
# = 'def'
|
|
138
150
|
# to:
|
|
@@ -149,7 +161,7 @@ Style/RescueModifier:
|
|
|
149
161
|
|
|
150
162
|
# Cops that remove commas can be a problem when lines are split on multiple ones.
|
|
151
163
|
# If we have a big array on more than one line, the removal of the comma generates
|
|
152
|
-
# invalid
|
|
164
|
+
# invalid Haml
|
|
153
165
|
Style/SymbolArray:
|
|
154
166
|
Enabled: false
|
|
155
167
|
|
|
@@ -158,7 +170,7 @@ Style/UnlessElse:
|
|
|
158
170
|
AutoCorrect: false
|
|
159
171
|
|
|
160
172
|
# If an array of strings was on multiple lines, this cop will make a %w(...) on multiple lines.
|
|
161
|
-
# Without the comma at the end of the first line, there the resulting
|
|
173
|
+
# Without the comma at the end of the first line, there the resulting Haml will be invalid, since the only
|
|
162
174
|
# case where a script can change line is after a comma.
|
|
163
175
|
Style/WordArray:
|
|
164
176
|
AutoCorrect: false
|
|
@@ -169,7 +181,7 @@ Layout/TrailingEmptyLines:
|
|
|
169
181
|
|
|
170
182
|
<% if rubocop_version < '1.8.1' %>
|
|
171
183
|
# There were a few bugs with this cop that got fixed in this version.
|
|
172
|
-
# Before, those bugs would generate invalid Ruby code and that would make it look like
|
|
184
|
+
# Before, those bugs would generate invalid Ruby code and that would make it look like Haml-lint is
|
|
173
185
|
# responsible, at least from the user's point of view.
|
|
174
186
|
Style/StringConcatenation:
|
|
175
187
|
Enabled: false
|
data/lib/haml_lint/adapter.rb
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
require_relative 'adapter/haml_5'
|
|
4
|
+
require_relative 'adapter/haml_6'
|
|
5
|
+
require_relative 'exceptions'
|
|
6
6
|
|
|
7
7
|
module HamlLint
|
|
8
8
|
# Determines the adapter to use for the current Haml version
|
|
@@ -19,7 +19,8 @@ module HamlLint
|
|
|
19
19
|
version = haml_version
|
|
20
20
|
case version
|
|
21
21
|
when '~> 5.0', '~> 5.1', '~> 5.2' then HamlLint::Adapter::Haml5
|
|
22
|
-
when '~> 6.0', '~> 6.0.a', '~> 6.1', '~> 6.2', '~> 6.3' then HamlLint::Adapter::Haml6
|
|
22
|
+
when '~> 6.0', '~> 6.0.a', '~> 6.1', '~> 6.2', '~> 6.3', '~> 6.4' then HamlLint::Adapter::Haml6
|
|
23
|
+
when '~> 7.0', '~> 7.1', '~> 7.2' then HamlLint::Adapter::Haml6
|
|
23
24
|
else fail HamlLint::Exceptions::UnknownHamlVersion, "Cannot handle Haml version: #{version}"
|
|
24
25
|
end
|
|
25
26
|
end
|
data/lib/haml_lint/cli.rb
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
require_relative '../haml_lint'
|
|
4
|
+
require_relative 'options'
|
|
5
5
|
|
|
6
6
|
require 'sysexits'
|
|
7
7
|
|
|
@@ -100,7 +100,11 @@ module HamlLint
|
|
|
100
100
|
# @return [HamlLint::Reporter]
|
|
101
101
|
def reporter_from_options(options)
|
|
102
102
|
if options[:auto_gen_config]
|
|
103
|
-
HamlLint::Reporter::DisabledConfigReporter.new(
|
|
103
|
+
HamlLint::Reporter::DisabledConfigReporter.new(
|
|
104
|
+
log,
|
|
105
|
+
limit: options[:auto_gen_exclude_limit] || HamlLint::Reporter::DisabledConfigReporter::DEFAULT_EXCLUDE_LIMIT,
|
|
106
|
+
options: options
|
|
107
|
+
)
|
|
104
108
|
else
|
|
105
109
|
options.fetch(:reporter, HamlLint::Reporter::DefaultReporter).new(log)
|
|
106
110
|
end
|
data/lib/haml_lint/constants.rb
CHANGED
data/lib/haml_lint/directive.rb
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
module HamlLint
|
|
4
4
|
# Handles linter configuration transformation via Haml comments.
|
|
5
5
|
class Directive
|
|
6
|
-
LINTER_REGEXP = /(?:[A-Z]\w+)
|
|
6
|
+
LINTER_REGEXP = /(?:[A-Z]\w+)/
|
|
7
7
|
|
|
8
8
|
DIRECTIVE_REGEXP = /
|
|
9
9
|
# "haml-lint:" with optional spacing
|
|
@@ -14,7 +14,7 @@ module HamlLint
|
|
|
14
14
|
|
|
15
15
|
# "all" or a comma-separated list (with optional spaces) of linters
|
|
16
16
|
(?<linters>all | (?:#{LINTER_REGEXP}\s*,\s*)* #{LINTER_REGEXP})
|
|
17
|
-
/x
|
|
17
|
+
/x
|
|
18
18
|
|
|
19
19
|
# Constructs a directive from source code as a given line.
|
|
20
20
|
#
|
data/lib/haml_lint/document.rb
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
require_relative 'adapter'
|
|
4
4
|
|
|
5
5
|
module HamlLint
|
|
6
6
|
# Represents a parsed Haml document and its associated metadata.
|
|
@@ -14,6 +14,9 @@ module HamlLint
|
|
|
14
14
|
# @return [String] Haml template file path
|
|
15
15
|
attr_reader :file
|
|
16
16
|
|
|
17
|
+
# @return [Boolean] true if source changes (from autocorrect) should be written to stdout instead of disk
|
|
18
|
+
attr_reader :write_to_stdout
|
|
19
|
+
|
|
17
20
|
# @return [HamlLint::Tree::Node] Root of the parse tree
|
|
18
21
|
attr_reader :tree
|
|
19
22
|
|
|
@@ -36,10 +39,12 @@ module HamlLint
|
|
|
36
39
|
# @param source [String] Haml code to parse
|
|
37
40
|
# @param options [Hash]
|
|
38
41
|
# @option options :file [String] file name of document that was parsed
|
|
42
|
+
# @option options :write_to_stdout [Boolean] true if source changes should be written to stdout
|
|
39
43
|
# @raise [Haml::Parser::Error] if there was a problem parsing the document
|
|
40
44
|
def initialize(source, options)
|
|
41
45
|
@config = options[:config]
|
|
42
46
|
@file = options.fetch(:file, STRING_SOURCE)
|
|
47
|
+
@write_to_stdout = options[:write_to_stdout]
|
|
43
48
|
@source_was_changed = false
|
|
44
49
|
process_source(source)
|
|
45
50
|
end
|
|
@@ -82,7 +87,11 @@ module HamlLint
|
|
|
82
87
|
if file == STRING_SOURCE
|
|
83
88
|
raise HamlLint::Exceptions::InvalidFilePath, 'Cannot write without :file option'
|
|
84
89
|
end
|
|
85
|
-
|
|
90
|
+
if @write_to_stdout
|
|
91
|
+
$stdout << unstrip_frontmatter(source)
|
|
92
|
+
else
|
|
93
|
+
File.write(file, unstrip_frontmatter(source))
|
|
94
|
+
end
|
|
86
95
|
@source_was_changed = false
|
|
87
96
|
end
|
|
88
97
|
|
|
@@ -123,8 +132,8 @@ module HamlLint
|
|
|
123
132
|
# @param original_tree [Haml::Parser::ParseNode]
|
|
124
133
|
# @return [Haml::Tree::Node]
|
|
125
134
|
def process_tree(original_tree)
|
|
126
|
-
# Remove the trailing empty
|
|
127
|
-
# the end of the
|
|
135
|
+
# Remove the trailing empty Haml comment that the parser creates to signal
|
|
136
|
+
# the end of the Haml document
|
|
128
137
|
if Gem::Requirement.new('~> 4.0.0').satisfied_by?(Gem.loaded_specs['haml'].version)
|
|
129
138
|
original_tree.children.pop
|
|
130
139
|
end
|
|
@@ -133,7 +142,7 @@ module HamlLint
|
|
|
133
142
|
convert_tree(original_tree)
|
|
134
143
|
end
|
|
135
144
|
|
|
136
|
-
# Converts a
|
|
145
|
+
# Converts a Haml parse tree to a tree of {HamlLint::Tree::Node} objects.
|
|
137
146
|
#
|
|
138
147
|
# This provides a cleaner interface with which the linters can interact with
|
|
139
148
|
# the parse tree.
|
data/lib/haml_lint/exceptions.rb
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
# Collection of exceptions that can be raised by the
|
|
3
|
+
# Collection of exceptions that can be raised by the Haml Lint application.
|
|
4
4
|
module HamlLint::Exceptions
|
|
5
5
|
# Raised when a {Configuration} could not be loaded from a file.
|
|
6
6
|
class ConfigurationError < StandardError; end
|
|
@@ -17,7 +17,7 @@ module HamlLint::Exceptions
|
|
|
17
17
|
# Raised when an invalid file path is specified
|
|
18
18
|
class InvalidFilePath < StandardError; end
|
|
19
19
|
|
|
20
|
-
# Raised when a problem occurs parsing a
|
|
20
|
+
# Raised when a problem occurs parsing a Haml document.
|
|
21
21
|
class ParseError < ::Haml::SyntaxError; end
|
|
22
22
|
|
|
23
23
|
# Raised when attempting to execute `Runner` with options that would result in
|
|
@@ -14,8 +14,8 @@ module Haml::Util
|
|
|
14
14
|
Thread.current[:haml_lint_unescape_interpolation_to_original_cache] ||= {}
|
|
15
15
|
end
|
|
16
16
|
|
|
17
|
-
# As soon as a HamlLint::Document has finished processing a
|
|
18
|
-
# get a copy of this cache and clear up for the next
|
|
17
|
+
# As soon as a HamlLint::Document has finished processing a Haml source, this gets called to
|
|
18
|
+
# get a copy of this cache and clear up for the next Haml processing
|
|
19
19
|
def self.unescape_interpolation_to_original_cache_take_and_wipe
|
|
20
20
|
value = unescape_interpolation_to_original_cache.dup
|
|
21
21
|
unescape_interpolation_to_original_cache.clear
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
module HamlLint
|
|
4
4
|
# Provides an interface which when included allows a class to visit nodes in
|
|
5
|
-
# the parse tree of a
|
|
5
|
+
# the parse tree of a Haml document.
|
|
6
6
|
module HamlVisitor
|
|
7
7
|
def visit(node)
|
|
8
8
|
# Keep track of whether this block was consumed by the visitor. This
|
data/lib/haml_lint/lint.rb
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module HamlLint
|
|
4
|
-
# Contains information about a problem or issue with a
|
|
4
|
+
# Contains information about a problem or issue with a Haml document.
|
|
5
5
|
class Lint
|
|
6
6
|
# @return [Boolean] If the error was corrected by auto-correct
|
|
7
7
|
attr_reader :corrected
|
|
8
8
|
|
|
9
|
+
# @return [Boolean] If the error could be corrected by auto-correct
|
|
10
|
+
attr_reader :correctable
|
|
11
|
+
|
|
9
12
|
# @return [String] file path to which the lint applies
|
|
10
13
|
attr_reader :filename
|
|
11
14
|
|
|
@@ -28,13 +31,14 @@ module HamlLint
|
|
|
28
31
|
# @param line [Fixnum]
|
|
29
32
|
# @param message [String]
|
|
30
33
|
# @param severity [Symbol]
|
|
31
|
-
def initialize(linter, filename, line, message, severity = :warning, corrected: false) # rubocop:disable Metrics/ParameterLists
|
|
34
|
+
def initialize(linter, filename, line, message, severity = :warning, corrected: false, correctable: false) # rubocop:disable Metrics/ParameterLists
|
|
32
35
|
@linter = linter
|
|
33
36
|
@filename = filename
|
|
34
37
|
@line = line || 0
|
|
35
38
|
@message = message
|
|
36
39
|
@severity = Severity.new(severity)
|
|
37
40
|
@corrected = corrected
|
|
41
|
+
@correctable = correctable
|
|
38
42
|
end
|
|
39
43
|
|
|
40
44
|
# Return whether this lint has a severity of error.
|
|
@@ -45,7 +49,8 @@ module HamlLint
|
|
|
45
49
|
end
|
|
46
50
|
|
|
47
51
|
def inspect
|
|
48
|
-
"#{self.class.name}(corrected=#{corrected},
|
|
52
|
+
"#{self.class.name}(corrected=#{corrected}, correctable=#{correctable}, " \
|
|
53
|
+
"filename=#{filename}, line=#{line}, " \
|
|
49
54
|
"linter=#{linter.class.name}, message=#{message}, severity=#{severity})"
|
|
50
55
|
end
|
|
51
56
|
end
|
|
@@ -3,12 +3,29 @@
|
|
|
3
3
|
module HamlLint
|
|
4
4
|
# Checks for tabs that are placed for alignment of tag content
|
|
5
5
|
class Linter::AlignmentTabs < Linter
|
|
6
|
-
|
|
6
|
+
include LinterRegistry
|
|
7
|
+
|
|
8
|
+
supports_autocorrect(true)
|
|
9
|
+
autocorrect_safe(false)
|
|
10
|
+
|
|
11
|
+
REGEX = /[^\s*]\t+/
|
|
12
|
+
# Same as +REGEX+, but captures the character preceding the alignment tabs
|
|
13
|
+
# so it can be kept while the tabs themselves are collapsed to a space.
|
|
14
|
+
CORRECTION_REGEX = /([^\s*])\t+/
|
|
7
15
|
|
|
8
16
|
def visit_tag(node)
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
17
|
+
return unless REGEX.match?(node.source_code)
|
|
18
|
+
|
|
19
|
+
record_lint(node, 'Avoid using tabs for alignment', corrected: correct(node))
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
# @return [Boolean] whether a correction was applied
|
|
25
|
+
def correct(node)
|
|
26
|
+
index = node.line - 1
|
|
27
|
+
line = autocorrected_lines[index]
|
|
28
|
+
correct_line(index, line.gsub(CORRECTION_REGEX) { "#{::Regexp.last_match(1)} " })
|
|
12
29
|
end
|
|
13
30
|
end
|
|
14
31
|
end
|
|
@@ -18,15 +18,19 @@ module HamlLint
|
|
|
18
18
|
class Linter::ClassAttributeWithStaticValue < Linter
|
|
19
19
|
include LinterRegistry
|
|
20
20
|
|
|
21
|
+
supports_autocorrect(true)
|
|
22
|
+
|
|
21
23
|
STATIC_TYPES = %i[str sym].freeze
|
|
22
24
|
|
|
23
|
-
VALID_CLASS_REGEX = /^-?[_a-zA-Z]+[_a-zA-Z0-9-]
|
|
25
|
+
VALID_CLASS_REGEX = /^-?[_a-zA-Z]+[_a-zA-Z0-9-]*$/
|
|
24
26
|
|
|
25
27
|
def visit_tag(node)
|
|
26
|
-
|
|
28
|
+
class_value = static_class_value(node.dynamic_attributes_sources)
|
|
29
|
+
return unless class_value
|
|
27
30
|
|
|
31
|
+
corrected = correct_class_attribute(node, class_value)
|
|
28
32
|
record_lint(node, 'Avoid defining `class` in attributes hash ' \
|
|
29
|
-
'for static class names')
|
|
33
|
+
'for static class names', corrected: corrected)
|
|
30
34
|
end
|
|
31
35
|
|
|
32
36
|
private
|
|
@@ -35,28 +39,64 @@ module HamlLint
|
|
|
35
39
|
code.start_with?('{') && code.end_with?('}')
|
|
36
40
|
end
|
|
37
41
|
|
|
38
|
-
|
|
42
|
+
# @return [String, nil]
|
|
43
|
+
def static_class_value(attributes_sources)
|
|
39
44
|
attributes_sources.each do |code|
|
|
40
45
|
ast_root = parse_ruby(surrounded_by_braces?(code) ? code : "{#{code}}")
|
|
41
46
|
next unless ast_root # RuboCop linter will report syntax errors
|
|
42
47
|
|
|
43
48
|
ast_root.children.each do |pair|
|
|
44
|
-
|
|
49
|
+
value = static_class_attribute_value(pair)
|
|
50
|
+
return value if value
|
|
45
51
|
end
|
|
46
52
|
end
|
|
47
53
|
|
|
48
|
-
|
|
54
|
+
nil
|
|
49
55
|
end
|
|
50
56
|
|
|
51
|
-
|
|
52
|
-
|
|
57
|
+
# @return [String, nil]
|
|
58
|
+
def static_class_attribute_value(pair)
|
|
59
|
+
return nil if (children = pair.children).empty?
|
|
53
60
|
|
|
54
61
|
key, value = children
|
|
55
62
|
|
|
56
|
-
STATIC_TYPES.include?(key.type) &&
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
63
|
+
return nil unless STATIC_TYPES.include?(key.type) &&
|
|
64
|
+
key.children.first.to_sym == :class &&
|
|
65
|
+
STATIC_TYPES.include?(value.type)
|
|
66
|
+
|
|
67
|
+
class_name = value.children.first.to_s
|
|
68
|
+
VALID_CLASS_REGEX.match?(class_name) ? class_name : nil
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# @return [Boolean]
|
|
72
|
+
def correct_class_attribute(node, class_value)
|
|
73
|
+
hash_source = node.hash_attributes_source
|
|
74
|
+
return false unless hash_source
|
|
75
|
+
return false if hash_source.include?("\n")
|
|
76
|
+
return false unless node.dynamic_attributes_source.keys == [:hash]
|
|
77
|
+
return false unless sole_pair?(node.dynamic_attributes_sources)
|
|
78
|
+
return false if node.static_classes.flatten.include?(class_value)
|
|
79
|
+
|
|
80
|
+
index = node.line - 1
|
|
81
|
+
line = autocorrected_lines[index]
|
|
82
|
+
old = "#{node.static_attributes_source}#{hash_source}"
|
|
83
|
+
return false unless line.include?(old)
|
|
84
|
+
|
|
85
|
+
new = "#{node.static_attributes_source}.#{class_value}"
|
|
86
|
+
correct_line(index, line.sub(old, new))
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Whether the attribute hash contains exactly one key/value pair (the class).
|
|
90
|
+
#
|
|
91
|
+
# @return [Boolean]
|
|
92
|
+
def sole_pair?(attributes_sources)
|
|
93
|
+
return false unless attributes_sources.size == 1
|
|
94
|
+
|
|
95
|
+
code = attributes_sources.first
|
|
96
|
+
ast_root = parse_ruby(surrounded_by_braces?(code) ? code : "{#{code}}")
|
|
97
|
+
return false unless ast_root
|
|
98
|
+
|
|
99
|
+
ast_root.children.size == 1
|
|
60
100
|
end
|
|
61
101
|
end
|
|
62
102
|
end
|
|
@@ -5,11 +5,7 @@ module HamlLint
|
|
|
5
5
|
class Linter::ClassesBeforeIds < Linter
|
|
6
6
|
include LinterRegistry
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
TYPES_BY_PREFIX = {
|
|
10
|
-
'.' => :class,
|
|
11
|
-
'#' => :id,
|
|
12
|
-
}.freeze
|
|
8
|
+
supports_autocorrect(true)
|
|
13
9
|
|
|
14
10
|
MSG = '%s should be listed before %s (%s should precede %s)'
|
|
15
11
|
|
|
@@ -23,14 +19,27 @@ module HamlLint
|
|
|
23
19
|
next unless next_val.start_with?(first) &&
|
|
24
20
|
current_val.start_with?(second)
|
|
25
21
|
|
|
22
|
+
corrected = correct_attribute_order(node, components)
|
|
26
23
|
failure_message = format(MSG, *(attribute_type_order + [next_val, current_val]))
|
|
27
|
-
record_lint(node, failure_message)
|
|
24
|
+
record_lint(node, failure_message, corrected: corrected)
|
|
28
25
|
break
|
|
29
26
|
end
|
|
30
27
|
end
|
|
31
28
|
|
|
32
29
|
private
|
|
33
30
|
|
|
31
|
+
# @param node [HamlLint::Tree::TagNode]
|
|
32
|
+
# @param components [Array<String>] the `.class`/`#id` components in source order
|
|
33
|
+
# @return [Boolean]
|
|
34
|
+
def correct_attribute_order(node, components)
|
|
35
|
+
classes, ids = components.partition { |component| component.start_with?('.') }
|
|
36
|
+
new_source = (ids_first? ? ids + classes : classes + ids).join
|
|
37
|
+
|
|
38
|
+
index = node.line - 1
|
|
39
|
+
line = autocorrected_lines[index]
|
|
40
|
+
correct_line(index, line.sub(node.static_attributes_source, new_source))
|
|
41
|
+
end
|
|
42
|
+
|
|
34
43
|
def attribute_prefix_order
|
|
35
44
|
default = %w[. #]
|
|
36
45
|
default.reverse! if ids_first?
|
|
@@ -5,6 +5,9 @@ module HamlLint
|
|
|
5
5
|
class Linter::ConsecutiveComments < Linter
|
|
6
6
|
include LinterRegistry
|
|
7
7
|
|
|
8
|
+
supports_autocorrect(true)
|
|
9
|
+
autocorrect_safe(false)
|
|
10
|
+
|
|
8
11
|
COMMENT_DETECTOR = ->(child) { child.type == :haml_comment }
|
|
9
12
|
|
|
10
13
|
def visit_haml_comment(node)
|
|
@@ -16,13 +19,29 @@ module HamlLint
|
|
|
16
19
|
config['max_consecutive'] + 1,
|
|
17
20
|
) do |group|
|
|
18
21
|
group.each { |group_node| reported_nodes << group_node }
|
|
22
|
+
corrected = correct_group(group)
|
|
19
23
|
record_lint(group.first,
|
|
20
|
-
"#{group.count} consecutive comments can be merged into one"
|
|
24
|
+
"#{group.count} consecutive comments can be merged into one",
|
|
25
|
+
corrected: corrected)
|
|
21
26
|
end
|
|
22
27
|
end
|
|
23
28
|
|
|
24
29
|
private
|
|
25
30
|
|
|
31
|
+
# @return [Boolean]
|
|
32
|
+
def correct_group(group)
|
|
33
|
+
first_index = group.first.line - 1
|
|
34
|
+
continuation_indent = "#{autocorrected_lines[first_index][/\A\s*/]} "
|
|
35
|
+
|
|
36
|
+
changed = false
|
|
37
|
+
group[1..].each do |group_node|
|
|
38
|
+
index = group_node.line - 1
|
|
39
|
+
line = autocorrected_lines[index]
|
|
40
|
+
changed = true if correct_line(index, continuation_indent + line.sub(/\A\s*-#\s?/, ''))
|
|
41
|
+
end
|
|
42
|
+
changed
|
|
43
|
+
end
|
|
44
|
+
|
|
26
45
|
def possible_group(node)
|
|
27
46
|
node.subsequents.unshift(node)
|
|
28
47
|
end
|