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-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 9BAEF1F9FF for ; Tue, 23 Mar 2021 05:02:18 +0000 (UTC) From: Eric Wong To: meta@public-inbox.org Subject: [PATCH 2/2] lei mark: add support for (bash) completion Date: Tue, 23 Mar 2021 11:02:18 +0600 Message-Id: <20210323050218.28873-3-e@80x24.org> In-Reply-To: <20210323050218.28873-1-e@80x24.org> References: <20210323050218.28873-1-e@80x24.org> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit List-Id: Only lightly tested, this seems to suffer from the same problem as external completions for network URLs with colons in them. In any case, its usable enough for me. The core LEI module now supports completions for lazy-loaded commands, too, so we'll be able to do completions for other commands more easily. --- lib/PublicInbox/LEI.pm | 27 ++++++++++++++---------- lib/PublicInbox/LeiMark.pm | 43 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 11 deletions(-) diff --git a/lib/PublicInbox/LEI.pm b/lib/PublicInbox/LEI.pm index 91c95239..0be417eb 100644 --- a/lib/PublicInbox/LEI.pm +++ b/lib/PublicInbox/LEI.pm @@ -604,6 +604,19 @@ EOM } } +sub lazy_cb ($$$) { + my ($self, $cmd, $pfx) = @_; + my $ucmd = $cmd; + $ucmd =~ tr/-/_/; + my $cb; + $cb = $self->can($pfx.$ucmd) and return $cb; + my $base = $ucmd; + $base =~ s/_([a-z])/\u$1/g; + my $pkg = "PublicInbox::Lei\u$base"; + ($INC{"PublicInbox/Lei\u$base.pm"} // eval("require $pkg")) ? + $pkg->can($pfx.$ucmd) : undef; +} + sub dispatch { my ($self, $cmd, @argv) = @_; local $current_lei = $self; # for __WARN__ @@ -616,14 +629,7 @@ sub dispatch { push @{$self->{opt}->{substr($cmd, 1, 1)}}, $v; $cmd = shift(@argv) // return _help($self, 'no command given'); } - my $func = "lei_$cmd"; - $func =~ tr/-/_/; - my $cb = __PACKAGE__->can($func) // ($CMD{$cmd} ? do { - my $mod = "PublicInbox::Lei\u$cmd"; - ($INC{"PublicInbox/Lei\u$cmd.pm"} // - eval("require $mod")) ? $mod->can($func) : undef; - } : undef); - if ($cb) { + if (my $cb = lazy_cb(__PACKAGE__, $cmd, 'lei_')) { optparse($self, $cmd, \@argv) or return; $self->{opt}->{c} and (_tmp_cfg($self) // return); if (my $chdir = $self->{opt}->{C}) { @@ -808,9 +814,8 @@ sub lei__complete { @v; } grep(/\A(?:[\w-]+\|)*$opt\b.*?(?:\t$cmd)?\z/, keys %OPTDESC); } - $cmd =~ tr/-/_/; - if (my $sub = $self->can("_complete_$cmd")) { - puts $self, $sub->($self, @argv, $cur ? ($cur) : ()); + if (my $cb = lazy_cb($self, $cmd, '_complete_')) { + puts $self, $cb->($self, @argv, $cur ? ($cur) : ()); } # TODO: URLs, pathnames, OIDs, MIDs, etc... See optparse() for # proto parsing. diff --git a/lib/PublicInbox/LeiMark.pm b/lib/PublicInbox/LeiMark.pm index aa52ad5a..7b50aa51 100644 --- a/lib/PublicInbox/LeiMark.pm +++ b/lib/PublicInbox/LeiMark.pm @@ -174,4 +174,47 @@ sub ipc_atfork_child { PublicInbox::OnDestroy->new($$, \¬e_missing, $self); } +# 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 ($) { + my ($argv) = @_; + # 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) // ''; + if (@$argv) { + my @x = @$argv; + if ($cur eq ':' && @x) { + push @x, $cur; + $cur = ''; + } + while (@x > 2 && $x[0] !~ /\A[+\-](?:kw|L)\z/ && + $x[1] ne ':') { + shift @x; + } + if (@x >= 2) { # qw(kw : $KEYWORD) or qw(kw :) + $re = join('', @x); + } else { # just return everything and hope for the best + $re = join('', @$argv); + } + $re = quotemeta($re); + } + ($cur, $re); +} + +# FIXME: same problems as _complete_forget_external and similar +sub _complete_mark { + my ($self, @argv) = @_; + my @all = map { ("+kw:$_", "-kw:$_") } @KW; + return @all if !@argv; + my ($cur, $re) = _complete_mark_common(\@argv); + 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); +} + 1;