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
data/lib/sass/engine.rb
CHANGED
@@ -9,12 +9,14 @@ require 'sass/tree/directive_node'
|
|
9
9
|
require 'sass/tree/variable_node'
|
10
10
|
require 'sass/tree/mixin_def_node'
|
11
11
|
require 'sass/tree/mixin_node'
|
12
|
+
require 'sass/tree/extend_node'
|
12
13
|
require 'sass/tree/if_node'
|
13
14
|
require 'sass/tree/while_node'
|
14
15
|
require 'sass/tree/for_node'
|
15
16
|
require 'sass/tree/debug_node'
|
16
17
|
require 'sass/tree/warn_node'
|
17
18
|
require 'sass/tree/import_node'
|
19
|
+
require 'sass/selector'
|
18
20
|
require 'sass/environment'
|
19
21
|
require 'sass/script'
|
20
22
|
require 'sass/scss'
|
@@ -79,60 +81,49 @@ module Sass
|
|
79
81
|
end
|
80
82
|
|
81
83
|
# The character that begins a CSS property.
|
82
|
-
# @private
|
83
84
|
PROPERTY_CHAR = ?:
|
84
85
|
|
85
86
|
# The character that designates that
|
86
87
|
# a property should be assigned to a SassScript expression.
|
87
|
-
# @private
|
88
88
|
SCRIPT_CHAR = ?=
|
89
89
|
|
90
90
|
# The character that designates the beginning of a comment,
|
91
91
|
# either Sass or CSS.
|
92
|
-
# @private
|
93
92
|
COMMENT_CHAR = ?/
|
94
93
|
|
95
94
|
# The character that follows the general COMMENT_CHAR and designates a Sass comment,
|
96
95
|
# which is not output as a CSS comment.
|
97
|
-
# @private
|
98
96
|
SASS_COMMENT_CHAR = ?/
|
99
97
|
|
100
98
|
# The character that follows the general COMMENT_CHAR and designates a CSS comment,
|
101
99
|
# which is embedded in the CSS document.
|
102
|
-
# @private
|
103
100
|
CSS_COMMENT_CHAR = ?*
|
104
101
|
|
105
102
|
# The character used to denote a compiler directive.
|
106
|
-
# @private
|
107
103
|
DIRECTIVE_CHAR = ?@
|
108
104
|
|
109
105
|
# Designates a non-parsed rule.
|
110
|
-
# @private
|
111
106
|
ESCAPE_CHAR = ?\\
|
112
107
|
|
113
108
|
# Designates block as mixin definition rather than CSS rules to output
|
114
|
-
# @private
|
115
109
|
MIXIN_DEFINITION_CHAR = ?=
|
116
110
|
|
117
111
|
# Includes named mixin declared using MIXIN_DEFINITION_CHAR
|
118
|
-
# @private
|
119
112
|
MIXIN_INCLUDE_CHAR = ?+
|
120
113
|
|
121
114
|
# The regex that matches properties of the form `name: prop`.
|
122
|
-
# @private
|
123
115
|
PROPERTY_NEW_MATCHER = /^[^\s:"\[]+\s*[=:](\s|$)/
|
124
116
|
|
125
117
|
# The regex that matches and extracts data from
|
126
118
|
# properties of the form `name: prop`.
|
127
|
-
# @private
|
128
119
|
PROPERTY_NEW = /^([^\s=:"]+)\s*(=|:)(?:\s+|$)(.*)/
|
129
120
|
|
130
121
|
# The regex that matches and extracts data from
|
131
122
|
# properties of the form `:name prop`.
|
132
|
-
# @private
|
133
123
|
PROPERTY_OLD = /^:([^\s=:"]+)\s*(=?)(?:\s+|$)(.*)/
|
134
124
|
|
135
125
|
# The default options for Sass::Engine.
|
126
|
+
# @api public
|
136
127
|
DEFAULT_OPTIONS = {
|
137
128
|
:style => :nested,
|
138
129
|
:load_paths => ['.'],
|
@@ -382,7 +373,10 @@ WARNING
|
|
382
373
|
# if we're using the new property syntax
|
383
374
|
Tree::RuleNode.new(parse_interp(line.text))
|
384
375
|
else
|
385
|
-
|
376
|
+
name, eq, value = line.text.scan(PROPERTY_OLD)[0]
|
377
|
+
raise SyntaxError.new("Invalid property: \"#{line.text}\".",
|
378
|
+
:line => @line) if name.nil? || value.nil?
|
379
|
+
parse_property(name, parse_interp(name), eq, value, :old, line)
|
386
380
|
end
|
387
381
|
when ?!, ?$
|
388
382
|
parse_variable(line)
|
@@ -401,20 +395,29 @@ WARNING
|
|
401
395
|
parse_mixin_include(line, root)
|
402
396
|
end
|
403
397
|
else
|
404
|
-
|
405
|
-
parse_property(line, PROPERTY_NEW)
|
406
|
-
else
|
407
|
-
Tree::RuleNode.new(parse_interp(line.text))
|
408
|
-
end
|
398
|
+
parse_property_or_rule(line)
|
409
399
|
end
|
410
400
|
end
|
411
401
|
|
412
|
-
def
|
413
|
-
|
402
|
+
def parse_property_or_rule(line)
|
403
|
+
scanner = StringScanner.new(line.text)
|
404
|
+
hack_char = scanner.scan(/[:\*\.]|\#(?!\{)/)
|
405
|
+
parser = Sass::SCSS::SassParser.new(scanner, @line)
|
414
406
|
|
415
|
-
|
416
|
-
|
407
|
+
unless res = parser.parse_interp_ident
|
408
|
+
return Tree::RuleNode.new(parse_interp(line.text))
|
409
|
+
end
|
410
|
+
res.unshift(hack_char) if hack_char
|
411
|
+
|
412
|
+
name = line.text[0...scanner.pos]
|
413
|
+
if scanner.scan(/\s*([:=])(?:\s|$)/)
|
414
|
+
parse_property(name, res, scanner[1], scanner.rest, :new, line)
|
415
|
+
else
|
416
|
+
Tree::RuleNode.new(res + parse_interp(scanner.rest))
|
417
|
+
end
|
418
|
+
end
|
417
419
|
|
420
|
+
def parse_property(name, parsed_name, eq, value, prop, line)
|
418
421
|
if value.strip.empty?
|
419
422
|
expr = Sass::Script::String.new("")
|
420
423
|
else
|
@@ -427,9 +430,7 @@ WARNING
|
|
427
430
|
@line, line.offset + 1, @options[:filename])
|
428
431
|
end
|
429
432
|
end
|
430
|
-
Tree::PropNode.new(
|
431
|
-
parse_interp(name), expr,
|
432
|
-
property_regx == PROPERTY_OLD ? :old : :new)
|
433
|
+
Tree::PropNode.new(parse_interp(name), expr, prop)
|
433
434
|
end
|
434
435
|
|
435
436
|
def parse_variable(line)
|
@@ -500,6 +501,12 @@ WARNING
|
|
500
501
|
:line => @line + 1) unless line.children.empty?
|
501
502
|
offset = line.offset + line.text.index(value).to_i
|
502
503
|
Tree::DebugNode.new(parse_script(value, :offset => offset))
|
504
|
+
elsif directive == "extend"
|
505
|
+
raise SyntaxError.new("Invalid extend directive '@extend': expected expression.") unless value
|
506
|
+
raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath extend directives.",
|
507
|
+
:line => @line + 1) unless line.children.empty?
|
508
|
+
offset = line.offset + line.text.index(value).to_i
|
509
|
+
Tree::ExtendNode.new(parse_interp(value, offset))
|
503
510
|
elsif directive == "warn"
|
504
511
|
raise SyntaxError.new("Invalid warn directive '@warn': expected expression.") unless value
|
505
512
|
raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath warn directives.",
|
@@ -552,7 +559,6 @@ WARNING
|
|
552
559
|
nil
|
553
560
|
end
|
554
561
|
|
555
|
-
# @private
|
556
562
|
MIXIN_DEF_RE = /^(?:=|@mixin)\s*(#{Sass::SCSS::RX::IDENT})(.*)$/
|
557
563
|
def parse_mixin_definition(line)
|
558
564
|
name, arg_string = line.text.scan(MIXIN_DEF_RE).first
|
@@ -565,7 +571,6 @@ WARNING
|
|
565
571
|
Tree::MixinDefNode.new(name, args)
|
566
572
|
end
|
567
573
|
|
568
|
-
# @private
|
569
574
|
MIXIN_INCLUDE_RE = /^(?:\+|@include)\s*(#{Sass::SCSS::RX::IDENT})(.*)$/
|
570
575
|
def parse_mixin_include(line, root)
|
571
576
|
name, arg_string = line.text.scan(MIXIN_INCLUDE_RE).first
|
@@ -605,15 +610,15 @@ WARNING
|
|
605
610
|
end
|
606
611
|
end
|
607
612
|
|
608
|
-
def parse_interp(text)
|
609
|
-
self.class.parse_interp(text, @line, :filename => @filename)
|
613
|
+
def parse_interp(text, offset = 0)
|
614
|
+
self.class.parse_interp(text, @line, offset, :filename => @filename)
|
610
615
|
end
|
611
616
|
|
612
617
|
# It's important that this have strings (at least)
|
613
618
|
# at the beginning, the end, and between each Script::Node.
|
614
619
|
#
|
615
620
|
# @private
|
616
|
-
def self.parse_interp(text, line, options)
|
621
|
+
def self.parse_interp(text, line, offset, options)
|
617
622
|
res = []
|
618
623
|
rest = Haml::Shared.handle_interpolation text do |scan|
|
619
624
|
escapes = scan[2].size
|
@@ -623,7 +628,7 @@ WARNING
|
|
623
628
|
else
|
624
629
|
res << "\\" * [0, escapes - 1].max
|
625
630
|
res << Script::Parser.new(
|
626
|
-
scan, line, scan.pos - scan.matched_size, options).
|
631
|
+
scan, line, offset + scan.pos - scan.matched_size, options).
|
627
632
|
parse_interpolated
|
628
633
|
end
|
629
634
|
end
|
data/lib/sass/files.rb
CHANGED
@@ -20,7 +20,7 @@ module Sass
|
|
20
20
|
options = Sass::Engine::DEFAULT_OPTIONS.merge(options)
|
21
21
|
text = File.read(filename)
|
22
22
|
|
23
|
-
if options[:cache]
|
23
|
+
if options[:cache] || options[:read_cache]
|
24
24
|
compiled_filename = sassc_filename(filename, options)
|
25
25
|
sha = Digest::SHA1.hexdigest(text)
|
26
26
|
|
@@ -22,6 +22,7 @@ module Sass
|
|
22
22
|
# *WARNING*: It is important not to retain the instance for too long,
|
23
23
|
# as its instance-level caches are never explicitly expired.
|
24
24
|
class StalenessChecker
|
25
|
+
DELETED = 1.0/0.0 # positive Infinity
|
25
26
|
@dependencies_cache = {}
|
26
27
|
|
27
28
|
class << self
|
@@ -47,15 +48,9 @@ module Sass
|
|
47
48
|
# @param template_file [String] The location of the Sass or SCSS template
|
48
49
|
# that is compiled to `css_file`.
|
49
50
|
def stylesheet_needs_update?(css_file, template_file)
|
50
|
-
template_file = File.expand_path(template_file)
|
51
|
+
template_file, css_mtime = File.expand_path(template_file), mtime(css_file)
|
51
52
|
|
52
|
-
|
53
|
-
@dependencies.delete(template_file)
|
54
|
-
true
|
55
|
-
else
|
56
|
-
css_mtime = mtime(css_file)
|
57
|
-
mtime(template_file) > css_mtime || dependencies_stale?(template_file, css_mtime)
|
58
|
-
end
|
53
|
+
css_mtime == DELETED || dependency_updated?(css_mtime).call(template_file)
|
59
54
|
end
|
60
55
|
|
61
56
|
# Returns whether or not a given CSS file is out of date
|
@@ -87,7 +82,12 @@ module Sass
|
|
87
82
|
end
|
88
83
|
|
89
84
|
def mtime(filename)
|
90
|
-
@mtimes[filename] ||=
|
85
|
+
@mtimes[filename] ||= begin
|
86
|
+
File.mtime(filename).to_i
|
87
|
+
rescue Errno::ENOENT
|
88
|
+
@dependencies.delete(filename)
|
89
|
+
DELETED
|
90
|
+
end
|
91
91
|
end
|
92
92
|
|
93
93
|
def dependencies(filename)
|
data/lib/sass/plugin.rb
CHANGED
@@ -231,6 +231,27 @@ module Sass
|
|
231
231
|
end
|
232
232
|
end
|
233
233
|
|
234
|
+
# Updates all stylesheets, even those that aren't out-of-date.
|
235
|
+
# Ignores the cache.
|
236
|
+
#
|
237
|
+
# @param individual_files [Array<(String, String)>]
|
238
|
+
# A list of files to check for updates
|
239
|
+
# **in addition to those specified by the
|
240
|
+
# {file:SASS_REFERENCE.md#template_location-option `:template_location` option}.**
|
241
|
+
# The first string in each pair is the location of the Sass/SCSS file,
|
242
|
+
# the second is the location of the CSS file that it should be compiled to.
|
243
|
+
# @see #update_stylesheets
|
244
|
+
def force_update_stylesheets(individual_files = [])
|
245
|
+
old_options = options
|
246
|
+
self.options = options.dup
|
247
|
+
options[:never_update] = false
|
248
|
+
options[:always_update] = true
|
249
|
+
options[:cache] = false
|
250
|
+
update_stylesheets(individual_files)
|
251
|
+
ensure
|
252
|
+
self.options = old_options
|
253
|
+
end
|
254
|
+
|
234
255
|
# Watches the template directory (or directories)
|
235
256
|
# and updates the CSS files whenever the related Sass/SCSS files change.
|
236
257
|
# `watch` never returns.
|
data/lib/sass/script/color.rb
CHANGED
@@ -19,7 +19,6 @@ module Sass::Script
|
|
19
19
|
class << self; include Haml::Util; end
|
20
20
|
|
21
21
|
# A hash from color names to `[red, green, blue]` value arrays.
|
22
|
-
# @private
|
23
22
|
HTML4_COLORS = map_vals({
|
24
23
|
'black' => 0x000000,
|
25
24
|
'silver' => 0xc0c0c0,
|
@@ -39,7 +38,6 @@ module Sass::Script
|
|
39
38
|
'aqua' => 0x00ffff
|
40
39
|
}) {|color| (0..2).map {|n| color >> (n << 3) & 0xff}.reverse}
|
41
40
|
# A hash from `[red, green, blue]` value arrays to color names.
|
42
|
-
# @private
|
43
41
|
HTML4_COLORS_REVERSE = map_hash(HTML4_COLORS) {|k, v| [v, k]}
|
44
42
|
|
45
43
|
# Constructs an RGB or HSL color object,
|
@@ -108,7 +106,10 @@ module Sass::Script
|
|
108
106
|
end
|
109
107
|
|
110
108
|
[:saturation, :lightness].each do |k|
|
111
|
-
next if @attrs[k].nil?
|
109
|
+
next if @attrs[k].nil?
|
110
|
+
@attrs[k] = 0 if @attrs[k] < 0.00001 && @attrs[k] > -0.00001
|
111
|
+
@attrs[k] = 100 if @attrs[k] - 100 < 0.00001 && @attrs[k] - 100 > -0.00001
|
112
|
+
next if (0..100).include?(@attrs[k])
|
112
113
|
raise Sass::SyntaxError.new("#{k.to_s.capitalize} must be between 0 and 100")
|
113
114
|
end
|
114
115
|
|
@@ -1,11 +1,21 @@
|
|
1
1
|
module Sass
|
2
2
|
module Script
|
3
|
+
# This is a subclass of {Lexer} for use in parsing plain CSS properties.
|
4
|
+
#
|
5
|
+
# @see Sass::SCSS::CssParser
|
3
6
|
class CssLexer < Lexer
|
7
|
+
private
|
8
|
+
|
4
9
|
def token
|
5
10
|
important || super
|
6
11
|
end
|
7
12
|
|
8
|
-
def string(*args)
|
13
|
+
def string(re, *args)
|
14
|
+
if re == :uri
|
15
|
+
return unless uri = scan(URI)
|
16
|
+
return [:string, Script::String.new(uri)]
|
17
|
+
end
|
18
|
+
|
9
19
|
return unless scan(STRING)
|
10
20
|
[:string, Script::String.new((@scanner[1] || @scanner[2]).gsub(/\\(['"])/, '\1'), :string)]
|
11
21
|
end
|
@@ -3,6 +3,9 @@ require 'sass/script/css_lexer'
|
|
3
3
|
|
4
4
|
module Sass
|
5
5
|
module Script
|
6
|
+
# This is a subclass of {Parser} for use in parsing plain CSS properties.
|
7
|
+
#
|
8
|
+
# @see Sass::SCSS::CssParser
|
6
9
|
class CssParser < Parser
|
7
10
|
private
|
8
11
|
|
@@ -21,7 +24,7 @@ module Sass
|
|
21
24
|
# Short-circuit all the SassScript-only productions
|
22
25
|
alias_method :interpolation, :concat
|
23
26
|
alias_method :or_expr, :div
|
24
|
-
alias_method :unary_div, :
|
27
|
+
alias_method :unary_div, :ident
|
25
28
|
alias_method :paren, :string
|
26
29
|
end
|
27
30
|
end
|
data/lib/sass/script/funcall.rb
CHANGED
@@ -17,6 +17,15 @@ module Sass
|
|
17
17
|
# @return [Array<Script::Node>]
|
18
18
|
attr_reader :args
|
19
19
|
|
20
|
+
# Don't set the context for child nodes if this is `url()`,
|
21
|
+
# since `url()` allows quoted strings.
|
22
|
+
#
|
23
|
+
# @param context [Symbol]
|
24
|
+
# @see Node#context=
|
25
|
+
def context=(context)
|
26
|
+
super unless @name == "url"
|
27
|
+
end
|
28
|
+
|
20
29
|
# @param name [String] See \{#name}
|
21
30
|
# @param name [Array<Script::Node>] See \{#args}
|
22
31
|
def initialize(name, args)
|
@@ -1,5 +1,15 @@
|
|
1
1
|
module Sass::Script
|
2
|
+
# A SassScript object representing `#{}` interpolation outside a string.
|
3
|
+
#
|
4
|
+
# @see StringInterpolation
|
2
5
|
class Interpolation < Node
|
6
|
+
# Interpolation in a property is of the form `before #{mid} after`.
|
7
|
+
#
|
8
|
+
# @param before [Node] The SassScript before the interpolation
|
9
|
+
# @param mid [Node] The SassScript within the interpolation
|
10
|
+
# @param after [Node] The SassScript after the interpolation
|
11
|
+
# @param wb [Boolean] Whether there was whitespace between `before` and `#{`
|
12
|
+
# @param wa [Boolean] Whether there was whitespace between `}` and `after`
|
3
13
|
def initialize(before, mid, after, wb, wa)
|
4
14
|
@before = before
|
5
15
|
@mid = mid
|
@@ -8,10 +18,12 @@ module Sass::Script
|
|
8
18
|
@whitespace_after = wa
|
9
19
|
end
|
10
20
|
|
21
|
+
# @return [String] A human-readable s-expression representation of the interpolation
|
11
22
|
def inspect
|
12
23
|
"(interpolation #{@before.inspect} #{@mid.inspect} #{@after.inspect})"
|
13
24
|
end
|
14
25
|
|
26
|
+
# @see Node#to_sass
|
15
27
|
def to_sass(opts = {})
|
16
28
|
res = ""
|
17
29
|
res << @before.to_sass(opts) if @before
|
@@ -22,12 +34,21 @@ module Sass::Script
|
|
22
34
|
res
|
23
35
|
end
|
24
36
|
|
37
|
+
# Returns the three components of the interpolation, `before`, `mid`, and `after`.
|
38
|
+
#
|
39
|
+
# @return [Array<Node>]
|
40
|
+
# @see #initialize
|
41
|
+
# @see Node#children
|
25
42
|
def children
|
26
43
|
[@before, @mid, @after].compact
|
27
44
|
end
|
28
45
|
|
29
46
|
protected
|
30
47
|
|
48
|
+
# Evaluates the interpolation.
|
49
|
+
#
|
50
|
+
# @param environment [Sass::Environment] The environment in which to evaluate the SassScript
|
51
|
+
# @return [Sass::Script::String] The SassScript string that is the value of the interpolation
|
31
52
|
def _perform(environment)
|
32
53
|
res = ""
|
33
54
|
res << @before.perform(environment).to_s if @before
|
data/lib/sass/script/lexer.rb
CHANGED
@@ -40,7 +40,6 @@ module Sass
|
|
40
40
|
attr_reader :offset
|
41
41
|
|
42
42
|
# A hash from operator strings to the corresponding token types.
|
43
|
-
# @private
|
44
43
|
OPERATORS = {
|
45
44
|
'+' => :plus,
|
46
45
|
'-' => :minus,
|
@@ -67,10 +66,8 @@ module Sass
|
|
67
66
|
'{' => :lcurly,
|
68
67
|
}
|
69
68
|
|
70
|
-
# @private
|
71
69
|
OPERATORS_REVERSE = Haml::Util.map_hash(OPERATORS) {|k, v| [v, k]}
|
72
70
|
|
73
|
-
# @private
|
74
71
|
TOKEN_NAMES = Haml::Util.map_hash(OPERATORS_REVERSE) {|k, v| [k, v.inspect]}.merge({
|
75
72
|
:const => "variable (e.g. $foo)",
|
76
73
|
:ident => "identifier (e.g. middle)",
|
@@ -79,22 +76,19 @@ module Sass
|
|
79
76
|
|
80
77
|
# A list of operator strings ordered with longer names first
|
81
78
|
# so that `>` and `<` don't clobber `>=` and `<=`.
|
82
|
-
# @private
|
83
79
|
OP_NAMES = OPERATORS.keys.sort_by {|o| -o.size}
|
84
80
|
|
85
81
|
# A sub-list of {OP_NAMES} that only includes operators
|
86
82
|
# with identifier names.
|
87
|
-
# @private
|
88
83
|
IDENT_OP_NAMES = OP_NAMES.select {|k, v| k =~ /^\w+/}
|
89
84
|
|
90
85
|
# A hash of regular expressions that are used for tokenizing.
|
91
|
-
# @private
|
92
86
|
REGULAR_EXPRESSIONS = {
|
93
87
|
:whitespace => /\s+/,
|
94
88
|
:comment => COMMENT,
|
95
89
|
:single_line_comment => SINGLE_LINE_COMMENT,
|
96
90
|
:variable => /([!\$])(#{IDENT})/,
|
97
|
-
:ident => IDENT
|
91
|
+
:ident => /(#{IDENT})(\()?/,
|
98
92
|
:number => /(-)?(?:(\d*\.\d+)|(\d+))([a-zA-Z%]+)?/,
|
99
93
|
:color => HEXCOLOR,
|
100
94
|
:bool => /(true|false)\b/,
|
@@ -120,6 +114,8 @@ module Sass
|
|
120
114
|
[:single, false] => string_re("'", "'"),
|
121
115
|
[:double, true] => string_re('', '"'),
|
122
116
|
[:single, true] => string_re('', "'"),
|
117
|
+
[:uri, false] => /url\(#{W}(#{URLCHAR}*?)(#{W}\)|(?=#\{))/,
|
118
|
+
[:uri, true] => /(#{URLCHAR}*?)(#{W}\)|(?=#\{))/,
|
123
119
|
}
|
124
120
|
|
125
121
|
# @param str [String, StringScanner] The source text to lex
|
@@ -179,11 +175,24 @@ module Sass
|
|
179
175
|
@scanner.eos? && @tok.nil?
|
180
176
|
end
|
181
177
|
|
178
|
+
# Raise an error to the effect that `name` was expected in the input stream
|
179
|
+
# and wasn't found.
|
180
|
+
#
|
181
|
+
# This calls \{#unpeek!} to rewind the scanner to immediately after
|
182
|
+
# the last returned token.
|
183
|
+
#
|
184
|
+
# @param name [String] The name of the entity that was expected but not found
|
185
|
+
# @raise [Sass::SyntaxError]
|
182
186
|
def expected!(name)
|
183
187
|
unpeek!
|
184
188
|
Sass::SCSS::Parser.expected(@scanner, name, @line)
|
185
189
|
end
|
186
190
|
|
191
|
+
# Records all non-comment text the lexer consumes within the block
|
192
|
+
# and returns it as a string.
|
193
|
+
#
|
194
|
+
# @yield A block in which text is recorded
|
195
|
+
# @return [String]
|
187
196
|
def str
|
188
197
|
old_pos = @tok ? @tok.pos : @scanner.pos
|
189
198
|
yield
|
@@ -216,8 +225,8 @@ module Sass
|
|
216
225
|
end
|
217
226
|
|
218
227
|
variable || string(:double, false) || string(:single, false) || number ||
|
219
|
-
color || bool ||
|
220
|
-
ident_op || ident || op
|
228
|
+
color || bool || string(:uri, false) || raw(UNICODERANGE) ||
|
229
|
+
special_fun || ident_op || ident || op
|
221
230
|
end
|
222
231
|
|
223
232
|
def variable
|
@@ -236,14 +245,20 @@ module Sass
|
|
236
245
|
end
|
237
246
|
|
238
247
|
def ident
|
239
|
-
return unless
|
240
|
-
[:ident,
|
248
|
+
return unless scan(REGULAR_EXPRESSIONS[:ident])
|
249
|
+
[@scanner[2] ? :funcall : :ident, @scanner[1]]
|
241
250
|
end
|
242
251
|
|
243
252
|
def string(re, open)
|
244
253
|
return unless scan(STRING_REGULAR_EXPRESSIONS[[re, open]])
|
245
254
|
@interpolation_stack << re if @scanner[2].empty? # Started an interpolated section
|
246
|
-
|
255
|
+
str =
|
256
|
+
if re == :uri
|
257
|
+
Script::String.new("#{'url(' unless open}#{@scanner[1]}#{')' unless @scanner[2].empty?}")
|
258
|
+
else
|
259
|
+
Script::String.new(@scanner[1].gsub(/\\(['"]|\#\{)/, '\1'), :string)
|
260
|
+
end
|
261
|
+
[:string, str]
|
247
262
|
end
|
248
263
|
|
249
264
|
def number
|
@@ -269,11 +284,13 @@ module Sass
|
|
269
284
|
return unless str1 = scan(/(calc|expression|progid:[a-z\.]*)\(/i)
|
270
285
|
str2, _ = Haml::Shared.balance(@scanner, ?(, ?), 1)
|
271
286
|
c = str2.count("\n")
|
287
|
+
old_line = @line
|
288
|
+
old_offset = @offset
|
272
289
|
@line += c
|
273
290
|
@offset = (c == 0 ? @offset + str2.size : str2[/\n(.*)/, 1].size)
|
274
291
|
[:special_fun,
|
275
292
|
Haml::Util.merge_adjacent_strings(
|
276
|
-
[str1] + Sass::Engine.parse_interp(str2,
|
293
|
+
[str1] + Sass::Engine.parse_interp(str2, old_line, old_offset, @options)),
|
277
294
|
str1.size + str2.size]
|
278
295
|
end
|
279
296
|
|
data/lib/sass/script/node.rb
CHANGED
data/lib/sass/script/number.rb
CHANGED
@@ -30,6 +30,7 @@ module Sass::Script
|
|
30
30
|
# The precision with which numbers will be printed to CSS files.
|
31
31
|
# For example, if this is `1000.0`,
|
32
32
|
# `3.1415926` will be printed as `3.142`.
|
33
|
+
# @api public
|
33
34
|
PRECISION = 1000.0
|
34
35
|
|
35
36
|
# @param value [Numeric] The value of the number
|
@@ -115,7 +116,7 @@ module Sass::Script
|
|
115
116
|
# @raise [NoMethodError] if `other` is an invalid type
|
116
117
|
def times(other)
|
117
118
|
if other.is_a? Number
|
118
|
-
|
119
|
+
operate(other, :*)
|
119
120
|
elsif other.is_a? Color
|
120
121
|
other.times(self)
|
121
122
|
else
|
@@ -303,7 +304,7 @@ module Sass::Script
|
|
303
304
|
# @return [Boolean] Whether or not this number can be compared with the other.
|
304
305
|
def comparable_to?(other)
|
305
306
|
begin
|
306
|
-
|
307
|
+
operate(other, :+)
|
307
308
|
true
|
308
309
|
rescue Sass::UnitConversionError
|
309
310
|
false
|
@@ -323,7 +324,7 @@ module Sass::Script
|
|
323
324
|
rv
|
324
325
|
end
|
325
326
|
|
326
|
-
|
327
|
+
private
|
327
328
|
|
328
329
|
def operate(other, operation)
|
329
330
|
this = self
|
@@ -381,9 +382,7 @@ module Sass::Script
|
|
381
382
|
end
|
382
383
|
|
383
384
|
# A hash of unit names to their index in the conversion table
|
384
|
-
# @private
|
385
385
|
CONVERTABLE_UNITS = {"in" => 0, "cm" => 1, "pc" => 2, "mm" => 3, "pt" => 4}
|
386
|
-
# @private
|
387
386
|
CONVERSION_TABLE = [[ 1, 2.54, 6, 25.4, 72 ], # in
|
388
387
|
[ nil, 1, 2.36220473, 10, 28.3464567], # cm
|
389
388
|
[ nil, nil, 1, 4.23333333, 12 ], # pc
|
data/lib/sass/script/parser.rb
CHANGED
@@ -120,7 +120,6 @@ module Sass
|
|
120
120
|
new(*args).parse
|
121
121
|
end
|
122
122
|
|
123
|
-
# @private
|
124
123
|
PRECEDENCE = [
|
125
124
|
:comma, :single_eq, :concat, :or, :and,
|
126
125
|
[:eq, :neq],
|
@@ -214,24 +213,24 @@ RUBY
|
|
214
213
|
unary :plus, :unary_minus
|
215
214
|
unary :minus, :unary_div
|
216
215
|
unary :div, :unary_not # For strings, so /foo/bar works
|
217
|
-
unary :not, :
|
216
|
+
unary :not, :ident
|
218
217
|
|
219
|
-
def
|
220
|
-
return
|
218
|
+
def ident
|
219
|
+
return funcall unless @lexer.peek && @lexer.peek.type == :ident
|
221
220
|
return if @stop_at && @stop_at.include?(@lexer.peek.value)
|
222
221
|
|
223
222
|
name = @lexer.next
|
224
|
-
|
225
|
-
|
226
|
-
if color = Color::HTML4_COLORS[name.value]
|
227
|
-
return node(Color.new(color))
|
228
|
-
end
|
229
|
-
node(Script::String.new(name.value, :identifier))
|
230
|
-
else
|
231
|
-
args = fn_arglist || []
|
232
|
-
assert_tok(:rparen)
|
233
|
-
node(Script::Funcall.new(name.value, args))
|
223
|
+
if color = Color::HTML4_COLORS[name.value]
|
224
|
+
return node(Color.new(color))
|
234
225
|
end
|
226
|
+
node(Script::String.new(name.value, :identifier))
|
227
|
+
end
|
228
|
+
|
229
|
+
def funcall
|
230
|
+
return raw unless tok = try_tok(:funcall)
|
231
|
+
args = fn_arglist || []
|
232
|
+
assert_tok(:rparen)
|
233
|
+
node(Script::Funcall.new(tok.value, args))
|
235
234
|
end
|
236
235
|
|
237
236
|
def defn_arglist!(must_have_default)
|
data/lib/sass/script/string.rb
CHANGED
@@ -15,6 +15,10 @@ module Sass::Script
|
|
15
15
|
# @return [Symbol] `:string` or `:identifier`
|
16
16
|
attr_reader :type
|
17
17
|
|
18
|
+
# In addition to setting the \{#context} of the string,
|
19
|
+
# this sets the string to be an identifier if the context is `:equals`.
|
20
|
+
#
|
21
|
+
# @see Node#context=
|
18
22
|
def context=(context)
|
19
23
|
super
|
20
24
|
@type = :identifier if context == :equals
|
@@ -29,6 +33,7 @@ module Sass::Script
|
|
29
33
|
@type = type
|
30
34
|
end
|
31
35
|
|
36
|
+
# @see Literal#plus
|
32
37
|
def plus(other)
|
33
38
|
other_str = other.is_a?(Sass::Script::String) ? other.value : other.to_s
|
34
39
|
Sass::Script::String.new(self.value + other_str, self.type)
|
@@ -47,12 +52,13 @@ module Sass::Script
|
|
47
52
|
def to_sass(opts = {})
|
48
53
|
type = opts[:type] || self.type
|
49
54
|
if type == :identifier
|
50
|
-
if context == :equals && Sass::SCSS::RX
|
55
|
+
if context == :equals && self.value !~ Sass::SCSS::RX::URI &&
|
56
|
+
Sass::SCSS::RX.escape_ident(self.value).include?(?\\)
|
51
57
|
return "unquote(#{Sass::Script::String.new(self.value, :string).to_sass})"
|
52
58
|
elsif context == :equals && self.value.size == 0
|
53
59
|
return %q{""}
|
54
60
|
end
|
55
|
-
return self.value
|
61
|
+
return self.value.gsub("\n", " ")
|
56
62
|
end
|
57
63
|
|
58
64
|
return "\"#{value.gsub('"', "\\\"")}\"" if opts[:quote] == %q{"}
|