Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pass PowerShell aliases down to posh-git TabExpansion autocomplete function #257

Closed
humidair1999 opened this issue Feb 21, 2016 · 23 comments

Comments

@humidair1999
Copy link

I have PS aliases for git, defined as such:

function git-branch-delete { git branch -D $args }
Set-Alias gbd git-branch-delete

function git-checkout { git checkout $args }
Set-Alias gco git-checkout

The $args represents a branch name, which posh-git would typically autocomplete. However, since aliases are not passed down to posh-git, autocompletion doesn't work.

I monkey-patched this functionality myself, using the technique described in this SO answer:

function TabExpansion($line, $lastWord) {
    # aliases that have some form of tab autocomplete functionality
    # NOTE: ADDED POST-INSTALL BY USER
    $line = $line -replace '^gco ', 'git checkout '
    $line = $line -replace '^gbd ', 'git branch '
    # end aliases

    # rest of the function...
}

It'd be super rad if posh-git could somehow retrieve existing PS aliases related to git functionality like this and patch it into posh-git's autocompletion functionality.

I prefer to use PS aliases over git aliases, primarily for brevity - I'm able to do gcm instead of git co or similar.

@dahlbyk
Copy link
Owner

dahlbyk commented Feb 6, 2017

Working through open issues and realized that I never commented. This is a great idea and I would totally accept a PR for a generalized version of this.

@MisinformedDNA
Copy link

I can easily see aliases working, but functions are tricky.

For aliases, we just need to insert (Get-Alias $alias).Definition somewhere.

Assuming we have

function git-checkout { git checkout $args }
set-alias gco git-checkout

I have no idea how I would interpret that. Would I grab the ScriptBlock to do a find/replace? But what if it's multiple lines? What if I executed something in the ScriptBlock? Well we can't run the function as is because we aren't ready to execute. We still want tab completion. But what if the function returned a string? Assume

> function git-checkout { "git checkout" }
> set-alias gco git-checkout

Then we could run the following:

$command = (Get-Alias $alias).Definition
if (Test-Path Function:/$command) {    # Check if the command is actually a function
    $command = & $alias    # execute
}

Since we are now only dealing with strings we could just create a "dictionary file", kind of like from Export-Alias.

Thoughts?

@dahlbyk
Copy link
Owner

dahlbyk commented May 4, 2018

posh-git wouldn't be responsible for executing the function, we just need to tease out the definition.

You can get the function definition like this. I would try a regex to match the definition on git <args> $args, then swap the match in before expansion.

@MisinformedDNA
Copy link

What would you do if there were multiple lines in the definition?

@dahlbyk
Copy link
Owner

dahlbyk commented May 4, 2018

What would you do if there were multiple lines in the definition?

Similar to how we parse $lastBlock, I think you'd want to match between [;|]. I haven't tested it, but something like "(^|[|;])\s*(?<cmd>$(Get-AliasPattern git).* \`$args)\s*($|[|;])", then Expand-GitCommand $matches['cmd'].

This would be a great opportunity to TDD handling different function definitions. 😀

@Clijsters
Copy link

I would grab this, since I realized something similiar some months ago.

@jrusbatch
Copy link

I'd also love to see this! I alias git to g out of laziness, and I only just now realized that that was the reason I can't tab-complete branch names anymore.

@dahlbyk
Copy link
Owner

dahlbyk commented Sep 11, 2019

@jrusbatch: I'd also love to see this! I alias git to g out of laziness, and I only just now realized that that was the reason I can't tab-complete branch names anymore.

If g is a normal PowerShell alias (i.e. Set-Alias g git) it should Just Work™️. Are you finding that not to be the case?

@jrusbatch
Copy link

@dahlbyk Correct. Although, I was declaring my alias with New-Alias -Name g -Value git -Option Private in my Profile.ps1. After removing -Option Private the tab completion functionality works. I don't remember why I specified that option so I think I'm good for now. Thanks!

@szelpe
Copy link

szelpe commented May 16, 2020

@dahlbyk I have the exact same setup, i.e. having Set-Alias g git in my $profile and tab completion with g worked in beta2 and beta3 but now broken in beta4. Simply does nothing if I type g fet<TAB>.

@szelpe
Copy link

szelpe commented May 16, 2020

Also, if I set the UseLegacyTabExpansion to $True it starts to work again.

@rkeithhill
Copy link
Collaborator

rkeithhill commented May 16, 2020

@szelpe Your bug is a little different since you alias directly to the git executable as opposed to aliasing to a custom function. Can you submit a new issue?

It turns out PowerShell's built-in Register-ArgumentCompleter does not take into account aliases to native apps that have registered completers. I think the fix for this is pretty easy.

@szelpe
Copy link

szelpe commented May 17, 2020

@szelpe Your bug is a little different since you alias directly to the git executable as opposed to aliasing to a custom function. Can you submit a new issue?

It turns out PowerShell's built-in Register-ArgumentCompleter does not take into account aliases to native apps that have registered completers. I think the fix for this is pretty easy.

Sure: #769

@rkeithhill
Copy link
Collaborator

Implemented via PR #779. Thanks @csc027!

@suhaibbaba
Copy link

suhaibbaba commented Feb 1, 2023

How can do that?
I need to create alias like
gcob master
gcob = git checkout -b
when write gcob m and click tab, I need to show master

@MisinformedDNA
Copy link

$scriptBlock = {
     param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)

     Expand-GitCommand 'git checkout -b '
 }

Register-ArgumentCompleter -CommandName gcob -ScriptBlock $scriptBlock

@suhaibbaba
Copy link

suhaibbaba commented Feb 4, 2023

@MisinformedDNA
Thanks but It doesn't autocomplete from last word
I mean
gcob mas
It should complete it to gcob master

@MisinformedDNA
Copy link

MisinformedDNA commented Feb 4, 2023

Try this:

$scriptBlock = {
     param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)

     Expand-GitCommand "git checkout -b $commandName"
 }
 Register-ArgumentCompleter -CommandName gcob -ScriptBlock $scriptBlock

@suhaibbaba
Copy link

@MisinformedDNA Thank you and it's working fine
but can you please explain it? and if I need to create multiple custom commands, do I need to create multiple script-blocks?
I mean, I need to create

  1. gco => git checkout
  2. gcob => git checkout -b
  3. gp => git push

like this

Now my profile

function gcob {
    param ($commandComplete)
    git checkout -b ${commandComplete}
}

$scriptBlock = {
     param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
     Expand-GitCommand "git checkout -b $commandName"
}

Register-ArgumentCompleter -CommandName gcob -ScriptBlock $scriptBlock

@MisinformedDNA
Copy link

Yes, you need separate script blocks for each one.

@suhaibbaba
Copy link

@MisinformedDNA gotcha!
Thanks for helping

@MisinformedDNA
Copy link

I find that passing in a parameter doesn't work correctly in certain scenarios. For instance, if you run this:

function gl { 
    param ($commandComplete)
    git log $commandComplete
}
$scriptBlock = {
    param($wordToComplete, $commandAst, $cursorPosition)

    Expand-GitCommand "git log $wordToComplete"
}
Register-ArgumentCompleter -Native -CommandName gl -ScriptBlock $scriptBlock

gl-<TAB>

You will get

gl -commandComplete

Instead, I use:

function gl { git log @args }
$scriptBlock = {
    param($wordToComplete, $commandAst, $cursorPosition)

    Expand-GitCommand "git log $wordToComplete"
}
Register-ArgumentCompleter -Native -CommandName gl -ScriptBlock $scriptBlock

NOTE: I've also explicitly set the native parameter (even though it is implied) and changed the names of the script parameters to reflect the native parameters.

@mrbeardad
Copy link

The code below works well for me. It will complete the parameter of git subcommand or subsubcommand correctly.

function RegisterGit {
  Invoke-Expression -Command "function global:$($args[0]) { git $($args[1]) @args }"
  $tab = [Scriptblock]::Create("
    param(`$wordToComplete, `$commandAst, `$cursorPosition)
    `$cmdline = `$commandAst.ToString().Replace(`"$($args[0])`", `"`")
    if (`$wordToComplete.Length -ne 0) { `$tail = '' } else { `$tail = ' ' }
    Expand-GitCommand `"git $($args[1]) `$cmdline`$tail`"
  ")
  Register-ArgumentCompleter -CommandName $args[0] -ScriptBlock $tab
}

# define function gst to execute git remote, and register the tab completer for it.
RegisterGit gr "remote"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

9 participants