1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
| | # Copyright (C) all contributors <meta@public-inbox.org>
# License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt>
# *-external commands of lei
package PublicInbox::LeiExternal;
use strict;
use v5.10.1;
use PublicInbox::Config qw(glob2re);
sub externals_each {
my ($self, $cb, @arg) = @_;
my $cfg = $self->_lei_cfg;
my %boost;
for my $sec (grep(/\Aexternal\./, @{$cfg->{-section_order}})) {
my $loc = substr($sec, length('external.'));
$boost{$loc} = $cfg->{"$sec.boost"};
}
return \%boost if !wantarray && !$cb;
# highest boost first, but stable for alphabetic tie break
use sort 'stable';
my @order = sort { $boost{$b} <=> $boost{$a} } sort keys %boost;
if (ref($cb) eq 'CODE') {
for my $loc (@order) {
$cb->(@arg, $loc, $boost{$loc});
}
} elsif (ref($cb) eq 'HASH') {
%$cb = %boost;
}
@order; # scalar or array
}
sub ext_canonicalize {
my $location = $_[-1]; # $_[0] may be $lei
if ($location !~ m!\Ahttps?://!) {
PublicInbox::Config::rel2abs_collapsed($location);
} else {
require URI;
my $uri = URI->new($location)->canonical;
my $path = $uri->path . '/';
$path =~ tr!/!/!s; # squeeze redundant '/'
$uri->path($path);
$uri->as_string;
}
}
# get canonicalized externals list matching $loc
# $is_exclude denotes it's for --exclude
# otherwise it's for --only/--include is assumed
sub get_externals {
my ($self, $loc, $is_exclude) = @_;
return (ext_canonicalize($loc)) if -e $loc;
my @m;
my @cur = externals_each($self);
my $do_glob = !$self->{opt}->{globoff}; # glob by default
if ($do_glob && (my $re = glob2re($loc))) {
@m = grep(m!$re/?\z!, @cur);
return @m if scalar(@m);
} elsif (index($loc, '/') < 0) { # exact basename match:
@m = grep(m!/\Q$loc\E/?\z!, @cur);
return @m if scalar(@m) == 1;
} elsif ($is_exclude) { # URL, maybe:
my $canon = ext_canonicalize($loc);
@m = grep(m!\A\Q$canon\E\z!, @cur);
return @m if scalar(@m) == 1;
} else { # URL:
return (ext_canonicalize($loc));
}
if (scalar(@m) == 0) {
die "`$loc' is unknown\n";
} else {
die("`$loc' is ambiguous:\n", map { "\t$_\n" } @m, "\n");
}
}
sub canonicalize_excludes {
my ($lei, $excludes) = @_;
my %x;
for my $loc (@$excludes) {
my @l = get_externals($lei, $loc, 1);
$x{$_} = 1 for @l;
}
\%x;
}
# returns an anonymous sub which returns an array of potential results
sub complete_url_prepare {
my $argv = $_[-1]; # $_[0] may be $lei
# Workaround bash default COMP_WORDBREAKS splitting URLs to
# ['https', ':', '//', ...]. COMP_WORDBREAKS is global for all
# completions loaded, not just ours, so we can't change it.
# cf. contrib/completion/lei-completion.bash
my ($pfx, $cur) = ('', pop(@$argv) // '');
if (@$argv) {
my @x = @$argv;
if ($cur =~ /\A[:;=]\z/) { # COMP_WORDBREAKS + URL union
push @x, $cur;
$cur = '';
}
while (@x && $pfx !~ m!\A(?: (?:[\+\-]?(?:L|kw):) |
(?:(?:imap|nntp|http)s?:) |
(?:--\w?\z)|(?:-\w?\z) )!x) {
$pfx = pop(@x).$pfx;
}
}
my $re = qr!\A\Q$pfx\E(\Q$cur\E.*)!;
my $match_cb = sub {
# the "//;" here (for AUTH=ANONYMOUS) interacts badly with
# bash tab completion, strip it out for now since our commands
# work w/o it. Not sure if there's a better solution...
$_[0] =~ s!//;AUTH=ANONYMOUS\@!//!i;
# only return the part specified on the CLI
# don't duplicate if already 100% completed
$_[0] =~ $re ? ($cur eq $1 ? () : $1) : ()
};
wantarray ? ($pfx, $cur, $match_cb) : $match_cb;
}
1;
|