class SCSSLint::ControlCommentProcessor
Tracks which lines have been disabled for a given linter.
Public Class Methods
new(linter)
click to toggle source
# File lib/scss_lint/control_comment_processor.rb, line 6 def initialize(linter) @disable_stack = [] @disabled_lines = Set.new @linter = linter end
Public Instance Methods
after_node_visit(node)
click to toggle source
Executed after a node has been visited.
@param node [Sass::Tree::Node]
# File lib/scss_lint/control_comment_processor.rb, line 41 def after_node_visit(node) while @disable_stack.any? && @disable_stack.last.node_parent == node pop_control_comment_stack(node) end end
before_node_visit(node)
click to toggle source
Executed before a node has been visited.
@param node [Sass::Tree::Node]
# File lib/scss_lint/control_comment_processor.rb, line 22 def before_node_visit(node) return unless command = extract_command(node) linters = command[:linters] return unless linters.include?('all') || linters.include?(@linter.name) process_command(command[:action], node) # Is the control comment the only thing on this line? return if node.is_a?(Sass::Tree::RuleNode) || %r{^\s*(//|/\*)}.match(@linter.engine.lines[node.line - 1]) # Otherwise, pop since we only want comment to apply to the single line pop_control_comment_stack(node) end
filter_lints(lints)
click to toggle source
Filter lints given the comments that were processed in the document.
@param lints [Array<SCSSLint::Lint>]
# File lib/scss_lint/control_comment_processor.rb, line 15 def filter_lints(lints) lints.reject { |lint| @disabled_lines.include?(lint.location.line) } end
Private Instance Methods
extract_command(node)
click to toggle source
# File lib/scss_lint/control_comment_processor.rb, line 49 def extract_command(node) comment = case node when Sass::Tree::CommentNode node.value.first when Sass::Tree::RuleNode node.rule.select { |chunk| chunk.is_a?(String) }.join end return unless match = %r{ (/|\*)\s* # Comment start marker scss-lint: (?<action>disable|enable)\s+ (?<linters>.*?) \s*(?:\*/|\n) # Comment end marker or end of line }x.match(comment) { action: match[:action], linters: match[:linters].split(/\s*,\s*|\s+/), } end
last_child(node)
click to toggle source
Gets the child of the node that resides on the lowest line in the file.
This is necessary due to the fact that our monkey patching of the parse tree’s {#children} method does not return nodes sorted by their line number.
Returns ‘nil` if node has no children or no children with associated line numbers.
@param node [Sass::Tree::Node, Sass::Script::Tree::Node
] @return [Sass::Tree::Node, Sass::Script::Tree::Node
]
# File lib/scss_lint/control_comment_processor.rb, line 113 def last_child(node) last = node.children.inject(node) do |lowest, child| return lowest unless child.respond_to?(:line) lowest.line < child.line ? child : lowest end # In this case, none of the children have associated line numbers or the # node has no children at all, so return `nil`. return if last == node last end
pop_control_comment_stack(node)
click to toggle source
# File lib/scss_lint/control_comment_processor.rb, line 81 def pop_control_comment_stack(node) return unless comment_node = @disable_stack.pop start_line = comment_node.line # Find the deepest child that has a line number to which a lint might # apply (if it is a control comment enable node, it will be the line of # the comment itself). child = node prev_child = node until [nil, prev_child].include?(child = last_child(child)) prev_child = child end # Fall back to prev_child if last_child() returned nil (i.e. node had no # children with line numbers) end_line = (child || prev_child).line @disabled_lines.merge(start_line..end_line) end
process_command(command, node)
click to toggle source
# File lib/scss_lint/control_comment_processor.rb, line 72 def process_command(command, node) case command when 'disable' @disable_stack << node when 'enable' pop_control_comment_stack(node) end end