haml-edge 2.3.209 → 2.3.210
Sign up to get free protection for your applications and to get access to all the features.
- data/.yardopts +2 -0
- data/EDGE_GEM_VERSION +1 -1
- data/Rakefile +24 -2
- data/VERSION +1 -1
- data/lib/haml/exec.rb +11 -4
- data/lib/haml/filters.rb +3 -0
- data/lib/haml/helpers/action_view_extensions.rb +4 -2
- data/lib/haml/helpers/action_view_mods.rb +6 -4
- data/lib/haml/helpers.rb +2 -10
- data/lib/haml/html.rb +0 -1
- data/lib/haml/precompiler.rb +37 -30
- data/lib/haml/railtie.rb +6 -2
- data/lib/haml/root.rb +4 -0
- data/lib/haml/template.rb +2 -0
- data/lib/haml/util/subset_map.rb +101 -0
- data/lib/haml/util.rb +74 -0
- data/lib/haml.rb +5 -2
- data/lib/sass/engine.rb +36 -31
- data/lib/sass/files.rb +1 -1
- data/lib/sass/plugin/staleness_checker.rb +9 -9
- data/lib/sass/plugin.rb +21 -0
- data/lib/sass/script/color.rb +4 -3
- data/lib/sass/script/css_lexer.rb +11 -1
- data/lib/sass/script/css_parser.rb +4 -1
- data/lib/sass/script/funcall.rb +9 -0
- data/lib/sass/script/interpolation.rb +21 -0
- data/lib/sass/script/lexer.rb +30 -13
- data/lib/sass/script/node.rb +1 -1
- data/lib/sass/script/number.rb +4 -5
- data/lib/sass/script/parser.rb +13 -14
- data/lib/sass/script/string.rb +8 -2
- data/lib/sass/script/string_interpolation.rb +27 -4
- data/lib/sass/script.rb +1 -2
- data/lib/sass/scss/css_parser.rb +5 -3
- data/lib/sass/scss/parser.rb +146 -64
- data/lib/sass/scss/rx.rb +9 -1
- data/lib/sass/scss/sass_parser.rb +11 -0
- data/lib/sass/scss/script_lexer.rb +2 -0
- data/lib/sass/scss/static_parser.rb +48 -0
- data/lib/sass/scss.rb +3 -0
- data/lib/sass/selector/abstract_sequence.rb +40 -0
- data/lib/sass/selector/comma_sequence.rb +80 -0
- data/lib/sass/selector/sequence.rb +194 -0
- data/lib/sass/selector/simple.rb +107 -0
- data/lib/sass/selector/simple_sequence.rb +161 -0
- data/lib/sass/selector.rb +353 -0
- data/lib/sass/tree/comment_node.rb +1 -0
- data/lib/sass/tree/debug_node.rb +1 -0
- data/lib/sass/tree/directive_node.rb +1 -0
- data/lib/sass/tree/extend_node.rb +60 -0
- data/lib/sass/tree/for_node.rb +1 -0
- data/lib/sass/tree/if_node.rb +2 -0
- data/lib/sass/tree/import_node.rb +2 -0
- data/lib/sass/tree/mixin_def_node.rb +1 -0
- data/lib/sass/tree/mixin_node.rb +21 -5
- data/lib/sass/tree/node.rb +59 -12
- data/lib/sass/tree/prop_node.rb +20 -21
- data/lib/sass/tree/root_node.rb +8 -17
- data/lib/sass/tree/rule_node.rb +49 -100
- data/lib/sass/tree/variable_node.rb +1 -0
- data/lib/sass/tree/warn_node.rb +1 -0
- data/lib/sass/tree/while_node.rb +1 -0
- data/lib/sass.rb +1 -0
- data/test/haml/engine_test.rb +185 -3
- data/test/haml/helper_test.rb +25 -2
- data/test/haml/template_test.rb +2 -2
- data/test/haml/templates/helpers.haml +13 -0
- data/test/haml/util/subset_map_test.rb +91 -0
- data/test/haml/util_test.rb +25 -0
- data/test/sass/conversion_test.rb +23 -3
- data/test/sass/engine_test.rb +50 -7
- data/test/sass/extend_test.rb +1045 -0
- data/test/sass/results/complex.css +0 -1
- data/test/sass/results/script.css +1 -1
- data/test/sass/script_conversion_test.rb +16 -0
- data/test/sass/script_test.rb +37 -4
- data/test/sass/scss/css_test.rb +17 -3
- data/test/sass/scss/rx_test.rb +1 -1
- data/test/sass/scss/scss_test.rb +30 -0
- data/test/sass/templates/complex.sass +0 -2
- data/test/test_helper.rb +5 -0
- metadata +17 -3
@@ -1,15 +1,26 @@
|
|
1
1
|
module Sass::Script
|
2
|
+
# A SassScript object representing `#{}` interpolation within a string.
|
3
|
+
#
|
4
|
+
# @see Interpolation
|
2
5
|
class StringInterpolation < Node
|
6
|
+
# Interpolation in a string is of the form `"before #{mid} after"`,
|
7
|
+
# where `before` and `after` may include more interpolation.
|
8
|
+
#
|
9
|
+
# @param before [Node] The string before the interpolation
|
10
|
+
# @param mid [Node] The SassScript within the interpolation
|
11
|
+
# @param after [Node] The string after the interpolation
|
3
12
|
def initialize(before, mid, after)
|
4
13
|
@before = before
|
5
14
|
@mid = mid
|
6
15
|
@after = after
|
7
16
|
end
|
8
17
|
|
18
|
+
# @return [String] A human-readable s-expression representation of the interpolation
|
9
19
|
def inspect
|
10
20
|
"(string_interpolation #{@before.inspect} #{@mid.inspect} #{@after.inspect})"
|
11
21
|
end
|
12
22
|
|
23
|
+
# @see Node#to_sass
|
13
24
|
def to_sass(opts = {})
|
14
25
|
# We can get rid of all of this when we remove the deprecated :equals context
|
15
26
|
before_unquote, before_quote_char, before_str = parse_str(@before.to_sass(opts))
|
@@ -39,21 +50,33 @@ module Sass::Script
|
|
39
50
|
res
|
40
51
|
end
|
41
52
|
|
53
|
+
# Returns the three components of the interpolation, `before`, `mid`, and `after`.
|
54
|
+
#
|
55
|
+
# @return [Array<Node>]
|
56
|
+
# @see #initialize
|
57
|
+
# @see Node#children
|
42
58
|
def children
|
43
59
|
[@before, @mid, @after].compact
|
44
60
|
end
|
45
61
|
|
46
62
|
protected
|
47
63
|
|
64
|
+
# Evaluates the interpolation.
|
65
|
+
#
|
66
|
+
# @param environment [Sass::Environment] The environment in which to evaluate the SassScript
|
67
|
+
# @return [Sass::Script::String] The SassScript string that is the value of the interpolation
|
48
68
|
def _perform(environment)
|
49
69
|
res = ""
|
50
|
-
|
51
|
-
|
52
|
-
|
70
|
+
before = @before.perform(environment)
|
71
|
+
res << before.value
|
72
|
+
mid = @mid.perform(environment)
|
73
|
+
res << (mid.is_a?(Sass::Script::String) ? mid.value : mid.to_s)
|
53
74
|
res << @after.perform(environment).value
|
54
|
-
Sass::Script::String.new(res,
|
75
|
+
Sass::Script::String.new(res, before.type)
|
55
76
|
end
|
56
77
|
|
78
|
+
private
|
79
|
+
|
57
80
|
def parse_str(str)
|
58
81
|
case str
|
59
82
|
when /^unquote\((["'])(.*)\1\)$/
|
data/lib/sass/script.rb
CHANGED
@@ -13,11 +13,9 @@ module Sass
|
|
13
13
|
# This module contains code that handles the parsing and evaluation of SassScript.
|
14
14
|
module Script
|
15
15
|
# The regular expression used to parse variables.
|
16
|
-
# @private
|
17
16
|
MATCH = /^[!\$](#{Sass::SCSS::RX::IDENT})\s*((?:\|\|)?=|:)\s*(.+?)(!(?i:default))?$/
|
18
17
|
|
19
18
|
# The regular expression used to validate variables without matching.
|
20
|
-
# @private
|
21
19
|
VALIDATE = /^[!\$]#{Sass::SCSS::RX::IDENT}$/
|
22
20
|
|
23
21
|
# Parses a string of SassScript
|
@@ -50,6 +48,7 @@ You can use `sass-convert --in-place --from sass2 file.sass' to convert files au
|
|
50
48
|
MESSAGE
|
51
49
|
end
|
52
50
|
|
51
|
+
# @private
|
53
52
|
def self.equals_warning(types, name, val, guarded, line, offset, filename)
|
54
53
|
Haml::Util.haml_warn <<MESSAGE
|
55
54
|
DEPRECATION WARNING:
|
data/lib/sass/scss/css_parser.rb
CHANGED
@@ -2,15 +2,17 @@ require 'sass/script/css_parser'
|
|
2
2
|
|
3
3
|
module Sass
|
4
4
|
module SCSS
|
5
|
-
|
5
|
+
# This is a subclass of {Parser} which only parses plain CSS.
|
6
|
+
# It doesn't support any Sass extensions, such as interpolation,
|
7
|
+
# parent references, nested selectors, and so forth.
|
8
|
+
# It does support all the same CSS hacks as the SCSS parser, though.
|
9
|
+
class CssParser < StaticParser
|
6
10
|
private
|
7
11
|
|
8
|
-
def variable; nil; end
|
9
12
|
def parent_selector; nil; end
|
10
13
|
def interpolation; nil; end
|
11
14
|
def interp_string; tok(STRING); end
|
12
15
|
def interp_ident(ident = IDENT); tok(ident); end
|
13
|
-
def expected_property_separator; '":"'; end
|
14
16
|
def use_css_import?; true; end
|
15
17
|
|
16
18
|
def special_directive(name)
|
data/lib/sass/scss/parser.rb
CHANGED
@@ -6,10 +6,12 @@ module Sass
|
|
6
6
|
# The parser for SCSS.
|
7
7
|
# It parses a string of code into a tree of {Sass::Tree::Node}s.
|
8
8
|
class Parser
|
9
|
-
# @param str [String] The source document to parse
|
10
|
-
|
9
|
+
# @param str [String, StringScanner] The source document to parse
|
10
|
+
# @param line [Fixnum] The line on which the source string appeared,
|
11
|
+
# if it's part of another document
|
12
|
+
def initialize(str, line = 1)
|
11
13
|
@template = str
|
12
|
-
@line =
|
14
|
+
@line = line
|
13
15
|
@strs = []
|
14
16
|
end
|
15
17
|
|
@@ -18,19 +20,39 @@ module Sass
|
|
18
20
|
# @return [Sass::Tree::RootNode] The root node of the document tree
|
19
21
|
# @raise [Sass::SyntaxError] if there's a syntax error in the document
|
20
22
|
def parse
|
21
|
-
|
22
|
-
Haml::Util.check_encoding(@template) do |msg, line|
|
23
|
-
raise Sass::SyntaxError.new(msg, :line => line)
|
24
|
-
end.gsub("\r", ""))
|
23
|
+
init_scanner!
|
25
24
|
root = stylesheet
|
26
25
|
expected("selector or at-rule") unless @scanner.eos?
|
27
26
|
root
|
28
27
|
end
|
29
28
|
|
29
|
+
# Parses an identifier with interpolation.
|
30
|
+
# Note that this won't assert that the identifier takes up the entire input string;
|
31
|
+
# it's meant to be used with `StringScanner`s as part of other parsers.
|
32
|
+
#
|
33
|
+
# @return [Array<String, Sass::Script::Node>, nil]
|
34
|
+
# The interpolated identifier, or nil if none could be parsed
|
35
|
+
def parse_interp_ident
|
36
|
+
init_scanner!
|
37
|
+
interp_ident
|
38
|
+
end
|
39
|
+
|
30
40
|
private
|
31
41
|
|
32
42
|
include Sass::SCSS::RX
|
33
43
|
|
44
|
+
def init_scanner!
|
45
|
+
@scanner =
|
46
|
+
if @template.is_a?(StringScanner)
|
47
|
+
@template
|
48
|
+
else
|
49
|
+
StringScanner.new(
|
50
|
+
Haml::Util.check_encoding(@template) do |msg, line|
|
51
|
+
raise Sass::SyntaxError.new(msg, :line => line)
|
52
|
+
end.gsub("\r", ""))
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
34
56
|
def stylesheet
|
35
57
|
node = node(Sass::Tree::RootNode.new(@scanner.string))
|
36
58
|
block_contents(node, :stylesheet) {s(node)}
|
@@ -77,7 +99,7 @@ module Sass
|
|
77
99
|
node << comment
|
78
100
|
end
|
79
101
|
|
80
|
-
DIRECTIVES = Set[:mixin, :include, :debug, :warn, :for, :while, :if, :import, :media]
|
102
|
+
DIRECTIVES = Set[:mixin, :include, :debug, :warn, :for, :while, :if, :extend, :import, :media]
|
81
103
|
|
82
104
|
def directive
|
83
105
|
return unless tok(/@/)
|
@@ -106,32 +128,32 @@ module Sass
|
|
106
128
|
|
107
129
|
def special_directive(name)
|
108
130
|
sym = name.gsub('-', '_').to_sym
|
109
|
-
DIRECTIVES.include?(sym) && send(sym)
|
131
|
+
DIRECTIVES.include?(sym) && send("#{sym}_directive")
|
110
132
|
end
|
111
133
|
|
112
|
-
def
|
134
|
+
def mixin_directive
|
113
135
|
name = tok! IDENT
|
114
136
|
args = sass_script(:parse_mixin_definition_arglist)
|
115
137
|
ss
|
116
138
|
block(node(Sass::Tree::MixinDefNode.new(name, args)), :directive)
|
117
139
|
end
|
118
140
|
|
119
|
-
def
|
141
|
+
def include_directive
|
120
142
|
name = tok! IDENT
|
121
143
|
args = sass_script(:parse_mixin_include_arglist)
|
122
144
|
ss
|
123
145
|
node(Sass::Tree::MixinNode.new(name, args))
|
124
146
|
end
|
125
147
|
|
126
|
-
def
|
148
|
+
def debug_directive
|
127
149
|
node(Sass::Tree::DebugNode.new(sass_script(:parse)))
|
128
150
|
end
|
129
151
|
|
130
|
-
def
|
152
|
+
def warn_directive
|
131
153
|
node(Sass::Tree::WarnNode.new(sass_script(:parse)))
|
132
154
|
end
|
133
155
|
|
134
|
-
def
|
156
|
+
def for_directive
|
135
157
|
tok!(/\$/)
|
136
158
|
var = tok! IDENT
|
137
159
|
ss
|
@@ -148,13 +170,13 @@ module Sass
|
|
148
170
|
block(node(Sass::Tree::ForNode.new(var, from, to, exclusive)), :directive)
|
149
171
|
end
|
150
172
|
|
151
|
-
def
|
173
|
+
def while_directive
|
152
174
|
expr = sass_script(:parse)
|
153
175
|
ss
|
154
176
|
block(node(Sass::Tree::WhileNode.new(expr)), :directive)
|
155
177
|
end
|
156
178
|
|
157
|
-
def
|
179
|
+
def if_directive
|
158
180
|
expr = sass_script(:parse)
|
159
181
|
ss
|
160
182
|
node = block(node(Sass::Tree::IfNode.new(expr)), :directive)
|
@@ -173,7 +195,11 @@ module Sass
|
|
173
195
|
else_block(node)
|
174
196
|
end
|
175
197
|
|
176
|
-
def
|
198
|
+
def extend_directive
|
199
|
+
node(Sass::Tree::ExtendNode.new(expr!(:selector)))
|
200
|
+
end
|
201
|
+
|
202
|
+
def import_directive
|
177
203
|
@expected = "string or url()"
|
178
204
|
arg = tok(STRING) || tok!(URI)
|
179
205
|
path = @scanner[1] || @scanner[2] || @scanner[3]
|
@@ -190,7 +216,7 @@ module Sass
|
|
190
216
|
|
191
217
|
def use_css_import?; false; end
|
192
218
|
|
193
|
-
def
|
219
|
+
def media_directive
|
194
220
|
val = str {media_query_list}.strip
|
195
221
|
block(node(Sass::Tree::DirectiveNode.new("@media #{val}")), :directive)
|
196
222
|
end
|
@@ -264,15 +290,7 @@ module Sass
|
|
264
290
|
end
|
265
291
|
|
266
292
|
def ruleset
|
267
|
-
rules =
|
268
|
-
return unless v = selector
|
269
|
-
rules.concat v
|
270
|
-
|
271
|
-
while tok(/,/)
|
272
|
-
rules << ',' << str {ss}
|
273
|
-
rules.concat expr!(:selector)
|
274
|
-
end
|
275
|
-
|
293
|
+
return unless rules = selector_sequence
|
276
294
|
block(node(Sass::Tree::RuleNode.new(rules.flatten.compact)), :ruleset)
|
277
295
|
end
|
278
296
|
|
@@ -296,6 +314,7 @@ module Sass
|
|
296
314
|
end
|
297
315
|
|
298
316
|
def block_child(context)
|
317
|
+
return variable || directive || ruleset if context == :stylesheet
|
299
318
|
variable || directive || declaration_or_ruleset
|
300
319
|
end
|
301
320
|
|
@@ -340,75 +359,124 @@ module Sass
|
|
340
359
|
@use_property_exception = old_use_property_exception
|
341
360
|
end
|
342
361
|
|
362
|
+
def selector_sequence
|
363
|
+
if sel = tok(STATIC_SELECTOR)
|
364
|
+
return [sel]
|
365
|
+
end
|
366
|
+
|
367
|
+
rules = []
|
368
|
+
return unless v = selector
|
369
|
+
rules.concat v
|
370
|
+
|
371
|
+
while tok(/,/)
|
372
|
+
rules << ',' << str {ss}
|
373
|
+
rules.concat expr!(:selector)
|
374
|
+
end
|
375
|
+
rules
|
376
|
+
end
|
377
|
+
|
343
378
|
def selector
|
379
|
+
return unless sel = _selector
|
380
|
+
sel.to_a
|
381
|
+
end
|
382
|
+
|
383
|
+
def _selector
|
344
384
|
# The combinator here allows the "> E" hack
|
345
|
-
return unless
|
346
|
-
|
385
|
+
return unless val = combinator || simple_selector_sequence
|
386
|
+
nl = str{ss}.include?("\n")
|
387
|
+
res = []
|
388
|
+
res << val
|
389
|
+
res << "\n" if nl
|
347
390
|
|
348
|
-
while
|
349
|
-
res <<
|
350
|
-
res.
|
391
|
+
while val = combinator || simple_selector_sequence
|
392
|
+
res << val
|
393
|
+
res << "\n" if str{ss}.include?("\n")
|
351
394
|
end
|
352
|
-
res
|
395
|
+
Selector::Sequence.new(res.compact)
|
353
396
|
end
|
354
397
|
|
355
398
|
def combinator
|
356
|
-
tok(PLUS) || tok(GREATER) || tok(TILDE)
|
399
|
+
tok(PLUS) || tok(GREATER) || tok(TILDE)
|
357
400
|
end
|
358
401
|
|
359
402
|
def simple_selector_sequence
|
360
403
|
# This allows for stuff like http://www.w3.org/TR/css3-animations/#keyframes-
|
361
404
|
return expr unless e = element_name || id_selector || class_selector ||
|
362
|
-
attrib || negation || pseudo || parent_selector ||
|
405
|
+
attrib || negation || pseudo || parent_selector || interpolation_selector
|
363
406
|
res = [e]
|
364
407
|
|
365
408
|
# The tok(/\*/) allows the "E*" hack
|
366
409
|
while v = element_name || id_selector || class_selector ||
|
367
|
-
attrib || negation || pseudo ||
|
410
|
+
attrib || negation || pseudo || interpolation_selector ||
|
411
|
+
(tok(/\*/) && Selector::Universal.new(nil))
|
368
412
|
res << v
|
369
413
|
end
|
370
|
-
|
414
|
+
expected('"{"') if tok?(/&/)
|
415
|
+
|
416
|
+
Selector::SimpleSequence.new(res)
|
371
417
|
end
|
372
418
|
|
373
419
|
def parent_selector
|
374
|
-
tok(/&/)
|
420
|
+
return unless tok(/&/)
|
421
|
+
Selector::Parent.new
|
375
422
|
end
|
376
423
|
|
377
424
|
def class_selector
|
378
425
|
return unless tok(/\./)
|
379
426
|
@expected = "class name"
|
380
|
-
|
427
|
+
Selector::Class.new(merge(expr!(:interp_ident)))
|
381
428
|
end
|
382
429
|
|
383
430
|
def id_selector
|
384
431
|
return unless tok(/#(?!\{)/)
|
385
432
|
@expected = "id name"
|
386
|
-
|
433
|
+
Selector::Id.new(merge(expr!(:interp_name)))
|
387
434
|
end
|
388
435
|
|
389
436
|
def element_name
|
390
|
-
return unless name = interp_ident || tok(/\*/) || tok?(/\|/)
|
437
|
+
return unless name = interp_ident || tok(/\*/) || (tok?(/\|/) && "")
|
391
438
|
if tok(/\|/)
|
392
439
|
@expected = "element name or *"
|
393
|
-
|
440
|
+
ns = name
|
441
|
+
name = interp_ident || tok!(/\*/)
|
442
|
+
end
|
443
|
+
|
444
|
+
if name == '*'
|
445
|
+
Selector::Universal.new(merge(ns))
|
446
|
+
else
|
447
|
+
Selector::Element.new(merge(name), merge(ns))
|
394
448
|
end
|
395
|
-
|
449
|
+
end
|
450
|
+
|
451
|
+
def interpolation_selector
|
452
|
+
return unless script = interpolation
|
453
|
+
Selector::Interpolation.new(script)
|
396
454
|
end
|
397
455
|
|
398
456
|
def attrib
|
399
457
|
return unless tok(/\[/)
|
400
|
-
|
458
|
+
ss
|
459
|
+
ns, name = attrib_name!
|
460
|
+
ss
|
401
461
|
|
402
|
-
if
|
462
|
+
if op = tok(/=/) ||
|
403
463
|
tok(INCLUDES) ||
|
404
464
|
tok(DASHMATCH) ||
|
405
465
|
tok(PREFIXMATCH) ||
|
406
466
|
tok(SUFFIXMATCH) ||
|
407
467
|
tok(SUBSTRINGMATCH)
|
408
468
|
@expected = "identifier or string"
|
409
|
-
|
469
|
+
ss
|
470
|
+
if val = tok(IDENT)
|
471
|
+
val = [val]
|
472
|
+
else
|
473
|
+
val = expr!(:interp_string)
|
474
|
+
end
|
475
|
+
ss
|
410
476
|
end
|
411
|
-
|
477
|
+
tok(/\]/)
|
478
|
+
|
479
|
+
Selector::Attribute.new(merge(name), merge(ns), op, merge(val))
|
412
480
|
end
|
413
481
|
|
414
482
|
def attrib_name!
|
@@ -422,19 +490,23 @@ module Sass
|
|
422
490
|
end
|
423
491
|
else
|
424
492
|
# *|E or |E
|
425
|
-
ns = tok(/\*/) || ""
|
493
|
+
ns = [tok(/\*/) || ""]
|
426
494
|
tok!(/\|/)
|
427
495
|
name = expr!(:interp_ident)
|
428
496
|
end
|
429
|
-
return
|
497
|
+
return ns, name
|
430
498
|
end
|
431
499
|
|
432
500
|
def pseudo
|
433
501
|
return unless s = tok(/::?/)
|
434
502
|
@expected = "pseudoclass or pseudoelement"
|
435
|
-
|
436
|
-
|
437
|
-
|
503
|
+
name = expr!(:interp_ident)
|
504
|
+
if tok(/\(/)
|
505
|
+
ss
|
506
|
+
arg = expr!(:pseudo_expr)
|
507
|
+
tok!(/\)/)
|
508
|
+
end
|
509
|
+
Selector::Pseudo.new(s == ':' ? :class : :element, merge(name), merge(arg))
|
438
510
|
end
|
439
511
|
|
440
512
|
def pseudo_expr
|
@@ -450,10 +522,11 @@ module Sass
|
|
450
522
|
|
451
523
|
def negation
|
452
524
|
return unless tok(NOT)
|
453
|
-
|
525
|
+
ss
|
454
526
|
@expected = "selector"
|
455
|
-
|
456
|
-
|
527
|
+
sel = element_name || id_selector || class_selector || attrib || expr!(:pseudo)
|
528
|
+
tok!(/\)/)
|
529
|
+
Selector::Negation.new(sel)
|
457
530
|
end
|
458
531
|
|
459
532
|
def declaration
|
@@ -467,8 +540,8 @@ module Sass
|
|
467
540
|
end
|
468
541
|
ss
|
469
542
|
|
470
|
-
|
471
|
-
space, value =
|
543
|
+
tok!(/:/)
|
544
|
+
space, value = value!
|
472
545
|
ss
|
473
546
|
require_block = tok?(/\{/)
|
474
547
|
|
@@ -478,16 +551,19 @@ module Sass
|
|
478
551
|
nested_properties! node, space
|
479
552
|
end
|
480
553
|
|
481
|
-
def
|
482
|
-
'":" or "="'
|
483
|
-
end
|
484
|
-
|
485
|
-
def value
|
486
|
-
return unless tok(/:/)
|
554
|
+
def value!
|
487
555
|
space = !str {ss}.empty?
|
488
556
|
@use_property_exception ||= space || !tok?(IDENT)
|
489
557
|
|
490
558
|
return true, Sass::Script::String.new("") if tok?(/\{/)
|
559
|
+
# This is a bit of a dirty trick:
|
560
|
+
# if the value is completely static,
|
561
|
+
# we don't parse it at all, and instead return a plain old string
|
562
|
+
# containing the value.
|
563
|
+
# This results in a dramatic speed increase.
|
564
|
+
if val = tok(STATIC_VALUE)
|
565
|
+
return space, Sass::Script::String.new(val.strip)
|
566
|
+
end
|
491
567
|
return space, sass_script(:parse)
|
492
568
|
end
|
493
569
|
|
@@ -616,6 +692,10 @@ MESSAGE
|
|
616
692
|
result
|
617
693
|
end
|
618
694
|
|
695
|
+
def merge(arr)
|
696
|
+
arr && Haml::Util.merge_adjacent_strings([arr].flatten)
|
697
|
+
end
|
698
|
+
|
619
699
|
EXPR_NAMES = {
|
620
700
|
:media_query => "media query (e.g. print, screen, print and screen)",
|
621
701
|
:media_expr => "media expression (e.g. (min-device-width: 800px)))",
|
@@ -623,11 +703,13 @@ MESSAGE
|
|
623
703
|
:interp_ident => "identifier",
|
624
704
|
:interp_name => "identifier",
|
625
705
|
:expr => "expression (e.g. 1px, bold)",
|
706
|
+
:_selector => "selector",
|
707
|
+
:simple_selector_sequence => "selector",
|
626
708
|
}
|
627
709
|
|
628
710
|
TOK_NAMES = Haml::Util.to_hash(
|
629
711
|
Sass::SCSS::RX.constants.map {|c| [Sass::SCSS::RX.const_get(c), c.downcase]}).
|
630
|
-
merge(IDENT => "identifier", /[;}]/ => '";"'
|
712
|
+
merge(IDENT => "identifier", /[;}]/ => '";"')
|
631
713
|
|
632
714
|
def tok?(rx)
|
633
715
|
@scanner.match?(rx)
|
data/lib/sass/scss/rx.rb
CHANGED
@@ -65,7 +65,8 @@ module Sass
|
|
65
65
|
NAME = /#{NMCHAR}+/
|
66
66
|
NUM = /[0-9]+|[0-9]*.[0-9]+/
|
67
67
|
STRING = /#{STRING1}|#{STRING2}/
|
68
|
-
|
68
|
+
URLCHAR = /[#%&*-~]|#{NONASCII}|#{ESCAPE}/
|
69
|
+
URL = /(#{URLCHAR}*)/
|
69
70
|
W = /[ \t\r\n\f]*/
|
70
71
|
|
71
72
|
# This is more liberal than the spec's definition,
|
@@ -108,6 +109,13 @@ module Sass
|
|
108
109
|
# Custom
|
109
110
|
HEXCOLOR = /\#[0-9a-fA-F]{3}(?:[0-9a-fA-F]{3})?/
|
110
111
|
INTERP_START = /#\{/
|
112
|
+
|
113
|
+
STRING1_NOINTERP = /\"((?:[^\n\r\f\\"#]|#(?!\{)|\\#{NL}|#{ESCAPE})*)\"/
|
114
|
+
STRING2_NOINTERP = /\'((?:[^\n\r\f\\'#]|#(?!\{)|\\#{NL}|#{ESCAPE})*)\'/
|
115
|
+
STRING_NOINTERP = /#{STRING1_NOINTERP}|#{STRING2_NOINTERP}/
|
116
|
+
STATIC_VALUE = /(#{NMCHAR}|#{STRING1_NOINTERP}|\s(?!%)|#[a-f0-9]|[,%]|\.[0-9]|\!important)+(?=[;}])/i
|
117
|
+
|
118
|
+
STATIC_SELECTOR = /(#{NMCHAR}|\s|[,>+*]|[:#.]#{NMSTART})+(?=[{])/i
|
111
119
|
end
|
112
120
|
end
|
113
121
|
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module Sass
|
2
|
+
module SCSS
|
3
|
+
# A subclass of {Parser} that parses code in Sass documents
|
4
|
+
# using some SCSS constructs.
|
5
|
+
# This is necessary because SassScript in Sass supports `!`-style variables,
|
6
|
+
# whereas in SCSS it doesn't.
|
7
|
+
class SassParser < Parser
|
8
|
+
@sass_script_parser = Sass::Script::Parser
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -4,6 +4,8 @@ module Sass
|
|
4
4
|
# that makes them usable by {SCSS::Parser} to parse SassScript.
|
5
5
|
# In particular, the lexer doesn't support `!` for a variable prefix.
|
6
6
|
module ScriptLexer
|
7
|
+
private
|
8
|
+
|
7
9
|
def variable
|
8
10
|
return [:raw, "!important"] if scan(Sass::SCSS::RX::IMPORTANT)
|
9
11
|
_variable(/(\$)(#{Sass::SCSS::RX::IDENT})/)
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Sass
|
2
|
+
module SCSS
|
3
|
+
# A parser for a static SCSS tree.
|
4
|
+
# Parses with SCSS extensions, like nested rules and parent selectors,
|
5
|
+
# but without dynamic SassScript.
|
6
|
+
# This is useful for e.g. \{#parse\_selector parsing selectors}
|
7
|
+
# after resolving the interpolation.
|
8
|
+
class StaticParser < Parser
|
9
|
+
# Parses the text as a selector.
|
10
|
+
#
|
11
|
+
# @param line [Fixnum] The line on which the selector appears.
|
12
|
+
# Used for error reporting
|
13
|
+
# @param filename [String, nil] The file in which the selector appears,
|
14
|
+
# or nil if there is no such file.
|
15
|
+
# Used for error reporting
|
16
|
+
# @return [Selector::CommaSequence] The parsed selector
|
17
|
+
# @raise [Sass::SyntaxError] if there's a syntax error in the selector
|
18
|
+
def parse_selector(line, filename)
|
19
|
+
init_scanner!
|
20
|
+
selectors = [expr!(:_selector)]
|
21
|
+
while tok(/,/)
|
22
|
+
ws = str{ss}
|
23
|
+
selectors << expr!(:_selector)
|
24
|
+
selectors[-1] = Selector::Sequence.new(["\n"] + selectors.last.members) if ws.include?("\n")
|
25
|
+
end
|
26
|
+
expected("selector") unless @scanner.eos?
|
27
|
+
seq = Selector::CommaSequence.new(selectors)
|
28
|
+
seq.line = line
|
29
|
+
seq.filename = filename
|
30
|
+
seq
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def variable; nil; end
|
36
|
+
def script_value; nil; end
|
37
|
+
def interpolation; nil; end
|
38
|
+
def interp_string; s = tok(STRING) and [s]; end
|
39
|
+
def interp_ident(ident = IDENT); s = tok(ident) and [s]; end
|
40
|
+
def use_css_import?; true; end
|
41
|
+
|
42
|
+
def special_directive(name)
|
43
|
+
return unless name == 'media' || name == 'import'
|
44
|
+
super
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/lib/sass/scss.rb
CHANGED
@@ -2,6 +2,9 @@ require 'sass/scss/rx'
|
|
2
2
|
require 'sass/scss/script_lexer'
|
3
3
|
require 'sass/scss/script_parser'
|
4
4
|
require 'sass/scss/parser'
|
5
|
+
require 'sass/scss/sass_parser'
|
6
|
+
require 'sass/scss/static_parser'
|
7
|
+
require 'sass/scss/css_parser'
|
5
8
|
|
6
9
|
module Sass
|
7
10
|
# SCSS is the CSS syntax for Sass.
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Sass
|
2
|
+
module Selector
|
3
|
+
# The abstract parent class of the various selector sequence classes.
|
4
|
+
#
|
5
|
+
# All subclasses should implement a `members` method
|
6
|
+
# that returns an array of object that respond to `#line=` and `#filename=`.
|
7
|
+
class AbstractSequence
|
8
|
+
# The line of the Sass template on which this selector was declared.
|
9
|
+
#
|
10
|
+
# @return [Fixnum]
|
11
|
+
attr_reader :line
|
12
|
+
|
13
|
+
# The name of the file in which this selector was declared.
|
14
|
+
#
|
15
|
+
# @return [String, nil]
|
16
|
+
attr_reader :filename
|
17
|
+
|
18
|
+
# Sets the line of the Sass template on which this selector was declared.
|
19
|
+
# This also sets the line for all child selectors.
|
20
|
+
#
|
21
|
+
# @param line [Fixnum]
|
22
|
+
# @return [Fixnum]
|
23
|
+
def line=(line)
|
24
|
+
members.each {|m| m.line = line}
|
25
|
+
@line = line
|
26
|
+
end
|
27
|
+
|
28
|
+
# Sets the name of the file in which this selector was declared,
|
29
|
+
# or `nil` if it was not declared in a file (e.g. on stdin).
|
30
|
+
# This also sets the filename for all child selectors.
|
31
|
+
#
|
32
|
+
# @param filename [String, nil]
|
33
|
+
# @return [String, nil]
|
34
|
+
def filename=(filename)
|
35
|
+
members.each {|m| m.filename = filename}
|
36
|
+
@filename = filename
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|