zsh-users
 help / color / mirror / code / Atom feed
* Setup git-stash completion for a function: $line is wrong
@ 2017-03-12 16:37 Daniel Hahler
  2017-03-12 22:27 ` Bart Schaefer
  0 siblings, 1 reply; 6+ messages in thread
From: Daniel Hahler @ 2017-03-12 16:37 UTC (permalink / raw)
  To: zsh-users


[-- Attachment #1.1: Type: text/plain, Size: 1911 bytes --]

I am using a helper method to setup completion for functions that are
used as extended aliases.

  # Helper to setup completion for functions, e.g.
  # "complete_function gcf git commit --fixup" will setup completion for
  # "gcf" => "git commit --fixup".
  complete_function() {
    local f=$1; shift
    compdef -e "words=($* \"${(@)words[2,-1]}\"); ((CURRENT+=$(( $#*-1 )))); _normal" $f
  }

Example usage:
  complete_function gsta git stash

This works in general, but for the case above I am getting
"-- no argument or option --" with "gsta drop <tab>".

The reason for this is that $line in _git_stash is just (''), instead of
(drop '') (when using "git stash drop" directly).

  _git-stash () {
    local curcontext=$curcontext state line ret=1
    …
    _arguments -C \
      '*::: :->args' \
      ${save_arguments//#\(/(* } && ret=0

    if [[ -n $state ]]; then
      if (( CURRENT == 1 )); then
        …
        _describe -t commands command commands && ret=0
      else
        …
        curcontext=${curcontext%:*}-$line[1]:
        compset -n 1

        case $line[1] in

From looking at _arguments it looks like $line is managed through
"comparguments -W line opt_args", but does not seem to be documented:

  comparguments
    This  is used by the _arguments function to do the argument and
    command line parsing.  Like compdescribe it has an option -i to do
    the parsing  and  initialize some internal state and various options
    to access the state information to decide what should be completed.

Probably something else needs to be handled to setup completion in this
case (through compdef -e), or can it be made to work with _normal like
with other completions?

Additionally, I think that zsh itself should provide a way to more
easily setup completion for functions (i.e. something like my wrapper
function above).


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 195 bytes --]

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

* Re: Setup git-stash completion for a function: $line is wrong
  2017-03-12 16:37 Setup git-stash completion for a function: $line is wrong Daniel Hahler
@ 2017-03-12 22:27 ` Bart Schaefer
  2017-03-14 23:59   ` Daniel Hahler
  0 siblings, 1 reply; 6+ messages in thread
From: Bart Schaefer @ 2017-03-12 22:27 UTC (permalink / raw)
  To: zsh-users

On Mar 12,  5:37pm, Daniel Hahler wrote:
} 
} I am using a helper method to setup completion for functions that are
} used as extended aliases.
} 
}   # Helper to setup completion for functions, e.g.
}   # "complete_function gcf git commit --fixup" will setup completion for
}   # "gcf" => "git commit --fixup".
}   complete_function() {
}     local f=$1; shift
}     compdef -e "words=($* \"${(@)words[2,-1]}\"); ((CURRENT+=$(( $#*-1 )))); _normal" $f
}   }

This can't be right.  Because of the double quotes around the argument
of compdef -e, ${(@)words[2,-1]} will expand when complete_function
is executed, not when the compdef value is needed.  Same for $(( $#*-1 ))
which by the way is the same as $(( $# - 1 )).  Also as you are already
inside (( )) you don't need $(( )).

Try it this way:

  complete_function() {           
    local f=$1; shift
    compdef -e "words[1]=( ${${(qq)@}} ); (( CURRENT += $# )); _normal" $f
  }

There's a bit of magic there using an extra ${...} around ${(qq)@} to
force the multi-word expansion of $@ back into a single string so that
the outer double-quotes won't split it the way "$@" is normally split.

} Additionally, I think that zsh itself should provide a way to more
} easily setup completion for functions (i.e. something like my wrapper
} function above).

How would you envision this to work?  How does "zsh itself" know what
someone is going to do inside a function body?

There's already (compdef cmd=service) e.g.

    compdef gsta=git

for wrappers that don't insert things into their argument words to act
exactly like a pre-existing completion.  Also, using an alias instead
of a function wrapper, i.e.,

    alias gsta='git stash'

should "just work" because completion will expand the alias before it
builds $words and looks up the completion.


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

* Re: Setup git-stash completion for a function: $line is wrong
  2017-03-12 22:27 ` Bart Schaefer
@ 2017-03-14 23:59   ` Daniel Hahler
  2017-03-16 21:02     ` Bart Schaefer
  0 siblings, 1 reply; 6+ messages in thread
From: Daniel Hahler @ 2017-03-14 23:59 UTC (permalink / raw)
  To: zsh-users


[-- Attachment #1.1: Type: text/plain, Size: 3262 bytes --]

On 12.03.2017 23:27, Bart Schaefer wrote:
> On Mar 12,  5:37pm, Daniel Hahler wrote:
> } 
> } I am using a helper method to setup completion for functions that are
> } used as extended aliases.
> } 
> }   # Helper to setup completion for functions, e.g.
> }   # "complete_function gcf git commit --fixup" will setup completion for
> }   # "gcf" => "git commit --fixup".
> }   complete_function() {
> }     local f=$1; shift
> }     compdef -e "words=($* \"${(@)words[2,-1]}\"); ((CURRENT+=$(( $#*-1 )))); _normal" $f
> }   }
> 
> This can't be right.  Because of the double quotes around the argument
> of compdef -e, ${(@)words[2,-1]} will expand when complete_function
> is executed, not when the compdef value is needed.  Same for $(( $#*-1 ))
> which by the way is the same as $(( $# - 1 )).  Also as you are already
> inside (( )) you don't need $(( )).
> 
> Try it this way:
> 
>   complete_function() {
>     local f=$1; shift
>     compdef -e "words[1]=( ${${(qq)@}} ); (( CURRENT += $# )); _normal" $f
>   }
> 
> There's a bit of magic there using an extra ${...} around ${(qq)@} to
> force the multi-word expansion of $@ back into a single string so that
> the outer double-quotes won't split it the way "$@" is normally split.

Thank you!
It did not work initially, but luckily it seems to be just an off-by-one
error when incrementing CURRENT.  The following works:

  complete_function() {
    local f=$1; shift
    compdef -e "words[1]=( ${${(qq)@}} ); (( CURRENT += $# - 1 )); _normal" $f
  }
  complete_function gsta git stash
  gsta drop <tab>

> } Additionally, I think that zsh itself should provide a way to more
> } easily setup completion for functions (i.e. something like my wrapper
> } function above).
> 
> How would you envision this to work?  How does "zsh itself" know what
> someone is going to do inside a function body?
> 
> There's already (compdef cmd=service) e.g.
> 
>     compdef gsta=git
> 
> for wrappers that don't insert things into their argument words to act
> exactly like a pre-existing completion.

Yes, I am aware of that, and what I mean is more or less something in this
regard, e.g. by making it handle command+arguments.

    compdef gsta='git stash'

Since that would be incompatible with commands that contain spaces,
maybe a list could be used:

    compdef gsta=(git stash)

> Also, using an alias instead of a function wrapper, i.e.,
> 
>     alias gsta='git stash'
> 
> should "just work" because completion will expand the alias before it
> builds $words and looks up the completion.

I am using it with the following to automatically update a ctags tags file,
but I have other, more involving functions-as-aliases - so using an alias
directly is not feasible:

  gsta() {
    $_git_cmd stash "$@"
    local ret=$?
    if [[ -z "$1" || "$1" == 'pop' || "$1" == 'apply' ]]; then
      _update_git_ctags $ret
    fi
    return $ret
  }
  complete_function gsta git stash

btw: Daniel Shahaf suggested on IRC to use a wrapper function for "git"
altogether:

  alias gsta='git stash drop'
  git() {
    if [[ $1 == stash && $2 == drop ]]; then
      ...
    else command git "$@"; fi
  }


Thanks,
Daniel.


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 195 bytes --]

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

* Re: Setup git-stash completion for a function: $line is wrong
  2017-03-14 23:59   ` Daniel Hahler
@ 2017-03-16 21:02     ` Bart Schaefer
  2017-03-16 23:54       ` Daniel Shahaf
  0 siblings, 1 reply; 6+ messages in thread
From: Bart Schaefer @ 2017-03-16 21:02 UTC (permalink / raw)
  To: zsh-users

On Mar 15, 12:59am, Daniel Hahler wrote:
}
} It did not work initially, but luckily it seems to be just an off-by-one
} error when incrementing CURRENT.

Indeed, sorry about that.  words[1] is being replaced by $@ rather than
appended-to, so you have to take one away from the increment.

} > There's already (compdef cmd=service) e.g.
} > 
} >     compdef gsta=git
} 
} Yes, I am aware of that, and what I mean is more or less something in this
} regard, e.g. by making it handle command+arguments.

For other readers of this thread:  Any input here on whether this is
actually a common use case?

} maybe a list could be used:
} 
}     compdef gsta=(git stash)

That would require that 'compdef' be made a keyword, otherwise that's a
syntax error.  I don't think we're likely to embed compsys that deeply
into the shell syntax.

We could maybe do what some other compsys functions do:

compdef gsta='(git stash)'

Not sure what that would require of the lower-level code where the value
of the compdef is evaluated.

Probably the right way to approach this is to have a completer "_wrapper"
which looks up the replacement command via zstyle, fixes up $words etc.,
and then calls _complete.  The potentially tricky part is preventing the
replacement from changing the context, i.e. adding syntactic tokens
like globbing or pipes, or at least failing gracefully in that event.

} I am using it with the following to automatically update a ctags tags file

This sounds more like a job for a git hook ... except there isn't one
for "stash".



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

* Re: Setup git-stash completion for a function: $line is wrong
  2017-03-16 21:02     ` Bart Schaefer
@ 2017-03-16 23:54       ` Daniel Shahaf
  2017-03-21  4:42         ` Bart Schaefer
  0 siblings, 1 reply; 6+ messages in thread
From: Daniel Shahaf @ 2017-03-16 23:54 UTC (permalink / raw)
  To: zsh-users

Bart Schaefer wrote on Thu, Mar 16, 2017 at 14:02:24 -0700:
> On Mar 15, 12:59am, Daniel Hahler wrote:
> } Bart Schaefer:
> } > There's already (compdef cmd=service) e.g.
> } > 
> } >     compdef gsta=git
> } 
> } Yes, I am aware of that, and what I mean is more or less something in this
> } regard, e.g. by making it handle command+arguments.
> 
> For other readers of this thread:  Any input here on whether this is
> actually a common use case?

As a datapoint: in my zshrc, I have 12 wrapper functions of the «foo()
{ command foo "$@" }» form, and only one function that would benefit
from the proposed compdef enhancement.

That function takes 'git rev-list' arguments and runs 'tig' (a Git
history viewer, like gitk) on them:
.
    mytig() {
      if (( $# )); then
        tig --no-walk $(git log --pretty=%H "$@")
      else
        tig --date-order --all -150
      fi
    }

Writing a completion function for this helper today is a little tricky,
since 'autoload +X _git' doesn't define _git-log().  Under the proposal,
defining completion for this helper would simply be «compdef mytig='git
log --pretty=%H'».  (Or whatever syntax is decided on)

Cheers,

Daniel


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

* Re: Setup git-stash completion for a function: $line is wrong
  2017-03-16 23:54       ` Daniel Shahaf
@ 2017-03-21  4:42         ` Bart Schaefer
  0 siblings, 0 replies; 6+ messages in thread
From: Bart Schaefer @ 2017-03-21  4:42 UTC (permalink / raw)
  To: zsh-users

On Mar 16, 11:54pm, Daniel Shahaf wrote:
}
} Writing a completion function for this helper today is a little tricky,
} since 'autoload +X _git' doesn't define _git-log().  Under the proposal,
} defining completion for this helper would simply be "compdef mytig='git
} log --pretty=%H'".  (Or whatever syntax is decided on)

Doing this by edit of compdef + _dispatch would be quite messy.  However,
I think there's an easier way to go about it than the approach taken by
Daniel Hahler.

Instead of defining a function that creates compdef commands, it seems
more obvious to me to define a function that can be passed to compdef.
That is, instead of

    complete_function gsta git stash	# calls compdef

why not

    compdef '_compwrap git stash' gsta
    compdef '_compwrap git log --pretty=%H' mytig

where

    _compwrap () {
      set -- "$@" "${(@)words[2,-1]}"
      local -a words=( "$@" )
      local _comp_command _comp_command1 _comp_command2
      _set_command
      _dispatch -s "$_comp_command" \
        "$_comp_command1" "$_comp_command2" -default-
    }

Calling _dispatch directly allows the current context to remain set to
:completion::complete:gsta:* rather than change to *:git:*.  It't not
clear to me whether that's desirable.  There might be some other local
declarations needed to avoid cluttering downstream namespace in the
event that nothing is completed here, but I think you get the idea.


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

end of thread, other threads:[~2017-03-21  4:41 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-03-12 16:37 Setup git-stash completion for a function: $line is wrong Daniel Hahler
2017-03-12 22:27 ` Bart Schaefer
2017-03-14 23:59   ` Daniel Hahler
2017-03-16 21:02     ` Bart Schaefer
2017-03-16 23:54       ` Daniel Shahaf
2017-03-21  4:42         ` Bart Schaefer

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).