forked from sds/scss-lint
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathempty_line_between_blocks.rb
108 lines (87 loc) · 2.93 KB
/
empty_line_between_blocks.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# frozen_string_literal: true
module SCSSLint
# Reports the lack of empty lines between block defintions.
class Linter::EmptyLineBetweenBlocks < Linter
include LinterRegistry
def visit_atroot(node)
check(node, '@at-root')
yield
end
def visit_function(node)
check(node, '@function')
yield
end
def visit_media(node)
check(node, '@media')
yield
end
def visit_mixin(node)
# Ignore @includes which don't have any block content
check(node, '@include') if node.children
.any? { |child| child.is_a?(Sass::Tree::Node) }
yield
end
def visit_mixindef(node)
check(node, '@mixin')
yield
end
def visit_rule(node)
check(node, 'Rule')
yield
end
private
MESSAGE_FORMAT = '%s declaration should be %s by an empty line'.freeze
def check(node, type)
return if config['ignore_single_line_blocks'] && node_on_single_line?(node)
check_preceding_node(node, type)
check_following_node(node, type)
end
def check_following_node(node, type)
return unless (following_node = next_node(node)) &&
(next_start_line = following_node.line)
# Special case: ignore comments immediately after a closing brace
return if comment_after_closing_brace?(following_node, next_start_line)
# Special case: ignore `@else` nodes which are children of the parent `@if`
return if else_node?(following_node)
# Otherwise check if line before the next node's starting line is blank
return if next_line_blank?(next_start_line)
add_lint(next_start_line - 1, MESSAGE_FORMAT % [type, 'followed'])
end
def comment_after_closing_brace?(node, next_start_line)
line = engine.lines[next_start_line - 1].strip
node.is_a?(Sass::Tree::CommentNode) &&
line =~ %r{\s*\}?\s*/(/|\*)}
end
def next_line_blank?(next_start_line)
engine.lines[next_start_line - 2].strip.empty?
end
# In cases where the previous node is not a block declaration, we won't
# have run any checks against it, so we need to check here if the previous
# line is an empty line
def check_preceding_node(node, type)
case prev_node(node)
when
nil,
Sass::Tree::FunctionNode,
Sass::Tree::MixinNode,
Sass::Tree::MixinDefNode,
Sass::Tree::RuleNode,
Sass::Tree::CommentNode
# Ignore
else
unless engine.lines[node.line - 2].strip.empty?
add_lint(node.line, MESSAGE_FORMAT % [type, 'preceded'])
end
end
end
def next_node(node)
return unless siblings = node_siblings(node)
siblings[siblings.index(node) + 1] if siblings.count > 1
end
def prev_node(node)
return unless siblings = node_siblings(node)
index = siblings.index(node)
siblings[index - 1] if index > 0 && siblings.count > 1
end
end
end