haml 3.0.25 → 3.1.0.alpha.2

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of haml might be problematic. Click here for more details.

Files changed (212) hide show
  1. data/.yardopts +1 -1
  2. data/CONTRIBUTING +0 -1
  3. data/EDGE_GEM_VERSION +1 -0
  4. data/MIT-LICENSE +1 -1
  5. data/README.md +10 -175
  6. data/Rakefile +56 -84
  7. data/VERSION +1 -1
  8. data/VERSION_NAME +1 -1
  9. data/init.rb +1 -1
  10. data/lib/haml.rb +14 -12
  11. data/lib/haml/engine.rb +1 -1
  12. data/lib/haml/exec.rb +19 -316
  13. data/lib/haml/helpers/action_view_extensions.rb +1 -1
  14. data/lib/haml/html.rb +69 -76
  15. data/lib/haml/precompiler.rb +34 -41
  16. data/lib/haml/railtie.rb +4 -6
  17. data/lib/haml/template/plugin.rb +6 -16
  18. data/lib/haml/util.rb +91 -107
  19. data/lib/haml/version.rb +7 -0
  20. data/test/benchmark.rb +2 -9
  21. data/test/haml/engine_test.rb +195 -92
  22. data/test/haml/html2haml/erb_tests.rb +0 -14
  23. data/test/haml/util_test.rb +32 -0
  24. data/test/test_helper.rb +0 -39
  25. metadata +96 -324
  26. data/bin/css2sass +0 -13
  27. data/bin/sass +0 -8
  28. data/bin/sass-convert +0 -7
  29. data/extra/haml-mode.el +0 -753
  30. data/extra/sass-mode.el +0 -207
  31. data/lib/haml/util/subset_map.rb +0 -101
  32. data/lib/sass.rb +0 -29
  33. data/lib/sass/callbacks.rb +0 -52
  34. data/lib/sass/css.rb +0 -294
  35. data/lib/sass/engine.rb +0 -720
  36. data/lib/sass/environment.rb +0 -143
  37. data/lib/sass/error.rb +0 -198
  38. data/lib/sass/files.rb +0 -160
  39. data/lib/sass/less.rb +0 -382
  40. data/lib/sass/plugin.rb +0 -279
  41. data/lib/sass/plugin/configuration.rb +0 -221
  42. data/lib/sass/plugin/generic.rb +0 -15
  43. data/lib/sass/plugin/merb.rb +0 -37
  44. data/lib/sass/plugin/rack.rb +0 -47
  45. data/lib/sass/plugin/rails.rb +0 -32
  46. data/lib/sass/plugin/staleness_checker.rb +0 -123
  47. data/lib/sass/repl.rb +0 -58
  48. data/lib/sass/script.rb +0 -63
  49. data/lib/sass/script/bool.rb +0 -18
  50. data/lib/sass/script/color.rb +0 -491
  51. data/lib/sass/script/css_lexer.rb +0 -29
  52. data/lib/sass/script/css_parser.rb +0 -31
  53. data/lib/sass/script/funcall.rb +0 -77
  54. data/lib/sass/script/functions.rb +0 -861
  55. data/lib/sass/script/interpolation.rb +0 -70
  56. data/lib/sass/script/lexer.rb +0 -337
  57. data/lib/sass/script/literal.rb +0 -236
  58. data/lib/sass/script/node.rb +0 -112
  59. data/lib/sass/script/number.rb +0 -423
  60. data/lib/sass/script/operation.rb +0 -95
  61. data/lib/sass/script/parser.rb +0 -401
  62. data/lib/sass/script/string.rb +0 -67
  63. data/lib/sass/script/string_interpolation.rb +0 -93
  64. data/lib/sass/script/unary_operation.rb +0 -57
  65. data/lib/sass/script/variable.rb +0 -48
  66. data/lib/sass/scss.rb +0 -17
  67. data/lib/sass/scss/css_parser.rb +0 -46
  68. data/lib/sass/scss/parser.rb +0 -855
  69. data/lib/sass/scss/rx.rb +0 -126
  70. data/lib/sass/scss/sass_parser.rb +0 -11
  71. data/lib/sass/scss/script_lexer.rb +0 -15
  72. data/lib/sass/scss/script_parser.rb +0 -25
  73. data/lib/sass/scss/static_parser.rb +0 -40
  74. data/lib/sass/selector.rb +0 -361
  75. data/lib/sass/selector/abstract_sequence.rb +0 -62
  76. data/lib/sass/selector/comma_sequence.rb +0 -82
  77. data/lib/sass/selector/sequence.rb +0 -237
  78. data/lib/sass/selector/simple.rb +0 -113
  79. data/lib/sass/selector/simple_sequence.rb +0 -136
  80. data/lib/sass/tree/charset_node.rb +0 -37
  81. data/lib/sass/tree/comment_node.rb +0 -128
  82. data/lib/sass/tree/debug_node.rb +0 -36
  83. data/lib/sass/tree/directive_node.rb +0 -75
  84. data/lib/sass/tree/extend_node.rb +0 -65
  85. data/lib/sass/tree/for_node.rb +0 -55
  86. data/lib/sass/tree/if_node.rb +0 -69
  87. data/lib/sass/tree/import_node.rb +0 -102
  88. data/lib/sass/tree/mixin_def_node.rb +0 -48
  89. data/lib/sass/tree/mixin_node.rb +0 -111
  90. data/lib/sass/tree/node.rb +0 -464
  91. data/lib/sass/tree/prop_node.rb +0 -220
  92. data/lib/sass/tree/root_node.rb +0 -163
  93. data/lib/sass/tree/rule_node.rb +0 -261
  94. data/lib/sass/tree/variable_node.rb +0 -39
  95. data/lib/sass/tree/warn_node.rb +0 -42
  96. data/lib/sass/tree/while_node.rb +0 -36
  97. data/test/haml/util/subset_map_test.rb +0 -91
  98. data/test/sass/callbacks_test.rb +0 -61
  99. data/test/sass/conversion_test.rb +0 -1218
  100. data/test/sass/css2sass_test.rb +0 -364
  101. data/test/sass/data/hsl-rgb.txt +0 -319
  102. data/test/sass/engine_test.rb +0 -2267
  103. data/test/sass/extend_test.rb +0 -1348
  104. data/test/sass/functions_test.rb +0 -556
  105. data/test/sass/less_conversion_test.rb +0 -653
  106. data/test/sass/more_results/more1.css +0 -9
  107. data/test/sass/more_results/more1_with_line_comments.css +0 -26
  108. data/test/sass/more_results/more_import.css +0 -29
  109. data/test/sass/more_templates/_more_partial.sass +0 -2
  110. data/test/sass/more_templates/more1.sass +0 -23
  111. data/test/sass/more_templates/more_import.sass +0 -11
  112. data/test/sass/plugin_test.rb +0 -433
  113. data/test/sass/results/alt.css +0 -4
  114. data/test/sass/results/basic.css +0 -9
  115. data/test/sass/results/compact.css +0 -5
  116. data/test/sass/results/complex.css +0 -86
  117. data/test/sass/results/compressed.css +0 -1
  118. data/test/sass/results/expanded.css +0 -19
  119. data/test/sass/results/import.css +0 -31
  120. data/test/sass/results/import_charset.css +0 -4
  121. data/test/sass/results/import_charset_1_8.css +0 -4
  122. data/test/sass/results/import_charset_ibm866.css +0 -4
  123. data/test/sass/results/line_numbers.css +0 -49
  124. data/test/sass/results/mixins.css +0 -95
  125. data/test/sass/results/multiline.css +0 -24
  126. data/test/sass/results/nested.css +0 -22
  127. data/test/sass/results/options.css +0 -1
  128. data/test/sass/results/parent_ref.css +0 -13
  129. data/test/sass/results/script.css +0 -16
  130. data/test/sass/results/scss_import.css +0 -31
  131. data/test/sass/results/scss_importee.css +0 -2
  132. data/test/sass/results/subdir/nested_subdir/nested_subdir.css +0 -1
  133. data/test/sass/results/subdir/subdir.css +0 -3
  134. data/test/sass/results/units.css +0 -11
  135. data/test/sass/results/warn.css +0 -0
  136. data/test/sass/results/warn_imported.css +0 -0
  137. data/test/sass/script_conversion_test.rb +0 -314
  138. data/test/sass/script_test.rb +0 -470
  139. data/test/sass/scss/css_test.rb +0 -916
  140. data/test/sass/scss/rx_test.rb +0 -156
  141. data/test/sass/scss/scss_test.rb +0 -1122
  142. data/test/sass/scss/test_helper.rb +0 -37
  143. data/test/sass/templates/_imported_charset_ibm866.sass +0 -4
  144. data/test/sass/templates/_imported_charset_utf8.sass +0 -4
  145. data/test/sass/templates/_partial.sass +0 -2
  146. data/test/sass/templates/alt.sass +0 -16
  147. data/test/sass/templates/basic.sass +0 -23
  148. data/test/sass/templates/bork1.sass +0 -2
  149. data/test/sass/templates/bork2.sass +0 -2
  150. data/test/sass/templates/bork3.sass +0 -2
  151. data/test/sass/templates/bork4.sass +0 -2
  152. data/test/sass/templates/compact.sass +0 -17
  153. data/test/sass/templates/complex.sass +0 -305
  154. data/test/sass/templates/compressed.sass +0 -15
  155. data/test/sass/templates/expanded.sass +0 -17
  156. data/test/sass/templates/import.sass +0 -12
  157. data/test/sass/templates/import_charset.sass +0 -7
  158. data/test/sass/templates/import_charset_1_8.sass +0 -4
  159. data/test/sass/templates/import_charset_ibm866.sass +0 -9
  160. data/test/sass/templates/importee.less +0 -2
  161. data/test/sass/templates/importee.sass +0 -19
  162. data/test/sass/templates/line_numbers.sass +0 -13
  163. data/test/sass/templates/mixin_bork.sass +0 -5
  164. data/test/sass/templates/mixins.sass +0 -76
  165. data/test/sass/templates/multiline.sass +0 -20
  166. data/test/sass/templates/nested.sass +0 -25
  167. data/test/sass/templates/nested_bork1.sass +0 -2
  168. data/test/sass/templates/nested_bork2.sass +0 -2
  169. data/test/sass/templates/nested_bork3.sass +0 -2
  170. data/test/sass/templates/nested_bork4.sass +0 -2
  171. data/test/sass/templates/nested_mixin_bork.sass +0 -6
  172. data/test/sass/templates/options.sass +0 -2
  173. data/test/sass/templates/parent_ref.sass +0 -25
  174. data/test/sass/templates/script.sass +0 -101
  175. data/test/sass/templates/scss_import.scss +0 -11
  176. data/test/sass/templates/scss_importee.scss +0 -1
  177. data/test/sass/templates/subdir/nested_subdir/_nested_partial.sass +0 -2
  178. data/test/sass/templates/subdir/nested_subdir/nested_subdir.sass +0 -3
  179. data/test/sass/templates/subdir/subdir.sass +0 -6
  180. data/test/sass/templates/units.sass +0 -11
  181. data/test/sass/templates/warn.sass +0 -3
  182. data/test/sass/templates/warn_imported.sass +0 -4
  183. data/vendor/fssm/LICENSE +0 -20
  184. data/vendor/fssm/README.markdown +0 -55
  185. data/vendor/fssm/Rakefile +0 -59
  186. data/vendor/fssm/VERSION.yml +0 -5
  187. data/vendor/fssm/example.rb +0 -9
  188. data/vendor/fssm/fssm.gemspec +0 -77
  189. data/vendor/fssm/lib/fssm.rb +0 -33
  190. data/vendor/fssm/lib/fssm/backends/fsevents.rb +0 -36
  191. data/vendor/fssm/lib/fssm/backends/inotify.rb +0 -26
  192. data/vendor/fssm/lib/fssm/backends/polling.rb +0 -25
  193. data/vendor/fssm/lib/fssm/backends/rubycocoa/fsevents.rb +0 -131
  194. data/vendor/fssm/lib/fssm/monitor.rb +0 -26
  195. data/vendor/fssm/lib/fssm/path.rb +0 -91
  196. data/vendor/fssm/lib/fssm/pathname.rb +0 -502
  197. data/vendor/fssm/lib/fssm/state/directory.rb +0 -57
  198. data/vendor/fssm/lib/fssm/state/file.rb +0 -24
  199. data/vendor/fssm/lib/fssm/support.rb +0 -63
  200. data/vendor/fssm/lib/fssm/tree.rb +0 -176
  201. data/vendor/fssm/profile/prof-cache.rb +0 -40
  202. data/vendor/fssm/profile/prof-fssm-pathname.html +0 -1231
  203. data/vendor/fssm/profile/prof-pathname.rb +0 -68
  204. data/vendor/fssm/profile/prof-plain-pathname.html +0 -988
  205. data/vendor/fssm/profile/prof.html +0 -2379
  206. data/vendor/fssm/spec/path_spec.rb +0 -75
  207. data/vendor/fssm/spec/root/duck/quack.txt +0 -0
  208. data/vendor/fssm/spec/root/file.css +0 -0
  209. data/vendor/fssm/spec/root/file.rb +0 -0
  210. data/vendor/fssm/spec/root/file.yml +0 -0
  211. data/vendor/fssm/spec/root/moo/cow.txt +0 -0
  212. data/vendor/fssm/spec/spec_helper.rb +0 -14
@@ -1,67 +0,0 @@
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
- # 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=
22
- def context=(context)
23
- super
24
- @type = :identifier if context == :equals
25
- end
26
-
27
- # Creates a new string.
28
- #
29
- # @param value [String] See \{#value}
30
- # @param type [Symbol] See \{#type}
31
- def initialize(value, type = :identifier)
32
- super(value)
33
- @type = type
34
- end
35
-
36
- # @see Literal#plus
37
- def plus(other)
38
- other_str = other.is_a?(Sass::Script::String) ? other.value : other.to_s
39
- Sass::Script::String.new(self.value + other_str, self.type)
40
- end
41
-
42
- # @see Node#to_s
43
- def to_s(opts = {})
44
- if self.type == :identifier
45
- return %q{""} if context == :equals && self.value.size == 0
46
- return self.value.gsub("\n", " ")
47
- end
48
-
49
- return "\"#{value.gsub('"', "\\\"")}\"" if opts[:quote] == %q{"}
50
- return "'#{value.gsub("'", "\\'")}'" if opts[:quote] == %q{'}
51
- return "\"#{value}\"" unless value.include?('"')
52
- return "'#{value}'" unless value.include?("'")
53
- "\"#{value.gsub('"', "\\\"")}\"" #'
54
- end
55
-
56
- # @see Node#to_sass
57
- def to_sass(opts = {})
58
- if self.type == :identifier && context == :equals &&
59
- self.value !~ Sass::SCSS::RX::URI &&
60
- Sass::SCSS::RX.escape_ident(self.value).include?(?\\)
61
- return "unquote(#{Sass::Script::String.new(self.value, :string).to_sass})"
62
- else
63
- return to_s
64
- end
65
- end
66
- end
67
- end
@@ -1,93 +0,0 @@
1
- module Sass::Script
2
- # A SassScript object representing `#{}` interpolation within a string.
3
- #
4
- # @see Interpolation
5
- class StringInterpolation < Node
6
- # Interpolation in a string is of the form `"before #{mid} after"`,
7
- # where `before` and `after` may include more interpolation.
8
- #
9
- # @param before [Node] The string before the interpolation
10
- # @param mid [Node] The SassScript within the interpolation
11
- # @param after [Node] The string after the interpolation
12
- def initialize(before, mid, after)
13
- @before = before
14
- @mid = mid
15
- @after = after
16
- end
17
-
18
- # @return [String] A human-readable s-expression representation of the interpolation
19
- def inspect
20
- "(string_interpolation #{@before.inspect} #{@mid.inspect} #{@after.inspect})"
21
- end
22
-
23
- # @see Node#to_sass
24
- def to_sass(opts = {})
25
- # We can get rid of all of this when we remove the deprecated :equals context
26
- before_unquote, before_quote_char, before_str = parse_str(@before.to_sass(opts))
27
- after_unquote, after_quote_char, after_str = parse_str(@after.to_sass(opts))
28
- unquote = before_unquote || after_unquote ||
29
- (before_quote_char && !after_quote_char && !after_str.empty?) ||
30
- (!before_quote_char && after_quote_char && !before_str.empty?)
31
- quote_char =
32
- if before_quote_char && after_quote_char && before_quote_char != after_quote_char
33
- before_str.gsub!("\\'", "'")
34
- before_str.gsub!('"', "\\\"")
35
- after_str.gsub!("\\'", "'")
36
- after_str.gsub!('"', "\\\"")
37
- '"'
38
- else
39
- before_quote_char || after_quote_char
40
- end
41
-
42
- res = ""
43
- res << 'unquote(' if unquote
44
- res << quote_char if quote_char
45
- res << before_str
46
- res << '#{' << @mid.to_sass(opts) << '}'
47
- res << after_str
48
- res << quote_char if quote_char
49
- res << ')' if unquote
50
- res
51
- end
52
-
53
- # Returns the three components of the interpolation, `before`, `mid`, and `after`.
54
- #
55
- # @return [Array<Node>]
56
- # @see #initialize
57
- # @see Node#children
58
- def children
59
- [@before, @mid, @after].compact
60
- end
61
-
62
- protected
63
-
64
- # Evaluates the interpolation.
65
- #
66
- # @param environment [Sass::Environment] The environment in which to evaluate the SassScript
67
- # @return [Sass::Script::String] The SassScript string that is the value of the interpolation
68
- def _perform(environment)
69
- res = ""
70
- before = @before.perform(environment)
71
- res << before.value
72
- mid = @mid.perform(environment)
73
- res << (mid.is_a?(Sass::Script::String) ? mid.value : mid.to_s)
74
- res << @after.perform(environment).value
75
- opts(Sass::Script::String.new(res, before.type))
76
- end
77
-
78
- private
79
-
80
- def parse_str(str)
81
- case str
82
- when /^unquote\((["'])(.*)\1\)$/
83
- return true, $1, $2
84
- when '""'
85
- return false, nil, ""
86
- when /^(["'])(.*)\1$/
87
- return false, $1, $2
88
- else
89
- return false, nil, str
90
- end
91
- end
92
- end
93
- end
@@ -1,57 +0,0 @@
1
- module Sass::Script
2
- # A SassScript parse node representing a unary operation,
3
- # such as `-$b` or `not true`.
4
- #
5
- # Currently only `-`, `/`, and `not` are unary operators.
6
- class UnaryOperation < Node
7
- # @param operand [Script::Node] The parse-tree node
8
- # for the object of the operator
9
- # @param operator [Symbol] The operator to perform
10
- def initialize(operand, operator)
11
- @operand = operand
12
- @operator = operator
13
- super()
14
- end
15
-
16
- # @return [String] A human-readable s-expression representation of the operation
17
- def inspect
18
- "(#{@operator.inspect} #{@operand.inspect})"
19
- end
20
-
21
- # @see Node#to_sass
22
- def to_sass(opts = {})
23
- operand = @operand.to_sass(opts)
24
- if @operand.is_a?(Operation) ||
25
- (@operator == :minus &&
26
- (operand =~ Sass::SCSS::RX::IDENT) == 0)
27
- operand = "(#{@operand.to_sass(opts)})"
28
- end
29
- op = Lexer::OPERATORS_REVERSE[@operator]
30
- op + (op =~ /[a-z]/ ? " " : "") + operand
31
- end
32
-
33
- # Returns the operand of the operation.
34
- #
35
- # @return [Array<Node>]
36
- # @see Node#children
37
- def children
38
- [@operand]
39
- end
40
-
41
- protected
42
-
43
- # Evaluates the operation.
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 operation
47
- # @raise [Sass::SyntaxError] if the operation is undefined for the operand
48
- def _perform(environment)
49
- operator = "unary_#{@operator}"
50
- literal = @operand.perform(environment)
51
- literal.send(operator)
52
- rescue NoMethodError => e
53
- raise e unless e.name.to_s == operator.to_s
54
- raise Sass::SyntaxError.new("Undefined unary operation: \"#{@operator} #{literal}\".")
55
- end
56
- end
57
- end
@@ -1,48 +0,0 @@
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
- # @param name [String] See \{#name}
11
- def initialize(name)
12
- @name = name
13
- super()
14
- end
15
-
16
- # @return [String] A string representation of the variable
17
- def inspect(opts = {})
18
- return "!important" if name == "important"
19
- "$#{dasherize(name, opts)}"
20
- end
21
- alias_method :to_sass, :inspect
22
-
23
- # Returns an empty array.
24
- #
25
- # @return [Array<Node>] empty
26
- # @see Node#children
27
- def children
28
- []
29
- end
30
-
31
- protected
32
-
33
- # Evaluates the variable.
34
- #
35
- # @param environment [Sass::Environment] The environment in which to evaluate the SassScript
36
- # @return [Literal] The SassScript object that is the value of the variable
37
- # @raise [Sass::SyntaxError] if the variable is undefined
38
- def _perform(environment)
39
- raise SyntaxError.new("Undefined variable: \"$#{name}\".") unless val = environment.var(name)
40
- if val.is_a?(Number)
41
- val = val.dup
42
- val.original = nil
43
- end
44
- return val
45
- end
46
- end
47
- end
48
- end
@@ -1,17 +0,0 @@
1
- require 'sass/scss/rx'
2
- require 'sass/scss/script_lexer'
3
- require 'sass/scss/script_parser'
4
- require 'sass/scss/parser'
5
- require 'sass/scss/sass_parser'
6
- require 'sass/scss/static_parser'
7
- require 'sass/scss/css_parser'
8
-
9
- module Sass
10
- # SCSS is the CSS syntax for Sass.
11
- # It parses into the same syntax tree as Sass,
12
- # and generates the same sort of output CSS.
13
- #
14
- # This module contains code for the parsing of SCSS.
15
- # The evaluation is handled by the broader {Sass} module.
16
- module SCSS; end
17
- end
@@ -1,46 +0,0 @@
1
- require 'sass/script/css_parser'
2
-
3
- module Sass
4
- module SCSS
5
- # This is a subclass of {Parser} which only parses plain CSS.
6
- # It doesn't support any Sass extensions, such as interpolation,
7
- # parent references, nested selectors, and so forth.
8
- # It does support all the same CSS hacks as the SCSS parser, though.
9
- class CssParser < StaticParser
10
- # Parse a selector, and return its value as a string.
11
- #
12
- # @return [String, nil] The parsed selector, or nil if no selector was parsed
13
- # @raise [Sass::SyntaxError] if there's a syntax error in the selector
14
- def parse_selector_string
15
- init_scanner!
16
- str {return unless selector}
17
- end
18
-
19
- private
20
-
21
- def parent_selector; nil; end
22
- def interpolation; nil; end
23
- def interp_string; tok(STRING); end
24
- def interp_ident(ident = IDENT); tok(ident); end
25
- def use_css_import?; true; end
26
-
27
- def block_child(context)
28
- case context
29
- when :ruleset
30
- declaration
31
- when :stylesheet
32
- directive || ruleset
33
- when :directive
34
- directive || declaration_or_ruleset
35
- end
36
- end
37
-
38
- def nested_properties!(node, space)
39
- expected('expression (e.g. 1px, bold)');
40
- end
41
-
42
- @sass_script_parser = Class.new(Sass::Script::CssParser)
43
- @sass_script_parser.send(:include, ScriptParser)
44
- end
45
- end
46
- end
@@ -1,855 +0,0 @@
1
- require 'strscan'
2
- require 'set'
3
-
4
- module Sass
5
- module SCSS
6
- # The parser for SCSS.
7
- # It parses a string of code into a tree of {Sass::Tree::Node}s.
8
- class Parser
9
- # @param str [String, StringScanner] The source document to parse.
10
- # Note that `Parser` *won't* raise a nice error message if this isn't properly parsed;
11
- # for that, you should use the higher-level {Sass::Engine} or {Sass::CSS}.
12
- # @param line [Fixnum] The line on which the source string appeared,
13
- # if it's part of another document
14
- def initialize(str, line = 1)
15
- @template = str
16
- @line = line
17
- @strs = []
18
- end
19
-
20
- # Parses an SCSS document.
21
- #
22
- # @return [Sass::Tree::RootNode] The root node of the document tree
23
- # @raise [Sass::SyntaxError] if there's a syntax error in the document
24
- def parse
25
- init_scanner!
26
- root = stylesheet
27
- expected("selector or at-rule") unless @scanner.eos?
28
- root
29
- end
30
-
31
- # Parses an identifier with interpolation.
32
- # Note that this won't assert that the identifier takes up the entire input string;
33
- # it's meant to be used with `StringScanner`s as part of other parsers.
34
- #
35
- # @return [Array<String, Sass::Script::Node>, nil]
36
- # The interpolated identifier, or nil if none could be parsed
37
- def parse_interp_ident
38
- init_scanner!
39
- interp_ident
40
- end
41
-
42
- private
43
-
44
- include Sass::SCSS::RX
45
-
46
- def init_scanner!
47
- @scanner =
48
- if @template.is_a?(StringScanner)
49
- @template
50
- else
51
- StringScanner.new(@template.gsub("\r", ""))
52
- end
53
- end
54
-
55
- def stylesheet
56
- node = node(Sass::Tree::RootNode.new(@scanner.string))
57
- block_contents(node, :stylesheet) {s(node)}
58
- end
59
-
60
- def s(node)
61
- while tok(S) || tok(CDC) || tok(CDO) || (c = tok(SINGLE_LINE_COMMENT)) || (c = tok(COMMENT))
62
- next unless c
63
- process_comment c, node
64
- c = nil
65
- end
66
- true
67
- end
68
-
69
- def ss
70
- nil while tok(S) || tok(SINGLE_LINE_COMMENT) || tok(COMMENT)
71
- true
72
- end
73
-
74
- def ss_comments(node)
75
- while tok(S) || (c = tok(SINGLE_LINE_COMMENT)) || (c = tok(COMMENT))
76
- next unless c
77
- process_comment c, node
78
- c = nil
79
- end
80
-
81
- true
82
- end
83
-
84
- def whitespace
85
- return unless tok(S) || tok(SINGLE_LINE_COMMENT) || tok(COMMENT)
86
- ss
87
- end
88
-
89
- def process_comment(text, node)
90
- single_line = text =~ /^\/\//
91
- pre_str = single_line ? "" : @scanner.
92
- string[0...@scanner.pos].
93
- reverse[/.*?\*\/(.*?)($|\Z)/, 1].
94
- reverse.gsub(/[^\s]/, ' ')
95
- text = text.sub(/^\s*\/\//, '/*').gsub(/^\s*\/\//, ' *') + ' */' if single_line
96
- comment = Sass::Tree::CommentNode.new(pre_str + text, single_line)
97
- comment.line = @line - text.count("\n")
98
- node << comment
99
- end
100
-
101
- DIRECTIVES = Set[:mixin, :include, :debug, :warn, :for, :while, :if, :else,
102
- :extend, :import, :media, :charset]
103
-
104
- def directive
105
- return unless tok(/@/)
106
- name = tok!(IDENT)
107
- ss
108
-
109
- if dir = special_directive(name)
110
- return dir
111
- end
112
-
113
- # Most at-rules take expressions (e.g. @import),
114
- # but some (e.g. @page) take selector-like arguments
115
- val = str {break unless expr}
116
- val ||= CssParser.new(@scanner, @line).parse_selector_string
117
- node = node(Sass::Tree::DirectiveNode.new("@#{name} #{val}".strip))
118
-
119
- if tok(/\{/)
120
- node.has_children = true
121
- block_contents(node, :directive)
122
- tok!(/\}/)
123
- end
124
-
125
- node
126
- end
127
-
128
- def special_directive(name)
129
- sym = name.gsub('-', '_').to_sym
130
- DIRECTIVES.include?(sym) && send("#{sym}_directive")
131
- end
132
-
133
- def mixin_directive
134
- name = tok! IDENT
135
- args = sass_script(:parse_mixin_definition_arglist)
136
- ss
137
- block(node(Sass::Tree::MixinDefNode.new(name, args)), :directive)
138
- end
139
-
140
- def include_directive
141
- name = tok! IDENT
142
- args = sass_script(:parse_mixin_include_arglist)
143
- ss
144
- node(Sass::Tree::MixinNode.new(name, args))
145
- end
146
-
147
- def debug_directive
148
- node(Sass::Tree::DebugNode.new(sass_script(:parse)))
149
- end
150
-
151
- def warn_directive
152
- node(Sass::Tree::WarnNode.new(sass_script(:parse)))
153
- end
154
-
155
- def for_directive
156
- tok!(/\$/)
157
- var = tok! IDENT
158
- ss
159
-
160
- tok!(/from/)
161
- from = sass_script(:parse_until, Set["to", "through"])
162
- ss
163
-
164
- @expected = '"to" or "through"'
165
- exclusive = (tok(/to/) || tok!(/through/)) == 'to'
166
- to = sass_script(:parse)
167
- ss
168
-
169
- block(node(Sass::Tree::ForNode.new(var, from, to, exclusive)), :directive)
170
- end
171
-
172
- def while_directive
173
- expr = sass_script(:parse)
174
- ss
175
- block(node(Sass::Tree::WhileNode.new(expr)), :directive)
176
- end
177
-
178
- def if_directive
179
- expr = sass_script(:parse)
180
- ss
181
- node = block(node(Sass::Tree::IfNode.new(expr)), :directive)
182
- pos = @scanner.pos
183
- line = @line
184
- ss
185
-
186
- else_block(node) ||
187
- begin
188
- # Backtrack in case there are any comments we want to parse
189
- @scanner.pos = pos
190
- @line = line
191
- node
192
- end
193
- end
194
-
195
- def else_block(node)
196
- return unless tok(/@else/)
197
- ss
198
- else_node = block(
199
- Sass::Tree::IfNode.new((sass_script(:parse) if tok(/if/))),
200
- :directive)
201
- node.add_else(else_node)
202
- pos = @scanner.pos
203
- line = @line
204
- ss
205
-
206
- else_block(node) ||
207
- begin
208
- # Backtrack in case there are any comments we want to parse
209
- @scanner.pos = pos
210
- @line = line
211
- node
212
- end
213
- end
214
-
215
- def else_directive
216
- raise Sass::SyntaxError.new(
217
- "Invalid CSS: @else must come after @if", :line => @line)
218
- end
219
-
220
- def extend_directive
221
- node(Sass::Tree::ExtendNode.new(expr!(:selector)))
222
- end
223
-
224
- def import_directive
225
- values = []
226
-
227
- loop do
228
- values << expr!(:import_arg)
229
- break if use_css_import? || !tok(/,\s*/)
230
- end
231
-
232
- return values
233
- end
234
-
235
- def import_arg
236
- return unless arg = tok(STRING) || (uri = tok!(URI))
237
- path = @scanner[1] || @scanner[2] || @scanner[3]
238
- ss
239
-
240
- media = str {media_query_list}.strip
241
-
242
- if uri || path =~ /^http:\/\// || !media.strip.empty? || use_css_import?
243
- return node(Sass::Tree::DirectiveNode.new("@import #{arg} #{media}".strip))
244
- end
245
-
246
- node(Sass::Tree::ImportNode.new(path.strip))
247
- end
248
-
249
- def use_css_import?; false; end
250
-
251
- def media_directive
252
- val = str {media_query_list}.strip
253
- block(node(Sass::Tree::DirectiveNode.new("@media #{val}")), :directive)
254
- end
255
-
256
- # http://www.w3.org/TR/css3-mediaqueries/#syntax
257
- def media_query_list
258
- return unless media_query
259
-
260
- ss
261
- while tok(/,/)
262
- ss; expr!(:media_query); ss
263
- end
264
-
265
- true
266
- end
267
-
268
- def media_query
269
- if tok(/only|not/i)
270
- ss
271
- @expected = "media type (e.g. print, screen)"
272
- tok!(IDENT)
273
- ss
274
- elsif !tok(IDENT) && !media_expr
275
- return
276
- end
277
-
278
- ss
279
- while tok(/and/i)
280
- ss; expr!(:media_expr); ss
281
- end
282
-
283
- true
284
- end
285
-
286
- def media_expr
287
- return unless tok(/\(/)
288
- ss
289
- @expected = "media feature (e.g. min-device-width, color)"
290
- tok!(IDENT)
291
- ss
292
-
293
- if tok(/:/)
294
- ss; expr!(:expr)
295
- end
296
- tok!(/\)/)
297
- ss
298
-
299
- true
300
- end
301
-
302
- def charset_directive
303
- tok! STRING
304
- name = @scanner[1] || @scanner[2]
305
- ss
306
- node(Sass::Tree::CharsetNode.new(name))
307
- end
308
-
309
- def variable
310
- return unless tok(/\$/)
311
- name = tok!(IDENT)
312
- ss; tok!(/:/); ss
313
-
314
- expr = sass_script(:parse)
315
- guarded = tok(DEFAULT)
316
- node(Sass::Tree::VariableNode.new(name, expr, guarded))
317
- end
318
-
319
- def operator
320
- # Many of these operators (all except / and ,)
321
- # are disallowed by the CSS spec,
322
- # but they're included here for compatibility
323
- # with some proprietary MS properties
324
- str {ss if tok(/[\/,:.=]/)}
325
- end
326
-
327
- def unary_operator
328
- tok(/[+-]/)
329
- end
330
-
331
- def ruleset
332
- return unless rules = selector_sequence
333
- block(node(Sass::Tree::RuleNode.new(rules.flatten.compact)), :ruleset)
334
- end
335
-
336
- def block(node, context)
337
- node.has_children = true
338
- tok!(/\{/)
339
- block_contents(node, context)
340
- tok!(/\}/)
341
- node
342
- end
343
-
344
- # A block may contain declarations and/or rulesets
345
- def block_contents(node, context)
346
- block_given? ? yield : ss_comments(node)
347
- node << (child = block_child(context))
348
- while tok(/;/) || has_children?(child)
349
- block_given? ? yield : ss_comments(node)
350
- node << (child = block_child(context))
351
- end
352
- node
353
- end
354
-
355
- def block_child(context)
356
- return variable || directive || ruleset if context == :stylesheet
357
- variable || directive || declaration_or_ruleset
358
- end
359
-
360
- def has_children?(child_or_array)
361
- return false unless child_or_array
362
- return child_or_array.last.has_children if child_or_array.is_a?(Array)
363
- return child_or_array.has_children
364
- end
365
-
366
- # This is a nasty hack, and the only place in the parser
367
- # that requires backtracking.
368
- # The reason is that we can't figure out if certain strings
369
- # are declarations or rulesets with fixed finite lookahead.
370
- # For example, "foo:bar baz baz baz..." could be either a property
371
- # or a selector.
372
- #
373
- # To handle this, we simply check if it works as a property
374
- # (which is the most common case)
375
- # and, if it doesn't, try it as a ruleset.
376
- #
377
- # We could eke some more efficiency out of this
378
- # by handling some easy cases (first token isn't an identifier,
379
- # no colon after the identifier, whitespace after the colon),
380
- # but I'm not sure the gains would be worth the added complexity.
381
- def declaration_or_ruleset
382
- pos = @scanner.pos
383
- line = @line
384
- old_use_property_exception, @use_property_exception =
385
- @use_property_exception, false
386
- begin
387
- decl = declaration
388
- unless decl && decl.has_children
389
- # We want an exception if it's not there,
390
- # but we don't want to consume if it is
391
- tok!(/[;}]/) unless tok?(/[;}]/)
392
- end
393
- return decl
394
- rescue Sass::SyntaxError => decl_err
395
- end
396
-
397
- @line = line
398
- @scanner.pos = pos
399
-
400
- begin
401
- return ruleset
402
- rescue Sass::SyntaxError => ruleset_err
403
- raise @use_property_exception ? decl_err : ruleset_err
404
- end
405
- ensure
406
- @use_property_exception = old_use_property_exception
407
- end
408
-
409
- def selector_sequence
410
- if sel = tok(STATIC_SELECTOR)
411
- return [sel]
412
- end
413
-
414
- rules = []
415
- return unless v = selector
416
- rules.concat v
417
-
418
- while tok(/,/)
419
- rules << ',' << str {ss}
420
- rules.concat expr!(:selector)
421
- end
422
- rules
423
- end
424
-
425
- def selector
426
- return unless sel = _selector
427
- sel.to_a
428
- end
429
-
430
- def selector_comma_sequence
431
- return unless sel = _selector
432
- selectors = [sel]
433
- while tok(/,/)
434
- ws = str{ss}
435
- selectors << expr!(:_selector)
436
- selectors[-1] = Selector::Sequence.new(["\n"] + selectors.last.members) if ws.include?("\n")
437
- end
438
- Selector::CommaSequence.new(selectors)
439
- end
440
-
441
- def _selector
442
- # The combinator here allows the "> E" hack
443
- return unless val = combinator || simple_selector_sequence
444
- nl = str{ss}.include?("\n")
445
- res = []
446
- res << val
447
- res << "\n" if nl
448
-
449
- while val = combinator || simple_selector_sequence
450
- res << val
451
- res << "\n" if str{ss}.include?("\n")
452
- end
453
- Selector::Sequence.new(res.compact)
454
- end
455
-
456
- def combinator
457
- tok(PLUS) || tok(GREATER) || tok(TILDE)
458
- end
459
-
460
- def simple_selector_sequence
461
- # This allows for stuff like http://www.w3.org/TR/css3-animations/#keyframes-
462
- return expr unless e = element_name || id_selector || class_selector ||
463
- attrib || negation || pseudo || parent_selector || interpolation_selector
464
- res = [e]
465
-
466
- # The tok(/\*/) allows the "E*" hack
467
- while v = element_name || id_selector || class_selector ||
468
- attrib || negation || pseudo || interpolation_selector ||
469
- (tok(/\*/) && Selector::Universal.new(nil))
470
- res << v
471
- end
472
-
473
- if tok?(/&/)
474
- begin
475
- expected('"{"')
476
- rescue Sass::SyntaxError => e
477
- e.message << "\n\n" << <<MESSAGE
478
- In Sass 3, the parent selector & can only be used where element names are valid,
479
- since it could potentially be replaced by an element name.
480
- MESSAGE
481
- raise e
482
- end
483
- end
484
-
485
- Selector::SimpleSequence.new(res)
486
- end
487
-
488
- def parent_selector
489
- return unless tok(/&/)
490
- Selector::Parent.new
491
- end
492
-
493
- def class_selector
494
- return unless tok(/\./)
495
- @expected = "class name"
496
- Selector::Class.new(merge(expr!(:interp_ident)))
497
- end
498
-
499
- def id_selector
500
- return unless tok(/#(?!\{)/)
501
- @expected = "id name"
502
- Selector::Id.new(merge(expr!(:interp_name)))
503
- end
504
-
505
- def element_name
506
- return unless name = interp_ident || tok(/\*/) || (tok?(/\|/) && "")
507
- if tok(/\|/)
508
- @expected = "element name or *"
509
- ns = name
510
- name = interp_ident || tok!(/\*/)
511
- end
512
-
513
- if name == '*'
514
- Selector::Universal.new(merge(ns))
515
- else
516
- Selector::Element.new(merge(name), merge(ns))
517
- end
518
- end
519
-
520
- def interpolation_selector
521
- return unless script = interpolation
522
- Selector::Interpolation.new(script)
523
- end
524
-
525
- def attrib
526
- return unless tok(/\[/)
527
- ss
528
- ns, name = attrib_name!
529
- ss
530
-
531
- if op = tok(/=/) ||
532
- tok(INCLUDES) ||
533
- tok(DASHMATCH) ||
534
- tok(PREFIXMATCH) ||
535
- tok(SUFFIXMATCH) ||
536
- tok(SUBSTRINGMATCH)
537
- @expected = "identifier or string"
538
- ss
539
- if val = tok(IDENT)
540
- val = [val]
541
- else
542
- val = expr!(:interp_string)
543
- end
544
- ss
545
- end
546
- tok(/\]/)
547
-
548
- Selector::Attribute.new(merge(name), merge(ns), op, merge(val))
549
- end
550
-
551
- def attrib_name!
552
- if name_or_ns = interp_ident
553
- # E, E|E
554
- if tok(/\|(?!=)/)
555
- ns = name_or_ns
556
- name = interp_ident
557
- else
558
- name = name_or_ns
559
- end
560
- else
561
- # *|E or |E
562
- ns = [tok(/\*/) || ""]
563
- tok!(/\|/)
564
- name = expr!(:interp_ident)
565
- end
566
- return ns, name
567
- end
568
-
569
- def pseudo
570
- return unless s = tok(/::?/)
571
- @expected = "pseudoclass or pseudoelement"
572
- name = expr!(:interp_ident)
573
- if tok(/\(/)
574
- ss
575
- arg = expr!(:pseudo_expr)
576
- tok!(/\)/)
577
- end
578
- Selector::Pseudo.new(s == ':' ? :class : :element, merge(name), merge(arg))
579
- end
580
-
581
- def pseudo_expr
582
- return unless e = tok(PLUS) || tok(/-/) || tok(NUMBER) ||
583
- interp_string || tok(IDENT) || interpolation
584
- res = [e, str{ss}]
585
- while e = tok(PLUS) || tok(/-/) || tok(NUMBER) ||
586
- interp_string || tok(IDENT) || interpolation
587
- res << e << str{ss}
588
- end
589
- res
590
- end
591
-
592
- def negation
593
- return unless name = tok(NOT) || tok(MOZ_ANY)
594
- ss
595
- @expected = "selector"
596
- sel = selector_comma_sequence
597
- tok!(/\)/)
598
- Selector::SelectorPseudoClass.new(name[1...-1], sel)
599
- end
600
-
601
- def declaration
602
- # This allows the "*prop: val", ":prop: val", and ".prop: val" hacks
603
- if s = tok(/[:\*\.]|\#(?!\{)/)
604
- @use_property_exception = s !~ /[\.\#]/
605
- name = [s, str{ss}, *expr!(:interp_ident)]
606
- else
607
- return unless name = interp_ident
608
- name = [name] if name.is_a?(String)
609
- end
610
- if comment = tok(COMMENT)
611
- name << comment
612
- end
613
- ss
614
-
615
- tok!(/:/)
616
- space, value = value!
617
- ss
618
- require_block = tok?(/\{/)
619
-
620
- node = node(Sass::Tree::PropNode.new(name.flatten.compact, value, :new))
621
-
622
- return node unless require_block
623
- nested_properties! node, space
624
- end
625
-
626
- def value!
627
- space = !str {ss}.empty?
628
- @use_property_exception ||= space || !tok?(IDENT)
629
-
630
- return true, Sass::Script::String.new("") if tok?(/\{/)
631
- # This is a bit of a dirty trick:
632
- # if the value is completely static,
633
- # we don't parse it at all, and instead return a plain old string
634
- # containing the value.
635
- # This results in a dramatic speed increase.
636
- if val = tok(STATIC_VALUE)
637
- return space, Sass::Script::String.new(val.strip)
638
- end
639
- return space, sass_script(:parse)
640
- end
641
-
642
- def plain_value
643
- return unless tok(/:/)
644
- space = !str {ss}.empty?
645
- @use_property_exception ||= space || !tok?(IDENT)
646
-
647
- expression = expr
648
- expression << tok(IMPORTANT) if expression
649
- # expression, space, value
650
- return expression, space, expression || [""]
651
- end
652
-
653
- def nested_properties!(node, space)
654
- raise Sass::SyntaxError.new(<<MESSAGE, :line => @line) unless space
655
- Invalid CSS: a space is required between a property and its definition
656
- when it has other properties nested beneath it.
657
- MESSAGE
658
-
659
- @use_property_exception = true
660
- @expected = 'expression (e.g. 1px, bold) or "{"'
661
- block(node, :property)
662
- end
663
-
664
- def expr
665
- return unless t = term
666
- res = [t, str{ss}]
667
-
668
- while (o = operator) && (t = term)
669
- res << o << t << str{ss}
670
- end
671
-
672
- res
673
- end
674
-
675
- def term
676
- unless e = tok(NUMBER) ||
677
- tok(URI) ||
678
- function ||
679
- tok(STRING) ||
680
- tok(UNICODERANGE) ||
681
- tok(IDENT) ||
682
- tok(HEXCOLOR)
683
-
684
- return unless op = unary_operator
685
- @expected = "number or function"
686
- return [op, tok(NUMBER) || expr!(:function)]
687
- end
688
- e
689
- end
690
-
691
- def function
692
- return unless name = tok(FUNCTION)
693
- if name == "expression(" || name == "calc("
694
- str, _ = Haml::Shared.balance(@scanner, ?(, ?), 1)
695
- [name, str]
696
- else
697
- [name, str{ss}, expr, tok!(/\)/)]
698
- end
699
- end
700
-
701
- def interpolation
702
- return unless tok(INTERP_START)
703
- sass_script(:parse_interpolated)
704
- end
705
-
706
- def interp_string
707
- _interp_string(:double) || _interp_string(:single)
708
- end
709
-
710
- def _interp_string(type)
711
- return unless start = tok(Sass::Script::Lexer::STRING_REGULAR_EXPRESSIONS[[type, false]])
712
- res = [start]
713
-
714
- mid_re = Sass::Script::Lexer::STRING_REGULAR_EXPRESSIONS[[type, true]]
715
- # @scanner[2].empty? means we've started an interpolated section
716
- while @scanner[2] == '#{'
717
- @scanner.pos -= 2 # Don't consume the #{
718
- res.last.slice!(-2..-1)
719
- res << expr!(:interpolation) << tok(mid_re)
720
- end
721
- res
722
- end
723
-
724
- def interp_ident(start = IDENT)
725
- return unless val = tok(start) || interpolation
726
- res = [val]
727
- while val = tok(NAME) || interpolation
728
- res << val
729
- end
730
- res
731
- end
732
-
733
- def interp_name
734
- interp_ident NAME
735
- end
736
-
737
- def str
738
- @strs.push ""
739
- yield
740
- @strs.last
741
- ensure
742
- @strs.pop
743
- end
744
-
745
- def str?
746
- @strs.push ""
747
- yield && @strs.last
748
- ensure
749
- @strs.pop
750
- end
751
-
752
- def node(node)
753
- node.line = @line
754
- node
755
- end
756
-
757
- @sass_script_parser = Class.new(Sass::Script::Parser)
758
- @sass_script_parser.send(:include, ScriptParser)
759
- # @private
760
- def self.sass_script_parser; @sass_script_parser; end
761
-
762
- def sass_script(*args)
763
- parser = self.class.sass_script_parser.new(@scanner, @line,
764
- @scanner.pos - (@scanner.string[0...@scanner.pos].rindex("\n") || 0))
765
- result = parser.send(*args)
766
- @line = parser.line
767
- result
768
- end
769
-
770
- def merge(arr)
771
- arr && Haml::Util.merge_adjacent_strings([arr].flatten)
772
- end
773
-
774
- EXPR_NAMES = {
775
- :media_query => "media query (e.g. print, screen, print and screen)",
776
- :media_expr => "media expression (e.g. (min-device-width: 800px)))",
777
- :pseudo_expr => "expression (e.g. fr, 2n+1)",
778
- :interp_ident => "identifier",
779
- :interp_name => "identifier",
780
- :expr => "expression (e.g. 1px, bold)",
781
- :selector_comma_sequence => "selector",
782
- :simple_selector_sequence => "selector",
783
- :import_arg => "file to import (string or url())",
784
- }
785
-
786
- TOK_NAMES = Haml::Util.to_hash(
787
- Sass::SCSS::RX.constants.map {|c| [Sass::SCSS::RX.const_get(c), c.downcase]}).
788
- merge(IDENT => "identifier", /[;}]/ => '";"')
789
-
790
- def tok?(rx)
791
- @scanner.match?(rx)
792
- end
793
-
794
- def expr!(name)
795
- (e = send(name)) && (return e)
796
- expected(EXPR_NAMES[name] || name.to_s)
797
- end
798
-
799
- def tok!(rx)
800
- (t = tok(rx)) && (return t)
801
- name = TOK_NAMES[rx]
802
-
803
- unless name
804
- # Display basic regexps as plain old strings
805
- string = rx.source.gsub(/\\(.)/, '\1')
806
- name = rx.source == Regexp.escape(string) ? string.inspect : rx.inspect
807
- end
808
-
809
- expected(name)
810
- end
811
-
812
- def expected(name)
813
- self.class.expected(@scanner, @expected || name, @line)
814
- end
815
-
816
- # @private
817
- def self.expected(scanner, expected, line)
818
- pos = scanner.pos
819
-
820
- after = scanner.string[0...pos]
821
- # Get rid of whitespace between pos and the last token,
822
- # but only if there's a newline in there
823
- after.gsub!(/\s*\n\s*$/, '')
824
- # Also get rid of stuff before the last newline
825
- after.gsub!(/.*\n/, '')
826
- after = "..." + after[-15..-1] if after.size > 18
827
-
828
- was = scanner.rest.dup
829
- # Get rid of whitespace between pos and the next token,
830
- # but only if there's a newline in there
831
- was.gsub!(/^\s*\n\s*/, '')
832
- # Also get rid of stuff after the next newline
833
- was.gsub!(/\n.*/, '')
834
- was = was[0...15] + "..." if was.size > 18
835
-
836
- raise Sass::SyntaxError.new(
837
- "Invalid CSS after \"#{after}\": expected #{expected}, was \"#{was}\"",
838
- :line => line)
839
- end
840
-
841
- def tok(rx)
842
- res = @scanner.scan(rx)
843
- if res
844
- @line += res.count("\n")
845
- @expected = nil
846
- if !@strs.empty? && rx != COMMENT && rx != SINGLE_LINE_COMMENT
847
- @strs.each {|s| s << res}
848
- end
849
- end
850
-
851
- res
852
- end
853
- end
854
- end
855
- end