unofficial mirror of meta@public-inbox.org
 help / color / mirror / Atom feed
* [PATCH] lei: improve bash completion involving colons
@ 2023-03-23 21:45 Eric Wong
  0 siblings, 0 replies; only message in thread
From: Eric Wong @ 2023-03-23 21:45 UTC (permalink / raw)
  To: meta

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 <meta@public-inbox.org>
+# Copyright (C) all contributors <meta@public-inbox.org>
 # License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt>
 
 # 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) {

^ permalink raw reply related	[flat|nested] only message in thread

only message in thread, other threads:[~2023-03-23 21:45 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-03-23 21:45 [PATCH] lei: improve bash completion involving colons Eric Wong

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).