haml-edge 2.3.179 → 2.3.180

Sign up to get free protection for your applications and to get access to all the features.
Files changed (92) hide show
  1. data/EDGE_GEM_VERSION +1 -1
  2. data/README.md +88 -149
  3. data/VERSION +1 -1
  4. data/bin/css2sass +7 -1
  5. data/bin/sass-convert +7 -0
  6. data/lib/haml/exec.rb +95 -22
  7. data/lib/haml/template.rb +1 -1
  8. data/lib/haml/util.rb +50 -0
  9. data/lib/sass.rb +1 -1
  10. data/lib/sass/css.rb +38 -210
  11. data/lib/sass/engine.rb +121 -47
  12. data/lib/sass/files.rb +28 -19
  13. data/lib/sass/plugin.rb +32 -43
  14. data/lib/sass/repl.rb +1 -1
  15. data/lib/sass/script.rb +25 -6
  16. data/lib/sass/script/bool.rb +1 -0
  17. data/lib/sass/script/color.rb +2 -2
  18. data/lib/sass/script/css_lexer.rb +22 -0
  19. data/lib/sass/script/css_parser.rb +28 -0
  20. data/lib/sass/script/funcall.rb +17 -9
  21. data/lib/sass/script/functions.rb +46 -1
  22. data/lib/sass/script/interpolation.rb +42 -0
  23. data/lib/sass/script/lexer.rb +142 -34
  24. data/lib/sass/script/literal.rb +28 -12
  25. data/lib/sass/script/node.rb +57 -1
  26. data/lib/sass/script/number.rb +18 -3
  27. data/lib/sass/script/operation.rb +44 -8
  28. data/lib/sass/script/parser.rb +149 -24
  29. data/lib/sass/script/string.rb +50 -2
  30. data/lib/sass/script/unary_operation.rb +25 -10
  31. data/lib/sass/script/variable.rb +20 -11
  32. data/lib/sass/scss.rb +14 -0
  33. data/lib/sass/scss/css_parser.rb +39 -0
  34. data/lib/sass/scss/parser.rb +683 -0
  35. data/lib/sass/scss/rx.rb +112 -0
  36. data/lib/sass/scss/script_lexer.rb +13 -0
  37. data/lib/sass/scss/script_parser.rb +25 -0
  38. data/lib/sass/tree/comment_node.rb +58 -16
  39. data/lib/sass/tree/debug_node.rb +7 -2
  40. data/lib/sass/tree/directive_node.rb +38 -34
  41. data/lib/sass/tree/for_node.rb +6 -0
  42. data/lib/sass/tree/if_node.rb +13 -0
  43. data/lib/sass/tree/import_node.rb +26 -7
  44. data/lib/sass/tree/mixin_def_node.rb +18 -0
  45. data/lib/sass/tree/mixin_node.rb +16 -1
  46. data/lib/sass/tree/node.rb +98 -27
  47. data/lib/sass/tree/prop_node.rb +97 -20
  48. data/lib/sass/tree/root_node.rb +37 -0
  49. data/lib/sass/tree/rule_node.rb +88 -60
  50. data/lib/sass/tree/variable_node.rb +9 -5
  51. data/lib/sass/tree/while_node.rb +4 -0
  52. data/test/haml/results/filters.xhtml +1 -1
  53. data/test/haml/util_test.rb +28 -0
  54. data/test/sass/conversion_test.rb +884 -0
  55. data/test/sass/css2sass_test.rb +46 -21
  56. data/test/sass/engine_test.rb +680 -160
  57. data/test/sass/functions_test.rb +27 -0
  58. data/test/sass/more_results/more_import.css +1 -1
  59. data/test/sass/more_templates/more_import.sass +3 -3
  60. data/test/sass/plugin_test.rb +28 -8
  61. data/test/sass/results/compact.css +1 -1
  62. data/test/sass/results/complex.css +5 -5
  63. data/test/sass/results/compressed.css +1 -1
  64. data/test/sass/results/expanded.css +1 -1
  65. data/test/sass/results/import.css +3 -1
  66. data/test/sass/results/mixins.css +12 -12
  67. data/test/sass/results/nested.css +1 -1
  68. data/test/sass/results/parent_ref.css +4 -4
  69. data/test/sass/results/script.css +3 -3
  70. data/test/sass/results/scss_import.css +15 -0
  71. data/test/sass/results/scss_importee.css +2 -0
  72. data/test/sass/script_conversion_test.rb +153 -0
  73. data/test/sass/script_test.rb +44 -54
  74. data/test/sass/scss/css_test.rb +811 -0
  75. data/test/sass/scss/rx_test.rb +156 -0
  76. data/test/sass/scss/scss_test.rb +871 -0
  77. data/test/sass/scss/test_helper.rb +37 -0
  78. data/test/sass/templates/alt.sass +2 -2
  79. data/test/sass/templates/bork1.sass +1 -1
  80. data/test/sass/templates/import.sass +4 -4
  81. data/test/sass/templates/importee.sass +3 -3
  82. data/test/sass/templates/line_numbers.sass +1 -1
  83. data/test/sass/templates/mixins.sass +2 -2
  84. data/test/sass/templates/nested_mixin_bork.sass +1 -1
  85. data/test/sass/templates/options.sass +1 -1
  86. data/test/sass/templates/parent_ref.sass +2 -2
  87. data/test/sass/templates/script.sass +69 -69
  88. data/test/sass/templates/scss_import.scss +10 -0
  89. data/test/sass/templates/scss_importee.scss +1 -0
  90. data/test/sass/templates/units.sass +10 -10
  91. data/test/test_helper.rb +4 -4
  92. metadata +27 -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,7 +30,7 @@ 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
39
34
  end
40
35
 
41
36
  # Returns `true` if this is a silent comment
@@ -46,6 +41,45 @@ module Sass::Tree
46
41
  style == :compressed || @silent
47
42
  end
48
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"
81
+ end
82
+
49
83
  protected
50
84
 
51
85
  # Computes the CSS for the comment.
@@ -59,15 +93,11 @@ module Sass::Tree
59
93
  # @see #invisible?
60
94
  def _to_s(tabs = 0, _ = nil)
61
95
  return if invisible?
62
- spaces = ' ' * (tabs - 1)
63
-
64
- content = (value.split("\n") + lines.map {|l| l.text})
65
- return spaces + "/* */" if content.empty?
66
- content.map! {|l| (l.empty? ? "" : " ") + l}
67
- content.first.gsub!(/^ /, '')
68
- content.last.gsub!(%r{ ?\*/ *$}, '')
96
+ spaces = (' ' * [tabs - 1 - value[/^ */].size, 0].max)
69
97
 
70
- spaces + "/* " + content.join(style == :compact ? '' : "\n#{spaces} *") + " */"
98
+ content = value.gsub(/^/, spaces)
99
+ content.gsub!(/\n +(\* *)?/, ' ') if style == :compact
100
+ content
71
101
  end
72
102
 
73
103
  # Removes this node from the tree if it's a silent comment.
@@ -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
@@ -22,49 +22,53 @@ module Sass::Tree
22
22
 
23
23
  protected
24
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
+
25
31
  # Computes the CSS for the directive.
26
32
  #
27
33
  # @param tabs [Fixnum] The level of indentation for the CSS
28
34
  # @return [String] The resulting CSS
29
35
  def _to_s(tabs)
30
- if children.empty?
31
- value + ";"
32
- else
33
- result = if style == :compressed
34
- "#{value}{"
35
- else
36
- "#{' ' * (tabs - 1)}#{value} {" + (style == :compact ? ' ' : "\n")
37
- end
38
- was_prop = false
39
- first = true
40
- children.each do |child|
41
- next if child.invisible?
42
- if style == :compact
43
- if child.is_a?(PropNode)
44
- result << "#{child.to_s(first || was_prop ? 1 : tabs + 1)} "
45
- else
46
- if was_prop
47
- result[-1] = "\n"
48
- end
49
- rendered = child.to_s(tabs + 1).dup
50
- rendered = rendered.lstrip if first
51
- result << rendered.rstrip + "\n"
52
- end
53
- was_prop = child.is_a?(PropNode)
54
- first = false
55
- elsif style == :compressed
56
- result << (was_prop ? ";#{child.to_s(1)}" : child.to_s(1))
57
- was_prop = child.is_a?(PropNode)
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)} "
58
50
  else
59
- 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"
60
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"
61
65
  end
62
- result.rstrip + if style == :compressed
63
- "}"
64
- else
65
- (style == :expanded ? "\n" : " ") + "}\n"
66
- end
67
66
  end
67
+ result.rstrip + if style == :compressed
68
+ "}"
69
+ else
70
+ (style == :expanded ? "\n" : " ") + "}\n"
71
+ end
68
72
  end
69
73
  end
70
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
  #
@@ -37,6 +37,19 @@ module Sass::Tree
37
37
 
38
38
  protected
39
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
+
40
53
  # Runs the child nodes if the conditional expression is true;
41
54
  # otherwise, tries the \{#else} nodes.
42
55
  #
@@ -4,6 +4,11 @@ module Sass
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
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
@@ -12,6 +17,25 @@ module Sass
12
17
 
13
18
  def invisible?; to_s.empty?; end
14
19
 
20
+ # Returns the resolved name of the imported file,
21
+ # as returned by \{Sass::Files#find\_file\_to\_import}.
22
+ #
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
29
+ end
30
+
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
38
+
15
39
  protected
16
40
 
17
41
  # @see Node#_cssize
@@ -29,20 +53,15 @@ module Sass
29
53
  # @param environment [Sass::Environment] The lexical environment containing
30
54
  # variable and mixin values
31
55
  def _perform(environment)
32
- full_filename = import
33
56
  return DirectiveNode.new("@import url(#{full_filename})") if full_filename =~ /\.css$/
34
-
35
- node = dup
36
- node.perform!(environment, full_filename)
37
- node
57
+ super
38
58
  end
39
59
 
40
60
  # Parses the imported file and runs the dynamic Sass for it.
41
61
  #
42
62
  # @param environment [Sass::Environment] The lexical environment containing
43
63
  # variable and mixin values
44
- # @param full_filename [String] The full path to the Sass file to import
45
- def perform!(environment, full_filename)
64
+ def perform!(environment)
46
65
  root = Sass::Files.tree_for(full_filename, @options)
47
66
  @template = root.template
48
67
  self.children = root.children