From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on dcvr.yhbt.net X-Spam-Level: X-Spam-ASN: X-Spam-Status: No, score=-4.2 required=3.0 tests=ALL_TRUSTED,BAYES_00, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF shortcircuit=no autolearn=ham autolearn_force=no version=3.4.6 Received: from localhost (dcvr.yhbt.net [127.0.0.1]) by dcvr.yhbt.net (Postfix) with ESMTP id 60A691F428 for ; Thu, 23 Mar 2023 21:45:45 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=80x24.org; s=selector1; t=1679607945; bh=9BliHwbFLW+z1UuJz+iZeg7OH76+MzAOmqkg9WVCKfc=; h=From:To:Subject:Date:From; b=ge/5JMAlYegCS4PD7Gbm9YkynMkjBQQ0MkmpQVV51KTusE6xLp58gFV1QI1S0DHtr KPvQJQjXHvDllJfF68X+NqQwzpNxKOjafAUJu8hGLEO6aRPLmnyzXI/U7DKyy97aJ9 5scEkt5j7uVHTniQS6/sSBjllxdoeHlNdlsRYDhU= From: Eric Wong To: meta@public-inbox.org Subject: [PATCH] lei: improve bash completion involving colons Date: Thu, 23 Mar 2023 21:45:45 +0000 Message-Id: <20230323214545.3022890-1-e@80x24.org> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit List-Id: This fixes completions of labels (`+L:' for `lei import' and `L:' for `lei q') so they can appear anywhere in the command-line. I mainly wanted this for `lei import $URL +L:label', but this also fixes `lei forget-external' completions for URLs (which involve colons). --- contrib/completion/lei-completion.bash | 15 ++++++++----- lib/PublicInbox/LeiExternal.pm | 31 +++++++++++--------------- lib/PublicInbox/LeiForgetExternal.pm | 8 ++----- lib/PublicInbox/LeiImport.pm | 22 +++++++++++------- lib/PublicInbox/LeiQuery.pm | 2 ++ 5 files changed, 40 insertions(+), 38 deletions(-) diff --git a/contrib/completion/lei-completion.bash b/contrib/completion/lei-completion.bash index 5c137e68..b86afa2c 100644 --- a/contrib/completion/lei-completion.bash +++ b/contrib/completion/lei-completion.bash @@ -1,16 +1,19 @@ -# Copyright (C) 2020-2021 all contributors +# Copyright (C) all contributors # License: AGPL-3.0+ # preliminary bash completion support for lei (Local Email Interface) # Needs a lot of work, see `lei__complete' in lib/PublicInbox::LEI.pm _lei() { local wordlist="$(lei _complete ${COMP_WORDS[@]})" - case $wordlist in - *':'* | *'='* | '//'*) compopt -o nospace ;; - *) compopt +o nospace ;; # the default - esac wordlist="${wordlist//;/\\\\;}" # escape ';' for ';UIDVALIDITY' and such - COMPREPLY=($(compgen -W "$wordlist" -- "${COMP_WORDS[COMP_CWORD]}")) + + local word="${COMP_WORDS[COMP_CWORD]}" + if test "$word" = ':' && test $COMP_CWORD -ge 1 + then + COMPREPLY=($(compgen -W "$wordlist" --)) + else + COMPREPLY=($(compgen -W "$wordlist" -- "$word")) + fi return 0 } complete -o default -o bashdefault -F _lei lei diff --git a/lib/PublicInbox/LeiExternal.pm b/lib/PublicInbox/LeiExternal.pm index 3e2a2288..31b9bd1e 100644 --- a/lib/PublicInbox/LeiExternal.pm +++ b/lib/PublicInbox/LeiExternal.pm @@ -86,39 +86,34 @@ sub canonicalize_excludes { # returns an anonymous sub which returns an array of potential results sub complete_url_prepare { my $argv = $_[-1]; # $_[0] may be $lei - # Workaround bash word-splitting URLs to ['https', ':', '//' ...] - # Maybe there's a better way to go about this in - # contrib/completion/lei-completion.bash - my $re = ''; - my $cur = pop(@$argv) // ''; + # 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 eq ':' && @x) { + if ($cur =~ /\A[:;=]\z/) { # COMP_WORDBREAKS + URL union push @x, $cur; $cur = ''; } - while (@x > 2 && $x[0] !~ /\A(?:http|nntp|imap)s?\z/i && - $x[1] ne ':') { - shift @x; + while (@x && $pfx !~ m!\A(?: (?:[\+\-]?(?:L|kw):) | + (?:(?:imap|nntp|http)s?:) | + (?:--\w?\z)|(?:-\w?\z) )!x) { + $pfx = pop(@x).$pfx; } - if (@x >= 2) { # qw(https : hostname : 443) or qw(http :) - $re = join('', @x); - } else { # just filter out the flags and hope for the best - $re = join('', grep(!/^-/, @$argv)); - } - $re = quotemeta($re); } + 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; - $_[0] =~ s!;!\\;!g; # only return the part specified on the CLI # don't duplicate if already 100% completed - $_[0] =~ /\A$re(\Q$cur\E.*)/ ? ($cur eq $1 ? () : $1) : () + $_[0] =~ $re ? ($cur eq $1 ? () : $1) : () }; - wantarray ? ($re, $cur, $match_cb) : $match_cb; + wantarray ? ($pfx, $cur, $match_cb) : $match_cb; } 1; diff --git a/lib/PublicInbox/LeiForgetExternal.pm b/lib/PublicInbox/LeiForgetExternal.pm index 07f0ac80..39bfc60b 100644 --- a/lib/PublicInbox/LeiForgetExternal.pm +++ b/lib/PublicInbox/LeiForgetExternal.pm @@ -32,14 +32,10 @@ sub lei_forget_external { sub _complete_forget_external { my ($lei, @argv) = @_; my $cfg = $lei->_lei_cfg or return (); - my ($cur, $re, $match_cb) = $lei->complete_url_prepare(\@argv); - # FIXME: bash completion off "http:" or "https:" when the last - # character is a colon doesn't work properly even if we're - # returning "//$HTTP_HOST/$PATH_INFO/", not sure why, could - # be a bash issue. + my ($pfx, $cur, $match_cb) = $lei->complete_url_prepare(\@argv); map { $match_cb->(substr($_, length('external.'))); - } grep(/\Aexternal\.$re\Q$cur/, @{$cfg->{-section_order}}); + } grep(/\Aexternal\.\Q$pfx$cur/, @{$cfg->{-section_order}}); } 1; diff --git a/lib/PublicInbox/LeiImport.pm b/lib/PublicInbox/LeiImport.pm index 2d91e4c4..9053048a 100644 --- a/lib/PublicInbox/LeiImport.pm +++ b/lib/PublicInbox/LeiImport.pm @@ -115,18 +115,24 @@ sub lei_import { # the main "lei import" method sub _complete_import { my ($lei, @argv) = @_; - my ($re, $cur, $match_cb) = $lei->complete_url_prepare(\@argv); - my @k = $lei->url_folder_cache->keys($argv[-1] // undef, 1); + my $has_arg = @argv; + my ($pfx, $cur, $match_cb) = $lei->complete_url_prepare(\@argv); + my @try = $has_arg ? ($pfx.$cur, $argv[-1]) : ($argv[-1]); + push(@try, undef) if defined $try[-1]; + my (@f, @k); + for (@try) { + @k = $lei->url_folder_cache->keys($_, 1) and last; + } my @L = eval { $lei->_lei_store->search->all_terms('L') }; push(@k, map { "+L:$_" } @L); - my @m = map { $match_cb->($_) } @k; - my %f = map { $_ => 1 } (@m ? @m : @k); if (my $lms = $lei->lms) { - @k = $lms->folders($argv[-1] // undef, 1); - @m = map { $match_cb->($_) } @k; - if (@m) { @f{@m} = @m } else { @f{@k} = @k } + for (@try) { + @f = $lms->folders($_, 1) and last; + } + push @k, @f; } - keys %f; + my @m = map { $match_cb->($_) } @k; + @m ? @m : @k; } no warnings 'once'; diff --git a/lib/PublicInbox/LeiQuery.pm b/lib/PublicInbox/LeiQuery.pm index 358574ea..3337e5d4 100644 --- a/lib/PublicInbox/LeiQuery.pm +++ b/lib/PublicInbox/LeiQuery.pm @@ -173,6 +173,8 @@ no query allowed on command-line with --stdin # shell completion helper called by lei__complete sub _complete_q { my ($self, @argv) = @_; + join('', @argv) =~ /\bL:\S*\z/ and + return eval { $self->_lei_store->search->all_terms('L') }; my @cur; my $cb = $self->lazy_cb(qw(forget-external _complete_)); while (@argv) {