haml_lint 0.45.0 → 0.47.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/bin/haml-lint +1 -1
- data/config/default.yml +6 -28
- data/config/forced_rubocop_config.yml +171 -0
- data/lib/haml_lint/adapter/haml_4.rb +18 -0
- data/lib/haml_lint/adapter/haml_5.rb +11 -0
- data/lib/haml_lint/adapter/haml_6.rb +11 -0
- data/lib/haml_lint/cli.rb +8 -3
- data/lib/haml_lint/configuration_loader.rb +9 -10
- 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/rubocop.rb +353 -59
- data/lib/haml_lint/linter/space_before_script.rb +8 -10
- 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 +509 -0
- data/lib/haml_lint/ruby_extraction/coordinator.rb +181 -0
- data/lib/haml_lint/ruby_extraction/haml_comment_chunk.rb +54 -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 +132 -0
- data/lib/haml_lint/ruby_extraction/tag_attributes_chunk.rb +39 -0
- data/lib/haml_lint/ruby_extraction/tag_script_chunk.rb +30 -0
- data/lib/haml_lint/ruby_extractor.rb +11 -10
- 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/tag_node.rb +5 -9
- data/lib/haml_lint/utils.rb +130 -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 +25 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e7a818da3ed1d58db0ad35b864c6c90d8036173109f6609da452da8b496dd1d5
|
4
|
+
data.tar.gz: ca045650d3373d2fa48d840c684c14b3d46a497f39e91eb9058b1827f3bd74b7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0e2d83720d795e4589d534087c4db2be681eeae95cecda6432917345dea5e4c59e8ba7d65633fb2d4cd0ea86eafc0ea89b8fc51f994cc0b7973a50b0fa0a0abd
|
7
|
+
data.tar.gz: 11fc0ea04b7dae506bb0e264d204bdcdcaa96a65230ade8416ae56094a46f95deb3a1006200919804769dcc1c7adf7ab407bbfdf863355248eea9b0e7858308a
|
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,34 +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/FirstHashElementIndentation
|
101
|
-
- Layout/HashAlignment
|
102
|
-
- Layout/IndentationWidth
|
103
|
-
- Layout/LineLength # renamed from Metrics/LineLength in rubocop 0.79.0
|
104
|
-
- Layout/ParameterAlignment
|
105
|
-
- Layout/TrailingBlankLines # renamed to Layout/TrailingEmptyLines in rubocop 0.77
|
106
|
-
- Layout/TrailingEmptyLines
|
107
|
-
- Layout/TrailingWhitespace
|
108
|
-
- Metrics/BlockLength
|
109
|
-
- Metrics/BlockNesting
|
110
|
-
- Metrics/LineLength
|
111
|
-
- Naming/FileName
|
112
|
-
- Style/FrozenStringLiteralComment
|
113
|
-
- Style/IfUnlessModifier
|
114
|
-
- Style/Next
|
115
|
-
- 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: []
|
116
94
|
|
117
95
|
RubyComments:
|
118
96
|
enabled: true
|
@@ -0,0 +1,171 @@
|
|
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
|
+
# In some case, this cop can cause a change in the spacing.
|
126
|
+
# In HAML 5.2, going from (absurd example for clarity):
|
127
|
+
# = 'abc' rescue nil
|
128
|
+
# = 'def'
|
129
|
+
# to:
|
130
|
+
# = begin
|
131
|
+
# - 'abc'
|
132
|
+
# - rescue StandardError
|
133
|
+
# - nil
|
134
|
+
# = 'def'
|
135
|
+
# Will remove the only whitespace (a \n) that is between the abc and the def.
|
136
|
+
# This could affect spacing, ex: seeing "abc def" become "abcdef" when rendering
|
137
|
+
# after running this auto-correct
|
138
|
+
Style/RescueModifier:
|
139
|
+
AutoCorrect: false
|
140
|
+
|
141
|
+
# Cops that remove commas can be a problem when lines are split on multiple ones.
|
142
|
+
# If we have a big array on more than one line, the removal of the comma generates
|
143
|
+
# invalid HAML
|
144
|
+
Style/SymbolArray:
|
145
|
+
Enabled: false
|
146
|
+
|
147
|
+
# This can easily change the order of the markers in the document, which result in un-transferable corrections
|
148
|
+
Style/UnlessElse:
|
149
|
+
AutoCorrect: false
|
150
|
+
|
151
|
+
# If an array of strings was on multiple lines, this cop will make a %w(...) on multiple lines.
|
152
|
+
# Without the comma at the end of the first line, there the resulting HAML will be invalid, since the only
|
153
|
+
# case where a script can change line is after a comma.
|
154
|
+
Style/WordArray:
|
155
|
+
AutoCorrect: false
|
156
|
+
|
157
|
+
# Not RuboCop's job
|
158
|
+
Layout/TrailingEmptyLines:
|
159
|
+
Enabled: false
|
160
|
+
|
161
|
+
<% if rubocop_version < '1.8.1' %>
|
162
|
+
# There were a few bugs with this cop that got fixed in this version.
|
163
|
+
# Before, those bugs would generate invalid Ruby code and that would make it look like HAML-lint is
|
164
|
+
# responsible, at least from the user's point of view.
|
165
|
+
Style/StringConcatenation:
|
166
|
+
Enabled: false
|
167
|
+
<% end %>
|
168
|
+
|
169
|
+
# There is already a linter dedicated to this in haml-lint
|
170
|
+
Layout/LineLength:
|
171
|
+
Enabled: false
|
@@ -18,9 +18,21 @@ module HamlLint
|
|
18
18
|
# @param source [String] Haml code to parse
|
19
19
|
# @param options [Haml::Options]
|
20
20
|
def initialize(source, options = Haml::Options.new)
|
21
|
+
@source = source
|
21
22
|
@parser = Haml::Parser.new(source, options)
|
22
23
|
end
|
23
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
|
+
|
24
36
|
# @!method
|
25
37
|
# Parses the source code into an abstract syntax tree
|
26
38
|
#
|
@@ -39,6 +51,12 @@ module HamlLint
|
|
39
51
|
# @api private
|
40
52
|
# @return [Haml::Parser] the Haml 4 parser
|
41
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
|
42
60
|
end
|
43
61
|
end
|
44
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
|
@@ -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.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
|
+
|
33
44
|
private
|
34
45
|
|
35
46
|
# The Haml parser to adapt for HamlLint
|
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,7 +49,7 @@ 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] ||= []
|
53
54
|
context[:loaded_files].map! { |config_file| File.expand_path(config_file) }
|
54
55
|
context[:exclude_files] ||= []
|
@@ -85,13 +86,11 @@ module HamlLint
|
|
85
86
|
#
|
86
87
|
# @param file [String]
|
87
88
|
# @return [HamlLint::Configuration]
|
88
|
-
def load_from_file(file)
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
{}
|
94
|
-
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
|
95
94
|
|
96
95
|
if hash.key?('inherit_from')
|
97
96
|
hash['inherits_from'] ||= []
|
@@ -157,7 +156,7 @@ module HamlLint
|
|
157
156
|
#
|
158
157
|
# @param gem_name [String] name of the gem
|
159
158
|
# @param relative_config_path [String] path of the file to resolve, relative to the gem root
|
160
|
-
# @return [
|
159
|
+
# @return [String]
|
161
160
|
def gem_config_path(gem_name, relative_config_path)
|
162
161
|
if defined?(Bundler)
|
163
162
|
gem = Bundler.load.specs[gem_name].first
|
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
|
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Haml does heavy transformations to strings that contain interpolation without a way
|
4
|
+
# of perfectly inverting that transformation.
|
5
|
+
#
|
6
|
+
# We need this monkey patch to have a way of recovering the original strings as they
|
7
|
+
# are in the haml files, so that we can use them and then autocorrect them.
|
8
|
+
#
|
9
|
+
# The HamlLint::Document carries over a hash of interpolation to original string. The
|
10
|
+
# below patches are there to extract said information from Haml's parsing.
|
11
|
+
module Haml::Util
|
12
|
+
# The cache for the current Thread (technically Fiber)
|
13
|
+
def self.unescape_interpolation_to_original_cache
|
14
|
+
Thread.current[:haml_lint_unescape_interpolation_to_original_cache] ||= {}
|
15
|
+
end
|
16
|
+
|
17
|
+
# As soon as a HamlLint::Document has finished processing a HAML souce, this gets called to
|
18
|
+
# get a copy of this cache and clear up for the next HAML processing
|
19
|
+
def self.unescape_interpolation_to_original_cache_take_and_wipe
|
20
|
+
value = unescape_interpolation_to_original_cache.dup
|
21
|
+
unescape_interpolation_to_original_cache.clear
|
22
|
+
value
|
23
|
+
end
|
24
|
+
|
25
|
+
# Overriding the unescape_interpolation method to store the return and original string
|
26
|
+
# in the cache.
|
27
|
+
def unescape_interpolation_with_original_tracking(str, escape_html = nil)
|
28
|
+
value = unescape_interpolation_without_original_tracking(str, escape_html)
|
29
|
+
Haml::Util.unescape_interpolation_to_original_cache[value] = str
|
30
|
+
value
|
31
|
+
end
|
32
|
+
|
33
|
+
alias unescape_interpolation_without_original_tracking unescape_interpolation
|
34
|
+
alias unescape_interpolation unescape_interpolation_with_original_tracking
|
35
|
+
end
|
@@ -40,7 +40,7 @@ module HamlLint
|
|
40
40
|
#
|
41
41
|
# @param patterns [Array<String>]
|
42
42
|
# @return [Array<String>]
|
43
|
-
def extract_files_from(patterns) # rubocop:disable Metrics
|
43
|
+
def extract_files_from(patterns) # rubocop:disable Metrics
|
44
44
|
files = []
|
45
45
|
|
46
46
|
patterns.each do |pattern|
|
@@ -74,7 +74,7 @@ module HamlLint
|
|
74
74
|
# @param path [String]
|
75
75
|
# @return [String]
|
76
76
|
def normalize_path(path)
|
77
|
-
path.start_with?(".#{File::SEPARATOR}") ? path[2
|
77
|
+
path.start_with?(".#{File::SEPARATOR}") ? path[2..] : path
|
78
78
|
end
|
79
79
|
|
80
80
|
# Whether the given file should be treated as a Haml file.
|
data/lib/haml_lint/lint.rb
CHANGED
@@ -3,6 +3,9 @@
|
|
3
3
|
module HamlLint
|
4
4
|
# Contains information about a problem or issue with a HAML document.
|
5
5
|
class Lint
|
6
|
+
# @return [Boolean] If the error was corrected by auto-correct
|
7
|
+
attr_reader :corrected
|
8
|
+
|
6
9
|
# @return [String] file path to which the lint applies
|
7
10
|
attr_reader :filename
|
8
11
|
|
@@ -25,12 +28,13 @@ module HamlLint
|
|
25
28
|
# @param line [Fixnum]
|
26
29
|
# @param message [String]
|
27
30
|
# @param severity [Symbol]
|
28
|
-
def initialize(linter, filename, line, message, severity = :warning)
|
31
|
+
def initialize(linter, filename, line, message, severity = :warning, corrected: false) # rubocop:disable Metrics/ParameterLists
|
29
32
|
@linter = linter
|
30
33
|
@filename = filename
|
31
34
|
@line = line || 0
|
32
35
|
@message = message
|
33
36
|
@severity = Severity.new(severity)
|
37
|
+
@corrected = corrected
|
34
38
|
end
|
35
39
|
|
36
40
|
# Return whether this lint has a severity of error.
|
@@ -39,5 +43,10 @@ module HamlLint
|
|
39
43
|
def error?
|
40
44
|
@severity.error?
|
41
45
|
end
|
46
|
+
|
47
|
+
def inspect
|
48
|
+
"#{self.class.name}(corrected=#{corrected}, filename=#{filename}, line=#{line}, " \
|
49
|
+
"linter=#{linter.class.name}, message=#{message}, severity=#{severity})"
|
50
|
+
end
|
42
51
|
end
|
43
52
|
end
|
@@ -7,18 +7,19 @@ module HamlLint
|
|
7
7
|
|
8
8
|
def visit_root(root)
|
9
9
|
return if document.source.empty?
|
10
|
+
line_number = document.last_non_empty_line
|
10
11
|
|
11
|
-
node = root.node_for_line(
|
12
|
+
node = root.node_for_line(line_number)
|
12
13
|
return if node.disabled?(self)
|
13
14
|
|
14
15
|
ends_with_newline = document.source.end_with?("\n")
|
15
16
|
|
16
17
|
if config['present']
|
17
18
|
unless ends_with_newline
|
18
|
-
record_lint(
|
19
|
+
record_lint(line_number, 'Files should end with a trailing newline')
|
19
20
|
end
|
20
21
|
elsif ends_with_newline
|
21
|
-
record_lint(
|
22
|
+
record_lint(line_number, 'Files should not end with a trailing newline')
|
22
23
|
end
|
23
24
|
end
|
24
25
|
end
|
@@ -7,8 +7,8 @@ module HamlLint
|
|
7
7
|
|
8
8
|
# Allowed leading indentation for each character type.
|
9
9
|
INDENT_REGEX = {
|
10
|
-
space: /^
|
11
|
-
tab: /^\t*(?!
|
10
|
+
space: /^ *(?!\t)/,
|
11
|
+
tab: /^\t*(?! )/,
|
12
12
|
}.freeze
|
13
13
|
|
14
14
|
LEADING_SPACES_REGEX = /^( +)(?! )/.freeze
|
@@ -45,7 +45,7 @@ module HamlLint
|
|
45
45
|
root.children.each do |top_node|
|
46
46
|
# once we've found one line with leading space, there's no need to check any more lines
|
47
47
|
# `haml` will check indenting_at_start, deeper_indenting, inconsistent_indentation
|
48
|
-
break if top_node.children.find do |node|
|
48
|
+
break if top_node.children.find do |node| # rubocop:disable Lint/UnreachableLoop
|
49
49
|
line = node.source_code
|
50
50
|
leading_space = LEADING_SPACES_REGEX.match(line)
|
51
51
|
|