haml_lint 0.40.0 → 0.51.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/haml-lint +1 -1
- data/config/default.yml +9 -27
- data/config/forced_rubocop_config.yml +180 -0
- data/lib/haml_lint/adapter/haml_4.rb +20 -0
- data/lib/haml_lint/adapter/haml_5.rb +11 -0
- data/lib/haml_lint/adapter/haml_6.rb +59 -0
- data/lib/haml_lint/adapter.rb +2 -0
- data/lib/haml_lint/cli.rb +8 -3
- data/lib/haml_lint/configuration_loader.rb +49 -13
- data/lib/haml_lint/document.rb +89 -8
- data/lib/haml_lint/exceptions.rb +6 -0
- data/lib/haml_lint/extensions/haml_util_unescape_interpolation_tracking.rb +35 -0
- data/lib/haml_lint/file_finder.rb +2 -2
- data/lib/haml_lint/lint.rb +10 -1
- data/lib/haml_lint/linter/final_newline.rb +4 -3
- data/lib/haml_lint/linter/implicit_div.rb +1 -1
- data/lib/haml_lint/linter/indentation.rb +3 -3
- data/lib/haml_lint/linter/no_placeholders.rb +18 -0
- data/lib/haml_lint/linter/repeated_id.rb +2 -1
- data/lib/haml_lint/linter/rubocop.rb +353 -59
- data/lib/haml_lint/linter/space_before_script.rb +8 -10
- data/lib/haml_lint/linter/trailing_empty_lines.rb +22 -0
- data/lib/haml_lint/linter/unnecessary_string_output.rb +1 -1
- data/lib/haml_lint/linter/view_length.rb +1 -1
- data/lib/haml_lint/linter.rb +60 -10
- data/lib/haml_lint/linter_registry.rb +3 -5
- data/lib/haml_lint/logger.rb +2 -2
- data/lib/haml_lint/options.rb +26 -2
- data/lib/haml_lint/rake_task.rb +2 -2
- data/lib/haml_lint/reporter/hash_reporter.rb +1 -3
- data/lib/haml_lint/reporter/offense_count_reporter.rb +1 -1
- data/lib/haml_lint/reporter/utils.rb +33 -4
- data/lib/haml_lint/ruby_extraction/ad_hoc_chunk.rb +24 -0
- data/lib/haml_lint/ruby_extraction/base_chunk.rb +114 -0
- data/lib/haml_lint/ruby_extraction/chunk_extractor.rb +672 -0
- data/lib/haml_lint/ruby_extraction/coordinator.rb +181 -0
- data/lib/haml_lint/ruby_extraction/haml_comment_chunk.rb +34 -0
- data/lib/haml_lint/ruby_extraction/implicit_end_chunk.rb +17 -0
- data/lib/haml_lint/ruby_extraction/interpolation_chunk.rb +26 -0
- data/lib/haml_lint/ruby_extraction/non_ruby_filter_chunk.rb +32 -0
- data/lib/haml_lint/ruby_extraction/placeholder_marker_chunk.rb +40 -0
- data/lib/haml_lint/ruby_extraction/ruby_filter_chunk.rb +33 -0
- data/lib/haml_lint/ruby_extraction/ruby_source.rb +5 -0
- data/lib/haml_lint/ruby_extraction/script_chunk.rb +251 -0
- data/lib/haml_lint/ruby_extraction/tag_attributes_chunk.rb +63 -0
- data/lib/haml_lint/ruby_extraction/tag_script_chunk.rb +30 -0
- data/lib/haml_lint/ruby_parser.rb +11 -1
- data/lib/haml_lint/runner.rb +35 -3
- data/lib/haml_lint/spec/matchers/report_lint.rb +22 -7
- data/lib/haml_lint/spec/normalize_indent.rb +2 -2
- data/lib/haml_lint/spec/shared_linter_context.rb +9 -1
- data/lib/haml_lint/spec/shared_rubocop_autocorrect_context.rb +158 -0
- data/lib/haml_lint/spec.rb +1 -0
- data/lib/haml_lint/tree/filter_node.rb +10 -0
- data/lib/haml_lint/tree/node.rb +13 -4
- data/lib/haml_lint/tree/script_node.rb +7 -1
- data/lib/haml_lint/tree/silent_script_node.rb +16 -1
- data/lib/haml_lint/tree/tag_node.rb +5 -9
- data/lib/haml_lint/utils.rb +135 -5
- data/lib/haml_lint/version.rb +1 -1
- data/lib/haml_lint/version_comparer.rb +25 -0
- data/lib/haml_lint.rb +12 -0
- metadata +29 -15
- data/lib/haml_lint/ruby_extractor.rb +0 -222
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 71dc6acf56f28f4f6878ff300a653d1fb6c5cc6fa9da23c71813463ea9e1b67a
|
4
|
+
data.tar.gz: af2fa134ec9718c24d019e20c359c9a965d6d09490e6f57fe202654f5f7f95a3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 73c8a4c7fbd68cfbeacd29e5d313dcf9ae120a624a0359fecf5ca25396a8e68a0cfbad68b0ac9854af82a9233ffb26a9adf7db156f8dd3b5eb8b7a83773db035
|
7
|
+
data.tar.gz: fcb59570ff3b999f81f12208993ab75118013b0262586478145d7d0eaa9bab78002d9808757e10025324082f895bb0962743133898dd31dbb6c3db73d4c7f68c
|
data/bin/haml-lint
CHANGED
data/config/default.yml
CHANGED
@@ -76,6 +76,9 @@ linters:
|
|
76
76
|
MultilineScript:
|
77
77
|
enabled: true
|
78
78
|
|
79
|
+
NoPlaceholders:
|
80
|
+
enabled: false
|
81
|
+
|
79
82
|
ObjectReferenceAttributes:
|
80
83
|
enabled: true
|
81
84
|
|
@@ -85,33 +88,9 @@ linters:
|
|
85
88
|
|
86
89
|
RuboCop:
|
87
90
|
enabled: true
|
88
|
-
#
|
89
|
-
#
|
90
|
-
ignored_cops:
|
91
|
-
- Lint/BlockAlignment
|
92
|
-
- Lint/EndAlignment
|
93
|
-
- Lint/Void
|
94
|
-
- Layout/AlignHash # renamed to Layout/HashAlignment in rubocop 0.77
|
95
|
-
- Layout/AlignParameters # renamed to Layout/ParameterAlignment in rubocop 0.77
|
96
|
-
- Layout/ArgumentAlignment
|
97
|
-
- Layout/CaseIndentation
|
98
|
-
- Layout/ElseAlignment
|
99
|
-
- Layout/EndOfLine
|
100
|
-
- Layout/HashAlignment
|
101
|
-
- Layout/IndentationWidth
|
102
|
-
- Layout/LineLength # renamed from Metrics/LineLength in rubocop 0.79.0
|
103
|
-
- Layout/ParameterAlignment
|
104
|
-
- Layout/TrailingBlankLines # renamed to Layout/TrailingEmptyLines in rubocop 0.77
|
105
|
-
- Layout/TrailingEmptyLines
|
106
|
-
- Layout/TrailingWhitespace
|
107
|
-
- Metrics/BlockLength
|
108
|
-
- Metrics/BlockNesting
|
109
|
-
- Metrics/LineLength
|
110
|
-
- Naming/FileName
|
111
|
-
- Style/FrozenStringLiteralComment
|
112
|
-
- Style/IfUnlessModifier
|
113
|
-
- Style/Next
|
114
|
-
- Style/WhileUntilModifier
|
91
|
+
# Users can ignore cops using this configuration instead of editing their rubocop configuration.
|
92
|
+
# Mostly there for backward compatibility.
|
93
|
+
ignored_cops: []
|
115
94
|
|
116
95
|
RubyComments:
|
117
96
|
enabled: true
|
@@ -126,6 +105,9 @@ linters:
|
|
126
105
|
TagName:
|
127
106
|
enabled: true
|
128
107
|
|
108
|
+
TrailingEmptyLines:
|
109
|
+
enabled: true
|
110
|
+
|
129
111
|
TrailingWhitespace:
|
130
112
|
enabled: true
|
131
113
|
|
@@ -0,0 +1,180 @@
|
|
1
|
+
# These are some configurations that are required for RuboCop because:
|
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
|
+
#
|
6
|
+
# So these configuration should not be overwritable by users.
|
7
|
+
|
8
|
+
Layout/ArgumentAlignment:
|
9
|
+
# The alternative, with_fixed_indentation, breaks because we sometimes remove indentation when
|
10
|
+
# dealing with multi-line scripts. (Because a line starting with "=" adds a "HL.out = " to the
|
11
|
+
# intermediary Ruby source, which requires indentation, and the removal of the indentation)
|
12
|
+
EnforcedStyle: with_first_argument
|
13
|
+
|
14
|
+
Layout/ArrayAlignment:
|
15
|
+
# The alternative, with_fixed_indentation, breaks because we sometimes remove indentation when
|
16
|
+
# dealing with multi-line scripts. (Because a line starting with "=" adds a "HL.out = " to the
|
17
|
+
# intermediary Ruby source, which requires indentation, and the removal of the indentation)
|
18
|
+
EnforcedStyle: with_first_element
|
19
|
+
|
20
|
+
# In Haml, there are edge cases when `when` is indented, such as this one:
|
21
|
+
# - case 1
|
22
|
+
# - when 1
|
23
|
+
# - when 2
|
24
|
+
# foo
|
25
|
+
# This generates 2 `end` when building Ruby.
|
26
|
+
# So it's safer to make the `when` be not indented, to avoid auto-correct chains that could turn
|
27
|
+
# a valid `if` into an invalid `case` such as above.
|
28
|
+
Layout/CaseIndentation:
|
29
|
+
Enabled: true
|
30
|
+
EnforcedStyle: end
|
31
|
+
IndentOneStep: false # Need to force the `false`
|
32
|
+
|
33
|
+
# Need this cop so that code gets formatted similarly to Haml's indentation,
|
34
|
+
# since HAML-Lint relies on Ruby's indentation being the same as Haml's.
|
35
|
+
Layout/ElseAlignment:
|
36
|
+
Enabled: true
|
37
|
+
|
38
|
+
# Need this cop so that code gets formatted similarly to Haml's indentation,
|
39
|
+
# since HAML-Lint relies on Ruby's indentation being the same as Haml's.
|
40
|
+
Layout/EndAlignment:
|
41
|
+
EnforcedStyleAlignWith: start_of_line
|
42
|
+
Enabled: true
|
43
|
+
|
44
|
+
# We generate the ruby content, this is basically useless and should be a lint in HAML-Lint
|
45
|
+
Layout/EndOfLine:
|
46
|
+
Enabled: false
|
47
|
+
|
48
|
+
# Turning this cop on can turn
|
49
|
+
# = content_tag(:span) do
|
50
|
+
# - foo
|
51
|
+
# - bar
|
52
|
+
#
|
53
|
+
# Into
|
54
|
+
# - HL.out =
|
55
|
+
# - content_tag(:span) do
|
56
|
+
# - foo
|
57
|
+
# - bar
|
58
|
+
#
|
59
|
+
# Which is wrong... It would take too much analysis to detect and fix that situation.
|
60
|
+
Layout/MultilineAssignmentLayout:
|
61
|
+
Enabled: false
|
62
|
+
|
63
|
+
Layout/ParameterAlignment:
|
64
|
+
# The alternative, with_fixed_indentation, breaks because we sometimes remove indentation when
|
65
|
+
# dealing with multi-line scripts. (Because a line starting with "=" adds a "HL.out = " to the
|
66
|
+
# intermediary Ruby source, which requires indentation, and the removal of the indentation)
|
67
|
+
EnforcedStyle: with_first_parameter
|
68
|
+
|
69
|
+
# HamlLint generate lots of extra code which would make blocks much longer
|
70
|
+
Metrics/BlockLength:
|
71
|
+
Enabled: false
|
72
|
+
|
73
|
+
# The nesting may be due to the html's nesting nature... These lints are probably not helpful
|
74
|
+
Metrics/BlockNesting:
|
75
|
+
Enabled: false
|
76
|
+
|
77
|
+
# The file names are generated by HamlLint, so any related lint would be unfixable by the user
|
78
|
+
Naming/FileName:
|
79
|
+
Enabled: false
|
80
|
+
|
81
|
+
# HAML doesn't properly support multiline blocks using { }, only using do/end.
|
82
|
+
# If you don't consider the { } block for indentation, things "works", but the indentation is misleading.
|
83
|
+
# For example, this works:
|
84
|
+
# - a = lambda {
|
85
|
+
# - if abc
|
86
|
+
# - something
|
87
|
+
# - }
|
88
|
+
# But if you indented the 2 lines within { }, then HAML would add an extra `end` and the generated
|
89
|
+
# ruby would be invalid.
|
90
|
+
Style/BlockDelimiters:
|
91
|
+
# So we need this cop to cleanup those cases and turn them to `end`.
|
92
|
+
Enabled: true
|
93
|
+
EnforcedStyle: line_count_based
|
94
|
+
# We don't allow the default "Can be anything" exception for lambda/proc
|
95
|
+
<%= rubocop_version < '1.33' ? 'IgnoredMethods' : 'AllowedMethods' %>: []
|
96
|
+
|
97
|
+
# We don't support correcting HAML comments
|
98
|
+
Style/CommentAnnotation:
|
99
|
+
AutoCorrect: false
|
100
|
+
|
101
|
+
# If this was enabled, the equal sign would bubble up in a if like this:
|
102
|
+
# - if a
|
103
|
+
# = abc
|
104
|
+
# - else
|
105
|
+
# = bcd
|
106
|
+
# Into:
|
107
|
+
# = if a
|
108
|
+
# - abc
|
109
|
+
# - else
|
110
|
+
# - bcd
|
111
|
+
# Feels like this might be annoying or less visibly intuitive.
|
112
|
+
Style/ConditionalAssignment:
|
113
|
+
Enabled: false
|
114
|
+
|
115
|
+
# If this gets added, it wont do anything anyways.
|
116
|
+
Style/FrozenStringLiteralComment:
|
117
|
+
Enabled: false
|
118
|
+
|
119
|
+
# Looking at the changelog, this cop has quite a few bugfixes over time.
|
120
|
+
# It still has problematic behaviors for us, such as breaking a line into multiple
|
121
|
+
# ones with high indentation, which doesn't work for haml
|
122
|
+
Style/IfUnlessModifier:
|
123
|
+
AutoCorrect: false
|
124
|
+
|
125
|
+
<% if rubocop_version >= '1.37.0' %>
|
126
|
+
# This new cop can trigger on the here-doc we use for filters that contain interpolation.
|
127
|
+
# Ex:
|
128
|
+
# :javascript
|
129
|
+
# hello #{world} \. bad escape
|
130
|
+
Style/RedundantStringEscape:
|
131
|
+
Enabled: false
|
132
|
+
<% end %>
|
133
|
+
|
134
|
+
# In some case, this cop can cause a change in the spacing.
|
135
|
+
# In HAML 5.2, going from (absurd example for clarity):
|
136
|
+
# = 'abc' rescue nil
|
137
|
+
# = 'def'
|
138
|
+
# to:
|
139
|
+
# = begin
|
140
|
+
# - 'abc'
|
141
|
+
# - rescue StandardError
|
142
|
+
# - nil
|
143
|
+
# = 'def'
|
144
|
+
# Will remove the only whitespace (a \n) that is between the abc and the def.
|
145
|
+
# This could affect spacing, ex: seeing "abc def" become "abcdef" when rendering
|
146
|
+
# after running this auto-correct
|
147
|
+
Style/RescueModifier:
|
148
|
+
AutoCorrect: false
|
149
|
+
|
150
|
+
# Cops that remove commas can be a problem when lines are split on multiple ones.
|
151
|
+
# If we have a big array on more than one line, the removal of the comma generates
|
152
|
+
# invalid HAML
|
153
|
+
Style/SymbolArray:
|
154
|
+
Enabled: false
|
155
|
+
|
156
|
+
# This can easily change the order of the markers in the document, which result in un-transferable corrections
|
157
|
+
Style/UnlessElse:
|
158
|
+
AutoCorrect: false
|
159
|
+
|
160
|
+
# 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 HAML will be invalid, since the only
|
162
|
+
# case where a script can change line is after a comma.
|
163
|
+
Style/WordArray:
|
164
|
+
AutoCorrect: false
|
165
|
+
|
166
|
+
# Not RuboCop's job
|
167
|
+
Layout/TrailingEmptyLines:
|
168
|
+
Enabled: false
|
169
|
+
|
170
|
+
<% if rubocop_version < '1.8.1' %>
|
171
|
+
# 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 HAML-lint is
|
173
|
+
# responsible, at least from the user's point of view.
|
174
|
+
Style/StringConcatenation:
|
175
|
+
Enabled: false
|
176
|
+
<% end %>
|
177
|
+
|
178
|
+
# There is already a linter dedicated to this in haml-lint
|
179
|
+
Layout/LineLength:
|
180
|
+
Enabled: false
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'forwardable'
|
4
|
+
|
3
5
|
module HamlLint
|
4
6
|
class Adapter
|
5
7
|
# Adapts the Haml::Parser from Haml 4 for use in HamlLint
|
@@ -16,9 +18,21 @@ module HamlLint
|
|
16
18
|
# @param source [String] Haml code to parse
|
17
19
|
# @param options [Haml::Options]
|
18
20
|
def initialize(source, options = Haml::Options.new)
|
21
|
+
@source = source
|
19
22
|
@parser = Haml::Parser.new(source, options)
|
20
23
|
end
|
21
24
|
|
25
|
+
def precompile
|
26
|
+
# Haml uses the filters as part of precompilation... we don't care about those,
|
27
|
+
# but without this tweak, it would fail on filters that are not loaded.
|
28
|
+
real_defined = Haml::Filters.defined
|
29
|
+
Haml::Filters.instance_variable_set(:@defined, Hash.new { real_defined['plain'] })
|
30
|
+
|
31
|
+
::Haml::Engine.new(source).precompiled
|
32
|
+
ensure
|
33
|
+
Haml::Filters.instance_variable_set(:@defined, real_defined)
|
34
|
+
end
|
35
|
+
|
22
36
|
# @!method
|
23
37
|
# Parses the source code into an abstract syntax tree
|
24
38
|
#
|
@@ -37,6 +51,12 @@ module HamlLint
|
|
37
51
|
# @api private
|
38
52
|
# @return [Haml::Parser] the Haml 4 parser
|
39
53
|
attr_reader :parser
|
54
|
+
|
55
|
+
# The Haml code to parse
|
56
|
+
#
|
57
|
+
# @api private
|
58
|
+
# @return [String] Haml code to parse
|
59
|
+
attr_reader :source
|
40
60
|
end
|
41
61
|
end
|
42
62
|
end
|
@@ -30,6 +30,17 @@ module HamlLint
|
|
30
30
|
parser.call(source)
|
31
31
|
end
|
32
32
|
|
33
|
+
def precompile
|
34
|
+
# Haml uses the filters as part of precompilation... we don't care about those,
|
35
|
+
# but without this tweak, it would fail on filters that are not loaded.
|
36
|
+
real_defined = Haml::Filters.defined
|
37
|
+
Haml::Filters.instance_variable_set(:@defined, Hash.new { real_defined['plain'] })
|
38
|
+
|
39
|
+
::Haml::Engine.new(source).precompiled
|
40
|
+
ensure
|
41
|
+
Haml::Filters.instance_variable_set(:@defined, real_defined)
|
42
|
+
end
|
43
|
+
|
33
44
|
private
|
34
45
|
|
35
46
|
# The Haml parser to adapt for HamlLint
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module HamlLint
|
4
|
+
class Adapter
|
5
|
+
# Adapts the Haml::Parser from Haml 5 for use in HamlLint
|
6
|
+
# :reek:UncommunicativeModuleName
|
7
|
+
class Haml6 < Adapter
|
8
|
+
# Parses the specified Haml code into an abstract syntax tree
|
9
|
+
#
|
10
|
+
# @example
|
11
|
+
# HamlLint::Adapter::Haml6.new('%div')
|
12
|
+
#
|
13
|
+
# @api public
|
14
|
+
# @param source [String] Haml code to parse
|
15
|
+
# @param options [private Haml::Parser::ParserOptions]
|
16
|
+
def initialize(source, options = {})
|
17
|
+
@source = source
|
18
|
+
@parser = Haml::Parser.new(options)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Parses the source code into an abstract syntax tree
|
22
|
+
#
|
23
|
+
# @example
|
24
|
+
# HamlLint::Adapter::Haml6.new('%div').parse
|
25
|
+
#
|
26
|
+
# @api public
|
27
|
+
# @return [Haml::Parser::ParseNode]
|
28
|
+
# @raise [Haml::Error]
|
29
|
+
def parse
|
30
|
+
parser.call(source)
|
31
|
+
end
|
32
|
+
|
33
|
+
def precompile
|
34
|
+
# Haml uses the filters as part of precompilation... we don't care about those,
|
35
|
+
# but without this tweak, it would fail on filters that are not loaded.
|
36
|
+
real_defined = Haml::Filters.registered
|
37
|
+
Haml::Filters.instance_variable_set(:@registered, Hash.new { real_defined['plain'] })
|
38
|
+
|
39
|
+
::Haml::Engine.new.call(source)
|
40
|
+
ensure
|
41
|
+
Haml::Filters.instance_variable_set(:@registered, real_defined)
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
# The Haml parser to adapt for HamlLint
|
47
|
+
#
|
48
|
+
# @api private
|
49
|
+
# @return [Haml::Parser] the Haml 4 parser
|
50
|
+
attr_reader :parser
|
51
|
+
|
52
|
+
# The Haml code to parse
|
53
|
+
#
|
54
|
+
# @api private
|
55
|
+
# @return [String] Haml code to parse
|
56
|
+
attr_reader :source
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
data/lib/haml_lint/adapter.rb
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require 'haml_lint/adapter/haml_4'
|
4
4
|
require 'haml_lint/adapter/haml_5'
|
5
|
+
require 'haml_lint/adapter/haml_6'
|
5
6
|
require 'haml_lint/exceptions'
|
6
7
|
|
7
8
|
module HamlLint
|
@@ -20,6 +21,7 @@ module HamlLint
|
|
20
21
|
case version
|
21
22
|
when '~> 4.0' then HamlLint::Adapter::Haml4
|
22
23
|
when '~> 5.0', '~> 5.1', '~> 5.2' then HamlLint::Adapter::Haml5
|
24
|
+
when '~> 6.0', '~> 6.0.a', '~> 6.1', '~> 6.2' then HamlLint::Adapter::Haml6
|
23
25
|
else fail HamlLint::Exceptions::UnknownHamlVersion, "Cannot handle Haml version: #{version}"
|
24
26
|
end
|
25
27
|
end
|
data/lib/haml_lint/cli.rb
CHANGED
@@ -7,7 +7,7 @@ require 'sysexits'
|
|
7
7
|
|
8
8
|
module HamlLint
|
9
9
|
# Command line application interface.
|
10
|
-
class CLI
|
10
|
+
class CLI
|
11
11
|
# Create a CLI that outputs to the specified logger.
|
12
12
|
#
|
13
13
|
# @param logger [HamlLint::Logger]
|
@@ -34,9 +34,14 @@ module HamlLint
|
|
34
34
|
# Given the provided options, execute the appropriate command.
|
35
35
|
#
|
36
36
|
# @return [Integer] exit status code
|
37
|
-
def act_on_options(options)
|
37
|
+
def act_on_options(options) # rubocop:disable Metrics
|
38
38
|
configure_logger(options)
|
39
|
-
|
39
|
+
if options[:debug]
|
40
|
+
ENV['HAML_LINT_DEBUG'] = 'true'
|
41
|
+
end
|
42
|
+
if options[:internal_debug]
|
43
|
+
ENV['HAML_LINT_INTERNAL_DEBUG'] = 'true'
|
44
|
+
end
|
40
45
|
if options[:help]
|
41
46
|
print_help(options)
|
42
47
|
Sysexits::EX_OK
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require 'pathname'
|
4
4
|
require 'yaml'
|
5
|
+
require 'erb'
|
5
6
|
|
6
7
|
module HamlLint
|
7
8
|
# Manages configuration file loading.
|
@@ -31,7 +32,7 @@ module HamlLint
|
|
31
32
|
def default_path_to_config
|
32
33
|
directory = File.expand_path(Dir.pwd)
|
33
34
|
config_file = possible_config_files(directory).find(&:file?)
|
34
|
-
config_file
|
35
|
+
config_file&.to_path
|
35
36
|
end
|
36
37
|
|
37
38
|
# Loads the built-in default configuration.
|
@@ -48,13 +49,20 @@ module HamlLint
|
|
48
49
|
# @option context :exclude_files [Array<String>] files that should not
|
49
50
|
# be loaded even if they're requested via inherits_from
|
50
51
|
# @return [HamlLint::Configuration]
|
51
|
-
def load_file(file, context = {})
|
52
|
+
def load_file(file, context = {}) # rubocop:disable Metrics
|
52
53
|
context[:loaded_files] ||= []
|
54
|
+
context[:loaded_files].map! { |config_file| File.expand_path(config_file) }
|
53
55
|
context[:exclude_files] ||= []
|
54
|
-
|
56
|
+
context[:exclude_files].map! { |config_file| File.expand_path(config_file) }
|
57
|
+
config = load_from_file(File.expand_path(file))
|
55
58
|
|
56
|
-
|
57
|
-
|
59
|
+
configs = if context[:loaded_files].any?
|
60
|
+
[resolve_inheritance(config, context), config]
|
61
|
+
else
|
62
|
+
[default_configuration, resolve_inheritance(config, context), config]
|
63
|
+
end
|
64
|
+
|
65
|
+
configs.reduce { |acc, elem| acc.merge(elem) }
|
58
66
|
rescue Psych::SyntaxError, Errno::ENOENT => e
|
59
67
|
raise HamlLint::Exceptions::ConfigurationError,
|
60
68
|
"Unable to load configuration from '#{file}': #{e}",
|
@@ -78,19 +86,29 @@ module HamlLint
|
|
78
86
|
#
|
79
87
|
# @param file [String]
|
80
88
|
# @return [HamlLint::Configuration]
|
81
|
-
def load_from_file(file)
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
{}
|
87
|
-
end
|
89
|
+
def load_from_file(file) # rubocop:disable Metrics
|
90
|
+
content = File.read(file)
|
91
|
+
|
92
|
+
processed_content = HamlLint::Utils.process_erb(content)
|
93
|
+
hash = (YAML.safe_load(processed_content) || {}).to_hash
|
88
94
|
|
89
95
|
if hash.key?('inherit_from')
|
90
96
|
hash['inherits_from'] ||= []
|
91
97
|
hash['inherits_from'].concat(Array(hash.delete('inherit_from')))
|
92
98
|
end
|
93
99
|
|
100
|
+
if hash.key?('inherit_gem')
|
101
|
+
hash['inherits_from'] ||= []
|
102
|
+
|
103
|
+
gems = hash.delete('inherit_gem')
|
104
|
+
(gems || {}).each_pair.reverse_each do |gem_name, config_path|
|
105
|
+
Array(config_path).reverse_each do |path|
|
106
|
+
# Put gem configuration first so local configuration overrides it.
|
107
|
+
hash['inherits_from'].unshift gem_config_path(gem_name, path)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
94
112
|
HamlLint::Configuration.new(hash, file)
|
95
113
|
end
|
96
114
|
|
@@ -129,10 +147,28 @@ module HamlLint
|
|
129
147
|
# @return [HamlLint::Configuration]
|
130
148
|
def resolve_inheritance(config, context)
|
131
149
|
Array(config['inherits_from'])
|
132
|
-
.map { |config_file| resolve(config_file, context) }
|
150
|
+
.map { |config_file| resolve(File.expand_path(config_file), context) }
|
133
151
|
.compact
|
134
152
|
.reduce { |acc, elem| acc.merge(elem) } || config
|
135
153
|
end
|
154
|
+
|
155
|
+
# Resolves the config file path relative to a gem
|
156
|
+
#
|
157
|
+
# @param gem_name [String] name of the gem
|
158
|
+
# @param relative_config_path [String] path of the file to resolve, relative to the gem root
|
159
|
+
# @return [String]
|
160
|
+
def gem_config_path(gem_name, relative_config_path)
|
161
|
+
if defined?(Bundler)
|
162
|
+
gem = Bundler.load.specs[gem_name].first
|
163
|
+
gem_path = gem.full_gem_path if gem
|
164
|
+
end
|
165
|
+
|
166
|
+
gem_path ||= Gem::Specification.find_by_name(gem_name).gem_dir
|
167
|
+
|
168
|
+
File.join(gem_path, relative_config_path)
|
169
|
+
rescue Gem::LoadError => e
|
170
|
+
raise Gem::LoadError, "Unable to find gem #{gem_name}; is the gem installed? #{e}"
|
171
|
+
end
|
136
172
|
end
|
137
173
|
end
|
138
174
|
end
|
data/lib/haml_lint/document.rb
CHANGED
@@ -23,6 +23,14 @@ module HamlLint
|
|
23
23
|
# @return [Array<String>] original source code as an array of lines
|
24
24
|
attr_reader :source_lines
|
25
25
|
|
26
|
+
# @return [Boolean] true if the source was changed (by autocorrect)
|
27
|
+
attr_reader :source_was_changed
|
28
|
+
|
29
|
+
# @return [String] the indentation used in the file
|
30
|
+
attr_reader :indentation
|
31
|
+
|
32
|
+
attr_reader :unescape_interpolation_to_original_cache
|
33
|
+
|
26
34
|
# Parses the specified Haml code into a {Document}.
|
27
35
|
#
|
28
36
|
# @param source [String] Haml code to parse
|
@@ -32,22 +40,80 @@ module HamlLint
|
|
32
40
|
def initialize(source, options)
|
33
41
|
@config = options[:config]
|
34
42
|
@file = options.fetch(:file, STRING_SOURCE)
|
35
|
-
|
43
|
+
@source_was_changed = false
|
36
44
|
process_source(source)
|
37
45
|
end
|
38
46
|
|
47
|
+
# Returns the last non empty line of the document or 1 if all lines are empty
|
48
|
+
#
|
49
|
+
# @return [Integer] last non empty line of the document or 1 if all lines are empty
|
50
|
+
def last_non_empty_line
|
51
|
+
index = source_lines.rindex { |l| !l.empty? }
|
52
|
+
(index || 0) + 1
|
53
|
+
end
|
54
|
+
|
55
|
+
# Reparses the new source and remember that the document was changed
|
56
|
+
# Used when auto-correct does changes to the file. If the source hasn't changed,
|
57
|
+
# then the document will not be marked as changed.
|
58
|
+
#
|
59
|
+
# If the new_source fails to parse, automatically reparses the previous source
|
60
|
+
# to bring the document back to how it should be before re-raising the parse exception
|
61
|
+
#
|
62
|
+
# @param source [String] Haml code to parse
|
63
|
+
def change_source(new_source)
|
64
|
+
return if new_source == @source
|
65
|
+
check_new_source_compatible(new_source)
|
66
|
+
|
67
|
+
old_source = @source
|
68
|
+
begin
|
69
|
+
process_source(new_source)
|
70
|
+
@source_was_changed = true
|
71
|
+
rescue HamlLint::Exceptions::ParseError
|
72
|
+
# Reprocess the previous_source so that other linters can work on this document
|
73
|
+
# object from a clean slate
|
74
|
+
process_source(old_source)
|
75
|
+
raise
|
76
|
+
end
|
77
|
+
nil
|
78
|
+
end
|
79
|
+
|
80
|
+
def write_to_disk!
|
81
|
+
return unless @source_was_changed
|
82
|
+
if file == STRING_SOURCE
|
83
|
+
raise HamlLint::Exceptions::InvalidFilePath, 'Cannot write without :file option'
|
84
|
+
end
|
85
|
+
File.write(file, unstrip_frontmatter(source))
|
86
|
+
@source_was_changed = false
|
87
|
+
end
|
88
|
+
|
39
89
|
private
|
40
90
|
|
41
91
|
# @param source [String] Haml code to parse
|
42
92
|
# @raise [HamlLint::Exceptions::ParseError] if there was a problem parsing
|
43
|
-
def process_source(source)
|
93
|
+
def process_source(source) # rubocop:disable Metrics/MethodLength
|
44
94
|
@source = process_encoding(source)
|
45
95
|
@source = strip_frontmatter(source)
|
46
|
-
|
47
|
-
|
48
|
-
@
|
96
|
+
# the -1 is to keep the empty strings at the end of the array when the source
|
97
|
+
# ended with multiple new-lines
|
98
|
+
@source_lines = @source.split(/\r\n|\r|\n/, -1)
|
99
|
+
adapter = HamlLint::Adapter.detect_class.new(@source)
|
100
|
+
parsed_tree = adapter.parse
|
101
|
+
@indentation = adapter.send(:parser).instance_variable_get(:@indentation)
|
102
|
+
@tree = process_tree(parsed_tree)
|
103
|
+
@unescape_interpolation_to_original_cache =
|
104
|
+
Haml::Util.unescape_interpolation_to_original_cache_take_and_wipe
|
49
105
|
rescue Haml::Error => e
|
50
|
-
|
106
|
+
location = if e.line
|
107
|
+
"#{@file}:#{e.line}"
|
108
|
+
else
|
109
|
+
@file
|
110
|
+
end
|
111
|
+
msg = if ENV['HAML_LINT_DEBUG'] == 'true'
|
112
|
+
"#{location} (DEBUG: source follows) - #{e.message}\n#{source}\n------"
|
113
|
+
else
|
114
|
+
"#{location} - #{e.message}"
|
115
|
+
end
|
116
|
+
error = HamlLint::Exceptions::ParseError.new(msg, e.line)
|
51
117
|
raise error
|
52
118
|
end
|
53
119
|
|
@@ -114,11 +180,26 @@ module HamlLint
|
|
114
180
|
(---|\.\.\.)\s*$\n?/mx
|
115
181
|
|
116
182
|
if config['skip_frontmatter'] && match = source.match(frontmatter)
|
117
|
-
|
118
|
-
|
183
|
+
@stripped_frontmatter = match[0]
|
184
|
+
@nb_newlines_for_frontmatter = match[0].count("\n")
|
185
|
+
source.sub!(frontmatter, "\n" * @nb_newlines_for_frontmatter)
|
119
186
|
end
|
120
187
|
|
121
188
|
source
|
122
189
|
end
|
190
|
+
|
191
|
+
def check_new_source_compatible(new_source)
|
192
|
+
if @stripped_frontmatter && !new_source.start_with?("\n" * @nb_newlines_for_frontmatter)
|
193
|
+
raise HamlLint::Exceptions::IncompatibleNewSource,
|
194
|
+
"Internal error: new_source doesn't start with enough newlines for the Front Matter that was stripped"
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
def unstrip_frontmatter(source)
|
199
|
+
return source unless @stripped_frontmatter
|
200
|
+
check_new_source_compatible(source)
|
201
|
+
|
202
|
+
source.sub("\n" * @nb_newlines_for_frontmatter, @stripped_frontmatter)
|
203
|
+
end
|
123
204
|
end
|
124
205
|
end
|
data/lib/haml_lint/exceptions.rb
CHANGED
@@ -5,6 +5,12 @@ module HamlLint::Exceptions
|
|
5
5
|
# Raised when a {Configuration} could not be loaded from a file.
|
6
6
|
class ConfigurationError < StandardError; end
|
7
7
|
|
8
|
+
# Raised trying to change source with incompatible one (ex: due to frontmatter)
|
9
|
+
class IncompatibleNewSource < StandardError; end
|
10
|
+
|
11
|
+
# Raised when linter's autocorrection cause an infinite loop
|
12
|
+
class InfiniteLoopError < StandardError; end
|
13
|
+
|
8
14
|
# Raised when invalid/incompatible command line options are provided.
|
9
15
|
class InvalidCLIOption < StandardError; end
|
10
16
|
|