unboxed-less 1.2.12 → 1.2.19

Sign up to get free protection for your applications and to get access to all the features.
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 [http://lesscss.org]
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
- #require 'profile'
43
- puts "benchmarking... "
44
- less, tree = File.read("spec/less/big.less"), nil
45
-
46
- parse = Benchmark.measure do
47
- tree = Less::Engine.new(less).parse(false)
48
- end.total.round(2)
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.12
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
- :color => true
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
@@ -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
- require 'ext'
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
@@ -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
- print "* #{is_new ? 'Created' : 'Updated'} " +
63
- "#{@destination.split('/').last}\n: " if watch?
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
@@ -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 / mixin / ruleset / comment)* {
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 ':' s expressions tail:(ws ',' ws expressions)* s (';'/ ws &'}') ws {
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 / number) ')')?)+ / attribute+ / '@media' / '@font-face'
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