haml-edge 2.3.179 → 2.3.180

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 (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