railsdog-less 1.2.17

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 (75) hide show
  1. data/.gitignore +4 -0
  2. data/CHANGELOG +62 -0
  3. data/LICENSE +179 -0
  4. data/README.md +48 -0
  5. data/Rakefile +61 -0
  6. data/VERSION +1 -0
  7. data/bin/lessc +102 -0
  8. data/lib/less.rb +44 -0
  9. data/lib/less/command.rb +110 -0
  10. data/lib/less/engine.rb +54 -0
  11. data/lib/less/engine/grammar/common.tt +29 -0
  12. data/lib/less/engine/grammar/entity.tt +142 -0
  13. data/lib/less/engine/grammar/less.tt +338 -0
  14. data/lib/less/engine/nodes.rb +9 -0
  15. data/lib/less/engine/nodes/element.rb +281 -0
  16. data/lib/less/engine/nodes/entity.rb +79 -0
  17. data/lib/less/engine/nodes/function.rb +84 -0
  18. data/lib/less/engine/nodes/literal.rb +171 -0
  19. data/lib/less/engine/nodes/property.rb +229 -0
  20. data/lib/less/engine/nodes/ruleset.rb +12 -0
  21. data/lib/less/engine/nodes/selector.rb +44 -0
  22. data/lib/less/ext.rb +60 -0
  23. data/railsdog-less.gemspec +125 -0
  24. data/spec/command_spec.rb +102 -0
  25. data/spec/css/accessors.css +18 -0
  26. data/spec/css/big.css +3768 -0
  27. data/spec/css/colors.css +14 -0
  28. data/spec/css/comments.css +9 -0
  29. data/spec/css/css-3.css +20 -0
  30. data/spec/css/css.css +50 -0
  31. data/spec/css/functions.css +6 -0
  32. data/spec/css/import.css +12 -0
  33. data/spec/css/lazy-eval.css +1 -0
  34. data/spec/css/mixins-args.css +32 -0
  35. data/spec/css/mixins.css +28 -0
  36. data/spec/css/operations.css +28 -0
  37. data/spec/css/parens.css +20 -0
  38. data/spec/css/rulesets.css +17 -0
  39. data/spec/css/scope.css +11 -0
  40. data/spec/css/selectors.css +13 -0
  41. data/spec/css/strings.css +12 -0
  42. data/spec/css/variables.css +8 -0
  43. data/spec/css/whitespace.css +7 -0
  44. data/spec/engine_spec.rb +126 -0
  45. data/spec/less/accessors.less +20 -0
  46. data/spec/less/big.less +4810 -0
  47. data/spec/less/colors.less +35 -0
  48. data/spec/less/comments.less +46 -0
  49. data/spec/less/css-3.less +51 -0
  50. data/spec/less/css.less +104 -0
  51. data/spec/less/exceptions/mixed-units-error.less +3 -0
  52. data/spec/less/exceptions/name-error-1.0.less +3 -0
  53. data/spec/less/exceptions/syntax-error-1.0.less +3 -0
  54. data/spec/less/functions.less +6 -0
  55. data/spec/less/hidden.less +25 -0
  56. data/spec/less/import.less +8 -0
  57. data/spec/less/import/import-test-a.less +2 -0
  58. data/spec/less/import/import-test-b.less +8 -0
  59. data/spec/less/import/import-test-c.less +7 -0
  60. data/spec/less/import/import-test-d.css +1 -0
  61. data/spec/less/lazy-eval.less +6 -0
  62. data/spec/less/literal-css.less +11 -0
  63. data/spec/less/mixins-args.less +59 -0
  64. data/spec/less/mixins.less +43 -0
  65. data/spec/less/operations.less +39 -0
  66. data/spec/less/parens.less +26 -0
  67. data/spec/less/rulesets.less +30 -0
  68. data/spec/less/scope.less +32 -0
  69. data/spec/less/selectors.less +24 -0
  70. data/spec/less/strings.less +14 -0
  71. data/spec/less/variables.less +29 -0
  72. data/spec/less/whitespace.less +34 -0
  73. data/spec/spec.css +50 -0
  74. data/spec/spec_helper.rb +8 -0
  75. metadata +150 -0
@@ -0,0 +1,9 @@
1
+ $:.unshift File.dirname(__FILE__)
2
+
3
+ require 'engine/nodes/entity'
4
+ require 'engine/nodes/function'
5
+ require 'engine/nodes/ruleset'
6
+ require 'engine/nodes/element'
7
+ require 'engine/nodes/property'
8
+ require 'engine/nodes/literal'
9
+ require 'engine/nodes/selector'
@@ -0,0 +1,281 @@
1
+ module Less
2
+ module Node
3
+ #
4
+ # Element
5
+ #
6
+ # div {...}
7
+ #
8
+ # TODO: Look into making @rules its own hash-like class
9
+ # TODO: Look into whether selector should be child by default
10
+ #
11
+ class Element
12
+ include Enumerable
13
+ include Entity
14
+
15
+ attr_accessor :rules, :selector, :file,
16
+ :set, :imported, :name
17
+
18
+ def initialize name = "", selector = ''
19
+ @name = name
20
+ @set, @imported = [], []
21
+ @rules = [] # Holds all the nodes under this element's hierarchy
22
+ @selector = Selector[selector.strip].new # descendant | child | adjacent
23
+ end
24
+
25
+ def class?; name =~ /^\./ end
26
+ def id?; name =~ /^#/ end
27
+ def universal?; name == '*' end
28
+
29
+ def tag?
30
+ not id? || class? || universal?
31
+ end
32
+
33
+ # Top-most node?
34
+ def root?
35
+ parent.nil?
36
+ end
37
+
38
+ def empty?
39
+ @rules.empty?
40
+ end
41
+
42
+ def leaf?
43
+ elements.empty?
44
+ end
45
+
46
+ # Group similar rulesets together
47
+ # This is horrible, horrible code,
48
+ # but it'll have to do until I find
49
+ # a proper way to do it.
50
+ def group
51
+ elements = self.elements.reject {|e| e.is_a?(Mixin::Def) }
52
+ return self unless elements.size > 1
53
+
54
+ stack, result, matched = elements.dup, [], false
55
+
56
+ elements.each do
57
+ e = stack.first
58
+ result << e unless matched
59
+
60
+ matched = stack[1..-1].each do |ee|
61
+ if e.equiv? ee and e.elements.size == 0
62
+ self[e].set << ee
63
+ stack.shift
64
+ else
65
+ stack.shift
66
+ break false
67
+ end
68
+ end if stack.size > 1
69
+ end
70
+ @rules -= (elements - result)
71
+ self
72
+ end
73
+
74
+ #
75
+ # Accessors for the different nodes in @rules
76
+ #
77
+ def identifiers; @rules.select {|r| r.kind_of? Property } end
78
+ def properties; @rules.select {|r| r.instance_of? Property } end
79
+ def variables; @rules.select {|r| r.instance_of? Variable } end
80
+ def elements; @rules.select {|r| r.kind_of? Element } end
81
+ def mixins; @rules.select {|r| r.instance_of? Mixin::Call} end
82
+ def parameters; [] end
83
+
84
+ # Select a child element
85
+ # TODO: Implement full selector syntax & merge with descend()
86
+ def [] key
87
+ case key
88
+ when Entity
89
+ @rules.find {|i| i.eql? key }
90
+ when String
91
+ @rules.find {|i| i.to_s == key }
92
+ else raise ArgumentError
93
+ end
94
+ end
95
+
96
+ def == other
97
+ name == other.name
98
+ end
99
+
100
+ def eql? other
101
+ super and self.equiv? other
102
+ end
103
+
104
+ def equiv? other
105
+ rules.size == other.rules.size and
106
+ !rules.zip(other.rules).map do |a, b|
107
+ a.to_css == b.to_css
108
+ end.include?(false)
109
+ end
110
+
111
+ # Same as above, except with a specific selector
112
+ # TODO: clean this up or implement it differently
113
+ def descend selector, element
114
+ if selector.is_a? Child
115
+ s = self[element.name].selector
116
+ self[element.name] if s.is_a? Child or s.is_a? Descendant
117
+ elsif selector.is_a? Descendant
118
+ self[element.name]
119
+ else
120
+ self[element.name] if self[element.name].selector.class == selector.class
121
+ end
122
+ end
123
+
124
+ #
125
+ # Add an arbitrary node to this element
126
+ #
127
+ def << obj
128
+ if obj.kind_of? Node::Entity
129
+ obj.parent = self
130
+ @rules << obj
131
+ else
132
+ raise ArgumentError, "argument can't be a #{obj.class}"
133
+ end
134
+ end
135
+
136
+ def last; elements.last end
137
+ def first; elements.first end
138
+ def to_s; root?? '*' : name end
139
+
140
+ #
141
+ # Entry point for the css conversion
142
+ #
143
+ def to_css path = [], env = nil
144
+ path << @selector.to_css << name unless root?
145
+
146
+ # puts "to_css env: #{env ? env.variables : "nil"}"
147
+ content = @rules.select do |r|
148
+ r.is_a?(Mixin::Call) || r.instance_of?(Property)
149
+ end.map do |i|
150
+ ' ' * 2 + i.to_css(env)
151
+ end.compact.reject(&:empty?) * "\n"
152
+
153
+ content = content.include?("\n") ? "\n#{content}\n" : " #{content.strip} "
154
+
155
+ ruleset = if is_a?(Mixin::Def)
156
+ content.strip
157
+ else
158
+ !content.strip.empty??
159
+ "#{[path.reject(&:empty?).join.strip,
160
+ *@set.map(&:name)].uniq * ', '} {#{content}}\n" : ""
161
+ end
162
+
163
+ ruleset + elements.reject {|e| e.is_a?(Mixin::Def) }.map do |i|
164
+ i.to_css(path, env)
165
+ end.reject(&:empty?).join
166
+
167
+ ensure
168
+ 2.times { path.pop }
169
+ end
170
+
171
+ #
172
+ # Find the nearest node in the hierarchy or raise a NameError
173
+ #
174
+ def nearest ident, type = nil
175
+ ary = type || ident =~ /^[.#]/ ? :elements : :variables
176
+ path.map do |node|
177
+ node.send(ary).find {|i| i.to_s == ident }
178
+ end.compact.first.tap do |result|
179
+ raise VariableNameError, ("#{ident} in #{self.to_s}") if result.nil? && type != :mixin
180
+ end
181
+ end
182
+
183
+ #
184
+ # Traverse the whole tree, returning each leaf (recursive)
185
+ #
186
+ def each path = [], &blk
187
+ elements.each do |element|
188
+ path << element
189
+ yield element, path if element.leaf?
190
+ element.each path, &blk
191
+ path.pop
192
+ end
193
+ self
194
+ end
195
+
196
+ def inspect depth = 0
197
+ indent = lambda {|i| '. ' * i }
198
+ put = lambda {|ary| ary.map {|i| indent[ depth + 1 ] + i.inspect } * "\n"}
199
+
200
+ (root?? "\n" : "") + [
201
+ indent[ depth ] + self.to_s,
202
+ put[ variables ],
203
+ put[ properties ],
204
+ put[ mixins ],
205
+ elements.map {|i| i.inspect( depth + 1 ) } * "\n"
206
+ ].reject(&:empty?).join("\n") + "\n" + indent[ depth ]
207
+ end
208
+ end
209
+
210
+ module Mixin
211
+ class Call
212
+ include Entity
213
+
214
+ def initialize mixin, params, parent
215
+ # puts "Initializing a Mixin::Call #{mixin}"
216
+ @mixin = mixin
217
+ self.parent = parent
218
+ @params = params.each do |e|
219
+ e.parent = self.parent
220
+ end
221
+ end
222
+
223
+ def to_css env = nil
224
+ # puts "\n\n"
225
+ # puts "call .#{@mixin.name} #{@params} <#{@params.class}>"
226
+ @mixin.call(@params.map {|e| e.evaluate(env) })
227
+ end
228
+
229
+ def inspect
230
+ "#{@mixin.to_s} (#{@params})"
231
+ end
232
+ end
233
+
234
+ class Def < Element
235
+ attr_accessor :params
236
+
237
+ def initialize name, params = []
238
+ super name
239
+ @params = params.each do |param|
240
+ param.parent = self
241
+ end
242
+ end
243
+
244
+ def call args = []
245
+ if e = @rules.find {|r| r.is_a? Element }
246
+ raise CompileError, "#{e} in #{self.inspect}: can't nest selectors inside a dynamic mixin."
247
+ end
248
+
249
+ env = Element.new
250
+
251
+ @params.zip(args).each do |param, val|
252
+ env << (val ? Variable.new(param.to_s, Expression.new([val])) : param)
253
+ end
254
+
255
+ #b ? Node::Variable.new(a.to_s, Expression.new([b])) : a
256
+
257
+ # puts "#{self.inspect}"
258
+ # puts "env: #{env.variables} root?: #{env.root?}"
259
+ # puts "\nTOCSS"
260
+ to_css([], env)
261
+ end
262
+
263
+ def variables
264
+ params + super
265
+ end
266
+
267
+ def to_s
268
+ '.' + name
269
+ end
270
+
271
+ def inspect
272
+ ".#{name}()"
273
+ end
274
+
275
+ def to_css path, env
276
+ super(path, env)
277
+ end
278
+ end
279
+ end
280
+ end
281
+ end
@@ -0,0 +1,79 @@
1
+ module Less
2
+ #
3
+ # Node::Entity
4
+ #
5
+ # Everything in the tree is an Entity
6
+ #
7
+ # Mixin/Class hierarchy
8
+ #
9
+ # - Entity
10
+ # - Element
11
+ # - Entity
12
+ # - Function
13
+ # - Keyword
14
+ # - Literal
15
+ # - Color
16
+ # - Number
17
+ # - String
18
+ # - FontFamily
19
+ # - Property
20
+ # - Variable
21
+ #
22
+ # TODO: Use delegate class -> @rules
23
+ #
24
+ module Node
25
+ module Entity
26
+ attr_accessor :parent
27
+
28
+ def initialize value, parent = nil
29
+ super value
30
+ @parent = parent
31
+ end
32
+
33
+ #
34
+ # Returns the path from any given node, to the root
35
+ #
36
+ # ex: ['color', 'p', '#header', 'body', '*']
37
+ #
38
+ def path node = self
39
+ path = []
40
+ while node do
41
+ path << node
42
+ node = node.parent
43
+ end
44
+ path
45
+ end
46
+
47
+ def root
48
+ path.last
49
+ end
50
+
51
+ def inspect; to_s end
52
+ def to_css; to_s end
53
+ def to_s; super end
54
+ end
55
+
56
+ #
57
+ # An anonymous node, for all the 'other' stuff
58
+ # which doesn't need any specific functionality.
59
+ #
60
+ class Anonymous < String
61
+ include Entity
62
+ end
63
+
64
+ #
65
+ # + * - /
66
+ #
67
+ class Operator < String
68
+ def to_ruby
69
+ self
70
+ end
71
+ end
72
+
73
+ class Paren < String
74
+ def to_ruby
75
+ self
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,84 @@
1
+ module Less
2
+ #
3
+ # Functions useable from within the style-sheet go here
4
+ #
5
+ module Functions
6
+ def rgb *rgb
7
+ rgba rgb, 1.0
8
+ end
9
+
10
+ def hsl *args
11
+ hsla *[args, 1.0].flatten
12
+ end
13
+
14
+ #
15
+ # RGBA to Node::Color
16
+ #
17
+ def rgba *rgba
18
+ Node::Color.new *rgba.flatten
19
+ end
20
+
21
+ #
22
+ # HSLA to RGBA
23
+ #
24
+ def hsla h, s, l, a = 1.0
25
+ m2 = ( l <= 0.5 ) ? l * ( s + 1 ) : l + s - l * s
26
+ m1 = l * 2 - m2;
27
+
28
+ hue = lambda do |h|
29
+ h = h < 0 ? h + 1 : (h > 1 ? h - 1 : h)
30
+ if h * 6 < 1 then m1 + (m2 - m1) * h * 6
31
+ elsif h * 2 < 1 then m2
32
+ elsif h * 3 < 2 then m1 + (m2 - m1) * (2/3 - h) * 6
33
+ else m1
34
+ end
35
+ end
36
+
37
+ rgba hue[ h + 1/3 ], hue[ h ], hue[ h - 1/3 ], a
38
+ end
39
+
40
+ def self.available
41
+ self.instance_methods.map(&:to_sym)
42
+ end
43
+ end
44
+
45
+ module Node
46
+ #
47
+ # A CSS function, like rgb() or url()
48
+ #
49
+ # it calls functions from the Functions module
50
+ #
51
+ class Function < String
52
+ include Entity
53
+ include Functions
54
+
55
+ def initialize name, args
56
+ @args = if args.is_a? Array
57
+ args.map {|e| e.is_a?(Expression) ? e : Expression.new(e, self)}
58
+ else
59
+ [args]
60
+ end
61
+
62
+ super name
63
+ end
64
+
65
+ def to_css env = nil
66
+ self.evaluate(env).to_css
67
+ end
68
+
69
+ #
70
+ # Call the function
71
+ #
72
+ # If the function isn't found, we just print it out,
73
+ # this is the case for url(), for example,
74
+ #
75
+ def evaluate context = nil
76
+ if Functions.available.include? self.to_sym
77
+ send to_sym, *@args
78
+ else
79
+ Node::Anonymous.new("#{to_sym}(#{@args.map(&:to_css) * ', '})")
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end