blob: 689fdd0e0d33d5cea56e3b49e8a14480db114c25 [file] [log] [blame]
module Sass::Script::Value
# A SassScript object representing a CSS list.
# This includes both comma-separated lists and space-separated lists.
class List < Base
# The Ruby array containing the contents of the list.
#
# @return [Array<Value>]
attr_reader :value
alias_method :to_a, :value
# The operator separating the values of the list.
# Either `:comma` or `:space`.
#
# @return [Symbol]
attr_reader :separator
# Whether the list is surrounded by square brackets.
#
# @return [Boolean]
attr_reader :bracketed
# Creates a new list.
#
# @param value [Array<Value>] See \{#value}
# @param separator [Symbol] See \{#separator}
# @param bracketed [Boolean] See \{#bracketed}
def initialize(value, separator: nil, bracketed: false)
super(value)
@separator = separator
@bracketed = bracketed
end
# @see Value#options=
def options=(options)
super
value.each {|v| v.options = options}
end
# @see Value#eq
def eq(other)
Sass::Script::Value::Bool.new(
other.is_a?(List) && value == other.value &&
separator == other.separator && bracketed == other.bracketed)
end
def hash
@hash ||= [value, separator, bracketed].hash
end
# @see Value#to_s
def to_s(opts = {})
if !bracketed && value.empty?
raise Sass::SyntaxError.new("#{inspect} isn't a valid CSS value.")
end
members = value.
reject {|e| e.is_a?(Null) || e.is_a?(List) && e.value.empty?}.
map {|e| e.to_s(opts)}
contents = members.join(sep_str)
bracketed ? "[#{contents}]" : contents
end
# @see Value#to_sass
def to_sass(opts = {})
return bracketed ? "[]" : "()" if value.empty?
members = value.map do |v|
if element_needs_parens?(v)
"(#{v.to_sass(opts)})"
else
v.to_sass(opts)
end
end
if separator == :comma && members.length == 1
return "#{bracketed ? '[' : '('}#{members.first},#{bracketed ? ']' : ')'}"
end
contents = members.join(sep_str(nil))
bracketed ? "[#{contents}]" : contents
end
# @see Value#to_h
def to_h
return {} if value.empty?
super
end
# @see Value#inspect
def inspect
(bracketed ? '[' : '(') +
value.map {|e| e.inspect}.join(sep_str(nil)) +
(bracketed ? ']' : ')')
end
# Asserts an index is within the list.
#
# @private
#
# @param list [Sass::Script::Value::List] The list for which the index should be checked.
# @param n [Sass::Script::Value::Number] The index being checked.
def self.assert_valid_index(list, n)
if !n.int? || n.to_i == 0
raise ArgumentError.new("List index #{n} must be a non-zero integer")
elsif list.to_a.size == 0
raise ArgumentError.new("List index is #{n} but list has no items")
elsif n.to_i.abs > (size = list.to_a.size)
raise ArgumentError.new(
"List index is #{n} but list is only #{size} item#{'s' if size != 1} long")
end
end
private
def element_needs_parens?(element)
if element.is_a?(List)
return false if element.value.length < 2
return false if element.bracketed
precedence = Sass::Script::Parser.precedence_of(separator || :space)
return Sass::Script::Parser.precedence_of(element.separator || :space) <= precedence
end
return false unless separator == :space
return false unless element.is_a?(Sass::Script::Tree::UnaryOperation)
element.operator == :minus || element.operator == :plus
end
def sep_str(opts = options)
return ' ' if separator == :space
return ',' if opts && opts[:style] == :compressed
', '
end
end
end