From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on dcvr.yhbt.net X-Spam-Level: X-Spam-ASN: X-Spam-Status: No, score=-4.0 required=3.0 tests=ALL_TRUSTED,BAYES_00 shortcircuit=no autolearn=ham autolearn_force=no version=3.4.2 Received: from localhost (dcvr.yhbt.net [127.0.0.1]) by dcvr.yhbt.net (Postfix) with ESMTP id CF4DB1FC0D for ; Tue, 21 Sep 2021 07:41:59 +0000 (UTC) From: Eric Wong To: meta@public-inbox.org Subject: [PATCH 08/12] lei: various completion improvements Date: Tue, 21 Sep 2021 07:41:55 +0000 Message-Id: <20210921074159.20052-9-e@80x24.org> In-Reply-To: <20210921074159.20052-1-e@80x24.org> References: <20210921074159.20052-1-e@80x24.org> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit List-Id: "lei export-kw" no longer completes for anonymous sources. More commands use "lei refresh-mail-sync" as a basis for their completion work, as well. ";AUTH=ANONYMOUS@" is stripped from completions since it was preventing bash completion from working on AUTH=ANONYMOUS IMAP URLs. I'm not sure if there's a better way, but all of our code works fine without specifying AUTH=ANONYMOUS as a command-line arg. Finally, we fallback to using more candidates if none can be found, allowing multiple URLs to be completed. --- lib/PublicInbox/LeiExportKw.pm | 8 ++++++-- lib/PublicInbox/LeiExternal.pm | 5 +++++ lib/PublicInbox/LeiForgetMailSync.pm | 5 +++-- lib/PublicInbox/LeiImport.pm | 12 +++++++----- lib/PublicInbox/LeiInspect.pm | 7 +++---- lib/PublicInbox/LeiLcat.pm | 7 +++---- lib/PublicInbox/LeiLsMailSource.pm | 11 ++++++----- lib/PublicInbox/LeiMailSync.pm | 13 ++++++++----- lib/PublicInbox/LeiRefreshMailSync.pm | 12 ++++++++++-- lib/PublicInbox/LeiTag.pm | 14 ++++++++------ lib/PublicInbox/SharedKV.pm | 19 +++++++++++++------ 11 files changed, 72 insertions(+), 41 deletions(-) diff --git a/lib/PublicInbox/LeiExportKw.pm b/lib/PublicInbox/LeiExportKw.pm index d5533a2a..cea9beeb 100644 --- a/lib/PublicInbox/LeiExportKw.pm +++ b/lib/PublicInbox/LeiExportKw.pm @@ -127,9 +127,13 @@ EOM sub _complete_export_kw { my ($lei, @argv) = @_; - my $lms = $lei->lms or return; + my $lms = $lei->lms or return (); my $match_cb = $lei->complete_url_prepare(\@argv); - map { $match_cb->($_) } $lms->folders; + # filter-out read-only sources: + my @k = grep(!m!(?://;AUTH=ANONYMOUS\@|\A(?:nntps?|s?news)://)!, + $lms->folders($argv[-1], 1)); + my @m = map { $match_cb->($_) } @k; + @m ? @m : @k; } no warnings 'once'; diff --git a/lib/PublicInbox/LeiExternal.pm b/lib/PublicInbox/LeiExternal.pm index d802f0e2..f8e610ca 100644 --- a/lib/PublicInbox/LeiExternal.pm +++ b/lib/PublicInbox/LeiExternal.pm @@ -241,6 +241,11 @@ sub complete_url_prepare { $re = quotemeta($re); } 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) : () diff --git a/lib/PublicInbox/LeiForgetMailSync.pm b/lib/PublicInbox/LeiForgetMailSync.pm index d85616cc..762910ed 100644 --- a/lib/PublicInbox/LeiForgetMailSync.pm +++ b/lib/PublicInbox/LeiForgetMailSync.pm @@ -10,7 +10,7 @@ package PublicInbox::LeiForgetMailSync; use strict; use v5.10.1; -use PublicInbox::LeiExportKw; +use PublicInbox::LeiRefreshMailSync; sub lei_forget_mail_sync { my ($lei, @folders) = @_; @@ -20,6 +20,7 @@ sub lei_forget_mail_sync { $lms->forget_folders(@folders); } -*_complete_forget_mail_sync = \&PublicInbox::LeiExportKw::_complete_export_kw; +*_complete_forget_mail_sync = + \&PublicInbox::LeiRefreshMailSync::_complete_refresh_mail_sync; 1; diff --git a/lib/PublicInbox/LeiImport.pm b/lib/PublicInbox/LeiImport.pm index 3c30db8d..397292d4 100644 --- a/lib/PublicInbox/LeiImport.pm +++ b/lib/PublicInbox/LeiImport.pm @@ -121,12 +121,14 @@ sub lei_import { # the main "lei import" method sub _complete_import { my ($lei, @argv) = @_; - my $match_cb = $lei->complete_url_prepare(\@argv); - my @m = map { $match_cb->($_) } $lei->url_folder_cache->keys; - my %f = map { $_ => 1 } @m; + my ($re, $cur, $match_cb) = $lei->complete_url_prepare(\@argv); + my @k = $lei->url_folder_cache->keys($argv[-1], 1); + my @m = map { $match_cb->($_) } @k; + my %f = map { $_ => 1 } (@m ? @m : @k); if (my $lms = $lei->lms) { - @m = map { $match_cb->($_) } $lms->folders; - @f{@m} = @m; + @k = $lms->folders($argv[-1], 1); + @m = map { $match_cb->($_) } @k; + if (@m) { @f{@m} = @m } else { @f{@k} = @k } } keys %f; } diff --git a/lib/PublicInbox/LeiInspect.pm b/lib/PublicInbox/LeiInspect.pm index 8e128580..2158b996 100644 --- a/lib/PublicInbox/LeiInspect.pm +++ b/lib/PublicInbox/LeiInspect.pm @@ -269,10 +269,9 @@ no args allowed on command-line with --stdin } sub _complete_inspect { - my ($lei, @argv) = @_; - my $lms = $lei->lms or return; - my $match_cb = $lei->complete_url_prepare(\@argv); - map { $match_cb->($_) } $lms->folders; + require PublicInbox::LeiRefreshMailSync; + PublicInbox::LeiRefreshMailSync::_complete_refresh_mail_sync(@_); + # TODO: message-ids?, blobs? could get expensive... } 1; diff --git a/lib/PublicInbox/LeiLcat.pm b/lib/PublicInbox/LeiLcat.pm index 0902c213..c13e2153 100644 --- a/lib/PublicInbox/LeiLcat.pm +++ b/lib/PublicInbox/LeiLcat.pm @@ -164,10 +164,9 @@ no args allowed on command-line with --stdin } sub _complete_lcat { - my ($lei, @argv) = @_; - my $lms = $lei->lms or return; - my $match_cb = $lei->complete_url_prepare(\@argv); - map { $match_cb->($_) } $lms->folders; + require PublicInbox::LeiRefreshMailSync; + PublicInbox::LeiRefreshMailSync::_complete_refresh_mail_sync(@_); + # TODO: message-ids?, blobs? could get expensive... } 1; diff --git a/lib/PublicInbox/LeiLsMailSource.pm b/lib/PublicInbox/LeiLsMailSource.pm index 2265969a..1db15d57 100644 --- a/lib/PublicInbox/LeiLsMailSource.pm +++ b/lib/PublicInbox/LeiLsMailSource.pm @@ -107,12 +107,13 @@ sub lei_ls_mail_source { sub _complete_ls_mail_source { my ($lei, @argv) = @_; my $match_cb = $lei->complete_url_prepare(\@argv); - my @m = map { $match_cb->($_) } $lei->url_folder_cache->keys; - my %f = map { $_ => 1 } @m; + my @k = $lei->url_folder_cache->keys($argv[-1], 1); + my @m = map { $match_cb->($_) } @k; + my %f = map { $_ => 1 } (@m ? @m : @k); if (my $lms = $lei->lms) { - @m = map { $match_cb->($_) } grep( - m!\A(?:imaps?|nntps?|s?news)://!, $lms->folders); - @f{@m} = @m; + @k = $lms->folders($argv[-1], 1); + @m = map { $match_cb->($_) } grep(m!\A[a-z]+://!, @k); + if (@m) { @f{@m} = @m } else { @f{@k} = @k } } keys %f; } diff --git a/lib/PublicInbox/LeiMailSync.pm b/lib/PublicInbox/LeiMailSync.pm index 522a5ebc..91cd1c93 100644 --- a/lib/PublicInbox/LeiMailSync.pm +++ b/lib/PublicInbox/LeiMailSync.pm @@ -299,16 +299,19 @@ sub locations_for { # returns a list of folders used for completion sub folders { - my ($self, $pfx) = @_; - my $dbh = $self->{dbh} //= dbh_new($self); + my ($self, @pfx) = @_; my $sql = 'SELECT loc FROM folders'; - my @pfx; - if (defined $pfx) { + if (defined($pfx[0])) { $sql .= ' WHERE loc LIKE ? ESCAPE ?'; - @pfx = ($pfx, '\\'); + my $anywhere = !!$pfx[1]; + $pfx[1] = '\\'; $pfx[0] =~ s/([%_\\])/\\$1/g; # glob chars $pfx[0] .= '%'; + substr($pfx[0], 0, 0, '%') if $anywhere; + } else { + @pfx = (); # [0] may've been undef } + my $dbh = $self->{dbh} //= dbh_new($self); map { $_->[0] } @{$dbh->selectall_arrayref($sql, undef, @pfx)}; } diff --git a/lib/PublicInbox/LeiRefreshMailSync.pm b/lib/PublicInbox/LeiRefreshMailSync.pm index 51e89b23..eb842843 100644 --- a/lib/PublicInbox/LeiRefreshMailSync.pm +++ b/lib/PublicInbox/LeiRefreshMailSync.pm @@ -7,7 +7,7 @@ package PublicInbox::LeiRefreshMailSync; use strict; use v5.10.1; use parent qw(PublicInbox::IPC PublicInbox::LeiInput); -use PublicInbox::LeiExportKw; +use PublicInbox::LeiImport; use PublicInbox::InboxWritable qw(eml_from_path); use PublicInbox::Import; @@ -97,8 +97,16 @@ sub ipc_atfork_child { # needed for PublicInbox::LeiPmdir undef; } +sub _complete_refresh_mail_sync { + my ($lei, @argv) = @_; + my $lms = $lei->lms or return (); + my $match_cb = $lei->complete_url_prepare(\@argv); + my @k = $lms->folders($argv[-1], 1); + my @m = map { $match_cb->($_) } @k; + @m ? @m : @k +} + no warnings 'once'; -*_complete_refresh_mail_sync = \&PublicInbox::LeiExportKw::_complete_export_kw; *net_merge_all_done = \&PublicInbox::LeiInput::input_only_net_merge_all_done; 1; diff --git a/lib/PublicInbox/LeiTag.pm b/lib/PublicInbox/LeiTag.pm index 9bbf0d79..817b87f8 100644 --- a/lib/PublicInbox/LeiTag.pm +++ b/lib/PublicInbox/LeiTag.pm @@ -74,7 +74,7 @@ sub ipc_atfork_child { # Workaround bash word-splitting s to ['kw', ':', 'keyword' ...] # Maybe there's a better way to go about this in # contrib/completion/lei-completion.bash -sub _complete_mark_common ($) { +sub _complete_tag_common ($) { my ($argv) = @_; # Workaround bash word-splitting URLs to ['https', ':', '//' ...] # Maybe there's a better way to go about this in @@ -104,16 +104,18 @@ sub _complete_mark_common ($) { # FIXME: same problems as _complete_forget_external and similar sub _complete_tag { my ($self, @argv) = @_; + require PublicInbox::LeiImport; + my @in = PublicInbox::LeiImport::_complete_import(@_); my @L = eval { $self->_lei_store->search->all_terms('L') }; - my @all = ((map { ("+kw:$_", "-kw:$_") } @PublicInbox::LeiInput::KW), + my @kwL = ((map { ("+kw:$_", "-kw:$_") } @PublicInbox::LeiInput::KW), (map { ("+L:$_", "-L:$_") } @L)); - return @all if !@argv; - my ($cur, $re) = _complete_mark_common(\@argv); - map { + my ($cur, $re) = _complete_tag_common(\@argv); + my @m = map { # only return the part specified on the CLI # don't duplicate if already 100% completed /\A$re(\Q$cur\E.*)/ ? ($cur eq $1 ? () : $1) : (); - } grep(/$re\Q$cur/, @all); + } grep(/$re\Q$cur/, @kwL); + (@in, (@m ? @m : @kwL)); } no warnings 'once'; # the following works even when LeiAuth is lazy-loaded diff --git a/lib/PublicInbox/SharedKV.pm b/lib/PublicInbox/SharedKV.pm index 3487e820..645bb57c 100644 --- a/lib/PublicInbox/SharedKV.pm +++ b/lib/PublicInbox/SharedKV.pm @@ -84,12 +84,19 @@ SELECT k,v FROM kv } sub keys { - my ($self) = @_; - my $sth = $self->dbh->prepare_cached(<<'', undef, 1); -SELECT k FROM kv - - $sth->execute; - map { $_->[0] } @{$sth->fetchall_arrayref}; + my ($self, @pfx) = @_; + my $sql = 'SELECT k FROM kv'; + if (defined $pfx[0]) { + $sql .= ' WHERE k LIKE ? ESCAPE ?'; + my $anywhere = !!$pfx[1]; + $pfx[1] = '\\'; + $pfx[0] =~ s/([%_\\])/\\$1/g; # glob chars + $pfx[0] .= '%'; + substr($pfx[0], 0, 0, '%') if $anywhere; + } else { + @pfx = (); # [0] may've been undef + } + map { $_->[0] } @{$self->dbh->selectall_arrayref($sql, undef, @pfx)}; } sub delete_by_val {