oreorenasass 3.4.4 → 3.4.5
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.
- checksums.yaml +4 -4
- data/MIT-LICENSE +1 -1
- data/README.md +50 -70
- data/Rakefile +5 -26
- data/VERSION +1 -1
- data/VERSION_NAME +1 -1
- data/bin/sass +1 -1
- data/bin/scss +1 -1
- data/lib/sass.rb +12 -19
- data/lib/sass/cache_stores/base.rb +2 -2
- data/lib/sass/cache_stores/chain.rb +1 -2
- data/lib/sass/cache_stores/filesystem.rb +5 -1
- data/lib/sass/cache_stores/memory.rb +1 -1
- data/lib/sass/cache_stores/null.rb +2 -2
- data/lib/sass/callbacks.rb +0 -1
- data/lib/sass/css.rb +13 -11
- data/lib/sass/engine.rb +173 -424
- data/lib/sass/environment.rb +58 -148
- data/lib/sass/error.rb +14 -11
- data/lib/sass/exec.rb +703 -5
- data/lib/sass/importers/base.rb +6 -49
- data/lib/sass/importers/filesystem.rb +19 -44
- data/lib/sass/logger.rb +4 -1
- data/lib/sass/logger/base.rb +4 -2
- data/lib/sass/logger/log_level.rb +7 -3
- data/lib/sass/media.rb +23 -20
- data/lib/sass/plugin.rb +7 -7
- data/lib/sass/plugin/compiler.rb +145 -304
- data/lib/sass/plugin/configuration.rb +23 -18
- data/lib/sass/plugin/merb.rb +1 -1
- data/lib/sass/plugin/staleness_checker.rb +3 -3
- data/lib/sass/repl.rb +3 -3
- data/lib/sass/script.rb +8 -35
- data/lib/sass/script/{value/arg_list.rb → arg_list.rb} +25 -9
- data/lib/sass/script/bool.rb +18 -0
- data/lib/sass/script/color.rb +606 -0
- data/lib/sass/script/css_lexer.rb +4 -8
- data/lib/sass/script/css_parser.rb +2 -5
- data/lib/sass/script/funcall.rb +245 -0
- data/lib/sass/script/functions.rb +408 -1491
- data/lib/sass/script/interpolation.rb +79 -0
- data/lib/sass/script/lexer.rb +68 -172
- data/lib/sass/script/list.rb +85 -0
- data/lib/sass/script/literal.rb +221 -0
- data/lib/sass/script/{tree/node.rb → node.rb} +12 -22
- data/lib/sass/script/{value/null.rb → null.rb} +7 -14
- data/lib/sass/script/{value/number.rb → number.rb} +75 -152
- data/lib/sass/script/{tree/operation.rb → operation.rb} +24 -17
- data/lib/sass/script/parser.rb +110 -245
- data/lib/sass/script/string.rb +51 -0
- data/lib/sass/script/{tree/string_interpolation.rb → string_interpolation.rb} +4 -5
- data/lib/sass/script/{tree/unary_operation.rb → unary_operation.rb} +6 -6
- data/lib/sass/script/variable.rb +58 -0
- data/lib/sass/scss/css_parser.rb +3 -9
- data/lib/sass/scss/parser.rb +421 -450
- data/lib/sass/scss/rx.rb +11 -19
- data/lib/sass/scss/static_parser.rb +7 -321
- data/lib/sass/selector.rb +194 -68
- data/lib/sass/selector/abstract_sequence.rb +14 -29
- data/lib/sass/selector/comma_sequence.rb +25 -108
- data/lib/sass/selector/sequence.rb +66 -159
- data/lib/sass/selector/simple.rb +25 -23
- data/lib/sass/selector/simple_sequence.rb +63 -173
- data/lib/sass/shared.rb +1 -1
- data/lib/sass/supports.rb +15 -13
- data/lib/sass/tree/charset_node.rb +1 -1
- data/lib/sass/tree/comment_node.rb +3 -3
- data/lib/sass/tree/css_import_node.rb +11 -11
- data/lib/sass/tree/debug_node.rb +2 -2
- data/lib/sass/tree/directive_node.rb +4 -21
- data/lib/sass/tree/each_node.rb +8 -8
- data/lib/sass/tree/extend_node.rb +7 -14
- data/lib/sass/tree/for_node.rb +4 -4
- data/lib/sass/tree/function_node.rb +4 -9
- data/lib/sass/tree/if_node.rb +1 -1
- data/lib/sass/tree/import_node.rb +5 -4
- data/lib/sass/tree/media_node.rb +14 -4
- data/lib/sass/tree/mixin_def_node.rb +4 -4
- data/lib/sass/tree/mixin_node.rb +8 -21
- data/lib/sass/tree/node.rb +12 -54
- data/lib/sass/tree/prop_node.rb +20 -39
- data/lib/sass/tree/return_node.rb +2 -3
- data/lib/sass/tree/root_node.rb +3 -19
- data/lib/sass/tree/rule_node.rb +22 -35
- data/lib/sass/tree/supports_node.rb +13 -0
- data/lib/sass/tree/trace_node.rb +1 -2
- data/lib/sass/tree/variable_node.rb +3 -9
- data/lib/sass/tree/visitors/base.rb +8 -5
- data/lib/sass/tree/visitors/check_nesting.rb +19 -49
- data/lib/sass/tree/visitors/convert.rb +56 -74
- data/lib/sass/tree/visitors/cssize.rb +74 -202
- data/lib/sass/tree/visitors/deep_copy.rb +5 -10
- data/lib/sass/tree/visitors/extend.rb +7 -7
- data/lib/sass/tree/visitors/perform.rb +185 -278
- data/lib/sass/tree/visitors/set_options.rb +6 -20
- data/lib/sass/tree/visitors/to_css.rb +81 -234
- data/lib/sass/tree/warn_node.rb +2 -2
- data/lib/sass/tree/while_node.rb +2 -2
- data/lib/sass/util.rb +152 -522
- data/lib/sass/util/multibyte_string_scanner.rb +0 -2
- data/lib/sass/util/subset_map.rb +3 -4
- data/lib/sass/util/test.rb +1 -0
- data/lib/sass/version.rb +22 -20
- data/test/Gemfile +3 -0
- data/test/Gemfile.lock +10 -0
- data/test/sass/cache_test.rb +20 -62
- data/test/sass/callbacks_test.rb +1 -1
- data/test/sass/conversion_test.rb +2 -296
- data/test/sass/css2sass_test.rb +4 -23
- data/test/sass/engine_test.rb +354 -411
- data/test/sass/exec_test.rb +2 -2
- data/test/sass/extend_test.rb +145 -324
- data/test/sass/functions_test.rb +86 -873
- data/test/sass/importer_test.rb +21 -241
- data/test/sass/logger_test.rb +1 -1
- data/test/sass/more_results/more_import.css +1 -1
- data/test/sass/plugin_test.rb +26 -16
- data/test/sass/results/compact.css +1 -1
- data/test/sass/results/complex.css +4 -4
- data/test/sass/results/expanded.css +1 -1
- data/test/sass/results/import.css +1 -1
- data/test/sass/results/import_charset_ibm866.css +2 -2
- data/test/sass/results/mixins.css +17 -17
- data/test/sass/results/nested.css +1 -1
- data/test/sass/results/parent_ref.css +2 -2
- data/test/sass/results/script.css +3 -3
- data/test/sass/results/scss_import.css +1 -1
- data/test/sass/script_conversion_test.rb +7 -36
- data/test/sass/script_test.rb +53 -485
- data/test/sass/scss/css_test.rb +28 -143
- data/test/sass/scss/rx_test.rb +4 -4
- data/test/sass/scss/scss_test.rb +325 -2119
- data/test/sass/templates/scss_import.scss +1 -2
- data/test/sass/test_helper.rb +1 -1
- data/test/sass/util/multibyte_string_scanner_test.rb +1 -1
- data/test/sass/util/subset_map_test.rb +2 -2
- data/test/sass/util_test.rb +1 -86
- data/test/test_helper.rb +8 -37
- metadata +19 -66
- data/lib/sass/exec/base.rb +0 -187
- data/lib/sass/exec/sass_convert.rb +0 -264
- data/lib/sass/exec/sass_scss.rb +0 -424
- data/lib/sass/features.rb +0 -47
- data/lib/sass/script/tree.rb +0 -16
- data/lib/sass/script/tree/funcall.rb +0 -306
- data/lib/sass/script/tree/interpolation.rb +0 -118
- data/lib/sass/script/tree/list_literal.rb +0 -77
- data/lib/sass/script/tree/literal.rb +0 -45
- data/lib/sass/script/tree/map_literal.rb +0 -64
- data/lib/sass/script/tree/selector.rb +0 -26
- data/lib/sass/script/tree/variable.rb +0 -57
- data/lib/sass/script/value.rb +0 -11
- data/lib/sass/script/value/base.rb +0 -240
- data/lib/sass/script/value/bool.rb +0 -35
- data/lib/sass/script/value/color.rb +0 -680
- data/lib/sass/script/value/helpers.rb +0 -262
- data/lib/sass/script/value/list.rb +0 -113
- data/lib/sass/script/value/map.rb +0 -70
- data/lib/sass/script/value/string.rb +0 -97
- data/lib/sass/selector/pseudo.rb +0 -256
- data/lib/sass/source/map.rb +0 -210
- data/lib/sass/source/position.rb +0 -39
- data/lib/sass/source/range.rb +0 -41
- data/lib/sass/stack.rb +0 -120
- data/lib/sass/tree/at_root_node.rb +0 -83
- data/lib/sass/tree/error_node.rb +0 -18
- data/lib/sass/tree/keyframe_rule_node.rb +0 -15
- data/lib/sass/util/cross_platform_random.rb +0 -19
- data/lib/sass/util/normalized_map.rb +0 -130
- data/lib/sass/util/ordered_hash.rb +0 -192
- data/test/sass/compiler_test.rb +0 -232
- data/test/sass/encoding_test.rb +0 -219
- data/test/sass/source_map_test.rb +0 -977
- data/test/sass/superselector_test.rb +0 -191
- data/test/sass/util/normalized_map_test.rb +0 -51
- data/test/sass/value_helpers_test.rb +0 -179
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'sass/script/literal'
|
2
|
+
|
3
|
+
module Sass::Script
|
4
|
+
# A SassScript object representing a CSS string *or* a CSS identifier.
|
5
|
+
class String < Literal
|
6
|
+
# The Ruby value of the string.
|
7
|
+
#
|
8
|
+
# @return [String]
|
9
|
+
attr_reader :value
|
10
|
+
|
11
|
+
# Whether this is a CSS string or a CSS identifier.
|
12
|
+
# The difference is that strings are written with double-quotes,
|
13
|
+
# while identifiers aren't.
|
14
|
+
#
|
15
|
+
# @return [Symbol] `:string` or `:identifier`
|
16
|
+
attr_reader :type
|
17
|
+
|
18
|
+
# Creates a new string.
|
19
|
+
#
|
20
|
+
# @param value [String] See \{#value}
|
21
|
+
# @param type [Symbol] See \{#type}
|
22
|
+
def initialize(value, type = :identifier)
|
23
|
+
super(value)
|
24
|
+
@type = type
|
25
|
+
end
|
26
|
+
|
27
|
+
# @see Literal#plus
|
28
|
+
def plus(other)
|
29
|
+
other_str = other.is_a?(Sass::Script::String) ? other.value : other.to_s
|
30
|
+
Sass::Script::String.new(self.value + other_str, self.type)
|
31
|
+
end
|
32
|
+
|
33
|
+
# @see Node#to_s
|
34
|
+
def to_s(opts = {})
|
35
|
+
if @type == :identifier
|
36
|
+
return @value.gsub(/\n\s*/, " ")
|
37
|
+
end
|
38
|
+
|
39
|
+
return "\"#{value.gsub('"', "\\\"")}\"" if opts[:quote] == %q{"}
|
40
|
+
return "'#{value.gsub("'", "\\'")}'" if opts[:quote] == %q{'}
|
41
|
+
return "\"#{value}\"" unless value.include?('"')
|
42
|
+
return "'#{value}'" unless value.include?("'")
|
43
|
+
"\"#{value.gsub('"', "\\\"")}\"" #'
|
44
|
+
end
|
45
|
+
|
46
|
+
# @see Node#to_sass
|
47
|
+
def to_sass(opts = {})
|
48
|
+
to_s
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
module Sass::Script
|
1
|
+
module Sass::Script
|
2
2
|
# A SassScript object representing `#{}` interpolation within a string.
|
3
3
|
#
|
4
4
|
# @see Interpolation
|
@@ -74,16 +74,15 @@ module Sass::Script::Tree
|
|
74
74
|
# Evaluates the interpolation.
|
75
75
|
#
|
76
76
|
# @param environment [Sass::Environment] The environment in which to evaluate the SassScript
|
77
|
-
# @return [Sass::Script::
|
78
|
-
# The SassScript string that is the value of the interpolation
|
77
|
+
# @return [Sass::Script::String] The SassScript string that is the value of the interpolation
|
79
78
|
def _perform(environment)
|
80
79
|
res = ""
|
81
80
|
before = @before.perform(environment)
|
82
81
|
res << before.value
|
83
82
|
mid = @mid.perform(environment)
|
84
|
-
res << (mid.is_a?(Sass::Script::
|
83
|
+
res << (mid.is_a?(Sass::Script::String) ? mid.value : mid.to_s)
|
85
84
|
res << @after.perform(environment).value
|
86
|
-
opts(Sass::Script::
|
85
|
+
opts(Sass::Script::String.new(res, before.type))
|
87
86
|
end
|
88
87
|
|
89
88
|
private
|
@@ -1,4 +1,4 @@
|
|
1
|
-
module Sass::Script
|
1
|
+
module Sass::Script
|
2
2
|
# A SassScript parse node representing a unary operation,
|
3
3
|
# such as `-$b` or `not true`.
|
4
4
|
#
|
@@ -31,7 +31,7 @@ module Sass::Script::Tree
|
|
31
31
|
(operand =~ Sass::SCSS::RX::IDENT) == 0)
|
32
32
|
operand = "(#{@operand.to_sass(opts)})"
|
33
33
|
end
|
34
|
-
op =
|
34
|
+
op = Lexer::OPERATORS_REVERSE[@operator]
|
35
35
|
op + (op =~ /[a-z]/ ? " " : "") + operand
|
36
36
|
end
|
37
37
|
|
@@ -55,15 +55,15 @@ module Sass::Script::Tree
|
|
55
55
|
# Evaluates the operation.
|
56
56
|
#
|
57
57
|
# @param environment [Sass::Environment] The environment in which to evaluate the SassScript
|
58
|
-
# @return [
|
58
|
+
# @return [Literal] The SassScript object that is the value of the operation
|
59
59
|
# @raise [Sass::SyntaxError] if the operation is undefined for the operand
|
60
60
|
def _perform(environment)
|
61
61
|
operator = "unary_#{@operator}"
|
62
|
-
|
63
|
-
|
62
|
+
literal = @operand.perform(environment)
|
63
|
+
literal.send(operator)
|
64
64
|
rescue NoMethodError => e
|
65
65
|
raise e unless e.name.to_s == operator.to_s
|
66
|
-
raise Sass::SyntaxError.new("Undefined unary operation: \"#{@operator} #{
|
66
|
+
raise Sass::SyntaxError.new("Undefined unary operation: \"#{@operator} #{literal}\".")
|
67
67
|
end
|
68
68
|
end
|
69
69
|
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Sass
|
2
|
+
module Script
|
3
|
+
# A SassScript parse node representing a variable.
|
4
|
+
class Variable < Node
|
5
|
+
# The name of the variable.
|
6
|
+
#
|
7
|
+
# @return [String]
|
8
|
+
attr_reader :name
|
9
|
+
|
10
|
+
# The underscored name of the variable.
|
11
|
+
#
|
12
|
+
# @return [String]
|
13
|
+
attr_reader :underscored_name
|
14
|
+
|
15
|
+
# @param name [String] See \{#name}
|
16
|
+
def initialize(name)
|
17
|
+
@name = name
|
18
|
+
@underscored_name = name.gsub(/-/,"_")
|
19
|
+
super()
|
20
|
+
end
|
21
|
+
|
22
|
+
# @return [String] A string representation of the variable
|
23
|
+
def inspect(opts = {})
|
24
|
+
"$#{dasherize(name, opts)}"
|
25
|
+
end
|
26
|
+
alias_method :to_sass, :inspect
|
27
|
+
|
28
|
+
# Returns an empty array.
|
29
|
+
#
|
30
|
+
# @return [Array<Node>] empty
|
31
|
+
# @see Node#children
|
32
|
+
def children
|
33
|
+
[]
|
34
|
+
end
|
35
|
+
|
36
|
+
# @see Node#deep_copy
|
37
|
+
def deep_copy
|
38
|
+
dup
|
39
|
+
end
|
40
|
+
|
41
|
+
protected
|
42
|
+
|
43
|
+
# Evaluates the variable.
|
44
|
+
#
|
45
|
+
# @param environment [Sass::Environment] The environment in which to evaluate the SassScript
|
46
|
+
# @return [Literal] The SassScript object that is the value of the variable
|
47
|
+
# @raise [Sass::SyntaxError] if the variable is undefined
|
48
|
+
def _perform(environment)
|
49
|
+
raise SyntaxError.new("Undefined variable: \"$#{name}\".") unless val = environment.var(name)
|
50
|
+
if val.is_a?(Number)
|
51
|
+
val = val.dup
|
52
|
+
val.original = nil
|
53
|
+
end
|
54
|
+
return val
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
data/lib/sass/scss/css_parser.rb
CHANGED
@@ -11,7 +11,7 @@ module Sass
|
|
11
11
|
|
12
12
|
def placeholder_selector; nil; end
|
13
13
|
def parent_selector; nil; end
|
14
|
-
def interpolation
|
14
|
+
def interpolation; nil; end
|
15
15
|
def use_css_import?; true; end
|
16
16
|
|
17
17
|
def block_child(context)
|
@@ -25,14 +25,8 @@ module Sass
|
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
28
|
-
def nested_properties!(node)
|
29
|
-
expected('expression (e.g. 1px, bold)')
|
30
|
-
end
|
31
|
-
|
32
|
-
def ruleset
|
33
|
-
start_pos = source_position
|
34
|
-
return unless (selector = selector_comma_sequence)
|
35
|
-
block(node(Sass::Tree::RuleNode.new(selector, range(start_pos)), start_pos), :ruleset)
|
28
|
+
def nested_properties!(node, space)
|
29
|
+
expected('expression (e.g. 1px, bold)');
|
36
30
|
end
|
37
31
|
|
38
32
|
@sass_script_parser = Class.new(Sass::Script::CssParser)
|
data/lib/sass/scss/parser.rb
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
# -*- coding: utf-8 -*-
|
2
1
|
require 'set'
|
3
2
|
|
4
3
|
module Sass
|
@@ -6,30 +5,16 @@ module Sass
|
|
6
5
|
# The parser for SCSS.
|
7
6
|
# It parses a string of code into a tree of {Sass::Tree::Node}s.
|
8
7
|
class Parser
|
9
|
-
# Expose for the SASS parser.
|
10
|
-
attr_accessor :offset
|
11
|
-
|
12
8
|
# @param str [String, StringScanner] The source document to parse.
|
13
9
|
# Note that `Parser` *won't* raise a nice error message if this isn't properly parsed;
|
14
10
|
# for that, you should use the higher-level {Sass::Engine} or {Sass::CSS}.
|
15
|
-
# @param filename [String] The name of the file being parsed. Used for
|
16
|
-
#
|
17
|
-
# @param importer [Sass::Importers::Base] The importer used to import the
|
18
|
-
# file being parsed. Used for source maps.
|
19
|
-
# @param line [Fixnum] The 1-based line on which the source string appeared,
|
11
|
+
# @param filename [String] The name of the file being parsed. Used for warnings.
|
12
|
+
# @param line [Fixnum] The line on which the source string appeared,
|
20
13
|
# if it's part of another document.
|
21
|
-
|
22
|
-
# which the source string starts. Used for error reporting and sourcemap
|
23
|
-
# building.
|
24
|
-
# @comment
|
25
|
-
# rubocop:disable ParameterLists
|
26
|
-
def initialize(str, filename, importer, line = 1, offset = 1)
|
27
|
-
# rubocop:enable ParameterLists
|
14
|
+
def initialize(str, filename, line = 1)
|
28
15
|
@template = str
|
29
16
|
@filename = filename
|
30
|
-
@importer = importer
|
31
17
|
@line = line
|
32
|
-
@offset = offset
|
33
18
|
@strs = []
|
34
19
|
end
|
35
20
|
|
@@ -40,7 +25,7 @@ module Sass
|
|
40
25
|
def parse
|
41
26
|
init_scanner!
|
42
27
|
root = stylesheet
|
43
|
-
expected("selector or at-rule") unless
|
28
|
+
expected("selector or at-rule") unless @scanner.eos?
|
44
29
|
root
|
45
30
|
end
|
46
31
|
|
@@ -48,7 +33,7 @@ module Sass
|
|
48
33
|
# Note that this won't assert that the identifier takes up the entire input string;
|
49
34
|
# it's meant to be used with `StringScanner`s as part of other parsers.
|
50
35
|
#
|
51
|
-
# @return [Array<String, Sass::Script::
|
36
|
+
# @return [Array<String, Sass::Script::Node>, nil]
|
52
37
|
# The interpolated identifier, or nil if none could be parsed
|
53
38
|
def parse_interp_ident
|
54
39
|
init_scanner!
|
@@ -63,22 +48,10 @@ module Sass
|
|
63
48
|
def parse_media_query_list
|
64
49
|
init_scanner!
|
65
50
|
ql = media_query_list
|
66
|
-
expected("media query list") unless
|
51
|
+
expected("media query list") unless @scanner.eos?
|
67
52
|
ql
|
68
53
|
end
|
69
54
|
|
70
|
-
# Parses an at-root query.
|
71
|
-
#
|
72
|
-
# @return [Array<String, Sass::Script;:Tree::Node>] The interpolated query.
|
73
|
-
# @raise [Sass::SyntaxError] if there's a syntax error in the query,
|
74
|
-
# or if it doesn't take up the entire input string.
|
75
|
-
def parse_at_root_query
|
76
|
-
init_scanner!
|
77
|
-
query = at_root_query
|
78
|
-
expected("@at-root query list") unless query && @scanner.eos?
|
79
|
-
query
|
80
|
-
end
|
81
|
-
|
82
55
|
# Parses a supports query condition.
|
83
56
|
#
|
84
57
|
# @return [Sass::Supports::Condition] The parsed condition
|
@@ -87,7 +60,7 @@ module Sass
|
|
87
60
|
def parse_supports_condition
|
88
61
|
init_scanner!
|
89
62
|
condition = supports_condition
|
90
|
-
expected("supports condition") unless
|
63
|
+
expected("supports condition") unless @scanner.eos?
|
91
64
|
condition
|
92
65
|
end
|
93
66
|
|
@@ -95,14 +68,6 @@ module Sass
|
|
95
68
|
|
96
69
|
include Sass::SCSS::RX
|
97
70
|
|
98
|
-
def source_position
|
99
|
-
Sass::Source::Position.new(@line, @offset)
|
100
|
-
end
|
101
|
-
|
102
|
-
def range(start_pos, end_pos = source_position)
|
103
|
-
Sass::Source::Range.new(start_pos, end_pos, @filename, @importer)
|
104
|
-
end
|
105
|
-
|
106
71
|
def init_scanner!
|
107
72
|
@scanner =
|
108
73
|
if @template.is_a?(StringScanner)
|
@@ -113,7 +78,7 @@ module Sass
|
|
113
78
|
end
|
114
79
|
|
115
80
|
def stylesheet
|
116
|
-
node = node(Sass::Tree::RootNode.new(@scanner.string)
|
81
|
+
node = node(Sass::Tree::RootNode.new(@scanner.string))
|
117
82
|
block_contents(node, :stylesheet) {s(node)}
|
118
83
|
end
|
119
84
|
|
@@ -147,33 +112,21 @@ module Sass
|
|
147
112
|
end
|
148
113
|
|
149
114
|
def process_comment(text, node)
|
150
|
-
silent = text =~
|
151
|
-
loud = !silent && text =~ %r{
|
115
|
+
silent = text =~ /^\/\//
|
116
|
+
loud = !silent && text =~ %r{^/[/*]!}
|
152
117
|
line = @line - text.count("\n")
|
153
118
|
|
154
119
|
if silent
|
155
|
-
value = [text.sub(
|
120
|
+
value = [text.sub(/^\s*\/\//, '/*').gsub(/^\s*\/\//, ' *') + ' */']
|
156
121
|
else
|
157
|
-
value = Sass::Engine.parse_interp(
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
if newline_before_comment
|
163
|
-
string_before_comment[newline_before_comment + 1..-1]
|
164
|
-
else
|
165
|
-
string_before_comment
|
166
|
-
end
|
167
|
-
value.unshift(last_line_before_comment.gsub(/[^\s]/, ' '))
|
122
|
+
value = Sass::Engine.parse_interp(text, line, @scanner.pos - text.size, :filename => @filename)
|
123
|
+
value.unshift(@scanner.
|
124
|
+
string[0...@scanner.pos].
|
125
|
+
reverse[/.*?\*\/(.*?)($|\Z)/, 1].
|
126
|
+
reverse.gsub(/[^\s]/, ' '))
|
168
127
|
end
|
169
128
|
|
170
|
-
type = if silent
|
171
|
-
:silent
|
172
|
-
elsif loud
|
173
|
-
:loud
|
174
|
-
else
|
175
|
-
:normal
|
176
|
-
end
|
129
|
+
type = if silent then :silent elsif loud then :loud else :normal end
|
177
130
|
comment = Sass::Tree::CommentNode.new(value, type)
|
178
131
|
comment.line = line
|
179
132
|
node << comment
|
@@ -181,29 +134,31 @@ module Sass
|
|
181
134
|
|
182
135
|
DIRECTIVES = Set[:mixin, :include, :function, :return, :debug, :warn, :for,
|
183
136
|
:each, :while, :if, :else, :extend, :import, :media, :charset, :content,
|
184
|
-
:_moz_document
|
137
|
+
:_moz_document]
|
185
138
|
|
186
139
|
PREFIXED_DIRECTIVES = Set[:supports]
|
187
140
|
|
188
141
|
def directive
|
189
|
-
start_pos = source_position
|
190
142
|
return unless tok(/@/)
|
191
143
|
name = tok!(IDENT)
|
192
144
|
ss
|
193
145
|
|
194
|
-
if
|
146
|
+
if dir = special_directive(name)
|
195
147
|
return dir
|
196
|
-
elsif
|
148
|
+
elsif dir = prefixed_directive(name)
|
197
149
|
return dir
|
198
150
|
end
|
199
151
|
|
200
|
-
|
152
|
+
# Most at-rules take expressions (e.g. @import),
|
153
|
+
# but some (e.g. @page) take selector-like arguments.
|
154
|
+
# Some take no arguments at all.
|
155
|
+
val = expr || selector
|
201
156
|
val = val ? ["@#{name} "] + Sass::Util.strip_string_array(val) : ["@#{name}"]
|
202
|
-
directive_body(val
|
157
|
+
directive_body(val)
|
203
158
|
end
|
204
159
|
|
205
|
-
def directive_body(value
|
206
|
-
node = Sass::Tree::DirectiveNode.new(value)
|
160
|
+
def directive_body(value)
|
161
|
+
node = node(Sass::Tree::DirectiveNode.new(value))
|
207
162
|
|
208
163
|
if tok(/\{/)
|
209
164
|
node.has_children = true
|
@@ -211,32 +166,31 @@ module Sass
|
|
211
166
|
tok!(/\}/)
|
212
167
|
end
|
213
168
|
|
214
|
-
node
|
169
|
+
node
|
215
170
|
end
|
216
171
|
|
217
|
-
def special_directive(name
|
172
|
+
def special_directive(name)
|
218
173
|
sym = name.gsub('-', '_').to_sym
|
219
|
-
DIRECTIVES.include?(sym) && send("#{sym}_directive"
|
174
|
+
DIRECTIVES.include?(sym) && send("#{sym}_directive")
|
220
175
|
end
|
221
176
|
|
222
|
-
def prefixed_directive(name
|
223
|
-
sym =
|
224
|
-
PREFIXED_DIRECTIVES.include?(sym) && send("#{sym}_directive", name
|
177
|
+
def prefixed_directive(name)
|
178
|
+
sym = name.gsub(/^-[a-z0-9]+-/i, '').gsub('-', '_').to_sym
|
179
|
+
PREFIXED_DIRECTIVES.include?(sym) && send("#{sym}_directive", name)
|
225
180
|
end
|
226
181
|
|
227
|
-
def mixin_directive
|
182
|
+
def mixin_directive
|
228
183
|
name = tok! IDENT
|
229
184
|
args, splat = sass_script(:parse_mixin_definition_arglist)
|
230
185
|
ss
|
231
|
-
block(node(Sass::Tree::MixinDefNode.new(name, args, splat)
|
186
|
+
block(node(Sass::Tree::MixinDefNode.new(name, args, splat)), :directive)
|
232
187
|
end
|
233
188
|
|
234
|
-
def include_directive
|
189
|
+
def include_directive
|
235
190
|
name = tok! IDENT
|
236
|
-
args, keywords, splat
|
191
|
+
args, keywords, splat = sass_script(:parse_mixin_include_arglist)
|
237
192
|
ss
|
238
|
-
include_node = node(
|
239
|
-
Sass::Tree::MixinNode.new(name, args, keywords, splat, kwarg_splat), start_pos)
|
193
|
+
include_node = node(Sass::Tree::MixinNode.new(name, args, keywords, splat))
|
240
194
|
if tok?(/\{/)
|
241
195
|
include_node.has_children = true
|
242
196
|
block(include_node, :directive)
|
@@ -245,31 +199,31 @@ module Sass
|
|
245
199
|
end
|
246
200
|
end
|
247
201
|
|
248
|
-
def content_directive
|
202
|
+
def content_directive
|
249
203
|
ss
|
250
|
-
node(Sass::Tree::ContentNode.new
|
204
|
+
node(Sass::Tree::ContentNode.new)
|
251
205
|
end
|
252
206
|
|
253
|
-
def function_directive
|
207
|
+
def function_directive
|
254
208
|
name = tok! IDENT
|
255
209
|
args, splat = sass_script(:parse_function_definition_arglist)
|
256
210
|
ss
|
257
|
-
block(node(Sass::Tree::FunctionNode.new(name, args, splat)
|
211
|
+
block(node(Sass::Tree::FunctionNode.new(name, args, splat)), :function)
|
258
212
|
end
|
259
213
|
|
260
|
-
def return_directive
|
261
|
-
node(Sass::Tree::ReturnNode.new(sass_script(:parse))
|
214
|
+
def return_directive
|
215
|
+
node(Sass::Tree::ReturnNode.new(sass_script(:parse)))
|
262
216
|
end
|
263
217
|
|
264
|
-
def debug_directive
|
265
|
-
node(Sass::Tree::DebugNode.new(sass_script(:parse))
|
218
|
+
def debug_directive
|
219
|
+
node(Sass::Tree::DebugNode.new(sass_script(:parse)))
|
266
220
|
end
|
267
221
|
|
268
|
-
def warn_directive
|
269
|
-
node(Sass::Tree::WarnNode.new(sass_script(:parse))
|
222
|
+
def warn_directive
|
223
|
+
node(Sass::Tree::WarnNode.new(sass_script(:parse)))
|
270
224
|
end
|
271
225
|
|
272
|
-
def for_directive
|
226
|
+
def for_directive
|
273
227
|
tok!(/\$/)
|
274
228
|
var = tok! IDENT
|
275
229
|
ss
|
@@ -283,37 +237,31 @@ module Sass
|
|
283
237
|
to = sass_script(:parse)
|
284
238
|
ss
|
285
239
|
|
286
|
-
block(node(Sass::Tree::ForNode.new(var, from, to, exclusive)
|
240
|
+
block(node(Sass::Tree::ForNode.new(var, from, to, exclusive)), :directive)
|
287
241
|
end
|
288
242
|
|
289
|
-
def each_directive
|
243
|
+
def each_directive
|
290
244
|
tok!(/\$/)
|
291
|
-
|
245
|
+
var = tok! IDENT
|
292
246
|
ss
|
293
|
-
while tok(/,/)
|
294
|
-
ss
|
295
|
-
tok!(/\$/)
|
296
|
-
vars << tok!(IDENT)
|
297
|
-
ss
|
298
|
-
end
|
299
247
|
|
300
248
|
tok!(/in/)
|
301
249
|
list = sass_script(:parse)
|
302
250
|
ss
|
303
251
|
|
304
|
-
block(node(Sass::Tree::EachNode.new(
|
252
|
+
block(node(Sass::Tree::EachNode.new(var, list)), :directive)
|
305
253
|
end
|
306
254
|
|
307
|
-
def while_directive
|
255
|
+
def while_directive
|
308
256
|
expr = sass_script(:parse)
|
309
257
|
ss
|
310
|
-
block(node(Sass::Tree::WhileNode.new(expr)
|
258
|
+
block(node(Sass::Tree::WhileNode.new(expr)), :directive)
|
311
259
|
end
|
312
260
|
|
313
|
-
def if_directive
|
261
|
+
def if_directive
|
314
262
|
expr = sass_script(:parse)
|
315
263
|
ss
|
316
|
-
node = block(node(Sass::Tree::IfNode.new(expr)
|
264
|
+
node = block(node(Sass::Tree::IfNode.new(expr)), :directive)
|
317
265
|
pos = @scanner.pos
|
318
266
|
line = @line
|
319
267
|
ss
|
@@ -328,11 +276,10 @@ module Sass
|
|
328
276
|
end
|
329
277
|
|
330
278
|
def else_block(node)
|
331
|
-
start_pos = source_position
|
332
279
|
return unless tok(/@else/)
|
333
280
|
ss
|
334
281
|
else_node = block(
|
335
|
-
|
282
|
+
Sass::Tree::IfNode.new((sass_script(:parse) if tok(/if/))),
|
336
283
|
:directive)
|
337
284
|
node.add_else(else_node)
|
338
285
|
pos = @scanner.pos
|
@@ -348,20 +295,18 @@ module Sass
|
|
348
295
|
end
|
349
296
|
end
|
350
297
|
|
351
|
-
def else_directive
|
298
|
+
def else_directive
|
352
299
|
err("Invalid CSS: @else must come after @if")
|
353
300
|
end
|
354
301
|
|
355
|
-
def extend_directive
|
356
|
-
|
357
|
-
@expected = "selector"
|
358
|
-
selector = Sass::Util.strip_string_array(expr!(:almost_any_value))
|
302
|
+
def extend_directive
|
303
|
+
selector = expr!(:selector_sequence)
|
359
304
|
optional = tok(OPTIONAL)
|
360
305
|
ss
|
361
|
-
node(Sass::Tree::ExtendNode.new(selector, !!optional
|
306
|
+
node(Sass::Tree::ExtendNode.new(selector, !!optional))
|
362
307
|
end
|
363
308
|
|
364
|
-
def import_directive
|
309
|
+
def import_directive
|
365
310
|
values = []
|
366
311
|
|
367
312
|
loop do
|
@@ -371,40 +316,42 @@ module Sass
|
|
371
316
|
ss
|
372
317
|
end
|
373
318
|
|
374
|
-
values
|
319
|
+
return values
|
375
320
|
end
|
376
321
|
|
377
322
|
def import_arg
|
378
|
-
|
379
|
-
return unless (str =
|
323
|
+
line = @line
|
324
|
+
return unless (str = tok(STRING)) || (uri = tok?(/url\(/i))
|
380
325
|
if uri
|
381
326
|
str = sass_script(:parse_string)
|
382
327
|
ss
|
383
328
|
media = media_query_list
|
384
329
|
ss
|
385
|
-
return node(Tree::CssImportNode.new(str, media.to_a)
|
330
|
+
return node(Tree::CssImportNode.new(str, media.to_a))
|
386
331
|
end
|
332
|
+
|
333
|
+
path = @scanner[1] || @scanner[2]
|
387
334
|
ss
|
388
335
|
|
389
336
|
media = media_query_list
|
390
|
-
if
|
391
|
-
|
392
|
-
|
337
|
+
if path =~ /^(https?:)?\/\// || media || use_css_import?
|
338
|
+
node = Sass::Tree::CssImportNode.new(str, media.to_a)
|
339
|
+
else
|
340
|
+
node = Sass::Tree::ImportNode.new(path.strip)
|
393
341
|
end
|
394
|
-
|
395
|
-
node
|
342
|
+
node.line = line
|
343
|
+
node
|
396
344
|
end
|
397
345
|
|
398
346
|
def use_css_import?; false; end
|
399
347
|
|
400
|
-
def media_directive
|
401
|
-
block(node(Sass::Tree::MediaNode.new(expr!(:media_query_list).to_a)
|
348
|
+
def media_directive
|
349
|
+
block(node(Sass::Tree::MediaNode.new(expr!(:media_query_list).to_a)), :directive)
|
402
350
|
end
|
403
351
|
|
404
352
|
# http://www.w3.org/TR/css3-mediaqueries/#syntax
|
405
353
|
def media_query_list
|
406
|
-
query = media_query
|
407
|
-
return unless query
|
354
|
+
return unless query = media_query
|
408
355
|
queries = [query]
|
409
356
|
|
410
357
|
ss
|
@@ -417,7 +364,7 @@ module Sass
|
|
417
364
|
end
|
418
365
|
|
419
366
|
def media_query
|
420
|
-
if
|
367
|
+
if ident1 = interp_ident
|
421
368
|
ss
|
422
369
|
ident2 = interp_ident
|
423
370
|
ss
|
@@ -437,8 +384,7 @@ module Sass
|
|
437
384
|
if query
|
438
385
|
expr = expr!(:media_expr)
|
439
386
|
else
|
440
|
-
expr = media_expr
|
441
|
-
return unless expr
|
387
|
+
return unless expr = media_expr
|
442
388
|
end
|
443
389
|
query ||= Sass::Media::Query.new([], [], [])
|
444
390
|
query.expressions << expr
|
@@ -451,9 +397,8 @@ module Sass
|
|
451
397
|
query
|
452
398
|
end
|
453
399
|
|
454
|
-
def
|
455
|
-
interp = interpolation
|
456
|
-
return interp if interp
|
400
|
+
def media_expr
|
401
|
+
interp = interpolation and return interp
|
457
402
|
return unless tok(/\(/)
|
458
403
|
res = ['(']
|
459
404
|
ss
|
@@ -469,15 +414,11 @@ module Sass
|
|
469
414
|
res
|
470
415
|
end
|
471
416
|
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
alias_method :at_root_query, :query_expr
|
476
|
-
|
477
|
-
def charset_directive(start_pos)
|
478
|
-
name = expr!(:string)
|
417
|
+
def charset_directive
|
418
|
+
tok! STRING
|
419
|
+
name = @scanner[1] || @scanner[2]
|
479
420
|
ss
|
480
|
-
node(Sass::Tree::CharsetNode.new(name)
|
421
|
+
node(Sass::Tree::CharsetNode.new(name))
|
481
422
|
end
|
482
423
|
|
483
424
|
# The document directive is specified in
|
@@ -488,65 +429,34 @@ module Sass
|
|
488
429
|
# We could parse all document directives according to Mozilla's syntax,
|
489
430
|
# but if someone's using e.g. @-webkit-document we don't want them to
|
490
431
|
# think WebKit works sans quotes.
|
491
|
-
def _moz_document_directive
|
432
|
+
def _moz_document_directive
|
492
433
|
res = ["@-moz-document "]
|
493
434
|
loop do
|
494
|
-
res << str
|
495
|
-
|
496
|
-
|
497
|
-
else
|
498
|
-
break
|
499
|
-
end
|
435
|
+
res << str{ss} << expr!(:moz_document_function)
|
436
|
+
break unless c = tok(/,/)
|
437
|
+
res << c
|
500
438
|
end
|
501
|
-
directive_body(res.flatten
|
439
|
+
directive_body(res.flatten)
|
502
440
|
end
|
503
441
|
|
504
442
|
def moz_document_function
|
505
|
-
val = interp_uri || _interp_string(:url_prefix) ||
|
443
|
+
return unless val = interp_uri || _interp_string(:url_prefix) ||
|
506
444
|
_interp_string(:domain) || function(!:allow_var) || interpolation
|
507
|
-
return unless val
|
508
445
|
ss
|
509
446
|
val
|
510
447
|
end
|
511
448
|
|
512
|
-
def at_root_directive(start_pos)
|
513
|
-
if tok?(/\(/) && (expr = at_root_query)
|
514
|
-
return block(node(Sass::Tree::AtRootNode.new(expr), start_pos), :directive)
|
515
|
-
end
|
516
|
-
|
517
|
-
at_root_node = node(Sass::Tree::AtRootNode.new, start_pos)
|
518
|
-
rule_node = ruleset
|
519
|
-
return block(at_root_node, :stylesheet) unless rule_node
|
520
|
-
at_root_node << rule_node
|
521
|
-
at_root_node
|
522
|
-
end
|
523
|
-
|
524
|
-
def at_root_directive_list
|
525
|
-
return unless (first = tok(IDENT))
|
526
|
-
arr = [first]
|
527
|
-
ss
|
528
|
-
while (e = tok(IDENT))
|
529
|
-
arr << e
|
530
|
-
ss
|
531
|
-
end
|
532
|
-
arr
|
533
|
-
end
|
534
|
-
|
535
|
-
def error_directive(start_pos)
|
536
|
-
node(Sass::Tree::ErrorNode.new(sass_script(:parse)), start_pos)
|
537
|
-
end
|
538
|
-
|
539
449
|
# http://www.w3.org/TR/css3-conditional/
|
540
|
-
def supports_directive(name
|
450
|
+
def supports_directive(name)
|
541
451
|
condition = expr!(:supports_condition)
|
542
|
-
node = Sass::Tree::SupportsNode.new(name, condition)
|
452
|
+
node = node(Sass::Tree::SupportsNode.new(name, condition))
|
543
453
|
|
544
454
|
tok!(/\{/)
|
545
455
|
node.has_children = true
|
546
456
|
block_contents(node, :directive)
|
547
457
|
tok!(/\}/)
|
548
458
|
|
549
|
-
node
|
459
|
+
node
|
550
460
|
end
|
551
461
|
|
552
462
|
def supports_condition
|
@@ -560,21 +470,20 @@ module Sass
|
|
560
470
|
end
|
561
471
|
|
562
472
|
def supports_operator
|
563
|
-
cond = supports_condition_in_parens
|
564
|
-
return unless
|
565
|
-
|
473
|
+
return unless cond = supports_condition_in_parens
|
474
|
+
return cond unless op = tok(/and|or/i)
|
475
|
+
begin
|
566
476
|
ss
|
567
477
|
cond = Sass::Supports::Operator.new(
|
568
478
|
cond, expr!(:supports_condition_in_parens), op)
|
569
|
-
end
|
479
|
+
end while op = tok(/and|or/i)
|
570
480
|
cond
|
571
481
|
end
|
572
482
|
|
573
483
|
def supports_condition_in_parens
|
574
|
-
interp = supports_interpolation
|
575
|
-
return interp if interp
|
484
|
+
interp = supports_interpolation and return interp
|
576
485
|
return unless tok(/\(/); ss
|
577
|
-
if
|
486
|
+
if cond = supports_condition
|
578
487
|
tok!(/\)/); ss
|
579
488
|
cond
|
580
489
|
else
|
@@ -592,33 +501,19 @@ module Sass
|
|
592
501
|
end
|
593
502
|
|
594
503
|
def supports_interpolation
|
595
|
-
interp = interpolation
|
596
|
-
return unless interp
|
504
|
+
return unless interp = interpolation
|
597
505
|
ss
|
598
506
|
Sass::Supports::Interpolation.new(interp)
|
599
507
|
end
|
600
508
|
|
601
509
|
def variable
|
602
510
|
return unless tok(/\$/)
|
603
|
-
start_pos = source_position
|
604
511
|
name = tok!(IDENT)
|
605
512
|
ss; tok!(/:/); ss
|
606
513
|
|
607
514
|
expr = sass_script(:parse)
|
608
|
-
|
609
|
-
|
610
|
-
if flag_name == 'default'
|
611
|
-
guarded ||= true
|
612
|
-
elsif flag_name == 'global'
|
613
|
-
global ||= true
|
614
|
-
else
|
615
|
-
raise Sass::SyntaxError.new("Invalid flag \"!#{flag_name}\".", :line => @line)
|
616
|
-
end
|
617
|
-
ss
|
618
|
-
end
|
619
|
-
|
620
|
-
result = Sass::Tree::VariableNode.new(name, expr, guarded, global)
|
621
|
-
node(result, start_pos)
|
515
|
+
guarded = tok(DEFAULT)
|
516
|
+
node(Sass::Tree::VariableNode.new(name, expr, guarded))
|
622
517
|
end
|
623
518
|
|
624
519
|
def operator
|
@@ -630,10 +525,8 @@ module Sass
|
|
630
525
|
end
|
631
526
|
|
632
527
|
def ruleset
|
633
|
-
|
634
|
-
|
635
|
-
block(node(
|
636
|
-
Sass::Tree::RuleNode.new(rules, range(start_pos)), start_pos), :ruleset)
|
528
|
+
return unless rules = selector_sequence
|
529
|
+
block(node(Sass::Tree::RuleNode.new(rules.flatten.compact)), :ruleset)
|
637
530
|
end
|
638
531
|
|
639
532
|
def block(node, context)
|
@@ -664,239 +557,355 @@ module Sass
|
|
664
557
|
def has_children?(child_or_array)
|
665
558
|
return false unless child_or_array
|
666
559
|
return child_or_array.last.has_children if child_or_array.is_a?(Array)
|
667
|
-
child_or_array.has_children
|
560
|
+
return child_or_array.has_children
|
668
561
|
end
|
669
562
|
|
670
|
-
#
|
671
|
-
#
|
672
|
-
#
|
673
|
-
#
|
674
|
-
#
|
675
|
-
#
|
676
|
-
# * If the entity doesn't start with an identifier followed by a colon,
|
677
|
-
# it's a selector. There are some additional mostly-unimportant cases
|
678
|
-
# here to support various declaration hacks.
|
679
|
-
#
|
680
|
-
# * If the colon is followed by another colon, it's a selector.
|
563
|
+
# This is a nasty hack, and the only place in the parser
|
564
|
+
# that requires a large amount of backtracking.
|
565
|
+
# The reason is that we can't figure out if certain strings
|
566
|
+
# are declarations or rulesets with fixed finite lookahead.
|
567
|
+
# For example, "foo:bar baz baz baz..." could be either a property
|
568
|
+
# or a selector.
|
681
569
|
#
|
682
|
-
#
|
683
|
-
#
|
684
|
-
#
|
570
|
+
# To handle this, we simply check if it works as a property
|
571
|
+
# (which is the most common case)
|
572
|
+
# and, if it doesn't, try it as a ruleset.
|
685
573
|
#
|
686
|
-
#
|
687
|
-
#
|
688
|
-
#
|
689
|
-
#
|
690
|
-
# * If the declaration value value valid but is followed by "{", backtrack
|
691
|
-
# and parse it as a selector anyway. This ensures that ".foo:bar {" is
|
692
|
-
# always parsed as a selector and never as a property with nested
|
693
|
-
# properties beneath it.
|
574
|
+
# We could eke some more efficiency out of this
|
575
|
+
# by handling some easy cases (first token isn't an identifier,
|
576
|
+
# no colon after the identifier, whitespace after the colon),
|
577
|
+
# but I'm not sure the gains would be worth the added complexity.
|
694
578
|
def declaration_or_ruleset
|
695
|
-
|
696
|
-
|
579
|
+
old_use_property_exception, @use_property_exception =
|
580
|
+
@use_property_exception, false
|
581
|
+
decl_err = catch_error do
|
582
|
+
decl = declaration
|
583
|
+
unless decl && decl.has_children
|
584
|
+
# We want an exception if it's not there,
|
585
|
+
# but we don't want to consume if it is
|
586
|
+
tok!(/[;}]/) unless tok?(/[;}]/)
|
587
|
+
end
|
588
|
+
return decl
|
589
|
+
end
|
697
590
|
|
698
|
-
|
699
|
-
|
700
|
-
|
701
|
-
|
702
|
-
|
703
|
-
|
704
|
-
|
591
|
+
ruleset_err = catch_error {return ruleset}
|
592
|
+
rethrow(@use_property_exception ? decl_err : ruleset_err)
|
593
|
+
ensure
|
594
|
+
@use_property_exception = old_use_property_exception
|
595
|
+
end
|
596
|
+
|
597
|
+
def selector_sequence
|
598
|
+
if sel = tok(STATIC_SELECTOR, true)
|
599
|
+
return [sel]
|
705
600
|
end
|
706
601
|
|
707
|
-
|
708
|
-
|
602
|
+
rules = []
|
603
|
+
return unless v = selector
|
604
|
+
rules.concat v
|
605
|
+
|
606
|
+
ws = ''
|
607
|
+
while tok(/,/)
|
608
|
+
ws << str {ss}
|
609
|
+
if v = selector
|
610
|
+
rules << ',' << ws
|
611
|
+
rules.concat v
|
612
|
+
ws = ''
|
613
|
+
end
|
709
614
|
end
|
615
|
+
rules
|
616
|
+
end
|
710
617
|
|
711
|
-
|
712
|
-
|
618
|
+
def selector
|
619
|
+
return unless sel = _selector
|
620
|
+
sel.to_a
|
713
621
|
end
|
714
622
|
|
715
|
-
|
716
|
-
|
717
|
-
|
718
|
-
|
719
|
-
|
720
|
-
|
721
|
-
|
722
|
-
|
723
|
-
|
724
|
-
|
725
|
-
|
726
|
-
# val" hacks.
|
727
|
-
name_start_pos = source_position
|
728
|
-
if (s = tok(/[:\*\.]|\#(?!\{)/))
|
729
|
-
name = [s, str {ss}]
|
730
|
-
return name unless (ident = interp_ident)
|
731
|
-
name << ident
|
732
|
-
else
|
733
|
-
return unless (name = interp_ident)
|
734
|
-
name = Array(name)
|
623
|
+
def selector_comma_sequence
|
624
|
+
return unless sel = _selector
|
625
|
+
selectors = [sel]
|
626
|
+
ws = ''
|
627
|
+
while tok(/,/)
|
628
|
+
ws << str{ss}
|
629
|
+
if sel = _selector
|
630
|
+
selectors << sel
|
631
|
+
selectors[-1] = Selector::Sequence.new(["\n"] + selectors.last.members) if ws.include?("\n")
|
632
|
+
ws = ''
|
633
|
+
end
|
735
634
|
end
|
635
|
+
Selector::CommaSequence.new(selectors)
|
636
|
+
end
|
736
637
|
|
737
|
-
|
738
|
-
|
638
|
+
def _selector
|
639
|
+
# The combinator here allows the "> E" hack
|
640
|
+
return unless val = combinator || simple_selector_sequence
|
641
|
+
nl = str{ss}.include?("\n")
|
642
|
+
res = []
|
643
|
+
res << val
|
644
|
+
res << "\n" if nl
|
645
|
+
|
646
|
+
while val = combinator || simple_selector_sequence
|
647
|
+
res << val
|
648
|
+
res << "\n" if str{ss}.include?("\n")
|
649
|
+
end
|
650
|
+
Selector::Sequence.new(res.compact)
|
651
|
+
end
|
652
|
+
|
653
|
+
def combinator
|
654
|
+
tok(PLUS) || tok(GREATER) || tok(TILDE) || reference_combinator
|
655
|
+
end
|
656
|
+
|
657
|
+
def reference_combinator
|
658
|
+
return unless tok(/\//)
|
659
|
+
res = ['/']
|
660
|
+
ns, name = expr!(:qualified_name)
|
661
|
+
res << ns << '|' if ns
|
662
|
+
res << name << tok!(/\//)
|
663
|
+
res = res.flatten
|
664
|
+
res = res.join '' if res.all? {|e| e.is_a?(String)}
|
665
|
+
res
|
666
|
+
end
|
667
|
+
|
668
|
+
def simple_selector_sequence
|
669
|
+
# Returning expr by default allows for stuff like
|
670
|
+
# http://www.w3.org/TR/css3-animations/#keyframes-
|
671
|
+
return expr(!:allow_var) unless e = element_name || id_selector ||
|
672
|
+
class_selector || placeholder_selector || attrib || pseudo ||
|
673
|
+
parent_selector || interpolation_selector
|
674
|
+
res = [e]
|
675
|
+
|
676
|
+
# The tok(/\*/) allows the "E*" hack
|
677
|
+
while v = id_selector || class_selector || placeholder_selector || attrib ||
|
678
|
+
pseudo || interpolation_selector ||
|
679
|
+
(tok(/\*/) && Selector::Universal.new(nil))
|
680
|
+
res << v
|
739
681
|
end
|
740
|
-
|
741
|
-
|
742
|
-
|
743
|
-
|
744
|
-
|
745
|
-
|
746
|
-
|
747
|
-
|
748
|
-
|
749
|
-
|
750
|
-
|
751
|
-
|
752
|
-
|
753
|
-
|
754
|
-
if tok?(/\{/)
|
755
|
-
# Properties that are ambiguous with selectors can't have additional
|
756
|
-
# properties nested beneath them.
|
757
|
-
tok!(/;/) if could_be_selector
|
758
|
-
elsif !tok?(/[;{}]/)
|
759
|
-
# We want an exception if there's no valid end-of-property character
|
760
|
-
# exists, but we don't want to consume it if it does.
|
761
|
-
tok!(/[;{}]/)
|
682
|
+
|
683
|
+
pos = @scanner.pos
|
684
|
+
line = @line
|
685
|
+
if sel = str? {simple_selector_sequence}
|
686
|
+
@scanner.pos = pos
|
687
|
+
@line = line
|
688
|
+
begin
|
689
|
+
# If we see "*E", don't force a throw because this could be the
|
690
|
+
# "*prop: val" hack.
|
691
|
+
expected('"{"') if res.length == 1 && res[0].is_a?(Selector::Universal)
|
692
|
+
throw_error {expected('"{"')}
|
693
|
+
rescue Sass::SyntaxError => e
|
694
|
+
e.message << "\n\n\"#{sel}\" may only be used at the beginning of a compound selector."
|
695
|
+
raise e
|
762
696
|
end
|
763
697
|
end
|
764
698
|
|
765
|
-
|
766
|
-
|
699
|
+
Selector::SimpleSequence.new(res, tok(/!/))
|
700
|
+
end
|
701
|
+
|
702
|
+
def parent_selector
|
703
|
+
return unless tok(/&/)
|
704
|
+
Selector::Parent.new
|
705
|
+
end
|
706
|
+
|
707
|
+
def class_selector
|
708
|
+
return unless tok(/\./)
|
709
|
+
@expected = "class name"
|
710
|
+
Selector::Class.new(merge(expr!(:interp_ident)))
|
711
|
+
end
|
767
712
|
|
768
|
-
|
769
|
-
|
770
|
-
|
771
|
-
|
713
|
+
def id_selector
|
714
|
+
return unless tok(/#(?!\{)/)
|
715
|
+
@expected = "id name"
|
716
|
+
Selector::Id.new(merge(expr!(:interp_name)))
|
717
|
+
end
|
718
|
+
|
719
|
+
def placeholder_selector
|
720
|
+
return unless tok(/%/)
|
721
|
+
@expected = "placeholder name"
|
722
|
+
Selector::Placeholder.new(merge(expr!(:interp_ident)))
|
723
|
+
end
|
772
724
|
|
773
|
-
|
725
|
+
def element_name
|
726
|
+
ns, name = Sass::Util.destructure(qualified_name(:allow_star_name))
|
727
|
+
return unless ns || name
|
728
|
+
|
729
|
+
if name == '*'
|
730
|
+
Selector::Universal.new(merge(ns))
|
731
|
+
else
|
732
|
+
Selector::Element.new(merge(name), merge(ns))
|
774
733
|
end
|
734
|
+
end
|
735
|
+
|
736
|
+
def qualified_name(allow_star_name=false)
|
737
|
+
return unless name = interp_ident || tok(/\*/) || (tok?(/\|/) && "")
|
738
|
+
return nil, name unless tok(/\|/)
|
775
739
|
|
776
|
-
|
740
|
+
return name, expr!(:interp_ident) unless allow_star_name
|
741
|
+
@expected = "identifier or *"
|
742
|
+
return name, interp_ident || tok!(/\*/)
|
743
|
+
end
|
744
|
+
|
745
|
+
def interpolation_selector
|
746
|
+
return unless script = interpolation
|
747
|
+
Selector::Interpolation.new(script)
|
748
|
+
end
|
749
|
+
|
750
|
+
def attrib
|
751
|
+
return unless tok(/\[/)
|
752
|
+
ss
|
753
|
+
ns, name = attrib_name!
|
777
754
|
ss
|
778
|
-
require_block = tok?(/\{/)
|
779
755
|
|
780
|
-
|
781
|
-
|
782
|
-
|
783
|
-
|
756
|
+
if op = tok(/=/) ||
|
757
|
+
tok(INCLUDES) ||
|
758
|
+
tok(DASHMATCH) ||
|
759
|
+
tok(PREFIXMATCH) ||
|
760
|
+
tok(SUFFIXMATCH) ||
|
761
|
+
tok(SUBSTRINGMATCH)
|
762
|
+
@expected = "identifier or string"
|
763
|
+
ss
|
764
|
+
val = interp_ident || expr!(:interp_string)
|
765
|
+
ss
|
766
|
+
end
|
767
|
+
flags = interp_ident || interp_string
|
768
|
+
tok!(/\]/)
|
784
769
|
|
785
|
-
|
786
|
-
nested_properties! node
|
770
|
+
Selector::Attribute.new(merge(name), merge(ns), op, merge(val), merge(flags))
|
787
771
|
end
|
788
772
|
|
789
|
-
|
790
|
-
|
791
|
-
|
792
|
-
|
793
|
-
|
794
|
-
|
795
|
-
|
796
|
-
|
797
|
-
|
798
|
-
|
799
|
-
|
800
|
-
|
801
|
-
|
802
|
-
|
803
|
-
sel << tok
|
773
|
+
def attrib_name!
|
774
|
+
if name_or_ns = interp_ident
|
775
|
+
# E, E|E
|
776
|
+
if tok(/\|(?!=)/)
|
777
|
+
ns = name_or_ns
|
778
|
+
name = interp_ident
|
779
|
+
else
|
780
|
+
name = name_or_ns
|
781
|
+
end
|
782
|
+
else
|
783
|
+
# *|E or |E
|
784
|
+
ns = [tok(/\*/) || ""]
|
785
|
+
tok!(/\|/)
|
786
|
+
name = expr!(:interp_ident)
|
804
787
|
end
|
805
|
-
|
788
|
+
return ns, name
|
806
789
|
end
|
807
790
|
|
808
|
-
def
|
809
|
-
tok(
|
810
|
-
|
811
|
-
|
812
|
-
|
813
|
-
|
814
|
-
|
815
|
-
|
816
|
-
|
817
|
-
|
818
|
-
|
819
|
-
)
|
820
|
-
|
821
|
-
|
791
|
+
def pseudo
|
792
|
+
return unless s = tok(/::?/)
|
793
|
+
@expected = "pseudoclass or pseudoelement"
|
794
|
+
name = expr!(:interp_ident)
|
795
|
+
if tok(/\(/)
|
796
|
+
ss
|
797
|
+
arg = expr!(:pseudo_arg)
|
798
|
+
while tok(/,/)
|
799
|
+
arg << ',' << str{ss}
|
800
|
+
arg.concat expr!(:pseudo_arg)
|
801
|
+
end
|
802
|
+
tok!(/\)/)
|
803
|
+
end
|
804
|
+
Selector::Pseudo.new(s == ':' ? :class : :element, merge(name), merge(arg))
|
805
|
+
end
|
806
|
+
|
807
|
+
def pseudo_arg
|
808
|
+
# In the CSS spec, every pseudo-class/element either takes a pseudo
|
809
|
+
# expression or a selector comma sequence as an argument. However, we
|
810
|
+
# don't want to have to know which takes which, so we handle both at
|
811
|
+
# once.
|
812
|
+
#
|
813
|
+
# However, there are some ambiguities between the two. For instance, "n"
|
814
|
+
# could start a pseudo expression like "n+1", or it could start a
|
815
|
+
# selector like "n|m". In order to handle this, we must regrettably
|
816
|
+
# backtrack.
|
817
|
+
expr, sel = nil, nil
|
818
|
+
pseudo_err = catch_error do
|
819
|
+
expr = pseudo_expr
|
820
|
+
next if tok?(/[,)]/)
|
821
|
+
expr = nil
|
822
|
+
expected '")"'
|
823
|
+
end
|
824
|
+
|
825
|
+
return expr if expr
|
826
|
+
sel_err = catch_error {sel = selector}
|
827
|
+
return sel if sel
|
828
|
+
rethrow pseudo_err if pseudo_err
|
829
|
+
rethrow sel_err if sel_err
|
830
|
+
return
|
831
|
+
end
|
832
|
+
|
833
|
+
def pseudo_expr
|
834
|
+
return unless e = tok(PLUS) || tok(/[-*]/) || tok(NUMBER) ||
|
835
|
+
interp_string || tok(IDENT) || interpolation
|
836
|
+
res = [e, str{ss}]
|
837
|
+
while e = tok(PLUS) || tok(/[-*]/) || tok(NUMBER) ||
|
838
|
+
interp_string || tok(IDENT) || interpolation
|
839
|
+
res << e << str{ss}
|
840
|
+
end
|
841
|
+
res
|
822
842
|
end
|
823
843
|
|
824
844
|
def declaration
|
825
|
-
# This allows the "*prop: val", ":prop: val", "
|
826
|
-
|
827
|
-
|
828
|
-
|
829
|
-
name = [s, str {ss}, *expr!(:interp_ident)]
|
845
|
+
# This allows the "*prop: val", ":prop: val", and ".prop: val" hacks
|
846
|
+
if s = tok(/[:\*\.]|\#(?!\{)/)
|
847
|
+
@use_property_exception = s !~ /[\.\#]/
|
848
|
+
name = [s, str{ss}, *expr!(:interp_ident)]
|
830
849
|
else
|
831
|
-
return unless
|
832
|
-
name =
|
850
|
+
return unless name = interp_ident
|
851
|
+
name = [name] if name.is_a?(String)
|
833
852
|
end
|
834
|
-
|
835
|
-
if (comment = tok(COMMENT))
|
853
|
+
if comment = tok(COMMENT)
|
836
854
|
name << comment
|
837
855
|
end
|
838
|
-
name_end_pos = source_position
|
839
856
|
ss
|
840
857
|
|
841
858
|
tok!(/:/)
|
842
|
-
|
843
|
-
value_start_pos = source_position
|
844
|
-
value = value!
|
845
|
-
value_end_pos = source_position
|
859
|
+
space, value = value!
|
846
860
|
ss
|
847
861
|
require_block = tok?(/\{/)
|
848
862
|
|
849
|
-
node = node(Sass::Tree::PropNode.new(name.flatten.compact, value, :new)
|
850
|
-
name_start_pos, value_end_pos)
|
851
|
-
node.name_source_range = range(name_start_pos, name_end_pos)
|
852
|
-
node.value_source_range = range(value_start_pos, value_end_pos)
|
863
|
+
node = node(Sass::Tree::PropNode.new(name.flatten.compact, value, :new))
|
853
864
|
|
854
865
|
return node unless require_block
|
855
|
-
nested_properties! node
|
866
|
+
nested_properties! node, space
|
856
867
|
end
|
857
868
|
|
858
869
|
def value!
|
859
|
-
|
860
|
-
|
861
|
-
str.line = source_position.line
|
862
|
-
str.source_range = range(source_position)
|
863
|
-
return str
|
864
|
-
end
|
870
|
+
space = !str {ss}.empty?
|
871
|
+
@use_property_exception ||= space || !tok?(IDENT)
|
865
872
|
|
866
|
-
|
873
|
+
return true, Sass::Script::String.new("") if tok?(/\{/)
|
867
874
|
# This is a bit of a dirty trick:
|
868
875
|
# if the value is completely static,
|
869
876
|
# we don't parse it at all, and instead return a plain old string
|
870
877
|
# containing the value.
|
871
878
|
# This results in a dramatic speed increase.
|
872
|
-
if
|
873
|
-
|
874
|
-
str.line = start_pos.line
|
875
|
-
str.source_range = range(start_pos)
|
876
|
-
return str
|
879
|
+
if val = tok(STATIC_VALUE, true)
|
880
|
+
return space, Sass::Script::String.new(val.strip)
|
877
881
|
end
|
878
|
-
sass_script(:parse)
|
882
|
+
return space, sass_script(:parse)
|
879
883
|
end
|
880
884
|
|
881
|
-
def nested_properties!(node)
|
885
|
+
def nested_properties!(node, space)
|
886
|
+
err(<<MESSAGE) unless space
|
887
|
+
Invalid CSS: a space is required between a property and its definition
|
888
|
+
when it has other properties nested beneath it.
|
889
|
+
MESSAGE
|
890
|
+
|
891
|
+
@use_property_exception = true
|
882
892
|
@expected = 'expression (e.g. 1px, bold) or "{"'
|
883
893
|
block(node, :property)
|
884
894
|
end
|
885
895
|
|
886
896
|
def expr(allow_var = true)
|
887
|
-
t = term(allow_var)
|
888
|
-
|
889
|
-
res = [t, str {ss}]
|
897
|
+
return unless t = term(allow_var)
|
898
|
+
res = [t, str{ss}]
|
890
899
|
|
891
900
|
while (o = operator) && (t = term(allow_var))
|
892
|
-
res << o << t << str
|
901
|
+
res << o << t << str{ss}
|
893
902
|
end
|
894
903
|
|
895
904
|
res.flatten
|
896
905
|
end
|
897
906
|
|
898
907
|
def term(allow_var)
|
899
|
-
e = tok(NUMBER) ||
|
908
|
+
if e = tok(NUMBER) ||
|
900
909
|
interp_uri ||
|
901
910
|
function(allow_var) ||
|
902
911
|
interp_string ||
|
@@ -904,42 +913,36 @@ module Sass
|
|
904
913
|
interp_ident ||
|
905
914
|
tok(HEXCOLOR) ||
|
906
915
|
(allow_var && var_expr)
|
907
|
-
|
916
|
+
return e
|
917
|
+
end
|
908
918
|
|
909
|
-
op = tok(/[+-]/)
|
910
|
-
return unless op
|
919
|
+
return unless op = tok(/[+-]/)
|
911
920
|
@expected = "number or function"
|
912
|
-
[op,
|
913
|
-
|
921
|
+
return [op, tok(NUMBER) || function(allow_var) ||
|
922
|
+
(allow_var && var_expr) || expr!(:interpolation)]
|
914
923
|
end
|
915
924
|
|
916
925
|
def function(allow_var)
|
917
|
-
name = tok(FUNCTION)
|
918
|
-
return unless name
|
926
|
+
return unless name = tok(FUNCTION)
|
919
927
|
if name == "expression(" || name == "calc("
|
920
928
|
str, _ = Sass::Shared.balance(@scanner, ?(, ?), 1)
|
921
929
|
[name, str]
|
922
930
|
else
|
923
|
-
[name, str
|
931
|
+
[name, str{ss}, expr(allow_var), tok!(/\)/)]
|
924
932
|
end
|
925
933
|
end
|
926
934
|
|
927
935
|
def var_expr
|
928
936
|
return unless tok(/\$/)
|
929
937
|
line = @line
|
930
|
-
var = Sass::Script::
|
938
|
+
var = Sass::Script::Variable.new(tok!(IDENT))
|
931
939
|
var.line = line
|
932
940
|
var
|
933
941
|
end
|
934
942
|
|
935
|
-
def interpolation
|
943
|
+
def interpolation
|
936
944
|
return unless tok(INTERP_START)
|
937
|
-
sass_script(:parse_interpolated
|
938
|
-
end
|
939
|
-
|
940
|
-
def string
|
941
|
-
return unless tok(STRING)
|
942
|
-
Sass::Script::Value::String.value(@scanner[1] || @scanner[2])
|
945
|
+
sass_script(:parse_interpolated)
|
943
946
|
end
|
944
947
|
|
945
948
|
def interp_string
|
@@ -951,8 +954,7 @@ module Sass
|
|
951
954
|
end
|
952
955
|
|
953
956
|
def _interp_string(type)
|
954
|
-
start = tok(Sass::Script::Lexer::STRING_REGULAR_EXPRESSIONS[type][false])
|
955
|
-
return unless start
|
957
|
+
return unless start = tok(Sass::Script::Lexer::STRING_REGULAR_EXPRESSIONS[type][false])
|
956
958
|
res = [start]
|
957
959
|
|
958
960
|
mid_re = Sass::Script::Lexer::STRING_REGULAR_EXPRESSIONS[type][true]
|
@@ -966,20 +968,21 @@ module Sass
|
|
966
968
|
end
|
967
969
|
|
968
970
|
def interp_ident(start = IDENT)
|
969
|
-
val = tok(start) || interpolation
|
970
|
-
return unless val
|
971
|
+
return unless val = tok(start) || interpolation || tok(IDENT_HYPHEN_INTERP, true)
|
971
972
|
res = [val]
|
972
|
-
while
|
973
|
+
while val = tok(NAME) || interpolation
|
973
974
|
res << val
|
974
975
|
end
|
975
976
|
res
|
976
977
|
end
|
977
978
|
|
978
979
|
def interp_ident_or_var
|
979
|
-
id = interp_ident
|
980
|
-
|
981
|
-
|
982
|
-
|
980
|
+
(id = interp_ident) and return id
|
981
|
+
(var = var_expr) and return [var]
|
982
|
+
end
|
983
|
+
|
984
|
+
def interp_name
|
985
|
+
interp_ident NAME
|
983
986
|
end
|
984
987
|
|
985
988
|
def str
|
@@ -993,35 +996,29 @@ module Sass
|
|
993
996
|
def str?
|
994
997
|
pos = @scanner.pos
|
995
998
|
line = @line
|
996
|
-
offset = @offset
|
997
999
|
@strs.push ""
|
998
1000
|
throw_error {yield} && @strs.last
|
999
1001
|
rescue Sass::SyntaxError
|
1000
1002
|
@scanner.pos = pos
|
1001
1003
|
@line = line
|
1002
|
-
@offset = offset
|
1003
1004
|
nil
|
1004
1005
|
ensure
|
1005
1006
|
@strs.pop
|
1006
1007
|
end
|
1007
1008
|
|
1008
|
-
def node(node
|
1009
|
-
node.line =
|
1010
|
-
node.source_range = range(start_pos, end_pos)
|
1009
|
+
def node(node)
|
1010
|
+
node.line = @line
|
1011
1011
|
node
|
1012
1012
|
end
|
1013
1013
|
|
1014
1014
|
@sass_script_parser = Class.new(Sass::Script::Parser)
|
1015
1015
|
@sass_script_parser.send(:include, ScriptParser)
|
1016
|
-
|
1017
|
-
|
1018
|
-
# @private
|
1019
|
-
attr_accessor :sass_script_parser
|
1020
|
-
end
|
1016
|
+
# @private
|
1017
|
+
def self.sass_script_parser; @sass_script_parser; end
|
1021
1018
|
|
1022
1019
|
def sass_script(*args)
|
1023
|
-
parser = self.class.sass_script_parser.new(@scanner, @line,
|
1024
|
-
|
1020
|
+
parser = self.class.sass_script_parser.new(@scanner, @line,
|
1021
|
+
@scanner.pos - (@scanner.string[0...@scanner.pos].rindex("\n") || 0))
|
1025
1022
|
result = parser.send(*args)
|
1026
1023
|
unless @strs.empty?
|
1027
1024
|
# Convert to CSS manually so that comments are ignored.
|
@@ -1029,7 +1026,6 @@ module Sass
|
|
1029
1026
|
@strs.each {|s| s << src}
|
1030
1027
|
end
|
1031
1028
|
@line = parser.line
|
1032
|
-
@offset = parser.offset
|
1033
1029
|
result
|
1034
1030
|
rescue Sass::SyntaxError => e
|
1035
1031
|
throw(:_sass_parser_error, true) if @throw_error
|
@@ -1044,51 +1040,41 @@ module Sass
|
|
1044
1040
|
:media_query => "media query (e.g. print, screen, print and screen)",
|
1045
1041
|
:media_query_list => "media query (e.g. print, screen, print and screen)",
|
1046
1042
|
:media_expr => "media expression (e.g. (min-device-width: 800px))",
|
1047
|
-
:
|
1048
|
-
:at_root_directive_list => '* or identifier',
|
1049
|
-
:pseudo_args => "expression (e.g. fr, 2n+1)",
|
1043
|
+
:pseudo_arg => "expression (e.g. fr, 2n+1)",
|
1050
1044
|
:interp_ident => "identifier",
|
1045
|
+
:interp_name => "identifier",
|
1051
1046
|
:qualified_name => "identifier",
|
1052
1047
|
:expr => "expression (e.g. 1px, bold)",
|
1048
|
+
:_selector => "selector",
|
1053
1049
|
:selector_comma_sequence => "selector",
|
1054
|
-
:
|
1050
|
+
:simple_selector_sequence => "selector",
|
1055
1051
|
:import_arg => "file to import (string or url())",
|
1056
1052
|
:moz_document_function => "matching function (e.g. url-prefix(), domain())",
|
1057
1053
|
:supports_condition => "@supports condition (e.g. (display: flexbox))",
|
1058
1054
|
:supports_condition_in_parens => "@supports condition (e.g. (display: flexbox))",
|
1059
|
-
:a_n_plus_b => "An+B expression",
|
1060
|
-
:keyframes_selector_component => "from, to, or a percentage",
|
1061
|
-
:keyframes_selector => "keyframes selector (e.g. 10%)"
|
1062
1055
|
}
|
1063
1056
|
|
1064
|
-
TOK_NAMES = Sass::Util.to_hash(
|
1065
|
-
[Sass::SCSS::RX.const_get(c), c.downcase]
|
1066
|
-
|
1067
|
-
IDENT => "identifier",
|
1068
|
-
/[;{}]/ => '";"',
|
1069
|
-
/\b(without|with)\b/ => '"with" or "without"'
|
1070
|
-
)
|
1057
|
+
TOK_NAMES = Sass::Util.to_hash(
|
1058
|
+
Sass::SCSS::RX.constants.map {|c| [Sass::SCSS::RX.const_get(c), c.downcase]}).
|
1059
|
+
merge(IDENT => "identifier", /[;}]/ => '";"')
|
1071
1060
|
|
1072
1061
|
def tok?(rx)
|
1073
1062
|
@scanner.match?(rx)
|
1074
1063
|
end
|
1075
1064
|
|
1076
1065
|
def expr!(name)
|
1077
|
-
e = send(name)
|
1078
|
-
return e if e
|
1066
|
+
(e = send(name)) && (return e)
|
1079
1067
|
expected(EXPR_NAMES[name] || name.to_s)
|
1080
1068
|
end
|
1081
1069
|
|
1082
1070
|
def tok!(rx)
|
1083
|
-
t = tok(rx)
|
1084
|
-
return t if t
|
1071
|
+
(t = tok(rx)) && (return t)
|
1085
1072
|
name = TOK_NAMES[rx]
|
1086
1073
|
|
1087
1074
|
unless name
|
1088
1075
|
# Display basic regexps as plain old strings
|
1089
|
-
source = rx.source.gsub(/\\\//, '/')
|
1090
1076
|
string = rx.source.gsub(/\\(.)/, '\1')
|
1091
|
-
name = source == Regexp.escape(string) ? string.inspect : rx.inspect
|
1077
|
+
name = rx.source == Regexp.escape(string) ? string.inspect : rx.inspect
|
1092
1078
|
end
|
1093
1079
|
|
1094
1080
|
expected(name)
|
@@ -1115,12 +1101,10 @@ module Sass
|
|
1115
1101
|
old_throw_error, @throw_error = @throw_error, true
|
1116
1102
|
pos = @scanner.pos
|
1117
1103
|
line = @line
|
1118
|
-
offset = @offset
|
1119
1104
|
expected = @expected
|
1120
1105
|
if catch(:_sass_parser_error) {yield; false}
|
1121
1106
|
@scanner.pos = pos
|
1122
1107
|
@line = line
|
1123
|
-
@offset = offset
|
1124
1108
|
@expected = expected
|
1125
1109
|
{:pos => pos, :line => line, :expected => @expected, :block => block}
|
1126
1110
|
end
|
@@ -1183,15 +1167,7 @@ module Sass
|
|
1183
1167
|
@scanner.pos -= @scanner[-1].length
|
1184
1168
|
res.slice!(-@scanner[-1].length..-1)
|
1185
1169
|
end
|
1186
|
-
|
1187
|
-
newline_count = res.count(NEWLINE)
|
1188
|
-
if newline_count > 0
|
1189
|
-
@line += newline_count
|
1190
|
-
@offset = res[res.rindex(NEWLINE)..-1].size
|
1191
|
-
else
|
1192
|
-
@offset += res.size
|
1193
|
-
end
|
1194
|
-
|
1170
|
+
@line += res.count(NEWLINE)
|
1195
1171
|
@expected = nil
|
1196
1172
|
if !@strs.empty? && rx != COMMENT && rx != SINGLE_LINE_COMMENT
|
1197
1173
|
@strs.each {|s| s << res}
|
@@ -1199,11 +1175,6 @@ module Sass
|
|
1199
1175
|
res
|
1200
1176
|
end
|
1201
1177
|
end
|
1202
|
-
|
1203
|
-
# Remove a vendor prefix from `str`.
|
1204
|
-
def deprefix(str)
|
1205
|
-
str.gsub(/^-[a-zA-Z0-9]+-/, '')
|
1206
|
-
end
|
1207
1178
|
end
|
1208
1179
|
end
|
1209
1180
|
end
|