haml 2.2.24 → 3.0.0.beta.1
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.
- data/.yardopts +0 -1
- data/README.md +91 -151
- data/REMEMBER +11 -1
- data/Rakefile +73 -55
- data/VERSION +1 -1
- data/VERSION_NAME +1 -1
- data/bin/css2sass +7 -1
- data/bin/sass-convert +7 -0
- data/extra/haml-mode.el +2 -1
- data/lib/haml/buffer.rb +22 -4
- data/lib/haml/engine.rb +5 -1
- data/lib/haml/exec.rb +231 -46
- data/lib/haml/filters.rb +19 -8
- data/lib/haml/helpers.rb +47 -20
- data/lib/haml/helpers/action_view_extensions.rb +2 -4
- data/lib/haml/helpers/action_view_mods.rb +11 -8
- data/lib/haml/helpers/xss_mods.rb +13 -2
- data/lib/haml/html.rb +179 -48
- data/lib/haml/html/erb.rb +141 -0
- data/lib/haml/precompiler.rb +40 -15
- data/lib/haml/railtie.rb +1 -5
- data/lib/haml/root.rb +3 -0
- data/lib/haml/template.rb +4 -14
- data/lib/haml/util.rb +120 -30
- data/lib/haml/version.rb +25 -2
- data/lib/sass.rb +5 -1
- data/lib/sass/callbacks.rb +50 -0
- data/lib/sass/css.rb +40 -191
- data/lib/sass/engine.rb +170 -74
- data/lib/sass/environment.rb +8 -2
- data/lib/sass/error.rb +163 -25
- data/lib/sass/files.rb +31 -28
- data/lib/sass/plugin.rb +268 -87
- data/lib/sass/plugin/rails.rb +9 -4
- data/lib/sass/repl.rb +1 -1
- data/lib/sass/script.rb +31 -29
- data/lib/sass/script/bool.rb +1 -0
- data/lib/sass/script/color.rb +290 -23
- data/lib/sass/script/css_lexer.rb +22 -0
- data/lib/sass/script/css_parser.rb +28 -0
- data/lib/sass/script/funcall.rb +22 -3
- data/lib/sass/script/functions.rb +523 -33
- data/lib/sass/script/interpolation.rb +42 -0
- data/lib/sass/script/lexer.rb +169 -52
- data/lib/sass/script/literal.rb +58 -9
- data/lib/sass/script/node.rb +79 -1
- data/lib/sass/script/number.rb +20 -5
- data/lib/sass/script/operation.rb +49 -3
- data/lib/sass/script/parser.rb +162 -28
- data/lib/sass/script/string.rb +50 -2
- data/lib/sass/script/unary_operation.rb +25 -2
- data/lib/sass/script/variable.rb +21 -4
- data/lib/sass/scss.rb +14 -0
- data/lib/sass/scss/css_parser.rb +39 -0
- data/lib/sass/scss/parser.rb +683 -0
- data/lib/sass/scss/rx.rb +112 -0
- data/lib/sass/scss/script_lexer.rb +13 -0
- data/lib/sass/scss/script_parser.rb +25 -0
- data/lib/sass/tree/comment_node.rb +69 -27
- data/lib/sass/tree/debug_node.rb +7 -2
- data/lib/sass/tree/directive_node.rb +41 -35
- data/lib/sass/tree/for_node.rb +6 -0
- data/lib/sass/tree/if_node.rb +13 -1
- data/lib/sass/tree/import_node.rb +52 -27
- data/lib/sass/tree/mixin_def_node.rb +18 -0
- data/lib/sass/tree/mixin_node.rb +41 -6
- data/lib/sass/tree/node.rb +197 -70
- data/lib/sass/tree/prop_node.rb +152 -57
- data/lib/sass/tree/root_node.rb +118 -0
- data/lib/sass/tree/rule_node.rb +193 -96
- data/lib/sass/tree/variable_node.rb +9 -5
- data/lib/sass/tree/while_node.rb +4 -0
- data/test/benchmark.rb +5 -5
- data/test/haml/engine_test.rb +147 -10
- data/test/haml/{rhtml/_av_partial_1.rhtml → erb/_av_partial_1.erb} +1 -1
- data/test/haml/{rhtml/_av_partial_2.rhtml → erb/_av_partial_2.erb} +1 -1
- data/test/haml/{rhtml/action_view.rhtml → erb/action_view.erb} +1 -1
- data/test/haml/{rhtml/standard.rhtml → erb/standard.erb} +0 -0
- data/test/haml/helper_test.rb +91 -24
- data/test/haml/html2haml/erb_tests.rb +410 -0
- data/test/haml/html2haml_test.rb +210 -66
- data/test/haml/results/filters.xhtml +1 -1
- data/test/haml/results/just_stuff.xhtml +2 -0
- data/test/haml/spec_test.rb +44 -0
- data/test/haml/template_test.rb +22 -2
- data/test/haml/templates/helpers.haml +0 -13
- data/test/haml/templates/just_stuff.haml +2 -0
- data/test/haml/util_test.rb +48 -0
- data/test/sass/callbacks_test.rb +61 -0
- data/test/sass/conversion_test.rb +884 -0
- data/test/sass/css2sass_test.rb +99 -18
- data/test/sass/data/hsl-rgb.txt +319 -0
- data/test/sass/engine_test.rb +1049 -131
- data/test/sass/functions_test.rb +398 -47
- data/test/sass/more_results/more_import.css +1 -1
- data/test/sass/more_templates/more_import.sass +3 -3
- data/test/sass/plugin_test.rb +184 -10
- data/test/sass/results/compact.css +1 -1
- data/test/sass/results/complex.css +5 -5
- data/test/sass/results/compressed.css +1 -1
- data/test/sass/results/expanded.css +1 -1
- data/test/sass/results/import.css +3 -1
- data/test/sass/results/mixins.css +12 -12
- data/test/sass/results/nested.css +1 -1
- data/test/sass/results/options.css +1 -0
- data/test/sass/results/parent_ref.css +4 -4
- data/test/sass/results/script.css +3 -3
- data/test/sass/results/scss_import.css +15 -0
- data/test/sass/results/scss_importee.css +2 -0
- data/test/sass/script_conversion_test.rb +153 -0
- data/test/sass/script_test.rb +137 -70
- data/test/sass/scss/css_test.rb +811 -0
- data/test/sass/scss/rx_test.rb +156 -0
- data/test/sass/scss/scss_test.rb +871 -0
- data/test/sass/scss/test_helper.rb +37 -0
- data/test/sass/templates/alt.sass +2 -2
- data/test/sass/templates/bork1.sass +2 -0
- data/test/sass/templates/bork3.sass +2 -0
- data/test/sass/templates/bork4.sass +2 -0
- data/test/sass/templates/import.sass +4 -4
- data/test/sass/templates/importee.sass +3 -3
- data/test/sass/templates/line_numbers.sass +1 -1
- data/test/sass/templates/mixin_bork.sass +5 -0
- data/test/sass/templates/mixins.sass +2 -2
- data/test/sass/templates/nested_bork1.sass +2 -0
- data/test/sass/templates/nested_bork2.sass +2 -0
- data/test/sass/templates/nested_bork3.sass +2 -0
- data/test/sass/templates/nested_bork4.sass +2 -0
- data/test/sass/templates/nested_mixin_bork.sass +6 -0
- data/test/sass/templates/options.sass +2 -0
- data/test/sass/templates/parent_ref.sass +2 -2
- data/test/sass/templates/script.sass +69 -69
- data/test/sass/templates/scss_import.scss +10 -0
- data/test/sass/templates/scss_importee.scss +1 -0
- data/test/sass/templates/units.sass +10 -10
- data/test/test_helper.rb +20 -8
- data/vendor/fssm/LICENSE +20 -0
- data/vendor/fssm/README.markdown +55 -0
- data/vendor/fssm/Rakefile +59 -0
- data/vendor/fssm/VERSION.yml +5 -0
- data/vendor/fssm/example.rb +9 -0
- data/vendor/fssm/fssm.gemspec +77 -0
- data/vendor/fssm/lib/fssm.rb +33 -0
- data/vendor/fssm/lib/fssm/backends/fsevents.rb +36 -0
- data/vendor/fssm/lib/fssm/backends/inotify.rb +26 -0
- data/vendor/fssm/lib/fssm/backends/polling.rb +25 -0
- data/vendor/fssm/lib/fssm/backends/rubycocoa/fsevents.rb +131 -0
- data/vendor/fssm/lib/fssm/monitor.rb +26 -0
- data/vendor/fssm/lib/fssm/path.rb +91 -0
- data/vendor/fssm/lib/fssm/pathname.rb +502 -0
- data/vendor/fssm/lib/fssm/state/directory.rb +57 -0
- data/vendor/fssm/lib/fssm/state/file.rb +24 -0
- data/vendor/fssm/lib/fssm/support.rb +63 -0
- data/vendor/fssm/lib/fssm/tree.rb +176 -0
- data/vendor/fssm/profile/prof-cache.rb +40 -0
- data/vendor/fssm/profile/prof-fssm-pathname.html +1231 -0
- data/vendor/fssm/profile/prof-pathname.rb +68 -0
- data/vendor/fssm/profile/prof-plain-pathname.html +988 -0
- data/vendor/fssm/profile/prof.html +2379 -0
- data/vendor/fssm/spec/path_spec.rb +75 -0
- data/vendor/fssm/spec/root/duck/quack.txt +0 -0
- data/vendor/fssm/spec/root/file.css +0 -0
- data/vendor/fssm/spec/root/file.rb +0 -0
- data/vendor/fssm/spec/root/file.yml +0 -0
- data/vendor/fssm/spec/root/moo/cow.txt +0 -0
- data/vendor/fssm/spec/spec_helper.rb +14 -0
- metadata +94 -14
- data/test/sass/templates/bork.sass +0 -2
data/lib/haml/version.rb
CHANGED
@@ -23,11 +23,27 @@ module Haml
|
|
23
23
|
# :major => 2, :minor => 1, :teeny => 0
|
24
24
|
# }
|
25
25
|
#
|
26
|
+
# If a prerelease version of Haml is being used,
|
27
|
+
# the `:string` and `:number` fields will reflect the full version
|
28
|
+
# (e.g. `"2.2.beta.1"`), and the `:tiny` field will be `-1`.
|
29
|
+
# A `:prerelease` key will contain the name of the prerelease (e.g. `"beta"`),
|
30
|
+
# and a `:prerelease_number` key will contain the rerelease number.
|
31
|
+
# For example:
|
32
|
+
#
|
33
|
+
# {
|
34
|
+
# :string => "3.0.beta.1",
|
35
|
+
# :number => "3.0.beta.1",
|
36
|
+
# :major => 3, :minor => 0, :tiny => -1,
|
37
|
+
# :prerelease => "beta",
|
38
|
+
# :prerelease_number => 1
|
39
|
+
# }
|
40
|
+
#
|
26
41
|
# @return [{Symbol => String/Fixnum}] The version hash
|
27
42
|
def version
|
28
43
|
return @@version if defined?(@@version)
|
29
44
|
|
30
|
-
numbers = File.read(scope('VERSION')).strip.split('.').
|
45
|
+
numbers = File.read(scope('VERSION')).strip.split('.').
|
46
|
+
map {|n| n =~ /^[0-9]+$/ ? n.to_i : n}
|
31
47
|
name = File.read(scope('VERSION_NAME')).strip
|
32
48
|
@@version = {
|
33
49
|
:major => numbers[0],
|
@@ -35,7 +51,14 @@ module Haml
|
|
35
51
|
:teeny => numbers[2],
|
36
52
|
:name => name
|
37
53
|
}
|
38
|
-
|
54
|
+
|
55
|
+
if numbers[3].is_a?(String)
|
56
|
+
@@version[:teeny] = -1
|
57
|
+
@@version[:prerelease] = numbers[3]
|
58
|
+
@@version[:prerelease_number] = numbers[4]
|
59
|
+
end
|
60
|
+
|
61
|
+
@@version[:number] = numbers.join('.')
|
39
62
|
@@version[:string] = @@version[:number].dup
|
40
63
|
|
41
64
|
if rev = revision_number
|
data/lib/sass.rb
CHANGED
@@ -5,7 +5,7 @@ require 'haml/version'
|
|
5
5
|
|
6
6
|
# The module that contains everything Sass-related:
|
7
7
|
#
|
8
|
-
# * {Sass::Engine} is the class used to render Sass within Ruby code.
|
8
|
+
# * {Sass::Engine} is the class used to render Sass/SCSS within Ruby code.
|
9
9
|
# * {Sass::Plugin} is interfaces with web frameworks (Rails and Merb in particular).
|
10
10
|
# * {Sass::SyntaxError} is raised when Sass encounters an error.
|
11
11
|
# * {Sass::CSS} handles conversion of CSS to Sass.
|
@@ -20,5 +20,9 @@ module Sass
|
|
20
20
|
end
|
21
21
|
|
22
22
|
require 'haml/util'
|
23
|
+
|
24
|
+
dir = Haml::Util.scope("vendor/fssm/lib")
|
25
|
+
$LOAD_PATH.unshift dir unless $LOAD_PATH.include?(dir)
|
26
|
+
|
23
27
|
require 'sass/engine'
|
24
28
|
require 'sass/plugin' if defined?(Merb::Plugins)
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# A lightweight infrastructure for defining and running callbacks.
|
2
|
+
# Callbacks are defined using \{#define\_callback\} at the class level,
|
3
|
+
# and called using `run_#{name}` at the instance level.
|
4
|
+
#
|
5
|
+
# Clients can add callbacks by calling the generated `on_#{name}` method,
|
6
|
+
# and passing in a block that's run when the callback is activated.
|
7
|
+
#
|
8
|
+
# @example Define a callback
|
9
|
+
# class Munger
|
10
|
+
# extend Sass::Callbacks
|
11
|
+
# define_callback :string_munged
|
12
|
+
#
|
13
|
+
# def munge(str)
|
14
|
+
# res = str.gsub(/[a-z]/, '\1\1')
|
15
|
+
# run_string_munged str, res
|
16
|
+
# res
|
17
|
+
# end
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# @example Use a callback
|
21
|
+
# m = Munger.new
|
22
|
+
# m.on_string_munged {|str, res| puts "#{str} was munged into #{res}!"}
|
23
|
+
# m.munge "bar" #=> bar was munged into bbaarr!
|
24
|
+
module Sass::Callbacks
|
25
|
+
protected
|
26
|
+
|
27
|
+
# Define a callback with the given name.
|
28
|
+
# This will define an `on_#{name}` method
|
29
|
+
# that registers a block,
|
30
|
+
# and a `run_#{name}` method that runs that block
|
31
|
+
# (optionall with some arguments).
|
32
|
+
#
|
33
|
+
# @param name [Symbol] The name of the callback
|
34
|
+
# @return [void]
|
35
|
+
def define_callback(name)
|
36
|
+
class_eval <<RUBY
|
37
|
+
def on_#{name}(&block)
|
38
|
+
@_sass_callbacks ||= {}
|
39
|
+
(@_sass_callbacks[#{name.inspect}] ||= []) << block
|
40
|
+
end
|
41
|
+
|
42
|
+
def run_#{name}(*args)
|
43
|
+
return unless @_sass_callbacks
|
44
|
+
return unless @_sass_callbacks[#{name.inspect}]
|
45
|
+
@_sass_callbacks[#{name.inspect}].each {|c| c[*args]}
|
46
|
+
end
|
47
|
+
private :run_#{name}
|
48
|
+
RUBY
|
49
|
+
end
|
50
|
+
end
|
data/lib/sass/css.rb
CHANGED
@@ -1,69 +1,25 @@
|
|
1
1
|
require File.dirname(__FILE__) + '/../sass'
|
2
2
|
require 'sass/tree/node'
|
3
|
+
require 'sass/scss/css_parser'
|
3
4
|
require 'strscan'
|
4
5
|
|
5
6
|
module Sass
|
6
|
-
|
7
|
-
class Node
|
8
|
-
# Converts a node to Sass code that will generate it.
|
9
|
-
#
|
10
|
-
# @param tabs [Fixnum] The amount of tabulation to use for the Sass code
|
11
|
-
# @param opts [{Symbol => Object}] An options hash (see {Sass::CSS#initialize})
|
12
|
-
# @return [String] The Sass code corresponding to the node
|
13
|
-
def to_sass(tabs = 0, opts = {})
|
14
|
-
result = ''
|
15
|
-
|
16
|
-
children.each do |child|
|
17
|
-
result << "#{' ' * tabs}#{child.to_sass(0, opts)}\n"
|
18
|
-
end
|
19
|
-
|
20
|
-
result
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
class RuleNode
|
25
|
-
# @see Node#to_sass
|
26
|
-
def to_sass(tabs, opts = {})
|
27
|
-
name = rules.first
|
28
|
-
name = "\\" + name if name[0] == ?:
|
29
|
-
str = "\n#{' ' * tabs}#{name}#{children.any? { |c| c.is_a? PropNode } ? "\n" : ''}"
|
30
|
-
|
31
|
-
children.each do |child|
|
32
|
-
str << "#{child.to_sass(tabs + 1, opts)}"
|
33
|
-
end
|
34
|
-
|
35
|
-
str
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
class PropNode
|
40
|
-
# @see Node#to_sass
|
41
|
-
def to_sass(tabs, opts = {})
|
42
|
-
"#{' ' * tabs}#{opts[:old] ? ':' : ''}#{name}#{opts[:old] ? '' : ':'} #{value}\n"
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
class DirectiveNode
|
47
|
-
# @see Node#to_sass
|
48
|
-
def to_sass(tabs, opts = {})
|
49
|
-
"#{' ' * tabs}#{value}#{children.map {|c| c.to_sass(tabs + 1, opts)}}\n"
|
50
|
-
end
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
# This class converts CSS documents into Sass templates.
|
7
|
+
# This class converts CSS documents into Sass or SCSS templates.
|
55
8
|
# It works by parsing the CSS document into a {Sass::Tree} structure,
|
56
9
|
# and then applying various transformations to the structure
|
57
|
-
# to produce more concise and idiomatic Sass.
|
10
|
+
# to produce more concise and idiomatic Sass/SCSS.
|
58
11
|
#
|
59
12
|
# Example usage:
|
60
13
|
#
|
61
|
-
# Sass::CSS.new("p { color: blue }").render #=> "p\n color: blue"
|
14
|
+
# Sass::CSS.new("p { color: blue }").render(:sass) #=> "p\n color: blue"
|
15
|
+
# Sass::CSS.new("p { color: blue }").render(:scss) #=> "p {\n color: blue; }"
|
62
16
|
class CSS
|
63
17
|
# @param template [String] The CSS code
|
64
18
|
# @option options :old [Boolean] (false)
|
65
19
|
# Whether or not to output old property syntax
|
66
20
|
# (`:color blue` as opposed to `color: blue`).
|
21
|
+
# This is only meaningful when generating Sass code,
|
22
|
+
# rather than SCSS.
|
67
23
|
def initialize(template, options = {})
|
68
24
|
if template.is_a? IO
|
69
25
|
template = template.read
|
@@ -72,21 +28,23 @@ module Sass
|
|
72
28
|
@options = options.dup
|
73
29
|
# Backwards compatibility
|
74
30
|
@options[:old] = true if @options[:alternate] == false
|
75
|
-
@template =
|
31
|
+
@template = template
|
76
32
|
end
|
77
33
|
|
78
|
-
# Converts the CSS template into Sass code.
|
34
|
+
# Converts the CSS template into Sass or SCSS code.
|
79
35
|
#
|
80
|
-
# @
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
line
|
86
|
-
|
87
|
-
err.backtrace.unshift "(css):#{line}"
|
88
|
-
raise err
|
36
|
+
# @param fmt [Symbol] `:sass` or `:scss`, designating the format to return.
|
37
|
+
# @return [String] The resulting Sass or SCSS code
|
38
|
+
# @raise [Sass::SyntaxError] if there's an error parsing the CSS template
|
39
|
+
def render(fmt = :sass)
|
40
|
+
Haml::Util.check_encoding(@template) do |msg, line|
|
41
|
+
raise Sass::SyntaxError.new(msg, :line => line)
|
89
42
|
end
|
43
|
+
|
44
|
+
build_tree.send("to_#{fmt}", @options).strip + "\n"
|
45
|
+
rescue Sass::SyntaxError => err
|
46
|
+
err.modify_backtrace(:filename => @options[:filename] || '(css)')
|
47
|
+
raise err
|
90
48
|
end
|
91
49
|
|
92
50
|
private
|
@@ -95,9 +53,7 @@ module Sass
|
|
95
53
|
#
|
96
54
|
# @return [Tree::Node] The root node of the parsed tree
|
97
55
|
def build_tree
|
98
|
-
root =
|
99
|
-
whitespace
|
100
|
-
rules root
|
56
|
+
root = Sass::SCSS::CssParser.new(@template).parse
|
101
57
|
expand_commas root
|
102
58
|
parent_ref_rules root
|
103
59
|
remove_parent_refs root
|
@@ -106,117 +62,6 @@ module Sass
|
|
106
62
|
root
|
107
63
|
end
|
108
64
|
|
109
|
-
# Parses a set of CSS rules.
|
110
|
-
#
|
111
|
-
# @param root [Tree::Node] The parent node of the rules
|
112
|
-
def rules(root)
|
113
|
-
while r = rule
|
114
|
-
root << r
|
115
|
-
whitespace
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
|
-
# Parses a single CSS rule.
|
120
|
-
#
|
121
|
-
# @return [Tree::Node] The parsed rule
|
122
|
-
def rule
|
123
|
-
rule = ""
|
124
|
-
loop do
|
125
|
-
token = @template.scan(/(?:[^\{\};\/\s]|\/[^*])+/)
|
126
|
-
if token.nil?
|
127
|
-
return if rule.empty?
|
128
|
-
break
|
129
|
-
end
|
130
|
-
rule << token
|
131
|
-
break unless @template.match?(/\s|\/\*/)
|
132
|
-
whitespace
|
133
|
-
rule << " "
|
134
|
-
end
|
135
|
-
|
136
|
-
rule.strip!
|
137
|
-
directive = rule[0] == ?@
|
138
|
-
|
139
|
-
if directive
|
140
|
-
node = Tree::DirectiveNode.new(rule)
|
141
|
-
return node if @template.scan(/;/)
|
142
|
-
|
143
|
-
assert_match /\{/
|
144
|
-
whitespace
|
145
|
-
|
146
|
-
rules(node)
|
147
|
-
return node
|
148
|
-
end
|
149
|
-
|
150
|
-
assert_match /\{/
|
151
|
-
node = Tree::RuleNode.new(rule)
|
152
|
-
properties(node)
|
153
|
-
return node
|
154
|
-
end
|
155
|
-
|
156
|
-
# Parses a set of CSS properties within a rule.
|
157
|
-
#
|
158
|
-
# @param rule [Tree::RuleNode] The parent node of the properties
|
159
|
-
def properties(rule)
|
160
|
-
while @template.scan(/[^:\}\s]+/)
|
161
|
-
name = @template[0]
|
162
|
-
whitespace
|
163
|
-
|
164
|
-
assert_match /:/
|
165
|
-
|
166
|
-
value = ''
|
167
|
-
while @template.scan(/[^;\s\}]+/)
|
168
|
-
value << @template[0] << whitespace
|
169
|
-
end
|
170
|
-
|
171
|
-
assert_match /(;|(?=\}))/
|
172
|
-
rule << Tree::PropNode.new(name, value, nil)
|
173
|
-
end
|
174
|
-
|
175
|
-
assert_match /\}/
|
176
|
-
end
|
177
|
-
|
178
|
-
# Moves the scanner over a section of whitespace or comments.
|
179
|
-
#
|
180
|
-
# @return [String] The ignored whitespace
|
181
|
-
def whitespace
|
182
|
-
space = @template.scan(/\s*/) || ''
|
183
|
-
|
184
|
-
# If we've hit a comment,
|
185
|
-
# go past it and look for more whitespace
|
186
|
-
if @template.scan(/\/\*/)
|
187
|
-
@template.scan_until(/\*\//)
|
188
|
-
return space + whitespace
|
189
|
-
end
|
190
|
-
return space
|
191
|
-
end
|
192
|
-
|
193
|
-
# Moves the scanner over a regular expression,
|
194
|
-
# raising an exception if it doesn't match.
|
195
|
-
#
|
196
|
-
# @param re [Regexp] The regular expression to assert
|
197
|
-
def assert_match(re)
|
198
|
-
if @template.scan(re)
|
199
|
-
whitespace
|
200
|
-
return
|
201
|
-
end
|
202
|
-
|
203
|
-
line = @template.string[0..@template.pos].count "\n"
|
204
|
-
pos = @template.pos
|
205
|
-
|
206
|
-
after = @template.string[pos - 15...pos]
|
207
|
-
after = "..." + after if pos >= 15
|
208
|
-
|
209
|
-
# Display basic regexps as plain old strings
|
210
|
-
expected = re.source == Regexp.escape(re.source) ? "\"#{re.source}\"" : re.inspect
|
211
|
-
|
212
|
-
was = @template.rest[0...15]
|
213
|
-
was += "..." if @template.rest.size >= 15
|
214
|
-
raise Exception.new(<<MESSAGE)
|
215
|
-
Invalid CSS on line #{line + 1} after #{after.inspect}:
|
216
|
-
expected #{expected}, was #{was.inspect}
|
217
|
-
MESSAGE
|
218
|
-
end
|
219
|
-
|
220
65
|
# Transform
|
221
66
|
#
|
222
67
|
# foo, bar, baz
|
@@ -234,9 +79,9 @@ MESSAGE
|
|
234
79
|
# @param root [Tree::Node] The parent node
|
235
80
|
def expand_commas(root)
|
236
81
|
root.children.map! do |child|
|
237
|
-
next child unless Tree::RuleNode === child && child.
|
238
|
-
child.
|
239
|
-
node = Tree::RuleNode.new(rule.strip)
|
82
|
+
next child unless Tree::RuleNode === child && child.rule.first.include?(',')
|
83
|
+
child.rule.first.split(',').map do |rule|
|
84
|
+
node = Tree::RuleNode.new([rule.strip])
|
240
85
|
node.children = child.children
|
241
86
|
node
|
242
87
|
end
|
@@ -280,22 +125,26 @@ MESSAGE
|
|
280
125
|
# @param root [Tree::Node] The parent node
|
281
126
|
def parent_ref_rules(root)
|
282
127
|
current_rule = nil
|
283
|
-
root.children.
|
284
|
-
|
285
|
-
first, rest = child.rules.first.scan(/^(&?(?: .|[^ ])[^.#: \[]*)([.#: \[].*)?$/).first
|
128
|
+
root.children.map! do |child|
|
129
|
+
next child unless child.is_a?(Tree::RuleNode)
|
286
130
|
|
287
|
-
|
288
|
-
|
289
|
-
|
131
|
+
first, rest = child.rule.first.scan(/^(&?(?: .|[^ ])[^.#: \[]*)([.#: \[].*)?$/).first
|
132
|
+
|
133
|
+
if current_rule.nil? || current_rule.rule.first != first
|
134
|
+
current_rule = Tree::RuleNode.new([first])
|
290
135
|
end
|
291
136
|
|
292
137
|
if rest
|
293
|
-
child.
|
138
|
+
child.rule = ["&" + rest]
|
294
139
|
current_rule << child
|
295
140
|
else
|
296
141
|
current_rule.children += child.children
|
297
142
|
end
|
143
|
+
|
144
|
+
current_rule
|
298
145
|
end
|
146
|
+
root.children.compact!
|
147
|
+
root.children.uniq!
|
299
148
|
|
300
149
|
root.children.each { |v| parent_ref_rules(v) }
|
301
150
|
end
|
@@ -316,7 +165,7 @@ MESSAGE
|
|
316
165
|
def remove_parent_refs(root)
|
317
166
|
root.children.each do |child|
|
318
167
|
if child.is_a?(Tree::RuleNode)
|
319
|
-
child.
|
168
|
+
child.rule.first.gsub! /^& +/, ''
|
320
169
|
remove_parent_refs child
|
321
170
|
end
|
322
171
|
end
|
@@ -357,10 +206,10 @@ MESSAGE
|
|
357
206
|
while rule.children.size == 1 && rule.children.first.is_a?(Tree::RuleNode)
|
358
207
|
child = rule.children.first
|
359
208
|
|
360
|
-
if child.
|
361
|
-
rule.
|
209
|
+
if child.rule.first[0] == ?&
|
210
|
+
rule.rule = [child.rule.first.gsub(/^&/, rule.rule.first)]
|
362
211
|
else
|
363
|
-
rule.
|
212
|
+
rule.rule = ["#{rule.rule.first} #{child.rule.first}"]
|
364
213
|
end
|
365
214
|
|
366
215
|
rule.children = child.children
|
@@ -390,7 +239,7 @@ MESSAGE
|
|
390
239
|
next child unless child.is_a?(Tree::RuleNode)
|
391
240
|
|
392
241
|
if prev_rule && prev_rule.children == child.children
|
393
|
-
prev_rule.
|
242
|
+
prev_rule.rule.first << ", #{child.rule.first}"
|
394
243
|
next nil
|
395
244
|
end
|
396
245
|
|
data/lib/sass/engine.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'strscan'
|
2
2
|
require 'digest/sha1'
|
3
3
|
require 'sass/tree/node'
|
4
|
+
require 'sass/tree/root_node'
|
4
5
|
require 'sass/tree/rule_node'
|
5
6
|
require 'sass/tree/comment_node'
|
6
7
|
require 'sass/tree/prop_node'
|
@@ -15,6 +16,7 @@ require 'sass/tree/debug_node'
|
|
15
16
|
require 'sass/tree/import_node'
|
16
17
|
require 'sass/environment'
|
17
18
|
require 'sass/script'
|
19
|
+
require 'sass/scss'
|
18
20
|
require 'sass/error'
|
19
21
|
require 'sass/files'
|
20
22
|
require 'haml/shared'
|
@@ -122,7 +124,7 @@ module Sass
|
|
122
124
|
# The regex that matches and extracts data from
|
123
125
|
# properties of the form `name: prop`.
|
124
126
|
# @private
|
125
|
-
PROPERTY_NEW = /^([^\s=:"]+)
|
127
|
+
PROPERTY_NEW = /^([^\s=:"]+)\s*(=|:)(?:\s+|$)(.*)/
|
126
128
|
|
127
129
|
# The regex that matches and extracts data from
|
128
130
|
# properties of the form `:name prop`.
|
@@ -135,6 +137,7 @@ module Sass
|
|
135
137
|
:load_paths => ['.'],
|
136
138
|
:cache => true,
|
137
139
|
:cache_location => './.sass-cache',
|
140
|
+
:syntax => :sass,
|
138
141
|
}.freeze
|
139
142
|
|
140
143
|
# @param template [String] The Sass template.
|
@@ -171,11 +174,21 @@ module Sass
|
|
171
174
|
# @return [Sass::Tree::Node] The root of the parse tree.
|
172
175
|
# @raise [Sass::SyntaxError] if there's an error in the document
|
173
176
|
def to_tree
|
174
|
-
|
175
|
-
|
177
|
+
@template = check_encoding(@template) {|msg, line| raise Sass::SyntaxError.new(msg, :line => line)}
|
178
|
+
|
179
|
+
if @options[:syntax] == :scss
|
180
|
+
root = Sass::SCSS::Parser.new(@template).parse
|
181
|
+
else
|
182
|
+
root = Tree::RootNode.new(@template)
|
183
|
+
append_children(root, tree(tabulate(@template)).first, true)
|
184
|
+
end
|
185
|
+
|
176
186
|
root.options = @options
|
177
187
|
root
|
178
|
-
rescue SyntaxError => e
|
188
|
+
rescue SyntaxError => e
|
189
|
+
e.modify_backtrace(:filename => @options[:filename], :line => @line)
|
190
|
+
e.sass_template = @template
|
191
|
+
raise e
|
179
192
|
end
|
180
193
|
|
181
194
|
private
|
@@ -202,10 +215,11 @@ module Sass
|
|
202
215
|
|
203
216
|
tab_str ||= line_tab_str
|
204
217
|
|
205
|
-
raise SyntaxError.new("Indenting at the beginning of the document is illegal.",
|
206
|
-
|
207
|
-
|
208
|
-
|
218
|
+
raise SyntaxError.new("Indenting at the beginning of the document is illegal.",
|
219
|
+
:line => index) if first
|
220
|
+
|
221
|
+
raise SyntaxError.new("Indentation can't use both tabs and spaces.",
|
222
|
+
:line => index) if tab_str.include?(?\s) && tab_str.include?(?\t)
|
209
223
|
end
|
210
224
|
first &&= !tab_str.nil?
|
211
225
|
if tab_str.nil?
|
@@ -221,10 +235,14 @@ module Sass
|
|
221
235
|
end
|
222
236
|
|
223
237
|
line_tabs = line_tab_str.scan(tab_str).size
|
224
|
-
|
238
|
+
if tab_str * line_tabs != line_tab_str
|
239
|
+
message = <<END.strip.gsub("\n", ' ')
|
225
240
|
Inconsistent indentation: #{Haml::Shared.human_indentation line_tab_str, true} used for indentation,
|
226
241
|
but the rest of the document was indented using #{Haml::Shared.human_indentation tab_str}.
|
227
242
|
END
|
243
|
+
raise SyntaxError.new(message, :line => index)
|
244
|
+
end
|
245
|
+
|
228
246
|
lines << Line.new(line.strip, line_tabs, index, tab_str.size, @options[:filename], [])
|
229
247
|
end
|
230
248
|
lines
|
@@ -234,7 +252,7 @@ END
|
|
234
252
|
return unless last && last.comment?
|
235
253
|
return unless line =~ /^#{tab_str}/
|
236
254
|
unless line =~ /^(?:#{comment_tab_str})(.*)$/
|
237
|
-
raise SyntaxError.new(<<MSG.strip.gsub("\n", " "), index)
|
255
|
+
raise SyntaxError.new(<<MSG.strip.gsub("\n", " "), :line => index)
|
238
256
|
Inconsistent indentation:
|
239
257
|
previous line was indented by #{Haml::Shared.human_indentation comment_tab_str},
|
240
258
|
but this line was indented by #{Haml::Shared.human_indentation line[/^\s*/]}.
|
@@ -252,9 +270,8 @@ MSG
|
|
252
270
|
nodes = []
|
253
271
|
while (line = arr[i]) && line.tabs >= base
|
254
272
|
if line.tabs > base
|
255
|
-
|
256
|
-
|
257
|
-
end
|
273
|
+
raise SyntaxError.new("The line was indented #{line.tabs - base} levels deeper than the previous line.",
|
274
|
+
:line => line.index) if line.tabs > base + 1
|
258
275
|
|
259
276
|
nodes.last.children, i = tree(arr, i)
|
260
277
|
else
|
@@ -276,11 +293,7 @@ MSG
|
|
276
293
|
node.line = line.index
|
277
294
|
node.filename = line.filename
|
278
295
|
|
279
|
-
|
280
|
-
node.lines = line.children
|
281
|
-
else
|
282
|
-
append_children(node, line.children, false)
|
283
|
-
end
|
296
|
+
append_children(node, line.children, false)
|
284
297
|
end
|
285
298
|
|
286
299
|
node_or_nodes
|
@@ -288,11 +301,13 @@ MSG
|
|
288
301
|
|
289
302
|
def append_children(parent, children, root)
|
290
303
|
continued_rule = nil
|
304
|
+
continued_comment = nil
|
291
305
|
children.each do |line|
|
292
306
|
child = build_tree(parent, line, root)
|
293
307
|
|
294
308
|
if child.is_a?(Tree::RuleNode) && child.continued?
|
295
|
-
raise SyntaxError.new("Rules can't end in commas.",
|
309
|
+
raise SyntaxError.new("Rules can't end in commas.",
|
310
|
+
:line => child.line) unless child.children.empty?
|
296
311
|
if continued_rule
|
297
312
|
continued_rule.add_rules child
|
298
313
|
else
|
@@ -302,31 +317,35 @@ MSG
|
|
302
317
|
end
|
303
318
|
|
304
319
|
if continued_rule
|
305
|
-
raise SyntaxError.new("Rules can't end in commas.",
|
320
|
+
raise SyntaxError.new("Rules can't end in commas.",
|
321
|
+
:line => continued_rule.line) unless child.is_a?(Tree::RuleNode)
|
306
322
|
continued_rule.add_rules child
|
307
323
|
continued_rule.children = child.children
|
308
324
|
continued_rule, child = nil, continued_rule
|
309
325
|
end
|
310
326
|
|
327
|
+
if child.is_a?(Tree::CommentNode) && child.silent
|
328
|
+
if continued_comment &&
|
329
|
+
child.line == continued_comment.line +
|
330
|
+
continued_comment.value.count("\n") + 1
|
331
|
+
continued_comment.value << "\n" << child.value
|
332
|
+
next
|
333
|
+
end
|
334
|
+
|
335
|
+
continued_comment = child
|
336
|
+
end
|
337
|
+
|
311
338
|
check_for_no_children(child)
|
312
339
|
validate_and_append_child(parent, child, line, root)
|
313
340
|
end
|
314
341
|
|
315
|
-
raise SyntaxError.new("Rules can't end in commas.",
|
342
|
+
raise SyntaxError.new("Rules can't end in commas.",
|
343
|
+
:line => continued_rule.line) if continued_rule
|
316
344
|
|
317
345
|
parent
|
318
346
|
end
|
319
347
|
|
320
348
|
def validate_and_append_child(parent, child, line, root)
|
321
|
-
unless root
|
322
|
-
case child
|
323
|
-
when Tree::MixinDefNode
|
324
|
-
raise SyntaxError.new("Mixins may only be defined at the root of a document.", line.index)
|
325
|
-
when Tree::ImportNode
|
326
|
-
raise SyntaxError.new("Import directives may only be used at the root of a document.", line.index)
|
327
|
-
end
|
328
|
-
end
|
329
|
-
|
330
349
|
case child
|
331
350
|
when Array
|
332
351
|
child.each {|c| validate_and_append_child(parent, c, line, root)}
|
@@ -337,18 +356,10 @@ MSG
|
|
337
356
|
|
338
357
|
def check_for_no_children(node)
|
339
358
|
return unless node.is_a?(Tree::RuleNode) && node.children.empty?
|
340
|
-
|
359
|
+
Haml::Util.haml_warn(<<WARNING.strip)
|
341
360
|
WARNING on line #{node.line}#{" of #{node.filename}" if node.filename}:
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
WARNING on line #{node.line}#{" of #{node.filename}" if node.filename}:
|
346
|
-
Selector
|
347
|
-
#{node.rules.join("\n ")}
|
348
|
-
doesn't have any properties and will not be rendered.
|
349
|
-
LONG
|
350
|
-
|
351
|
-
warn(warning.strip)
|
361
|
+
This selector doesn't have any properties and will not be rendered.
|
362
|
+
WARNING
|
352
363
|
end
|
353
364
|
|
354
365
|
def parse_line(parent, line, root)
|
@@ -361,23 +372,23 @@ LONG
|
|
361
372
|
# which begin with ::,
|
362
373
|
# as well as pseudo-classes
|
363
374
|
# if we're using the new property syntax
|
364
|
-
Tree::RuleNode.new(line.text)
|
375
|
+
Tree::RuleNode.new(parse_interp(line.text))
|
365
376
|
else
|
366
377
|
parse_property(line, PROPERTY_OLD)
|
367
378
|
end
|
368
|
-
when
|
379
|
+
when ?!, ?$
|
369
380
|
parse_variable(line)
|
370
381
|
when COMMENT_CHAR
|
371
382
|
parse_comment(line.text)
|
372
383
|
when DIRECTIVE_CHAR
|
373
384
|
parse_directive(parent, line, root)
|
374
385
|
when ESCAPE_CHAR
|
375
|
-
Tree::RuleNode.new(line.text[1..-1])
|
386
|
+
Tree::RuleNode.new(parse_interp(line.text[1..-1]))
|
376
387
|
when MIXIN_DEFINITION_CHAR
|
377
388
|
parse_mixin_definition(line)
|
378
389
|
when MIXIN_INCLUDE_CHAR
|
379
390
|
if line.text[1].nil? || line.text[1] == ?\s
|
380
|
-
Tree::RuleNode.new(line.text)
|
391
|
+
Tree::RuleNode.new(parse_interp(line.text))
|
381
392
|
else
|
382
393
|
parse_mixin_include(line, root)
|
383
394
|
end
|
@@ -385,7 +396,7 @@ LONG
|
|
385
396
|
if line.text =~ PROPERTY_NEW_MATCHER
|
386
397
|
parse_property(line, PROPERTY_NEW)
|
387
398
|
else
|
388
|
-
Tree::RuleNode.new(line.text)
|
399
|
+
Tree::RuleNode.new(parse_interp(line.text))
|
389
400
|
end
|
390
401
|
end
|
391
402
|
end
|
@@ -393,30 +404,54 @@ LONG
|
|
393
404
|
def parse_property(line, property_regx)
|
394
405
|
name, eq, value = line.text.scan(property_regx)[0]
|
395
406
|
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
407
|
+
raise SyntaxError.new("Invalid property: \"#{line.text}\".",
|
408
|
+
:line => @line) if name.nil? || value.nil?
|
409
|
+
|
410
|
+
if value.strip.empty?
|
411
|
+
expr = Sass::Script::String.new("")
|
401
412
|
else
|
402
|
-
value
|
413
|
+
expr = parse_script(value, :offset => line.offset + line.text.index(value))
|
414
|
+
|
415
|
+
if eq.strip[0] == SCRIPT_CHAR
|
416
|
+
expr.context = :equals
|
417
|
+
Script.equals_warning("properties", name,
|
418
|
+
Sass::Tree::PropNode.val_to_sass(expr), false,
|
419
|
+
@line, line.offset + 1, @options[:filename])
|
420
|
+
end
|
403
421
|
end
|
404
|
-
Tree::PropNode.new(
|
422
|
+
Tree::PropNode.new(
|
423
|
+
parse_interp(name), expr,
|
424
|
+
property_regx == PROPERTY_OLD ? :old : :new)
|
405
425
|
end
|
406
426
|
|
407
427
|
def parse_variable(line)
|
408
|
-
name, op, value = line.text.scan(Script::MATCH)[0]
|
409
|
-
|
410
|
-
raise SyntaxError.new("
|
428
|
+
name, op, value, default = line.text.scan(Script::MATCH)[0]
|
429
|
+
guarded = op =~ /^\|\|/
|
430
|
+
raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath variable declarations.",
|
431
|
+
:line => @line + 1) unless line.children.empty?
|
432
|
+
raise SyntaxError.new("Invalid variable: \"#{line.text}\".",
|
433
|
+
:line => @line) unless name && value
|
434
|
+
Script.var_warning(name, @line, line.offset + 1, @options[:filename]) if line.text[0] == ?!
|
435
|
+
|
436
|
+
expr = parse_script(value, :offset => line.offset + line.text.index(value))
|
437
|
+
if op =~ /=$/
|
438
|
+
expr.context = :equals
|
439
|
+
type = guarded ? "variable defaults" : "variables"
|
440
|
+
Script.equals_warning(type, "$#{name}", expr.to_sass,
|
441
|
+
guarded, @line, line.offset + 1, @options[:filename])
|
442
|
+
end
|
411
443
|
|
412
|
-
Tree::VariableNode.new(name,
|
444
|
+
Tree::VariableNode.new(name, expr, default || guarded)
|
413
445
|
end
|
414
446
|
|
415
447
|
def parse_comment(line)
|
416
448
|
if line[1] == CSS_COMMENT_CHAR || line[1] == SASS_COMMENT_CHAR
|
417
|
-
|
449
|
+
silent = line[1] == SASS_COMMENT_CHAR
|
450
|
+
Tree::CommentNode.new(
|
451
|
+
format_comment_text(line[2..-1], silent),
|
452
|
+
silent)
|
418
453
|
else
|
419
|
-
Tree::RuleNode.new(line)
|
454
|
+
Tree::RuleNode.new(parse_interp(line))
|
420
455
|
end
|
421
456
|
end
|
422
457
|
|
@@ -427,8 +462,13 @@ LONG
|
|
427
462
|
# If value begins with url( or ",
|
428
463
|
# it's a CSS @import rule and we don't want to touch it.
|
429
464
|
if directive == "import" && value !~ /^(url\(|")/
|
430
|
-
raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath import directives.",
|
465
|
+
raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath import directives.",
|
466
|
+
:line => @line + 1) unless line.children.empty?
|
431
467
|
value.split(/,\s*/).map {|f| Tree::ImportNode.new(f)}
|
468
|
+
elsif directive == "mixin"
|
469
|
+
parse_mixin_definition(line)
|
470
|
+
elsif directive == "include"
|
471
|
+
parse_mixin_include(line, root)
|
432
472
|
elsif directive == "for"
|
433
473
|
parse_for(line, root, value)
|
434
474
|
elsif directive == "else"
|
@@ -441,7 +481,8 @@ LONG
|
|
441
481
|
Tree::IfNode.new(parse_script(value, :offset => offset))
|
442
482
|
elsif directive == "debug"
|
443
483
|
raise SyntaxError.new("Invalid debug directive '@debug': expected expression.") unless value
|
444
|
-
raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath debug directives.",
|
484
|
+
raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath debug directives.",
|
485
|
+
:line => @line + 1) unless line.children.empty?
|
445
486
|
offset = line.offset + line.text.index(value).to_i
|
446
487
|
Tree::DebugNode.new(parse_script(value, :offset => offset))
|
447
488
|
else
|
@@ -460,22 +501,26 @@ LONG
|
|
460
501
|
else
|
461
502
|
expected = "'to <expr>' or 'through <expr>'"
|
462
503
|
end
|
463
|
-
raise SyntaxError.new("Invalid for directive '@for #{text}': expected #{expected}."
|
504
|
+
raise SyntaxError.new("Invalid for directive '@for #{text}': expected #{expected}.")
|
505
|
+
end
|
506
|
+
raise SyntaxError.new("Invalid variable \"#{var}\".") unless var =~ Script::VALIDATE
|
507
|
+
if var.slice!(0) == ?!
|
508
|
+
offset = line.offset + line.text.index("!" + var) + 1
|
509
|
+
Script.var_warning(var, @line, offset, @options[:filename])
|
464
510
|
end
|
465
|
-
raise SyntaxError.new("Invalid variable \"#{var}\".", @line) unless var =~ Script::VALIDATE
|
466
511
|
|
467
512
|
parsed_from = parse_script(from_expr, :offset => line.offset + line.text.index(from_expr))
|
468
513
|
parsed_to = parse_script(to_expr, :offset => line.offset + line.text.index(to_expr))
|
469
|
-
Tree::ForNode.new(var
|
514
|
+
Tree::ForNode.new(var, parsed_from, parsed_to, to_name == 'to')
|
470
515
|
end
|
471
516
|
|
472
517
|
def parse_else(parent, line, text)
|
473
|
-
previous = parent.last
|
518
|
+
previous = parent.children.last
|
474
519
|
raise SyntaxError.new("@else must come after @if.") unless previous.is_a?(Tree::IfNode)
|
475
520
|
|
476
521
|
if text
|
477
522
|
if text !~ /^if\s+(.+)/
|
478
|
-
raise SyntaxError.new("Invalid else directive '@else #{text}': expected 'if <expr>'."
|
523
|
+
raise SyntaxError.new("Invalid else directive '@else #{text}': expected 'if <expr>'.")
|
479
524
|
end
|
480
525
|
expr = parse_script($1, :offset => line.offset + line.text.index($1))
|
481
526
|
end
|
@@ -486,30 +531,81 @@ LONG
|
|
486
531
|
nil
|
487
532
|
end
|
488
533
|
|
534
|
+
# @private
|
535
|
+
MIXIN_DEF_RE = /^(?:=|@mixin)\s*(#{Sass::SCSS::RX::IDENT})(.*)$/
|
489
536
|
def parse_mixin_definition(line)
|
490
|
-
name, arg_string = line.text.scan(
|
491
|
-
raise SyntaxError.new("Invalid mixin \"#{line.text[1..-1]}\"."
|
537
|
+
name, arg_string = line.text.scan(MIXIN_DEF_RE).first
|
538
|
+
raise SyntaxError.new("Invalid mixin \"#{line.text[1..-1]}\".") if name.nil?
|
492
539
|
|
493
540
|
offset = line.offset + line.text.size - arg_string.size
|
494
|
-
args = Script::Parser.new(arg_string.strip, @line, offset).
|
541
|
+
args = Script::Parser.new(arg_string.strip, @line, offset, @options).
|
542
|
+
parse_mixin_definition_arglist
|
495
543
|
default_arg_found = false
|
496
544
|
Tree::MixinDefNode.new(name, args)
|
497
545
|
end
|
498
546
|
|
547
|
+
# @private
|
548
|
+
MIXIN_INCLUDE_RE = /^(?:\+|@include)\s*(#{Sass::SCSS::RX::IDENT})(.*)$/
|
499
549
|
def parse_mixin_include(line, root)
|
500
|
-
name, arg_string = line.text.scan(
|
501
|
-
raise SyntaxError.new("Invalid mixin include \"#{line.text}\"."
|
550
|
+
name, arg_string = line.text.scan(MIXIN_INCLUDE_RE).first
|
551
|
+
raise SyntaxError.new("Invalid mixin include \"#{line.text}\".") if name.nil?
|
502
552
|
|
503
553
|
offset = line.offset + line.text.size - arg_string.size
|
504
|
-
args = Script::Parser.new(arg_string.strip, @line, offset).
|
505
|
-
|
554
|
+
args = Script::Parser.new(arg_string.strip, @line, offset, @options).
|
555
|
+
parse_mixin_include_arglist
|
556
|
+
raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath mixin directives.",
|
557
|
+
:line => @line + 1) unless line.children.empty?
|
506
558
|
Tree::MixinNode.new(name, args)
|
507
559
|
end
|
508
560
|
|
509
561
|
def parse_script(script, options = {})
|
510
562
|
line = options[:line] || @line
|
511
563
|
offset = options[:offset] || 0
|
512
|
-
Script.parse(script, line, offset, @options
|
564
|
+
Script.parse(script, line, offset, @options)
|
565
|
+
end
|
566
|
+
|
567
|
+
def format_comment_text(text, silent)
|
568
|
+
content = text.split("\n")
|
569
|
+
|
570
|
+
if content.first && content.first.strip.empty?
|
571
|
+
removed_first = true
|
572
|
+
content.shift
|
573
|
+
end
|
574
|
+
|
575
|
+
return silent ? "//" : "/* */" if content.empty?
|
576
|
+
content.map! {|l| (l.empty? ? "" : " ") + l}
|
577
|
+
content.first.gsub!(/^ /, '') unless removed_first
|
578
|
+
content.last.gsub!(%r{ ?\*/ *$}, '')
|
579
|
+
if silent
|
580
|
+
"//" + content.join("\n//")
|
581
|
+
else
|
582
|
+
"/*" + content.join("\n *") + " */"
|
583
|
+
end
|
584
|
+
end
|
585
|
+
|
586
|
+
def parse_interp(text)
|
587
|
+
self.class.parse_interp(text, @line, :filename => @filename)
|
588
|
+
end
|
589
|
+
|
590
|
+
# It's important that this have strings (at least)
|
591
|
+
# at the beginning, the end, and between each Script::Node.
|
592
|
+
#
|
593
|
+
# @private
|
594
|
+
def self.parse_interp(text, line, options)
|
595
|
+
res = []
|
596
|
+
rest = Haml::Shared.handle_interpolation text do |scan|
|
597
|
+
escapes = scan[2].size
|
598
|
+
res << scan.matched[0...-2 - escapes]
|
599
|
+
if escapes % 2 == 1
|
600
|
+
res << "\\" * (escapes - 1) << '#{'
|
601
|
+
else
|
602
|
+
res << "\\" * [0, escapes - 1].max
|
603
|
+
res << Script::Parser.new(
|
604
|
+
scan, line, scan.pos - scan.matched_size, options).
|
605
|
+
parse_interpolated
|
606
|
+
end
|
607
|
+
end
|
608
|
+
res << rest
|
513
609
|
end
|
514
610
|
end
|
515
611
|
end
|