less 1.0.16 → 1.1.3

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/Rakefile CHANGED
@@ -61,19 +61,30 @@ end
61
61
 
62
62
  begin
63
63
  require 'lib/less'
64
+ require 'benchmark'
64
65
 
65
66
  task :compile do
67
+ abort "compiling isn't necessary anymore."
66
68
  puts "compiling #{LESS_GRAMMAR.split('/').last}..."
67
69
  File.open(LESS_PARSER, 'w') {|f| f.write Treetop::Compiler::GrammarCompiler.new.ruby_source(LESS_GRAMMAR) }
68
70
  end
69
71
 
70
72
  task :benchmark do
71
- print "benchmarking... "
72
- less = File.read("spec/less/big.less")
73
- start = Time.now.to_f
74
- Less::Engine.new(less).parse
75
- total = Time.now.to_f - start
76
- puts "#{total}s"
73
+ #require 'profile'
74
+ puts "benchmarking... "
75
+ less, tree = File.read("spec/less/big.less"), nil
76
+
77
+ parse = Benchmark.measure do
78
+ tree = Less::Engine.new(less).parse(false)
79
+ end.total.round(2)
80
+
81
+ build = Benchmark.measure do
82
+ tree.build(Less::Node::Element.new)
83
+ end.total.round(2)
84
+
85
+ puts "parse: #{parse}s\nbuild: #{build}s"
86
+ puts "------------"
87
+ puts "total: #{parse + build}s"
77
88
  end
78
89
  end
79
90
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.0.16
1
+ 1.1.3
data/bin/lessc CHANGED
@@ -3,13 +3,23 @@
3
3
  $:.unshift File.dirname(__FILE__) + "/../lib"
4
4
 
5
5
  require 'optparse'
6
+ require 'rubygems'
6
7
  require 'less'
7
8
 
9
+ begin
10
+ require 'growl'
11
+ rescue LoadError
12
+ Less::GROWL = false
13
+ else
14
+ Less::GROWL = true
15
+ end
16
+
8
17
  # Argument defaults
9
18
  options = {
10
19
  :watch => false,
11
20
  :compress => false,
12
21
  :debug => false,
22
+ :growl => false
13
23
  }
14
24
 
15
25
  # Get arguments
@@ -22,6 +32,18 @@ opts = OptionParser.new do |o|
22
32
  options[:watch] = true
23
33
  end
24
34
 
35
+ # Growl
36
+ o.on("-g", "--growl", "growl notifications") do
37
+ if Less::GROWL && (Growl.installed? rescue false)
38
+ options[:growl] = true
39
+ elsif Less::GROWL
40
+ abort "Growl or 'growlnotify' wasn't found on your system."
41
+ else
42
+ abort "Growl gem not found, please install with: " +
43
+ "`sudo gem install visionmedia-growl -s http://gems.github.com`"
44
+ end
45
+ end
46
+
25
47
  # Compression needs a proper algorithm
26
48
  #
27
49
  # o.on("-x", "--compress", "compress css file") do
@@ -2,11 +2,11 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{less}
5
- s.version = "1.0.16"
5
+ s.version = "1.1.3"
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
8
  s.authors = ["cloudhead"]
9
- s.date = %q{2009-07-23}
9
+ s.date = %q{2009-07-27}
10
10
  s.default_executable = %q{lessc}
11
11
  s.description = %q{LESS is leaner CSS}
12
12
  s.email = %q{self@cloudhead.net}
@@ -23,11 +23,14 @@ Gem::Specification.new do |s|
23
23
  "VERSION",
24
24
  "bin/lessc",
25
25
  "less.gemspec",
26
+ "lib/ext.rb",
26
27
  "lib/less.rb",
27
28
  "lib/less/command.rb",
28
29
  "lib/less/engine.rb",
29
30
  "lib/less/engine/builder.rb",
30
- "lib/less/engine/less.tt",
31
+ "lib/less/engine/grammar/common.tt",
32
+ "lib/less/engine/grammar/entity.tt",
33
+ "lib/less/engine/grammar/less.tt",
31
34
  "lib/less/engine/nodes.rb",
32
35
  "lib/less/engine/nodes/element.rb",
33
36
  "lib/less/engine/nodes/entity.rb",
@@ -36,7 +39,6 @@ Gem::Specification.new do |s|
36
39
  "lib/less/engine/nodes/property.rb",
37
40
  "lib/less/engine/nodes/ruleset.rb",
38
41
  "lib/less/engine/nodes/selector.rb",
39
- "lib/less/engine/parser.rb",
40
42
  "lib/vendor/treetop/.gitignore",
41
43
  "lib/vendor/treetop/LICENSE",
42
44
  "lib/vendor/treetop/README",
@@ -0,0 +1,62 @@
1
+ module Treetop
2
+ module Runtime
3
+ class CompiledParser
4
+ def failure_message
5
+ return nil unless (tf = terminal_failures) && tf.size > 0
6
+ "on line #{failure_line}: expected " + (
7
+ tf.size == 1 ?
8
+ tf[0].expected_string :
9
+ "one of #{Less::YELLOW[tf.map {|f| f.expected_string }.uniq * ' ']}"
10
+ ) +
11
+ " got #{Less::YELLOW[input[failure_index]]}" +
12
+ " after:\n\n#{input[index...failure_index]}\n"
13
+ end
14
+ end
15
+ end
16
+ end
17
+
18
+ class Object
19
+ def verbose
20
+ $verbose = true
21
+ yield
22
+ ensure
23
+ $verbose = false
24
+ end
25
+
26
+ def tap
27
+ yield self
28
+ self
29
+ end
30
+
31
+ def log(s = '') puts "* #{s}" if $verbose end
32
+ def log!(s = '') puts "* #{s}" end
33
+ def error(s) $stderr.puts s end
34
+ def error!(s) raise Exception, s end
35
+ end
36
+
37
+ class Array
38
+ def dissolve
39
+ ary = flatten.compact
40
+ case ary.size
41
+ when 0 then []
42
+ when 1 then first
43
+ else ary
44
+ end
45
+ end
46
+
47
+ def one?
48
+ size == 1
49
+ end
50
+ end
51
+
52
+ class Class
53
+ def to_sym
54
+ self.to_s.to_sym
55
+ end
56
+ end
57
+
58
+ class Symbol
59
+ def to_proc
60
+ proc {|obj, *args| obj.send(self, *args) }
61
+ end
62
+ end
@@ -7,8 +7,9 @@ require 'delegate'
7
7
 
8
8
  LESS_ROOT = File.expand_path(File.dirname(__FILE__))
9
9
  LESS_PARSER = File.join(LESS_ROOT, 'less', 'engine', 'parser.rb')
10
- LESS_GRAMMAR = File.join(LESS_ROOT, 'less', 'engine', 'less.tt')
10
+ LESS_GRAMMAR = File.join(LESS_ROOT, 'less', 'engine', 'grammar')
11
11
 
12
+ require 'ext'
12
13
  require 'less/command'
13
14
  require 'less/engine'
14
15
 
@@ -30,66 +31,3 @@ module Less
30
31
  Engine.new(less).to_css
31
32
  end
32
33
  end
33
-
34
- module Treetop
35
- module Runtime
36
- class CompiledParser
37
- def failure_message
38
- return nil unless (tf = terminal_failures) && tf.size > 0
39
- "on line #{failure_line}: expected " + (
40
- tf.size == 1 ?
41
- tf[0].expected_string :
42
- "one of #{tf.map {|f| f.expected_string }.uniq * ' '}"
43
- ) +
44
- " got `#{input[failure_index]}`" +
45
- " after:\n\n#{input[index...failure_index]}\n"
46
- end
47
- end
48
- end
49
- end
50
-
51
- class Object
52
- def verbose
53
- $verbose = true
54
- yield
55
- ensure
56
- $verbose = false
57
- end
58
-
59
- def tap
60
- yield self
61
- self
62
- end
63
-
64
- def log(s = '') puts "* #{s}" if $verbose end
65
- def log!(s = '') puts "* #{s}" end
66
- def error(s) $stderr.puts s end
67
- def error!(s) raise Exception, s end
68
- end
69
-
70
- class Array
71
- def dissolve
72
- ary = flatten.compact
73
- case ary.size
74
- when 0 then []
75
- when 1 then first
76
- else ary
77
- end
78
- end
79
-
80
- def one?
81
- size == 1
82
- end
83
- end
84
-
85
- class Class
86
- def to_sym
87
- self.to_s.to_sym
88
- end
89
- end
90
-
91
- class Symbol
92
- def to_proc
93
- proc {|obj, *args| obj.send(self, *args) }
94
- end
95
- end
@@ -1,4 +1,12 @@
1
1
  module Less
2
+ ESC = "\033"
3
+ RESET = "#{ESC}[0m"
4
+
5
+ GREEN = lambda {|s| "#{ESC}[1;32m#{s}#{RESET}"}
6
+ RED = lambda {|s| "#{ESC}[1;31m#{s}#{RESET}"}
7
+ YELLOW = lambda {|s| "#{ESC}[1;33m#{s}#{RESET}"}
8
+ BOLD = lambda {|s| "#{ESC}[1m#{s}#{RESET}"}
9
+
2
10
  class Command
3
11
  attr_accessor :source, :destination, :options
4
12
 
@@ -59,19 +67,20 @@ module Less
59
67
  File.open( @destination, "w" ) do |file|
60
68
  file.write css
61
69
  end
62
- print "#{new ? '* [Created]' : '* [Updated]'} #{@destination.split('/').last}\n: " if watch?
70
+ print "#{GREEN['* ' + (new ? 'Created' : 'Updated')]} " +
71
+ "#{@destination.split('/').last}\n: " if watch?
63
72
  rescue Errno::ENOENT => e
64
73
  abort "#{e}"
65
74
  rescue SyntaxError => e
66
- err "#{e}\n", "Parse"
75
+ err "#{e}\n", "Syntax"
67
76
  rescue MixedUnitsError => e
68
77
  err "`#{e}` you're mixing units together! What do you expect?\n"
69
78
  rescue PathError => e
70
79
  err "`#{e}` was not found.\n", "Path"
71
80
  rescue VariableNameError => e
72
- err "`#{e}` is undefined.\n", "Name"
81
+ err "#{YELLOW[e]} is undefined.\n", "Name"
73
82
  rescue MixinNameError => e
74
- err "`#{e}` is undefined.\n", "Name"
83
+ err "#{YELLOW[e]} is undefined.\n", "Name"
75
84
  else
76
85
  true
77
86
  end
@@ -84,7 +93,14 @@ module Less
84
93
 
85
94
  def err s = '', type = ''
86
95
  type = type.strip + ' ' unless type.empty?
87
- print "! [#{type}Error] #{s}"
96
+ print "#{RED["! #{type}Error"]}: #{s}"
97
+ if @options[:growl]
98
+ growl = Growl.new
99
+ growl.title = "LESS"
100
+ growl.message = "#{type}Error in #@source!"
101
+ growl.run
102
+ false
103
+ end
88
104
  end
89
105
  end
90
- end
106
+ end
@@ -6,7 +6,9 @@ require 'engine/nodes'
6
6
  begin
7
7
  require 'engine/parser'
8
8
  rescue LoadError
9
- Treetop.load LESS_GRAMMAR
9
+ Treetop.load File.join(LESS_GRAMMAR, 'common.tt')
10
+ Treetop.load File.join(LESS_GRAMMAR, 'entity.tt')
11
+ Treetop.load File.join(LESS_GRAMMAR, 'less.tt')
10
12
  end
11
13
 
12
14
  module Less
@@ -23,12 +25,14 @@ module Less
23
25
  raise ArgumentError, "argument must be an instance of File or String!"
24
26
  end
25
27
 
26
- @parser = LessParser.new
28
+ @parser = StyleSheetParser.new
27
29
  end
28
30
 
29
- def parse env = Node::Element.new
31
+ def parse build = true, env = Node::Element.new
30
32
  root = @parser.parse(self.prepare)
31
33
 
34
+ return root unless build
35
+
32
36
  if root
33
37
  @tree = root.build env.tap {|e| e.file = @path }
34
38
  else
@@ -0,0 +1,29 @@
1
+ module Less
2
+ module StyleSheet
3
+ grammar Common
4
+ #
5
+ # Whitespace
6
+ #
7
+ rule s
8
+ [ ]*
9
+ end
10
+
11
+ rule S
12
+ [ ]+
13
+ end
14
+
15
+ rule ws
16
+ [\n ]*
17
+ end
18
+
19
+ rule WS
20
+ [\n ]+
21
+ end
22
+
23
+ # Non-space char
24
+ rule ns
25
+ ![ ;\n] .
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,139 @@
1
+ module Less
2
+ module StyleSheet
3
+ grammar Entity
4
+ #
5
+ # Entity: Any whitespace delimited token
6
+ #
7
+ rule entity
8
+ function / fonts / keyword / accessor / variable / literal / important
9
+ end
10
+
11
+ rule fonts
12
+ font family:(s ',' s font)+ {
13
+ def build
14
+ Node::FontFamily.new(all.map(&:build))
15
+ end
16
+
17
+ def all
18
+ [font] + family.elements.map {|f| f.font }
19
+ end
20
+ }
21
+ end
22
+
23
+ rule font
24
+ [a-zA-Z] [-a-zA-Z0-9]* {
25
+ def build
26
+ Node::Keyword.new(text_value)
27
+ end
28
+ } / string {
29
+ def build
30
+ Node::String.new(text_value)
31
+ end
32
+ }
33
+ end
34
+
35
+ #
36
+ # Tokens which don't need to be evaluated
37
+ #
38
+ rule literal
39
+ color / (dimension / [-a-z]+) '/' dimension {
40
+ def build
41
+ Node::Anonymous.new(text_value)
42
+ end
43
+ } / number unit {
44
+ def build
45
+ Node::Number.new(number.text_value, unit.text_value)
46
+ end
47
+ } / string {
48
+ def build
49
+ Node::String.new(text_value)
50
+ end
51
+ }
52
+ end
53
+
54
+ # !important
55
+ rule important
56
+ '!important' {
57
+ def build
58
+ Node::Keyword.new(text_value)
59
+ end
60
+ }
61
+ end
62
+
63
+ #
64
+ # `blue`, `small`, `normal` etc.
65
+ #
66
+ rule keyword
67
+ [-a-zA-Z]+ !ns {
68
+ def build
69
+ Node::Keyword.new(text_value)
70
+ end
71
+ }
72
+ end
73
+
74
+ #
75
+ # 'hello world' / "hello world"
76
+ #
77
+ rule string
78
+ "'" content:(!"'" . )* "'" {
79
+ def value
80
+ content.text_value
81
+ end
82
+ } / ["] content:(!["] . )* ["] {
83
+ def value
84
+ content.text_value
85
+ end
86
+ }
87
+ end
88
+
89
+ #
90
+ # Numbers & Units
91
+ #
92
+ rule dimension
93
+ number unit
94
+ end
95
+
96
+ rule number
97
+ '-'? [0-9]* '.' [0-9]+ / '-'? [0-9]+
98
+ end
99
+
100
+ rule unit
101
+ ('px'/'em'/'pc'/'%'/'pt'/'cm'/'mm')?
102
+ end
103
+
104
+ #
105
+ # Color
106
+ #
107
+ rule color
108
+ '#' rgb {
109
+ def build
110
+ Node::Color.new(*rgb.build)
111
+ end
112
+ } / fn:(('hsl'/'rgb') 'a'?) arguments {
113
+ def build
114
+ Node::Function.new(fn.text_value, arguments.build.flatten)
115
+ end
116
+ }
117
+ end
118
+
119
+ #
120
+ # 00ffdd / 0fd
121
+ #
122
+ rule rgb
123
+ r:(hex hex) g:(hex hex) b:(hex hex) {
124
+ def build
125
+ [r.text_value, g.text_value, b.text_value]
126
+ end
127
+ } / r:hex g:hex b:hex {
128
+ def build
129
+ [r.text_value, g.text_value, b.text_value].map {|c| c * 2 }
130
+ end
131
+ }
132
+ end
133
+
134
+ rule hex
135
+ [a-fA-F0-9]
136
+ end
137
+ end
138
+ end
139
+ end