зеркало из https://github.com/github/ruby.git
Add misc/expand_tabs.rb ported from auto-style.rb
This is implemented to close [Misc #16112] because all other options got
at least one objection, and nobody has objected to this solution.
This code is a little complicated for the purpose, but that's just
because it includes some historical code for auto-style.rb:
918a7c31b6/bin/auto-style.rb
Please feel free to improve this file as you like.
[Misc #16112]
This commit is contained in:
Родитель
d8d8015b93
Коммит
15eaedf805
|
@ -0,0 +1,171 @@
|
|||
#!/usr/bin/env ruby --disable-gems
|
||||
# Add the following line to your `.git/hooks/pre-commit`:
|
||||
#
|
||||
# $ ruby --disable-gems misc/expand_tabs.rb
|
||||
#
|
||||
|
||||
require 'shellwords'
|
||||
require 'tmpdir'
|
||||
ENV['LC_ALL'] = 'C'
|
||||
|
||||
class Git
|
||||
def initialize(oldrev, newrev)
|
||||
@oldrev = oldrev
|
||||
@newrev = newrev
|
||||
end
|
||||
|
||||
# ["foo/bar.c", "baz.h", ...]
|
||||
def updated_paths
|
||||
with_clean_env do
|
||||
IO.popen(['git', 'diff', '--cached', '--name-only', @newrev], &:readlines).each(&:chomp!)
|
||||
end
|
||||
end
|
||||
|
||||
# [0, 1, 4, ...]
|
||||
def updated_lines(file)
|
||||
lines = []
|
||||
revs_pattern = /\A0{40} /
|
||||
with_clean_env { IO.popen(['git', 'blame', '-l', '--', file], &:readlines) }.each_with_index do |line, index|
|
||||
if revs_pattern =~ line
|
||||
lines << index
|
||||
end
|
||||
end
|
||||
lines
|
||||
end
|
||||
|
||||
def add(file)
|
||||
git('add', file)
|
||||
end
|
||||
|
||||
def toplevel
|
||||
IO.popen(['git', 'rev-parse', '--show-toplevel'], &:read).chomp
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def git(*args)
|
||||
cmd = ['git', *args].shelljoin
|
||||
unless with_clean_env { system(cmd) }
|
||||
abort "Failed to run: #{cmd}"
|
||||
end
|
||||
end
|
||||
|
||||
def with_clean_env
|
||||
git_dir = ENV.delete('GIT_DIR') # this overcomes '-C' or pwd
|
||||
yield
|
||||
ensure
|
||||
ENV['GIT_DIR'] = git_dir if git_dir
|
||||
end
|
||||
end
|
||||
|
||||
DEFAULT_GEM_LIBS = %w[
|
||||
bundler
|
||||
cmath
|
||||
csv
|
||||
e2mmap
|
||||
fileutils
|
||||
forwardable
|
||||
ipaddr
|
||||
irb
|
||||
logger
|
||||
matrix
|
||||
mutex_m
|
||||
ostruct
|
||||
prime
|
||||
racc
|
||||
rdoc
|
||||
rexml
|
||||
rss
|
||||
scanf
|
||||
shell
|
||||
sync
|
||||
thwait
|
||||
tracer
|
||||
webrick
|
||||
]
|
||||
|
||||
DEFAULT_GEM_EXTS = %w[
|
||||
bigdecimal
|
||||
date
|
||||
dbm
|
||||
etc
|
||||
fcntl
|
||||
fiddle
|
||||
gdbm
|
||||
io/console
|
||||
json
|
||||
openssl
|
||||
psych
|
||||
sdbm
|
||||
stringio
|
||||
strscan
|
||||
zlib
|
||||
]
|
||||
|
||||
EXPANDTAB_IGNORED_FILES = [
|
||||
# default gems whose master is GitHub
|
||||
%r{\Abin/(?!erb)\w+\z},
|
||||
*DEFAULT_GEM_LIBS.flat_map { |lib|
|
||||
[
|
||||
%r{\Alib/#{lib}/},
|
||||
%r{\Alib/#{lib}\.gemspec\z},
|
||||
%r{\Alib/#{lib}\.rb\z},
|
||||
%r{\Atest/#{lib}/},
|
||||
]
|
||||
},
|
||||
*DEFAULT_GEM_EXTS.flat_map { |ext|
|
||||
[
|
||||
%r{\Aext/#{ext}/},
|
||||
%r{\Atest/#{ext}/},
|
||||
]
|
||||
},
|
||||
|
||||
# vendoring (ccan)
|
||||
%r{\Accan/},
|
||||
|
||||
# vendoring (onigmo)
|
||||
%r{\Aenc/},
|
||||
%r{\Ainclude/ruby/onigmo\.h\z},
|
||||
%r{\Areg.+\.(c|h)\z},
|
||||
|
||||
# explicit or implicit `c-file-style: "linux"`
|
||||
%r{\Aaddr2line\.c\z},
|
||||
%r{\Amissing/},
|
||||
%r{\Astrftime\.c\z},
|
||||
%r{\Avsnprintf\.c\z},
|
||||
]
|
||||
|
||||
git = Git.new('HEAD^', 'HEAD')
|
||||
|
||||
Dir.chdir(git.toplevel) do
|
||||
paths = git.updated_paths
|
||||
paths.select! {|l|
|
||||
/^\d/ !~ l and /\.bat\z/ !~ l and
|
||||
(/\A(?:config|[Mm]akefile|GNUmakefile|README)/ =~ File.basename(l) or
|
||||
/\A\z|\.(?:[chsy]|\d+|e?rb|tmpl|bas[eh]|z?sh|in|ma?k|def|src|trans|rdoc|ja|en|el|sed|awk|p[ly]|scm|mspec|html|)\z/ =~ File.extname(l))
|
||||
}
|
||||
files = paths.select {|n| File.file?(n)}
|
||||
exit if files.empty?
|
||||
|
||||
files.each do |f|
|
||||
src = File.binread(f) rescue next
|
||||
|
||||
expanded = false
|
||||
updated_lines = git.updated_lines(f)
|
||||
if !updated_lines.empty? && (f.end_with?('.c') || f.end_with?('.h') || f == 'insns.def') && EXPANDTAB_IGNORED_FILES.all? { |re| !f.match(re) }
|
||||
src.gsub!(/^.*$/).with_index do |line, lineno|
|
||||
if updated_lines.include?(lineno) && line.start_with?("\t") # last-committed line with hard tabs
|
||||
expanded = true
|
||||
line.sub(/\A\t+/) { |tabs| ' ' * (8 * tabs.length) }
|
||||
else
|
||||
line
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if expanded
|
||||
File.binwrite(f, src)
|
||||
git.add(f)
|
||||
end
|
||||
end
|
||||
end
|
Загрузка…
Ссылка в новой задаче