Indentinator 0.0.2 → 0.0.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.
Files changed (4) hide show
  1. data/bin/indentinator +87 -25
  2. data/lib/indentinator.rb +33 -29
  3. data/test/tests.rb +139 -9
  4. metadata +33 -6
data/bin/indentinator CHANGED
@@ -1,50 +1,112 @@
1
1
  #! /usr/bin/env ruby
2
+ $:.unshift(File.expand_path('../../lib', __FILE__)) if $0 == __FILE__
2
3
  require 'getopt/std'
3
4
  require 'indentinator'
4
5
  include Indentinator
5
6
 
6
- opts = Getopt::Std.getopts('c:vV')
7
- @to_amount = opts['c']
7
+ def parse_amount(val)
8
+ return nil if val.nil?
9
+ if val.to_i.to_s == val # is a number
10
+ ' ' * val.to_i
11
+ elsif val.downcase == 'tab'
12
+ "\t"
13
+ end
14
+ end
15
+
16
+ opts = Getopt::Std.getopts('ic:a:vVr')
17
+ @from_stdin = opts['i']
18
+ @amount = parse_amount(opts['a'])
19
+ @to_amount = parse_amount(opts['c'])
8
20
  @very_verbose = opts['V']
9
- @verbose = opts['v'] or @very_verbose
21
+ @verbose = opts['v'] || @very_verbose
22
+ @replace = opts['r'] && !@from_stdin
10
23
  @files = ARGV
11
- if @files.size == 0
24
+
25
+ if @files.size == 0 and not @from_stdin
12
26
  puts "Usage:
13
27
 
14
28
  indentinator [-c INDENT_AMOUNT] <file> [<file2>...]
15
29
 
16
30
  Options:
17
31
 
18
- -c Modify the indentation amount to INDENT_AMOUNT
32
+ -c INDENT_AMOUNT
33
+ Modify the indentation amount to INDENT_AMOUNT. The value is either
34
+ a number - in which case it indicates the number of spaces to use;
35
+ or TAB - which indicates to use tabs.
36
+
37
+ -a AMOUNT
38
+ Tell me the original AMOUNT of indentation to use
39
+ when converting the file's indentation.
40
+ Normally, this value is inferred by looking at the input.
41
+ This option takes the same values as -c.
42
+
43
+ -i Read input from stdin. This will ignore any file arguments.
44
+
45
+ -r Replace the original file. Please back up your file or make sure you
46
+ are using version control. Indentinator is not responsible for
47
+ any damages; it just indents.
48
+
19
49
  -v Verbose output
50
+
20
51
  -V Very verbose output
21
52
 
22
53
  "
23
54
  exit(0)
24
55
  end
25
56
 
26
- @files.each do |path|
27
- begin
28
- if File.directory?(path)
29
- puts "#{path} is a directory."
30
- next
57
+ def process_input(text, path)
58
+ lines = text.split("\n")
59
+ if @to_amount.nil? or @amount.nil?
60
+ @amount = indent_amount(lines)
61
+ end
62
+ if @to_amount.nil?
63
+ if @amount == ''
64
+ puts "#{path} has no indentation."
65
+ elsif @amount.match(/^ *$/)
66
+ puts "#{path} uses #{@amount.size} spaces."
67
+ elsif @amount == "\t"
68
+ puts "#{path} uses tabs."
69
+ else
70
+ puts "#{path} uses #{@amount.gsub(' ', 'SPACE ').gsub("\t", 'TAB ').strip}. WTF?"
71
+ end
72
+ else
73
+ output = convert_lines(lines, @amount, @to_amount).join("\n")
74
+ if @replace
75
+ write_file(path, output)
76
+ else
77
+ puts output
31
78
  end
32
- File.open(path) do |file|
33
- lines = file.read.split("\n")
34
- amount = indent_amount(lines)
35
- if @to_amount.nil?
36
- if amount == 0
37
- puts "#{path} has no indentation."
38
- else
39
- puts "#{path} uses #{amount} spaces."
40
- end
41
- else
42
- puts convert_lines(lines, amount, @to_amount).join("\n")
79
+ end
80
+ end
81
+
82
+ def read_file(path)
83
+ f = open(path, 'r')
84
+ text = f.read
85
+ f.close
86
+ text
87
+ end
88
+
89
+ def write_file(path, text)
90
+ f = open(path, 'w')
91
+ f.write(text)
92
+ f.close
93
+ end
94
+
95
+ if @from_stdin
96
+ process_file(STDIN, 'Input')
97
+ else
98
+ @files.each do |path|
99
+ begin
100
+ if File.directory?(path)
101
+ puts "#{path} is a directory."
102
+ next
43
103
  end
104
+ text = read_file(path)
105
+ process_input(text, path)
106
+ rescue Errno::ENOENT
107
+ puts "Can't open #{path}"
108
+ rescue ArgumentError
109
+ puts "Failed for #{path}. Is it binary?"
44
110
  end
45
- rescue Errno::ENOENT
46
- puts "Can't open #{path}"
47
- rescue ArgumentError
48
- puts "Failed for #{path}. Is it binary?"
49
111
  end
50
112
  end
data/lib/indentinator.rb CHANGED
@@ -1,28 +1,37 @@
1
+ class String
2
+ def -(other)
3
+ self.index(other) == 0 ? self[other.size..self.size] : nil
4
+ end
5
+ end
6
+
1
7
  module Indentinator
2
8
 
3
9
  def indent_amount(lines)
4
- curr = 0
10
+ curr = ''
5
11
  diffs = {}
12
+ # loop through the lines and count each indent difference
13
+ # between prev and current line in `diffs`
6
14
  lines.each_with_index do |line, idx|
7
- spaces = line.match(/^( *)/)[0]
8
- diff = spaces.size - curr
15
+ whites = line.match(/^([ \t]*)/)[0]
16
+ diff = whites - curr
9
17
  if @very_verbose
10
- puts "#{'%- 5d' % (idx + 1)}: #{diff}"
18
+ puts "#{'%- 5d' % (idx + 1)}: #{diff.size}" if diff
11
19
  end
12
- if diff > 0
20
+ if diff and diff.size > 0
13
21
  diffs[diff] ||= 0
14
22
  diffs[diff] = diffs[diff] + 1
15
23
  end
16
- curr = spaces.size
24
+ curr = whites
17
25
  end
26
+ # sort and pick the one w highest frequency
18
27
  if diffs.size > 0
19
28
  sorted = diffs.sort{|a, b| b[1] - a[1]}
20
29
  if @verbose
21
- puts sorted.map{|p| ('%- 5d=> ' % p[0]) + p[1].to_s}.join("\n")
30
+ puts sorted.map{|p| ('%- 5d=> ' % p[0].size) + p[1].to_s}.join("\n")
22
31
  end
23
32
  sorted[0][0]
24
33
  else
25
- 0
34
+ ''
26
35
  end
27
36
  end
28
37
 
@@ -31,30 +40,25 @@ def convert_indentation(text, from, to)
31
40
  end
32
41
 
33
42
  def convert_lines(lines, from, to)
34
- ratio = to.to_f / from
35
- curr_indent_org = 0
36
- curr_indent = 0
43
+ indents = [] # indentation stack
44
+ # loop through and convert line by line
37
45
  lines.map do |line|
38
- if line.empty?
39
- line
40
- else
41
- m = line.match(/^( *)(.*)$/)
42
- spaces = m[1]
43
- rest = m[2]
44
- indent = spaces.size - curr_indent_org
45
- curr_indent_org = spaces.size
46
- if indent == from # standard indent
47
- curr_indent += (indent * ratio).to_i
48
- elsif indent > 0 # non-standard indent
49
- curr_indent += indent
50
- elsif indent == 0 # no indent
51
-
52
- else # de-indent
53
- # reset back to use original indentation
54
- curr_indent = (curr_indent_org * ratio).round
46
+ next line if line.strip.empty?
47
+ m = line.match(/^([ \t]*)(.*)$/)
48
+ whites = m[1]
49
+ rest = m[2]
50
+ diff = whites - indents.join
51
+ if diff.nil?
52
+ # no match indent, could be a de-indent or a different indent pattern
53
+ # find longest matching indent in stack, and pop till that point
54
+ indents.pop
55
+ while indents.size > 0 and diff.nil?
56
+ indents.pop
57
+ diff = whites - indents.join
55
58
  end
56
- ' ' * curr_indent + rest
57
59
  end
60
+ indents.push(diff)
61
+ indents.map{|i| (i == from or i == "\t") ? to : i }.join + rest
58
62
  end
59
63
  end
60
64
 
data/test/tests.rb CHANGED
@@ -1,5 +1,61 @@
1
1
  require 'rspec'
2
- require 'indentinator.rb'
2
+ require 'indentinator'
3
+ include Indentinator
4
+
5
+ describe 'indent_amount' do
6
+ it "sniffs indent amount 2" do
7
+ text = "
8
+ line 1
9
+ line 2"
10
+ indent_amount(text.split("\n")).should == " " * 2
11
+ text = "
12
+ line 1
13
+ line 2"
14
+ indent_amount(text.split("\n")).should == " " * 4
15
+ end
16
+
17
+ it "should pick most frequent indentation" do
18
+ text = "
19
+ line 1
20
+ line 2
21
+ line 3
22
+ line 4"
23
+ indent_amount(text.split("\n")).should == " " * 4
24
+ end
25
+
26
+ it "should support tabs" do
27
+ lines = [
28
+ "line 1",
29
+ "\tline 2",
30
+ "\t\tline 3"
31
+ ]
32
+ indent_amount(lines).should == "\t"
33
+ end
34
+
35
+ it "should support tabs - adheres to frequency principle" do
36
+ lines = [
37
+ "line 1",
38
+ "\tline 2",
39
+ "\t\tline 3",
40
+ " line 4"
41
+ ]
42
+ indent_amount(lines).should == "\t"
43
+ end
44
+
45
+ it "should support tabs - adheres to frequency principle(2)" do
46
+ lines = [
47
+ "line 1",
48
+ "\tline 2",
49
+ "\t\tline 3",
50
+ "line 4",
51
+ " line 5",
52
+ " line 6",
53
+ " line 7"
54
+ ]
55
+ indent_amount(lines).should == " "
56
+ end
57
+ end
58
+
3
59
  describe 'convert_file' do
4
60
  it "converts basic indentation" do
5
61
  text = "
@@ -7,7 +63,7 @@ line 1
7
63
  line 2
8
64
  line 3
9
65
  line 4"
10
- convert_indentation(text, 2, 4).should == "
66
+ convert_indentation(text, ' ' * 2, ' ' * 4).should == "
11
67
  line 1
12
68
  line 2
13
69
  line 3
@@ -19,7 +75,7 @@ line 4"
19
75
  text = "
20
76
  line 1
21
77
  line 2"
22
- convert_indentation(text, 2, 4).should == text
78
+ convert_indentation(text, ' ' * 2, ' ' * 4).should == text
23
79
  end
24
80
 
25
81
  it "retains non-standard indentation even when is
@@ -29,7 +85,7 @@ line 1
29
85
  line 2
30
86
  line 3
31
87
  line 2"
32
- convert_indentation(text, 2, 4).should == "
88
+ convert_indentation(text, ' ' * 2, ' ' * 4).should == "
33
89
  line 1
34
90
  line 2
35
91
  line 3
@@ -42,7 +98,7 @@ line 1
42
98
  line 2
43
99
  line 3
44
100
  line 4"
45
- convert_indentation(text, 4, 2).should == "
101
+ convert_indentation(text, ' ' * 4, ' ' * 2).should == "
46
102
  line 1
47
103
  line 2
48
104
  line 3
@@ -56,7 +112,7 @@ line 0
56
112
  line 2
57
113
  line 3
58
114
  line 4"
59
- convert_indentation(text, 4, 2).should == "
115
+ convert_indentation(text, ' ' * 4, ' ' * 2).should == "
60
116
  line 0
61
117
  line 1
62
118
  line 2
@@ -70,7 +126,7 @@ line 0
70
126
  line 2
71
127
  line 3
72
128
  line 4"
73
- convert_indentation(text, 2, 4).should == "
129
+ convert_indentation(text, ' ' * 2, ' ' * 4).should == "
74
130
  line 0
75
131
  line 2
76
132
  line 3
@@ -85,7 +141,7 @@ line 0
85
141
  line 3
86
142
 
87
143
  line 4"
88
- convert_indentation(text, 4, 2).should == "
144
+ convert_indentation(text, ' ' * 4, ' ' * 2).should == "
89
145
  line 0
90
146
  line 1
91
147
  line 2
@@ -101,11 +157,85 @@ line 1
101
157
  line 3
102
158
 
103
159
  line 4"
104
- convert_indentation(text, 4, 2).should == "
160
+ convert_indentation(text, ' ' * 4, ' ' * 2).should == "
105
161
  line 1
106
162
  line 2
107
163
  line 3
108
164
 
109
165
  line 4"
110
166
  end
167
+
168
+ it "should de-indent property(ex 3)" do
169
+ text = "
170
+ line 2
171
+ line 3
172
+ line 4
173
+ line 5"
174
+ convert_indentation(text, ' ' * 2, ' ' * 4).should == "
175
+ line 2
176
+ line 3
177
+ line 4
178
+ line 5"
179
+ end
180
+
181
+ it "should de-indent property(ex 4)" do
182
+ text = "
183
+ line 2
184
+ line 3
185
+ line 4
186
+ line 5"
187
+ convert_indentation(text, ' ' * 2, ' ' * 4).should == text
188
+ end
189
+
190
+ it "should de-indent property(ex 5)" do
191
+ text = "
192
+ line 1
193
+ line 2
194
+ line 3
195
+ line 4
196
+ line 5
197
+ line 6"
198
+ convert_indentation(text, ' ' * 2, ' ' * 4).should == text
199
+ end
200
+
201
+ it "should support tabs" do
202
+ lines = [
203
+ "line 1",
204
+ "\tline 2",
205
+ "\tline 3",
206
+ "\t\tline 4"
207
+ ]
208
+ convert_lines(lines, "\t", " " * 4).should == [
209
+ "line 1",
210
+ " line 2",
211
+ " line 3",
212
+ " line 4"
213
+ ]
214
+ end
215
+
216
+ it "should support tabs (ex 2)" do
217
+ text = "
218
+ line 1
219
+ line 2
220
+ line 3
221
+ line 4"
222
+ convert_indentation(text, ' ' * 2, "\t").should == "
223
+ line 1
224
+ \tline 2
225
+ \t\tline 3
226
+ line 4"
227
+ end
228
+
229
+ it "tabs should be converted always" do
230
+ lines = [
231
+ "line 1",
232
+ " line 2",
233
+ "\tline 3"
234
+ ]
235
+ convert_lines(lines, ' ' * 2, ' ' * 4).should == [
236
+ "line 1",
237
+ " line 2",
238
+ " line 3"
239
+ ]
240
+ end
111
241
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: Indentinator
3
3
  version: !ruby/object:Gem::Version
4
- hash: 27
4
+ hash: 25
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 0
9
- - 2
10
- version: 0.0.2
9
+ - 3
10
+ version: 0.0.3
11
11
  platform: ruby
12
12
  authors:
13
13
  - Toby Ho
@@ -15,10 +15,37 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-03-24 00:00:00 -04:00
18
+ date: 2011-03-26 00:00:00 -04:00
19
19
  default_executable:
20
- dependencies: []
21
-
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: getopt
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 3
30
+ segments:
31
+ - 0
32
+ version: "0"
33
+ type: :runtime
34
+ version_requirements: *id001
35
+ - !ruby/object:Gem::Dependency
36
+ name: rspec
37
+ prerelease: false
38
+ requirement: &id002 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ hash: 3
44
+ segments:
45
+ - 0
46
+ version: "0"
47
+ type: :development
48
+ version_requirements: *id002
22
49
  description:
23
50
  email: airportyh@gmail.com
24
51
  executables: