zsh-users
 help / color / mirror / code / Atom feed
* Three questions about a completer
@ 2016-09-12 11:38 Jesper Nygårds
  2016-09-12 20:53 ` Oliver Kiddle
  0 siblings, 1 reply; 10+ messages in thread
From: Jesper Nygårds @ 2016-09-12 11:38 UTC (permalink / raw)
  To: Zsh Users

[-- Attachment #1: Type: text/plain, Size: 3089 bytes --]

I have a completer function that I have bound with menu-complete to alt-e
(or alt-E for reverse-menu-complete). The relevant code is below. I have
simplified what it does to generate the alternatives to not obscure what I
cannot get to work.

setopt BASH_AUTO_LIST

_list-result() {
    setopt LOCAL_OPTIONS EXTENDED_GLOB
    local -a hlist

    if [[ -n $NUMERIC ]]; then hlist=($HOME/*); else hlist=(/etc/*); fi

    [[ -n $words[CURRENT] ]] && hlist=(${(M)hlist:#(#i)*$words[CURRENT]*})

    compadd -V list_result -U -- $hlist
}

zle -C list-comp menu-complete _list-result
bindkey '\ee' list-comp
zstyle ':completion:list-comp:*:*' menu no

zle -C rev-list-comp reverse-menu-complete _list-result
bindkey '\eE' rev-list-comp
zstyle ':completion:rev-list-comp:*:*' menu no

This works reasonably well, but I have three questions:

1) I want to have the alternatives offered by consecutive presses of alt-e,
and I don't want the alternatives to be listed below the command line. To
achieve this, I have had to set the option BASH_AUTO_LIST. If this option
is not set, a list of alternatives is displayed as soon as I hit alt-e (and
at the same time the first alternative is put on the command line, which is
good). But I don't want this option to be set globally. I have not been
able to figure out how to make this menu NOT appear for this particular
completion, but without setting the global option. Is there a way to
achieve this?

2) The line starting with "[[ -n $words[CURRENT] ]] ..." is meant to make
sure that only files that match whats given on the command line (case
insensitively and anywhere within the file name) is offered as
alternatives. I does work, but it also means that reversing the order -
either by starting with alt-e and then hitting alt-E, or the opposite -
does not work. I assume that when I reverse the order, the widget is called
again, and since there is already a file on the command line (from pressing
alt-e once), there is only one file matching. Is there a better way to get
this behaviour? Again, I want to put a string on the command line, hit
alt-e, and only those files having a part matching what i wrote should be
offered. If I then reverse the order, I want the same list, i.e. those
files matching what I originally wrote.

3) I like the idea of using $NUMERIC to trigger alternative behaviour, in
this example getting the file names from another directory. That means I
can hit alt-1 and then alt-e and it gives me the matches from the
alternative directory. However, only alt-1 works. If I hit alt-2, the
NUMERIC variable is of course set to 2, and this makes the menu-completion
skip 2 places in the list every time I repeat alt-e. In other,
non-completion widgets, I have used this: "[[ -n $NUMERIC ]] && NUMERIC=1",
but it seems I cannot do this in a completion widget. I get: "read-only
variable: NUMERIC". Is there a better alternative to just have one
keybinding for a widget, but sometimes triggering an alternative behaviour
with a prefix? (I realise if I restrict myself to only hitting alt-1 it
works, but it's a bit limited).

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: Three questions about a completer
  2016-09-12 11:38 Three questions about a completer Jesper Nygårds
@ 2016-09-12 20:53 ` Oliver Kiddle
  2016-09-13 16:16   ` Jesper Nygårds
  0 siblings, 1 reply; 10+ messages in thread
From: Oliver Kiddle @ 2016-09-12 20:53 UTC (permalink / raw)
  To: Jesper Nygårds; +Cc: Zsh Users

Jesper Nygårds wrote:
> I have a completer function that I have bound with menu-complete to alt-e

> 1) I want to have the alternatives offered by consecutive presses of alt-e,
> and I don't want the alternatives to be listed below the command line. To

The hidden style should suppress listing the matches:
  zstyle ':completion:*list-comp:*' hidden all
But hidden is looked up from _description which you don't call.
You could add _wanted around the compadd but all the hidden style
actually does is cause the -n option to be passed to compadd which you
could do directly.

> 2) The line starting with "[[ -n $words[CURRENT] ]] ..." is meant to make
> sure that only files that match whats given on the command line (case
> insensitively and anywhere within the file name) is offered as

I would try using the completion matching control:

  compadd -n -p /etc/ -M 'l:|=* m:{[:lower:]}={[:upper:]}' /etc/*(:t)

> alternatives. I does work, but it also means that reversing the order -
> either by starting with alt-e and then hitting alt-E, or the opposite -
> does not work. I assume that when I reverse the order, the widget is called
> again, and since there is already a file on the command line (from pressing
> alt-e once), there is only one file matching. Is there a better way to get
> this behaviour? Again, I want to put a string on the command line, hit
> alt-e, and only those files having a part matching what i wrote should be
> offered. If I then reverse the order, I want the same list, i.e. those
> files matching what I originally wrote.

Using _menu generally does what I think you're describing. To use that,
you may want to use _generic instead of _list-result directly. I'd
recommend using _generic regardless of whether you want _menu, actually.
Other completers such as _match could also be useful.

  zle -C list-comp menu-complete _generic
  zstyle ':completion:*list-comp::::' completer _menu _list-result

The same thing can probably be done directly: _menu is not long but I
wonder whether it is right with the reverse widget.

> 3) I like the idea of using $NUMERIC to trigger alternative behaviour, in
> this example getting the file names from another directory. That means I

The menu completion widgets interpret the numeric argument. You can
always use a wrapper zle widget:

  zle-list-comp() {
    local dir
    (( NUMERIC == 1 )) && dir=/etc || dir=~
    zle list-comp -n 1
  }
  zle -N zle-list-comp

The -n 1 there passes 1 as the numeric argument on to list-comp. Needing
two widgets is a big ugly but I don't know of any alternative.

Oliver


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: Three questions about a completer
  2016-09-12 20:53 ` Oliver Kiddle
@ 2016-09-13 16:16   ` Jesper Nygårds
  2016-09-13 19:18     ` Jesper Nygårds
  0 siblings, 1 reply; 10+ messages in thread
From: Jesper Nygårds @ 2016-09-13 16:16 UTC (permalink / raw)
  To: Zsh Users

[-- Attachment #1: Type: text/plain, Size: 1527 bytes --]

On Mon, Sep 12, 2016 at 10:53 PM, Oliver Kiddle <okiddle@yahoo.co.uk> wrote:

> Jesper Nygårds wrote:
> >Again, I want to put a string on the command line, hit
> > alt-e, and only those files having a part matching what i wrote should be
> > offered. If I then reverse the order, I want the same list, i.e. those
> > files matching what I originally wrote.
>
> Using _menu generally does what I think you're describing. To use that,
> you may want to use _generic instead of _list-result directly. I'd
> recommend using _generic regardless of whether you want _menu, actually.
> Other completers such as _match could also be useful.
>
>   zle -C list-comp menu-complete _generic
>   zstyle ':completion:*list-comp::::' completer _menu _list-result
>
>
Perhaps I am missing something obvious, but I cannot get this to work. I
tried applying your suggestions, and ended up with this:

_list-result() {
    print In _list-result
    local -a hlist=($HOME/*)
    compadd -n -V list_result -M 'l:|=* m:{[:lower:]}={[:upper:]}' -- $hlist
}

zle -C list-comp menu-complete _generic
zle -C rev-list-comp reverse-menu-complete _generic
zstyle ':completion:*list-comp::::' completer _menu _list-result
bindkey '\ee' list-comp
bindkey '\eE' rev-list-comp

However, something is missing, because completion is not  triggered. In
fact, the debugging line "In _list-result" is not printed, so _list-result
is never called. I assume there is something more that I need to put in
place that I have missed?

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: Three questions about a completer
  2016-09-13 16:16   ` Jesper Nygårds
@ 2016-09-13 19:18     ` Jesper Nygårds
  2016-09-14  2:59       ` Bart Schaefer
  0 siblings, 1 reply; 10+ messages in thread
From: Jesper Nygårds @ 2016-09-13 19:18 UTC (permalink / raw)
  To: Zsh Users

[-- Attachment #1: Type: text/plain, Size: 937 bytes --]

Sorry, this was an oversight on my part. When reducing the problem, I had
worked with a minimal .zshrc, and removed even the lines
autoload -U compinit
compinit

So now, at least, I get the completion to trigger. However, I still have
the same problem: hitting alt-e for menu-complete works, and hitting alt-E
for reverse-menu-complete works as well, but switching direction doesn't:
zsh puts a space after the alternative on the command line, indicating that
there is only one matching alternative. And I interpret that to mean that
it takes whatever happens to be on the command line after the initial
menu-complete, and tries to match that against the list it gets from the
invocation of reverse-menu-complete. Naturally, only the alternative
already on the command line matches.

So I suppose the problem is to make zsh realize it is calling "the same"
completer, only reversing the direction of the listing. Is this possible to
do?

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: Three questions about a completer
  2016-09-13 19:18     ` Jesper Nygårds
@ 2016-09-14  2:59       ` Bart Schaefer
  2016-09-14  7:22         ` Jesper Nygårds
  0 siblings, 1 reply; 10+ messages in thread
From: Bart Schaefer @ 2016-09-14  2:59 UTC (permalink / raw)
  To: Zsh Users

On Sep 13,  9:18pm, Jesper Nygards wrote:
} 
} So now, at least, I get the completion to trigger. However, I still have
} the same problem: hitting alt-e for menu-complete works, and hitting alt-E
} for reverse-menu-complete works as well, but switching direction doesn't:

The doc for the _menu completer that Oliver mentioned, states:

     Note that this is independent of the setting of the
     MENU_COMPLETE option and does not work with the other menu
     completion widgets such as reverse-menu-complete, or
     accept-and-menu-complete.

So that suggestion is out.

>> 2) The line starting with "[[ -n $words[CURRENT] ]] ..." is meant to make
>> sure that only files that match whats given on the command line (case
>> insensitively and anywhere within the file name) is offered as
>> alternatives.

You should not have to do this yourself; this is what the completion
internals are made to do.  You need to caculate ALL the possible files
that fit the rest of your criteria, and "compadd" will take care of
filtering that set down to the ones that match what's on the line.

If you don't want to match by simple prefix then you do have to tell
compadd how to filter.  You do this with the matching control format
that Oliver outlined.

To stop the listing, and only menu through the matches, you assign
empty string to compstate[list].

Finally you need Oliver's wrapper technique to interpret NUMERIC.
The nice thing about menu-complete / reverse-menu-complete is that
if a menu is already in progress, it won't re-invoke the completer,
so if you start from the wrapper widget in the forward direction,
you can reverse without having to use the wrapper again.

So the whole thing looks something like:

    zle -C list-comp menu-complete _generic
    zle -C reve-list-comp reverse-menu-complete _generic

    n-list-comp() {
      local -a dirs=( $HOME /etc /bin )
      local __lsdir=${dirs[${NUMERIC:-1}]
      zle list-comp -w -n 1
    }
    zle -N n-list-comp
    # Similarly for n-rev-list-comp to start in reverse with NUMERIC

    bindkey '\ee' n-list-comp
    bindkey '\eE' rev-list-comp    # or n-rev-list-comp if defined

    _list-result() {
      compadd -M 'l:|=* m:{[:lower:]}={[:upper:]}' -f ${__lsdir:-HOME}/*
      compstate[list]=''
    }
    zstyle ':completion:*list-comp::::' completer _list-result

Strictly speaking you don't even need rev-list-comp, you could just
invoke reverse-menu-complete provided that you always first enter
the menu in the forward direction.


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: Three questions about a completer
  2016-09-14  2:59       ` Bart Schaefer
@ 2016-09-14  7:22         ` Jesper Nygårds
  2016-09-14 16:39           ` Bart Schaefer
  0 siblings, 1 reply; 10+ messages in thread
From: Jesper Nygårds @ 2016-09-14  7:22 UTC (permalink / raw)
  To: Zsh Users

[-- Attachment #1: Type: text/plain, Size: 1818 bytes --]

Thank you for all your help, Bart and Oliver!

I am getting closer: the problem with numeric is now solved with your
suggestions, as well as turning off the listing of alternatives. However I
still have trouble reversing the menu completion direction.

I'll explain what it is I want. I believe you've already understood, but
just to make sure: say that I have files A, B and C in my home directory. I
type "print <alt-e>", and "A" is put on the line. I then hit "<alt-e>"
again, and the "A" is replaced by "B".

If I start over and type "print <alt-E>", "C" is put on the line. Another
"<alt-E>" gives me "B". So the two keybindings work independently:
"<alt-e>" gives the list from the beginning, "<alt-E>" from the end.

However, this is what is NOT working: I type "print <alt-e>", and get "A".
I hit "<alt-e>" again, and get "B". Now, I hit "<alt-E>" to go back in the
list. I expected to get "A" again, but instead "B" remains on the line.
It's as though the only thing matching is what's right now on the line.

Below is the entirety of my ~/.zshrc when I experiment with this. It's
Bart's solution with n-rev-list-comp added (and the compinit activation).

autoload -U compinit
compinit

zle -C list-comp menu-complete _generic
zle -C rev-list-comp reverse-menu-complete _generic

n-list-comp() {
    local -a dirs=( $HOME /etc /bin )
    local __lsdir=${dirs[${NUMERIC:-1}]}
    zle list-comp -w -n 1
}

n-rev-list-comp() {
    local -a dirs=( $HOME /etc /bin )
    local __lsdir=${dirs[${NUMERIC:-1}]}
    zle rev-list-comp -w -n 1
}

zle -N n-list-comp
zle -N n-rev-list-comp

bindkey '\ee' n-list-comp
bindkey '\eE' n-rev-list-comp

_list-result() {
    compadd -M 'l:|=* m:{[:lower:]}={[:upper:]}' -f ${__lsdir:-HOME}/*
    compstate[list]=''
}

zstyle ':completion:*list-comp::::' completer _list-result

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: Three questions about a completer
  2016-09-14  7:22         ` Jesper Nygårds
@ 2016-09-14 16:39           ` Bart Schaefer
  2016-09-14 20:32             ` Jesper Nygårds
  0 siblings, 1 reply; 10+ messages in thread
From: Bart Schaefer @ 2016-09-14 16:39 UTC (permalink / raw)
  To: Zsh Users

On Sep 14,  9:22am, Jesper Nygards wrote:
}
} I am getting closer: the problem with numeric is now solved with your
} suggestions, as well as turning off the listing of alternatives. However
} I still have trouble reversing the menu completion direction.

I think all you need is this:

    _list-result() {
	compadd -M 'l:|=* m:{[:lower:]}={[:upper:]}' -f ${__lsdir:-HOME}/*
	# Even if there are multiple matches, don't list them out
	compstate[list]=''
	# In case we are already in the middle of a menu completion,
	# ignore the compadd above and continue in the current menu
	compstate[insert]='menu'
    } 

It may be necessary in some edge cases to examine $compstate[insert]
and update it differently; read the doc and experiment with what the
state of $compstate[insert] is upon entry to _list-result.


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: Three questions about a completer
  2016-09-14 16:39           ` Bart Schaefer
@ 2016-09-14 20:32             ` Jesper Nygårds
  2016-09-15  4:49               ` Bart Schaefer
  2016-09-15  6:01               ` Jesper Nygårds
  0 siblings, 2 replies; 10+ messages in thread
From: Jesper Nygårds @ 2016-09-14 20:32 UTC (permalink / raw)
  To: Zsh Users

[-- Attachment #1: Type: text/plain, Size: 1471 bytes --]

Hmm, this was much harder than I thought. I see no difference with the
addition of the compstate[insert] line.

I tried this:

_list-result() {
    print "state: " $compstate[insert]
    compadd -M 'l:|=* m:{[:lower:]}={[:upper:]}' -f ${__lsdir:-HOME}/*
    compstate[list]=''
    compstate[insert]='menu'
}

In fact, I have not been able to provoke a situation where the first line
does not print out "state: menu". Both if I hit "<alt-e>" and "<alt-E>",
what is printed is always "state: menu".


On Wed, Sep 14, 2016 at 6:39 PM, Bart Schaefer <schaefer@brasslantern.com>
wrote:

> On Sep 14,  9:22am, Jesper Nygards wrote:
> }
> } I am getting closer: the problem with numeric is now solved with your
> } suggestions, as well as turning off the listing of alternatives. However
> } I still have trouble reversing the menu completion direction.
>
> I think all you need is this:
>
>     _list-result() {
>         compadd -M 'l:|=* m:{[:lower:]}={[:upper:]}' -f ${__lsdir:-HOME}/*
>         # Even if there are multiple matches, don't list them out
>         compstate[list]=''
>         # In case we are already in the middle of a menu completion,
>         # ignore the compadd above and continue in the current menu
>         compstate[insert]='menu'
>     }
>
> It may be necessary in some edge cases to examine $compstate[insert]
> and update it differently; read the doc and experiment with what the
> state of $compstate[insert] is upon entry to _list-result.
>

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: Three questions about a completer
  2016-09-14 20:32             ` Jesper Nygårds
@ 2016-09-15  4:49               ` Bart Schaefer
  2016-09-15  6:01               ` Jesper Nygårds
  1 sibling, 0 replies; 10+ messages in thread
From: Bart Schaefer @ 2016-09-15  4:49 UTC (permalink / raw)
  To: Zsh Users

On Sep 14, 10:32pm, Jesper Nygards wrote:
}
} Hmm, this was much harder than I thought. I see no difference with the
} addition of the compstate[insert] line.

Hm.  I don't have any problem making what you posted in users/21901
switch forward to backward or backward to forward, even without the
compstate[insert] assignment (as borne out by you finding it already
set to "menu" on entry).

I don't see where you've ever told us what version number of zsh you
have ... oh.

Date:   Mon Mar 7 13:15:40 2016 +0100

    38043: allow any completion widget to cycle matches for menu completion
    to fix reverse menu completion

So it works for me because I'm running the most recent dev build.

That means you'll have to do a bit of the work of preserving the menu:

    typeset -Hga __lsmatches

    _list-result () {
      if [[ $WIDGET != $LASTWIDGET ]]
      then
        if [[ $LASTWIDGET = *list-comp ]]
        then
          # Rotate stored completions to start in the right place
          # Replace (b) with (q) here if zsh complains about (b)
          integer here=$__lsmatches[(I)${(b)words[CURRENT]}]
          if ((here)); then
            __lsmatches=( $__lsmatches[here,-1] $__lsmatches[1,here-1] )
          fi
        else
          # Store the matching completions in case needed again
          compadd -O __lsmatches \
              -M 'l:|=* m:{[:lower:]}={[:upper:]}' -f ${__lsdir:-HOME}/*
        fi
        # Now add, preserving the order previously calculated
        compadd -V $WIDGET -U -f -a __lsmatches
      fi
      compstate[list]='' 
    }


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: Three questions about a completer
  2016-09-14 20:32             ` Jesper Nygårds
  2016-09-15  4:49               ` Bart Schaefer
@ 2016-09-15  6:01               ` Jesper Nygårds
  1 sibling, 0 replies; 10+ messages in thread
From: Jesper Nygårds @ 2016-09-15  6:01 UTC (permalink / raw)
  To: Zsh Users

[-- Attachment #1: Type: text/plain, Size: 1302 bytes --]

I realize my knowledge of how the completion system works in its internals
is too limited, I can't really interpret what's going on. I have simplified
the code to the one below (for now removing the fix for taking care of
$NUMERIC, to make sure it isn't a source of the problem):

This is my whole ~/.zshrc:

autoload -U compinit
compinit

zle -C list-comp menu-complete _generic
zle -C rev-list-comp reverse-menu-complete _generic

bindkey '\ee' list-comp
bindkey '\eE' rev-list-comp

_list-result() {
    print "state: " $compstate[insert]
    compadd -M 'l:|=* m:{[:lower:]}={[:upper:]}' -f $HOME/*
    compstate[list]=''
    compstate[insert]='menu'
}

zstyle ':completion:*list-comp::::' completer _list-result

If I type "print <alt-e>", I can see "state: menu" being printed once, and
further presses of "<alt-e>" does not redisplay it. So I assume the
possible matches is used by the menu-completion and not re-generated.
However, once I change direction with "<alt-E>", the message is printed
every time I press "<alt-E>". So it's like the completion system doesn't
recognize that list-comp and rev-list-comp are part of the same solution,
and _list-result is called to start the generation of matches again. And
since there's already an alternative on the command line, nothing else
matches.

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2016-09-15  6:28 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-09-12 11:38 Three questions about a completer Jesper Nygårds
2016-09-12 20:53 ` Oliver Kiddle
2016-09-13 16:16   ` Jesper Nygårds
2016-09-13 19:18     ` Jesper Nygårds
2016-09-14  2:59       ` Bart Schaefer
2016-09-14  7:22         ` Jesper Nygårds
2016-09-14 16:39           ` Bart Schaefer
2016-09-14 20:32             ` Jesper Nygårds
2016-09-15  4:49               ` Bart Schaefer
2016-09-15  6:01               ` Jesper Nygårds

Code repositories for project(s) associated with this public inbox

	https://git.vuxu.org/mirror/zsh/

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 NNTP newsgroup(s).