Complete-File
view release on metacpan or search on metacpan
lib/Complete/File.pm view on Meta::CPAN
dep_all => [recurse_matching => ['recurse']],
},
result_naked => 1,
result => {
schema => 'array',
},
};
sub complete_file {
require Complete::Path;
require Encode;
require File::Glob;
my %args = @_;
my $word = $args{word} // "";
my $handle_tilde = $args{handle_tilde} // 1;
my $allow_dot = $args{allow_dot} // 1;
# if word is starts with "~/" or "~foo/" replace it temporarily with user's
# name (so we can restore it back at the end). this is to mimic bash
# support. note that bash does not support case-insensitivity for "foo".
my $result_prefix;
my $starting_path = $args{starting_path} // '.';
if ($handle_tilde && $word =~ s!\A(~[^/]*)/!!) {
$result_prefix = "$1/";
my @dir = File::Glob::bsd_glob($1); # glob will expand ~foo to /home/foo
return [] unless @dir;
$starting_path = Encode::decode('UTF-8', $dir[0]);
} elsif ($allow_dot && $word =~ s!\A((?:\.\.?/+)+|/+)!!) {
# just an optimization to skip sequences of '../'
$starting_path = $1;
$result_prefix = $1;
$starting_path =~ s#/+\z## unless $starting_path =~ m!\A/!;
}
# bail if we don't allow dot and the path contains dot
return [] if !$allow_dot &&
$word =~ m!(?:\A|/)\.\.?(?:\z|/)!;
# prepare list_func
my $list = sub {
my ($path, $intdir, $isint) = @_;
opendir my($dh), $path or return undef;
my @res;
for (sort readdir $dh) {
# skip . and .. if leaf is empty, like in bash
next if ($_ eq '.' || $_ eq '..') && $intdir eq '';
next if $isint && !(-d "$path/$_");
push @res, Encode::decode('UTF-8', $_);
}
\@res;
};
# prepare filter_func
# from the filter option
my $filter;
if ($args{filter} && !ref($args{filter})) {
my @seqs = split /\s*\|\s*/, $args{filter};
$filter = sub {
my $name = shift;
my @st = stat($name) or return 0;
my $mode = $st[2];
my $pass;
SEQ:
for my $seq (@seqs) {
my $neg = sub { $_[0] };
for my $c (split //, $seq) {
if ($c eq '-') { $neg = sub { $_[0] ? 0 : 1 } }
elsif ($c eq 'r') { next SEQ unless $neg->($mode & 0400) }
elsif ($c eq 'w') { next SEQ unless $neg->($mode & 0200) }
elsif ($c eq 'x') { next SEQ unless $neg->($mode & 0100) }
elsif ($c eq 'f') { next SEQ unless $neg->($mode & 0100000)}
elsif ($c eq 'd') { next SEQ unless $neg->($mode & 0040000)}
else {
die "Unknown character in filter: $c (in $seq)";
}
}
$pass = 1; last SEQ;
}
$pass;
};
} elsif ($args{filter} && ref($args{filter}) eq 'CODE') {
$filter = $args{filter};
}
# from the file_regex_filter option
my $filter_fregex;
if ($args{file_regex_filter}) {
$filter_fregex = sub {
my $name = shift;
return 1 if -d $name;
return 0 unless -f _;
return 1 if $name =~ $args{file_regex_filter};
0;
};
}
# from the file_ext_filter option
my $filter_fext;
if ($args{file_ext_filter} && ref $args{file_ext_filter} eq 'Regexp') {
$filter_fext = sub {
my $name = shift;
return 1 if -d $name;
return 0 unless -f _;
my $ext = $name =~ /\.(\w+)\z/ ? $1 : '';
return 1 if $ext =~ $args{file_ext_filter};
0;
};
} elsif ($args{file_ext_filter} && ref $args{file_ext_filter} eq 'ARRAY') {
$filter_fext = sub {
my $name = shift;
return 1 if -d $name;
return 0 unless -f _;
my $ext = $name =~ /\.(\w+)\z/ ? $1 : '';
if ($Complete::Common::OPT_CI) {
$ext = lc($ext);
for my $e (@{ $args{file_ext_filter} }) {
return 1 if $ext eq lc($e);
}
} else {
for my $e (@{ $args{file_ext_filter} }) {
( run in 1.593 second using v1.01-cache-2.11-cpan-39bf76dae61 )