mirror of
https://github.com/athityakumar/colorls.git
synced 2024-09-12 05:31:14 +02:00
Group and process arguments in groups of files and directories
Fixes #233
This commit is contained in:
parent
4612549bc2
commit
0048e3883a
7 changed files with 179 additions and 108 deletions
|
@ -31,34 +31,38 @@ module ColorLS
|
|||
init_long_format(mode,show_group,show_user)
|
||||
@tree = {mode: mode == :tree, depth: tree_depth}
|
||||
@horizontal = mode == :horizontal
|
||||
process_git_status_details(git_status)
|
||||
@git_status = init_git_status(git_status)
|
||||
|
||||
init_colors colors
|
||||
|
||||
init_icons
|
||||
end
|
||||
|
||||
def ls(input)
|
||||
@input = (+input).force_encoding(ColorLS.file_encoding)
|
||||
@contents = init_contents(@input)
|
||||
def ls_dir(info)
|
||||
if @tree[:mode]
|
||||
print "\n"
|
||||
return tree_traverse(info.path, 0, 1, 2)
|
||||
end
|
||||
|
||||
@contents = Dir.entries(info.path, encoding: ColorLS.file_encoding)
|
||||
|
||||
filter_hidden_contents
|
||||
|
||||
@contents.map! { |e| FileInfo.dir_entry(info.path, e, link_info: @long) }
|
||||
|
||||
filter_contents if @show
|
||||
sort_contents if @sort
|
||||
group_contents if @group
|
||||
|
||||
return print "\n Nothing to show here\n".colorize(@colors[:empty]) if @contents.empty?
|
||||
|
||||
layout = case
|
||||
when @tree[:mode]
|
||||
print "\n"
|
||||
return tree_traverse(@input, 0, 1, 2)
|
||||
when @horizontal
|
||||
HorizontalLayout.new(@contents, item_widths, ColorLS.screen_width)
|
||||
when @one_per_line || @long
|
||||
SingleColumnLayout.new(@contents)
|
||||
else
|
||||
VerticalLayout.new(@contents, item_widths, ColorLS.screen_width)
|
||||
end
|
||||
ls
|
||||
end
|
||||
|
||||
layout.each_line do |line, widths|
|
||||
ls_line(line, widths)
|
||||
end
|
||||
def ls_files(files)
|
||||
@contents = files
|
||||
|
||||
ls
|
||||
end
|
||||
|
||||
def display_report
|
||||
|
@ -72,6 +76,23 @@ module ColorLS
|
|||
|
||||
private
|
||||
|
||||
def ls
|
||||
init_column_lengths
|
||||
|
||||
layout = case
|
||||
when @horizontal
|
||||
HorizontalLayout.new(@contents, item_widths, ColorLS.screen_width)
|
||||
when @one_per_line || @long
|
||||
SingleColumnLayout.new(@contents)
|
||||
else
|
||||
VerticalLayout.new(@contents, item_widths, ColorLS.screen_width)
|
||||
end
|
||||
|
||||
layout.each_line do |line, widths|
|
||||
ls_line(line, widths)
|
||||
end
|
||||
end
|
||||
|
||||
def init_colors(colors)
|
||||
@colors = colors
|
||||
@modes = Hash.new do |hash, key|
|
||||
|
@ -85,12 +106,26 @@ module ColorLS
|
|||
end
|
||||
end
|
||||
|
||||
def init_long_format(mode,show_group,show_user)
|
||||
def init_long_format(mode, show_group, show_user)
|
||||
@long = mode == :long
|
||||
@show_group = show_group
|
||||
@show_user = show_user
|
||||
end
|
||||
|
||||
def init_git_status(show_git)
|
||||
return {}.freeze unless show_git
|
||||
|
||||
# stores git status information per directory
|
||||
Hash.new do |hash, key|
|
||||
path = File.absolute_path key.parent
|
||||
if hash.key? path
|
||||
hash[path]
|
||||
else
|
||||
hash[path] = Git.status(path)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# how much characters an item occupies besides its name
|
||||
CHARS_PER_ITEM = 12
|
||||
|
||||
|
@ -98,28 +133,6 @@ module ColorLS
|
|||
@contents.map { |item| Unicode::DisplayWidth.of(item.show) + CHARS_PER_ITEM }
|
||||
end
|
||||
|
||||
def init_contents(path)
|
||||
info = FileInfo.new(path, link_info: @long)
|
||||
|
||||
if info.directory?
|
||||
@contents = Dir.entries(path, encoding: ColorLS.file_encoding)
|
||||
|
||||
filter_hidden_contents
|
||||
|
||||
@contents.map! { |e| FileInfo.new(File.join(path, e), link_info: @long) }
|
||||
|
||||
filter_contents if @show
|
||||
sort_contents if @sort
|
||||
group_contents if @group
|
||||
else
|
||||
@contents = [info]
|
||||
end
|
||||
|
||||
init_column_lengths
|
||||
|
||||
@contents
|
||||
end
|
||||
|
||||
def filter_hidden_contents
|
||||
@contents -= %w[. ..] unless @all
|
||||
@contents.keep_if { |x| !x.start_with? '.' } unless @all || @almost_all
|
||||
|
@ -230,42 +243,32 @@ module ColorLS
|
|||
mtime.colorize(@colors[:no_modifier])
|
||||
end
|
||||
|
||||
def process_git_status_details(git_status)
|
||||
@git_status = case
|
||||
when !git_status then nil
|
||||
when File.directory?(@input) then Git.status(@input)
|
||||
else Git.status(File.dirname(@input))
|
||||
end
|
||||
end
|
||||
|
||||
def git_info(content)
|
||||
return '' unless @git_status
|
||||
return '' unless (status = @git_status[content])
|
||||
|
||||
if content.directory?
|
||||
git_dir_info(content.name)
|
||||
git_dir_info(content, status)
|
||||
else
|
||||
git_file_info(content.name)
|
||||
git_file_info(status[content.name])
|
||||
end
|
||||
end
|
||||
|
||||
def git_file_info(path)
|
||||
unless @git_status[path]
|
||||
return ' ✓ '
|
||||
.encode(Encoding.default_external, undef: :replace, replace: '=')
|
||||
.colorize(@colors[:unchanged])
|
||||
end
|
||||
def git_file_info(status)
|
||||
return Git.colored_status_symbols(status, @colors) if status
|
||||
|
||||
Git.colored_status_symbols(@git_status[path], @colors)
|
||||
' ✓ '
|
||||
.encode(Encoding.default_external, undef: :replace, replace: '=')
|
||||
.colorize(@colors[:unchanged])
|
||||
end
|
||||
|
||||
def git_dir_info(path)
|
||||
modes = if path == '.'
|
||||
Set.new(@git_status.values).flatten
|
||||
def git_dir_info(content, status)
|
||||
modes = if content.path == '.'
|
||||
Set.new(status.values).flatten
|
||||
else
|
||||
@git_status[path]
|
||||
status[content.name]
|
||||
end
|
||||
|
||||
if modes.empty? && Dir.empty?(File.join(@input, path))
|
||||
if modes.empty? && Dir.empty?(content.path)
|
||||
' '
|
||||
else
|
||||
Git.colored_status_symbols(modes, @colors)
|
||||
|
@ -300,12 +303,12 @@ module ColorLS
|
|||
str.encode(Encoding.default_external, undef: :replace, replace: '')
|
||||
end
|
||||
|
||||
def fetch_string(path, content, key, color, increment)
|
||||
def fetch_string(content, key, color, increment)
|
||||
@count[increment] += 1
|
||||
value = increment == :folders ? @folders[key] : @files[key]
|
||||
logo = value.gsub(/\\u[\da-f]{4}/i) { |m| [m[-4..-1].to_i(16)].pack('U') }
|
||||
name = content.show
|
||||
name = make_link(path, name) if @hyperlink
|
||||
name = make_link(content) if @hyperlink
|
||||
name += content.directory? ? '/' : ' '
|
||||
entry = "#{out_encode(logo)} #{out_encode(name)}"
|
||||
entry = entry.bright if !content.directory? && content.executable?
|
||||
|
@ -317,7 +320,7 @@ module ColorLS
|
|||
padding = 0
|
||||
line = +''
|
||||
chunk.each_with_index do |content, i|
|
||||
entry = fetch_string(@input, content, *options(content))
|
||||
entry = fetch_string(content, *options(content))
|
||||
line << ' ' * padding
|
||||
line << ' ' << entry.encode(Encoding.default_external, undef: :replace)
|
||||
padding = widths[i] - Unicode::DisplayWidth.of(content.show) - CHARS_PER_ITEM
|
||||
|
@ -354,12 +357,26 @@ module ColorLS
|
|||
[key, color, group]
|
||||
end
|
||||
|
||||
def tree_contents(path)
|
||||
@contents = Dir.entries(path, encoding: ColorLS.file_encoding)
|
||||
|
||||
filter_hidden_contents
|
||||
|
||||
@contents.map! { |e| FileInfo.dir_entry(path, e, link_info: @long) }
|
||||
|
||||
filter_contents if @show
|
||||
sort_contents if @sort
|
||||
group_contents if @group
|
||||
|
||||
@contents
|
||||
end
|
||||
|
||||
def tree_traverse(path, prespace, depth, indent)
|
||||
contents = init_contents(path)
|
||||
contents = tree_contents(path)
|
||||
contents.each do |content|
|
||||
icon = content == contents.last || content.directory? ? ' └──' : ' ├──'
|
||||
print tree_branch_preprint(prespace, indent, icon).colorize(@colors[:tree])
|
||||
print " #{fetch_string(path, content, *options(content))} \n"
|
||||
print " #{fetch_string(content, *options(content))} \n"
|
||||
next unless content.directory?
|
||||
|
||||
tree_traverse("#{path}/#{content}", prespace + indent, depth + 1, indent) if keep_going(depth)
|
||||
|
@ -376,9 +393,9 @@ module ColorLS
|
|||
' │ ' * (prespace/indent) + prespace_icon + '─' * indent
|
||||
end
|
||||
|
||||
def make_link(path, name)
|
||||
uri = Addressable::URI.convert_path(File.absolute_path(File.join(path, name)))
|
||||
"\033]8;;#{uri}\007#{name}\033]8;;\007"
|
||||
def make_link(content)
|
||||
uri = Addressable::URI.convert_path(File.absolute_path(content.path))
|
||||
"\033]8;;#{uri}\007#{content.name}\033]8;;\007"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -9,19 +9,24 @@ module ColorLS
|
|||
@@users = {} # rubocop:disable Style/ClassVars
|
||||
@@groups = {} # rubocop:disable Style/ClassVars
|
||||
|
||||
attr_reader :stats, :name
|
||||
|
||||
def initialize(path, link_info: true)
|
||||
@name = File.basename(path)
|
||||
@stats = File.lstat(path)
|
||||
attr_reader :stats, :name, :path, :parent
|
||||
|
||||
def initialize(name:, parent:, path: nil, link_info: true)
|
||||
@name = name
|
||||
@parent = parent
|
||||
@path = path.nil? ? File.join(parent, name) : path
|
||||
@stats = File.lstat(@path)
|
||||
@show_name = nil
|
||||
|
||||
handle_symlink(path) if link_info && @stats.symlink?
|
||||
handle_symlink(@path) if link_info && @stats.symlink?
|
||||
end
|
||||
|
||||
def self.info(path)
|
||||
FileInfo.new(path)
|
||||
def self.info(path, link_info: true)
|
||||
FileInfo.new(name: File.basename(path), parent: File.dirname(path), path: path, link_info: link_info)
|
||||
end
|
||||
|
||||
def self.dir_entry(dir, child, link_info: true)
|
||||
FileInfo.new(name: child, parent: dir, link_info: link_info)
|
||||
end
|
||||
|
||||
def show
|
||||
|
|
|
@ -12,6 +12,7 @@ module ColorLS
|
|||
|
||||
@opts = default_opts
|
||||
@show_report = false
|
||||
@exit_status_code = 0
|
||||
|
||||
parse_options
|
||||
|
||||
|
@ -54,29 +55,42 @@ module ColorLS
|
|||
warn "WARN: #{e}, check your locale settings"
|
||||
end
|
||||
|
||||
def group_files_and_directories
|
||||
infos = @args.flat_map do |arg|
|
||||
FileInfo.info(arg)
|
||||
rescue Errno::ENOENT
|
||||
$stderr.puts "colorls: Specified path '#{arg}' doesn't exist.".colorize(:red)
|
||||
@exit_status_code = 2
|
||||
[]
|
||||
rescue SystemCallError => e
|
||||
$stderr.puts "#{path}: #{e}".colorize(:red)
|
||||
@exit_status_code = 2
|
||||
[]
|
||||
end
|
||||
|
||||
infos.group_by(&:directory?).values_at(true, false)
|
||||
end
|
||||
|
||||
def process_args
|
||||
core = Core.new(**@opts)
|
||||
|
||||
exit_status_code = 0
|
||||
directories, files = group_files_and_directories
|
||||
|
||||
@args.sort!.each_with_index do |path, i|
|
||||
unless File.exist?(path)
|
||||
$stderr.puts "\n Specified path '#{path}' doesn't exist.".colorize(:red)
|
||||
exit_status_code = 2
|
||||
next
|
||||
end
|
||||
core.ls_files(files) unless files.nil?
|
||||
|
||||
puts '' if i.positive?
|
||||
puts "\n#{path}:" if Dir.exist?(path) && @args.size > 1
|
||||
directories&.sort_by! do |a|
|
||||
CLocale.strxfrm(a.name)
|
||||
end&.each do |dir|
|
||||
puts "\n#{dir.show}:" if @args.size > 1
|
||||
|
||||
core.ls(path)
|
||||
core.ls_dir(dir)
|
||||
rescue SystemCallError => e
|
||||
$stderr.puts "#{path}: #{e}".colorize(:red)
|
||||
$stderr.puts "#{dir}: #{e}".colorize(:red)
|
||||
end
|
||||
|
||||
core.display_report if @show_report
|
||||
|
||||
exit_status_code
|
||||
@exit_status_code
|
||||
end
|
||||
|
||||
def default_opts
|
||||
|
|
|
@ -10,18 +10,23 @@ module ColorLS
|
|||
|
||||
return unless success
|
||||
|
||||
prefix = Pathname.new(prefix)
|
||||
prefix_path = Pathname.new(prefix)
|
||||
|
||||
git_status = Hash.new { |hash, key| hash[key] = Set.new }
|
||||
|
||||
git_subdir_status(repo_path) do |mode, file|
|
||||
path = Pathname.new(file).relative_path_from(prefix)
|
||||
|
||||
git_status[path.descend.first.cleanpath.to_s].add(mode)
|
||||
if file == prefix
|
||||
git_status.default = Set[mode].freeze
|
||||
else
|
||||
path = Pathname.new(file).relative_path_from(prefix_path)
|
||||
git_status[path.descend.first.cleanpath.to_s].add(mode)
|
||||
end
|
||||
end
|
||||
|
||||
warn "git status failed in #{repo_path}" unless $CHILD_STATUS.success?
|
||||
|
||||
git_status
|
||||
git_status.default = Set.new.freeze if git_status.default.nil?
|
||||
git_status.freeze
|
||||
end
|
||||
|
||||
def self.colored_status_symbols(modes, colors)
|
||||
|
|
|
@ -17,6 +17,7 @@ RSpec.describe ColorLS::Core do
|
|||
directory?: true,
|
||||
owner: 'user',
|
||||
name: imagenes,
|
||||
path: '.',
|
||||
show: imagenes,
|
||||
nlink: 1,
|
||||
size: 128,
|
||||
|
@ -58,10 +59,9 @@ RSpec.describe ColorLS::Core do
|
|||
|
||||
allow(::Dir).to receive(:entries).and_return([camera])
|
||||
|
||||
allow(ColorLS::FileInfo).to receive(:new).and_return(dir_info)
|
||||
allow(ColorLS::FileInfo).to receive(:new).with(File.join(imagenes, camera), link_info: false) { file_info }
|
||||
allow(ColorLS::FileInfo).to receive(:new).and_return(file_info)
|
||||
|
||||
expect { subject.ls('Imágenes') }.to output(/mara/).to_stdout
|
||||
expect { subject.ls_dir(dir_info) }.to output(/mara/).to_stdout
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -94,7 +94,12 @@ RSpec.describe ColorLS::Flags do
|
|||
executable?: false
|
||||
)
|
||||
|
||||
allow(ColorLS::FileInfo).to receive(:new).with("#{FIXTURES}/a.txt", link_info: true) { file_info }
|
||||
allow(ColorLS::FileInfo).to receive(:new).with(
|
||||
path: File.join(FIXTURES, 'a.txt'),
|
||||
parent: FIXTURES,
|
||||
name: 'a.txt',
|
||||
link_info: true
|
||||
) { file_info }
|
||||
|
||||
expect { subject }.to output(/r-Sr-Sr-T .* a.txt/mx).to_stdout
|
||||
end
|
||||
|
@ -123,7 +128,12 @@ RSpec.describe ColorLS::Flags do
|
|||
executable?: false
|
||||
)
|
||||
|
||||
allow(ColorLS::FileInfo).to receive(:new).with("#{FIXTURES}/a.txt", link_info: true) { file_info }
|
||||
allow(ColorLS::FileInfo).to receive(:new).with(
|
||||
path: File.join(FIXTURES, 'a.txt'),
|
||||
parent: FIXTURES,
|
||||
name: 'a.txt',
|
||||
link_info: true
|
||||
) { file_info }
|
||||
|
||||
expect { subject }.to output(/\S+\s+ 5 .* a.txt/mx).to_stdout
|
||||
end
|
||||
|
@ -354,7 +364,7 @@ RSpec.describe ColorLS::Flags do
|
|||
let(:args) { ['not_exist_file'] }
|
||||
|
||||
it 'exits with status code 2' do # rubocop:todo RSpec/MultipleExpectations
|
||||
expect { subject }.to output(/ Specified path 'not_exist_file' doesn't exist./).to_stderr
|
||||
expect { subject }.to output(/colorls: Specified path 'not_exist_file' doesn't exist./).to_stderr
|
||||
expect(subject).to eq 2
|
||||
end
|
||||
end
|
||||
|
@ -386,7 +396,12 @@ RSpec.describe ColorLS::Flags do
|
|||
executable?: false
|
||||
)
|
||||
|
||||
allow(ColorLS::FileInfo).to receive(:new).with("#{FIXTURES}/a.txt", link_info: true) { file_info }
|
||||
allow(ColorLS::FileInfo).to receive(:new).with(
|
||||
path: File.join(FIXTURES, 'a.txt'),
|
||||
parent: FIXTURES,
|
||||
name: 'a.txt',
|
||||
link_info: true
|
||||
) { file_info }
|
||||
end
|
||||
|
||||
it 'lists without group info' do
|
||||
|
@ -425,7 +440,12 @@ RSpec.describe ColorLS::Flags do
|
|||
executable?: false
|
||||
)
|
||||
|
||||
allow(ColorLS::FileInfo).to receive(:new).with("#{FIXTURES}/a.txt", link_info: true) { file_info }
|
||||
allow(ColorLS::FileInfo).to receive(:new).with(
|
||||
path: File.join(FIXTURES, 'a.txt'),
|
||||
parent: FIXTURES,
|
||||
name: 'a.txt',
|
||||
link_info: true
|
||||
) { file_info }
|
||||
end
|
||||
|
||||
it 'lists with group info' do
|
||||
|
@ -464,7 +484,12 @@ RSpec.describe ColorLS::Flags do
|
|||
executable?: false
|
||||
)
|
||||
|
||||
allow(ColorLS::FileInfo).to receive(:new).with("#{FIXTURES}/a.txt", link_info: true) { file_info }
|
||||
allow(ColorLS::FileInfo).to receive(:new).with(
|
||||
path: File.join(FIXTURES, 'a.txt'),
|
||||
parent: FIXTURES,
|
||||
name: 'a.txt',
|
||||
link_info: true
|
||||
) { file_info }
|
||||
end
|
||||
|
||||
it 'lists without group info' do
|
||||
|
@ -503,7 +528,12 @@ RSpec.describe ColorLS::Flags do
|
|||
executable?: false
|
||||
)
|
||||
|
||||
allow(ColorLS::FileInfo).to receive(:new).with("#{FIXTURES}/a.txt", link_info: true) { file_info }
|
||||
allow(ColorLS::FileInfo).to receive(:new).with(
|
||||
path: File.join(FIXTURES, 'a.txt'),
|
||||
parent: FIXTURES,
|
||||
name: 'a.txt',
|
||||
link_info: true
|
||||
) { file_info }
|
||||
end
|
||||
|
||||
it 'lists without group info' do
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# frozen_string_literal: true
|
||||
# frozen_string_literal: false
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
|
|
Loading…
Reference in a new issue