sass 3.1.12 → 3.1.13
Sign up to get free protection for your applications and to get access to all the features.
- data/VERSION +1 -1
- data/lib/sass/cache_stores/filesystem.rb +1 -0
- data/lib/sass/css.rb +0 -1
- data/lib/sass/engine.rb +4 -5
- data/lib/sass/script.rb +0 -1
- data/lib/sass/script/funcall.rb +2 -2
- data/lib/sass/script/lexer.rb +1 -3
- data/lib/sass/script/literal.rb +0 -20
- data/lib/sass/script/operation.rb +8 -0
- data/lib/sass/scss/parser.rb +2 -3
- data/lib/sass/selector.rb +15 -3
- data/lib/sass/selector/sequence.rb +65 -5
- data/lib/sass/selector/simple.rb +1 -1
- data/lib/sass/shared.rb +2 -4
- data/lib/sass/tree/visitors/convert.rb +2 -1
- data/lib/sass/tree/visitors/cssize.rb +20 -6
- data/lib/sass/tree/visitors/perform.rb +11 -0
- data/lib/sass/tree/visitors/to_css.rb +11 -3
- data/lib/sass/util.rb +34 -1
- data/lib/sass/util/multibyte_string_scanner.rb +134 -0
- data/test/sass/engine_test.rb +65 -6
- data/test/sass/extend_test.rb +88 -0
- data/test/sass/more_results/more_import.css +2 -2
- data/test/sass/results/import.css +2 -2
- data/test/sass/results/import_charset.css +1 -0
- data/test/sass/results/import_charset_1_8.css +1 -0
- data/test/sass/results/import_charset_ibm866.css +1 -0
- data/test/sass/results/scss_import.css +2 -2
- data/test/sass/script_test.rb +5 -0
- data/test/sass/scss/scss_test.rb +3 -3
- data/test/sass/templates/import_charset.sass +2 -0
- data/test/sass/templates/import_charset_1_8.sass +2 -0
- data/test/sass/templates/import_charset_ibm866.sass +2 -0
- data/test/sass/util/multibyte_string_scanner_test.rb +147 -0
- data/test/sass/util_test.rb +6 -0
- metadata +7 -4
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
3.1.
|
1
|
+
3.1.13
|
data/lib/sass/css.rb
CHANGED
data/lib/sass/engine.rb
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
require 'strscan'
|
2
1
|
require 'set'
|
3
2
|
require 'digest/sha1'
|
4
3
|
require 'sass/cache_stores'
|
@@ -219,7 +218,7 @@ module Sass
|
|
219
218
|
# If you're compiling a single Sass file from the filesystem,
|
220
219
|
# use \{Sass::Engine.for\_file}.
|
221
220
|
# If you're compiling multiple files from the filesystem,
|
222
|
-
# use {Sass::Plugin.
|
221
|
+
# use {Sass::Plugin}.
|
223
222
|
#
|
224
223
|
# @param template [String] The Sass template.
|
225
224
|
# This template can be encoded using any encoding
|
@@ -565,7 +564,7 @@ WARNING
|
|
565
564
|
end
|
566
565
|
|
567
566
|
def parse_property_or_rule(line)
|
568
|
-
scanner =
|
567
|
+
scanner = Sass::Util::MultibyteStringScanner.new(line.text)
|
569
568
|
hack_char = scanner.scan(/[:\*\.]|\#(?!\{)/)
|
570
569
|
parser = Sass::SCSS::SassParser.new(scanner, @options[:filename], @line)
|
571
570
|
|
@@ -749,7 +748,7 @@ WARNING
|
|
749
748
|
raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath import directives.",
|
750
749
|
:line => @line + 1) unless line.children.empty?
|
751
750
|
|
752
|
-
scanner =
|
751
|
+
scanner = Sass::Util::MultibyteStringScanner.new(value)
|
753
752
|
values = []
|
754
753
|
|
755
754
|
loop do
|
@@ -783,7 +782,7 @@ WARNING
|
|
783
782
|
elsif uri
|
784
783
|
Tree::DirectiveNode.new("@import #{uri}")
|
785
784
|
elsif val =~ /^http:\/\//
|
786
|
-
Tree::DirectiveNode.new("@import
|
785
|
+
Tree::DirectiveNode.new("@import #{str}")
|
787
786
|
else
|
788
787
|
Tree::ImportNode.new(val)
|
789
788
|
end
|
data/lib/sass/script.rb
CHANGED
data/lib/sass/script/funcall.rb
CHANGED
@@ -36,7 +36,7 @@ module Sass
|
|
36
36
|
# @return [String] A string representation of the function call
|
37
37
|
def inspect
|
38
38
|
args = @args.map {|a| a.inspect}.join(', ')
|
39
|
-
keywords = @keywords.
|
39
|
+
keywords = Sass::Util.hash_to_a(@keywords).
|
40
40
|
map {|k, v| "$#{k}: #{v.inspect}"}.join(', ')
|
41
41
|
"#{name}(#{args}#{', ' unless args.empty? || keywords.empty?}#{keywords})"
|
42
42
|
end
|
@@ -44,7 +44,7 @@ module Sass
|
|
44
44
|
# @see Node#to_sass
|
45
45
|
def to_sass(opts = {})
|
46
46
|
args = @args.map {|a| a.to_sass(opts)}.join(', ')
|
47
|
-
keywords = @keywords.
|
47
|
+
keywords = Sass::Util.hash_to_a(@keywords).
|
48
48
|
map {|k, v| "$#{dasherize(k, opts)}: #{v.to_sass(opts)}"}.join(', ')
|
49
49
|
"#{dasherize(name, opts)}(#{args}#{', ' unless args.empty? || keywords.empty?}#{keywords})"
|
50
50
|
end
|
data/lib/sass/script/lexer.rb
CHANGED
@@ -1,7 +1,5 @@
|
|
1
1
|
require 'sass/scss/rx'
|
2
2
|
|
3
|
-
require 'strscan'
|
4
|
-
|
5
3
|
module Sass
|
6
4
|
module Script
|
7
5
|
# The lexical analyzer for SassScript.
|
@@ -126,7 +124,7 @@ module Sass
|
|
126
124
|
# @param options [{Symbol => Object}] An options hash;
|
127
125
|
# see {file:SASS_REFERENCE.md#sass_options the Sass options documentation}
|
128
126
|
def initialize(str, line, offset, options)
|
129
|
-
@scanner = str.is_a?(StringScanner) ? str :
|
127
|
+
@scanner = str.is_a?(StringScanner) ? str : Sass::Util::MultibyteStringScanner.new(str)
|
130
128
|
@line = line
|
131
129
|
@offset = offset
|
132
130
|
@options = options
|
data/lib/sass/script/literal.rb
CHANGED
@@ -55,26 +55,6 @@ The #options attribute is not set on this #{self.class}.
|
|
55
55
|
MSG
|
56
56
|
end
|
57
57
|
|
58
|
-
# The SassScript `and` operation.
|
59
|
-
#
|
60
|
-
# @param other [Literal] The right-hand side of the operator
|
61
|
-
# @return [Literal] The result of a logical and:
|
62
|
-
# `other` if this literal isn't a false {Bool},
|
63
|
-
# and this literal otherwise
|
64
|
-
def and(other)
|
65
|
-
to_bool ? other : self
|
66
|
-
end
|
67
|
-
|
68
|
-
# The SassScript `or` operation.
|
69
|
-
#
|
70
|
-
# @param other [Literal] The right-hand side of the operator
|
71
|
-
# @return [Literal] The result of the logical or:
|
72
|
-
# this literal if it isn't a false {Bool},
|
73
|
-
# and `other` otherwise
|
74
|
-
def or(other)
|
75
|
-
to_bool ? self : other
|
76
|
-
end
|
77
|
-
|
78
58
|
# The SassScript `==` operation.
|
79
59
|
# **Note that this returns a {Sass::Script::Bool} object,
|
80
60
|
# not a Ruby boolean**.
|
@@ -72,6 +72,14 @@ module Sass::Script
|
|
72
72
|
# @raise [Sass::SyntaxError] if the operation is undefined for the operands
|
73
73
|
def _perform(environment)
|
74
74
|
literal1 = @operand1.perform(environment)
|
75
|
+
|
76
|
+
# Special-case :and and :or to support short-circuiting.
|
77
|
+
if @operator == :and
|
78
|
+
return literal1.to_bool ? @operand2.perform(environment) : literal1
|
79
|
+
elsif @operator == :or
|
80
|
+
return literal1.to_bool ? literal1 : @operand2.perform(environment)
|
81
|
+
end
|
82
|
+
|
75
83
|
literal2 = @operand2.perform(environment)
|
76
84
|
|
77
85
|
begin
|
data/lib/sass/scss/parser.rb
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
require 'strscan'
|
2
1
|
require 'set'
|
3
2
|
|
4
3
|
module Sass
|
@@ -50,7 +49,7 @@ module Sass
|
|
50
49
|
if @template.is_a?(StringScanner)
|
51
50
|
@template
|
52
51
|
else
|
53
|
-
|
52
|
+
Sass::Util::MultibyteStringScanner.new(@template.gsub("\r", ""))
|
54
53
|
end
|
55
54
|
end
|
56
55
|
|
@@ -906,7 +905,7 @@ MESSAGE
|
|
906
905
|
if @throw_err
|
907
906
|
throw :_sass_parser_error, err
|
908
907
|
else
|
909
|
-
@scanner =
|
908
|
+
@scanner = Sass::Util::MultibyteStringScanner.new(@scanner.string)
|
910
909
|
@scanner.pos = err[:pos]
|
911
910
|
@line = err[:line]
|
912
911
|
@expected = err[:expected]
|
data/lib/sass/selector.rb
CHANGED
@@ -287,6 +287,13 @@ module Sass
|
|
287
287
|
# @return [Symbol]
|
288
288
|
attr_reader :type
|
289
289
|
|
290
|
+
# Some psuedo-class-syntax selectors (`:after` and `:before)
|
291
|
+
# are actually considered pseudo-elements
|
292
|
+
# and must be at the end of the selector to function properly.
|
293
|
+
#
|
294
|
+
# @return [Array<String>]
|
295
|
+
FINAL_SELECTORS = %w[after before]
|
296
|
+
|
290
297
|
# The name of the selector.
|
291
298
|
#
|
292
299
|
# @return [Array<String, Sass::Script::Node>]
|
@@ -312,6 +319,10 @@ module Sass
|
|
312
319
|
@arg = arg
|
313
320
|
end
|
314
321
|
|
322
|
+
def final?
|
323
|
+
type == :class && FINAL_SELECTORS.include?(name.first)
|
324
|
+
end
|
325
|
+
|
315
326
|
# @see Selector#to_a
|
316
327
|
def to_a
|
317
328
|
res = [@type == :class ? ":" : "::"] + @name
|
@@ -319,8 +330,8 @@ module Sass
|
|
319
330
|
res
|
320
331
|
end
|
321
332
|
|
322
|
-
# Returns `nil` if this is a
|
323
|
-
# and `sels` contains a
|
333
|
+
# Returns `nil` if this is a pseudoelement selector
|
334
|
+
# and `sels` contains a pseudoelement selector different than this one.
|
324
335
|
#
|
325
336
|
# @see Selector#unify
|
326
337
|
def unify(sels)
|
@@ -328,6 +339,7 @@ module Sass
|
|
328
339
|
sel.is_a?(Pseudo) && sel.type == :element &&
|
329
340
|
(sel.name != self.name || sel.arg != self.arg)
|
330
341
|
end
|
342
|
+
return sels + [self] if final?
|
331
343
|
super
|
332
344
|
end
|
333
345
|
end
|
@@ -346,7 +358,7 @@ module Sass
|
|
346
358
|
attr_reader :selector
|
347
359
|
|
348
360
|
# @param [String] The name of the pseudoclass
|
349
|
-
# @param [Selector::
|
361
|
+
# @param [Selector::CommaSequence] The selector argument
|
350
362
|
def initialize(name, selector)
|
351
363
|
@name = name
|
352
364
|
@selector = selector
|
@@ -134,10 +134,11 @@ module Sass
|
|
134
134
|
last_current.unshift(current.pop)
|
135
135
|
end
|
136
136
|
befores = Sass::Util.flatten(befores.map do |before|
|
137
|
-
subweave(before, current)
|
137
|
+
next [] unless sub = subweave(before, current)
|
138
|
+
sub.map {|seqs| seqs + last_current}
|
138
139
|
end, 1)
|
139
|
-
return befores if afters.empty?
|
140
140
|
end
|
141
|
+
return befores
|
141
142
|
end
|
142
143
|
|
143
144
|
# This interweaves two lists of selectors,
|
@@ -149,14 +150,14 @@ module Sass
|
|
149
150
|
# `.foo .baz .bar .bang`, `.foo .baz .bar.bang`, `.foo .baz .bang .bar`,
|
150
151
|
# and so on until `.baz .bang .foo .bar`.
|
151
152
|
#
|
152
|
-
# @overload def subweave(seq1, seq2)
|
153
153
|
# @param seq1 [Array<SimpleSequence or String>]
|
154
154
|
# @param seq2 [Array<SimpleSequence or String>]
|
155
155
|
# @return [Array<Array<SimpleSequence or String>>]
|
156
|
-
def subweave(seq1, seq2
|
156
|
+
def subweave(seq1, seq2)
|
157
157
|
return [seq2] if seq1.empty?
|
158
158
|
return [seq1] if seq2.empty?
|
159
159
|
|
160
|
+
return unless init = merge_initial_ops(seq1, seq2)
|
160
161
|
seq1 = group_selectors(seq1)
|
161
162
|
seq2 = group_selectors(seq2)
|
162
163
|
lcs = Sass::Util.lcs(seq2, seq1) do |s1, s2|
|
@@ -166,7 +167,7 @@ module Sass
|
|
166
167
|
next s1 if subweave_superselector?(s2, s1)
|
167
168
|
end
|
168
169
|
|
169
|
-
diff = []
|
170
|
+
diff = [[init]]
|
170
171
|
until lcs.empty?
|
171
172
|
diff << chunks(seq1, seq2) {|s| subweave_superselector?(s.first, lcs.first)} << [lcs.shift]
|
172
173
|
seq1.shift
|
@@ -178,6 +179,50 @@ module Sass
|
|
178
179
|
Sass::Util.paths(diff).map {|p| p.flatten}
|
179
180
|
end
|
180
181
|
|
182
|
+
# Extracts initial selector operators (`"+"`, `">"`, `"~"`, and `"\n"`)
|
183
|
+
# from two sequences and merges them together into a single array of
|
184
|
+
# selector operators.
|
185
|
+
#
|
186
|
+
# @param seq1 [Array<SimpleSequence or String>]
|
187
|
+
# @param seq2 [Array<SimpleSequence or String>]
|
188
|
+
# @return [Array<String>, nil] If there are no operators in the merged
|
189
|
+
# sequence, this will be the empty array. If the operators cannot be
|
190
|
+
# merged, this will be nil.
|
191
|
+
def merge_initial_ops(seq1, seq2)
|
192
|
+
ops1, ops2 = [], []
|
193
|
+
ops1 << seq1.shift while seq1.first.is_a?(String)
|
194
|
+
ops2 << seq2.shift while seq2.first.is_a?(String)
|
195
|
+
|
196
|
+
newline = false
|
197
|
+
newline ||= !!ops1.shift if ops1.first == "\n"
|
198
|
+
newline ||= !!ops2.shift if ops2.first == "\n"
|
199
|
+
|
200
|
+
# If neither sequence is a subsequence of the other, they cannot be
|
201
|
+
# merged successfully
|
202
|
+
lcs = Sass::Util.lcs(ops1, ops2)
|
203
|
+
return unless lcs == ops1 || lcs == ops2
|
204
|
+
return (newline ? ["\n"] : []) + (ops1.size > ops2.size ? ops1 : ops2)
|
205
|
+
end
|
206
|
+
|
207
|
+
# Takes initial subsequences of `seq1` and `seq2` and returns all
|
208
|
+
# orderings of those subsequences. The initial subsequences are determined
|
209
|
+
# by a block.
|
210
|
+
#
|
211
|
+
# Destructively removes the initial subsequences of `seq1` and `seq2`.
|
212
|
+
#
|
213
|
+
# For example, given `(A B C | D E)` and `(1 2 | 3 4 5)` (with `|`
|
214
|
+
# denoting the boundary of the initial subsequence), this would return
|
215
|
+
# `[(A B C 1 2), (1 2 A B C)]`. The sequences would then be `(D E)` and
|
216
|
+
# `(3 4 5)`.
|
217
|
+
#
|
218
|
+
# @param seq1 [Array]
|
219
|
+
# @param seq2 [Array]
|
220
|
+
# @yield [a] Used to determine when to cut off the initial subsequences.
|
221
|
+
# Called repeatedly for each sequence until it returns true.
|
222
|
+
# @yieldparam a [Array] A final subsequence of one input sequence after
|
223
|
+
# cutting off some initial subsequence.
|
224
|
+
# @yieldreturn [Boolean] Whether or not to cut off the initial subsequence
|
225
|
+
# here.
|
181
226
|
def chunks(seq1, seq2)
|
182
227
|
chunk1 = []
|
183
228
|
chunk1 << seq1.shift until yield seq1
|
@@ -189,6 +234,15 @@ module Sass
|
|
189
234
|
[chunk1 + chunk2, chunk2 + chunk1]
|
190
235
|
end
|
191
236
|
|
237
|
+
# Groups a sequence into subsequences. The subsequences are determined by
|
238
|
+
# strings; adjacent non-string elements will be put into separate groups,
|
239
|
+
# but any element adjacent to a string will be grouped with that string.
|
240
|
+
#
|
241
|
+
# For example, `(A B "C" D E "F" G "H" "I" J)` will become `[(A) (B "C" D)
|
242
|
+
# (E "F" G "H" "I" J)]`.
|
243
|
+
#
|
244
|
+
# @param seq [Array]
|
245
|
+
# @return [Array<Array>]
|
192
246
|
def group_selectors(seq)
|
193
247
|
newseq = []
|
194
248
|
tail = seq.dup
|
@@ -202,6 +256,12 @@ module Sass
|
|
202
256
|
return newseq
|
203
257
|
end
|
204
258
|
|
259
|
+
# Given two sequences of simple selectors, returns whether `sseq1` is a
|
260
|
+
# superselector of `sseq2`.
|
261
|
+
#
|
262
|
+
# @param sseq1 [Array<SimpleSelector or String>]
|
263
|
+
# @param sseq2 [Array<SimpleSelector or String>]
|
264
|
+
# @return [Boolean]
|
205
265
|
def subweave_superselector?(sseq1, sseq2)
|
206
266
|
if sseq1.size > 1
|
207
267
|
# More complex selectors are never superselectors of less complex ones
|
data/lib/sass/selector/simple.rb
CHANGED
@@ -79,7 +79,7 @@ module Sass
|
|
79
79
|
sels_with_ix = Sass::Util.enum_with_index(sels)
|
80
80
|
_, i =
|
81
81
|
if self.is_a?(Pseudo) || self.is_a?(SelectorPseudoClass)
|
82
|
-
sels_with_ix.find {|sel, _| sel.is_a?(Pseudo) && sels.last.type == :element}
|
82
|
+
sels_with_ix.find {|sel, _| sel.is_a?(Pseudo) && (sels.last.final? || sels.last.type == :element)}
|
83
83
|
else
|
84
84
|
sels_with_ix.find {|sel, _| sel.is_a?(Pseudo) || sel.is_a?(SelectorPseudoClass)}
|
85
85
|
end
|
data/lib/sass/shared.rb
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
require 'strscan'
|
2
|
-
|
3
1
|
module Sass
|
4
2
|
# This module contains functionality that's shared between Haml and Sass.
|
5
3
|
module Shared
|
@@ -16,7 +14,7 @@ module Sass
|
|
16
14
|
# @yieldparam scan [StringScanner] The scanner scanning through the string
|
17
15
|
# @return [String] The text remaining in the scanner after all `#{`s have been processed
|
18
16
|
def handle_interpolation(str)
|
19
|
-
scan =
|
17
|
+
scan = Sass::Util::MultibyteStringScanner.new(str)
|
20
18
|
yield scan while scan.scan(/(.*?)(\\*)\#\{/m)
|
21
19
|
scan.rest
|
22
20
|
end
|
@@ -40,7 +38,7 @@ module Sass
|
|
40
38
|
# `["Foo (Bar (Baz bang) bop)", " (Bang (bop bip))"]` in the example above.
|
41
39
|
def balance(scanner, start, finish, count = 0)
|
42
40
|
str = ''
|
43
|
-
scanner =
|
41
|
+
scanner = Sass::Util::MultibyteStringScanner.new(scanner) unless scanner.is_a? StringScanner
|
44
42
|
regexp = Regexp.new("(.*?)[\\#{start.chr}\\#{finish.chr}]", Regexp::MULTILINE)
|
45
43
|
while scanner.scan(regexp)
|
46
44
|
str << scanner.matched
|
@@ -177,7 +177,8 @@ class Sass::Tree::Visitors::Convert < Sass::Tree::Visitors::Base
|
|
177
177
|
def visit_mixin(node)
|
178
178
|
unless node.args.empty? && node.keywords.empty?
|
179
179
|
args = node.args.map {|a| a.to_sass(@options)}.join(", ")
|
180
|
-
keywords = node.keywords
|
180
|
+
keywords = Sass::Util.hash_to_a(node.keywords).
|
181
|
+
map {|k, v| "$#{dasherize(k)}: #{v.to_sass(@options)}"}.join(', ')
|
181
182
|
arglist = "(#{args}#{', ' unless args.empty? || keywords.empty?}#{keywords})"
|
182
183
|
end
|
183
184
|
"#{tab_str}#{@format == :sass ? '+' : '@include '}#{dasherize(node.name)}#{arglist}#{semi}\n"
|
@@ -52,12 +52,26 @@ class Sass::Tree::Visitors::Cssize < Sass::Tree::Visitors::Base
|
|
52
52
|
def visit_root(node)
|
53
53
|
yield
|
54
54
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
55
|
+
if parent.nil?
|
56
|
+
# In Ruby 1.9 we can make all @charset nodes invisible
|
57
|
+
# and infer the final @charset from the encoding of the final string.
|
58
|
+
if Sass::Util.ruby1_8?
|
59
|
+
charset = node.children.find {|c| c.is_a?(Sass::Tree::CharsetNode)}
|
60
|
+
node.children.reject! {|c| c.is_a?(Sass::Tree::CharsetNode)}
|
61
|
+
node.children.unshift charset if charset
|
62
|
+
end
|
63
|
+
|
64
|
+
imports = Sass::Util.extract!(node.children) do |c|
|
65
|
+
c.is_a?(Sass::Tree::DirectiveNode) && c.value =~ /^@import /i
|
66
|
+
end
|
67
|
+
charset_and_index = Sass::Util.ruby1_8? &&
|
68
|
+
node.children.each_with_index.find {|c, _| c.is_a?(Sass::Tree::CharsetNode)}
|
69
|
+
if charset_and_index
|
70
|
+
index = charset_and_index.last
|
71
|
+
node.children = node.children[0..index] + imports + node.children[index+1..-1]
|
72
|
+
else
|
73
|
+
node.children = imports + node.children
|
74
|
+
end
|
61
75
|
end
|
62
76
|
|
63
77
|
return node, @extends
|
@@ -226,6 +226,8 @@ END
|
|
226
226
|
# Runs SassScript interpolation in the selector,
|
227
227
|
# and then parses the result into a {Sass::Selector::CommaSequence}.
|
228
228
|
def visit_rule(node)
|
229
|
+
rule = node.rule
|
230
|
+
rule = rule.map {|e| e.is_a?(String) && e != ' ' ? e.strip : e} if node.style == :compressed
|
229
231
|
parser = Sass::SCSS::StaticParser.new(run_interp(node.rule), node.filename, node.line)
|
230
232
|
node.parsed_rules ||= parser.parse_selector
|
231
233
|
if node.options[:trace_selectors]
|
@@ -270,6 +272,15 @@ END
|
|
270
272
|
|
271
273
|
def visit_directive(node)
|
272
274
|
if node.value['#{']
|
275
|
+
if node.value =~ /^@import (?!url\()/
|
276
|
+
Sass::Util.sass_warn <<WARNING
|
277
|
+
DEPRECATION WARNING on line #{node.line}#{" of #{node.filename}" if node.filename}:
|
278
|
+
@import directives using \#{} interpolation will need to use url() in Sass 3.2.
|
279
|
+
For example:
|
280
|
+
|
281
|
+
@import url("http://\#{$url}/style.css");
|
282
|
+
WARNING
|
283
|
+
end
|
273
284
|
node.value = run_interp(Sass::Engine.parse_interp(node.value, node.line, 0, node.options))
|
274
285
|
end
|
275
286
|
yield
|
@@ -67,8 +67,10 @@ class Sass::Tree::Visitors::ToCss < Sass::Tree::Visitors::Base
|
|
67
67
|
end
|
68
68
|
|
69
69
|
def visit_directive(node)
|
70
|
+
was_in_directive = @in_directive
|
70
71
|
return node.value + ";" unless node.has_children
|
71
72
|
return node.value + " {}" if node.children.empty?
|
73
|
+
@in_directive = @in_directive || !node.is_a?(Sass::Tree::MediaNode)
|
72
74
|
result = if node.style == :compressed
|
73
75
|
"#{node.value}{"
|
74
76
|
else
|
@@ -101,6 +103,8 @@ class Sass::Tree::Visitors::ToCss < Sass::Tree::Visitors::Base
|
|
101
103
|
else
|
102
104
|
(node.style == :expanded ? "\n" : " ") + "}\n"
|
103
105
|
end
|
106
|
+
ensure
|
107
|
+
@in_directive = was_in_directive
|
104
108
|
end
|
105
109
|
|
106
110
|
def visit_media(node)
|
@@ -132,7 +136,11 @@ class Sass::Tree::Visitors::ToCss < Sass::Tree::Visitors::Base
|
|
132
136
|
|
133
137
|
joined_rules = node.resolved_rules.members.map do |seq|
|
134
138
|
rule_part = seq.to_a.join
|
135
|
-
|
139
|
+
if node.style == :compressed
|
140
|
+
rule_part.gsub!(/([^,])\s*\n\s*/m, '\1 ')
|
141
|
+
rule_part.gsub!(/\s*([,+>])\s*/m, '\1')
|
142
|
+
rule_part.strip!
|
143
|
+
end
|
136
144
|
rule_part
|
137
145
|
end.join(rule_separator)
|
138
146
|
|
@@ -144,7 +152,7 @@ class Sass::Tree::Visitors::ToCss < Sass::Tree::Visitors::Base
|
|
144
152
|
old_spaces = ' ' * @tabs
|
145
153
|
spaces = ' ' * (@tabs + 1)
|
146
154
|
if node.style != :compressed
|
147
|
-
if node.options[:debug_info]
|
155
|
+
if node.options[:debug_info] && !@in_directive
|
148
156
|
to_return << visit(debug_info_rule(node.debug_info, node.options)) << "\n"
|
149
157
|
elsif node.options[:trace_selectors]
|
150
158
|
to_return << "#{old_spaces}/* "
|
@@ -190,7 +198,7 @@ class Sass::Tree::Visitors::ToCss < Sass::Tree::Visitors::Base
|
|
190
198
|
|
191
199
|
def debug_info_rule(debug_info, options)
|
192
200
|
node = Sass::Tree::DirectiveNode.new("@media -sass-debug-info")
|
193
|
-
debug_info.map {|k, v| [k.to_s, v.to_s]}.
|
201
|
+
Sass::Util.hash_to_a(debug_info.map {|k, v| [k.to_s, v.to_s]}).each do |k, v|
|
194
202
|
rule = Sass::Tree::RuleNode.new([""])
|
195
203
|
rule.resolved_rules = Sass::Selector::CommaSequence.new(
|
196
204
|
[Sass::Selector::Sequence.new(
|
data/lib/sass/util.rb
CHANGED
@@ -2,7 +2,6 @@ require 'erb'
|
|
2
2
|
require 'set'
|
3
3
|
require 'enumerator'
|
4
4
|
require 'stringio'
|
5
|
-
require 'strscan'
|
6
5
|
require 'rbconfig'
|
7
6
|
|
8
7
|
require 'sass/root'
|
@@ -219,6 +218,20 @@ module Sass
|
|
219
218
|
lcs_backtrace(lcs_table(x, y, &block), x, y, x.size-1, y.size-1, &block)
|
220
219
|
end
|
221
220
|
|
221
|
+
# Converts a Hash to an Array. This is usually identical to `Hash#to_a`,
|
222
|
+
# with the following exceptions:
|
223
|
+
#
|
224
|
+
# * In Ruby 1.8, `Hash#to_a` is not deterministically ordered, but this is.
|
225
|
+
# * In Ruby 1.9 when running tests, this is ordered in the same way it would
|
226
|
+
# be under Ruby 1.8 (sorted key order rather than insertion order).
|
227
|
+
#
|
228
|
+
# @param hash [Hash]
|
229
|
+
# @return [Array]
|
230
|
+
def hash_to_a(hash)
|
231
|
+
return has.to_a unless ruby1_8? || defined?(Test::Unit)
|
232
|
+
return hash.sort_by {|k, v| k}
|
233
|
+
end
|
234
|
+
|
222
235
|
# Returns information about the caller of the previous method.
|
223
236
|
#
|
224
237
|
# @param entry [String] An entry in the `#caller` list, or a similarly formatted string
|
@@ -562,6 +575,24 @@ MSG
|
|
562
575
|
ruby1_8? ? enum.enum_slice(n) : enum.each_slice(n)
|
563
576
|
end
|
564
577
|
|
578
|
+
# Destructively removes all elements from an array that match a block, and
|
579
|
+
# returns the removed elements.
|
580
|
+
#
|
581
|
+
# @param array [Array] The array from which to remove elements.
|
582
|
+
# @yield [el] Called for each element.
|
583
|
+
# @yieldparam el [*] The element to test.
|
584
|
+
# @yieldreturn [Boolean] Whether or not to extract the element.
|
585
|
+
# @return [Array] The extracted elements.
|
586
|
+
def extract!(array)
|
587
|
+
out = []
|
588
|
+
array.reject! do |e|
|
589
|
+
next false unless yield e
|
590
|
+
out << e
|
591
|
+
true
|
592
|
+
end
|
593
|
+
out
|
594
|
+
end
|
595
|
+
|
565
596
|
# Returns the ASCII code of the given character.
|
566
597
|
#
|
567
598
|
# @param c [String] All characters but the first are ignored.
|
@@ -719,3 +750,5 @@ MSG
|
|
719
750
|
end
|
720
751
|
end
|
721
752
|
end
|
753
|
+
|
754
|
+
require 'sass/util/multibyte_string_scanner'
|
@@ -0,0 +1,134 @@
|
|
1
|
+
require 'strscan'
|
2
|
+
|
3
|
+
if Sass::Util.ruby1_8?
|
4
|
+
Sass::Util::MultibyteStringScanner = StringScanner
|
5
|
+
else
|
6
|
+
# A wrapper of the native StringScanner class that works correctly with
|
7
|
+
# multibyte character encodings. The native class deals only in bytes, not
|
8
|
+
# characters, for methods like [#pos] and [#matched_size]. This class deals
|
9
|
+
# only in characters, instead.
|
10
|
+
class Sass::Util::MultibyteStringScanner < StringScanner
|
11
|
+
def self.new(str)
|
12
|
+
return StringScanner.new(str) if str.ascii_only?
|
13
|
+
super
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize(str)
|
17
|
+
super
|
18
|
+
@mb_pos = 0
|
19
|
+
@mb_matched_size = nil
|
20
|
+
@mb_last_pos = nil
|
21
|
+
end
|
22
|
+
|
23
|
+
alias_method :byte_pos, :pos
|
24
|
+
alias_method :byte_matched_size, :matched_size
|
25
|
+
|
26
|
+
def check(pattern); _match super; end
|
27
|
+
def check_until(pattern); _matched super; end
|
28
|
+
def getch; _forward _match super; end
|
29
|
+
def match?(pattern); _size check(pattern); end
|
30
|
+
def matched_size; @mb_matched_size; end
|
31
|
+
def peek(len); string[@mb_pos, len]; end
|
32
|
+
alias_method :peep, :peek
|
33
|
+
def pos; @mb_pos; end
|
34
|
+
alias_method :pointer, :pos
|
35
|
+
def rest_size; rest.size; end
|
36
|
+
def scan(pattern); _forward _match super; end
|
37
|
+
def scan_until(pattern); _forward _matched super; end
|
38
|
+
def skip(pattern); _size scan(pattern); end
|
39
|
+
def skip_until(pattern); _matched _size scan_until(pattern); end
|
40
|
+
|
41
|
+
def get_byte
|
42
|
+
raise "MultibyteStringScanner doesn't support #get_byte."
|
43
|
+
end
|
44
|
+
|
45
|
+
def getbyte
|
46
|
+
raise "MultibyteStringScanner doesn't support #getbyte."
|
47
|
+
end
|
48
|
+
|
49
|
+
def pos=(n)
|
50
|
+
@mb_last_pos = nil
|
51
|
+
|
52
|
+
# We set position kind of a lot during parsing, so we want it to be as
|
53
|
+
# efficient as possible. This is complicated by the fact that UTF-8 is a
|
54
|
+
# variable-length encoding, so it's difficult to find the byte length that
|
55
|
+
# corresponds to a given character length.
|
56
|
+
#
|
57
|
+
# Our heuristic here is to try to count the fewest possible characters. So
|
58
|
+
# if the new position is close to the current one, just count the
|
59
|
+
# characters between the two; if the new position is closer to the
|
60
|
+
# beginning of the string, just count the characters from there.
|
61
|
+
if @mb_pos - n < @mb_pos / 2
|
62
|
+
# New position is close to old position
|
63
|
+
byte_delta = @mb_pos > n ? -string[n...@mb_pos].bytesize : string[@mb_pos...n].bytesize
|
64
|
+
super(byte_pos + byte_delta)
|
65
|
+
else
|
66
|
+
# New position is close to BOS
|
67
|
+
super(string[0...n].bytesize)
|
68
|
+
end
|
69
|
+
@mb_pos = n
|
70
|
+
end
|
71
|
+
|
72
|
+
def reset
|
73
|
+
@mb_pos = 0
|
74
|
+
@mb_matched_size = nil
|
75
|
+
@mb_last_pos = nil
|
76
|
+
super
|
77
|
+
end
|
78
|
+
|
79
|
+
def scan_full(pattern, advance_pointer_p, return_string_p)
|
80
|
+
res = _match super(pattern, advance_pointer_p, true)
|
81
|
+
_forward res if advance_pointer_p
|
82
|
+
return res if return_string_p
|
83
|
+
end
|
84
|
+
|
85
|
+
def search_full(pattern, advance_pointer_p, return_string_p)
|
86
|
+
res = super(pattern, advance_pointer_p, true)
|
87
|
+
_forward res if advance_pointer_p
|
88
|
+
_matched((res if return_string_p))
|
89
|
+
end
|
90
|
+
|
91
|
+
def string=(str)
|
92
|
+
@mb_pos = 0
|
93
|
+
@mb_matched_size = nil
|
94
|
+
@mb_last_pos = nil
|
95
|
+
super
|
96
|
+
end
|
97
|
+
|
98
|
+
def terminate
|
99
|
+
@mb_pos = string.size
|
100
|
+
@mb_matched_size = nil
|
101
|
+
@mb_last_pos = nil
|
102
|
+
super
|
103
|
+
end
|
104
|
+
alias_method :clear, :terminate
|
105
|
+
|
106
|
+
def unscan
|
107
|
+
super
|
108
|
+
@mb_pos = @mb_last_pos
|
109
|
+
@mb_last_pos = @mb_matched_size = nil
|
110
|
+
end
|
111
|
+
|
112
|
+
private
|
113
|
+
|
114
|
+
def _size(str)
|
115
|
+
str && str.size
|
116
|
+
end
|
117
|
+
|
118
|
+
def _match(str)
|
119
|
+
@mb_matched_size = str && str.size
|
120
|
+
str
|
121
|
+
end
|
122
|
+
|
123
|
+
def _matched(res)
|
124
|
+
_match matched
|
125
|
+
res
|
126
|
+
end
|
127
|
+
|
128
|
+
def _forward(str)
|
129
|
+
@mb_last_pos = @mb_pos
|
130
|
+
@mb_pos += str.size if str
|
131
|
+
str
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
data/test/sass/engine_test.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
# -*- coding: utf-8 -*-
|
3
|
-
require 'test_helper'
|
4
|
-
require '
|
3
|
+
require File.dirname(__FILE__) + '/../test_helper'
|
4
|
+
require File.dirname(__FILE__) + '/test_helper'
|
5
5
|
require 'sass/engine'
|
6
6
|
require 'stringio'
|
7
7
|
require 'mock_importer'
|
@@ -608,13 +608,21 @@ CSS
|
|
608
608
|
end
|
609
609
|
|
610
610
|
def test_http_import
|
611
|
-
assert_equal("@import
|
611
|
+
assert_equal("@import \"http://fonts.googleapis.com/css?family=Droid+Sans\";\n",
|
612
612
|
render("@import \"http://fonts.googleapis.com/css?family=Droid+Sans\""))
|
613
613
|
end
|
614
614
|
|
615
|
-
def
|
616
|
-
|
617
|
-
|
615
|
+
def test_import_with_interpolation
|
616
|
+
assert_warning(<<WARNING) do
|
617
|
+
DEPRECATION WARNING on line 2 of test_import_with_interpolation_inline.sass:
|
618
|
+
@import directives using \#{} interpolation will need to use url() in Sass 3.2.
|
619
|
+
For example:
|
620
|
+
|
621
|
+
@import url("http://\#{$url}/style.css");
|
622
|
+
WARNING
|
623
|
+
assert_equal("@import \"http://fonts.googleapis.com/css?family=Droid+Sans\";\n",
|
624
|
+
render("$family: unquote(\"Droid+Sans\")\n@import \"http://fonts.googleapis.com/css?family=\#{$family}\"\n"))
|
625
|
+
end
|
618
626
|
assert_equal("@import url(\"http://fonts.googleapis.com/css?family=Droid+Sans\");\n",
|
619
627
|
render("$family: unquote(\"Droid+Sans\")\n@import url(\"http://fonts.googleapis.com/css?family=\#{$family}\")\n"))
|
620
628
|
end
|
@@ -1006,6 +1014,23 @@ foo
|
|
1006
1014
|
SASS
|
1007
1015
|
end
|
1008
1016
|
|
1017
|
+
def test_debug_info_in_keyframes
|
1018
|
+
assert_equal(<<CSS, render(<<SASS, :debug_info => true))
|
1019
|
+
@-webkit-keyframes warm {
|
1020
|
+
from {
|
1021
|
+
color: black; }
|
1022
|
+
|
1023
|
+
to {
|
1024
|
+
color: red; } }
|
1025
|
+
CSS
|
1026
|
+
@-webkit-keyframes warm
|
1027
|
+
from
|
1028
|
+
color: black
|
1029
|
+
to
|
1030
|
+
color: red
|
1031
|
+
SASS
|
1032
|
+
end
|
1033
|
+
|
1009
1034
|
def test_empty_first_line
|
1010
1035
|
assert_equal("#a {\n b: c; }\n", render("#a\n\n b: c"))
|
1011
1036
|
end
|
@@ -2448,6 +2473,15 @@ CSS
|
|
2448
2473
|
SASS
|
2449
2474
|
end
|
2450
2475
|
|
2476
|
+
def test_selector_compression
|
2477
|
+
assert_equal <<CSS, render(<<SASS, :style => :compressed)
|
2478
|
+
a>b,c+d,:-moz-any(e,f,g){h:i}
|
2479
|
+
CSS
|
2480
|
+
a > b, c + d, :-moz-any(e, f, g)
|
2481
|
+
h: i
|
2482
|
+
SASS
|
2483
|
+
end
|
2484
|
+
|
2451
2485
|
# Encodings
|
2452
2486
|
|
2453
2487
|
unless Sass::Util.ruby1_8?
|
@@ -2560,6 +2594,31 @@ CSS
|
|
2560
2594
|
a: b
|
2561
2595
|
SASS
|
2562
2596
|
end
|
2597
|
+
|
2598
|
+
# Encoding Regression Test
|
2599
|
+
|
2600
|
+
def test_multibyte_prop_name
|
2601
|
+
assert_equal(<<CSS, render(<<SASS))
|
2602
|
+
@charset "UTF-8";
|
2603
|
+
#bar {
|
2604
|
+
cölor: blue; }
|
2605
|
+
CSS
|
2606
|
+
#bar
|
2607
|
+
cölor: blue
|
2608
|
+
SASS
|
2609
|
+
end
|
2610
|
+
|
2611
|
+
def test_multibyte_and_interpolation
|
2612
|
+
assert_equal(<<CSS, render(<<SCSS, :syntax => :scss))
|
2613
|
+
#bar {
|
2614
|
+
background: a 0%; }
|
2615
|
+
CSS
|
2616
|
+
#bar {
|
2617
|
+
//
|
2618
|
+
background: \#{a} 0%;
|
2619
|
+
}
|
2620
|
+
SCSS
|
2621
|
+
end
|
2563
2622
|
end
|
2564
2623
|
|
2565
2624
|
def test_original_filename_set
|
data/test/sass/extend_test.rb
CHANGED
@@ -741,6 +741,22 @@ SCSS
|
|
741
741
|
CSS
|
742
742
|
:foo.baz {a: b}
|
743
743
|
:bar {@extend .baz}
|
744
|
+
SCSS
|
745
|
+
|
746
|
+
assert_equal <<CSS, render(<<SCSS)
|
747
|
+
.baz:foo, :foo:after {
|
748
|
+
a: b; }
|
749
|
+
CSS
|
750
|
+
.baz:foo {a: b}
|
751
|
+
:after {@extend .baz}
|
752
|
+
SCSS
|
753
|
+
|
754
|
+
assert_equal <<CSS, render(<<SCSS)
|
755
|
+
.baz:after, :foo:after {
|
756
|
+
a: b; }
|
757
|
+
CSS
|
758
|
+
.baz:after {a: b}
|
759
|
+
:foo {@extend .baz}
|
744
760
|
SCSS
|
745
761
|
|
746
762
|
assert_equal <<CSS, render(<<SCSS)
|
@@ -1339,6 +1355,78 @@ CSS
|
|
1339
1355
|
SCSS
|
1340
1356
|
end
|
1341
1357
|
|
1358
|
+
# Regression Tests
|
1359
|
+
|
1360
|
+
def test_duplicated_selector_with_newlines
|
1361
|
+
assert_equal(<<CSS, render(<<SCSS))
|
1362
|
+
.example-1-1,
|
1363
|
+
.example-1-2,
|
1364
|
+
.my-page-1 .my-module-1-1,
|
1365
|
+
.example-1-3 {
|
1366
|
+
a: b; }
|
1367
|
+
CSS
|
1368
|
+
.example-1-1,
|
1369
|
+
.example-1-2,
|
1370
|
+
.example-1-3 {
|
1371
|
+
a: b;
|
1372
|
+
}
|
1373
|
+
|
1374
|
+
.my-page-1 .my-module-1-1 {@extend .example-1-2}
|
1375
|
+
SCSS
|
1376
|
+
end
|
1377
|
+
|
1378
|
+
def test_nested_selector_with_child_selector_hack_extendee
|
1379
|
+
assert_equal <<CSS, render(<<SCSS)
|
1380
|
+
> .foo, > foo bar {
|
1381
|
+
a: b; }
|
1382
|
+
CSS
|
1383
|
+
> .foo {a: b}
|
1384
|
+
foo bar {@extend .foo}
|
1385
|
+
SCSS
|
1386
|
+
end
|
1387
|
+
|
1388
|
+
def test_nested_selector_with_child_selector_hack_extender
|
1389
|
+
assert_equal <<CSS, render(<<SCSS)
|
1390
|
+
.foo .bar, > .foo foo bar, > foo .foo bar {
|
1391
|
+
a: b; }
|
1392
|
+
CSS
|
1393
|
+
.foo .bar {a: b}
|
1394
|
+
> foo bar {@extend .bar}
|
1395
|
+
SCSS
|
1396
|
+
end
|
1397
|
+
|
1398
|
+
def test_nested_selector_with_child_selector_hack_extender_and_extendee
|
1399
|
+
assert_equal <<CSS, render(<<SCSS)
|
1400
|
+
> .foo, > foo bar {
|
1401
|
+
a: b; }
|
1402
|
+
CSS
|
1403
|
+
> .foo {a: b}
|
1404
|
+
> foo bar {@extend .foo}
|
1405
|
+
SCSS
|
1406
|
+
end
|
1407
|
+
|
1408
|
+
def test_nested_selector_with_child_selector_hack_extender_and_sibling_selector_extendee
|
1409
|
+
assert_equal <<CSS, render(<<SCSS)
|
1410
|
+
~ .foo {
|
1411
|
+
a: b; }
|
1412
|
+
CSS
|
1413
|
+
~ .foo {a: b}
|
1414
|
+
> foo bar {@extend .foo}
|
1415
|
+
SCSS
|
1416
|
+
end
|
1417
|
+
|
1418
|
+
def test_nested_selector_with_child_selector_hack_extender_and_extendee_and_newline
|
1419
|
+
assert_equal <<CSS, render(<<SCSS)
|
1420
|
+
> .foo, > flip,
|
1421
|
+
> foo bar {
|
1422
|
+
a: b; }
|
1423
|
+
CSS
|
1424
|
+
> .foo {a: b}
|
1425
|
+
flip,
|
1426
|
+
> foo bar {@extend .foo}
|
1427
|
+
SCSS
|
1428
|
+
end
|
1429
|
+
|
1342
1430
|
private
|
1343
1431
|
|
1344
1432
|
def render(sass, options = {})
|
@@ -1,3 +1,5 @@
|
|
1
|
+
@import url(basic.css);
|
2
|
+
@import url(../results/complex.css);
|
1
3
|
imported { otherconst: hello; myconst: goodbye; pre-mixin: here; }
|
2
4
|
|
3
5
|
body { font: Arial; background: blue; }
|
@@ -22,8 +24,6 @@ body { font: Arial; background: blue; }
|
|
22
24
|
#content.user.show #container.top #column.right { width: 600px; }
|
23
25
|
#content.user.show #container.bottom { background: brown; }
|
24
26
|
|
25
|
-
@import url(basic.css);
|
26
|
-
@import url(../results/complex.css);
|
27
27
|
#foo { background-color: #bbaaff; }
|
28
28
|
|
29
29
|
nonimported { myconst: hello; otherconst: goodbye; post-mixin: here; }
|
@@ -1,3 +1,5 @@
|
|
1
|
+
@import url(basic.css);
|
2
|
+
@import url(../results/complex.css);
|
1
3
|
imported { otherconst: hello; myconst: goodbye; pre-mixin: here; }
|
2
4
|
|
3
5
|
body { font: Arial; background: blue; }
|
@@ -24,8 +26,6 @@ body { font: Arial; background: blue; }
|
|
24
26
|
#content.user.show #container.top #column.right { width: 600px; }
|
25
27
|
#content.user.show #container.bottom { background: brown; }
|
26
28
|
|
27
|
-
@import url(basic.css);
|
28
|
-
@import url(../results/complex.css);
|
29
29
|
#foo { background-color: #bbaaff; }
|
30
30
|
|
31
31
|
nonimported { myconst: hello; otherconst: goodbye; post-mixin: here; }
|
@@ -1,3 +1,5 @@
|
|
1
|
+
@import url(basic.css);
|
2
|
+
@import url(../results/complex.css);
|
1
3
|
imported { otherconst: hello; myconst: goodbye; pre-mixin: here; }
|
2
4
|
|
3
5
|
body { font: Arial; background: blue; }
|
@@ -24,8 +26,6 @@ body { font: Arial; background: blue; }
|
|
24
26
|
#content.user.show #container.top #column.right { width: 600px; }
|
25
27
|
#content.user.show #container.bottom { background: brown; }
|
26
28
|
|
27
|
-
@import url(basic.css);
|
28
|
-
@import url(../results/complex.css);
|
29
29
|
#foo { background-color: #bbaaff; }
|
30
30
|
|
31
31
|
nonimported { myconst: hello; otherconst: goodbye; post-mixin: here; }
|
data/test/sass/script_test.rb
CHANGED
@@ -432,6 +432,11 @@ SASS
|
|
432
432
|
assert_raise_message(Sass::SyntaxError, "wrong number of arguments (1 for 0) for `arg-error'") {resolve("arg-error(1)")}
|
433
433
|
end
|
434
434
|
|
435
|
+
def test_boolean_ops_short_circuit
|
436
|
+
assert_equal "false", resolve("$ie and $ie <= 7", {}, env('ie' => Sass::Script::Bool.new(false)))
|
437
|
+
assert_equal "true", resolve("$ie or $undef", {}, env('ie' => Sass::Script::Bool.new(true)))
|
438
|
+
end
|
439
|
+
|
435
440
|
# Regression Tests
|
436
441
|
|
437
442
|
def test_funcall_has_higher_precedence_than_color_name
|
data/test/sass/scss/scss_test.rb
CHANGED
@@ -1224,11 +1224,11 @@ SCSS
|
|
1224
1224
|
|
1225
1225
|
|
1226
1226
|
def test_newlines_removed_from_selectors_when_compressed
|
1227
|
-
assert_equal <<CSS, render(<<SCSS, :style
|
1227
|
+
assert_equal <<CSS, render(<<SCSS, :style => :compressed)
|
1228
1228
|
z a,z b{display:block}
|
1229
1229
|
CSS
|
1230
|
-
a
|
1231
|
-
b {
|
1230
|
+
a
|
1231
|
+
, b {
|
1232
1232
|
z & {
|
1233
1233
|
display: block;
|
1234
1234
|
}
|
@@ -0,0 +1,147 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
require File.dirname(__FILE__) + '/../../test_helper'
|
4
|
+
|
5
|
+
unless Sass::Util.ruby1_8?
|
6
|
+
class MultibyteStringScannerTest < Test::Unit::TestCase
|
7
|
+
def setup
|
8
|
+
@scanner = Sass::Util::MultibyteStringScanner.new("cölorfül")
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_initial
|
12
|
+
assert_scanner_state 0, 0, nil, nil
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_check
|
16
|
+
assert_equal 'cö', @scanner.check(/../)
|
17
|
+
assert_scanner_state 0, 0, 2, 3
|
18
|
+
assert_equal 0, @scanner.pos
|
19
|
+
assert_equal 0, @scanner.pos
|
20
|
+
assert_equal 2, @scanner.matched_size
|
21
|
+
assert_equal 3, @scanner.byte_matched_size
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_check_until
|
25
|
+
assert_equal 'cölorfü', @scanner.check_until(/f./)
|
26
|
+
assert_scanner_state 0, 0, 2, 3
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_getch
|
30
|
+
assert_equal 'c', @scanner.getch
|
31
|
+
assert_equal 'ö', @scanner.getch
|
32
|
+
assert_scanner_state 2, 3, 1, 2
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_match?
|
36
|
+
assert_equal 2, @scanner.match?(/../)
|
37
|
+
assert_scanner_state 0, 0, 2, 3
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_peek
|
41
|
+
assert_equal 'cö', @scanner.peek(2)
|
42
|
+
assert_scanner_state 0, 0, nil, nil
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_rest_size
|
46
|
+
assert_equal 'cö', @scanner.scan(/../)
|
47
|
+
assert_equal 6, @scanner.rest_size
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_scan
|
51
|
+
assert_equal 'cö', @scanner.scan(/../)
|
52
|
+
assert_scanner_state 2, 3, 2, 3
|
53
|
+
end
|
54
|
+
|
55
|
+
def test_scan_until
|
56
|
+
assert_equal 'cölorfü', @scanner.scan_until(/f./)
|
57
|
+
assert_scanner_state 7, 9, 2, 3
|
58
|
+
end
|
59
|
+
|
60
|
+
def test_skip
|
61
|
+
assert_equal 2, @scanner.skip(/../)
|
62
|
+
assert_scanner_state 2, 3, 2, 3
|
63
|
+
end
|
64
|
+
|
65
|
+
def test_skip_until
|
66
|
+
assert_equal 7, @scanner.skip_until(/f./)
|
67
|
+
assert_scanner_state 7, 9, 2, 3
|
68
|
+
end
|
69
|
+
|
70
|
+
def test_set_pos
|
71
|
+
@scanner.pos = 7
|
72
|
+
assert_scanner_state 7, 9, nil, nil
|
73
|
+
@scanner.pos = 6
|
74
|
+
assert_scanner_state 6, 7, nil, nil
|
75
|
+
@scanner.pos = 1
|
76
|
+
assert_scanner_state 1, 1, nil, nil
|
77
|
+
end
|
78
|
+
|
79
|
+
def test_reset
|
80
|
+
@scanner.scan(/../)
|
81
|
+
@scanner.reset
|
82
|
+
assert_scanner_state 0, 0, nil, nil
|
83
|
+
end
|
84
|
+
|
85
|
+
def test_scan_full
|
86
|
+
assert_equal 'cö', @scanner.scan_full(/../, true, true)
|
87
|
+
assert_scanner_state 2, 3, 2, 3
|
88
|
+
|
89
|
+
@scanner.reset
|
90
|
+
assert_equal 'cö', @scanner.scan_full(/../, false, true)
|
91
|
+
assert_scanner_state 0, 0, 2, 3
|
92
|
+
|
93
|
+
@scanner.reset
|
94
|
+
assert_nil @scanner.scan_full(/../, true, false)
|
95
|
+
assert_scanner_state 2, 3, 2, 3
|
96
|
+
|
97
|
+
@scanner.reset
|
98
|
+
assert_nil @scanner.scan_full(/../, false, false)
|
99
|
+
assert_scanner_state 0, 0, 2, 3
|
100
|
+
end
|
101
|
+
|
102
|
+
def test_search_full
|
103
|
+
assert_equal 'cölorfü', @scanner.search_full(/f./, true, true)
|
104
|
+
assert_scanner_state 7, 9, 2, 3
|
105
|
+
|
106
|
+
@scanner.reset
|
107
|
+
assert_equal 'cölorfü', @scanner.search_full(/f./, false, true)
|
108
|
+
assert_scanner_state 0, 0, 2, 3
|
109
|
+
|
110
|
+
@scanner.reset
|
111
|
+
assert_nil @scanner.search_full(/f./, true, false)
|
112
|
+
assert_scanner_state 7, 9, 2, 3
|
113
|
+
|
114
|
+
@scanner.reset
|
115
|
+
assert_nil @scanner.search_full(/f./, false, false)
|
116
|
+
assert_scanner_state 0, 0, 2, 3
|
117
|
+
end
|
118
|
+
|
119
|
+
def test_set_string
|
120
|
+
@scanner.scan(/../)
|
121
|
+
@scanner.string = 'föóbâr'
|
122
|
+
assert_scanner_state 0, 0, nil, nil
|
123
|
+
end
|
124
|
+
|
125
|
+
def test_terminate
|
126
|
+
@scanner.scan(/../)
|
127
|
+
@scanner.terminate
|
128
|
+
assert_scanner_state 8, 10, nil, nil
|
129
|
+
end
|
130
|
+
|
131
|
+
def test_unscan
|
132
|
+
@scanner.scan(/../)
|
133
|
+
@scanner.scan_until(/f./)
|
134
|
+
@scanner.unscan
|
135
|
+
assert_scanner_state 2, 3, nil, nil
|
136
|
+
end
|
137
|
+
|
138
|
+
private
|
139
|
+
|
140
|
+
def assert_scanner_state(pos, byte_pos, matched_size, byte_matched_size)
|
141
|
+
assert_equal pos, @scanner.pos, 'pos'
|
142
|
+
assert_equal byte_pos, @scanner.byte_pos, 'byte_pos'
|
143
|
+
assert_equal matched_size, @scanner.matched_size, 'matched_size'
|
144
|
+
assert_equal byte_matched_size, @scanner.byte_matched_size, 'byte_matched_size'
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
data/test/sass/util_test.rb
CHANGED
@@ -159,6 +159,12 @@ class UtilTest < Test::Unit::TestCase
|
|
159
159
|
enum_cons(%w[foo bar baz], 2).map {|s1, s2| "#{s1}#{s2}"})
|
160
160
|
end
|
161
161
|
|
162
|
+
def test_extract
|
163
|
+
arr = [1, 2, 3, 4, 5]
|
164
|
+
assert_equal([1, 3, 5], extract!(arr) {|e| e % 2 == 1})
|
165
|
+
assert_equal([2, 4], arr)
|
166
|
+
end
|
167
|
+
|
162
168
|
def test_ord
|
163
169
|
assert_equal(102, ord("f"))
|
164
170
|
assert_equal(98, ord("bar"))
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sass
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 25
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 3
|
8
8
|
- 1
|
9
|
-
-
|
10
|
-
version: 3.1.
|
9
|
+
- 13
|
10
|
+
version: 3.1.13
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Nathan Weizenbaum
|
@@ -17,7 +17,7 @@ autorequire:
|
|
17
17
|
bindir: bin
|
18
18
|
cert_chain: []
|
19
19
|
|
20
|
-
date:
|
20
|
+
date: 2012-02-03 00:00:00 -08:00
|
21
21
|
default_executable:
|
22
22
|
dependencies:
|
23
23
|
- !ruby/object:Gem::Dependency
|
@@ -116,6 +116,7 @@ files:
|
|
116
116
|
- lib/sass/root.rb
|
117
117
|
- lib/sass/importers.rb
|
118
118
|
- lib/sass/logger.rb
|
119
|
+
- lib/sass/util/multibyte_string_scanner.rb
|
119
120
|
- lib/sass/util/subset_map.rb
|
120
121
|
- lib/sass/scss.rb
|
121
122
|
- lib/sass/scss/static_parser.rb
|
@@ -257,6 +258,7 @@ files:
|
|
257
258
|
- test/sass/conversion_test.rb
|
258
259
|
- test/sass/script_test.rb
|
259
260
|
- test/sass/util/subset_map_test.rb
|
261
|
+
- test/sass/util/multibyte_string_scanner_test.rb
|
260
262
|
- test/sass/callbacks_test.rb
|
261
263
|
- test/sass/importer_test.rb
|
262
264
|
- test/sass/scss/css_test.rb
|
@@ -356,6 +358,7 @@ test_files:
|
|
356
358
|
- test/sass/conversion_test.rb
|
357
359
|
- test/sass/script_test.rb
|
358
360
|
- test/sass/util/subset_map_test.rb
|
361
|
+
- test/sass/util/multibyte_string_scanner_test.rb
|
359
362
|
- test/sass/callbacks_test.rb
|
360
363
|
- test/sass/importer_test.rb
|
361
364
|
- test/sass/scss/css_test.rb
|