functions/tabexpansion/Register-PSFTeppScriptblock.ps1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
function Register-PSFTeppScriptblock
{
    <#
        .SYNOPSIS
            Registers a scriptblock under name, to later be available for TabExpansion.
         
        .DESCRIPTION
            Registers a scriptblock under name, to later be available for TabExpansion.
  
   This system supports two separate types of input: Full or Simple.
  
   Simple:
   The scriptblock simply must return string values.
   PSFramework will then do the rest of the processing when the user asks for tab completion.
   This is the simple-most way to implement tab completion, for a full example, look at the first example in this help.
  
   Full:
   A full scriptblock implements all that is needed to provide Tab Expansion.
   For more details and guidance, see the following concept help:
    Get-Help about_psf_tabexpansion
         
        .PARAMETER ScriptBlock
            The scriptblock to register.
         
        .PARAMETER Name
            The name under which the scriptblock should be registered.
   It is recommended to prefix the name with the module (e.g.: mymodule.<name>), as names are shared across all implementing modules.
  
  .PARAMETER Mode
   Whether the script provided is a full or simple scriptblock.
   By default, this function automatically detects this, but just in case, you can override this detection.
  
  .PARAMETER CacheDuration
   How long a tab completion result is valid.
   By default, PSFramework tab completion will run the scriptblock on each call.
   This can be used together with a background refresh mechanism to offload the cost of expensive queries into the background.
   See Set-PSFTeppResult for details on how to refresh the cache.
  
  .EXAMPLE
   Register-PSFTeppScriptblock -Name "psalcohol-liquids" -ScriptBlock { "beer", "mead", "wine", "vodka", "whiskey", "rum" }
   Register-PSFTeppArgumentCompleter -Command Get-Alcohol -Parameter Type -Name "psalcohol-liquids"
  
   In step one we set a list of questionable liquids as the list of available beverages for parameter 'Type' on the command 'Get-Alcohol'
         
        .EXAMPLE
            Register-PSFTeppScriptblock -ScriptBlock $scriptBlock -Name MyFirstTeppScriptBlock
     
            Stores the scriptblock stored in $scriptBlock under the name "MyFirstTeppScriptBlock"
  
  .EXAMPLE
   $scriptBlock = { (Get-ChildItem (Get-PSFConfigValue -FullName mymodule.path.scripts -Fallback "$env:USERPROFILE\Documents\WindowsPowerShell\Scripts")).FullName }
   Register-PSFTeppScriptblock -Name mymodule-scripts -ScriptBlock $scriptBlock -Mode Simple
  
   Stores a simple scriptblock that will return a list of strings under the name "mymodule-scripts".
   The system will wrap all the stuff around this that is necessary to provide Tab Expansion and filter out output that doesn't fit the user input so far.
    #>

    [CmdletBinding(HelpUri = 'https://psframework.org/documentation/commands/PSFramework/Register-PSFTeppScriptblock')]
    Param (
        [Parameter(Mandatory = $true)]
        [System.Management.Automation.ScriptBlock]
        $ScriptBlock,
        
        [Parameter(Mandatory = $true)]
        [string]
        $Name,
        
        [PSFramework.TabExpansion.TeppScriptMode]
        $Mode = "Auto",
        
        [PSFramework.Parameter.TimeSpanParameter]
        $CacheDuration = 0
    )
    
    $scp = New-Object PSFramework.TabExpansion.ScriptContainer
    $scp.Name = $Name.ToLower()
    $scp.LastDuration = New-TimeSpan -Seconds -1
    $scp.LastResultsValidity = $CacheDuration
    
    if ($Mode -like "Auto")
    {
        $ast = [System.Management.Automation.Language.Parser]::ParseInput($ScriptBlock, [ref]$null, [ref]$null)
        $simple = $null -eq $ast.ParamBlock
    }
    elseif ($Mode -like "Simple") { $simple = $true }
    else { $simple = $false }
    
    if ($simple)
    {
        $scr = [scriptblock]::Create(@'
 param (
  $commandName,
   
  $parameterName,
   
  $wordToComplete,
   
  $commandAst,
   
  $fakeBoundParameter
 )
 
 $start = Get-Date
 $scriptContainer = [PSFramework.TabExpansion.TabExpansionHost]::Scripts["<name>"]
 if ($scriptContainer.ShouldExecute)
 {
  $scriptContainer.LastExecution = $start
    
  $innerScript = [ScriptBlock]::Create(($scriptContainer.InnerScriptBlock))
  # Use Write-Output to enumerate arrays properly, avoids trouble with persisting cached results
  try { $items = $innerScript.Invoke() | Write-Output }
  catch { $null = $scriptContainer.ErrorRecords.Enqueue($_) }
    
  foreach ($item in ($items | Where-Object { "$_" -like "$wordToComplete*"} | Sort-Object))
  {
   New-PSFTeppCompletionResult -CompletionText $item -ToolTip $item
  }
 
  $scriptContainer.LastDuration = (Get-Date) - $start
  if ($items) { $scriptContainer.LastResult = $items }
 }
 else
 {
  foreach ($item in ($scriptContainer.LastResult | Where-Object { "$_" -like "$wordToComplete*"} | Sort-Object))
  {
   New-PSFTeppCompletionResult -CompletionText $item -ToolTip $item
  }
 }
'@
.Replace("<name>", $Name.ToLower()))
        $scp.ScriptBlock = $scr
        $scp.InnerScriptBlock = $ScriptBlock
    }
    else
    {
        $scp.ScriptBlock = $ScriptBlock
    }
    [PSFramework.TabExpansion.TabExpansionHost]::Scripts[$Name.ToLower()] = $scp
}