eden 0.1.1

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.
Files changed (38) hide show
  1. data/CHANGELOG +4 -0
  2. data/LICENSE +20 -0
  3. data/README.md +48 -0
  4. data/Rakefile +10 -0
  5. data/bin/eden +132 -0
  6. data/lib/eden.rb +10 -0
  7. data/lib/eden/defaults.rb +26 -0
  8. data/lib/eden/formatter.rb +25 -0
  9. data/lib/eden/formatters/block_formatter.rb +45 -0
  10. data/lib/eden/formatters/indenter.rb +91 -0
  11. data/lib/eden/formatters/white_space_cleaner.rb +14 -0
  12. data/lib/eden/line.rb +65 -0
  13. data/lib/eden/source_file.rb +32 -0
  14. data/lib/eden/token.rb +62 -0
  15. data/lib/eden/tokenizer.rb +259 -0
  16. data/lib/eden/tokenizers/basic_tokenizer.rb +167 -0
  17. data/lib/eden/tokenizers/delimited_literal_tokenizer.rb +38 -0
  18. data/lib/eden/tokenizers/number_tokenizer.rb +68 -0
  19. data/lib/eden/tokenizers/operator_tokenizer.rb +211 -0
  20. data/lib/eden/tokenizers/regex_tokenizer.rb +37 -0
  21. data/lib/eden/tokenizers/string_tokenizer.rb +149 -0
  22. data/test/array_literal_tokenization_test.rb +43 -0
  23. data/test/basic_tokenization_test.rb +29 -0
  24. data/test/block_formatter_test.rb +47 -0
  25. data/test/class_var_token_test.rb +21 -0
  26. data/test/identifier_token_test.rb +140 -0
  27. data/test/indenter_test.rb +314 -0
  28. data/test/instance_var_token_test.rb +48 -0
  29. data/test/number_tokenization_test.rb +83 -0
  30. data/test/operator_tokenization_test.rb +180 -0
  31. data/test/regex_tokenization_test.rb +68 -0
  32. data/test/single_character_tokenization_test.rb +87 -0
  33. data/test/string_tokenization_test.rb +291 -0
  34. data/test/symbol_tokenization_test.rb +64 -0
  35. data/test/test_helper.rb +13 -0
  36. data/test/white_space_cleaner_test.rb +35 -0
  37. data/test/whitespace_token_test.rb +63 -0
  38. metadata +108 -0
data/CHANGELOG ADDED
@@ -0,0 +1,4 @@
1
+ 0.1.1
2
+ - Fixed banner message displayed when no params passed to eden.
3
+
4
+ 0.1.0 -- Initial Release
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 Jason Langenauer
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,48 @@
1
+ # Eden
2
+
3
+ Eden is a source-code formatter for Ruby. It's designed around a robust lexical analyzer able to handle
4
+ most of the dark corners of Ruby syntax, and able to be easily modified and extended.
5
+
6
+ ## Installation
7
+
8
+ Eden is available as a gem. To install it, simply run:
9
+
10
+ gem install eden
11
+
12
+ ## Configuration
13
+
14
+ Eden's formatter modules are able to be configured using a Ruby configuration DSL. Eden loads defaults when it
15
+ starts, but if you wish to override options on a project-specific basis, you can do so by creating
16
+ a file `config/eden.rb`. For example, if you wish to use tabs instead of spaces to indent a project's code, put
17
+ the following in `config/eden.rb`:
18
+
19
+ Indenter.configure do |i|
20
+ i.indent_characters_per_step 1
21
+ i.indent_character "\t"
22
+ end
23
+
24
+ Refer to `lib/eden/defaults.rb` for full configuration options.
25
+
26
+ ## Using Eden
27
+
28
+ Eden is designed to be used with a source-control system, so when it formats Ruby source files, it writes changes
29
+ to the file in place. The basic format for running Eden is:
30
+
31
+ eden command filenames
32
+
33
+ Eden understands 2 commands:
34
+
35
+ * `colorize` - Displays a ANSI colorized version of the source. This is mainly used for debugging the lexer.
36
+ * `rewrite` - Rewrites the source files in place to be correctly formatted
37
+
38
+ Examples:
39
+
40
+ eden rewrite source_file.rb
41
+
42
+ will rewrite source_file.rb
43
+
44
+ eden rewrite ./**/*.rb
45
+
46
+ will rewrite all the .rb files in the current directory and any subdirectories.
47
+
48
+ Eden was created by Jason Langenauer (jason@jasonlangenauer.com or @jasonlangenauer on Twitter). Any feedback/comments/suggestions gratefully received.
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'rake/testtask'
4
+
5
+ Rake::TestTask.new do |t|
6
+ t.libs << 'lib'
7
+ test_files = FileList['test/**/*_test.rb']
8
+ t.test_files = test_files
9
+ t.verbose = true
10
+ end
data/bin/eden ADDED
@@ -0,0 +1,132 @@
1
+ #!/usr/bin/env ruby
2
+ $:.unshift(File.dirname(__FILE__) + "/../lib")
3
+
4
+ require 'optparse'
5
+ require 'eden'
6
+
7
+ # Automatically load all the formatters
8
+ formatters = []
9
+
10
+ # Taken from ActiveSupport
11
+ def camelize(lower_case_and_underscored_word, first_letter_in_uppercase = true)
12
+ if first_letter_in_uppercase
13
+ lower_case_and_underscored_word.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
14
+ else
15
+ lower_case_and_underscored_word.first.downcase + camelize(lower_case_and_underscored_word)[1..-1]
16
+ end
17
+ end
18
+
19
+ # Load all the formatters
20
+ Dir.glob([File.dirname(__FILE__) + "/../lib/eden/formatters/*.rb"] ) do |file|
21
+ require "#{file}"
22
+ const_name = camelize( File.basename(file, ".rb"))
23
+ formatters << Object.const_get( const_name )
24
+ end
25
+
26
+ # Setup defaults
27
+ require 'eden/defaults'
28
+
29
+ # Load formatting customizations
30
+ if File.exists?("./config/eden.rb")
31
+ require './config/eden.rb'
32
+ end
33
+
34
+ # Displays a source file on STDOUT using ANSI escape codes for
35
+ # syntax highlighting
36
+ def colorize( sf )
37
+ sf.lines.each do |line|
38
+ print "[#{line.line_no}] "
39
+ line.tokens.flatten.each do |t|
40
+ case t.type
41
+ when :regex
42
+ print "\033[32m"
43
+ print t.content
44
+ print "\033[0m"
45
+ when :double_q_string, :single_q_string
46
+ print "\033[0;36m" + t.content + "\033[0m"
47
+ when :symbol
48
+ print "\033[31m" + t.content + "\033[0m"
49
+ when :instancevar
50
+ print "\033[1;34m" + t.content + "\033[0m"
51
+ when :comment
52
+ print "\033[1;30m" + t.content + "\033[0m"
53
+ else
54
+ if t.keyword?
55
+ print "\033[33m" + t.content + "\033[0m"
56
+ else
57
+ print t.content
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+
64
+ def debug( source_file )
65
+ source_file.lines.each do |l|
66
+ puts l.tokens.inspect
67
+ end
68
+ end
69
+
70
+
71
+ # Load default options
72
+ options = {}
73
+
74
+ # Command-line options parser
75
+ opts = OptionParser.new do |opts|
76
+ options[:recurse] = false
77
+ opts.banner = <<BANNER
78
+ usage: eden command [options] source_files
79
+ --
80
+ Valid command options:
81
+ colorize - display source file with syntax highlighting
82
+ rewrite - rewrite the source file in place with adjusted formatting
83
+
84
+ Options:
85
+ BANNER
86
+ opts.on('-R', '--recursive', "Recursively search subdirectories") do
87
+ options[:recurse] = true
88
+ end
89
+ end
90
+
91
+ source_files = []
92
+
93
+ # Parse the command line, and find out what we want to do
94
+ opts.parse!
95
+ cmd = ARGV.shift
96
+
97
+ unless cmd
98
+ puts opts
99
+ exit
100
+ end
101
+
102
+ cmd = cmd.downcase.to_sym
103
+
104
+ unless [:colorize, :analyse, :rewrite, :debug].include?(cmd)
105
+ puts opts
106
+ exit
107
+ end
108
+
109
+ sf = nil
110
+
111
+ begin
112
+ ARGV.each do |f|
113
+ sf = Eden::SourceFile.new( f )
114
+ sf.load!
115
+ sf.tokenize!
116
+ formatters.each do |formatter|
117
+ formatter.format( sf )
118
+ end
119
+ source_files << sf
120
+ case cmd
121
+ when :colorize then colorize( sf )
122
+ when :rewrite then sf.rewrite!
123
+ when :analyse then analyse( sf )
124
+ when :debug then debug( sf )
125
+ end
126
+ end
127
+ rescue => e
128
+ puts e
129
+ sf.lines.each { |l| puts l.joined_tokens }
130
+ end
131
+
132
+
data/lib/eden.rb ADDED
@@ -0,0 +1,10 @@
1
+ require 'eden/token'
2
+ require 'eden/tokenizer'
3
+ require 'eden/source_file'
4
+ require 'eden/line'
5
+
6
+ require 'eden/formatter'
7
+
8
+
9
+
10
+
@@ -0,0 +1,26 @@
1
+ WhiteSpaceCleaner.configure do |w|
2
+ # Remove any white space after the last non-whitespace token?
3
+ w.remove_trailing_whitespace true
4
+ end
5
+
6
+ Indenter.configure do |i|
7
+ # Make any change to code indents at all?
8
+ i.adjust_indents true
9
+
10
+ # What character should be used to pad the indents
11
+ i.indent_character ' '
12
+
13
+ # How many of the indent_character should be used at each level of indent
14
+ i.indent_characters_per_step 2
15
+ end
16
+
17
+ BlockFormatter.configure do |b|
18
+ # Modify block spacing at all?
19
+ b.adjust_blocks true
20
+
21
+ # What charater to pad blocks with?
22
+ b.padding_character " "
23
+
24
+ # How many padding characters to use at each end of the inline block?
25
+ b.padding_character_count 1
26
+ end
@@ -0,0 +1,25 @@
1
+ module Eden
2
+ # Formatter
3
+ #
4
+ # A base class providing configuration behaviour for Eden's source code
5
+ # formatters.
6
+ class Formatter
7
+ class << self
8
+ attr_accessor :options
9
+ end
10
+
11
+ def self.configure
12
+ yield self
13
+ end
14
+
15
+ def self.format( source_file )
16
+ raise "Format function not implmented."
17
+ end
18
+
19
+ def self.method_missing(name, *args, &block)
20
+ self.options ||= {}
21
+ self.options[name.to_sym] = *args[0]
22
+ end
23
+ end
24
+ end
25
+
@@ -0,0 +1,45 @@
1
+ # BlockFormatter
2
+ #
3
+ # Formats inline blocks ( including string interpolations ) by formatting the correct spacing
4
+ # around the content of the block.
5
+ #
6
+ # Options:
7
+ #
8
+ # - adjust_blocks - (true|false) Make any changes at all to blocks?
9
+ # - padding_character - (string) character to use to space between the content of the block and its braces
10
+ # - padding_character_count - (integer) number of spaces to use after/before each brace. Can be 0.
11
+
12
+ class BlockFormatter < Eden::Formatter
13
+ def self.format( source_file )
14
+ return unless options[:adjust_blocks]
15
+
16
+ space = (options[:padding_character] * options[:padding_character_count])
17
+ use_space = space.length != 0
18
+
19
+ source_file.each_line do |line|
20
+ line.tokens.each do |token|
21
+ if token.is?( :lcurly )
22
+ next_token = line.next_token( token )
23
+ if next_token && next_token.content != space
24
+ if next_token.is?(:whitespace)
25
+ next_token.content == space
26
+ else
27
+ new_space_token = Eden::Token.new(:whitespace, space)
28
+ line.insert_token_after(token, new_space_token)
29
+ end
30
+ end
31
+ elsif token.is?( :rcurly )
32
+ prev_token = line.previous_token( token )
33
+ if prev_token && prev_token.content != space
34
+ if prev_token.is?( :whitespace )
35
+ prev_token.content == space
36
+ else
37
+ new_space_token = Eden::Token.new(:whitespace, space)
38
+ line.insert_token_before(token, new_space_token)
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,91 @@
1
+ class Indenter < Eden::Formatter
2
+ def self.format( source_file )
3
+ return unless options[:adjust_indents]
4
+ @current_indent = 0
5
+ source_file.each_line do |line|
6
+ next if line.tokens[0] && line.tokens[0].is?(:heredoc_body)
7
+ calculate_pre_indent(line)
8
+ adjust_indent(line)
9
+ calculate_post_indent(line)
10
+ end
11
+ end
12
+
13
+ private
14
+ # Calculates changes in indent relative to the previous line due to tokens in the current line
15
+ def self.calculate_pre_indent( line )
16
+ line.tokens.each do |t|
17
+ if [:end, :rescue, :else, :elsif].include?(t.type)
18
+ @current_indent -= 1
19
+ line.tokens.each do |tok|
20
+ increase_indent! if [:class, :def, :module].include?(tok.type)
21
+ end
22
+ end
23
+
24
+ if t.is?( :when )
25
+ decrease_indent!
26
+ end
27
+ end
28
+ @current_indent = 0 if @current_indent < 0
29
+ end
30
+
31
+ def self.calculate_post_indent( line )
32
+ line.tokens.each do |t|
33
+ if [:class, :def, :module, :do, :begin, :rescue, :if, :else, :elsif, :case, :unless].include?(t.type)
34
+ increase_indent!
35
+
36
+ # Handle suffix conditionals
37
+ if [:if, :unless].include?(t.type)
38
+ prev_token = line.previous_non_whitespace_token(t)
39
+ decrease_indent! if !prev_token.nil? || (prev_token && prev_token.type == :equals)
40
+ end
41
+
42
+ line.tokens.each do |tok|
43
+ decrease_indent! if tok.is?( :end )
44
+ end
45
+ end
46
+
47
+ if [:for, :until, :while].include?(t.type)
48
+ increase_indent!
49
+
50
+ if [:until, :while].include?(t.type)
51
+ prev_token = line.previous_non_whitespace_token(t)
52
+ decrease_indent! if !prev_token.nil? || (prev_token && prev_token.type == :equals)
53
+ end
54
+
55
+ line.tokens.each do |tok|
56
+ decrease_indent! if tok.is?( :do )
57
+ end
58
+ end
59
+
60
+ if t.is?(:when)
61
+ increase_indent!
62
+ end
63
+ end
64
+ end
65
+
66
+ def self.adjust_indent( line )
67
+ return unless line.tokens[0]
68
+ if @current_indent == 0
69
+ line.tokens.delete_at(0) if line.tokens[0].type == :whitespace
70
+ else
71
+ if line.tokens[0].is?( :whitespace )
72
+ line.tokens[0].content = indent_content
73
+ else
74
+ indent_token = Eden::Token.new( :whitespace, indent_content )
75
+ line.tokens.unshift(indent_token)
76
+ end
77
+ end
78
+ end
79
+
80
+ def self.indent_content
81
+ options[:indent_character] * (options[:indent_characters_per_step] * @current_indent)
82
+ end
83
+
84
+ def self.increase_indent!
85
+ @current_indent += 1
86
+ end
87
+
88
+ def self.decrease_indent!
89
+ @current_indent -=1 if @current_indent > 0
90
+ end
91
+ end
@@ -0,0 +1,14 @@
1
+ class WhiteSpaceCleaner < Eden::Formatter
2
+ def self.format( source_file )
3
+ return unless options[:remove_trailing_whitespace]
4
+
5
+ source_file.each_line do |l|
6
+ i = l.tokens.size - 1
7
+ while( i >= 0 )
8
+ break unless l.tokens[i].is?( :whitespace ) || l.tokens[i].is?( :newline )
9
+ l.tokens.delete_at(i) if l.tokens[i].is?( :whitespace )
10
+ i -= 1
11
+ end
12
+ end
13
+ end
14
+ end
data/lib/eden/line.rb ADDED
@@ -0,0 +1,65 @@
1
+ module Eden
2
+ class Line
3
+ attr_accessor :line_no, :tokens
4
+
5
+ def initialize( line_no )
6
+ @line_no = line_no
7
+ @tokens = []
8
+ @warnings = []
9
+ end
10
+
11
+ def flatten!
12
+ @tokens.flatten!
13
+ self
14
+ end
15
+
16
+ def last_token_is_space?
17
+ @tokens[-1].type == :whitespace
18
+ end
19
+
20
+ def joined_tokens
21
+ tokens.map { |t| t.content }.join('')
22
+ end
23
+
24
+ def previous_token( token )
25
+ token_index = tokens.index( token )
26
+ return nil if token_index.nil? || token_index == 0
27
+ return tokens[token_index-1]
28
+ end
29
+
30
+ def previous_non_whitespace_token( token )
31
+ token_index = tokens.index( token )
32
+ return nil if token_index.nil? || token_index == 0
33
+ token_index -= 1
34
+ while( token_index != 0 )
35
+ return tokens[token_index] if tokens[token_index].type != :whitespace
36
+ token_index -= 1
37
+ end
38
+ return nil
39
+ end
40
+
41
+ def next_token( token )
42
+ token_index = tokens.index( token )
43
+ return nil if token_index.nil? || token_index == tokens.length
44
+ return tokens[token_index+1]
45
+ end
46
+
47
+ def insert_token_after( token, new_token )
48
+ token_index = tokens.index( token )
49
+ if token_index.nil?
50
+ tokens.push( new_token )
51
+ else
52
+ tokens.insert( token_index+1, new_token )
53
+ end
54
+ end
55
+
56
+ def insert_token_before( token, new_token )
57
+ token_index = tokens.index( token )
58
+ if token_index.nil?
59
+ tokens.unshift( new_token )
60
+ else
61
+ tokens.insert( token_index, new_token )
62
+ end
63
+ end
64
+ end
65
+ end