class SCSSLint::Linter::TrailingSemicolon

Checks for a trailing semicolon on statements within rule sets.

Public Instance Methods

visit_extend(node) click to toggle source
# File lib/scss_lint/linter/trailing_semicolon.rb, line 6
def visit_extend(node)
  check_semicolon(node)
end
visit_import(node) click to toggle source
# File lib/scss_lint/linter/trailing_semicolon.rb, line 46
def visit_import(node)
  # Ignore all but the last import for comma-separated @imports
  return if source_from_range(node.source_range) =~ /,\s*$/
  check_semicolon(node)
end
visit_mixin(node) { || ... } click to toggle source
# File lib/scss_lint/linter/trailing_semicolon.rb, line 38
def visit_mixin(node)
  if node.children.any?
    yield # Continue checking children
  else
    check_semicolon(node)
  end
end
visit_prop(node) { || ... } click to toggle source
# File lib/scss_lint/linter/trailing_semicolon.rb, line 30
def visit_prop(node)
  if node.children.any? { |n| n.is_a?(Sass::Tree::PropNode) }
    yield # Continue checking children
  else
    check_semicolon(node)
  end
end
visit_variable(node) click to toggle source
# File lib/scss_lint/linter/trailing_semicolon.rb, line 10
def visit_variable(node)
  # If the variable is using `!default` or `!global` (e.g. `$foo: bar
  # !default;`) then `node.expr` will give us the source range just for the
  # value (e.g. `bar`). In these cases, we want to use the source range of
  # `node`, which will give us most of the entire line (e.g. `foo: bar
  # !default`.
  return check_semicolon(node) if node.global || node.guarded

  # If the variable is a multi-line ListLiteral or MapLiteral, then
  # `node.expr` will give us everything except the last right paren, and
  # the semicolon if it exists. In these cases, use the source range of
  # `node` as above.
  if node.expr.is_a?(Sass::Script::Tree::ListLiteral) ||
      node.expr.is_a?(Sass::Script::Tree::MapLiteral)
    return check_semicolon(node)
  end

  check_semicolon(node.expr)
end

Private Instance Methods

check_semicolon(node) click to toggle source
# File lib/scss_lint/linter/trailing_semicolon.rb, line 54
def check_semicolon(node)
  if has_space_before_semicolon?(node)
    line = node.source_range.start_pos.line
    add_lint line,
             'Declaration should not have a space before '                   'the terminating semicolon'
  elsif !ends_with_semicolon?(node)
    line = node.source_range.start_pos.line
    add_lint line, 'Declaration should be terminated by a semicolon'
  elsif ends_with_multiple_semicolons?(node)
    line = node.source_range.start_pos.line
    add_lint line, 'Declaration should be terminated by a single semicolon'
  end
end
ends_with_multiple_semicolons?(node) click to toggle source
# File lib/scss_lint/linter/trailing_semicolon.rb, line 87
def ends_with_multiple_semicolons?(node)
  # Look one character past the end to see if there's another semicolon
  character_at(node.source_range.end_pos) == ';' &&
    character_at(node.source_range.end_pos, 1) == ';'
end
ends_with_semicolon?(node) click to toggle source

Checks that the node is ended by a semicolon (with no whitespace)

# File lib/scss_lint/linter/trailing_semicolon.rb, line 70
def ends_with_semicolon?(node)
  semicolon_after_parenthesis?(node) ||
    # Otherwise just check for a semicolon
    source_from_range(node.source_range) =~ /;(\s*})?$/
end
has_space_before_semicolon?(node) click to toggle source
# File lib/scss_lint/linter/trailing_semicolon.rb, line 93
def has_space_before_semicolon?(node)
  source_from_range(node.source_range) =~ /\s;(\s*})?$/
end
semicolon_after_parenthesis?(node) click to toggle source

Special case: Sass doesn't include the semicolon after an expression in the source range it reports, so we need a helper to check after the reported range.

# File lib/scss_lint/linter/trailing_semicolon.rb, line 79
def semicolon_after_parenthesis?(node)
  last_char = character_at(node.source_range.end_pos)
  char_after = character_at(node.source_range.end_pos, 1)
  (last_char == ')' && char_after == ';') ||
    ([last_char, char_after].include?("\n") &&
     engine.lines[node.source_range.end_pos.line] =~ /\);(\s*})?$/)
end