haml_lint 0.21.0 → 0.22.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 +18 -0
- data/lib/haml_lint/adapter/haml_4.rb +40 -0
- data/lib/haml_lint/adapter/haml_5.rb +46 -0
- data/lib/haml_lint/adapter.rb +36 -0
- data/lib/haml_lint/cli.rb +12 -9
- data/lib/haml_lint/comment_configuration.rb +39 -0
- data/lib/haml_lint/directive.rb +128 -0
- data/lib/haml_lint/document.rb +3 -1
- data/lib/haml_lint/exceptions.rb +6 -0
- data/lib/haml_lint/haml_visitor.rb +4 -2
- data/lib/haml_lint/lint.rb +2 -2
- data/lib/haml_lint/linter/alignment_tabs.rb +12 -0
- data/lib/haml_lint/linter/consecutive_comments.rb +19 -2
- data/lib/haml_lint/linter/consecutive_silent_scripts.rb +18 -2
- data/lib/haml_lint/linter/final_newline.rb +6 -5
- data/lib/haml_lint/linter/id_names.rb +28 -0
- data/lib/haml_lint/linter/indentation.rb +4 -2
- data/lib/haml_lint/linter/instance_variables.rb +77 -0
- data/lib/haml_lint/linter/line_length.rb +5 -3
- data/lib/haml_lint/linter/repeated_id.rb +34 -0
- data/lib/haml_lint/linter/syntax.rb +6 -0
- data/lib/haml_lint/linter/trailing_whitespace.rb +5 -4
- data/lib/haml_lint/linter.rb +1 -1
- data/lib/haml_lint/logger.rb +7 -1
- data/lib/haml_lint/options.rb +20 -4
- data/lib/haml_lint/parsed_ruby.rb +22 -0
- data/lib/haml_lint/rake_task.rb +16 -2
- data/lib/haml_lint/report.rb +46 -2
- data/lib/haml_lint/reporter/default_reporter.rb +7 -29
- data/lib/haml_lint/reporter/hash_reporter.rb +51 -0
- data/lib/haml_lint/reporter/hooks.rb +25 -0
- data/lib/haml_lint/reporter/json_reporter.rb +2 -45
- data/lib/haml_lint/reporter/progress_reporter.rb +47 -0
- data/lib/haml_lint/reporter/utils.rb +101 -0
- data/lib/haml_lint/reporter.rb +4 -0
- data/lib/haml_lint/runner.rb +70 -10
- data/lib/haml_lint/severity.rb +95 -0
- data/lib/haml_lint/tree/haml_comment_node.rb +18 -0
- data/lib/haml_lint/tree/node.rb +116 -16
- data/lib/haml_lint/tree/null_node.rb +15 -0
- data/lib/haml_lint/tree/root_node.rb +16 -0
- data/lib/haml_lint/tree/script_node.rb +9 -0
- data/lib/haml_lint/tree/silent_script_node.rb +7 -0
- data/lib/haml_lint/tree/tag_node.rb +24 -5
- data/lib/haml_lint/version.rb +1 -1
- data/lib/haml_lint.rb +1 -0
- metadata +41 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b7cae686b1a15d37a81d966b3c80c41fe51fbe2e
|
4
|
+
data.tar.gz: e1997a13d2721bedc442891b8a215e8e825180bf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 62926eeddd753e8af645a8db5cc621cbf3807362453b6913a5f47060b8e13baaa9a8e4dcaa54f32111bc927c53ad08ccfa3ad71c141150c800aacd50a01afac7
|
7
|
+
data.tar.gz: 8c77947ce1117683f9386724b01a9447f3d5964e958026f872c89d0eff1c7cd70455506ce109041d72a2caaaa83c63e52cdbdb46085707adf81dbe6d6f646b51
|
data/config/default.yml
CHANGED
@@ -8,6 +8,9 @@
|
|
8
8
|
skip_frontmatter: false
|
9
9
|
|
10
10
|
linters:
|
11
|
+
AlignmentTabs:
|
12
|
+
enabled: true
|
13
|
+
|
11
14
|
AltText:
|
12
15
|
enabled: false
|
13
16
|
|
@@ -37,9 +40,20 @@ linters:
|
|
37
40
|
HtmlAttributes:
|
38
41
|
enabled: true
|
39
42
|
|
43
|
+
IdNames:
|
44
|
+
enabled: true
|
45
|
+
style: lisp_case
|
46
|
+
|
40
47
|
ImplicitDiv:
|
41
48
|
enabled: true
|
42
49
|
|
50
|
+
InstanceVariables:
|
51
|
+
enabled: true
|
52
|
+
file_types: partials
|
53
|
+
matchers:
|
54
|
+
all: .*
|
55
|
+
partials: \A_.*\.haml\z
|
56
|
+
|
43
57
|
LeadingCommentSpace:
|
44
58
|
enabled: true
|
45
59
|
|
@@ -56,6 +70,10 @@ linters:
|
|
56
70
|
ObjectReferenceAttributes:
|
57
71
|
enabled: true
|
58
72
|
|
73
|
+
RepeatedId:
|
74
|
+
enabled: true
|
75
|
+
severity: error
|
76
|
+
|
59
77
|
RuboCop:
|
60
78
|
enabled: true
|
61
79
|
# These cops are incredibly noisy when it comes to HAML templates, so we
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module HamlLint
|
2
|
+
class Adapter
|
3
|
+
# Adapts the Haml::Parser from Haml 4 for use in HamlLint
|
4
|
+
# :reek:UncommunicativeModuleName
|
5
|
+
class Haml4 < Adapter
|
6
|
+
extend Forwardable
|
7
|
+
|
8
|
+
# Parses the specified Haml code into an abstract syntax tree
|
9
|
+
#
|
10
|
+
# @example
|
11
|
+
# HamlLint::Adapter::Haml4.new('%div')
|
12
|
+
#
|
13
|
+
# @api public
|
14
|
+
# @param source [String] Haml code to parse
|
15
|
+
# @param options [Haml::Options]
|
16
|
+
def initialize(source, options = Haml::Options.new)
|
17
|
+
@parser = Haml::Parser.new(source, options)
|
18
|
+
end
|
19
|
+
|
20
|
+
# @!method
|
21
|
+
# Parses the source code into an abstract syntax tree
|
22
|
+
#
|
23
|
+
# @example
|
24
|
+
# HamlLint::Adapter::Haml4.new('%div')
|
25
|
+
#
|
26
|
+
# @api public
|
27
|
+
# @return [Haml::Parser::ParseNode]
|
28
|
+
# @raise [Haml::Error]
|
29
|
+
def_delegator :parser, :parse
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
# The Haml parser to adapt for HamlLint
|
34
|
+
#
|
35
|
+
# @api private
|
36
|
+
# @return [Haml::Parser] the Haml 4 parser
|
37
|
+
attr_reader :parser
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module HamlLint
|
2
|
+
class Adapter
|
3
|
+
# Adapts the Haml::Parser from Haml 5 for use in HamlLint
|
4
|
+
# :reek:UncommunicativeModuleName
|
5
|
+
class Haml5 < Adapter
|
6
|
+
# Parses the specified Haml code into an abstract syntax tree
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# HamlLint::Adapter::Haml5.new('%div')
|
10
|
+
#
|
11
|
+
# @api public
|
12
|
+
# @param source [String] Haml code to parse
|
13
|
+
# @param options [Haml::Options]
|
14
|
+
def initialize(source, options = Haml::Options.new)
|
15
|
+
@source = source
|
16
|
+
@parser = Haml::Parser.new(options)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Parses the source code into an abstract syntax tree
|
20
|
+
#
|
21
|
+
# @example
|
22
|
+
# HamlLint::Adapter::Haml5.new('%div').parse
|
23
|
+
#
|
24
|
+
# @api public
|
25
|
+
# @return [Haml::Parser::ParseNode]
|
26
|
+
# @raise [Haml::Error]
|
27
|
+
def parse
|
28
|
+
parser.call(source)
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
# The Haml parser to adapt for HamlLint
|
34
|
+
#
|
35
|
+
# @api private
|
36
|
+
# @return [Haml::Parser] the Haml 4 parser
|
37
|
+
attr_reader :parser
|
38
|
+
|
39
|
+
# The Haml code to parse
|
40
|
+
#
|
41
|
+
# @api private
|
42
|
+
# @return [String] Haml code to parse
|
43
|
+
attr_reader :source
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'haml_lint/adapter/haml_4'
|
2
|
+
require 'haml_lint/adapter/haml_5'
|
3
|
+
require 'haml_lint/exceptions'
|
4
|
+
|
5
|
+
module HamlLint
|
6
|
+
# Determines the adapter to use for the current Haml version
|
7
|
+
class Adapter
|
8
|
+
# Detects the adapter to use for the current Haml version
|
9
|
+
#
|
10
|
+
# @example
|
11
|
+
# HamlLint::Adapter.detect_class.new('%div')
|
12
|
+
#
|
13
|
+
# @api public
|
14
|
+
# @return [Class] the adapter class
|
15
|
+
# @raise [HamlLint::Exceptions::UnknownHamlVersion]
|
16
|
+
def self.detect_class
|
17
|
+
version = haml_version
|
18
|
+
case version
|
19
|
+
when '~> 4.0' then HamlLint::Adapter::Haml4
|
20
|
+
when '~> 5.0' then HamlLint::Adapter::Haml5
|
21
|
+
else fail HamlLint::Exceptions::UnknownHamlVersion, "Cannot handle Haml version: #{version}"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# Determines the approximate version of Haml that is installed
|
26
|
+
#
|
27
|
+
# @api private
|
28
|
+
# @return [String] the approximate Haml version
|
29
|
+
def self.haml_version
|
30
|
+
Gem::Version
|
31
|
+
.new(Haml::VERSION)
|
32
|
+
.approximate_recommendation
|
33
|
+
end
|
34
|
+
private_class_method :haml_version
|
35
|
+
end
|
36
|
+
end
|
data/lib/haml_lint/cli.rb
CHANGED
@@ -33,7 +33,7 @@ module HamlLint
|
|
33
33
|
#
|
34
34
|
# @return [Integer] exit status code
|
35
35
|
def act_on_options(options)
|
36
|
-
|
36
|
+
configure_logger(options)
|
37
37
|
|
38
38
|
if options[:help]
|
39
39
|
print_help(options)
|
@@ -52,6 +52,14 @@ module HamlLint
|
|
52
52
|
end
|
53
53
|
end
|
54
54
|
|
55
|
+
# Given the provided options, configure the logger.
|
56
|
+
#
|
57
|
+
# @return [void]
|
58
|
+
def configure_logger(options)
|
59
|
+
log.color_enabled = options.fetch(:color, log.tty?)
|
60
|
+
log.summary_enabled = options.fetch(:summary, true)
|
61
|
+
end
|
62
|
+
|
55
63
|
# Outputs a message and returns an appropriate error code for the specified
|
56
64
|
# exception.
|
57
65
|
def handle_exception(ex)
|
@@ -79,16 +87,11 @@ module HamlLint
|
|
79
87
|
#
|
80
88
|
# @return [Integer] exit status code
|
81
89
|
def scan_for_lints(options)
|
82
|
-
report = Runner.new.run(options)
|
83
|
-
print_report(report, options)
|
84
|
-
report.failed? ? Sysexits::EX_DATAERR : Sysexits::EX_OK
|
85
|
-
end
|
86
|
-
|
87
|
-
# Outputs a report of the linter run using the specified reporter.
|
88
|
-
def print_report(report, options)
|
89
90
|
reporter = options.fetch(:reporter,
|
90
91
|
HamlLint::Reporter::DefaultReporter).new(log)
|
91
|
-
|
92
|
+
report = Runner.new.run(options.merge(reporter: reporter))
|
93
|
+
report.display
|
94
|
+
report.failed? ? Sysexits::EX_DATAERR : Sysexits::EX_OK
|
92
95
|
end
|
93
96
|
|
94
97
|
# Outputs a list of all currently available linters.
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module HamlLint
|
2
|
+
# Determines what linters are enabled or disabled via comments.
|
3
|
+
class CommentConfiguration
|
4
|
+
# Instantiates a new {HamlLint::CommentConfiguration}.
|
5
|
+
#
|
6
|
+
# @param node [HamlLint::Tree::Node] the node to configure
|
7
|
+
def initialize(node)
|
8
|
+
@directives = node.directives.reverse
|
9
|
+
end
|
10
|
+
|
11
|
+
# Checks whether a linter is disabled for the node.
|
12
|
+
#
|
13
|
+
# @api public
|
14
|
+
# @param linter_name [String] the name of the linter
|
15
|
+
# @return [true, false]
|
16
|
+
def disabled?(linter_name)
|
17
|
+
most_recent_disabled = directives_for(linter_name).map(&:disable?).first
|
18
|
+
|
19
|
+
most_recent_disabled || false
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
# The list of directives in order of precedence.
|
25
|
+
#
|
26
|
+
# @api private
|
27
|
+
# @return [Array<HamlLint::Directive>]
|
28
|
+
attr_reader :directives
|
29
|
+
|
30
|
+
# Finds all directives applicable to the given linter name.
|
31
|
+
#
|
32
|
+
# @api private
|
33
|
+
# @param linter_name [String] the name of the linter
|
34
|
+
# @return [Array<HamlLint::Directive>] the filtered directives
|
35
|
+
def directives_for(linter_name)
|
36
|
+
directives.select { |directive| (directive.linters & ['all', linter_name]).any? }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
module HamlLint
|
2
|
+
# Handles linter configuration transformation via Haml comments.
|
3
|
+
class Directive
|
4
|
+
LINTER_REGEXP = /(?:[A-Z]\w+)/
|
5
|
+
|
6
|
+
DIRECTIVE_REGEXP = /
|
7
|
+
# "haml-lint:" with optional spacing
|
8
|
+
\s*haml-lint\s*:\s*
|
9
|
+
|
10
|
+
# The mode - either disable or enable
|
11
|
+
(?<mode>(?:dis|en)able)\b\s*
|
12
|
+
|
13
|
+
# "all" or a comma-separated list (with optional spaces) of linters
|
14
|
+
(?<linters>all | (?:#{LINTER_REGEXP}\s*,\s*)* #{LINTER_REGEXP})
|
15
|
+
/x
|
16
|
+
|
17
|
+
# Constructs a directive from source code as a given line.
|
18
|
+
#
|
19
|
+
# @param source [String] the source code to analyze
|
20
|
+
# @param line [Integer] the line number the source starts at
|
21
|
+
# @return [HamlLint::Directive]
|
22
|
+
def self.from_line(source, line)
|
23
|
+
match = DIRECTIVE_REGEXP.match(source)
|
24
|
+
|
25
|
+
if match
|
26
|
+
new(source, line, match[:mode], match[:linters].split(/\s*,\s*/))
|
27
|
+
else
|
28
|
+
Null.new(source, line)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# Instantiates a new {HamlLint::Directive}
|
33
|
+
#
|
34
|
+
# @api semipublic
|
35
|
+
# @param source [String] the source code to analyze
|
36
|
+
# @param line [Integer] the line number the source starts at
|
37
|
+
# @param mode [String] the type of directive, one of "disable" or "enable"
|
38
|
+
# @param linters [Array<String>] the name of the linters to act upon
|
39
|
+
def initialize(source, line, mode, linters)
|
40
|
+
@source = source
|
41
|
+
@line = line
|
42
|
+
@mode = mode
|
43
|
+
@linters = linters
|
44
|
+
end
|
45
|
+
|
46
|
+
# The names of the linters to act upon.
|
47
|
+
#
|
48
|
+
# @return [String]
|
49
|
+
attr_reader :linters
|
50
|
+
|
51
|
+
# The mode of the directive. One of "disable" or "enable".
|
52
|
+
#
|
53
|
+
# @return [String]
|
54
|
+
attr_reader :mode
|
55
|
+
|
56
|
+
# Checks whether a directive is equivalent to another.
|
57
|
+
#
|
58
|
+
# @api public
|
59
|
+
# @param other [HamlLint::Directive] the other directive
|
60
|
+
# @return [true, false]
|
61
|
+
def ==(other)
|
62
|
+
super unless other.is_a?(HamlLint::Directive)
|
63
|
+
|
64
|
+
mode == other.mode && linters == other.linters
|
65
|
+
end
|
66
|
+
|
67
|
+
# Checks whether this is a disable directive.
|
68
|
+
#
|
69
|
+
# @return [true, false]
|
70
|
+
def disable?
|
71
|
+
mode == 'disable'
|
72
|
+
end
|
73
|
+
|
74
|
+
# Checks whether this is an enable directive.
|
75
|
+
#
|
76
|
+
# @return [true, false]
|
77
|
+
def enable?
|
78
|
+
mode == 'enable'
|
79
|
+
end
|
80
|
+
|
81
|
+
# Formats the directive for display in a console.
|
82
|
+
#
|
83
|
+
# @return [String]
|
84
|
+
def inspect
|
85
|
+
"#<HamlLint::Directive(mode=#{mode}, linters=#{linters})>"
|
86
|
+
end
|
87
|
+
|
88
|
+
# A null representation of a directive.
|
89
|
+
class Null < Directive
|
90
|
+
# Instantiates a new null directive.
|
91
|
+
#
|
92
|
+
# @param source [String] the source code to analyze
|
93
|
+
# @param line [Integer] the line number the source starts at
|
94
|
+
def initialize(source, line)
|
95
|
+
@source = source
|
96
|
+
@line = line
|
97
|
+
end
|
98
|
+
|
99
|
+
# Stubs out the disable check as false.
|
100
|
+
#
|
101
|
+
# @return [false]
|
102
|
+
def disable?
|
103
|
+
false
|
104
|
+
end
|
105
|
+
|
106
|
+
# Stubs out the ensable check as false.
|
107
|
+
#
|
108
|
+
# @return [false]
|
109
|
+
def enable?
|
110
|
+
false
|
111
|
+
end
|
112
|
+
|
113
|
+
# Formats the null directive for display in a console.
|
114
|
+
#
|
115
|
+
# @return [String]
|
116
|
+
def inspect
|
117
|
+
'#<HamlLint::Directive::Null>'
|
118
|
+
end
|
119
|
+
|
120
|
+
# Stubs out the linters.
|
121
|
+
#
|
122
|
+
# @return [Array]
|
123
|
+
def linters
|
124
|
+
[]
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
data/lib/haml_lint/document.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'haml_lint/adapter'
|
4
|
+
|
3
5
|
module HamlLint
|
4
6
|
# Represents a parsed Haml document and its associated metadata.
|
5
7
|
class Document
|
@@ -43,7 +45,7 @@ module HamlLint
|
|
43
45
|
@source = strip_frontmatter(source)
|
44
46
|
@source_lines = @source.split("\n")
|
45
47
|
|
46
|
-
@tree = process_tree(
|
48
|
+
@tree = process_tree(HamlLint::Adapter.detect_class.new(@source).parse)
|
47
49
|
rescue Haml::Error => ex
|
48
50
|
error = HamlLint::Exceptions::ParseError.new(ex.message, ex.line)
|
49
51
|
raise error
|
data/lib/haml_lint/exceptions.rb
CHANGED
@@ -15,4 +15,10 @@ module HamlLint::Exceptions
|
|
15
15
|
# Raised when attempting to execute `Runner` with options that would result in
|
16
16
|
# no linters being enabled.
|
17
17
|
class NoLintersError < StandardError; end
|
18
|
+
|
19
|
+
# Raised when an unsupported Haml version is detected
|
20
|
+
class UnknownHamlVersion < StandardError; end
|
21
|
+
|
22
|
+
# Raised when a severity is not recognized
|
23
|
+
class UnknownSeverity < StandardError; end
|
18
24
|
end
|
@@ -14,14 +14,16 @@ module HamlLint
|
|
14
14
|
visit_children(node) if descend == :children
|
15
15
|
end
|
16
16
|
|
17
|
-
|
17
|
+
disabled = node.disabled?(self)
|
18
|
+
|
19
|
+
safe_send("visit_#{node_name(node)}", node, &block) unless disabled
|
18
20
|
|
19
21
|
# Visit all children by default unless the block was invoked (indicating
|
20
22
|
# the user intends to not recurse further, or wanted full control over
|
21
23
|
# when the children were visited).
|
22
24
|
visit_children(node) unless block_called
|
23
25
|
|
24
|
-
safe_send("after_visit_#{node_name(node)}", node, &block)
|
26
|
+
safe_send("after_visit_#{node_name(node)}", node, &block) unless disabled
|
25
27
|
end
|
26
28
|
|
27
29
|
def visit_children(parent)
|
data/lib/haml_lint/lint.rb
CHANGED
@@ -28,14 +28,14 @@ module HamlLint
|
|
28
28
|
@filename = filename
|
29
29
|
@line = line || 0
|
30
30
|
@message = message
|
31
|
-
@severity = severity
|
31
|
+
@severity = Severity.new(severity)
|
32
32
|
end
|
33
33
|
|
34
34
|
# Return whether this lint has a severity of error.
|
35
35
|
#
|
36
36
|
# @return [Boolean]
|
37
37
|
def error?
|
38
|
-
@severity
|
38
|
+
@severity.error?
|
39
39
|
end
|
40
40
|
end
|
41
41
|
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module HamlLint
|
2
|
+
# Checks for tabs that are placed for alignment of tag content
|
3
|
+
class Linter::AlignmentTabs < Linter
|
4
|
+
REGEX = /[^\s*]\t+/
|
5
|
+
|
6
|
+
def visit_tag(node)
|
7
|
+
if REGEX.match(node.source_code)
|
8
|
+
record_lint(node, 'Avoid using tabs for alignment')
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -5,14 +5,31 @@ module HamlLint
|
|
5
5
|
|
6
6
|
COMMENT_DETECTOR = ->(child) { child.type == :haml_comment }
|
7
7
|
|
8
|
-
def
|
8
|
+
def visit_haml_comment(node)
|
9
|
+
return if previously_reported?(node)
|
10
|
+
|
9
11
|
HamlLint::Utils.for_consecutive_items(
|
10
|
-
node
|
12
|
+
possible_group(node),
|
11
13
|
COMMENT_DETECTOR,
|
12
14
|
) do |group|
|
15
|
+
group.each { |group_node| reported_nodes << group_node }
|
13
16
|
record_lint(group.first,
|
14
17
|
"#{group.count} consecutive comments can be merged into one")
|
15
18
|
end
|
16
19
|
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def possible_group(node)
|
24
|
+
node.subsequents.unshift(node)
|
25
|
+
end
|
26
|
+
|
27
|
+
def previously_reported?(node)
|
28
|
+
reported_nodes.include?(node)
|
29
|
+
end
|
30
|
+
|
31
|
+
def reported_nodes
|
32
|
+
@reported_nodes ||= []
|
33
|
+
end
|
17
34
|
end
|
18
35
|
end
|
@@ -8,9 +8,11 @@ module HamlLint
|
|
8
8
|
child.type == :silent_script && child.children.empty?
|
9
9
|
end
|
10
10
|
|
11
|
-
def
|
11
|
+
def visit_silent_script(node)
|
12
|
+
return if previously_reported?(node)
|
13
|
+
|
12
14
|
HamlLint::Utils.for_consecutive_items(
|
13
|
-
node
|
15
|
+
possible_group(node),
|
14
16
|
SILENT_SCRIPT_DETECTOR,
|
15
17
|
config['max_consecutive'] + 1,
|
16
18
|
) do |group|
|
@@ -19,5 +21,19 @@ module HamlLint
|
|
19
21
|
'into a single `:ruby` filter')
|
20
22
|
end
|
21
23
|
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def possible_group(node)
|
28
|
+
node.subsequents.unshift(node)
|
29
|
+
end
|
30
|
+
|
31
|
+
def previously_reported?(node)
|
32
|
+
reported_nodes.include?(node)
|
33
|
+
end
|
34
|
+
|
35
|
+
def reported_nodes
|
36
|
+
@reported_nodes ||= []
|
37
|
+
end
|
22
38
|
end
|
23
39
|
end
|
@@ -3,19 +3,20 @@ module HamlLint
|
|
3
3
|
class Linter::FinalNewline < Linter
|
4
4
|
include LinterRegistry
|
5
5
|
|
6
|
-
def visit_root(
|
6
|
+
def visit_root(root)
|
7
7
|
return if document.source.empty?
|
8
8
|
|
9
|
-
|
9
|
+
node = root.node_for_line(document.source_lines.count)
|
10
|
+
return if node.disabled?(self)
|
11
|
+
|
10
12
|
ends_with_newline = document.source.end_with?("\n")
|
11
13
|
|
12
14
|
if config['present']
|
13
15
|
unless ends_with_newline
|
14
|
-
record_lint(
|
16
|
+
record_lint(node, 'Files should end with a trailing newline')
|
15
17
|
end
|
16
18
|
elsif ends_with_newline
|
17
|
-
record_lint(
|
18
|
-
'Files should not end with a trailing newline')
|
19
|
+
record_lint(node, 'Files should not end with a trailing newline')
|
19
20
|
end
|
20
21
|
end
|
21
22
|
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module HamlLint
|
2
|
+
# Checks for `id` attributes in specific cases on tags.
|
3
|
+
class Linter::IdNames < Linter
|
4
|
+
include LinterRegistry
|
5
|
+
|
6
|
+
STYLIZED_NAMES = {
|
7
|
+
'camel_case' => 'camelCase',
|
8
|
+
'lisp_case' => 'lisp-case',
|
9
|
+
'pascal_case' => 'PascalCase',
|
10
|
+
'snake_case' => 'snake_case',
|
11
|
+
}.freeze
|
12
|
+
|
13
|
+
STYLES = {
|
14
|
+
'camel_case' => /\A[a-z][\da-zA-Z]+\z/,
|
15
|
+
'lisp_case' => /\A[\da-z-]+\z/,
|
16
|
+
'pascal_case' => /\A[A-Z][\da-zA-Z]+\z/,
|
17
|
+
'snake_case' => /\A[\da-z_]+\z/,
|
18
|
+
}.freeze
|
19
|
+
|
20
|
+
def visit_tag(node)
|
21
|
+
return unless (id = node.tag_id)
|
22
|
+
|
23
|
+
style = config['style'] || 'lisp_case'
|
24
|
+
matcher = STYLES[style]
|
25
|
+
record_lint(node, "`id` attribute must be in #{STYLIZED_NAMES[style]}") unless id =~ matcher
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -9,14 +9,16 @@ module HamlLint
|
|
9
9
|
tab: /^\t*(?![ ])/,
|
10
10
|
}.freeze
|
11
11
|
|
12
|
-
def visit_root(
|
12
|
+
def visit_root(root)
|
13
13
|
regex = INDENT_REGEX[config['character'].to_sym]
|
14
14
|
dummy_node = Struct.new(:line)
|
15
15
|
|
16
16
|
document.source_lines.each_with_index do |line, index|
|
17
17
|
next if line =~ regex
|
18
18
|
|
19
|
-
|
19
|
+
unless root.node_for_line(index).disabled?(self)
|
20
|
+
record_lint dummy_node.new(index + 1), 'Line contains tabs in indentation'
|
21
|
+
end
|
20
22
|
end
|
21
23
|
end
|
22
24
|
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
module HamlLint
|
2
|
+
# Checks for the presence of instance variables
|
3
|
+
class Linter::InstanceVariables < Linter
|
4
|
+
include LinterRegistry
|
5
|
+
|
6
|
+
# Enables the linter if the tree is for the right file type.
|
7
|
+
#
|
8
|
+
# @param [HamlLint::Tree::RootNode] the root of a syntax tree
|
9
|
+
# @return [true, false] whether the linter is enabled for the tree
|
10
|
+
def visit_root(node)
|
11
|
+
@enabled = matcher.match(File.basename(node.file)) ? true : false
|
12
|
+
end
|
13
|
+
|
14
|
+
# Checks for instance variables in script nodes when the linter is enabled.
|
15
|
+
#
|
16
|
+
# @param [HamlLint::Tree:ScriptNode]
|
17
|
+
# @return [void]
|
18
|
+
def visit_script(node)
|
19
|
+
return unless enabled?
|
20
|
+
|
21
|
+
if node.parsed_script.contains_instance_variables?
|
22
|
+
record_lint(node, "Avoid using instance variables in #{file_types} views")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# @!method visit_silent_script(node)
|
27
|
+
# Checks for instance variables in script nodes when the linter is enabled.
|
28
|
+
#
|
29
|
+
# @param [HamlLint::Tree:SilentScriptNode]
|
30
|
+
# @return [void]
|
31
|
+
alias visit_silent_script visit_script
|
32
|
+
|
33
|
+
# Checks for instance variables in tag nodes when the linter is enabled.
|
34
|
+
#
|
35
|
+
# @param [HamlLint::Tree:TagNode]
|
36
|
+
# @return [void]
|
37
|
+
def visit_tag(node)
|
38
|
+
return unless enabled?
|
39
|
+
|
40
|
+
visit_script(node) ||
|
41
|
+
if node.parsed_attributes.contains_instance_variables?
|
42
|
+
record_lint(node, "Avoid using instance variables in #{file_types} views")
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
# Tracks whether the linter is enabled for the file.
|
49
|
+
#
|
50
|
+
# @api private
|
51
|
+
# @return [true, false]
|
52
|
+
attr_reader :enabled
|
53
|
+
|
54
|
+
# @!method enabled?
|
55
|
+
# Checks whether the linter is enabled for the file.
|
56
|
+
#
|
57
|
+
# @api private
|
58
|
+
# @return [true, false]
|
59
|
+
alias enabled? enabled
|
60
|
+
|
61
|
+
# The type of files the linter is configured to check.
|
62
|
+
#
|
63
|
+
# @api private
|
64
|
+
# @return [String]
|
65
|
+
def file_types
|
66
|
+
@file_types ||= config['file_types'] || 'partial'
|
67
|
+
end
|
68
|
+
|
69
|
+
# The matcher to use for testing whether to check a file by file name.
|
70
|
+
#
|
71
|
+
# @api private
|
72
|
+
# @return [Regexp]
|
73
|
+
def matcher
|
74
|
+
@matcher ||= Regexp.new(config['matchers'][file_types] || '\A_.*\.haml\z')
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|