haml_lint 0.45.0 → 0.47.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 +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
|
|