unboxed-less 1.2.12 → 1.2.19
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.
- data/README.md +1 -1
- data/Rakefile +9 -18
- data/VERSION +1 -1
- data/bin/lessc +17 -7
- data/lib/less.rb +7 -6
- data/lib/less/command.rb +7 -2
- data/lib/less/engine.rb +8 -10
- data/lib/less/engine/grammar/entity.tt +16 -2
- data/lib/less/engine/grammar/less.tt +36 -27
- data/lib/less/engine/nodes/element.rb +24 -21
- data/lib/less/engine/nodes/function.rb +11 -2
- data/lib/less/engine/nodes/property.rb +41 -38
- data/lib/{ext.rb → less/ext.rb} +2 -2
- data/spec/css/css-3.css +3 -0
- data/spec/css/dash-prefix.css +12 -0
- data/spec/css/variables.css +1 -0
- data/spec/engine_spec.rb +4 -0
- data/spec/less/big.less +1261 -4807
- data/spec/less/css-3.less +6 -0
- data/spec/less/dash-prefix.less +21 -0
- data/spec/less/variables.less +5 -0
- data/unboxed-less.gemspec +5 -3
- metadata +5 -3
data/README.md
CHANGED
@@ -36,7 +36,7 @@ LESS allows you to write CSS the way (I think) it was meant to, that is: with *v
|
|
36
36
|
If you have CSS nightmares, just
|
37
37
|
$ lessc style.less
|
38
38
|
|
39
|
-
For more information, see you at
|
39
|
+
For more information, see you at <http://lesscss.org>
|
40
40
|
|
41
41
|
People without whom this wouldn't have happened a.k.a *Credits*
|
42
42
|
---------------------------------------------------------------
|
data/Rakefile
CHANGED
@@ -11,7 +11,6 @@ begin
|
|
11
11
|
s.add_dependency('treetop', '>= 1.4.2')
|
12
12
|
end
|
13
13
|
Jeweler::GemcutterTasks.new
|
14
|
-
Jeweler::RubyforgeTasks.new
|
15
14
|
rescue LoadError
|
16
15
|
puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
|
17
16
|
end
|
@@ -31,29 +30,21 @@ end
|
|
31
30
|
begin
|
32
31
|
require 'lib/less'
|
33
32
|
require 'benchmark'
|
34
|
-
|
33
|
+
|
35
34
|
task :compile do
|
36
35
|
abort "compiling isn't necessary anymore."
|
37
36
|
puts "compiling #{LESS_GRAMMAR.split('/').last}..."
|
38
37
|
File.open(LESS_PARSER, 'w') {|f| f.write Treetop::Compiler::GrammarCompiler.new.ruby_source(LESS_GRAMMAR) }
|
39
38
|
end
|
40
|
-
|
39
|
+
|
41
40
|
task :benchmark do
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
end
|
49
|
-
|
50
|
-
build = Benchmark.measure do
|
51
|
-
tree.build(Less::Node::Element.new)
|
52
|
-
end.total.round(2)
|
53
|
-
|
54
|
-
puts "parse: #{parse}s\nbuild: #{build}s"
|
55
|
-
puts "------------"
|
56
|
-
puts "total: #{parse + build}s"
|
41
|
+
less = File.read("spec/less/big.less")
|
42
|
+
result = nil
|
43
|
+
Benchmark.bmbm do |b|
|
44
|
+
b.report("parse: ") { result = Less::Engine.new(less).parse(false) }
|
45
|
+
b.report("build: ") { result = result.build(Less::Node::Element.new) }
|
46
|
+
b.report("compile:") { result.to_css }
|
47
|
+
end
|
57
48
|
end
|
58
49
|
end
|
59
50
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.2.
|
1
|
+
1.2.19
|
data/bin/lessc
CHANGED
@@ -20,19 +20,20 @@ options = {
|
|
20
20
|
:compress => false,
|
21
21
|
:debug => false,
|
22
22
|
:growl => false,
|
23
|
-
:
|
23
|
+
:timestamps => false,
|
24
|
+
:color => $stdout.tty?
|
24
25
|
}
|
25
26
|
|
26
27
|
# Get arguments
|
27
28
|
opts = OptionParser.new do |o|
|
28
29
|
o.banner = "usage: lessc source [destination] [--watch]"
|
29
30
|
o.separator ""
|
30
|
-
|
31
|
+
|
31
32
|
# Watch mode
|
32
33
|
o.on("-w", "--watch", "watch for changes") do
|
33
34
|
options[:watch] = true
|
34
35
|
end
|
35
|
-
|
36
|
+
|
36
37
|
# Growl
|
37
38
|
o.on("-g", "--growl", "growl notifications") do
|
38
39
|
if Less::GROWL && (Growl.installed? rescue false)
|
@@ -44,18 +45,27 @@ opts = OptionParser.new do |o|
|
|
44
45
|
"`sudo gem install visionmedia-growl -s http://gems.github.com`"
|
45
46
|
end
|
46
47
|
end
|
47
|
-
|
48
|
+
|
49
|
+
# Timestamps
|
50
|
+
o.on("-t", "--timestamps", "show timestamps in watch mode") do
|
51
|
+
options[:timestamps] = true
|
52
|
+
end
|
53
|
+
|
48
54
|
# No color in output
|
49
55
|
o.on("--no-color", "suppress color in output") do
|
50
56
|
options[:color] = false
|
51
57
|
end
|
52
|
-
|
58
|
+
|
59
|
+
o.on('--verbose', 'show success messages when using growl') do
|
60
|
+
options[:verbose] = true
|
61
|
+
end
|
62
|
+
|
53
63
|
# Compression needs a proper algorithm
|
54
64
|
#
|
55
65
|
# o.on("-x", "--compress", "compress css file") do
|
56
66
|
# options[:compress] = true
|
57
67
|
# end
|
58
|
-
|
68
|
+
|
59
69
|
o.separator ""
|
60
70
|
|
61
71
|
# Help
|
@@ -63,7 +73,7 @@ opts = OptionParser.new do |o|
|
|
63
73
|
puts opts
|
64
74
|
exit
|
65
75
|
end
|
66
|
-
|
76
|
+
|
67
77
|
o.on_tail("-d", "--debug", "show full error messages") do
|
68
78
|
options[:debug] = true
|
69
79
|
end
|
data/lib/less.rb
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
$:.unshift File.dirname(__FILE__)
|
2
|
-
|
3
1
|
require 'cgi'
|
4
2
|
require 'treetop'
|
5
3
|
require 'delegate'
|
@@ -8,24 +6,27 @@ LESS_ROOT = File.expand_path(File.dirname(__FILE__))
|
|
8
6
|
LESS_PARSER = File.join(LESS_ROOT, 'less', 'engine', 'parser.rb')
|
9
7
|
LESS_GRAMMAR = File.join(LESS_ROOT, 'less', 'engine', 'grammar')
|
10
8
|
|
11
|
-
|
9
|
+
$:.unshift File.dirname(__FILE__)
|
10
|
+
|
11
|
+
require 'less/ext'
|
12
12
|
require 'less/command'
|
13
13
|
require 'less/engine'
|
14
14
|
|
15
|
-
module Less
|
15
|
+
module Less
|
16
16
|
MixedUnitsError = Class.new(RuntimeError)
|
17
17
|
PathError = Class.new(RuntimeError)
|
18
18
|
VariableNameError = Class.new(NameError)
|
19
19
|
MixinNameError = Class.new(NameError)
|
20
20
|
SyntaxError = Class.new(RuntimeError)
|
21
21
|
ImportError = Class.new(RuntimeError)
|
22
|
-
|
22
|
+
CompileError = Class.new(RuntimeError)
|
23
|
+
|
23
24
|
$verbose = false
|
24
25
|
|
25
26
|
def self.version
|
26
27
|
File.read( File.join( File.dirname(__FILE__), '..', 'VERSION') ).strip
|
27
28
|
end
|
28
|
-
|
29
|
+
|
29
30
|
def self.parse less
|
30
31
|
Engine.new(less).to_css
|
31
32
|
end
|
data/lib/less/command.rb
CHANGED
@@ -36,6 +36,7 @@ module Less
|
|
36
36
|
|
37
37
|
# File has changed
|
38
38
|
if File.stat( @source ).mtime > File.stat( @destination ).mtime
|
39
|
+
print Time.now.strftime("%H:%M:%S -- ") if @options[:timestamps]
|
39
40
|
print "Change detected... "
|
40
41
|
|
41
42
|
# Loop until error is fixed
|
@@ -59,12 +60,16 @@ module Less
|
|
59
60
|
File.open( @destination, "w" ) do |file|
|
60
61
|
file.write css
|
61
62
|
end
|
62
|
-
|
63
|
-
|
63
|
+
|
64
|
+
act, file = (is_new ? 'Created' : 'Updated'), @destination.split('/').last
|
65
|
+
print "* #{act} #{file}\n: " if watch?
|
66
|
+
Growl.notify "#{act} #{file}", :title => 'LESS' if @options[:growl] && @options[:verbose]
|
64
67
|
rescue Errno::ENOENT => e
|
65
68
|
abort "#{e}"
|
66
69
|
rescue SyntaxError => e
|
67
70
|
err "#{e}\n", "Syntax"
|
71
|
+
rescue CompileError => e
|
72
|
+
err "#{e}\n", "Compile"
|
68
73
|
rescue MixedUnitsError => e
|
69
74
|
err "`#{e}` you're mixing units together! What do you expect?\n", "Mixed Units"
|
70
75
|
rescue PathError => e
|
data/lib/less/engine.rb
CHANGED
@@ -5,15 +5,13 @@ require 'engine/nodes'
|
|
5
5
|
begin
|
6
6
|
require 'engine/parser'
|
7
7
|
rescue LoadError
|
8
|
-
Treetop.load File.join(LESS_GRAMMAR, 'common.tt')
|
9
|
-
Treetop.load File.join(LESS_GRAMMAR, 'entity.tt')
|
10
8
|
Treetop.load File.join(LESS_GRAMMAR, 'less.tt')
|
11
9
|
end
|
12
10
|
|
13
11
|
module Less
|
14
12
|
class Engine
|
15
13
|
attr_reader :css, :less
|
16
|
-
|
14
|
+
|
17
15
|
def initialize obj, options = {}
|
18
16
|
@less = if obj.is_a? File
|
19
17
|
@path = File.dirname File.expand_path(obj.path)
|
@@ -23,16 +21,16 @@ module Less
|
|
23
21
|
else
|
24
22
|
raise ArgumentError, "argument must be an instance of File or String!"
|
25
23
|
end
|
26
|
-
|
24
|
+
|
27
25
|
@options = options
|
28
26
|
@parser = StyleSheetParser.new
|
29
27
|
end
|
30
|
-
|
28
|
+
|
31
29
|
def parse build = true, env = Node::Element.new
|
32
30
|
root = @parser.parse(self.prepare)
|
33
|
-
|
31
|
+
|
34
32
|
return root unless build
|
35
|
-
|
33
|
+
|
36
34
|
if root
|
37
35
|
env.file = @path
|
38
36
|
@tree = root.build env
|
@@ -43,13 +41,13 @@ module Less
|
|
43
41
|
@tree
|
44
42
|
end
|
45
43
|
alias :to_tree :parse
|
46
|
-
|
44
|
+
|
47
45
|
def to_css
|
48
46
|
@css || @css = self.parse.group.to_css
|
49
47
|
end
|
50
|
-
|
48
|
+
|
51
49
|
def prepare
|
52
50
|
@less.gsub(/\r\n/, "\n").gsub(/\t/, ' ')
|
53
51
|
end
|
54
52
|
end
|
55
|
-
end
|
53
|
+
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'grammar/common'
|
2
|
+
|
1
3
|
module Less
|
2
4
|
module StyleSheet
|
3
5
|
grammar Entity
|
@@ -5,7 +7,7 @@ module Less
|
|
5
7
|
# Entity: Any whitespace delimited token
|
6
8
|
#
|
7
9
|
rule entity
|
8
|
-
url / function / accessor / keyword / variable / literal / font
|
10
|
+
url / alpha / function / accessor / keyword / variable / literal / font
|
9
11
|
end
|
10
12
|
|
11
13
|
rule fonts
|
@@ -125,6 +127,18 @@ module Less
|
|
125
127
|
rule hex
|
126
128
|
[a-fA-F0-9]
|
127
129
|
end
|
130
|
+
|
131
|
+
#
|
132
|
+
# Special case for IE alpha filter
|
133
|
+
#
|
134
|
+
rule alpha
|
135
|
+
"alpha" '(' "opacity=" variable ')' {
|
136
|
+
def build env
|
137
|
+
var = variable.text_value
|
138
|
+
Node::Quoted.new(text_value.sub(var, env.nearest(var).evaluate.to_i.to_s))
|
139
|
+
end
|
140
|
+
}
|
141
|
+
end
|
128
142
|
end
|
129
143
|
end
|
130
|
-
end
|
144
|
+
end
|
@@ -1,10 +1,13 @@
|
|
1
|
+
require 'grammar/common'
|
2
|
+
require 'grammar/entity'
|
3
|
+
|
1
4
|
module Less
|
2
5
|
grammar StyleSheet
|
3
6
|
include Common
|
4
7
|
include Entity
|
5
|
-
|
8
|
+
|
6
9
|
rule primary
|
7
|
-
(import / declaration /
|
10
|
+
(import / declaration / ruleset / mixin / comment)* {
|
8
11
|
def build env = Less::Element.new
|
9
12
|
elements.map do |e|
|
10
13
|
e.build env if e.respond_to? :build
|
@@ -30,43 +33,41 @@ module Less
|
|
30
33
|
end
|
31
34
|
end
|
32
35
|
# Mixin Declaration
|
33
|
-
} / '.' name:[-a-zA-Z0-9_]+ ws parameters ws "{" ws primary ws "}" ws {
|
36
|
+
} / ws '.' name:[-a-zA-Z0-9_]+ ws parameters ws "{" ws primary ws "}" ws {
|
34
37
|
def build env
|
35
38
|
env << Node::Mixin::Def.new(name.text_value, parameters.build(env))
|
36
39
|
primary.build env.last
|
37
|
-
#env.last
|
38
40
|
end
|
39
41
|
}
|
40
42
|
end
|
41
|
-
|
43
|
+
|
42
44
|
rule mixin
|
43
45
|
name:('.' [-a-zA-Z0-9_]+) args:(arguments) s ';' ws {
|
44
46
|
def build env
|
45
|
-
definition = env.nearest(name.text_value, :mixin) or raise MixinNameError, name.text_value
|
47
|
+
definition = env.nearest(name.text_value, :mixin) or raise MixinNameError, "#{name.text_value}() in #{env}"
|
46
48
|
params = args.build.map {|i| Node::Expression.new i } unless args.empty?
|
47
49
|
env << Node::Mixin::Call.new(definition, params || [], env)
|
48
50
|
end
|
49
51
|
} / ws selectors ';' ws {
|
50
|
-
def build env
|
52
|
+
def build env
|
51
53
|
selectors.build(env, :mixin).each do |path|
|
52
54
|
rules = path.inject(env.root) do |current, node|
|
53
|
-
current.descend(node.selector, node) or raise MixinNameError, selectors.text_value
|
55
|
+
current.descend(node.selector, node) or raise MixinNameError, "#{selectors.text_value} in #{env}"
|
54
56
|
end.rules
|
55
57
|
env.rules += rules
|
56
|
-
#env.mix(rules)
|
57
58
|
end
|
58
59
|
end
|
59
|
-
}
|
60
|
+
}
|
60
61
|
end
|
61
|
-
|
62
|
+
|
62
63
|
rule selectors
|
63
64
|
ws selector tail:(s ',' ws selector)* ws {
|
64
|
-
def build env, method
|
65
|
+
def build env, method
|
65
66
|
all.map do |e|
|
66
67
|
e.send(method, env) if e.respond_to? method
|
67
68
|
end.compact
|
68
69
|
end
|
69
|
-
|
70
|
+
|
70
71
|
def all
|
71
72
|
[selector] + tail.elements.map {|e| e.selector }
|
72
73
|
end
|
@@ -84,7 +85,7 @@ module Less
|
|
84
85
|
node.last
|
85
86
|
end
|
86
87
|
end
|
87
|
-
|
88
|
+
|
88
89
|
def mixin env
|
89
90
|
sel.elements.map do |e|
|
90
91
|
Node::Element.new(e.element.text_value, e.select.text_value)
|
@@ -92,7 +93,7 @@ module Less
|
|
92
93
|
end
|
93
94
|
}
|
94
95
|
end
|
95
|
-
|
96
|
+
|
96
97
|
rule parameters
|
97
98
|
'(' s ')' {
|
98
99
|
def build env
|
@@ -104,7 +105,7 @@ module Less
|
|
104
105
|
e.build(env)
|
105
106
|
end
|
106
107
|
end
|
107
|
-
|
108
|
+
|
108
109
|
def all
|
109
110
|
[parameter] + tail.elements.map {|e| e.parameter }
|
110
111
|
end
|
@@ -118,7 +119,7 @@ module Less
|
|
118
119
|
end
|
119
120
|
}
|
120
121
|
end
|
121
|
-
|
122
|
+
|
122
123
|
rule import
|
123
124
|
ws "@import" S url:(string / url) medias? s ';' ws {
|
124
125
|
def build env
|
@@ -141,7 +142,7 @@ module Less
|
|
141
142
|
def build env = nil
|
142
143
|
Node::Function.new('url', value)
|
143
144
|
end
|
144
|
-
|
145
|
+
|
145
146
|
def value
|
146
147
|
Node::Quoted.new CGI.unescape(path.text_value)
|
147
148
|
end
|
@@ -157,10 +158,10 @@ module Less
|
|
157
158
|
# height: 100%;
|
158
159
|
#
|
159
160
|
rule declaration
|
160
|
-
ws name:(ident / variable) s ':'
|
161
|
+
ws name:(ident / variable) s ':' ws expressions tail:(ws ',' ws expressions)* s (';'/ ws &'}') ws {
|
161
162
|
def build env
|
162
163
|
result = all.map {|e| e.build(env) if e.respond_to? :build }.compact
|
163
|
-
env << (name.text_value =~ /^@/ ?
|
164
|
+
env << (name.text_value =~ /^@/ ?
|
164
165
|
Node::Variable : Node::Property).new(name.text_value, result, env)
|
165
166
|
end
|
166
167
|
|
@@ -190,12 +191,12 @@ module Less
|
|
190
191
|
def build env = nil
|
191
192
|
all.map {|e| e.build(env) if e.respond_to? :build }.compact
|
192
193
|
end
|
193
|
-
|
194
|
+
|
194
195
|
def all
|
195
196
|
[expression] + tail.elements.map {|f| f.expression } + [i]
|
196
197
|
end
|
197
198
|
# Catch-all rule
|
198
|
-
} / [-a-zA-Z0-9_
|
199
|
+
} / [-a-zA-Z0-9_.&*/=:,+? \[\]()#%]+ {
|
199
200
|
def build env
|
200
201
|
[Node::Anonymous.new(text_value)]
|
201
202
|
end
|
@@ -218,7 +219,7 @@ module Less
|
|
218
219
|
end
|
219
220
|
}
|
220
221
|
end
|
221
|
-
|
222
|
+
|
222
223
|
# !important
|
223
224
|
rule important
|
224
225
|
s '!' s 'important' {
|
@@ -247,7 +248,15 @@ module Less
|
|
247
248
|
# div / .class / #id / input[type="text"] / lang(fr)
|
248
249
|
#
|
249
250
|
rule element
|
250
|
-
((class / id / tag / ident) attribute* ('(' (selector /
|
251
|
+
((class / id / tag / ident) attribute* ('(' ([a-zA-Z]+ / pseudo_exp / selector / [0-9]+) ')')?)+
|
252
|
+
/ attribute+ / '@media' / '@font-face'
|
253
|
+
end
|
254
|
+
|
255
|
+
#
|
256
|
+
# 4n+1
|
257
|
+
#
|
258
|
+
rule pseudo_exp
|
259
|
+
'-'? ([0-9]+)? 'n' ([-+] [0-9]+)?
|
251
260
|
end
|
252
261
|
|
253
262
|
#
|
@@ -293,7 +302,7 @@ module Less
|
|
293
302
|
end
|
294
303
|
}
|
295
304
|
end
|
296
|
-
|
305
|
+
|
297
306
|
#
|
298
307
|
# Functions and arguments
|
299
308
|
#
|
@@ -312,7 +321,7 @@ module Less
|
|
312
321
|
e.build if e.respond_to? :build
|
313
322
|
end.compact
|
314
323
|
end
|
315
|
-
|
324
|
+
|
316
325
|
def all
|
317
326
|
[expressions] + tail.elements.map {|e| e.expressions }
|
318
327
|
end
|
@@ -323,4 +332,4 @@ module Less
|
|
323
332
|
}
|
324
333
|
end
|
325
334
|
end
|
326
|
-
end
|
335
|
+
end
|