haml 1.8.0 → 2.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. data/FAQ +138 -0
  2. data/MIT-LICENSE +1 -1
  3. data/{README → README.rdoc} +66 -3
  4. data/REVISION +1 -0
  5. data/Rakefile +115 -147
  6. data/VERSION +1 -1
  7. data/bin/css2sass +0 -0
  8. data/bin/haml +2 -1
  9. data/bin/html2haml +0 -0
  10. data/bin/sass +0 -0
  11. data/init.rb +6 -1
  12. data/lib/haml/buffer.rb +122 -64
  13. data/lib/haml/engine.rb +77 -46
  14. data/lib/haml/error.rb +15 -6
  15. data/lib/haml/exec.rb +61 -10
  16. data/lib/haml/filters.rb +229 -74
  17. data/lib/haml/helpers/action_view_extensions.rb +1 -1
  18. data/lib/haml/helpers/action_view_mods.rb +109 -24
  19. data/lib/haml/helpers.rb +137 -76
  20. data/lib/haml/html.rb +8 -8
  21. data/lib/haml/precompiler.rb +280 -153
  22. data/lib/haml/template/patch.rb +10 -3
  23. data/lib/haml/template/plugin.rb +61 -10
  24. data/lib/haml/template.rb +14 -9
  25. data/lib/haml.rb +483 -214
  26. data/lib/sass/constant/color.rb +13 -13
  27. data/lib/sass/constant/literal.rb +8 -7
  28. data/lib/sass/constant/nil.rb +9 -0
  29. data/lib/sass/constant/number.rb +10 -10
  30. data/lib/sass/constant/operation.rb +4 -4
  31. data/lib/sass/constant/string.rb +3 -3
  32. data/lib/sass/constant.rb +46 -77
  33. data/lib/sass/css.rb +130 -56
  34. data/lib/sass/engine.rb +131 -43
  35. data/lib/sass/plugin/merb.rb +48 -12
  36. data/lib/sass/plugin/rails.rb +10 -4
  37. data/lib/sass/plugin.rb +33 -10
  38. data/lib/sass/tree/attr_node.rb +5 -5
  39. data/lib/sass/tree/directive_node.rb +2 -7
  40. data/lib/sass/tree/node.rb +1 -12
  41. data/lib/sass/tree/rule_node.rb +39 -31
  42. data/lib/sass/tree/value_node.rb +1 -1
  43. data/lib/sass.rb +194 -19
  44. data/rails/init.rb +1 -0
  45. data/test/benchmark.rb +67 -80
  46. data/test/haml/engine_test.rb +368 -152
  47. data/test/haml/helper_test.rb +68 -16
  48. data/test/haml/html2haml_test.rb +3 -4
  49. data/test/haml/results/content_for_layout.xhtml +1 -2
  50. data/test/haml/results/eval_suppressed.xhtml +2 -4
  51. data/test/haml/results/filters.xhtml +38 -30
  52. data/test/haml/results/helpers.xhtml +4 -8
  53. data/test/haml/results/just_stuff.xhtml +8 -7
  54. data/test/haml/results/nuke_inner_whitespace.xhtml +40 -0
  55. data/test/haml/results/nuke_outer_whitespace.xhtml +148 -0
  56. data/test/haml/results/original_engine.xhtml +3 -7
  57. data/test/haml/results/partials.xhtml +1 -0
  58. data/test/haml/results/tag_parsing.xhtml +1 -6
  59. data/test/haml/results/very_basic.xhtml +2 -4
  60. data/test/haml/results/whitespace_handling.xhtml +13 -21
  61. data/test/haml/template_test.rb +42 -57
  62. data/test/haml/templates/_partial.haml +1 -0
  63. data/test/haml/templates/filters.haml +39 -21
  64. data/test/haml/templates/helpers.haml +10 -10
  65. data/test/haml/templates/just_stuff.haml +8 -3
  66. data/test/haml/templates/nuke_inner_whitespace.haml +32 -0
  67. data/test/haml/templates/nuke_outer_whitespace.haml +144 -0
  68. data/test/haml/templates/partials.haml +1 -1
  69. data/test/haml/templates/tag_parsing.haml +0 -3
  70. data/test/haml/templates/whitespace_handling.haml +10 -10
  71. data/test/sass/engine_test.rb +97 -39
  72. data/test/sass/plugin_test.rb +4 -7
  73. data/test/sass/results/constants.css +2 -0
  74. data/test/sass/results/import.css +2 -2
  75. data/test/sass/results/mixins.css +95 -0
  76. data/test/sass/results/multiline.css +24 -0
  77. data/test/sass/templates/constants.sass +3 -0
  78. data/test/sass/templates/import.sass +4 -1
  79. data/test/sass/templates/importee.sass +4 -0
  80. data/test/sass/templates/mixins.sass +76 -0
  81. data/test/sass/templates/multiline.sass +20 -0
  82. data/test/test_helper.rb +18 -0
  83. metadata +70 -53
  84. data/lib/haml/helpers/action_view_mods.rb.rej +0 -30
  85. data/lib/haml/util.rb +0 -18
  86. data/lib/sass/constant.rb.rej +0 -42
  87. data/test/haml/runner.rb +0 -16
  88. data/test/profile.rb +0 -65
  89. data/test/sass/engine_test.rb.rej +0 -18
@@ -21,9 +21,9 @@ module Sass::Constant # :nodoc:
21
21
  'teal' => 0x008080,
22
22
  'aqua' => 0x00ffff
23
23
  }
24
-
24
+
25
25
  REGEXP = /\##{"([0-9a-fA-F]{1,2})" * 3}/
26
-
26
+
27
27
  def parse(value)
28
28
  if (value =~ REGEXP)
29
29
  @value = value.scan(REGEXP)[0].map { |num| num.ljust(2, num).to_i(16) }
@@ -32,7 +32,7 @@ module Sass::Constant # :nodoc:
32
32
  @value = (0..2).map{ |n| color >> (n << 3) & 0xff }.reverse
33
33
  end
34
34
  end
35
-
35
+
36
36
  def plus(other)
37
37
  if other.is_a? Sass::Constant::String
38
38
  Sass::Constant::String.from_value(self.to_s + other.to_s)
@@ -40,7 +40,7 @@ module Sass::Constant # :nodoc:
40
40
  piecewise(other, :+)
41
41
  end
42
42
  end
43
-
43
+
44
44
  def minus(other)
45
45
  if other.is_a? Sass::Constant::String
46
46
  raise NoMethodError.new(nil, :minus)
@@ -48,7 +48,7 @@ module Sass::Constant # :nodoc:
48
48
  piecewise(other, :-)
49
49
  end
50
50
  end
51
-
51
+
52
52
  def times(other)
53
53
  if other.is_a? Sass::Constant::String
54
54
  raise NoMethodError.new(nil, :times)
@@ -56,7 +56,7 @@ module Sass::Constant # :nodoc:
56
56
  piecewise(other, :*)
57
57
  end
58
58
  end
59
-
59
+
60
60
  def div(other)
61
61
  if other.is_a? Sass::Constant::String
62
62
  raise NoMethodError.new(nil, :div)
@@ -64,7 +64,7 @@ module Sass::Constant # :nodoc:
64
64
  piecewise(other, :/)
65
65
  end
66
66
  end
67
-
67
+
68
68
  def mod(other)
69
69
  if other.is_a? Sass::Constant::String
70
70
  raise NoMethodError.new(nil, :mod)
@@ -72,24 +72,24 @@ module Sass::Constant # :nodoc:
72
72
  piecewise(other, :%)
73
73
  end
74
74
  end
75
-
75
+
76
76
  def to_s
77
77
  red, green, blue = @value.map { |num| num.to_s(16).rjust(2, '0') }
78
78
  "##{red}#{green}#{blue}"
79
79
  end
80
-
80
+
81
81
  protected
82
-
82
+
83
83
  def self.filter_value(value)
84
84
  value.map { |num| num.to_i }
85
85
  end
86
-
86
+
87
87
  private
88
-
88
+
89
89
  def piecewise(other, operation)
90
90
  other_num = other.is_a? Number
91
91
  other_val = other.value
92
-
92
+
93
93
  rgb = []
94
94
  for i in (0...3)
95
95
  res = @value[i].send(operation, other_num ? other_val : other_val[i])
@@ -4,6 +4,7 @@ module Sass::Constant; class Literal; end; end; # :nodoc:
4
4
  require 'sass/constant/string'
5
5
  require 'sass/constant/number'
6
6
  require 'sass/constant/color'
7
+ require 'sass/constant/nil'
7
8
 
8
9
  class Sass::Constant::Literal # :nodoc:
9
10
  # The regular expression matching numbers.
@@ -13,7 +14,7 @@ class Sass::Constant::Literal # :nodoc:
13
14
 
14
15
  # The regular expression matching colors.
15
16
  COLOR = /^\# (?: [\da-f]{3} | [\da-f]{6} ) | #{html_color_matcher}/ix
16
-
17
+
17
18
  def self.parse(value)
18
19
  case value
19
20
  when NUMBER
@@ -24,11 +25,11 @@ class Sass::Constant::Literal # :nodoc:
24
25
  Sass::Constant::String.new(value)
25
26
  end
26
27
  end
27
-
28
+
28
29
  def initialize(value = nil)
29
30
  self.parse(value) if value
30
31
  end
31
-
32
+
32
33
  def perform
33
34
  self
34
35
  end
@@ -36,15 +37,15 @@ class Sass::Constant::Literal # :nodoc:
36
37
  def concat(other)
37
38
  Sass::Constant::String.from_value("#{self.to_s} #{other.to_s}")
38
39
  end
39
-
40
+
40
41
  attr_reader :value
41
-
42
+
42
43
  protected
43
-
44
+
44
45
  def self.filter_value(value)
45
46
  value
46
47
  end
47
-
48
+
48
49
  def self.from_value(value)
49
50
  instance = self.new
50
51
  instance.instance_variable_set('@value', self.filter_value(value))
@@ -0,0 +1,9 @@
1
+ require 'sass/constant/literal'
2
+
3
+ module Sass::Constant # :nodoc:
4
+ class Nil < Literal # :nodoc:
5
+ def to_s
6
+ ''
7
+ end
8
+ end
9
+ end
@@ -8,9 +8,9 @@ module Sass::Constant # :nodoc:
8
8
  def parse(value)
9
9
  first, second, unit = value.scan(Literal::NUMBER)[0]
10
10
  @value = first.empty? ? second.to_i : "#{first}#{second}".to_f
11
- @unit = unit unless unit.empty?
11
+ @unit = unit.empty? ? nil : unit
12
12
  end
13
-
13
+
14
14
  def plus(other)
15
15
  if other.is_a? Number
16
16
  operate(other, :+)
@@ -20,7 +20,7 @@ module Sass::Constant # :nodoc:
20
20
  Sass::Constant::String.from_value(self.to_s + other.to_s)
21
21
  end
22
22
  end
23
-
23
+
24
24
  def minus(other)
25
25
  if other.is_a? Number
26
26
  operate(other, :-)
@@ -28,7 +28,7 @@ module Sass::Constant # :nodoc:
28
28
  raise NoMethodError.new(nil, :minus)
29
29
  end
30
30
  end
31
-
31
+
32
32
  def times(other)
33
33
  if other.is_a? Number
34
34
  operate(other, :*)
@@ -38,7 +38,7 @@ module Sass::Constant # :nodoc:
38
38
  raise NoMethodError.new(nil, :times)
39
39
  end
40
40
  end
41
-
41
+
42
42
  def div(other)
43
43
  if other.is_a? Number
44
44
  operate(other, :/)
@@ -46,7 +46,7 @@ module Sass::Constant # :nodoc:
46
46
  raise NoMethodError.new(nil, :div)
47
47
  end
48
48
  end
49
-
49
+
50
50
  def mod(other)
51
51
  if other.is_a? Number
52
52
  operate(other, :%)
@@ -54,13 +54,13 @@ module Sass::Constant # :nodoc:
54
54
  raise NoMethodError.new(nil, :mod)
55
55
  end
56
56
  end
57
-
57
+
58
58
  def to_s
59
59
  value = @value
60
60
  value = value.to_i if value % 1 == 0.0
61
61
  "#{value}#{@unit}"
62
62
  end
63
-
63
+
64
64
  protected
65
65
 
66
66
  def self.from_value(value, unit=nil)
@@ -68,7 +68,7 @@ module Sass::Constant # :nodoc:
68
68
  instance.instance_variable_set('@unit', unit)
69
69
  instance
70
70
  end
71
-
71
+
72
72
  def operate(other, operation)
73
73
  unit = nil
74
74
  if other.unit.nil?
@@ -78,7 +78,7 @@ module Sass::Constant # :nodoc:
78
78
  elsif other.unit == self.unit
79
79
  unit = self.unit
80
80
  else
81
- raise Sass::SyntaxError.new("Incompatible units: #{self.unit} and #{other.unit}")
81
+ raise Sass::SyntaxError.new("Incompatible units: #{self.unit} and #{other.unit}.")
82
82
  end
83
83
 
84
84
  Number.from_value(self.value.send(operation, other.value), unit)
@@ -9,13 +9,13 @@ module Sass::Constant # :nodoc:
9
9
  @operand2 = operand2
10
10
  @operator = operator
11
11
  end
12
-
12
+
13
13
  def to_s
14
14
  self.perform.to_s
15
15
  end
16
-
16
+
17
17
  protected
18
-
18
+
19
19
  def perform
20
20
  literal1 = @operand1.perform
21
21
  literal2 = @operand2.perform
@@ -23,7 +23,7 @@ module Sass::Constant # :nodoc:
23
23
  literal1.send(@operator, literal2)
24
24
  rescue NoMethodError => e
25
25
  raise e unless e.name.to_s == @operator.to_s
26
- raise Sass::SyntaxError.new("Undefined operation: \"#{literal1} #{@operator} #{literal2}\"")
26
+ raise Sass::SyntaxError.new("Undefined operation: \"#{literal1} #{@operator} #{literal2}\".")
27
27
  end
28
28
  end
29
29
  end
@@ -2,11 +2,11 @@ require 'sass/constant/literal'
2
2
 
3
3
  module Sass::Constant # :nodoc:
4
4
  class String < Literal # :nodoc:
5
-
5
+
6
6
  def parse(value)
7
7
  @value = value
8
8
  end
9
-
9
+
10
10
  def plus(other)
11
11
  Sass::Constant::String.from_value(self.to_s + other.to_s)
12
12
  end
@@ -14,7 +14,7 @@ module Sass::Constant # :nodoc:
14
14
  def funcall(other)
15
15
  Sass::Constant::String.from_value("#{self.to_s}(#{other.to_s})")
16
16
  end
17
-
17
+
18
18
  def to_s
19
19
  @value
20
20
  end
data/lib/sass/constant.rb CHANGED
@@ -8,13 +8,13 @@ module Sass
8
8
 
9
9
  # Whitespace characters
10
10
  WHITESPACE = [?\ , ?\t, ?\n, ?\r]
11
-
11
+
12
12
  # The character used to escape values
13
13
  ESCAPE_CHAR = ?\\
14
14
 
15
15
  # The character used to open and close strings
16
16
  STRING_CHAR = ?"
17
-
17
+
18
18
  # A mapping of syntactically-significant characters
19
19
  # to parsed symbols
20
20
  SYMBOLS = {
@@ -31,14 +31,14 @@ module Sass
31
31
  }
32
32
 
33
33
  # The regular expression used to parse constants
34
- MATCH = /^#{Regexp.escape(CONSTANT_CHAR.chr)}([^\s#{(SYMBOLS.keys + [ ?= ]).map {|c| Regexp.escape("#{c.chr}") }.join}]+)\s*=\s*(.+)/
35
-
34
+ MATCH = /^#{Regexp.escape(CONSTANT_CHAR.chr)}([^\s#{(SYMBOLS.keys + [ ?= ]).map {|c| Regexp.escape("#{c.chr}") }.join}]+)\s*((?:\|\|)?=)\s*(.+)/
35
+
36
36
  # First-order operations
37
37
  FIRST_ORDER = [:times, :div, :mod]
38
-
38
+
39
39
  # Second-order operations
40
40
  SECOND_ORDER = [:plus, :minus]
41
-
41
+
42
42
  class << self
43
43
  def parse(value, constants, line)
44
44
  begin
@@ -46,28 +46,28 @@ module Sass
46
46
  rescue Sass::SyntaxError => e
47
47
  if e.message == "Constant arithmetic error"
48
48
  e.instance_eval do
49
- @message += ": #{value.dump}"
49
+ @message += ": #{value.dump}."
50
50
  end
51
51
  end
52
52
  e.sass_line = line
53
53
  raise e
54
54
  end
55
55
  end
56
-
56
+
57
57
  private
58
-
58
+
59
59
  def tokenize(value)
60
60
  escaped = false
61
61
  is_string = false
62
62
  beginning_of_token = true
63
63
  str = ''
64
64
  to_return = []
65
-
65
+
66
66
  reset_str = Proc.new do
67
67
  to_return << str unless str.empty?
68
68
  ''
69
69
  end
70
-
70
+
71
71
  value.each_byte do |byte|
72
72
  unless escaped
73
73
  if byte == ESCAPE_CHAR
@@ -97,7 +97,7 @@ module Sass
97
97
  str = reset_str.call
98
98
  next
99
99
  end
100
-
100
+
101
101
  symbol = SYMBOLS[byte]
102
102
 
103
103
  # Adjacent values without an operator should be concatenated
@@ -119,95 +119,64 @@ module Sass
119
119
  next
120
120
  end
121
121
 
122
- # Is this a constant?
123
- if beginning_of_token && symbol == :const
124
- beginning_of_token = true
125
- to_return << :const
126
- next
127
- end
128
-
129
122
  # Are we looking at an operator?
130
123
  if symbol && (symbol != :mod || str.empty?)
131
124
  str = reset_str.call
132
- beginning_of_token = true
125
+ beginning_of_token = symbol != :close
133
126
  to_return << symbol
134
127
  next
135
128
  end
136
129
  end
137
130
  end
138
-
131
+
139
132
  escaped = false
140
133
  beginning_of_token = false
141
134
  str << byte.chr
142
135
  end
143
-
136
+
144
137
  if is_string
145
- raise Sass::SyntaxError.new("Unterminated string: #{value.dump}")
138
+ raise Sass::SyntaxError.new("Unterminated string: #{value.dump}.")
146
139
  end
147
140
  str = reset_str.call
148
141
  to_return
149
142
  end
150
-
151
- def parenthesize(value)
152
- parenthesize_helper(0, value, value.length)[0]
153
- end
154
-
155
- def parenthesize_helper(i, value, value_len, return_after_expr = false)
143
+
144
+ def parenthesize(value, return_after_expr = false)
156
145
  to_return = []
157
- beginning = i
158
- token = value[i]
159
-
160
- while i < value_len && token != :close
161
- if token == :open
162
- to_return.push(*value[beginning...i])
163
- sub, i = parenthesize_helper(i + 1, value, value_len)
164
- beginning = i
165
- to_return << sub
166
- elsif token == :neg
167
- if value[i + 1].nil?
168
- # This is never actually reached, but we'll leave it in just in case.
169
- raise Sass::SyntaxError.new("Unterminated unary minus.")
170
- elsif value[i + 1] == :open
171
- to_return.push(*value[beginning...i])
172
- sub, i = parenthesize_helper(i + 2, value, value_len)
173
- beginning = i
174
- to_return << [:neg, sub]
175
- elsif value[i + 1].is_a?(::Symbol)
176
- to_return.push(*value[beginning...i])
177
- sub, i = parenthesize_helper(i + 1, value, value_len, true)
178
- beginning = i
179
- to_return << [:neg, sub]
180
- else
181
- to_return.push(*value[beginning...i])
182
- to_return << [:neg, value[i + 1]]
183
- beginning = i = i + 2
184
- end
185
- return to_return[0], i if return_after_expr
186
- elsif token == :const
187
- raise Sass::SyntaxError.new("Unterminated constant.") if value[i + 1].nil?
188
- raise Sass::SyntaxError.new("Invalid constant.") unless value[i + 1].is_a?(::String)
189
-
190
- to_return.push(*value[beginning...i])
191
- to_return << [:const, value[i + 1]]
192
- beginning = i = i + 2
193
- return to_return[0], i if return_after_expr
146
+
147
+ while (token = value.shift) && token != :close
148
+ case token
149
+ when :open
150
+ to_return << parenthesize(value)
151
+ when :neg
152
+ # This is never actually reached, but we'll leave it in just in case.
153
+ raise Sass::SyntaxError.new("Unterminated unary minus.") if value.first.nil?
154
+ to_return << [:neg, parenthesize(value, true)]
155
+ when :const
156
+ raise Sass::SyntaxError.new("Unterminated constant.") if value.first.nil?
157
+ raise Sass::SyntaxError.new("Invalid constant.") unless value.first.is_a?(::String)
158
+
159
+ to_return << [:const, value.first]
160
+ value.shift
194
161
  else
195
- i += 1
162
+ to_return << token
196
163
  end
197
-
198
- token = value[i]
164
+
165
+ return to_return.first if return_after_expr
199
166
  end
200
- to_return.push(*value[beginning...i])
201
- return to_return, i + 1
167
+ return to_return
202
168
  end
203
-
169
+
204
170
  #--
205
171
  # TODO: Don't pass around original value;
206
172
  # have Constant.parse automatically add it to exception.
207
173
  #++
208
174
  def operationalize(value, constants)
209
175
  value = [value] unless value.is_a?(Array)
210
- if value.length == 1
176
+ case value.length
177
+ when 0
178
+ Sass::Constant::Nil.new
179
+ when 1
211
180
  value = value[0]
212
181
  if value.is_a? Array
213
182
  operationalize(value, constants)
@@ -216,7 +185,7 @@ module Sass
216
185
  else
217
186
  Literal.parse(value)
218
187
  end
219
- elsif value.length == 2
188
+ when 2
220
189
  if value[0] == :neg
221
190
  Operation.new(Sass::Constant::Number.new('0'), operationalize(value[1], constants), :minus)
222
191
  elsif value[0] == :const
@@ -224,7 +193,7 @@ module Sass
224
193
  else
225
194
  raise SyntaxError.new("Constant arithmetic error")
226
195
  end
227
- elsif value.length == 3
196
+ when 3
228
197
  Operation.new(operationalize(value[0], constants), operationalize(value[2], constants), value[1])
229
198
  else
230
199
  if SECOND_ORDER.include?(value[1]) && FIRST_ORDER.include?(value[3])
@@ -234,10 +203,10 @@ module Sass
234
203
  end
235
204
  end
236
205
  end
237
-
206
+
238
207
  def get_constant(value, constants)
239
208
  to_return = constants[value]
240
- raise SyntaxError.new("Undefined constant: \"!#{value}\"") unless to_return
209
+ raise SyntaxError.new("Undefined constant: \"!#{value}\".") unless to_return
241
210
  to_return
242
211
  end
243
212
  end