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.

Files changed (168) hide show
  1. data/.yardopts +0 -1
  2. data/README.md +91 -151
  3. data/REMEMBER +11 -1
  4. data/Rakefile +73 -55
  5. data/VERSION +1 -1
  6. data/VERSION_NAME +1 -1
  7. data/bin/css2sass +7 -1
  8. data/bin/sass-convert +7 -0
  9. data/extra/haml-mode.el +2 -1
  10. data/lib/haml/buffer.rb +22 -4
  11. data/lib/haml/engine.rb +5 -1
  12. data/lib/haml/exec.rb +231 -46
  13. data/lib/haml/filters.rb +19 -8
  14. data/lib/haml/helpers.rb +47 -20
  15. data/lib/haml/helpers/action_view_extensions.rb +2 -4
  16. data/lib/haml/helpers/action_view_mods.rb +11 -8
  17. data/lib/haml/helpers/xss_mods.rb +13 -2
  18. data/lib/haml/html.rb +179 -48
  19. data/lib/haml/html/erb.rb +141 -0
  20. data/lib/haml/precompiler.rb +40 -15
  21. data/lib/haml/railtie.rb +1 -5
  22. data/lib/haml/root.rb +3 -0
  23. data/lib/haml/template.rb +4 -14
  24. data/lib/haml/util.rb +120 -30
  25. data/lib/haml/version.rb +25 -2
  26. data/lib/sass.rb +5 -1
  27. data/lib/sass/callbacks.rb +50 -0
  28. data/lib/sass/css.rb +40 -191
  29. data/lib/sass/engine.rb +170 -74
  30. data/lib/sass/environment.rb +8 -2
  31. data/lib/sass/error.rb +163 -25
  32. data/lib/sass/files.rb +31 -28
  33. data/lib/sass/plugin.rb +268 -87
  34. data/lib/sass/plugin/rails.rb +9 -4
  35. data/lib/sass/repl.rb +1 -1
  36. data/lib/sass/script.rb +31 -29
  37. data/lib/sass/script/bool.rb +1 -0
  38. data/lib/sass/script/color.rb +290 -23
  39. data/lib/sass/script/css_lexer.rb +22 -0
  40. data/lib/sass/script/css_parser.rb +28 -0
  41. data/lib/sass/script/funcall.rb +22 -3
  42. data/lib/sass/script/functions.rb +523 -33
  43. data/lib/sass/script/interpolation.rb +42 -0
  44. data/lib/sass/script/lexer.rb +169 -52
  45. data/lib/sass/script/literal.rb +58 -9
  46. data/lib/sass/script/node.rb +79 -1
  47. data/lib/sass/script/number.rb +20 -5
  48. data/lib/sass/script/operation.rb +49 -3
  49. data/lib/sass/script/parser.rb +162 -28
  50. data/lib/sass/script/string.rb +50 -2
  51. data/lib/sass/script/unary_operation.rb +25 -2
  52. data/lib/sass/script/variable.rb +21 -4
  53. data/lib/sass/scss.rb +14 -0
  54. data/lib/sass/scss/css_parser.rb +39 -0
  55. data/lib/sass/scss/parser.rb +683 -0
  56. data/lib/sass/scss/rx.rb +112 -0
  57. data/lib/sass/scss/script_lexer.rb +13 -0
  58. data/lib/sass/scss/script_parser.rb +25 -0
  59. data/lib/sass/tree/comment_node.rb +69 -27
  60. data/lib/sass/tree/debug_node.rb +7 -2
  61. data/lib/sass/tree/directive_node.rb +41 -35
  62. data/lib/sass/tree/for_node.rb +6 -0
  63. data/lib/sass/tree/if_node.rb +13 -1
  64. data/lib/sass/tree/import_node.rb +52 -27
  65. data/lib/sass/tree/mixin_def_node.rb +18 -0
  66. data/lib/sass/tree/mixin_node.rb +41 -6
  67. data/lib/sass/tree/node.rb +197 -70
  68. data/lib/sass/tree/prop_node.rb +152 -57
  69. data/lib/sass/tree/root_node.rb +118 -0
  70. data/lib/sass/tree/rule_node.rb +193 -96
  71. data/lib/sass/tree/variable_node.rb +9 -5
  72. data/lib/sass/tree/while_node.rb +4 -0
  73. data/test/benchmark.rb +5 -5
  74. data/test/haml/engine_test.rb +147 -10
  75. data/test/haml/{rhtml/_av_partial_1.rhtml → erb/_av_partial_1.erb} +1 -1
  76. data/test/haml/{rhtml/_av_partial_2.rhtml → erb/_av_partial_2.erb} +1 -1
  77. data/test/haml/{rhtml/action_view.rhtml → erb/action_view.erb} +1 -1
  78. data/test/haml/{rhtml/standard.rhtml → erb/standard.erb} +0 -0
  79. data/test/haml/helper_test.rb +91 -24
  80. data/test/haml/html2haml/erb_tests.rb +410 -0
  81. data/test/haml/html2haml_test.rb +210 -66
  82. data/test/haml/results/filters.xhtml +1 -1
  83. data/test/haml/results/just_stuff.xhtml +2 -0
  84. data/test/haml/spec_test.rb +44 -0
  85. data/test/haml/template_test.rb +22 -2
  86. data/test/haml/templates/helpers.haml +0 -13
  87. data/test/haml/templates/just_stuff.haml +2 -0
  88. data/test/haml/util_test.rb +48 -0
  89. data/test/sass/callbacks_test.rb +61 -0
  90. data/test/sass/conversion_test.rb +884 -0
  91. data/test/sass/css2sass_test.rb +99 -18
  92. data/test/sass/data/hsl-rgb.txt +319 -0
  93. data/test/sass/engine_test.rb +1049 -131
  94. data/test/sass/functions_test.rb +398 -47
  95. data/test/sass/more_results/more_import.css +1 -1
  96. data/test/sass/more_templates/more_import.sass +3 -3
  97. data/test/sass/plugin_test.rb +184 -10
  98. data/test/sass/results/compact.css +1 -1
  99. data/test/sass/results/complex.css +5 -5
  100. data/test/sass/results/compressed.css +1 -1
  101. data/test/sass/results/expanded.css +1 -1
  102. data/test/sass/results/import.css +3 -1
  103. data/test/sass/results/mixins.css +12 -12
  104. data/test/sass/results/nested.css +1 -1
  105. data/test/sass/results/options.css +1 -0
  106. data/test/sass/results/parent_ref.css +4 -4
  107. data/test/sass/results/script.css +3 -3
  108. data/test/sass/results/scss_import.css +15 -0
  109. data/test/sass/results/scss_importee.css +2 -0
  110. data/test/sass/script_conversion_test.rb +153 -0
  111. data/test/sass/script_test.rb +137 -70
  112. data/test/sass/scss/css_test.rb +811 -0
  113. data/test/sass/scss/rx_test.rb +156 -0
  114. data/test/sass/scss/scss_test.rb +871 -0
  115. data/test/sass/scss/test_helper.rb +37 -0
  116. data/test/sass/templates/alt.sass +2 -2
  117. data/test/sass/templates/bork1.sass +2 -0
  118. data/test/sass/templates/bork3.sass +2 -0
  119. data/test/sass/templates/bork4.sass +2 -0
  120. data/test/sass/templates/import.sass +4 -4
  121. data/test/sass/templates/importee.sass +3 -3
  122. data/test/sass/templates/line_numbers.sass +1 -1
  123. data/test/sass/templates/mixin_bork.sass +5 -0
  124. data/test/sass/templates/mixins.sass +2 -2
  125. data/test/sass/templates/nested_bork1.sass +2 -0
  126. data/test/sass/templates/nested_bork2.sass +2 -0
  127. data/test/sass/templates/nested_bork3.sass +2 -0
  128. data/test/sass/templates/nested_bork4.sass +2 -0
  129. data/test/sass/templates/nested_mixin_bork.sass +6 -0
  130. data/test/sass/templates/options.sass +2 -0
  131. data/test/sass/templates/parent_ref.sass +2 -2
  132. data/test/sass/templates/script.sass +69 -69
  133. data/test/sass/templates/scss_import.scss +10 -0
  134. data/test/sass/templates/scss_importee.scss +1 -0
  135. data/test/sass/templates/units.sass +10 -10
  136. data/test/test_helper.rb +20 -8
  137. data/vendor/fssm/LICENSE +20 -0
  138. data/vendor/fssm/README.markdown +55 -0
  139. data/vendor/fssm/Rakefile +59 -0
  140. data/vendor/fssm/VERSION.yml +5 -0
  141. data/vendor/fssm/example.rb +9 -0
  142. data/vendor/fssm/fssm.gemspec +77 -0
  143. data/vendor/fssm/lib/fssm.rb +33 -0
  144. data/vendor/fssm/lib/fssm/backends/fsevents.rb +36 -0
  145. data/vendor/fssm/lib/fssm/backends/inotify.rb +26 -0
  146. data/vendor/fssm/lib/fssm/backends/polling.rb +25 -0
  147. data/vendor/fssm/lib/fssm/backends/rubycocoa/fsevents.rb +131 -0
  148. data/vendor/fssm/lib/fssm/monitor.rb +26 -0
  149. data/vendor/fssm/lib/fssm/path.rb +91 -0
  150. data/vendor/fssm/lib/fssm/pathname.rb +502 -0
  151. data/vendor/fssm/lib/fssm/state/directory.rb +57 -0
  152. data/vendor/fssm/lib/fssm/state/file.rb +24 -0
  153. data/vendor/fssm/lib/fssm/support.rb +63 -0
  154. data/vendor/fssm/lib/fssm/tree.rb +176 -0
  155. data/vendor/fssm/profile/prof-cache.rb +40 -0
  156. data/vendor/fssm/profile/prof-fssm-pathname.html +1231 -0
  157. data/vendor/fssm/profile/prof-pathname.rb +68 -0
  158. data/vendor/fssm/profile/prof-plain-pathname.html +988 -0
  159. data/vendor/fssm/profile/prof.html +2379 -0
  160. data/vendor/fssm/spec/path_spec.rb +75 -0
  161. data/vendor/fssm/spec/root/duck/quack.txt +0 -0
  162. data/vendor/fssm/spec/root/file.css +0 -0
  163. data/vendor/fssm/spec/root/file.rb +0 -0
  164. data/vendor/fssm/spec/root/file.yml +0 -0
  165. data/vendor/fssm/spec/root/moo/cow.txt +0 -0
  166. data/vendor/fssm/spec/spec_helper.rb +14 -0
  167. metadata +94 -14
  168. data/test/sass/templates/bork.sass +0 -2
@@ -0,0 +1,112 @@
1
+ module Sass
2
+ module SCSS
3
+ # A module containing regular expressions used
4
+ # for lexing tokens in an SCSS document.
5
+ # Most of these are taken from [the CSS3 spec](http://www.w3.org/TR/css3-syntax/#lexical),
6
+ # although some have been modified for various reasons.
7
+ module RX
8
+ # Takes a string and returns a CSS identifier
9
+ # that will have the value of the given string.
10
+ #
11
+ # @param str [String] The string to escape
12
+ # @return [String] The escaped string
13
+ def self.escape_ident(str)
14
+ return "" if str.empty?
15
+ return "\\#{str}" if str == '-' || str == '_'
16
+ out = ""
17
+ value = str.dup
18
+ out << value.slice!(0...1) if value =~ /^[-_]/
19
+ if value[0...1] =~ NMSTART
20
+ out << value.slice!(0...1)
21
+ else
22
+ out << escape_char(value.slice!(0...1))
23
+ end
24
+ out << value.gsub(/[^a-zA-Z0-9_-]/) {|c| escape_char c}
25
+ return out
26
+ end
27
+
28
+ # Escapes a single character for a CSS identifier.
29
+ #
30
+ # @param c [String] The character to escape. Should have length 1
31
+ # @return [String] The escaped character
32
+ # @private
33
+ def self.escape_char(c)
34
+ return "\\%06x" % Haml::Util.ord(c) unless c =~ /[ -\/:-~]/
35
+ return "\\#{c}"
36
+ end
37
+
38
+ # Creates a Regexp from a plain text string,
39
+ # escaping all significant characters.
40
+ #
41
+ # @param str [String] The text of the regexp
42
+ # @param flags [Fixnum] Flags for the created regular expression
43
+ # @return [Regexp]
44
+ # @private
45
+ def self.quote(str, flags = 0)
46
+ Regexp.new(Regexp.quote(str), flags)
47
+ end
48
+
49
+ H = /[0-9a-fA-F]/
50
+ NL = /\n|\r\n|\r|\f/
51
+ UNICODE = /\\#{H}{1,6}[ \t\r\n\f]?/
52
+ s = if Haml::Util.ruby1_8?
53
+ '\200-\377'
54
+ else
55
+ '\u{80}-\u{D7FF}\u{E000}-\u{FFFD}\u{10000}-\u{10FFFF}'
56
+ end
57
+ NONASCII = /[#{s}]/
58
+ ESCAPE = /#{UNICODE}|\\[ -~#{s}]/
59
+ NMSTART = /[a-zA-Z]|#{NONASCII}|#{ESCAPE}/
60
+ NMCHAR = /[a-zA-Z0-9_-]|#{NONASCII}|#{ESCAPE}/
61
+ STRING1 = /\"((?:[^\n\r\f\\"]|\\#{NL}|#{ESCAPE})*)\"/
62
+ STRING2 = /\'((?:[^\n\r\f\\']|\\#{NL}|#{ESCAPE})*)\'/
63
+
64
+ IDENT = /[-_]?#{NMSTART}#{NMCHAR}*/
65
+ NAME = /#{NMCHAR}+/
66
+ NUM = /[0-9]+|[0-9]*.[0-9]+/
67
+ STRING = /#{STRING1}|#{STRING2}/
68
+ URL = /((?:[!#%$&*-~]|#{NONASCII}|#{ESCAPE})*)/
69
+ W = /[ \t\r\n\f]*/
70
+
71
+ # This is more liberal than the spec's definition,
72
+ # but that definition didn't work well with the greediness rules
73
+ RANGE = /(?:#{H}|\?){1,6}/
74
+
75
+ ##
76
+
77
+ S = /[ \t\r\n\f]+/
78
+
79
+ COMMENT = /\/\*[^*]*\*+(?:[^\/][^*]*\*+)*\//
80
+ SINGLE_LINE_COMMENT = /\/\/.*(\n[ \t]*\/\/.*)*/
81
+
82
+ CDO = quote("<!--")
83
+ CDC = quote("-->")
84
+ INCLUDES = quote("~=")
85
+ DASHMATCH = quote("|=")
86
+ PREFIXMATCH = quote("^=")
87
+ SUFFIXMATCH = quote("$=")
88
+ SUBSTRINGMATCH = quote("*=")
89
+
90
+ HASH = /##{NAME}/
91
+
92
+ IMPORTANT = /!#{W}important/i
93
+ DEFAULT = /!#{W}default/i
94
+
95
+ NUMBER = /#{NUM}(?:#{IDENT}|%)?/
96
+
97
+ URI = /url\(#{W}(?:#{STRING}|#{URL})#{W}\)/i
98
+ FUNCTION = /#{IDENT}\(/
99
+
100
+ UNICODERANGE = /u\+(?:#{H}{1,6}-#{H}{1,6}|#{RANGE})/i
101
+
102
+ # Defined in http://www.w3.org/TR/css3-selectors/#lex
103
+ PLUS = /#{W}\+/
104
+ GREATER = /#{W}>/
105
+ TILDE = /#{W}~/
106
+ NOT = quote(":not(", Regexp::IGNORECASE)
107
+
108
+ # Custom
109
+ HEXCOLOR = /\#[0-9a-fA-F]{3}(?:[0-9a-fA-F]{3})?/
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,13 @@
1
+ module Sass
2
+ module SCSS
3
+ # A mixin for subclasses of {Sass::Script::Lexer}
4
+ # that makes them usable by {SCSS::Parser} to parse SassScript.
5
+ # In particular, the lexer doesn't support `!` for a variable prefix.
6
+ module ScriptLexer
7
+ def variable
8
+ return [:raw, "!important"] if scan(Sass::SCSS::RX::IMPORTANT)
9
+ _variable(/(\$)(#{Sass::SCSS::RX::IDENT})/)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,25 @@
1
+ module Sass
2
+ module SCSS
3
+ # A mixin for subclasses of {Sass::Script::Parser}
4
+ # that makes them usable by {SCSS::Parser} to parse SassScript.
5
+ # In particular, the parser won't raise an error
6
+ # when there's more content in the lexer once lexing is done.
7
+ # In addition, the parser doesn't support `!` for a variable prefix.
8
+ module ScriptParser
9
+ private
10
+
11
+ # @private
12
+ def lexer_class
13
+ klass = Class.new(super)
14
+ klass.send(:include, ScriptLexer)
15
+ klass
16
+ end
17
+
18
+ # Instead of raising an error when the parser is done,
19
+ # rewind the StringScanner so that it hasn't consumed the final token.
20
+ def assert_done
21
+ @lexer.unpeek!
22
+ end
23
+ end
24
+ end
25
+ end
@@ -5,12 +5,7 @@ module Sass::Tree
5
5
  #
6
6
  # @see Sass::Tree
7
7
  class CommentNode < Node
8
- # The lines of text nested beneath the comment.
9
- #
10
- # @return [Array<Sass::Engine::Line>]
11
- attr_accessor :lines
12
-
13
- # The text on the same line as the comment starter.
8
+ # The text of the comment, not including `/*` and `*/`.
14
9
  #
15
10
  # @return [String]
16
11
  attr_accessor :value
@@ -24,7 +19,7 @@ module Sass::Tree
24
19
  # @param silent [Boolean] See \{#silent}
25
20
  def initialize(value, silent)
26
21
  @lines = []
27
- @value = value[2..-1].strip
22
+ @value = normalize_indentation value
28
23
  @silent = silent
29
24
  super()
30
25
  end
@@ -35,9 +30,58 @@ module Sass::Tree
35
30
  # @return [Boolean] Whether or not this node and the other object
36
31
  # are the same
37
32
  def ==(other)
38
- self.class == other.class && value == other.value && silent == other.silent && lines == other.lines
33
+ self.class == other.class && value == other.value && silent == other.silent
34
+ end
35
+
36
+ # Returns `true` if this is a silent comment
37
+ # or the current style doesn't render comments.
38
+ #
39
+ # @return [Boolean]
40
+ def invisible?
41
+ style == :compressed || @silent
42
+ end
43
+
44
+ # @see Node#to_sass
45
+ def to_sass(tabs, opts = {})
46
+ content = value.gsub(/\*\/$/, '').rstrip
47
+ if content =~ /\A[ \t]/
48
+ # Re-indent SCSS comments like this:
49
+ # /* foo
50
+ # bar
51
+ # baz */
52
+ content.gsub!(/^/, ' ')
53
+ content.sub!(/\A([ \t]*)\/\*/, '/*\1')
54
+ end
55
+
56
+ content =
57
+ unless content.include?("\n")
58
+ content
59
+ else
60
+ content.gsub!(/\n( \*|\/\/)/, "\n ")
61
+ spaces = content.scan(/\n( *)/).map {|s| s.first.size}.min
62
+ if spaces >= 2
63
+ content
64
+ else
65
+ content.gsub(/\n#{' ' * spaces}/, "\n ")
66
+ end
67
+ end
68
+
69
+ content.gsub!(/^/, ' ' * tabs)
70
+ content.gsub!(/\A\/\*/, '//') if silent
71
+ content.rstrip + "\n"
72
+ end
73
+
74
+ def to_scss(tabs, opts = {})
75
+ spaces = (' ' * [tabs - value[/^ */].size, 0].max)
76
+ if silent
77
+ value.gsub(/^[\/ ]\*/, '//').gsub(/ *\*\/$/, '')
78
+ else
79
+ value
80
+ end.gsub(/^/, spaces) + "\n"
39
81
  end
40
82
 
83
+ protected
84
+
41
85
  # Computes the CSS for the comment.
42
86
  #
43
87
  # Returns `nil` if this is a silent comment
@@ -47,29 +91,15 @@ module Sass::Tree
47
91
  # @param tabs [Fixnum] The level of indentation for the CSS
48
92
  # @return [String, nil] The resulting CSS
49
93
  # @see #invisible?
50
- def to_s(tabs = 0, _ = nil)
94
+ def _to_s(tabs = 0, _ = nil)
51
95
  return if invisible?
52
- spaces = ' ' * (tabs - 1)
53
-
54
- content = (value.split("\n") + lines.map {|l| l.text})
55
- return spaces + "/* */" if content.empty?
56
- content.map! {|l| (l.empty? ? "" : " ") + l}
57
- content.first.gsub!(/^ /, '')
58
- content.last.gsub!(%r{ ?\*/ *$}, '')
59
-
60
- spaces + "/* " + content.join(style == :compact ? '' : "\n#{spaces} *") + " */"
61
- end
96
+ spaces = (' ' * [tabs - 1 - value[/^ */].size, 0].max)
62
97
 
63
- # Returns `true` if this is a silent comment
64
- # or the current style doesn't render comments.
65
- #
66
- # @return [Boolean]
67
- def invisible?
68
- style == :compressed || @silent
98
+ content = value.gsub(/^/, spaces)
99
+ content.gsub!(/\n +(\* *)?/, ' ') if style == :compact
100
+ content
69
101
  end
70
102
 
71
- protected
72
-
73
103
  # Removes this node from the tree if it's a silent comment.
74
104
  #
75
105
  # @param environment [Sass::Environment] The lexical environment containing
@@ -80,5 +110,17 @@ module Sass::Tree
80
110
  return [] if @silent
81
111
  self
82
112
  end
113
+
114
+ private
115
+
116
+ def normalize_indentation(str)
117
+ pre = str.split("\n").inject(str[/^[ \t]*/].split("")) do |pre, line|
118
+ line[/^[ \t]*/].split("").zip(pre).inject([]) do |arr, (a, b)|
119
+ break arr if a != b
120
+ arr + [a]
121
+ end
122
+ end.join
123
+ str.gsub(/^#{pre}/, '')
124
+ end
83
125
  end
84
126
  end
@@ -12,16 +12,21 @@ module Sass
12
12
 
13
13
  protected
14
14
 
15
+ def to_src(tabs, opts, fmt)
16
+ "#{' ' * tabs}@debug #{@expr.to_sass}#{semi fmt}\n"
17
+ end
18
+
15
19
  # Prints the expression to STDERR.
16
20
  #
17
21
  # @param environment [Sass::Environment] The lexical environment containing
18
22
  # variable and mixin values
19
23
  def _perform(environment)
20
24
  res = @expr.perform(environment)
25
+ res = res.value if res.is_a?(Sass::Script::String)
21
26
  if filename
22
- STDERR.puts "#{filename}:#{line} DEBUG: #{res}"
27
+ $stderr.puts "#{filename}:#{line} DEBUG: #{res}"
23
28
  else
24
- STDERR.puts "Line #{line} DEBUG: #{res}"
29
+ $stderr.puts "Line #{line} DEBUG: #{res}"
25
30
  end
26
31
  []
27
32
  end
@@ -20,49 +20,55 @@ module Sass::Tree
20
20
  super()
21
21
  end
22
22
 
23
+ protected
24
+
25
+ def to_src(tabs, opts, fmt)
26
+ res = "#{' ' * tabs}#{value}"
27
+ return res + "#{semi fmt}\n" if children.empty?
28
+ res + children_to_src(tabs, opts, fmt) + "\n"
29
+ end
30
+
23
31
  # Computes the CSS for the directive.
24
32
  #
25
33
  # @param tabs [Fixnum] The level of indentation for the CSS
26
34
  # @return [String] The resulting CSS
27
- def to_s(tabs)
28
- if children.empty?
29
- value + ";"
30
- else
31
- result = if style == :compressed
32
- "#{value}{"
33
- else
34
- "#{' ' * (tabs - 1)}#{value} {" + (style == :compact ? ' ' : "\n")
35
- end
36
- was_prop = false
37
- first = true
38
- children.each do |child|
39
- next if child.invisible?
40
- if style == :compact
41
- if child.is_a?(PropNode)
42
- result << "#{child.to_s(first || was_prop ? 1 : tabs + 1)} "
43
- else
44
- if was_prop
45
- result[-1] = "\n"
46
- end
47
- rendered = child.to_s(tabs + 1)
48
- rendered.lstrip! if first
49
- result << rendered
50
- end
51
- was_prop = child.is_a?(PropNode)
52
- first = false
53
- elsif style == :compressed
54
- result << (was_prop ? ";#{child.to_s(1)}" : child.to_s(1))
55
- was_prop = child.is_a?(PropNode)
35
+ def _to_s(tabs)
36
+ return value + ";" unless has_children
37
+ return value + " {}" if children.empty?
38
+ result = if style == :compressed
39
+ "#{value}{"
40
+ else
41
+ "#{' ' * (tabs - 1)}#{value} {" + (style == :compact ? ' ' : "\n")
42
+ end
43
+ was_prop = false
44
+ first = true
45
+ children.each do |child|
46
+ next if child.invisible?
47
+ if style == :compact
48
+ if child.is_a?(PropNode)
49
+ result << "#{child.to_s(first || was_prop ? 1 : tabs + 1)} "
56
50
  else
57
- result << child.to_s(tabs + 1) + "\n"
51
+ if was_prop
52
+ result[-1] = "\n"
53
+ end
54
+ rendered = child.to_s(tabs + 1).dup
55
+ rendered = rendered.lstrip if first
56
+ result << rendered.rstrip + "\n"
58
57
  end
58
+ was_prop = child.is_a?(PropNode)
59
+ first = false
60
+ elsif style == :compressed
61
+ result << (was_prop ? ";#{child.to_s(1)}" : child.to_s(1))
62
+ was_prop = child.is_a?(PropNode)
63
+ else
64
+ result << child.to_s(tabs + 1) + "\n"
59
65
  end
60
- result.rstrip + if style == :compressed
61
- "}"
62
- else
63
- (style == :expanded ? "\n" : " ") + "}\n"
64
- end
65
66
  end
67
+ result.rstrip + if style == :compressed
68
+ "}"
69
+ else
70
+ (style == :expanded ? "\n" : " ") + "}\n"
71
+ end
66
72
  end
67
73
  end
68
74
  end
@@ -20,6 +20,12 @@ module Sass::Tree
20
20
 
21
21
  protected
22
22
 
23
+ def to_src(tabs, opts, fmt)
24
+ to = @exclusive ? "to" : "through"
25
+ "#{' ' * tabs}@for $#{@var} from #{@from.to_sass} #{to} #{@to.to_sass}" +
26
+ children_to_src(tabs, opts, fmt)
27
+ end
28
+
23
29
  # Runs the child nodes once for each time through the loop,
24
30
  # varying the variable each time.
25
31
  #
@@ -30,7 +30,6 @@ module Sass::Tree
30
30
  @last_else = node
31
31
  end
32
32
 
33
- # @see Node#options=
34
33
  def options=(options)
35
34
  super
36
35
  self.else.options = options if self.else
@@ -38,6 +37,19 @@ module Sass::Tree
38
37
 
39
38
  protected
40
39
 
40
+ def to_src(tabs, opts, fmt, is_else = false)
41
+ name =
42
+ if !is_else; "if"
43
+ elsif @expr; "else if"
44
+ else; "else"
45
+ end
46
+ str = "#{' ' * tabs}@#{name}"
47
+ str << " #{@expr.to_sass}" if @expr
48
+ str << children_to_src(tabs, opts, fmt)
49
+ str << @else.send(:to_src, tabs, opts, fmt, true) if @else
50
+ str
51
+ end
52
+
41
53
  # Runs the child nodes if the conditional expression is true;
42
54
  # otherwise, tries the \{#else} nodes.
43
55
  #
@@ -3,38 +3,72 @@ module Sass
3
3
  # A static node that wraps the {Sass::Tree} for an `@import`ed file.
4
4
  # It doesn't have a functional purpose other than to add the `@import`ed file
5
5
  # to the backtrace if an error occurs.
6
- class ImportNode < Node
6
+ class ImportNode < RootNode
7
+ # The name of the imported file as it appears in the Sass document.
8
+ #
9
+ # @return [String]
10
+ attr_reader :imported_filename
11
+
7
12
  # @param imported_filename [String] The name of the imported file
8
13
  def initialize(imported_filename)
9
14
  @imported_filename = imported_filename
10
- super()
15
+ super(nil)
11
16
  end
12
17
 
13
- # Computes the CSS for the imported file.
18
+ def invisible?; to_s.empty?; end
19
+
20
+ # Returns the resolved name of the imported file,
21
+ # as returned by \{Sass::Files#find\_file\_to\_import}.
14
22
  #
15
- # @param args [Array] Ignored
16
- def to_s(*args)
17
- @to_s ||= (style == :compressed ? super().strip : super())
18
- rescue Sass::SyntaxError => e
19
- e.add_backtrace_entry(@filename)
20
- raise e
23
+ # @return [String] The filename of the imported file.
24
+ # This is an absolute path if the file is a `".sass"` or `".scss"` file.
25
+ # @raise [Sass::SyntaxError] if `filename` ends in `".sass"` or `".scss"`
26
+ # and no corresponding Sass file could be found.
27
+ def full_filename
28
+ @full_filename ||= import
21
29
  end
22
30
 
23
- def invisible?; to_s.empty?; end
31
+ def to_sass(tabs = 0, opts = {})
32
+ "#{' ' * tabs}@import #{@imported_filename}\n"
33
+ end
34
+
35
+ def to_scss(tabs = 0, opts = {})
36
+ "#{' ' * tabs}@import \"#{@imported_filename}\";\n"
37
+ end
24
38
 
25
39
  protected
26
40
 
27
- # Parses the imported file
28
- # and runs the dynamic Sass for it.
41
+ # @see Node#_cssize
42
+ def _cssize(*args)
43
+ super.children
44
+ rescue Sass::SyntaxError => e
45
+ e.modify_backtrace(:filename => children.first.filename)
46
+ e.add_backtrace(:filename => @filename, :line => @line)
47
+ raise e
48
+ end
49
+
50
+ # Returns a static DirectiveNode if this is importing a CSS file,
51
+ # or parses and includes the imported Sass file.
52
+ #
53
+ # @param environment [Sass::Environment] The lexical environment containing
54
+ # variable and mixin values
55
+ def _perform(environment)
56
+ return DirectiveNode.new("@import url(#{full_filename})") if full_filename =~ /\.css$/
57
+ super
58
+ end
59
+
60
+ # Parses the imported file and runs the dynamic Sass for it.
29
61
  #
30
62
  # @param environment [Sass::Environment] The lexical environment containing
31
63
  # variable and mixin values
32
64
  def perform!(environment)
33
- return unless full_filename = import
34
- self.children = Sass::Files.tree_for(full_filename, @options).children
65
+ root = Sass::Files.tree_for(full_filename, @options)
66
+ @template = root.template
67
+ self.children = root.children
35
68
  self.children = perform_children(environment)
36
69
  rescue Sass::SyntaxError => e
37
- e.add_backtrace_entry(@filename)
70
+ e.modify_backtrace(:filename => full_filename)
71
+ e.add_backtrace(:filename => @filename, :line => @line)
38
72
  raise e
39
73
  end
40
74
 
@@ -47,18 +81,9 @@ module Sass
47
81
  end
48
82
 
49
83
  def import
50
- begin
51
- full_filename = Sass::Files.find_file_to_import(@imported_filename, import_paths)
52
- rescue Exception => e
53
- raise SyntaxError.new(e.message, self.line)
54
- end
55
-
56
- if full_filename =~ /\.css$/
57
- @to_s = "@import url(#{full_filename});"
58
- return false
59
- end
60
-
61
- return full_filename
84
+ Sass::Files.find_file_to_import(@imported_filename, import_paths)
85
+ rescue Exception => e
86
+ raise SyntaxError.new(e.message, :line => self.line, :filename => @filename)
62
87
  end
63
88
  end
64
89
  end