sass-embedded 0.14.0 → 0.16.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/lib/sass/compile_error.rb +1 -10
  3. data/lib/sass/compile_result.rb +1 -5
  4. data/lib/sass/embedded/channel.rb +2 -2
  5. data/lib/sass/embedded/compile_context/function_registry.rb +85 -0
  6. data/lib/sass/embedded/compile_context/importer_registry.rb +99 -0
  7. data/lib/sass/embedded/compile_context/logger_registry.rb +45 -0
  8. data/lib/sass/embedded/compile_context/value_protofier.rb +237 -0
  9. data/lib/sass/embedded/compile_context.rb +31 -238
  10. data/lib/sass/embedded/compiler/requirements.rb +1 -1
  11. data/lib/sass/embedded/compiler.rb +10 -43
  12. data/lib/sass/embedded/{render.rb → legacy.rb} +79 -3
  13. data/lib/sass/embedded/observer.rb +2 -0
  14. data/lib/sass/embedded/protofier.rb +91 -0
  15. data/lib/sass/embedded/structifier.rb +30 -0
  16. data/lib/sass/embedded/varint.rb +33 -0
  17. data/lib/sass/embedded/version.rb +1 -1
  18. data/lib/sass/embedded/version_context.rb +2 -3
  19. data/lib/sass/embedded.rb +39 -54
  20. data/lib/sass/embedded_protocol.rb +0 -6
  21. data/lib/sass/logger/source_location.rb +3 -9
  22. data/lib/sass/logger/source_span.rb +1 -13
  23. data/lib/sass/logger.rb +3 -3
  24. data/lib/sass/script_error.rb +5 -0
  25. data/lib/sass/value/argument_list.rb +24 -0
  26. data/lib/sass/value/boolean.rb +42 -0
  27. data/lib/sass/value/color.rb +213 -0
  28. data/lib/sass/value/function.rb +35 -0
  29. data/lib/sass/value/fuzzy_math.rb +81 -0
  30. data/lib/sass/value/list.rb +60 -0
  31. data/lib/sass/value/map.rb +57 -0
  32. data/lib/sass/value/null.rb +39 -0
  33. data/lib/sass/value/number/unit.rb +186 -0
  34. data/lib/sass/value/number.rb +272 -0
  35. data/lib/sass/value/string.rb +43 -0
  36. data/lib/sass/value.rb +92 -0
  37. data/lib/sass.rb +51 -48
  38. metadata +30 -56
  39. data/lib/sass/embedded/url.rb +0 -35
  40. data/lib/sass/file_importer.rb +0 -10
  41. data/lib/sass/importer.rb +0 -14
  42. data/lib/sass/importer_result.rb +0 -14
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Sass
4
4
  class Embedded
5
- VERSION = '0.14.0'
5
+ VERSION = '0.16.2'
6
6
  end
7
7
  end
@@ -1,8 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative '../embedded_protocol'
4
- require_relative 'observer'
5
-
6
3
  module Sass
7
4
  class Embedded
8
5
  # The {Observer} for {Embedded#info}.
@@ -32,5 +29,7 @@ module Sass
32
29
  end
33
30
  end
34
31
  end
32
+
33
+ private_constant :VersionContext
35
34
  end
36
35
  end
data/lib/sass/embedded.rb CHANGED
@@ -1,19 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'compile_error'
4
- require_relative 'compile_result'
5
- require_relative 'embedded/channel'
6
- require_relative 'embedded/compile_context'
7
- require_relative 'embedded/render'
8
- require_relative 'embedded/url'
9
- require_relative 'embedded/version'
10
- require_relative 'embedded/version_context'
11
- require_relative 'importer_result'
12
- require_relative 'logger'
13
-
14
3
  module Sass
15
4
  # The {Embedded} host for using dart-sass-embedded. Each instance creates
16
- # its own {Channel}.
5
+ # its own communication {Channel} with a dedicated compiler process.
17
6
  #
18
7
  # @example
19
8
  # embedded = Sass::Embedded.new
@@ -48,27 +37,25 @@ module Sass
48
37
 
49
38
  raise ArgumentError, 'path must be set' if path.nil?
50
39
 
51
- message = CompileContext.new(@channel,
52
- path: path,
53
- source: nil,
54
- importer: nil,
55
- load_paths: load_paths,
56
- syntax: nil,
57
- url: nil,
58
- source_map: source_map,
59
- source_map_include_sources: source_map_include_sources,
60
- style: style,
61
- functions: functions,
62
- importers: importers,
63
- alert_color: alert_color,
64
- alert_ascii: alert_ascii,
65
- logger: logger,
66
- quiet_deps: quiet_deps,
67
- verbose: verbose).receive_message
68
-
69
- raise CompileError.from_proto(message.failure) if message.failure
70
-
71
- CompileResult.from_proto(message.success)
40
+ Protofier.from_proto_compile_response(
41
+ CompileContext.new(@channel,
42
+ path: path,
43
+ source: nil,
44
+ importer: nil,
45
+ load_paths: load_paths,
46
+ syntax: nil,
47
+ url: nil,
48
+ source_map: source_map,
49
+ source_map_include_sources: source_map_include_sources,
50
+ style: style,
51
+ functions: functions,
52
+ importers: importers,
53
+ alert_color: alert_color,
54
+ alert_ascii: alert_ascii,
55
+ logger: logger,
56
+ quiet_deps: quiet_deps,
57
+ verbose: verbose).receive_message
58
+ )
72
59
  end
73
60
 
74
61
  # The {Embedded#compile_string} method.
@@ -96,27 +83,25 @@ module Sass
96
83
  verbose: false)
97
84
  raise ArgumentError, 'source must be set' if source.nil?
98
85
 
99
- message = CompileContext.new(@channel,
100
- path: nil,
101
- source: source,
102
- importer: importer,
103
- load_paths: load_paths,
104
- syntax: syntax,
105
- url: url,
106
- source_map: source_map,
107
- source_map_include_sources: source_map_include_sources,
108
- style: style,
109
- functions: functions,
110
- importers: importers,
111
- alert_color: alert_color,
112
- alert_ascii: alert_ascii,
113
- logger: logger,
114
- quiet_deps: quiet_deps,
115
- verbose: verbose).receive_message
116
-
117
- raise CompileError.from_proto(message.failure) if message.failure
118
-
119
- CompileResult.from_proto(message.success)
86
+ Protofier.from_proto_compile_response(
87
+ CompileContext.new(@channel,
88
+ path: nil,
89
+ source: source,
90
+ importer: importer,
91
+ load_paths: load_paths,
92
+ syntax: syntax,
93
+ url: url,
94
+ source_map: source_map,
95
+ source_map_include_sources: source_map_include_sources,
96
+ style: style,
97
+ functions: functions,
98
+ importers: importers,
99
+ alert_color: alert_color,
100
+ alert_ascii: alert_ascii,
101
+ logger: logger,
102
+ quiet_deps: quiet_deps,
103
+ verbose: verbose).receive_message
104
+ )
120
105
  end
121
106
 
122
107
  # The {Embedded#info} method.
@@ -1,9 +1,3 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative '../../ext/sass/embedded_sass_pb'
4
-
5
- module Sass
6
- module EmbeddedProtocol
7
- PROTOCOL_ERROR_ID = 4_294_967_295
8
- end
9
- end
@@ -2,7 +2,9 @@
2
2
 
3
3
  module Sass
4
4
  module Logger
5
- # The {SourceLocation} in {SourceSpan}.
5
+ # A specific location within a source file.
6
+ #
7
+ # This is always associated with a {SourceSpan} which indicates which file it refers to.
6
8
  class SourceLocation
7
9
  attr_reader :offset, :line, :column
8
10
 
@@ -11,14 +13,6 @@ module Sass
11
13
  @line = line
12
14
  @column = column
13
15
  end
14
-
15
- def self.from_proto(source_location)
16
- return nil if source_location.nil?
17
-
18
- SourceLocation.new(source_location.offset,
19
- source_location.line,
20
- source_location.column)
21
- end
22
16
  end
23
17
  end
24
18
  end
@@ -1,10 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'source_location'
4
-
5
3
  module Sass
6
4
  module Logger
7
- # The {SourceSpan} in {CompileError}.
5
+ # A span of text within a source file.
8
6
  class SourceSpan
9
7
  attr_reader :start, :end, :text, :url, :context
10
8
 
@@ -15,16 +13,6 @@ module Sass
15
13
  @url = url == '' ? nil : url
16
14
  @context = context == '' ? nil : context
17
15
  end
18
-
19
- def self.from_proto(source_span)
20
- return nil if source_span.nil?
21
-
22
- SourceSpan.new(SourceLocation.from_proto(source_span.start),
23
- SourceLocation.from_proto(source_span.end),
24
- source_span.text,
25
- source_span.url,
26
- source_span.context)
27
- end
28
16
  end
29
17
  end
30
18
  end
data/lib/sass/logger.rb CHANGED
@@ -1,16 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Sass
4
- # The {Logger} module.
4
+ # A namespace for built-in Loggers.
5
5
  module Logger
6
6
  module_function
7
7
 
8
- # The instance of a silent {Logger}.
8
+ # A Logger that silently ignores all warnings and debug messages.
9
9
  def silent
10
10
  Silent
11
11
  end
12
12
 
13
- # The silent {Logger}.
13
+ # A Logger that silently ignores all warnings and debug messages.
14
14
  module Silent
15
15
  module_function
16
16
 
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sass
4
+ class ScriptError < StandardError; end
5
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sass
4
+ class Value
5
+ # Sass's argument list type.
6
+ #
7
+ # An argument list comes from a rest argument. It's distinct from a normal {List} in that it may contain a keyword
8
+ # map as well as the positional arguments.
9
+ class ArgumentList < Sass::Value::List
10
+ def initialize(contents = [], keywords = {}, separator = ',')
11
+ super(contents, separator: separator)
12
+
13
+ @id = 0
14
+ @keywords_accessed = false
15
+ @keywords = keywords.transform_keys(&:to_s).freeze
16
+ end
17
+
18
+ def keywords
19
+ @keywords_accessed = true
20
+ @keywords
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sass
4
+ class Value
5
+ # Sass's boolean type.
6
+ class Boolean < Sass::Value
7
+ def initialize(value) # rubocop:disable Lint/MissingSuper
8
+ @value = value
9
+ end
10
+
11
+ attr_reader :value
12
+
13
+ alias to_bool value
14
+
15
+ def assert_boolean(_name = nil)
16
+ self
17
+ end
18
+
19
+ def ==(other)
20
+ other.is_a?(Sass::Value::Boolean) && other.value == value
21
+ end
22
+
23
+ def hash
24
+ @hash ||= value.hash
25
+ end
26
+
27
+ def !
28
+ value ? Boolean::FALSE : Boolean::TRUE
29
+ end
30
+
31
+ # Sass's true value.
32
+ TRUE = Boolean.new(true)
33
+
34
+ # Sass's false value.
35
+ FALSE = Boolean.new(false)
36
+
37
+ def self.new(value)
38
+ value ? Boolean::TRUE : Boolean::FALSE
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,213 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sass
4
+ class Value
5
+ # Sass's color type.
6
+ #
7
+ # No matter what representation was originally used to create this color, all of its channels are accessible.
8
+ class Color < Sass::Value
9
+ def initialize(red: nil, green: nil, blue: nil, # rubocop:disable Lint/MissingSuper
10
+ hue: nil, saturation: nil, lightness: nil,
11
+ whiteness: nil, blackness: nil,
12
+ alpha: nil)
13
+ @alpha = alpha.nil? ? 1 : FuzzyMath.assert_between(alpha, 0, 1, 'alpha')
14
+ if red && green && blue
15
+ @red = FuzzyMath.assert_between(FuzzyMath.round(red), 0, 255, 'red')
16
+ @green = FuzzyMath.assert_between(FuzzyMath.round(green), 0, 255, 'green')
17
+ @blue = FuzzyMath.assert_between(FuzzyMath.round(blue), 0, 255, 'blue')
18
+ elsif hue && saturation && lightness
19
+ @hue = hue % 360
20
+ @saturation = FuzzyMath.assert_between(saturation, 0, 100, 'saturation')
21
+ @lightness = FuzzyMath.assert_between(lightness, 0, 100, 'lightness')
22
+ elsif hue && whiteness && blackness
23
+ @hue = hue % 360
24
+ @whiteness = FuzzyMath.assert_between(whiteness, 0, 100, 'whiteness')
25
+ @blackness = FuzzyMath.assert_between(blackness, 0, 100, 'blackness')
26
+ hwb_to_rgb
27
+ @whiteness = @blackness = nil
28
+ else
29
+ raise error 'Invalid Color'
30
+ end
31
+ end
32
+
33
+ def change(red: nil,
34
+ green: nil,
35
+ blue: nil,
36
+ hue: nil,
37
+ saturation: nil,
38
+ lightness: nil,
39
+ whiteness: nil,
40
+ blackness: nil,
41
+ alpha: nil)
42
+ if whiteness || blackness
43
+ Sass::Value::Color.new(hue: hue || self.hue,
44
+ whiteness: whiteness || self.whiteness,
45
+ blackness: blackness || self.blackness,
46
+ alpha: alpha || self.alpha)
47
+ elsif hue || saturation || lightness
48
+ Sass::Value::Color.new(hue: hue || self.hue,
49
+ saturation: saturation || self.saturation,
50
+ lightness: lightness || self.lightness,
51
+ alpha: alpha || self.alpha)
52
+ elsif red || green || blue
53
+ Sass::Value::Color.new(red: red ? FuzzyMath.round(red) : self.red,
54
+ green: green ? FuzzyMath.round(green) : self.green,
55
+ blue: blue ? FuzzyMath.round(blue) : self.blue,
56
+ alpha: alpha || self.alpha)
57
+ else
58
+ dup.instance_eval do
59
+ @alpha = FuzzyMath.assert_between(alpha, 0, 1, 'alpha')
60
+ self
61
+ end
62
+ end
63
+ end
64
+
65
+ def red
66
+ hsl_to_rgb if @red.nil?
67
+
68
+ @red
69
+ end
70
+
71
+ def green
72
+ hsl_to_rgb if @green.nil?
73
+
74
+ @green
75
+ end
76
+
77
+ def blue
78
+ hsl_to_rgb if @blue.nil?
79
+
80
+ @blue
81
+ end
82
+
83
+ def hue
84
+ rgb_to_hsl if @hue.nil?
85
+
86
+ @hue
87
+ end
88
+
89
+ def saturation
90
+ rgb_to_hsl if @saturation.nil?
91
+
92
+ @saturation
93
+ end
94
+
95
+ def lightness
96
+ rgb_to_hsl if @lightness.nil?
97
+
98
+ @lightness
99
+ end
100
+
101
+ def whiteness
102
+ @whiteness ||= Rational([red, green, blue].min, 255) * 100
103
+ end
104
+
105
+ def blackness
106
+ @blackness ||= 100 - (Rational([red, green, blue].max, 255) * 100)
107
+ end
108
+
109
+ attr_reader :alpha
110
+
111
+ def assert_color(_name = nil)
112
+ self
113
+ end
114
+
115
+ def ==(other)
116
+ other.is_a?(Sass::Value::Color) &&
117
+ other.red == red &&
118
+ other.green == green &&
119
+ other.blue == blue &&
120
+ other.alpha == alpha
121
+ end
122
+
123
+ def hash
124
+ @hash ||= red.hash ^ green.hash ^ blue.hash ^ alpha.hash
125
+ end
126
+
127
+ private
128
+
129
+ def rgb_to_hsl
130
+ scaled_red = Rational(red, 255)
131
+ scaled_green = Rational(green, 255)
132
+ scaled_blue = Rational(blue, 255)
133
+
134
+ max = [scaled_red, scaled_green, scaled_blue].max
135
+ min = [scaled_red, scaled_green, scaled_blue].min
136
+ delta = max - min
137
+
138
+ if max == min
139
+ @hue = 0
140
+ elsif max == scaled_red
141
+ @hue = (60 * (scaled_green - scaled_blue) / delta) % 360
142
+ elsif max == scaled_green
143
+ @hue = (120 + (60 * (scaled_blue - scaled_red) / delta)) % 360
144
+ elsif max == scaled_blue
145
+ @hue = (240 + (60 * (scaled_red - scaled_green) / delta)) % 360
146
+ end
147
+
148
+ lightness = @lightness = 50 * (max + min)
149
+
150
+ @saturation = if max == min
151
+ 0
152
+ elsif lightness < 50
153
+ 100 * delta / (max + min)
154
+ else
155
+ 100 * delta / (2 - max - min)
156
+ end
157
+ end
158
+
159
+ def hsl_to_rgb
160
+ scaled_hue = Rational(hue, 360)
161
+ scaled_saturation = Rational(saturation, 100)
162
+ scaled_lightness = Rational(lightness, 100)
163
+
164
+ tmp2 = if scaled_lightness <= 0.5
165
+ scaled_lightness * (scaled_saturation + 1)
166
+ else
167
+ scaled_lightness + scaled_saturation - (scaled_lightness * scaled_saturation)
168
+ end
169
+ tmp1 = (scaled_lightness * 2) - tmp2
170
+ @red = FuzzyMath.round(hsl_hue_to_rgb(tmp1, tmp2, scaled_hue + Rational(1, 3)) * 255)
171
+ @green = FuzzyMath.round(hsl_hue_to_rgb(tmp1, tmp2, scaled_hue) * 255)
172
+ @blue = FuzzyMath.round(hsl_hue_to_rgb(tmp1, tmp2, scaled_hue - Rational(1, 3)) * 255)
173
+ end
174
+
175
+ def hsl_hue_to_rgb(tmp1, tmp2, hue)
176
+ hue += 1 if hue.negative?
177
+ hue -= 1 if hue > 1
178
+
179
+ if hue < Rational(1, 6)
180
+ tmp1 + ((tmp2 - tmp1) * hue * 6)
181
+ elsif hue < Rational(1, 2)
182
+ tmp2
183
+ elsif hue < Rational(2, 3)
184
+ tmp1 + ((tmp2 - tmp1) * (Rational(2, 3) - hue) * 6)
185
+ else
186
+ tmp1
187
+ end
188
+ end
189
+
190
+ def hwb_to_rgb
191
+ scaled_hue = Rational(hue, 360)
192
+ scaled_whiteness = Rational(whiteness, 100)
193
+ scaled_blackness = Rational(blackness, 100)
194
+
195
+ sum = scaled_whiteness + scaled_blackness
196
+ if sum > 1
197
+ scaled_whiteness /= sum
198
+ scaled_blackness /= sum
199
+ end
200
+
201
+ factor = 1 - scaled_whiteness - scaled_blackness
202
+ @red = hwb_hue_to_rgb(factor, scaled_whiteness, scaled_hue + Rational(1, 3))
203
+ @green = hwb_hue_to_rgb(factor, scaled_whiteness, scaled_hue)
204
+ @blue = hwb_hue_to_rgb(factor, scaled_whiteness, scaled_hue - Rational(1, 3))
205
+ end
206
+
207
+ def hwb_hue_to_rgb(factor, scaled_whiteness, scaled_hue)
208
+ channel = (hsl_hue_to_rgb(0, 1, scaled_hue) * factor) + scaled_whiteness
209
+ FuzzyMath.round(channel * 255)
210
+ end
211
+ end
212
+ end
213
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sass
4
+ class Value
5
+ # Sass's function type.
6
+ class Function < Sass::Value
7
+ def initialize(id_or_signature, callback = nil) # rubocop:disable Lint/MissingSuper
8
+ if id_or_signature.is_a? Numeric
9
+ @id = id_or_signature
10
+ else
11
+ @signature = id_or_signature
12
+ @callback = callback
13
+ end
14
+ end
15
+
16
+ attr_reader :id, :signature, :callback
17
+
18
+ def assert_function(_name = nil)
19
+ self
20
+ end
21
+
22
+ def ==(other)
23
+ if id.nil?
24
+ other.equal? self
25
+ else
26
+ other.is_a?(Sass::Value::Function) && other.id == id
27
+ end
28
+ end
29
+
30
+ def hash
31
+ id.nil? ? signature.hash : id.hash
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sass
4
+ class Value
5
+ # Sass's {FuzzyMath} module.
6
+ module FuzzyMath
7
+ PRECISION = 10
8
+
9
+ EPSILON = 10.pow(-PRECISION - 1)
10
+
11
+ INVERSE_EPSILON = 1 / EPSILON
12
+
13
+ module_function
14
+
15
+ def equals(number1, number2)
16
+ (number1 - number2).abs < EPSILON
17
+ end
18
+
19
+ def less_than(number1, number2)
20
+ number1 < number2 && !equals(number1, number2)
21
+ end
22
+
23
+ def less_than_or_equals(number1, number2)
24
+ number1 < number2 || equals(number1, number2)
25
+ end
26
+
27
+ def greater_than(number1, number2)
28
+ number1 > number2 && !equals(number1, number2)
29
+ end
30
+
31
+ def greater_than_or_equals(number1, number2)
32
+ number1 > number2 || equals(number1, number2)
33
+ end
34
+
35
+ def integer?(number)
36
+ return false unless number.finite?
37
+ return true if number.integer?
38
+
39
+ equals((number - 0.5).abs % 1, 0.5)
40
+ end
41
+
42
+ def to_i(number)
43
+ integer?(number) ? number.round : nil
44
+ end
45
+
46
+ def round(number)
47
+ if number.positive?
48
+ less_than(number % 1, 0.5) ? number.floor : number.ceil
49
+ else
50
+ less_than_or_equals(number % 1, 0.5) ? number.floor : number.ceil
51
+ end
52
+ end
53
+
54
+ def between(number, min, max)
55
+ return min if equals(number, min)
56
+ return max if equals(number, max)
57
+ return number if number > min && number < max
58
+
59
+ nil
60
+ end
61
+
62
+ def assert_between(number, min, max, name)
63
+ result = between(number, min, max)
64
+ return result unless result.nil?
65
+
66
+ message = "#{number} must be between #{min} and #{max}."
67
+ raise Sass::ScriptError, name.nil? ? message : "$#{name}: #{message}"
68
+ end
69
+
70
+ def hash(number)
71
+ @hash ||= if number.finite?
72
+ (number * INVERSE_EPSILON).round.hash
73
+ else
74
+ number.hash
75
+ end
76
+ end
77
+ end
78
+
79
+ private_constant :FuzzyMath
80
+ end
81
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sass
4
+ class Value
5
+ # Sass's list type.
6
+ class List < Sass::Value
7
+ def initialize(contents = [], separator: ',', bracketed: false) # rubocop:disable Lint/MissingSuper
8
+ if separator.nil? && contents.length > 1
9
+ raise error 'A list with more than one element must have an explicit separator'
10
+ end
11
+
12
+ @contents = contents.freeze
13
+ @separator = separator.freeze
14
+ @bracketed = bracketed.freeze
15
+ end
16
+
17
+ attr_reader :contents, :separator
18
+
19
+ alias to_a contents
20
+
21
+ def bracketed?
22
+ @bracketed
23
+ end
24
+
25
+ def assert_map(name = nil)
26
+ to_a.empty? ? Sass::Value::Map.new({}) : super.assert_map(name)
27
+ end
28
+
29
+ def to_map
30
+ to_a.empty? ? Sass::Value::Map.new({}) : nil
31
+ end
32
+
33
+ def at(index)
34
+ index = index.floor
35
+ index = to_a.length + index if index.negative?
36
+ return nil if index.negative? || index >= to_a.length
37
+
38
+ to_a[index]
39
+ end
40
+
41
+ def ==(other)
42
+ (other.is_a?(Sass::Value::List) &&
43
+ other.contents == contents &&
44
+ other.separator == separator &&
45
+ other.bracketed? == bracketed?) ||
46
+ (to_a.empty? && other.is_a?(Sass::Value::Map) && other.to_a.empty?)
47
+ end
48
+
49
+ def hash
50
+ @hash ||= contents.hash
51
+ end
52
+
53
+ private
54
+
55
+ def to_a_length
56
+ to_a.length
57
+ end
58
+ end
59
+ end
60
+ end