lesstidy 0.0.1.pre
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/.gitignore +23 -0
- data/LICENSE +20 -0
- data/README.md +60 -0
- data/Rakefile +54 -0
- data/VERSION +1 -0
- data/bin/lesstidy +101 -0
- data/data/presets/column +9 -0
- data/data/presets/terse +5 -0
- data/data/presets/wide-column +3 -0
- data/lib/lesstidy.rb +20 -0
- data/lib/lesstidy/config.rb +33 -0
- data/lib/lesstidy/cutil.rb +73 -0
- data/lib/lesstidy/document.rb +20 -0
- data/lib/lesstidy/grammar/less.treetop +117 -0
- data/lib/lesstidy/grammar/less_syntax.rb +30 -0
- data/lib/lesstidy/nodes.rb +107 -0
- data/lib/lesstidy/preset.rb +4 -0
- data/lib/lesstidy/renderer.rb +147 -0
- data/lib/lesstidy/style.rb +32 -0
- data/lib/lesstidy/style_parser.rb +117 -0
- data/test/d.rb +31 -0
- data/test/fixtures/a.control.css +7 -0
- data/test/fixtures/a.default.css +32 -0
- data/test/fixtures/a.inspect.txt +29 -0
- data/test/fixtures/a.terse.css +7 -0
- data/test/fixtures/fail/c.css +4 -0
- data/test/fixtures/fail/search1.css +20 -0
- data/test/fixtures/fail/search2.css +9 -0
- data/test/fixtures/fail/search3.css +7 -0
- data/test/fixtures/fail/search3b.css +5 -0
- data/test/fixtures/fail/spec.css +24 -0
- data/test/fixtures/spec.control.css +24 -0
- data/test/fixtures/spec.default.css +95 -0
- data/test/fixtures/spec.inspect.txt +82 -0
- data/test/fixtures/spec.terse.css +21 -0
- data/test/fixtures/test-2.control.css +4 -0
- data/test/fixtures/test-2.default.css +33 -0
- data/test/fixtures/test-2.inspect.txt +37 -0
- data/test/fixtures/test-2.terse.css +11 -0
- data/test/fixtures/test.control.css +1 -0
- data/test/fixtures/test.default.css +30 -0
- data/test/fixtures/test.inspect.txt +35 -0
- data/test/fixtures/test.terse.css +8 -0
- data/test/helper.rb +10 -0
- data/test/test_blackbox.rb +29 -0
- data/test/test_lesstidy.rb +4 -0
- metadata +138 -0
data/.gitignore
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Rico Sta. Cruz
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
Lesstidy
|
2
|
+
========
|
3
|
+
|
4
|
+
A CSS tidying tool.
|
5
|
+
|
6
|
+
Installation (for text editors)
|
7
|
+
-------------------------------
|
8
|
+
|
9
|
+
- For VIM users, install `lesstidy-vim`.
|
10
|
+
- For Textmate users, install `lesstidy
|
11
|
+
|
12
|
+
Installation (command line)
|
13
|
+
---------------------------
|
14
|
+
|
15
|
+
To use the `lesstidy` command, type `gem install lesstidy --pre`.
|
16
|
+
|
17
|
+
Usage
|
18
|
+
-----
|
19
|
+
|
20
|
+
General usage is `lesstidy <input> [<output>]`.
|
21
|
+
|
22
|
+
Type `lesstidy --help` for tweaks.
|
23
|
+
|
24
|
+
.lesstidyopts file
|
25
|
+
------------------
|
26
|
+
|
27
|
+
Want to impose a LessTidy options standard to your project? Put a file called
|
28
|
+
`.lesstidyopts` in your project's folder. Whenever lesstidy is used without
|
29
|
+
any style options, it finds this file (relative to the current directory).
|
30
|
+
Put each option in it's own line.
|
31
|
+
|
32
|
+
Example:
|
33
|
+
|
34
|
+
# .lesstidyopts
|
35
|
+
--preset=column
|
36
|
+
--wrap-width=140
|
37
|
+
|
38
|
+
Even the text editor plugins (vim, textmate, etc) will refer to this file.
|
39
|
+
|
40
|
+
Examples
|
41
|
+
--------
|
42
|
+
|
43
|
+
lesstidy --preset=compact input.css
|
44
|
+
|
45
|
+
To do
|
46
|
+
-----
|
47
|
+
|
48
|
+
- More types
|
49
|
+
- Mixin declarations
|
50
|
+
- Var declarations
|
51
|
+
- import lines
|
52
|
+
- .lesstidyopts
|
53
|
+
|
54
|
+
Done:
|
55
|
+
|
56
|
+
- Presets
|
57
|
+
- bin/lesstidy
|
58
|
+
- blackbox testing
|
59
|
+
- Comments
|
60
|
+
- recursive wrapping
|
data/Rakefile
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "lesstidy"
|
8
|
+
gem.summary = "CSS formatting tool"
|
9
|
+
gem.description = "LessTidy takes your CSS (or LessCSS) file and rewrites it in a more readable format."
|
10
|
+
gem.email = "rico@sinefunc.com"
|
11
|
+
gem.homepage = "http://github.com/rstacruz/lesstidy"
|
12
|
+
gem.authors = ["Rico Sta. Cruz", "Sinefunc, Inc."]
|
13
|
+
gem.add_dependency "treetop"
|
14
|
+
gem.add_development_dependency "contest"
|
15
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
16
|
+
end
|
17
|
+
Jeweler::GemcutterTasks.new
|
18
|
+
rescue LoadError
|
19
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
20
|
+
end
|
21
|
+
|
22
|
+
require 'rake/testtask'
|
23
|
+
Rake::TestTask.new(:test) do |test|
|
24
|
+
test.libs << 'lib' << 'test'
|
25
|
+
test.pattern = 'test/**/test_*.rb'
|
26
|
+
test.verbose = true
|
27
|
+
end
|
28
|
+
|
29
|
+
begin
|
30
|
+
require 'rcov/rcovtask'
|
31
|
+
Rcov::RcovTask.new do |test|
|
32
|
+
test.libs << 'test'
|
33
|
+
test.pattern = 'test/**/test_*.rb'
|
34
|
+
test.verbose = true
|
35
|
+
end
|
36
|
+
rescue LoadError
|
37
|
+
task :rcov do
|
38
|
+
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
task :test => :check_dependencies
|
43
|
+
|
44
|
+
task :default => :test
|
45
|
+
|
46
|
+
require 'rake/rdoctask'
|
47
|
+
Rake::RDocTask.new do |rdoc|
|
48
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
49
|
+
|
50
|
+
rdoc.rdoc_dir = 'rdoc'
|
51
|
+
rdoc.title = "lesstidy #{version}"
|
52
|
+
rdoc.rdoc_files.include('README*')
|
53
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
54
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.1.pre
|
data/bin/lesstidy
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
lib_path = File.dirname(__FILE__) + "/../lib"
|
3
|
+
$:.unshift lib_path
|
4
|
+
|
5
|
+
require 'optparse'
|
6
|
+
require 'rubygems'
|
7
|
+
require 'lesstidy'
|
8
|
+
require 'ostruct'
|
9
|
+
|
10
|
+
options = OpenStruct.new({
|
11
|
+
:input => '-',
|
12
|
+
:output => '-',
|
13
|
+
:debug => false,
|
14
|
+
:style => Hash.new
|
15
|
+
})
|
16
|
+
|
17
|
+
opts = OptionParser.new do |o|
|
18
|
+
o.banner = "usage: lesstidy [<input>]"
|
19
|
+
o.separator ""
|
20
|
+
|
21
|
+
o.on("-d", "--debug", "show debug info instead") do
|
22
|
+
options.debug = true
|
23
|
+
end
|
24
|
+
|
25
|
+
o.on('-o', '--output FILE', 'output file') do |file|
|
26
|
+
options.output = file
|
27
|
+
end
|
28
|
+
|
29
|
+
# Help
|
30
|
+
o.on("-h", "--help", "show this message") do
|
31
|
+
puts opts
|
32
|
+
exit
|
33
|
+
end
|
34
|
+
|
35
|
+
Lesstidy::StyleParser.parse o, options.style
|
36
|
+
|
37
|
+
o.separator ""
|
38
|
+
o.separator "Available presets:"
|
39
|
+
Lesstidy::StyleParser.presets.each do |preset|
|
40
|
+
o.separator(" --preset=#{preset}")
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
begin
|
45
|
+
opts.parse!
|
46
|
+
rescue OptionParser::InvalidOption
|
47
|
+
puts opts
|
48
|
+
exit
|
49
|
+
end
|
50
|
+
|
51
|
+
if ARGV.size == 1
|
52
|
+
options.input = ARGV[0]
|
53
|
+
elsif ARGV.size > 1
|
54
|
+
puts opts
|
55
|
+
exit
|
56
|
+
end
|
57
|
+
|
58
|
+
begin
|
59
|
+
input = options.input == '-' ? STDIN : File.open(options.input, "r")
|
60
|
+
rescue Errno::ENOENT
|
61
|
+
STDERR << "Can't open input file for reading"
|
62
|
+
exit
|
63
|
+
end
|
64
|
+
|
65
|
+
begin
|
66
|
+
output = options.output == '-' ? STDOUT : File.open(options.output, "w")
|
67
|
+
rescue Errno::ENOENT
|
68
|
+
STDERR << "Can't open output file for reading"
|
69
|
+
exit
|
70
|
+
end
|
71
|
+
|
72
|
+
if options.style == {}
|
73
|
+
options.style = Lesstidy::StyleParser.defaults
|
74
|
+
end
|
75
|
+
|
76
|
+
contents = input.read
|
77
|
+
wrapper = {
|
78
|
+
:pre => /^[ \n\t]*/.match(contents).to_s,
|
79
|
+
:post => /[ \n\t]*$/.match(contents).to_s
|
80
|
+
}
|
81
|
+
|
82
|
+
begin
|
83
|
+
style = Lesstidy::Style.new(options.style)
|
84
|
+
doc = Lesstidy::Document.new(contents)
|
85
|
+
|
86
|
+
if options.debug
|
87
|
+
output << doc.inspect.strip
|
88
|
+
else
|
89
|
+
output << wrapper[:pre] << doc.to_css(style).strip << wrapper[:post] << "\n\n"
|
90
|
+
end
|
91
|
+
|
92
|
+
rescue Lesstidy::ParseError => e
|
93
|
+
output << "/* Parse error: #{e.message} */\n"
|
94
|
+
output << contents
|
95
|
+
end
|
96
|
+
|
97
|
+
|
98
|
+
input.close
|
99
|
+
output.close
|
100
|
+
|
101
|
+
|
data/data/presets/column
ADDED
data/data/presets/terse
ADDED
data/lib/lesstidy.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require "treetop"
|
2
|
+
|
3
|
+
module Lesstidy
|
4
|
+
prefix = File.join(File.dirname(__FILE__), 'lesstidy')
|
5
|
+
|
6
|
+
Treetop.load "#{prefix}/grammar/less.treetop"
|
7
|
+
|
8
|
+
autoload :Nodes, "#{prefix}/nodes"
|
9
|
+
autoload :Renderer, "#{prefix}/renderer"
|
10
|
+
autoload :Document, "#{prefix}/document"
|
11
|
+
autoload :Style, "#{prefix}/style"
|
12
|
+
autoload :CUtil, "#{prefix}/cutil"
|
13
|
+
autoload :Preset, "#{prefix}/preset"
|
14
|
+
autoload :Config, "#{prefix}/config"
|
15
|
+
autoload :StyleParser, "#{prefix}/style_parser"
|
16
|
+
|
17
|
+
Error = Class.new(::StandardError)
|
18
|
+
PresetNotFoundError = Class.new(Error)
|
19
|
+
ParseError = Class.new(Error)
|
20
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Lesstidy
|
2
|
+
class Config
|
3
|
+
class << self
|
4
|
+
NoRootError = Class.new(Exception)
|
5
|
+
|
6
|
+
def paths
|
7
|
+
r = []
|
8
|
+
begin
|
9
|
+
r << find_project_root
|
10
|
+
rescue NoRootError; end
|
11
|
+
r
|
12
|
+
end
|
13
|
+
|
14
|
+
def find_project_root(start = Dir.pwd)
|
15
|
+
check = File.expand_path(start)
|
16
|
+
ret = nil
|
17
|
+
while ret.nil?
|
18
|
+
# See if any of these files exist
|
19
|
+
['.lesstidy'].each do |config_name|
|
20
|
+
config_file = File.join(check, config_name)
|
21
|
+
ret ||= [check, config_file] if File.directory? config_file
|
22
|
+
end
|
23
|
+
|
24
|
+
# Traverse back (die if we reach the root)
|
25
|
+
old_check = check
|
26
|
+
check = File.expand_path(File.join(check, '..'))
|
27
|
+
raise NoRootError if check == old_check
|
28
|
+
end
|
29
|
+
ret
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module CUtil
|
2
|
+
class String < ::String
|
3
|
+
def prepend!(str)
|
4
|
+
self.replace(str + self)
|
5
|
+
end
|
6
|
+
|
7
|
+
def append!(str)
|
8
|
+
self << str
|
9
|
+
end
|
10
|
+
|
11
|
+
def wrap!(args = {})
|
12
|
+
temp = self.wrap(args)
|
13
|
+
self.replace(temp)
|
14
|
+
#self.replace(self.wrap(*args))
|
15
|
+
end
|
16
|
+
|
17
|
+
def lasplit(regex)
|
18
|
+
a = []
|
19
|
+
split(regex).each_slice(2) { |i| a << i.join('') }
|
20
|
+
a
|
21
|
+
end
|
22
|
+
|
23
|
+
def wrap(args = {})
|
24
|
+
# Inherit the given hashes
|
25
|
+
options = args
|
26
|
+
options[:regexp] ||= /([;,])/
|
27
|
+
options[:indent] ||= 0
|
28
|
+
options[:first_indent] ||= options[:indent]
|
29
|
+
|
30
|
+
width = options[:width]
|
31
|
+
|
32
|
+
first_indent = options[:first_indent]
|
33
|
+
indent = ''
|
34
|
+
indent = ' ' * options[:indent] if options[:indent]
|
35
|
+
|
36
|
+
ret = self.lasplit(options[:regexp]).inject(['']) do |a, chunk|
|
37
|
+
nl = a[-1] + chunk
|
38
|
+
|
39
|
+
line_width = first_indent ? (width - first_indent) : width
|
40
|
+
if nl.rstrip.size > line_width
|
41
|
+
# To wrap...
|
42
|
+
new_chunk = (indent + chunk.lstrip)
|
43
|
+
if a == ['']
|
44
|
+
a = [new_chunk]
|
45
|
+
else
|
46
|
+
a << new_chunk
|
47
|
+
end
|
48
|
+
|
49
|
+
first_indent = false
|
50
|
+
|
51
|
+
# If the new line exceeds the line width, rewrap!
|
52
|
+
if a[-1].size > width and not options[:no_rewrap]
|
53
|
+
a[-1] = String.new(a[-1]) unless a[-1].is_a? String
|
54
|
+
wrapped = [a[-1]]
|
55
|
+
wrapped = a[-1].wrap(args.merge({ :regexp => /( +)/, :no_rewrap => true, :array => true, :pad => false }))
|
56
|
+
a = a[0..-2]
|
57
|
+
a << wrapped if wrapped.size > 0
|
58
|
+
end
|
59
|
+
else
|
60
|
+
a[-1] = nl
|
61
|
+
end
|
62
|
+
a
|
63
|
+
end
|
64
|
+
|
65
|
+
# Pad the last line with spaces
|
66
|
+
ret[-1] = ret[-1] + (" " * [width - ret[-1].size, 0].max) if options[:pad]
|
67
|
+
|
68
|
+
# Stringify if needed
|
69
|
+
options[:array] ? ret : ret.join("\n")
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Lesstidy
|
2
|
+
class Document < Lesstidy::Nodes::Document
|
3
|
+
def initialize(str)
|
4
|
+
super
|
5
|
+
parser = LessParser.new
|
6
|
+
tree = parser.parse(str)
|
7
|
+
|
8
|
+
if tree
|
9
|
+
tree.build self
|
10
|
+
else
|
11
|
+
raise Lesstidy::ParseError, parser.failure_message
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.load(filename)
|
16
|
+
str = File.open(filename) { |f| f.read }
|
17
|
+
return self.new(str)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/less_syntax.rb"
|
2
|
+
|
3
|
+
# Document
|
4
|
+
# Import
|
5
|
+
# Variable
|
6
|
+
# Comment
|
7
|
+
# Ruleset
|
8
|
+
# Selector
|
9
|
+
# Mixin
|
10
|
+
# Stylerule (prop/val)
|
11
|
+
|
12
|
+
module Lesstidy
|
13
|
+
grammar Less
|
14
|
+
rule document
|
15
|
+
ws document_item* ws {
|
16
|
+
def build(env = nil)
|
17
|
+
env ||= Lesstidy::Nodes::Document.new
|
18
|
+
cascade env
|
19
|
+
end
|
20
|
+
}
|
21
|
+
end
|
22
|
+
|
23
|
+
rule document_item
|
24
|
+
block_comment {
|
25
|
+
def build(env)
|
26
|
+
env << Lesstidy::Nodes::Comment.new(comment.text_value)
|
27
|
+
end
|
28
|
+
} /
|
29
|
+
ruleset
|
30
|
+
end
|
31
|
+
|
32
|
+
rule ruleset
|
33
|
+
ws selector_group ws '{' rule_group ws '}' {
|
34
|
+
def build( env )
|
35
|
+
ruleset = Lesstidy::Nodes::Ruleset.new
|
36
|
+
env << ruleset
|
37
|
+
cascade ruleset
|
38
|
+
end
|
39
|
+
}
|
40
|
+
end
|
41
|
+
|
42
|
+
# Anything inside a `#selector { ... }`
|
43
|
+
rule rule_group
|
44
|
+
rule_group_item*
|
45
|
+
end
|
46
|
+
|
47
|
+
rule rule_group_item
|
48
|
+
ruleset /
|
49
|
+
stylerule {
|
50
|
+
def build( ruleset )
|
51
|
+
rule = Lesstidy::Nodes::Rule.new property.text_value, value.text_value
|
52
|
+
ruleset << rule
|
53
|
+
end
|
54
|
+
} /
|
55
|
+
mixin {
|
56
|
+
} /
|
57
|
+
block_comment {
|
58
|
+
def build(ruleset)
|
59
|
+
ruleset << Lesstidy::Nodes::Comment.new(comment.text_value)
|
60
|
+
end
|
61
|
+
}
|
62
|
+
end
|
63
|
+
|
64
|
+
rule selector_group
|
65
|
+
selector ( ws ',' ws selector )*
|
66
|
+
end
|
67
|
+
|
68
|
+
rule selector
|
69
|
+
( [A-Za-z0-9] / ':' / '>' / '*' / '(' / ')' / '[' / ']' / '-' / '_' / '#' / '.' / [\n\t ]+ / '&' )+ {
|
70
|
+
def build(ruleset)
|
71
|
+
sel = Lesstidy::Nodes::Selector.new self.text_value.strip
|
72
|
+
ruleset << sel
|
73
|
+
end
|
74
|
+
}
|
75
|
+
end
|
76
|
+
|
77
|
+
rule mixin
|
78
|
+
ws name:('.' [a-zA-Z0-9_\-]+) '(' params:(value_string) ')' ws ';' {
|
79
|
+
def build(ruleset)
|
80
|
+
mixin = Lesstidy::Nodes::Mixin.new name.text_value, params.text_value
|
81
|
+
ruleset << mixin
|
82
|
+
end
|
83
|
+
} /
|
84
|
+
ws name:('.' [a-zA-Z0-9_\-]*) ws ';' {
|
85
|
+
def build(ruleset)
|
86
|
+
mixin = Lesstidy::Nodes::Mixin.new name.text_value
|
87
|
+
ruleset << mixin
|
88
|
+
end
|
89
|
+
}
|
90
|
+
end
|
91
|
+
|
92
|
+
rule stylerule
|
93
|
+
ws property ws ':' ws value ws ';'
|
94
|
+
end
|
95
|
+
|
96
|
+
rule property
|
97
|
+
'-'? [A-Za-z]+ ( '-' [A-Za-z]* )*
|
98
|
+
end
|
99
|
+
|
100
|
+
rule value
|
101
|
+
(!';' . )*
|
102
|
+
end
|
103
|
+
|
104
|
+
# TODO: Add balancing of ()'s
|
105
|
+
rule value_string
|
106
|
+
[a-zA-Z0-9 \[\]\*\<\>\.,\#_\-@]*
|
107
|
+
end
|
108
|
+
|
109
|
+
rule block_comment
|
110
|
+
[\n\t ]* '/*' ' '* comment:(!(' '* '*/') . )* ' '* '*/'
|
111
|
+
end
|
112
|
+
|
113
|
+
rule ws
|
114
|
+
[\n\t ]*
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|