rubocop-git-kjanoudi 0.1.2
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.
- checksums.yaml +7 -0
- data/.gitignore +11 -0
- data/.travis.yml +31 -0
- data/Appraisals +30 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +26 -0
- data/README.md +41 -0
- data/Rakefile +15 -0
- data/bin/rubocop-git +5 -0
- data/gemfiles/0.24.gemfile +7 -0
- data/gemfiles/0.25.gemfile +7 -0
- data/gemfiles/0.26.gemfile +7 -0
- data/gemfiles/0.27.gemfile +7 -0
- data/gemfiles/0.28.gemfile +7 -0
- data/gemfiles/0.29.gemfile +7 -0
- data/gemfiles/0.30.gemfile +7 -0
- data/gemfiles/0.31.gemfile +7 -0
- data/gemfiles/0.32.gemfile +7 -0
- data/gemfiles/0.33.gemfile +7 -0
- data/gemfiles/0.34.gemfile +7 -0
- data/gemfiles/0.35.gemfile +7 -0
- data/gemfiles/0.36.gemfile +7 -0
- data/gemfiles/0.37.gemfile +7 -0
- data/gemfiles/0.38.gemfile +7 -0
- data/gemfiles/0.39.gemfile +7 -0
- data/gemfiles/0.40.gemfile +7 -0
- data/gemfiles/0.41.gemfile +7 -0
- data/gemfiles/0.42.gemfile +7 -0
- data/gemfiles/0.43.gemfile +7 -0
- data/gemfiles/0.44.gemfile +7 -0
- data/gemfiles/0.45.gemfile +7 -0
- data/gemfiles/0.46.gemfile +7 -0
- data/gemfiles/0.47.gemfile +7 -0
- data/hound.yml +254 -0
- data/lib/rubocop/git.rb +22 -0
- data/lib/rubocop/git/cli.rb +71 -0
- data/lib/rubocop/git/commit.rb +22 -0
- data/lib/rubocop/git/commit_file.rb +51 -0
- data/lib/rubocop/git/diff_parser.rb +31 -0
- data/lib/rubocop/git/file_violation.rb +5 -0
- data/lib/rubocop/git/line.rb +8 -0
- data/lib/rubocop/git/options.rb +84 -0
- data/lib/rubocop/git/patch.rb +63 -0
- data/lib/rubocop/git/pseudo_pull_request.rb +36 -0
- data/lib/rubocop/git/pseudo_resource.rb +16 -0
- data/lib/rubocop/git/rubo_comment.rb +92 -0
- data/lib/rubocop/git/runner.rb +67 -0
- data/lib/rubocop/git/style_checker.rb +43 -0
- data/lib/rubocop/git/style_guide.rb +98 -0
- data/lib/rubocop/git/version.rb +5 -0
- data/rubocop-git-kjanoudi.gemspec +27 -0
- data/test/rubocop/git/cli_test.rb +12 -0
- data/test/rubocop/git/options_test.rb +18 -0
- data/test/rubocop/git/runner_test.rb +23 -0
- data/test/test_helper.rb +2 -0
- metadata +173 -0
data/lib/rubocop/git.rb
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
require 'rubocop/git/version'
|
|
2
|
+
require 'rubocop'
|
|
3
|
+
require 'tempfile'
|
|
4
|
+
require 'fileutils'
|
|
5
|
+
|
|
6
|
+
module RuboCop
|
|
7
|
+
module Git
|
|
8
|
+
autoload :Commit, 'rubocop/git/commit'
|
|
9
|
+
autoload :CommitFile, 'rubocop/git/commit_file'
|
|
10
|
+
autoload :DiffParser, 'rubocop/git/diff_parser'
|
|
11
|
+
autoload :FileViolation, 'rubocop/git/file_violation'
|
|
12
|
+
autoload :Line, 'rubocop/git/line'
|
|
13
|
+
autoload :Options, 'rubocop/git/options'
|
|
14
|
+
autoload :Patch, 'rubocop/git/patch'
|
|
15
|
+
autoload :PseudoPullRequest, 'rubocop/git/pseudo_pull_request'
|
|
16
|
+
autoload :PseudoResource, 'rubocop/git/pseudo_resource'
|
|
17
|
+
autoload :Runner, 'rubocop/git/runner'
|
|
18
|
+
autoload :StyleChecker, 'rubocop/git/style_checker'
|
|
19
|
+
autoload :StyleGuide, 'rubocop/git/style_guide'
|
|
20
|
+
autoload :RuboComment, 'rubocop/git/rubo_comment'
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
require 'rubocop/git'
|
|
2
|
+
require 'optparse'
|
|
3
|
+
|
|
4
|
+
module RuboCop
|
|
5
|
+
module Git
|
|
6
|
+
class CLI
|
|
7
|
+
def run(args = ARGV)
|
|
8
|
+
@options = Options.new
|
|
9
|
+
parse_arguments(args)
|
|
10
|
+
Runner.new.run(@options)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
private
|
|
14
|
+
|
|
15
|
+
def parse_arguments(args)
|
|
16
|
+
@options.commits = option_parser.parse(args)
|
|
17
|
+
rescue OptionParser::InvalidOption, Options::Invalid => ex
|
|
18
|
+
warn "ERROR: #{ex.message}"
|
|
19
|
+
$stderr.puts
|
|
20
|
+
warn option_parser
|
|
21
|
+
exit 1
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def option_parser
|
|
25
|
+
@option_parser ||= OptionParser.new do |opt|
|
|
26
|
+
opt.banner << ' [[commit] commit]'
|
|
27
|
+
|
|
28
|
+
opt.on('-c', '--config FILE',
|
|
29
|
+
'Specify configuration file') do |config|
|
|
30
|
+
@options.config = config
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
opt.on('-r', '--require FILE',
|
|
34
|
+
'Require Ruby file') do |file|
|
|
35
|
+
require file
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
opt.on('-a', '--auto-correct', 'Auto-correct offenses.') do
|
|
39
|
+
@options.rubocop[:auto_correct] = true
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
opt.on('-d', '--debug', 'Display debug info') do
|
|
43
|
+
@options.rubocop[:debug] = true
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
opt.on('-D', '--display-cop-names',
|
|
47
|
+
'Display cop names in offense messages') do
|
|
48
|
+
@options.rubocop[:display_cop_names] = true
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
opt.on('--cached', 'git diff --cached') do
|
|
52
|
+
@options.cached = true
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
opt.on('--staged', 'synonym of --cached') do
|
|
56
|
+
@options.cached = true
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
opt.on('--hound', 'Hound compatibility mode') do
|
|
60
|
+
@options.hound = true
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
opt.on('--version', 'Display version') do
|
|
64
|
+
puts RuboCop::Git::VERSION
|
|
65
|
+
exit 0
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
require 'shellwords'
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Git
|
|
5
|
+
# ref. https://github.com/thoughtbot/hound/blob/d2f3933/app/models/commit.rb
|
|
6
|
+
class Commit
|
|
7
|
+
def initialize(options)
|
|
8
|
+
@options = options
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def file_content(filename)
|
|
12
|
+
if @options.cached
|
|
13
|
+
`git show :#{filename.shellescape}`
|
|
14
|
+
elsif @options.commit_last
|
|
15
|
+
`git show #{@options.commit_last.shellescape}:#{filename.shellescape}`
|
|
16
|
+
else
|
|
17
|
+
File.read(filename)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
module RuboCop::Git
|
|
2
|
+
# copy from https://github.com/thoughtbot/hound/blob/d2f3933/app/models/commit_file.rb
|
|
3
|
+
class CommitFile
|
|
4
|
+
def initialize(file, commit)
|
|
5
|
+
@file = file
|
|
6
|
+
@commit = commit
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def filename
|
|
10
|
+
@file.filename
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def content
|
|
14
|
+
@content ||= begin
|
|
15
|
+
unless removed?
|
|
16
|
+
@commit.file_content(filename)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def relevant_line?(line_number)
|
|
22
|
+
modified_lines.detect do |modified_line|
|
|
23
|
+
modified_line.line_number == line_number
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def removed?
|
|
28
|
+
@file.status == 'removed'
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def ruby?
|
|
32
|
+
filename.match(/.*\.rb$/)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def modified_lines
|
|
36
|
+
@modified_lines ||= patch.additions
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def modified_line_at(line_number)
|
|
40
|
+
modified_lines.detect do |modified_line|
|
|
41
|
+
modified_line.line_number == line_number
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
private
|
|
46
|
+
|
|
47
|
+
def patch
|
|
48
|
+
Patch.new(@file.patch)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
module RuboCop
|
|
2
|
+
module Git
|
|
3
|
+
class DiffParser
|
|
4
|
+
class << self
|
|
5
|
+
def parse(diff)
|
|
6
|
+
new.parse(diff)
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def parse(diff)
|
|
11
|
+
files = []
|
|
12
|
+
in_patch = false
|
|
13
|
+
|
|
14
|
+
diff.each_line do |line|
|
|
15
|
+
case line
|
|
16
|
+
when /^diff --git/
|
|
17
|
+
in_patch = false
|
|
18
|
+
when %r{^\+{3} b/(?<path>[^\t\n\r]+)}
|
|
19
|
+
files << PseudoResource.new(Regexp.last_match[:path])
|
|
20
|
+
when /^@@/
|
|
21
|
+
in_patch = true
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
files.last.patch << line if in_patch
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
files
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
module RuboCop
|
|
2
|
+
module Git
|
|
3
|
+
class Options
|
|
4
|
+
class Invalid < StandardError; end
|
|
5
|
+
|
|
6
|
+
HOUND_DEFAULT_CONFIG_FILE =
|
|
7
|
+
File.expand_path('../../../../hound.yml', __FILE__)
|
|
8
|
+
|
|
9
|
+
attr_accessor :config
|
|
10
|
+
attr_reader :cached, :hound, :rubocop
|
|
11
|
+
|
|
12
|
+
def initialize(hash_options = nil)
|
|
13
|
+
@config = nil
|
|
14
|
+
@cached = false
|
|
15
|
+
@hound = false
|
|
16
|
+
@rubocop = {}
|
|
17
|
+
@commits = []
|
|
18
|
+
|
|
19
|
+
from_hash(hash_options) if hash_options
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def cached=(cached_)
|
|
23
|
+
if cached_ && !@commits.empty?
|
|
24
|
+
fail Invalid, 'cached and commit cannot be specified together'
|
|
25
|
+
end
|
|
26
|
+
@cached = !!cached_
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def hound=(hound_)
|
|
30
|
+
@hound = !!hound_
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def rubocop=(rubocop_)
|
|
34
|
+
unless rubocop_.is_a?(Hash)
|
|
35
|
+
fail Invalid, "invalid rubocop: #{rubocop_.inspect}"
|
|
36
|
+
end
|
|
37
|
+
@rubocop = rubocop_
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def commits=(commits)
|
|
41
|
+
unless commits.is_a?(Array) && commits.length <= 2
|
|
42
|
+
fail Invalid, "invalid commits: #{commits.inspect}"
|
|
43
|
+
end
|
|
44
|
+
if !commits.empty? && cached
|
|
45
|
+
fail Invalid, 'cached and commit cannot be specified together'
|
|
46
|
+
end
|
|
47
|
+
@commits = commits
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def config_file
|
|
51
|
+
if hound
|
|
52
|
+
HOUND_DEFAULT_CONFIG_FILE
|
|
53
|
+
elsif config
|
|
54
|
+
config
|
|
55
|
+
elsif File.exist?(RuboCop::ConfigLoader::DOTFILE)
|
|
56
|
+
RuboCop::ConfigLoader::DOTFILE
|
|
57
|
+
else
|
|
58
|
+
RuboCop::ConfigLoader::DEFAULT_FILE
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def commit_first
|
|
63
|
+
@commits.first
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def commit_last
|
|
67
|
+
@commits.length == 1 ? false : @commits.last
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
private
|
|
71
|
+
|
|
72
|
+
def from_hash(hash_options)
|
|
73
|
+
hash_options = hash_options.dup
|
|
74
|
+
%w(config cached hound rubocop commits).each do |key|
|
|
75
|
+
value = hash_options.delete(key) || hash_options.delete(key.to_sym)
|
|
76
|
+
public_send("#{key}=", value)
|
|
77
|
+
end
|
|
78
|
+
unless hash_options.empty?
|
|
79
|
+
fail Invalid, "invalid keys: #{hash_options.keys.join(' ')}"
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
module RuboCop::Git
|
|
2
|
+
# copy from https://github.com/thoughtbot/hound/blob/d2f3933/app/models/patch.rb
|
|
3
|
+
class Patch
|
|
4
|
+
RANGE_INFORMATION_LINE = /^@@ .+\+(?<line_number>\d+),/
|
|
5
|
+
MODIFIED_LINE = /^\+(?!\+|\+)/
|
|
6
|
+
NOT_REMOVED_LINE = /^[^-]/
|
|
7
|
+
PATCH_INFO_LINE = /\+([0-9,]+)/
|
|
8
|
+
|
|
9
|
+
def initialize(body)
|
|
10
|
+
@body = body || ''
|
|
11
|
+
@changes = []
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def additions
|
|
15
|
+
line_number = 0
|
|
16
|
+
|
|
17
|
+
lines.each_with_index.inject(@changes) do |additions, (content, patch_position)|
|
|
18
|
+
case content
|
|
19
|
+
when RANGE_INFORMATION_LINE
|
|
20
|
+
line_number = Regexp.last_match[:line_number].to_i
|
|
21
|
+
when MODIFIED_LINE
|
|
22
|
+
additions << Line.new(content, line_number, patch_position)
|
|
23
|
+
line_number += 1
|
|
24
|
+
when NOT_REMOVED_LINE
|
|
25
|
+
line_number += 1
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
additions
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# maps out additions line numbers to indicate start and end of code changes
|
|
33
|
+
# [[5,7], [11,11]] indicates changes from line 5, 6, 7 and then
|
|
34
|
+
# another one at 11
|
|
35
|
+
def additions_map
|
|
36
|
+
if @changes.empty?
|
|
37
|
+
self.additions
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
map = []
|
|
41
|
+
starting_line = ending_line = 0
|
|
42
|
+
|
|
43
|
+
@changes.each do |addition|
|
|
44
|
+
if starting_line == 0
|
|
45
|
+
starting_line = ending_line = addition.line_number
|
|
46
|
+
elsif addition.line_number == ( ending_line + 1 )
|
|
47
|
+
ending_line = addition.line_number
|
|
48
|
+
else # this row is not part of the last rows "group"
|
|
49
|
+
map.push([starting_line, ending_line])
|
|
50
|
+
starting_line = ending_line = addition.line_number
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
map.push([starting_line, ending_line])
|
|
54
|
+
map
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
private
|
|
58
|
+
|
|
59
|
+
def lines
|
|
60
|
+
@body.lines
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
module RuboCop
|
|
2
|
+
module Git
|
|
3
|
+
# ref. https://github.com/thoughtbot/hound/blob/d2f3933/app/models/pull_request.rb
|
|
4
|
+
class PseudoPullRequest
|
|
5
|
+
HOUND_CONFIG_FILE = '.hound.yml'
|
|
6
|
+
|
|
7
|
+
def initialize(files, options)
|
|
8
|
+
@files = files
|
|
9
|
+
@options = options
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def pull_request_files
|
|
13
|
+
@files.map do |file|
|
|
14
|
+
build_commit_file(file)
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def config
|
|
19
|
+
return unless @options.hound
|
|
20
|
+
File.read(HOUND_CONFIG_FILE)
|
|
21
|
+
rescue Errno::ENOENT
|
|
22
|
+
nil
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
def build_commit_file(file)
|
|
28
|
+
CommitFile.new(file, head_commit)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def head_commit
|
|
32
|
+
@head_commit ||= Commit.new(@options)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
module RuboCop::Git
|
|
2
|
+
class RuboComment
|
|
3
|
+
RUBOCOP_DISABLE = "# rubocop:disable all\n"
|
|
4
|
+
RUBOCOP_ENABLE = "# rubocop:enable all\n"
|
|
5
|
+
WHITE_SPACE = /^\s*/
|
|
6
|
+
|
|
7
|
+
def initialize(files)
|
|
8
|
+
@edit_map = {}
|
|
9
|
+
@files = files
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# adds rubocop enable and disabled comments to files
|
|
13
|
+
# making sure only edited lines are processed by rubocop
|
|
14
|
+
def add_comments
|
|
15
|
+
@files.each do |file|
|
|
16
|
+
patch_info = Patch.new(file.patch).additions_map
|
|
17
|
+
temp_file = Tempfile.new('temp')
|
|
18
|
+
|
|
19
|
+
line_count = edited_line_count = current_patch = 0
|
|
20
|
+
in_patch = false
|
|
21
|
+
edit_locations = []
|
|
22
|
+
|
|
23
|
+
begin
|
|
24
|
+
File.open(file.filename, "r").each_line do |line|
|
|
25
|
+
line_count += 1
|
|
26
|
+
edited_line_count += 1
|
|
27
|
+
|
|
28
|
+
if line_count == patch_info[current_patch].first
|
|
29
|
+
temp_file.puts generate_spaces(line) + RUBOCOP_ENABLE
|
|
30
|
+
in_patch = true
|
|
31
|
+
edit_locations.push edited_line_count
|
|
32
|
+
edited_line_count += 1
|
|
33
|
+
|
|
34
|
+
elsif in_patch && patch_info[current_patch].last + 1 == line_count
|
|
35
|
+
temp_file.puts generate_spaces(line) + RUBOCOP_DISABLE
|
|
36
|
+
in_patch = false
|
|
37
|
+
edit_locations.push edited_line_count
|
|
38
|
+
edited_line_count += 1
|
|
39
|
+
current_patch += 1 unless (current_patch + 1) >= patch_info.size
|
|
40
|
+
|
|
41
|
+
elsif line_count == 1 #adds disable at top of file
|
|
42
|
+
temp_file.puts generate_spaces(line) + RUBOCOP_DISABLE
|
|
43
|
+
edit_locations.push edited_line_count
|
|
44
|
+
edited_line_count += 1
|
|
45
|
+
|
|
46
|
+
end
|
|
47
|
+
temp_file.puts line
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
temp_file.close
|
|
51
|
+
FileUtils.mv(temp_file.path, file.filename)
|
|
52
|
+
@edit_map[file.filename] = edit_locations
|
|
53
|
+
ensure
|
|
54
|
+
temp_file.close
|
|
55
|
+
temp_file.unlink
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# removes all added comments that where added from add_comments
|
|
61
|
+
def remove_comments
|
|
62
|
+
@files.each do |file|
|
|
63
|
+
temp_file = Tempfile.new('temp')
|
|
64
|
+
line_count = 0
|
|
65
|
+
|
|
66
|
+
begin
|
|
67
|
+
File.open(file.filename, "r").each_line do |line|
|
|
68
|
+
line_count += 1
|
|
69
|
+
temp_file.puts line unless @edit_map[file.filename].find_index line_count
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
temp_file.close
|
|
73
|
+
FileUtils.mv(temp_file.path, file.filename)
|
|
74
|
+
ensure
|
|
75
|
+
temp_file.close
|
|
76
|
+
temp_file.unlink
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
private
|
|
82
|
+
|
|
83
|
+
# generates whitespaces to make en/disable comments match line indent
|
|
84
|
+
# preventing rubocop errors
|
|
85
|
+
def generate_spaces(line)
|
|
86
|
+
whitespaces = ""
|
|
87
|
+
WHITE_SPACE.match(line).to_s.split('').size.times { whitespaces << " " }
|
|
88
|
+
whitespaces
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
end
|
|
92
|
+
end
|