haml 3.0.0.rc.4 → 3.0.0.rc.5
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of haml might be problematic. Click here for more details.
- data/Rakefile +2 -1
- data/VERSION +1 -1
- data/lib/haml/util.rb +51 -0
- data/lib/sass/script/string.rb +13 -17
- data/lib/sass/scss/parser.rb +12 -1
- data/lib/sass/selector/abstract_sequence.rb +22 -0
- data/lib/sass/selector/comma_sequence.rb +4 -9
- data/lib/sass/selector/sequence.rb +66 -42
- data/lib/sass/selector/simple.rb +3 -2
- data/lib/sass/selector/simple_sequence.rb +5 -11
- data/lib/sass/tree/import_node.rb +5 -0
- data/lib/sass/tree/node.rb +0 -11
- data/lib/sass/tree/root_node.rb +18 -3
- data/test/haml/util_test.rb +17 -0
- data/test/sass/engine_test.rb +4 -2
- data/test/sass/extend_test.rb +132 -25
- data/test/sass/less_conversion_test.rb +11 -4
- data/test/sass/scss/scss_test.rb +18 -3
- metadata +3 -3
data/Rakefile
CHANGED
@@ -263,7 +263,8 @@ OPTS
|
|
263
263
|
|
264
264
|
YARD::Rake::YardocTask.new do |t|
|
265
265
|
t.files = FileList.new(scope('lib/**/*.rb')) do |list|
|
266
|
-
list.exclude('lib/haml/template
|
266
|
+
list.exclude('lib/haml/template/patch.rb')
|
267
|
+
list.exclude('lib/haml/template/plugin.rb')
|
267
268
|
list.exclude('lib/haml/railtie.rb')
|
268
269
|
list.exclude('lib/haml/helpers/action_view_mods.rb')
|
269
270
|
list.exclude('lib/haml/helpers/xss_mods.rb')
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
3.0.0.rc.
|
1
|
+
3.0.0.rc.5
|
data/lib/haml/util.rb
CHANGED
@@ -193,6 +193,24 @@ module Haml
|
|
193
193
|
end
|
194
194
|
end
|
195
195
|
|
196
|
+
# Computes a single longest common subsequence for `x` and `y`.
|
197
|
+
# If there are more than one longest common subsequences,
|
198
|
+
# the one returned is that which starts first in `x`.
|
199
|
+
#
|
200
|
+
# @param x [Array]
|
201
|
+
# @param y [Array]
|
202
|
+
# @yield [a, b] An optional block to use in place of a check for equality
|
203
|
+
# between elements of `x` and `y`.
|
204
|
+
# @yieldreturn [Object, nil] If the two values register as equal,
|
205
|
+
# this will return the value to use in the LCS array.
|
206
|
+
# @return [Array] The LCS
|
207
|
+
def lcs(x, y, &block)
|
208
|
+
x = [nil, *x]
|
209
|
+
y = [nil, *y]
|
210
|
+
block ||= proc {|a, b| a == b && a}
|
211
|
+
lcs_backtrace(lcs_table(x, y, &block), x, y, x.size-1, y.size-1, &block)
|
212
|
+
end
|
213
|
+
|
196
214
|
# Returns information about the caller of the previous method.
|
197
215
|
#
|
198
216
|
# @param entry [String] An entry in the `#caller` list, or a similarly formatted string
|
@@ -564,5 +582,38 @@ METHOD
|
|
564
582
|
def static_method_name(name, *vars)
|
565
583
|
"#{name}_#{vars.map {|v| !!v}.join('_')}"
|
566
584
|
end
|
585
|
+
|
586
|
+
private
|
587
|
+
|
588
|
+
# Calculates the memoization table for the Least Common Subsequence algorithm.
|
589
|
+
# Algorithm from [Wikipedia](http://en.wikipedia.org/wiki/Longest_common_subsequence_problem#Computing_the_length_of_the_LCS)
|
590
|
+
def lcs_table(x, y)
|
591
|
+
c = Array.new(x.size) {[]}
|
592
|
+
x.size.times {|i| c[i][0] = 0}
|
593
|
+
y.size.times {|j| c[0][j] = 0}
|
594
|
+
(1...x.size).each do |i|
|
595
|
+
(1...y.size).each do |j|
|
596
|
+
c[i][j] =
|
597
|
+
if yield x[i], y[j]
|
598
|
+
c[i-1][j-1] + 1
|
599
|
+
else
|
600
|
+
[c[i][j-1], c[i-1][j]].max
|
601
|
+
end
|
602
|
+
end
|
603
|
+
end
|
604
|
+
return c
|
605
|
+
end
|
606
|
+
|
607
|
+
# Computes a single longest common subsequence for arrays x and y.
|
608
|
+
# Algorithm from [Wikipedia](http://en.wikipedia.org/wiki/Longest_common_subsequence_problem#Reading_out_an_LCS)
|
609
|
+
def lcs_backtrace(c, x, y, i, j, &block)
|
610
|
+
return [] if i == 0 || j == 0
|
611
|
+
if v = yield(x[i], y[j])
|
612
|
+
return lcs_backtrace(c, x, y, i-1, j-1, &block) << v
|
613
|
+
end
|
614
|
+
|
615
|
+
return lcs_backtrace(c, x, y, i, j-1, &block) if c[i][j-1] > c[i-1][j]
|
616
|
+
return lcs_backtrace(c, x, y, i-1, j, &block)
|
617
|
+
end
|
567
618
|
end
|
568
619
|
end
|
data/lib/sass/script/string.rb
CHANGED
@@ -41,23 +41,8 @@ module Sass::Script
|
|
41
41
|
|
42
42
|
# @see Node#to_s
|
43
43
|
def to_s(opts = {})
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
# @param opts [{Symbol => Object}]
|
48
|
-
# `opts[:type]` -- The type of string to render this as.
|
49
|
-
# `:string`s have double quotes, `:identifier`s do not.
|
50
|
-
# Defaults to `:identifier`.
|
51
|
-
# @see Node#to_sass
|
52
|
-
def to_sass(opts = {})
|
53
|
-
type = opts[:type] || self.type
|
54
|
-
if type == :identifier
|
55
|
-
if context == :equals && self.value !~ Sass::SCSS::RX::URI &&
|
56
|
-
Sass::SCSS::RX.escape_ident(self.value).include?(?\\)
|
57
|
-
return "unquote(#{Sass::Script::String.new(self.value, :string).to_sass})"
|
58
|
-
elsif context == :equals && self.value.size == 0
|
59
|
-
return %q{""}
|
60
|
-
end
|
44
|
+
if self.type == :identifier
|
45
|
+
return %q{""} if context == :equals && self.value.size == 0
|
61
46
|
return self.value.gsub("\n", " ")
|
62
47
|
end
|
63
48
|
|
@@ -67,5 +52,16 @@ module Sass::Script
|
|
67
52
|
return "'#{value}'" unless value.include?("'")
|
68
53
|
"\"#{value.gsub('"', "\\\"")}\"" #'
|
69
54
|
end
|
55
|
+
|
56
|
+
# @see Node#to_sass
|
57
|
+
def to_sass(opts = {})
|
58
|
+
if self.type == :identifier && context == :equals &&
|
59
|
+
self.value !~ Sass::SCSS::RX::URI &&
|
60
|
+
Sass::SCSS::RX.escape_ident(self.value).include?(?\\)
|
61
|
+
return "unquote(#{Sass::Script::String.new(self.value, :string).to_sass})"
|
62
|
+
else
|
63
|
+
return to_s
|
64
|
+
end
|
65
|
+
end
|
70
66
|
end
|
71
67
|
end
|
data/lib/sass/scss/parser.rb
CHANGED
@@ -411,7 +411,18 @@ module Sass
|
|
411
411
|
(tok(/\*/) && Selector::Universal.new(nil))
|
412
412
|
res << v
|
413
413
|
end
|
414
|
-
|
414
|
+
|
415
|
+
if tok?(/&/)
|
416
|
+
begin
|
417
|
+
expected('"{"')
|
418
|
+
rescue Sass::SyntaxError => e
|
419
|
+
e.message << "\n\n" << <<MESSAGE
|
420
|
+
In Sass 3, the parent selector & can only be used where element names are valid,
|
421
|
+
since it could potentially be replaced by an element name.
|
422
|
+
MESSAGE
|
423
|
+
raise e
|
424
|
+
end
|
425
|
+
end
|
415
426
|
|
416
427
|
Selector::SimpleSequence.new(res)
|
417
428
|
end
|
@@ -35,6 +35,28 @@ module Sass
|
|
35
35
|
members.each {|m| m.filename = filename}
|
36
36
|
@filename = filename
|
37
37
|
end
|
38
|
+
|
39
|
+
# Returns a hash code for this sequence.
|
40
|
+
#
|
41
|
+
# Subclasses should define `#_hash` rather than overriding this method,
|
42
|
+
# which automatically handles memoizing the result.
|
43
|
+
#
|
44
|
+
# @return [Fixnum]
|
45
|
+
def hash
|
46
|
+
@_hash ||= _hash
|
47
|
+
end
|
48
|
+
|
49
|
+
# Checks equality between this and another object.
|
50
|
+
#
|
51
|
+
# Subclasses should define `#_eql?` rather than overriding this method,
|
52
|
+
# which handles checking class equality and hash equality.
|
53
|
+
#
|
54
|
+
# @param other [Object] The object to test equality against
|
55
|
+
# @return [Boolean] Whether or not this is equal to `other`
|
56
|
+
def eql?(other)
|
57
|
+
other.class == self.class && other.hash == self.hash && _eql?(other)
|
58
|
+
end
|
59
|
+
alias_method :==, :eql?
|
38
60
|
end
|
39
61
|
end
|
40
62
|
end
|
@@ -61,18 +61,13 @@ module Sass
|
|
61
61
|
members.map {|m| m.inspect}.join(", ")
|
62
62
|
end
|
63
63
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
def hash
|
64
|
+
private
|
65
|
+
|
66
|
+
def _hash
|
68
67
|
members.hash
|
69
68
|
end
|
70
69
|
|
71
|
-
|
72
|
-
#
|
73
|
-
# @param other [Object] The object to test equality against
|
74
|
-
# @return [Boolean] Whether or not this is equal to `other`
|
75
|
-
def eql?(other)
|
70
|
+
def _eql?(other)
|
76
71
|
other.class == self.class && other.members.eql?(self.members)
|
77
72
|
end
|
78
73
|
end
|
@@ -118,22 +118,6 @@ module Sass
|
|
118
118
|
members.map {|m| m.inspect}.join(" ")
|
119
119
|
end
|
120
120
|
|
121
|
-
# Returns a hash code for this sequence.
|
122
|
-
#
|
123
|
-
# @return [Fixnum]
|
124
|
-
def hash
|
125
|
-
members.reject {|m| m == "\n"}.hash
|
126
|
-
end
|
127
|
-
|
128
|
-
# Checks equality between this and another object.
|
129
|
-
#
|
130
|
-
# @param other [Object] The object to test equality against
|
131
|
-
# @return [Boolean] Whether or not this is equal to `other`
|
132
|
-
def eql?(other)
|
133
|
-
other.class == self.class &&
|
134
|
-
other.members.reject {|m| m == "\n"}.eql?(self.members.reject {|m| m == "\n"})
|
135
|
-
end
|
136
|
-
|
137
121
|
private
|
138
122
|
|
139
123
|
# Conceptually, this expands "parenthesized selectors".
|
@@ -176,37 +160,77 @@ module Sass
|
|
176
160
|
def subweave(seq1, seq2, cache = {})
|
177
161
|
return [seq2] if seq1.empty?
|
178
162
|
return [seq1] if seq2.empty?
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
163
|
+
|
164
|
+
seq1 = group_selectors(seq1)
|
165
|
+
seq2 = group_selectors(seq2)
|
166
|
+
lcs = Haml::Util.lcs(seq2, seq1) do |s1, s2|
|
167
|
+
next s1 if s1 == s2
|
168
|
+
next unless s1.first.is_a?(SimpleSequence) && s2.first.is_a?(SimpleSequence)
|
169
|
+
next s2 if subweave_superselector?(s1, s2)
|
170
|
+
next s1 if subweave_superselector?(s2, s1)
|
171
|
+
end
|
172
|
+
|
173
|
+
diff = []
|
174
|
+
until lcs.empty?
|
175
|
+
diff << chunks(seq1, seq2) {|s| subweave_superselector?(s.first, lcs.first)} << [lcs.shift]
|
176
|
+
seq1.shift
|
177
|
+
seq2.shift
|
178
|
+
end
|
179
|
+
diff << chunks(seq1, seq2) {|s| s.empty?}
|
180
|
+
diff.reject! {|c| c.empty?}
|
181
|
+
|
182
|
+
Haml::Util.paths(diff).map {|p| p.flatten}
|
183
|
+
end
|
184
|
+
|
185
|
+
def chunks(seq1, seq2)
|
186
|
+
chunk1 = []
|
187
|
+
chunk1 << seq1.shift until yield seq1
|
188
|
+
chunk2 = []
|
189
|
+
chunk2 << seq2.shift until yield seq2
|
190
|
+
return [] if chunk1.empty? && chunk2.empty?
|
191
|
+
return [chunk2] if chunk1.empty?
|
192
|
+
return [chunk1] if chunk2.empty?
|
193
|
+
[chunk1 + chunk2, chunk2 + chunk1]
|
195
194
|
end
|
196
195
|
|
197
|
-
def
|
196
|
+
def group_selectors(seq)
|
197
|
+
newseq = []
|
198
198
|
tail = seq.dup
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
199
|
+
until tail.empty?
|
200
|
+
head = []
|
201
|
+
begin
|
202
|
+
head << tail.shift
|
203
|
+
end while !tail.empty? && head.last.is_a?(String) || tail.first.is_a?(String)
|
204
|
+
newseq << head
|
205
|
+
end
|
206
|
+
return newseq
|
207
|
+
end
|
208
|
+
|
209
|
+
def subweave_superselector?(sseq1, sseq2)
|
210
|
+
if sseq1.size > 1
|
211
|
+
# More complex selectors are never superselectors of less complex ones
|
212
|
+
return unless sseq2.size > 1
|
213
|
+
# .foo ~ .bar is a superselector of .foo + .bar
|
214
|
+
return unless sseq1[1] == "~" ? sseq2[1] != ">" : sseq2[1] == sseq1[1]
|
215
|
+
return unless sseq1.first.superselector?(sseq2.first)
|
216
|
+
return true if sseq1.size == 2
|
217
|
+
return false if sseq2.size == 2
|
218
|
+
return subweave_superselector?(sseq1[2..-1], sseq2[2..-1])
|
219
|
+
elsif sseq2.size > 1
|
220
|
+
return true if sseq2[1] == ">" && sseq1.first.superselector?(sseq2.first)
|
221
|
+
return false if sseq2.size == 2
|
222
|
+
return subweave_superselector?(sseq1, sseq2[2..-1])
|
223
|
+
else
|
224
|
+
sseq1.first.superselector?(sseq2.first)
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
def _hash
|
229
|
+
members.reject {|m| m == "\n"}.hash
|
204
230
|
end
|
205
231
|
|
206
|
-
def
|
207
|
-
|
208
|
-
unified = sseq1.last.unify(sseq2.last.members) unless sseq1.last.is_a?(String) || sseq2.last.is_a?(String)
|
209
|
-
sseq1[0...-1] << unified if unified
|
232
|
+
def _eql?(other)
|
233
|
+
other.members.reject {|m| m == "\n"}.eql?(self.members.reject {|m| m == "\n"})
|
210
234
|
end
|
211
235
|
end
|
212
236
|
end
|
data/lib/sass/selector/simple.rb
CHANGED
@@ -41,7 +41,7 @@ module Sass
|
|
41
41
|
#
|
42
42
|
# @return [Fixnum]
|
43
43
|
def hash
|
44
|
-
to_a.hash
|
44
|
+
@_hash ||= to_a.hash
|
45
45
|
end
|
46
46
|
|
47
47
|
# Checks equality between this and another object.
|
@@ -53,8 +53,9 @@ module Sass
|
|
53
53
|
# @param other [Object] The object to test equality against
|
54
54
|
# @return [Boolean] Whether or not this is equal to `other`
|
55
55
|
def eql?(other)
|
56
|
-
other.class == self.class && other.to_a.eql?(to_a)
|
56
|
+
other.class == self.class && other.hash == self.hash && other.to_a.eql?(to_a)
|
57
57
|
end
|
58
|
+
alias_method :==, :eql?
|
58
59
|
|
59
60
|
# Unifies this selector with a {SimpleSequence}'s {SimpleSequence#members members array},
|
60
61
|
# returning another `SimpleSequence` members array
|
@@ -122,20 +122,14 @@ module Sass
|
|
122
122
|
members.map {|m| m.inspect}.join
|
123
123
|
end
|
124
124
|
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
def hash
|
125
|
+
private
|
126
|
+
|
127
|
+
def _hash
|
129
128
|
[base, Haml::Util.set_hash(rest)].hash
|
130
129
|
end
|
131
130
|
|
132
|
-
|
133
|
-
|
134
|
-
# @param other [Object] The object to test equality against
|
135
|
-
# @return [Boolean] Whether or not this is equal to `other`
|
136
|
-
def eql?(other)
|
137
|
-
other.class == self.class && other.base.eql?(self.base) &&
|
138
|
-
Haml::Util.set_eql?(other.rest, self.rest)
|
131
|
+
def _eql?(other)
|
132
|
+
other.base.eql?(self.base) && Haml::Util.set_eql?(other.rest, self.rest)
|
139
133
|
end
|
140
134
|
end
|
141
135
|
end
|
data/lib/sass/tree/node.rb
CHANGED
@@ -120,17 +120,6 @@ module Sass
|
|
120
120
|
self.class == other.class && other.children == children
|
121
121
|
end
|
122
122
|
|
123
|
-
# Runs the dynamic Sass code *and* computes the CSS for the tree.
|
124
|
-
#
|
125
|
-
# @see #perform
|
126
|
-
# @see #to_s
|
127
|
-
def render
|
128
|
-
extends = Haml::Util::SubsetMap.new
|
129
|
-
result = perform(Environment.new).cssize(extends)
|
130
|
-
result = result.do_extend(extends) unless extends.empty?
|
131
|
-
result.to_s
|
132
|
-
end
|
133
|
-
|
134
123
|
# True if \{#to\_s} will return `nil`;
|
135
124
|
# that is, if the node shouldn't be rendered.
|
136
125
|
# Should only be called in a static tree.
|
data/lib/sass/tree/root_node.rb
CHANGED
@@ -21,6 +21,16 @@ module Sass
|
|
21
21
|
raise e
|
22
22
|
end
|
23
23
|
|
24
|
+
# Runs the dynamic Sass code *and* computes the CSS for the tree.
|
25
|
+
#
|
26
|
+
# @see #perform
|
27
|
+
# @see #to_s
|
28
|
+
def render
|
29
|
+
result, extends = perform(Environment.new).cssize
|
30
|
+
result = result.do_extend(extends) unless extends.empty?
|
31
|
+
result.to_s
|
32
|
+
end
|
33
|
+
|
24
34
|
# @see Node#perform
|
25
35
|
def perform(environment)
|
26
36
|
environment.options = @options if environment.options.nil? || environment.options.empty?
|
@@ -30,9 +40,14 @@ module Sass
|
|
30
40
|
raise e
|
31
41
|
end
|
32
42
|
|
33
|
-
#
|
34
|
-
|
35
|
-
|
43
|
+
# Like {Node#cssize}, except that this method
|
44
|
+
# will create its own `extends` map if necessary,
|
45
|
+
# and it returns that map along with the cssized tree.
|
46
|
+
#
|
47
|
+
# @return [(Tree::Node, Haml::Util::SubsetMap)] The resulting tree of static nodes
|
48
|
+
# *and* the extensions defined for this tree
|
49
|
+
def cssize(extends = Haml::Util::SubsetMap.new, parent = nil)
|
50
|
+
return super(extends), extends
|
36
51
|
rescue Sass::SyntaxError => e
|
37
52
|
e.sass_template = @template
|
38
53
|
raise e
|
data/test/haml/util_test.rb
CHANGED
@@ -100,6 +100,23 @@ class UtilTest < Test::Unit::TestCase
|
|
100
100
|
assert_equal([[1, 2, 3]], paths([[1], [2], [3]]))
|
101
101
|
end
|
102
102
|
|
103
|
+
def test_lcs
|
104
|
+
assert_equal([1, 2, 3], lcs([1, 2, 3], [1, 2, 3]))
|
105
|
+
assert_equal([], lcs([], [1, 2, 3]))
|
106
|
+
assert_equal([], lcs([1, 2, 3], []))
|
107
|
+
assert_equal([1, 2, 3], lcs([5, 1, 4, 2, 3, 17], [0, 0, 1, 2, 6, 3]))
|
108
|
+
|
109
|
+
assert_equal([1], lcs([1, 2, 3, 4], [4, 3, 2, 1]))
|
110
|
+
assert_equal([1, 2], lcs([1, 2, 3, 4], [3, 4, 1, 2]))
|
111
|
+
end
|
112
|
+
|
113
|
+
def test_lcs_with_block
|
114
|
+
assert_equal(["1", "2", "3"],
|
115
|
+
lcs([1, 4, 2, 5, 3], [1, 2, 3]) {|a, b| a == b && a.to_s})
|
116
|
+
assert_equal([-4, 2, 8],
|
117
|
+
lcs([-5, 3, 2, 8], [-4, 1, 8]) {|a, b| (a - b).abs <= 1 && [a, b].max})
|
118
|
+
end
|
119
|
+
|
103
120
|
def test_silence_warnings
|
104
121
|
old_stderr, $stderr = $stderr, StringIO.new
|
105
122
|
warn "Out"
|
data/test/sass/engine_test.rb
CHANGED
@@ -1608,13 +1608,15 @@ foo {
|
|
1608
1608
|
a: foo;
|
1609
1609
|
b: bar;
|
1610
1610
|
c: foo bar;
|
1611
|
-
d: foo, bar;
|
1611
|
+
d: foo, bar baz;
|
1612
|
+
e: foo bar, bar; }
|
1612
1613
|
CSS
|
1613
1614
|
foo
|
1614
1615
|
a= "foo"
|
1615
1616
|
b= bar
|
1616
1617
|
c= "foo" bar
|
1617
|
-
d= foo, "bar"
|
1618
|
+
d= foo, "bar baz"
|
1619
|
+
e= "foo bar", bar
|
1618
1620
|
SASS
|
1619
1621
|
end
|
1620
1622
|
end
|
data/test/sass/extend_test.rb
CHANGED
@@ -974,34 +974,73 @@ foo bar {@extend .foo}
|
|
974
974
|
SCSS
|
975
975
|
end
|
976
976
|
|
977
|
-
def
|
977
|
+
def test_nested_extender_alternates_parents
|
978
978
|
assert_equal <<CSS, render(<<SCSS)
|
979
|
-
.baz .foo, .baz foo
|
979
|
+
.baz .bip .foo, .baz .bip foo .grank bar, foo .grank .baz .bip bar {
|
980
980
|
a: b; }
|
981
981
|
CSS
|
982
|
-
.baz .foo {a: b}
|
983
|
-
foo bar {@extend .foo}
|
982
|
+
.baz .bip .foo {a: b}
|
983
|
+
foo .grank bar {@extend .foo}
|
984
984
|
SCSS
|
985
985
|
end
|
986
986
|
|
987
|
-
def
|
987
|
+
def test_nested_extender_unifies_identical_parents
|
988
988
|
assert_equal <<CSS, render(<<SCSS)
|
989
|
-
baz .foo, baz
|
989
|
+
.baz .bip .foo, .baz .bip bar {
|
990
990
|
a: b; }
|
991
991
|
CSS
|
992
|
-
baz .foo {a: b}
|
993
|
-
|
992
|
+
.baz .bip .foo {a: b}
|
993
|
+
.baz .bip bar {@extend .foo}
|
994
|
+
SCSS
|
995
|
+
end
|
996
|
+
|
997
|
+
def test_nested_extender_unifies_common_substring
|
998
|
+
assert_equal <<CSS, render(<<SCSS)
|
999
|
+
.baz .bip .bap .bink .foo, .baz .brat .bip .bap .bink bar, .brat .baz .bip .bap .bink bar {
|
1000
|
+
a: b; }
|
1001
|
+
CSS
|
1002
|
+
.baz .bip .bap .bink .foo {a: b}
|
1003
|
+
.brat .bip .bap bar {@extend .foo}
|
1004
|
+
SCSS
|
1005
|
+
end
|
1006
|
+
|
1007
|
+
def test_nested_extender_unifies_common_subseq
|
1008
|
+
assert_equal <<CSS, render(<<SCSS)
|
1009
|
+
.a .x .b .y .foo, .a .x .n .b .y .m bar, .a .n .x .b .y .m bar, .a .x .n .b .m .y bar, .a .n .x .b .m .y bar {
|
1010
|
+
a: b; }
|
1011
|
+
CSS
|
1012
|
+
.a .x .b .y .foo {a: b}
|
1013
|
+
.a .n .b .m bar {@extend .foo}
|
1014
|
+
SCSS
|
1015
|
+
end
|
1016
|
+
|
1017
|
+
def test_nested_extender_chooses_first_subseq
|
1018
|
+
assert_equal <<CSS, render(<<SCSS)
|
1019
|
+
.a .b .c .d .foo, .a .b .c .d .a .b .bar {
|
1020
|
+
a: b; }
|
1021
|
+
CSS
|
1022
|
+
.a .b .c .d .foo {a: b}
|
1023
|
+
.c .d .a .b .bar {@extend .foo}
|
1024
|
+
SCSS
|
1025
|
+
end
|
1026
|
+
|
1027
|
+
def test_nested_extender_counts_extended_subselectors
|
1028
|
+
assert_equal <<CSS, render(<<SCSS)
|
1029
|
+
.a .bip.bop .foo, .a .b .bip.bop .bar, .b .a .bip.bop .bar {
|
1030
|
+
a: b; }
|
1031
|
+
CSS
|
1032
|
+
.a .bip.bop .foo {a: b}
|
1033
|
+
.b .bip .bar {@extend .foo}
|
994
1034
|
SCSS
|
995
1035
|
end
|
996
1036
|
|
997
|
-
def
|
998
|
-
# Please, never ever do this in a real stylesheet
|
1037
|
+
def test_nested_extender_counts_extended_superselectors
|
999
1038
|
assert_equal <<CSS, render(<<SCSS)
|
1000
|
-
.
|
1039
|
+
.a .bip .foo, .a .b .bip.bop .bar, .b .a .bip.bop .bar {
|
1001
1040
|
a: b; }
|
1002
1041
|
CSS
|
1003
|
-
.
|
1004
|
-
.
|
1042
|
+
.a .bip .foo {a: b}
|
1043
|
+
.b .bip.bop .bar {@extend .foo}
|
1005
1044
|
SCSS
|
1006
1045
|
end
|
1007
1046
|
|
@@ -1015,33 +1054,101 @@ foo > bar {@extend .foo}
|
|
1015
1054
|
SCSS
|
1016
1055
|
end
|
1017
1056
|
|
1018
|
-
def
|
1057
|
+
def test_nested_extender_finds_common_selectors_around_child_selector
|
1019
1058
|
assert_equal <<CSS, render(<<SCSS)
|
1020
|
-
|
1059
|
+
a > b c .c1, a > b c .c2 {
|
1021
1060
|
a: b; }
|
1022
1061
|
CSS
|
1023
|
-
|
1024
|
-
|
1062
|
+
a > b c .c1 {a: b}
|
1063
|
+
a c .c2 {@extend .c1}
|
1064
|
+
SCSS
|
1065
|
+
|
1066
|
+
assert_equal <<CSS, render(<<SCSS)
|
1067
|
+
a > b c .c1, a > b c .c2 {
|
1068
|
+
a: b; }
|
1069
|
+
CSS
|
1070
|
+
a > b c .c1 {a: b}
|
1071
|
+
b c .c2 {@extend .c1}
|
1025
1072
|
SCSS
|
1026
1073
|
end
|
1027
1074
|
|
1028
|
-
def
|
1075
|
+
def test_nested_extender_doesnt_find_common_selectors_around_adjacent_sibling_selector
|
1029
1076
|
assert_equal <<CSS, render(<<SCSS)
|
1030
|
-
.
|
1077
|
+
a + b c .c1, a + b a c .c2, a a + b c .c2 {
|
1031
1078
|
a: b; }
|
1032
1079
|
CSS
|
1033
|
-
.
|
1034
|
-
|
1080
|
+
a + b c .c1 {a: b}
|
1081
|
+
a c .c2 {@extend .c1}
|
1082
|
+
SCSS
|
1083
|
+
|
1084
|
+
assert_equal <<CSS, render(<<SCSS)
|
1085
|
+
a + b c .c1, a a + b c .c2 {
|
1086
|
+
a: b; }
|
1087
|
+
CSS
|
1088
|
+
a + b c .c1 {a: b}
|
1089
|
+
a b .c2 {@extend .c1}
|
1090
|
+
SCSS
|
1091
|
+
|
1092
|
+
assert_equal <<CSS, render(<<SCSS)
|
1093
|
+
a + b c .c1, a + b c .c2 {
|
1094
|
+
a: b; }
|
1095
|
+
CSS
|
1096
|
+
a + b c .c1 {a: b}
|
1097
|
+
b c .c2 {@extend .c1}
|
1035
1098
|
SCSS
|
1036
1099
|
end
|
1037
1100
|
|
1038
|
-
def
|
1101
|
+
def test_nested_extender_doesnt_find_common_selectors_around_sibling_selector
|
1039
1102
|
assert_equal <<CSS, render(<<SCSS)
|
1040
|
-
|
1103
|
+
a ~ b c .c1, a ~ b a c .c2, a a ~ b c .c2 {
|
1041
1104
|
a: b; }
|
1042
1105
|
CSS
|
1043
|
-
|
1044
|
-
|
1106
|
+
a ~ b c .c1 {a: b}
|
1107
|
+
a c .c2 {@extend .c1}
|
1108
|
+
SCSS
|
1109
|
+
|
1110
|
+
assert_equal <<CSS, render(<<SCSS)
|
1111
|
+
a ~ b c .c1, a a ~ b c .c2 {
|
1112
|
+
a: b; }
|
1113
|
+
CSS
|
1114
|
+
a ~ b c .c1 {a: b}
|
1115
|
+
a b .c2 {@extend .c1}
|
1116
|
+
SCSS
|
1117
|
+
|
1118
|
+
assert_equal <<CSS, render(<<SCSS)
|
1119
|
+
a ~ b c .c1, a ~ b c .c2 {
|
1120
|
+
a: b; }
|
1121
|
+
CSS
|
1122
|
+
a ~ b c .c1 {a: b}
|
1123
|
+
b c .c2 {@extend .c1}
|
1124
|
+
SCSS
|
1125
|
+
end
|
1126
|
+
|
1127
|
+
def test_nested_extender_with_early_child_selectors_doesnt_subseq_them
|
1128
|
+
assert_equal <<CSS, render(<<SCSS)
|
1129
|
+
.bip > .bap .foo, .bip > .bap .grip > .bap .bar, .grip > .bap .bip > .bap .bar {
|
1130
|
+
a: b; }
|
1131
|
+
CSS
|
1132
|
+
.bip > .bap .foo {a: b}
|
1133
|
+
.grip > .bap .bar {@extend .foo}
|
1134
|
+
SCSS
|
1135
|
+
|
1136
|
+
assert_equal <<CSS, render(<<SCSS)
|
1137
|
+
.bap > .bip .foo, .bap > .bip .bap > .grip .bar, .bap > .grip .bap > .bip .bar {
|
1138
|
+
a: b; }
|
1139
|
+
CSS
|
1140
|
+
.bap > .bip .foo {a: b}
|
1141
|
+
.bap > .grip .bar {@extend .foo}
|
1142
|
+
SCSS
|
1143
|
+
end
|
1144
|
+
|
1145
|
+
def test_nested_extender_with_child_selector_unifies
|
1146
|
+
assert_equal <<CSS, render(<<SCSS)
|
1147
|
+
.baz.foo, foo > bar.baz {
|
1148
|
+
a: b; }
|
1149
|
+
CSS
|
1150
|
+
.baz.foo {a: b}
|
1151
|
+
foo > bar {@extend .foo}
|
1045
1152
|
SCSS
|
1046
1153
|
end
|
1047
1154
|
|
@@ -37,8 +37,8 @@ LESS
|
|
37
37
|
end
|
38
38
|
|
39
39
|
def test_import
|
40
|
-
path = File.dirname(__FILE__) + "/templates/importee.less"
|
41
|
-
resolved_path = File.dirname(__FILE__) + "/templates/importee"
|
40
|
+
path = relative_path_to(File.dirname(__FILE__) + "/templates/importee.less")
|
41
|
+
resolved_path = relative_path_to(File.dirname(__FILE__) + "/templates/importee")
|
42
42
|
assert_renders <<SCSS, <<LESS
|
43
43
|
@import "#{resolved_path}";
|
44
44
|
@import "#{resolved_path}";
|
@@ -57,8 +57,8 @@ LESS
|
|
57
57
|
end
|
58
58
|
|
59
59
|
def test_mixins_found_through_import
|
60
|
-
path = File.dirname(__FILE__) + "/templates/importee.less"
|
61
|
-
resolved_path = File.dirname(__FILE__) + "/templates/importee"
|
60
|
+
path = relative_path_to(File.dirname(__FILE__) + "/templates/importee.less")
|
61
|
+
resolved_path = relative_path_to(File.dirname(__FILE__) + "/templates/importee")
|
62
62
|
assert_renders <<SCSS, <<LESS
|
63
63
|
@import "#{resolved_path}";
|
64
64
|
|
@@ -618,6 +618,13 @@ LESS
|
|
618
618
|
def assert_renders(scss, less)
|
619
619
|
assert_equal(scss, Less::Engine.new(less).to_tree.to_sass_tree.to_scss)
|
620
620
|
end
|
621
|
+
|
622
|
+
# Necessary because Less can't import absolute files
|
623
|
+
def relative_path_to(file)
|
624
|
+
file = Pathname.new(file)
|
625
|
+
pwd = file.absolute? ? Dir.pwd : "."
|
626
|
+
file.relative_path_from(Pathname.new(pwd))
|
627
|
+
end
|
621
628
|
end
|
622
629
|
|
623
630
|
rescue LoadError => e
|
data/test/sass/scss/scss_test.rb
CHANGED
@@ -950,7 +950,12 @@ SCSS
|
|
950
950
|
end
|
951
951
|
|
952
952
|
def test_parent_in_mid_selector_error
|
953
|
-
assert_raise(Sass::SyntaxError,
|
953
|
+
assert_raise(Sass::SyntaxError, <<MESSAGE) {render <<SCSS}
|
954
|
+
Invalid CSS after ".foo": expected "{", was "&.bar"
|
955
|
+
|
956
|
+
In Sass 3, the parent selector & can only be used where element names are valid,
|
957
|
+
since it could potentially be replaced by an element name.
|
958
|
+
MESSAGE
|
954
959
|
flim {
|
955
960
|
.foo&.bar {a: b}
|
956
961
|
}
|
@@ -958,7 +963,12 @@ SCSS
|
|
958
963
|
end
|
959
964
|
|
960
965
|
def test_parent_in_mid_selector_error
|
961
|
-
assert_raise(Sass::SyntaxError,
|
966
|
+
assert_raise(Sass::SyntaxError, <<MESSAGE) {render <<SCSS}
|
967
|
+
Invalid CSS after ".foo.bar": expected "{", was "&"
|
968
|
+
|
969
|
+
In Sass 3, the parent selector & can only be used where element names are valid,
|
970
|
+
since it could potentially be replaced by an element name.
|
971
|
+
MESSAGE
|
962
972
|
flim {
|
963
973
|
.foo.bar& {a: b}
|
964
974
|
}
|
@@ -966,7 +976,12 @@ SCSS
|
|
966
976
|
end
|
967
977
|
|
968
978
|
def test_double_parent_selector_error
|
969
|
-
assert_raise(Sass::SyntaxError,
|
979
|
+
assert_raise(Sass::SyntaxError, <<MESSAGE) {render <<SCSS}
|
980
|
+
Invalid CSS after "&": expected "{", was "&"
|
981
|
+
|
982
|
+
In Sass 3, the parent selector & can only be used where element names are valid,
|
983
|
+
since it could potentially be replaced by an element name.
|
984
|
+
MESSAGE
|
970
985
|
flim {
|
971
986
|
&& {a: b}
|
972
987
|
}
|
metadata
CHANGED
@@ -7,8 +7,8 @@ version: !ruby/object:Gem::Version
|
|
7
7
|
- 0
|
8
8
|
- 0
|
9
9
|
- rc
|
10
|
-
-
|
11
|
-
version: 3.0.0.rc.
|
10
|
+
- 5
|
11
|
+
version: 3.0.0.rc.5
|
12
12
|
platform: ruby
|
13
13
|
authors:
|
14
14
|
- Nathan Weizenbaum
|
@@ -17,7 +17,7 @@ autorequire:
|
|
17
17
|
bindir: bin
|
18
18
|
cert_chain: []
|
19
19
|
|
20
|
-
date: 2010-05-
|
20
|
+
date: 2010-05-07 00:00:00 -07:00
|
21
21
|
default_executable:
|
22
22
|
dependencies:
|
23
23
|
- !ruby/object:Gem::Dependency
|