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

One attempt at providing a prompt function during module import. #349

Merged
merged 7 commits into from
Jan 4, 2017

Conversation

rkeithhill
Copy link
Collaborator

Fix #217. This would only export the prompt function if the prompt function is still set to the default.

I'm not suggesting we accept this just yet, just wanted to put something up folks could play with.

Fix #217.  This would only export the prompt function if the prompt function is still set to the default.
This was a tip from @lzybkr and it seems to solve the issue with removal of the module wiping out the prompt function.  Yay!
@rkeithhill rkeithhill changed the title One attempt to exporting a prompt function. One attempt at providing a prompt function during module import. Jan 3, 2017
@@ -11,26 +11,79 @@ if ($psv.Major -lt 3 -and !$NoVersionWarn) {
"To suppress this warning, change your profile to include 'Import-Module posh-git -Args `$true'.")
}

Push-Location $psScriptRoot
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we just getting lucky that nobody using PS v2 has complained about this breaking anything?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There was a pop-location later but if something crashed out before the pop-location, it would have left the current location to the module's root dir.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh yeah, $PSScriptRoot works only inside module (PSM1) files for PS v2 but not in PS1 files IIRC. That was fixed in v3 I believe.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, that makes more sense 👍

$Global:VcsPromptStatuses += $PoshGitVcsPrompt
$ExecutionContext.SessionState.Module.OnRemove = {
$Global:VcsPromptStatuses = $Global:VcsPromptStatuses | Where-Object { $_ -ne $PoshGitVcsPrompt }
Copy link
Owner

@dahlbyk dahlbyk Jan 3, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why remove?

Update: Nevermind, I see it was just moved into the .psm1.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BTW I need to update that logic a bit to check if someone might have installed a different prompt function after we installed the posh-git default one.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And as a "module level" operation, posh-git.psm1 seemed like a more fitting place for this handler.

Also handle the case where the user creates a different prompt function *after* posh-git sets the prompt function.  In that case, we won't restore the original prompt function that we stashed.
@rkeithhill
Copy link
Collaborator Author

rkeithhill commented Jan 4, 2017

I've done some testing on PS v2 and PS v5 both with modified prompt functions and without. It is looking pretty good and I'm feeling better about posh-git installing this when the user hasn't modified their prompt function.

BTW when the user has their own prompt function that uses posh-git commands, we don't remove it. This has the consequence of causing posh-git to be immediately reloaded when their prompt is evaluated after remove-module posh-git.

There are several workarounds including configuring a dumb prompt to allow posh-git to be removed e.g.:

function prompt { "$(pwd)> " }

or for v3 or higher

Set-Item Function:\prompt ([Runspace]::DefaultRunspace.InitialSessionState.Commands['prompt'].Definition)

They can also close all PowerShell sessions and start powershell with the -noprofile parameter. I believe this may be important in certain install situations (perhaps with Chocolatey). For PowerShellGet, each different version of posh-git will be installed into its own dir. So there shouldn't be any conflicts with updating.

@@ -306,8 +306,4 @@ $PoshGitVcsPrompt = {
Write-GitStatus $GitStatus
}

# Install handler for removal/unload of the module
$Global:VcsPromptStatuses += $PoshGitVcsPrompt
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And as a "module level" operation, posh-git.psm1 seemed like a more fitting place for this handler.

I wonder if this should move alongside the removal operation?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we do that, should we also move the decl/assignment of $PoshGitVcsPrompt just above this line as well?

# The latter is more desirable.
$pathInfo = $ExecutionContext.SessionState.Path.CurrentLocation
$curPath = if ($pathInfo.Drive) { $pathInfo.Path } else { $pathInfo.ProviderPath }
if ($curPath -and $curPath.ToLower().StartsWith($Home.ToLower()))
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a fan of the $Home check, nice addition!

  1. Favor the StringComparison overload over comparing ToLowered strings
  2. According to MSDN:

    The string behavior of the file system, registry keys and values, and environment variables is best represented by StringComparison.OrdinalIgnoreCase.

Therefore: $curPath.StartsWith($Home, [StringComparison]::OrdinalIgnoreCase)

Commence countdown to case-insensitivity bug on Linux: 5...4...3...

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can check for Linux and use Ordinal.

$currentPromptDef = if ($funcInfo = Get-Command prompt -ErrorAction SilentlyContinue) { $funcInfo.Definition }
if (!$currentPromptDef -or ($currentPromptDef -eq $defaultPromptDef)) {
Set-Item Function:\prompt -Value {
$origLastExitCode = $LASTEXITCODE
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PS amateur question: any reason to use $global:LASTEXITCODE here, as below?

$promptSuffix = "`n[DBG]: PS>> "
}
else {
$promptSuffix = "`nPS> "
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer not to change the default posh-git prompt since it's what non-customizing users (like me 😀) are now used to. But we could add PromptPrefix and PromptSuffix (and PromptDebug*ix?) to $GitPromptSettings to make this particular arrangement easier to achieve without modifying prompt.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK by me. I was just floating this is an idea. If we added a setting, what would it be called? CommandEntryOnNewline?


# If there is no prompt function or the prompt function is the default, export the posh-git prompt function.
$promptReplaced = $false
$currentPromptDef = if ($funcInfo = Get-Command prompt -ErrorAction SilentlyContinue) { $funcInfo.Definition }
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason to prefer your construction over this one?

$currentPromptDef = Get-Command prompt -ErrorAction SilentlyContinue) | Select-Object -ExpandProperty Definition

$promptSuffix
}

$promptReplaced = $true
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we attach a property to our prompt instead of using a separate flag? I don't suppose there's any way to set its Source = "posh-git" like all the other functions we export?

@dahlbyk
Copy link
Owner

dahlbyk commented Jan 4, 2017

BTW when the user has their own prompt function that uses posh-git commands, we don't remove it. This has the consequence of causing posh-git to be immediately reloaded when their prompt is evaluated after remove-module posh-git.

Interesting... So my first thought is to have OnRemove check if the prompt it's about to restore includes Write-VcsStatus, and if so create a global Write-VcsStatus no-op on its way out (perhaps with some sort of warning that it did so). Or always create Write-VcsStatus just in case. Or let the reinstall loop happen - how dare they uninstall posh-git, anyway?

Then again, technically Write-VcsStatus is meant to be the shim through which posh-hg and friends are also exposed, so that function really shouldn't belong to posh-git at all. So we could make posh-git depend on a posh-vcs module (maintained here or moved to posh-vcs) which provides Write-VcsStatus, Un/Register-VcsStatusPrompt, and the default prompt infrastructure. Removing posh-git would then simply Unregister-VcsStatusPrompt, effectively punting the reinstall cycle to posh-vcs. I have no idea if there remains any demand for VCS support in PowerShell other than Git. (https://github.com/dahlbyk/posh-tf never took off :trollface:)

Or we could hack it (Write-VcsStatus no-op or equivalent) until 1.0 at which point we retire the Write-VcsStatus cruft in favor of a posh-git prompt that calls Write-GitStatus.

@rkeithhill
Copy link
Collaborator Author

I hadn't quite grokked what the Vcs* functions were for yet. Thanks for the info.

Or we could hack it (Write-VcsStatus no-op or equivalent) until 1.0

But in my prompt function I use Write-GitStatus (Get-GitStatus) so this could get a bit messy. I don't think uninstall would be all that common. In fact, remove-module posh-git is likely to be rare as well. And this would only effect folks who have a customized prompt function. So I guess I'm not sure we need to worry about this for 0.7.0 other than to be aware that it could crop up from time-to-time. Plus there are simple workarounds.

@dahlbyk
Copy link
Owner

dahlbyk commented Jan 4, 2017

So how about we just log a warning OnRemove if we are restoring a non-default prompt, regardless of its contents, e.g. "If prompt still references posh-git functions, it may cause the module to re-import.")? That should be enough info to explain a reimport without overcomplicating things.

@rkeithhill
Copy link
Collaborator Author

Sounds good to me. I'm working on an update to this PR to address the StringComparison issue and a way to select a two line prompt. Any thoughts on the name of that setting? I'm currently using CommandEntryOnNewline but maybe it should be TwoLinePrompt?

@dahlbyk
Copy link
Owner

dahlbyk commented Jan 4, 2017

Any thoughts on the name of that setting? I'm currently using CommandEntryOnNewline but maybe it should be TwoLinePrompt?

I think we can get away with:

# Write the Git status summary information.
Write-VcsStatus

$debugMode = (Test-Path Variable:\PSDebugContext) -or [runspace]::DefaultRunspace.Debugger.InBreakpoint

$global:LASTEXITCODE = $origLastExitCode
if ($debugMode) {
    Write-Host (if ($GitPromptSettings.PromptDebugSuffix) { $GitPromptSettings.PromptDebugSuffix } else { ' [DBG]>> ' })
} else {
    Write-Host (if ($GitPromptSettings.PromptSuffix) { $GitPromptSettings.PromptSuffix } else { '> ' })
}

Your two-line prompt config becomes:

$GitPromptSettings.PromptDebugSuffix = '`n[DBG]: PS>> '
$GitPromptSettings.PromptSuffix = '`nPS> '
# plus Powerline config if applicable, which posh-git doesn't need to know

And anything more complex could be handled by a custom prompt.

@rkeithhill
Copy link
Collaborator Author

What if we defaulted these two settings e.g.:

$GitPromptSettings.PromptDebugSuffix = ' [DBG]: >> '
$GitPromptSettings.PromptSuffix = '> '

That would also help folks know how to use these two settings.

Change path string comparison to use [StringComparison] appropriate for the platform.

Add warning in OnRemove that let's user know that their custom prompt function may cause posh-git to be re-imported.

Change to use [scriptblock]::Create() otherwise $debugMode condition lied on PS v2 (although it worked fine on v5).  Anyway, this change fixes this on v2.  Also removed script to mess with PSReadline''s ExtraPromptLineCount.
Copy link
Owner

@dahlbyk dahlbyk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just trivial stuff now. I'm liking where this has ended up.

Should we also drop the prompt definition from profile.example.ps1?

$currentPath = if ($pathInfo.Drive) { $pathInfo.Path } else { $pathInfo.ProviderPath }

# File system paths are case-sensitive on Linux and case-insensitive on Windows and macOS
if (($PSVersionTable.PSVersion.Major -ge 6) -and $IsLinux) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Micro-optimization, but we could calculate $stringComparison once, assuming we can get the scriptblock to close over the variable.

$promptReplaced = $true
}

# Install handler for removal/unload of the module
$ExecutionContext.SessionState.Module.OnRemove = {
$Global:VcsPromptStatuses = $Global:VcsPromptStatuses | Where-Object { $_ -ne $PoshGitVcsPrompt }
$global:VcsPromptStatuses = $global:VcsPromptStatuses | Where-Object { $_ -ne $PoshGitVcsPrompt }

if ($promptReplaced) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wondering if this flag has been made obsolete by the $promptDef -eq $poshGitPromptScriptBlock check? I suppose it's an optimization, but I wouldn't be opposed to reducing complexity at the expense of a marginally slower uninstall.

@rkeithhill
Copy link
Collaborator Author

rkeithhill commented Jan 4, 2017

Should we also drop the prompt definition from profile.example.ps1?

I think so. Perhaps we can rename that as well? I tend to following a pattern in my profile scripts of <module-name>_config.ps1. It's a practice I've ripped off from Bash.

but we could calculate $stringComparison once

The test takes between 2 & 4 mSecs and StringComparison is an enum (no heap impact). It's probably not worth bothering.

Wondering if this flag has been made obsolete

There's a small chance someone could have created a custom prompt function by copying that code. In which case, we would remove it. I could go either way.

@dahlbyk
Copy link
Owner

dahlbyk commented Jan 4, 2017

I think so. Perhaps we can rename that as well?

Well anyone using install.ps1 has a $PROFILE reference to profile.example.ps1, so we should rename/deprecate that file with prejudice. I'm inclined to defer fiddling there until after 0.7 drops when we settle on the install/import strategy for 1.0 (#328). Even then, it's probably more user-friendly for 1.0 to ship a profile.example.ps1 that simply warns that the file is obsolete and that profiles should be updated to not reference it since it will be going away with 2.0.

The test takes between 2 & 4 mSecs and StringComparison is an enum (no heap impact). It's probably not worth bothering.

👍

Wondering if this flag has been made obsolete

There's a small chance someone could have created a custom prompt function by copying that code. In which case, we would remove it. I could go either way.

I'm all for simplifying the code we have to maintain. If someone customizes their prompt poorly, that's on them - they can see all our source here.

@dahlbyk dahlbyk added this to the v0.7 milestone Jan 4, 2017
Update to warning message in OnRemove.
@rkeithhill
Copy link
Collaborator Author

rkeithhill commented Jan 4, 2017

I'm inclined to defer fiddling there until after 0.7 drops when we settle on the install/import strategy for 1.0 (#328).

👍

Should we also drop the prompt definition from profile.example.ps1?

Should we still remove the prompt function? I'm guessing yes but wanted to double check.

@rkeithhill
Copy link
Collaborator Author

rkeithhill commented Jan 4, 2017

Also, if someone sets their $GitPromptSettings.PromptSuffix to something like $null or an empty string, PowerShell will not like that and use PS> instead e.g.:

~\GitHub\rkeithhill\posh-git [auto-export-prompt ≡]> $GitPromptSettings.PromptSuffix = ''
~\GitHub\rkeithhill\posh-git [auto-export-prompt ≡]PS>

I can put in a check for $null or empty string and then force the $promptSuffix to ' '. But should we try to save the user from themselves?

@dahlbyk
Copy link
Owner

dahlbyk commented Jan 4, 2017

Should we still remove the prompt function? I'm guessing yes but wanted to double check.

Yes.

I can put in a check for $null or empty string and then force the $promptSuffix to ' '. But should we try to save the user from themselves?

That's what motivated the extra complexity in my version - if you don't give a good override, you get the default experience. Still a good idea to set the settings properties to match the default.

if ($debugMode) {
    Write-Host (if ($GitPromptSettings.PromptDebugSuffix) { $GitPromptSettings.PromptDebugSuffix } else { ' [DBG]>> ' })
} else {
    Write-Host (if ($GitPromptSettings.PromptSuffix) { $GitPromptSettings.PromptSuffix } else { '> ' })
}

Add check for $prompSuffix null/empty.
@dahlbyk
Copy link
Owner

dahlbyk commented Jan 4, 2017

I'm feeling pretty good about this! Is there any more testing you want to do on v2 or v5 (is v3 a concern)?

@rkeithhill
Copy link
Collaborator Author

OK tested on v3. Works fine. I think this is ready to be merged.

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

Successfully merging this pull request may close these issues.

2 participants