lesstidy 0.0.1.pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. data/.gitignore +23 -0
  2. data/LICENSE +20 -0
  3. data/README.md +60 -0
  4. data/Rakefile +54 -0
  5. data/VERSION +1 -0
  6. data/bin/lesstidy +101 -0
  7. data/data/presets/column +9 -0
  8. data/data/presets/terse +5 -0
  9. data/data/presets/wide-column +3 -0
  10. data/lib/lesstidy.rb +20 -0
  11. data/lib/lesstidy/config.rb +33 -0
  12. data/lib/lesstidy/cutil.rb +73 -0
  13. data/lib/lesstidy/document.rb +20 -0
  14. data/lib/lesstidy/grammar/less.treetop +117 -0
  15. data/lib/lesstidy/grammar/less_syntax.rb +30 -0
  16. data/lib/lesstidy/nodes.rb +107 -0
  17. data/lib/lesstidy/preset.rb +4 -0
  18. data/lib/lesstidy/renderer.rb +147 -0
  19. data/lib/lesstidy/style.rb +32 -0
  20. data/lib/lesstidy/style_parser.rb +117 -0
  21. data/test/d.rb +31 -0
  22. data/test/fixtures/a.control.css +7 -0
  23. data/test/fixtures/a.default.css +32 -0
  24. data/test/fixtures/a.inspect.txt +29 -0
  25. data/test/fixtures/a.terse.css +7 -0
  26. data/test/fixtures/fail/c.css +4 -0
  27. data/test/fixtures/fail/search1.css +20 -0
  28. data/test/fixtures/fail/search2.css +9 -0
  29. data/test/fixtures/fail/search3.css +7 -0
  30. data/test/fixtures/fail/search3b.css +5 -0
  31. data/test/fixtures/fail/spec.css +24 -0
  32. data/test/fixtures/spec.control.css +24 -0
  33. data/test/fixtures/spec.default.css +95 -0
  34. data/test/fixtures/spec.inspect.txt +82 -0
  35. data/test/fixtures/spec.terse.css +21 -0
  36. data/test/fixtures/test-2.control.css +4 -0
  37. data/test/fixtures/test-2.default.css +33 -0
  38. data/test/fixtures/test-2.inspect.txt +37 -0
  39. data/test/fixtures/test-2.terse.css +11 -0
  40. data/test/fixtures/test.control.css +1 -0
  41. data/test/fixtures/test.default.css +30 -0
  42. data/test/fixtures/test.inspect.txt +35 -0
  43. data/test/fixtures/test.terse.css +8 -0
  44. data/test/helper.rb +10 -0
  45. data/test/test_blackbox.rb +29 -0
  46. data/test/test_lesstidy.rb +4 -0
  47. metadata +138 -0
data/.gitignore ADDED
@@ -0,0 +1,23 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
22
+ doc
23
+ .rvmrc
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
+
@@ -0,0 +1,9 @@
1
+ --selector-width=19
2
+ --subrule-indent=2
3
+ --open-brace-spaces=s|s
4
+ --close-brace-spaces=|n
5
+ --semicolon-spaces=|s
6
+ --colon-spaces=|s
7
+ --no-selector-wrap
8
+ --wrap-width=120
9
+ --property-indent=3
@@ -0,0 +1,5 @@
1
+ --open-brace-spaces=s|s
2
+ --close-brace-spaces=|n
3
+ --no-wrap
4
+ --no-selector-wrap
5
+ --semicolon-spaces=|s
@@ -0,0 +1,3 @@
1
+ --preset=column
2
+ --selector-width=40
3
+ --wrap-width=400
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