haml-edge 2.3.209 → 2.3.210
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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{"}
|