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