src/NuGet.Clients/NuGet.PackageManagement.PowerShellCmdlets/Scripts/nuget.psm1 (325 lines of code) (raw):

# make sure we stop on exceptions $ErrorActionPreference = "Stop" # This object reprents the result value for tab expansion functions when no result is returned. # This is so that we can distinguish it from $null, which has different semantics $NoResultValue = New-Object PSObject -Property @{ NoResult = $true } # Hashtable that stores tab expansion definitions $TabExpansionCommands = New-Object 'System.Collections.Hashtable' -ArgumentList @([System.StringComparer]::InvariantCultureIgnoreCase) function Register-TabExpansion { <# .SYNOPSIS Registers a tab expansion for the parameters of the specified command. .DESCRIPTION Registers a tab expansion for the parameters of the specified command. .PARAMETER Name Name of the command the expansion is for. .EXAMPLE PS> Register-TabExpansion 'Set-Color', @{'color' = {'blue', 'green', 'red'}} This adds a tab expansion to the Set-Color command. Set-Color contains a single parameter, color, with three possible expansion values. #> [CmdletBinding()] param( [parameter(Mandatory = $true)] [string]$Name, [parameter(Mandatory = $true)] $Definition ) # transfer $definition data into a new hashtable that compare values using InvariantCultureIgnoreCase $normalizedDefinition = New-Object 'System.Collections.Hashtable' -ArgumentList @([System.StringComparer]::InvariantCultureIgnoreCase) $definition.GetEnumerator() | % { $normalizedDefinition[$_.Name] = $_.Value } $TabExpansionCommands[$Name] = $normalizedDefinition } Register-TabExpansion 'Get-Package' @{ 'Source' = { GetPackageSources } 'ProjectName' = { GetProjectNames } } Register-TabExpansion 'Install-Package' @{ 'Id' = { param($context) GetRemotePackageIds $context } 'ProjectName' = { GetProjectNames } 'Version' = { param($context) GetRemotePackageVersions $context } 'Source' = { GetPackageSources } 'DependencyVersion' = { GetEnumNames 'NuGet.Resolver.DependencyBehavior' } 'FileConflictAction' = { GetEnumNames 'NuGet.ProjectManagement.FileConflictAction' } } Register-TabExpansion 'Uninstall-Package' @{ 'Id' = { param($context) GetInstalledPackageIds $context } 'ProjectName' = { GetProjectNames } 'Version' = { GetInstalledPackageVersions $context } } Register-TabExpansion 'Update-Package' @{ 'Id' = { param($context) GetInstalledPackageIds $context } 'ProjectName' = { GetProjectNames } 'Version' = { param($context) # Only show available versions if an id was specified if ($context.id) { # Find the installed package (this might be nothing since we could have a partial id) $versions = @() $packages = @(Get-Package $context.id | ? { $_.Id -eq $context.id }) if($packages.Count) { $package = @($packages | Sort-Object Version)[0] $versions = GetRemotePackageUpdateVersions $context } $versions } } 'Source' = { GetPackageSources } 'FileConflictAction' = { GetEnumNames 'NuGet.ProjectManagement.FileConflictAction' } } Register-TabExpansion 'Add-BindingRedirect' @{ 'ProjectName' = { GetProjectNames } } Register-TabExpansion 'Get-Project' @{ 'Name' = { GetProjectNames } } function HasProperty($context, $name) { return $context.psobject.properties | ? { $_.Name -eq $name } } function IsPrereleaseSet($context) { # Need to figure out a better way to do this. return (HasProperty $context 'IncludePreRelease') -or (HasProperty $context 'PreRelease') -or (HasProperty $context 'Pre') } function GetPackages($context) { $parameters = @{} if ($context.Id) { $parameters.Id = $context.Id } if ($context.Source) { $parameters.source = $context.Source } if (IsPrereleaseSet $context) { $parameters.IncludePreRelease = $true } # StartWith switch is implicity set for TabExpansion command return TabExpansion-Package @parameters -ExcludeVersionInfo } function GetProjectNames { $uniqueNames = @(Get-Project -All | Select-Object -ExpandProperty ProjectName) $simpleNames = Get-Project -All | Select-Object -ExpandProperty Name $safeNames = @($simpleNames | Group-Object | Where-Object { $_.Count -eq 1 } | Select-Object -ExpandProperty Name) ($uniqueNames + $safeNames) | Select-Object -Unique | Sort-Object } function GetInstalledPackageIds($context) { $parameters = @{} if ($context.Id) { $parameters.filter = $context.id } Get-Package @parameters -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Id -Unique } function GetRemotePackageIds($context) { $parameters = @{} if ($context.Id) { $parameters.filter = $context.Id } if ($context.Source) { $parameters.source = $context.Source } if (IsPrereleaseSet $context) { $parameters.IncludePrerelease = $true } try { return Get-RemotePackageId @parameters } catch { # If the server doesn't have the JSON API endpoints, get the remote package IDs the old way. return GetPackages $context | Select-Object -ExpandProperty Id -Unique } } function GetPackageSources() { $componentModel = Get-VSComponentModel $repositoryProvider = $componentModel.GetService([NuGet.Protocol.Core.Types.ISourceRepositoryProvider]) $allSources = $repositoryProvider.PackageSourceProvider.LoadPackageSources() $allSources | Select-Object -ExpandProperty Name } function GetEnumNames($typeName) { # Sort the enumerations in alphabetical order to make it consistent with TabExpansion2 return [System.Enum]::GetNames($typeName) | Sort-Object } function GetInstalledPackageVersions($context) { $parameters = @{} if ($context.id) { $parameters.Filter = $context.id } GetAndSortVersions (Get-Package @parameters -ErrorAction SilentlyContinue) } function GetRemotePackageVersions($context) { $parameters = @{} if ($context.Id -eq $null) { return @() } if ($context.Id) { $parameters.id = $context.Id } if ($context.Source) { $parameters.source = $context.Source } if (IsPrereleaseSet $context) { $parameters.IncludePreRelease = $true } try { return Get-RemotePackageVersion @parameters | %{ [NuGet.SemanticVersion]::Parse($_) } | Sort-Object -Descending } catch { # If the server doesn't have the JSON API endpoints, get the remote package versions the old way. $parameters = @{} if ($context.Id) { $parameters.Id = $context.Id } if ($context.Source) { $parameters.source = $context.Source } if (IsPrereleaseSet $context) { $parameters.IncludePreRelease = $true } $parameters.AllVersions = $true # StartWith switch is implicity set for TabExpansion command GetAndSortVersions(TabExpansion-Package @parameters -ExactMatch -ErrorAction SilentlyContinue) } } function GetRemotePackageUpdateVersions($context) { $parameters = @{} if ($context.Id -eq $null) { return @() } if ($context.Id) { $parameters.id = $context.Id } if ($context.Source) { $parameters.source = $context.Source } if (IsPrereleaseSet $context) { $parameters.IncludePreRelease = $true } try { return Get-RemotePackageVersion @parameters | %{ [NuGet.SemanticVersion]::Parse($_) } | Sort-Object -Descending } catch { # If the server doesn't have the JSON API endpoints, get the remote package versions the old way. $parameters = @{} if ($context.Id) { $parameters.Filter = $context.Id } if ($context.Source) { $parameters.source = $context.Source } if (IsPrereleaseSet $context) { $parameters.IncludePreRelease = $true } $parameters.Updates = $true GetAndSortVersions(Get-Package @parameters -AllVersions -ErrorAction SilentlyContinue) } } function GetAndSortVersions($packages) { $packages | Select -Unique -ExpandProperty Versions | %{ if($_ -is [string]) { [NuGet.SemanticVersion]::Parse($_) } else { $_ } } | Sort-Object -Descending } function NugetTabExpansion($line, $lastWord) { # Parse the command $parsedCommand = [NuGetConsole.Host.PowerShell.CommandParser]::Parse($line) # Get the command definition $definition = $TabExpansionCommands[$parsedCommand.CommandName] # See if we've registered a command for intellisense if($definition) { # Get the command that we're trying to show intellisense for $command = Get-Command $parsedCommand.CommandName -ErrorAction SilentlyContinue if($command) { # We're trying to find out what parameter we're trying to show intellisense for based on # either the name of the an argument or index e.g. "Install-Package -Id " "Install-Package " $argument = $parsedCommand.CompletionArgument $index = $parsedCommand.CompletionIndex if(!$argument -and $index -ne $null) { do { # Get the argument name for this index $argument = GetArgumentName $command $index if(!$argument) { break } # If there is already a value for this argument, then check the next one index. # This is so we don't show duplicate intellisense e.g. "Install-Package -Id elmah {tab}". # The above statement shouldn't show intellisense for id since it already has a value if($parsedCommand.Arguments[$argument] -eq $null) { $value = $parsedCommand.Arguments[$index] if(!$value) { $value = '' } $parsedCommand.Arguments[$argument] = $value break } else { $index++ } } while($true); } if($argument) { # Populate the arguments dictionary with the name and value of the # associated index. i.e. for the command "Install-Package elmah" arguments should have # an entries with { 0, "elmah" } and { "Id", "elmah" } $arguments = New-Object 'System.Collections.Hashtable' -ArgumentList @([System.StringComparer]::InvariantCultureIgnoreCase) $parsedCommand.Arguments.Keys | Where-Object { $_ -is [int] } | %{ $argName = GetArgumentName $command $_ $arguments[$argName] = $parsedCommand.Arguments[$_] } # Copy the arguments over to the parsed command arguments $arguments.Keys | %{ $parsedCommand.Arguments[$_] = $arguments[$_] } # If the argument is a true argument of this command and not a partial argument # and there is a non null value (empty is valid), then we execute the script block # for this parameter (if specified) $action = $definition[$argument] $argumentValue = $parsedCommand.Arguments[$argument] if($command.Parameters[$argument] -and $argumentValue -ne $null -and $action) { $context = New-Object PSObject -Property $parsedCommand.Arguments $results = @(& $action $context) if($results.Count -eq 0) { return $null } # Use the argument value to filter results $results = $results | %{ $_.ToString() } | Where-Object { $_.StartsWith($argumentValue, "OrdinalIgnoreCase") } return NormalizeResults $results } } } } return $NoResultValue } function NormalizeResults($results) { $results | %{ $result = $_ # Add quotes to a result if it contains whitespace or a quote $addQuotes = $result.Contains(" ") -or $result.Contains("'") -or $result.Contains("`t") if($addQuotes) { $result = "'" + $result.Replace("'", "''") + "'" } return $result } } function GetArgumentName($command, $index) { # Next we try to find the parameter name for the parameter index (in the default parameter set) $parameterSet = $Command.DefaultParameterSet if(!$parameterSet) { $parameterSet = '__AllParameterSets' } return $command.Parameters.Values | ?{ $_.ParameterSets[$parameterSet].Position -eq $index } | Select -ExpandProperty Name } function Format-ProjectName { param( [parameter(position=0, mandatory=$true)] [validatenotnull()] $Project, [parameter(position=1, mandatory=$true)] [validaterange(6, 1000)] [int]$ColWidth ) return $project.name }