diff --git a/Gemfile.lock b/Gemfile.lock index 049ae52..23520a5 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -2,11 +2,13 @@ PATH remote: . specs: format-staged (0.0.3) + colorize GEM remote: http://rubygems.org/ specs: ast (2.4.2) + colorize (0.8.1) parallel (1.22.1) parser (3.1.2.0) ast (~> 2.4.1) diff --git a/bin/git-format-staged b/bin/git-format-staged index 6e20263..f325ac3 100755 --- a/bin/git-format-staged +++ b/bin/git-format-staged @@ -57,6 +57,10 @@ parser = OptionParser.new do |opt| puts FormatStaged::VERSION exit end + + opt.on('--[no-]color', 'Colorizes output') do |value| + parameters[:color_output] = value + end end parser.parse! diff --git a/format-staged.gemspec b/format-staged.gemspec index bf7935a..6711751 100644 --- a/format-staged.gemspec +++ b/format-staged.gemspec @@ -17,6 +17,8 @@ Gem::Specification.new do |s| s.license = 'MIT' s.required_ruby_version = '~> 2.7' + s.add_dependency 'colorize' + s.add_development_dependency 'rake', '~> 13.0' s.add_development_dependency 'rubocop', '~> 1.29' s.add_development_dependency 'rubocop-rake', '~> 0.6' diff --git a/lib/format-staged/entry.rb b/lib/format-staged/entry.rb index 3684b7d..4a5962d 100644 --- a/lib/format-staged/entry.rb +++ b/lib/format-staged/entry.rb @@ -1,6 +1,10 @@ # frozen_string_literal: true class FormatStaged + ## + # Entry in the git index. + # + # Data as produced by `git diff-index` class Entry PATTERN = /^ :(?\d+)\s diff --git a/lib/format-staged/io.rb b/lib/format-staged/io.rb index 6e99768..04b633b 100644 --- a/lib/format-staged/io.rb +++ b/lib/format-staged/io.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require 'English' + class FormatStaged def get_output(*args, lines: true, silent: false) puts "> #{args.join(' ')}" if @verbose @@ -30,16 +31,32 @@ class FormatStaged [pid, r] end - def read_output(r, lines: true, silent: false) - result = r.read + def read_output(output, lines: true, silent: false) + result = output.read splits = result.split("\n") if @verbose && !silent splits.each do |line| puts "< #{line}" end end - r.close + output.close lines ? splits : result end + + def fail!(message) + abort "💣 #{message.red}" + end + + def warning(message) + warn "⚠️ #{message.yellow}" + end + + def info(message) + puts message.blue + end + + def verbose_info(message) + puts "ℹ️ #{message}" if verbose + end end diff --git a/lib/format_staged.rb b/lib/format_staged.rb index 2201c21..0cae333 100644 --- a/lib/format_staged.rb +++ b/lib/format_staged.rb @@ -5,21 +5,29 @@ require 'format-staged/version' require 'format-staged/entry' require 'format-staged/io' require 'shellwords' +require 'colorize' +## +# Runs staged changes through a formatting tool class FormatStaged attr_reader :formatter, :patterns, :update, :write, :verbose - def initialize(formatter:, patterns:, update: true, write: true, verbose: true) + def initialize(formatter:, patterns:, update: true, write: true, verbose: true, color_output: nil) @formatter = formatter @patterns = patterns @update = update @write = write @verbose = verbose + + String.disable_colorization = !(color_output || $stdout.isatty) end def run - root = get_output('git', 'rev-parse', '--show-toplevel').first + verbose_info "Finding repository root" + root = get_output('git', 'rev-parse', '--show-toplevel', lines: false).chomp + verbose_info "Repo at #{root}" + verbose_info "Listing staged files" files = get_output('git', 'diff-index', '--cached', '--diff-filter=AM', '--no-renames', 'HEAD') .map { |line| Entry.new(line, root: root) } .reject(&:symlink?) @@ -36,12 +44,12 @@ class FormatStaged return true unless write if new_hash == file.dst_hash - puts "Unchanged #{file.src_path}" + info "Unchanged #{file.src_path}" return false end if object_is_empty new_hash - puts "Skipping #{file.src_path}, formatted file is empty" + info "Skipping #{file.src_path}, formatted file is empty" return false end @@ -51,7 +59,7 @@ class FormatStaged begin patch_working_file file, new_hash rescue StandardError => e - puts "Warning: failed updating #{file.src_path} in working copy: #{e}" + warning "failed updating #{file.src_path} in working copy: #{e}" end end @@ -59,7 +67,7 @@ class FormatStaged end def format_object(file) - puts "Formatting #{file.src_path}" + info "Formatting #{file.src_path}" format_command = formatter.sub('{}', file.src_path.shellescape) @@ -87,6 +95,8 @@ class FormatStaged end def patch_working_file(file, new_hash) + info "Updating working copy" + patch = get_output 'git', 'diff', file.dst_hash, new_hash, lines: false, silent: true patch.gsub! "a/#{file.dst_hash}", "a/#{file.src_path}" patch.gsub! "b/#{new_hash}", "b/#{file.src_path}"