SnippetPredictor.PSReadLine.psm1
|
$script:SnippetPredictorKeyHandlerSession = $null $script:SnippetPredictorPredictionKeyHandlerSession = $null $script:SnippetPredictorKeyHandlerBindings = @() function Invoke-SnippetPredictorKeyHandler { [CmdletBinding()] param( [ValidateSet(-1, 1)] [int] $Direction ) $line = $null $cursor = 0 [Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState( [ref] $line, [ref] $cursor ) $session = $script:SnippetPredictorKeyHandlerSession $isCursorAtEnd = $cursor -eq $line.Length if ( $isCursorAtEnd -and $null -ne $session -and $line -eq $session.LastReplacement -and $session.Matches.Count -gt 0 ) { $nextIndex = ($session.Index + $Direction + $session.Matches.Count) % $session.Matches.Count $nextReplacement = $session.Matches[$nextIndex] [Microsoft.PowerShell.PSConsoleReadLine]::Replace( 0, $line.Length, $nextReplacement ) $script:SnippetPredictorKeyHandlerSession = [pscustomobject]@{ Matches = $session.Matches Index = $nextIndex LastReplacement = $nextReplacement } return $true } [string[]] $completions = $isCursorAtEnd ? [SnippetPredictor.Integration]::GetCompletionTexts($line) : $null if ($completions.Count -gt 0) { $index = $Direction -lt 0 ? $completions.Count - 1 : 0 $replacement = $completions[$index] [Microsoft.PowerShell.PSConsoleReadLine]::Replace( 0, $line.Length, $replacement ) $script:SnippetPredictorKeyHandlerSession = if ($completions.Count -gt 1) { [pscustomobject]@{ Matches = $completions Index = $index LastReplacement = $replacement } } else { $null } return $true } $script:SnippetPredictorKeyHandlerSession = $null return $false } function Invoke-SnippetPredictorPredictionKeyHandler { [CmdletBinding()] param( $Key, $Arg, [ValidateSet(-1, 1)] [int] $Direction ) $line = $null $cursor = 0 [Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState( [ref] $line, [ref] $cursor ) $session = $script:SnippetPredictorPredictionKeyHandlerSession $isCursorAtEnd = $cursor -eq $line.Length $isContinuation = ( $isCursorAtEnd -and $null -ne $session -and $line -eq $session.LastReplacement ) [string[]] $completions = if ($isCursorAtEnd -and -not $isContinuation) { [SnippetPredictor.Integration]::GetCompletionTexts($line) } if ($isContinuation -or $completions.Count -gt 0) { if ($Direction -lt 0) { [Microsoft.PowerShell.PSConsoleReadLine]::PreviousSuggestion($Key, $Arg) } else { [Microsoft.PowerShell.PSConsoleReadLine]::NextSuggestion($Key, $Arg) } [Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState( [ref] $line, [ref] $cursor ) $script:SnippetPredictorPredictionKeyHandlerSession = [pscustomobject]@{ LastReplacement = $line } return $true } $script:SnippetPredictorPredictionKeyHandlerSession = $null return $false } $script:SnippetPredictorTabCompleteNextComposableHandler = { param($key, $arg) Invoke-SnippetPredictorKeyHandler -Direction 1 } $script:SnippetPredictorTabCompletePreviousComposableHandler = { param($key, $arg) Invoke-SnippetPredictorKeyHandler -Direction -1 } $script:SnippetPredictorNextSuggestionComposableHandler = { param($key, $arg) Invoke-SnippetPredictorPredictionKeyHandler -Key $key -Arg $arg -Direction 1 } $script:SnippetPredictorPreviousSuggestionComposableHandler = { param($key, $arg) Invoke-SnippetPredictorPredictionKeyHandler -Key $key -Arg $arg -Direction -1 } $script:SnippetPredictorTabCompleteNextHandler = { param($key, $arg) if (-not (& $script:SnippetPredictorTabCompleteNextComposableHandler $key $arg)) { [Microsoft.PowerShell.PSConsoleReadLine]::TabCompleteNext($key, $arg) } } $script:SnippetPredictorTabCompletePreviousHandler = { param($key, $arg) if (-not (& $script:SnippetPredictorTabCompletePreviousComposableHandler $key $arg)) { [Microsoft.PowerShell.PSConsoleReadLine]::TabCompletePrevious($key, $arg) } } function Test-SnippetPredictorKeyHandlerBinding { [CmdletBinding()] param( [Parameter(Mandatory)] [pscustomobject] $Binding ) $current = Get-PSReadLineKeyHandler -Chord $Binding.Chord -ErrorAction SilentlyContinue | Where-Object Function -CEQ $Binding.BriefDescription return $null -ne $current } function Remove-SnippetPredictorKeyHandlerBinding { [CmdletBinding()] param( [Parameter(Mandatory)] [pscustomobject] $Binding ) if (-not (Test-SnippetPredictorKeyHandlerBinding -Binding $Binding)) { return } switch -CaseSensitive ($Binding.Chord) { 'Tab' { Set-PSReadLineKeyHandler -Chord $Binding.Chord -Function TabCompleteNext -ErrorAction Stop } 'Shift+Tab' { Set-PSReadLineKeyHandler -Chord $Binding.Chord -Function TabCompletePrevious -ErrorAction Stop } default { Remove-PSReadLineKeyHandler -Chord $Binding.Chord -ErrorAction Stop } } } function Remove-SnippetPredictorKeyHandlerBindings { [CmdletBinding()] param() $remainingBindings = [System.Collections.Generic.List[object]]::new() $cleanupErrors = [System.Collections.Generic.List[System.Management.Automation.ErrorRecord]]::new() foreach ($binding in $script:SnippetPredictorKeyHandlerBindings) { try { Remove-SnippetPredictorKeyHandlerBinding -Binding $binding } catch { $remainingBindings.Add($binding) $cleanupErrors.Add($_) } } $script:SnippetPredictorKeyHandlerBindings = $remainingBindings.ToArray() $script:SnippetPredictorKeyHandlerSession = $null if ($cleanupErrors.Count -gt 0) { $PSCmdlet.ThrowTerminatingError($cleanupErrors[0]) } } function New-SnippetPredictorKeyHandler { [CmdletBinding()] param( [ValidateSet( 'TabCompleteNext', 'TabCompletePrevious', 'NextSuggestion', 'PreviousSuggestion' )] [string] $Action = 'TabCompleteNext' ) switch ($Action) { 'TabCompletePrevious' { $script:SnippetPredictorTabCompletePreviousComposableHandler } 'NextSuggestion' { $script:SnippetPredictorNextSuggestionComposableHandler } 'PreviousSuggestion' { $script:SnippetPredictorPreviousSuggestionComposableHandler } default { $script:SnippetPredictorTabCompleteNextComposableHandler } } } function Disable-SnippetPredictorKeyHandler { [CmdletBinding()] param() Remove-SnippetPredictorKeyHandlerBindings } function Enable-SnippetPredictorKeyHandler { [CmdletBinding()] param( [string] $NextChord = 'Tab', [string] $PreviousChord = 'Shift+Tab' ) if ($NextChord -ceq $PreviousChord) { throw 'NextChord and PreviousChord must be different.' } Remove-SnippetPredictorKeyHandlerBindings $script:SnippetPredictorKeyHandlerSession = $null $bindings = @( [pscustomobject]@{ Chord = $NextChord BriefDescription = 'SnippetPredictorTabCompleteNext' Description = 'Complete SnippetPredictor input using the next candidate' ScriptBlock = $script:SnippetPredictorTabCompleteNextHandler } [pscustomobject]@{ Chord = $PreviousChord BriefDescription = 'SnippetPredictorTabCompletePrevious' Description = 'Complete SnippetPredictor input using the previous candidate' ScriptBlock = $script:SnippetPredictorTabCompletePreviousHandler } ) $registeredBindings = [System.Collections.Generic.List[object]]::new() try { foreach ($binding in $bindings) { Set-PSReadLineKeyHandler ` -Chord $binding.Chord ` -ScriptBlock $binding.ScriptBlock ` -BriefDescription $binding.BriefDescription ` -Description $binding.Description ` -ErrorAction Stop $registeredBindings.Add($binding) } } catch { $registrationError = $_ $remainingBindings = [System.Collections.Generic.List[object]]::new() foreach ($binding in $registeredBindings) { try { Remove-SnippetPredictorKeyHandlerBinding -Binding $binding } catch { $remainingBindings.Add($binding) Write-Error -ErrorRecord $_ -ErrorAction Continue } } $script:SnippetPredictorKeyHandlerBindings = $remainingBindings.ToArray() throw $registrationError } $script:SnippetPredictorKeyHandlerBindings = $bindings } $ExecutionContext.SessionState.Module.OnRemove += { Remove-SnippetPredictorKeyHandlerBindings } Export-ModuleMember -Function @( 'Disable-SnippetPredictorKeyHandler' 'Enable-SnippetPredictorKeyHandler' 'New-SnippetPredictorKeyHandler' ) |