| 
									
										
										
										
											2023-01-12 20:51:19 +01:00
										 |  |  | # powershell completion for restic                               -*- shell-script -*- | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function __restic_debug { | 
					
						
							|  |  |  |     if ($env:BASH_COMP_DEBUG_FILE) { | 
					
						
							|  |  |  |         "$args" | Out-File -Append -FilePath "$env:BASH_COMP_DEBUG_FILE" | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | filter __restic_escapeStringWithSpecialChars { | 
					
						
							|  |  |  |     $_ -replace '\s|#|@|\$|;|,|''|\{|\}|\(|\)|"|`|\||<|>|&','`$&' | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-26 11:30:37 +02:00
										 |  |  | [scriptblock]${__resticCompleterBlock} = { | 
					
						
							| 
									
										
										
										
											2023-01-12 20:51:19 +01:00
										 |  |  |     param( | 
					
						
							|  |  |  |             $WordToComplete, | 
					
						
							|  |  |  |             $CommandAst, | 
					
						
							|  |  |  |             $CursorPosition | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Get the current command line and convert into a string | 
					
						
							|  |  |  |     $Command = $CommandAst.CommandElements | 
					
						
							|  |  |  |     $Command = "$Command" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     __restic_debug "" | 
					
						
							|  |  |  |     __restic_debug "========= starting completion logic ==========" | 
					
						
							|  |  |  |     __restic_debug "WordToComplete: $WordToComplete Command: $Command CursorPosition: $CursorPosition" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # The user could have moved the cursor backwards on the command-line. | 
					
						
							|  |  |  |     # We need to trigger completion from the $CursorPosition location, so we need | 
					
						
							|  |  |  |     # to truncate the command-line ($Command) up to the $CursorPosition location. | 
					
						
							|  |  |  |     # Make sure the $Command is longer then the $CursorPosition before we truncate. | 
					
						
							|  |  |  |     # This happens because the $Command does not include the last space. | 
					
						
							|  |  |  |     if ($Command.Length -gt $CursorPosition) { | 
					
						
							|  |  |  |         $Command=$Command.Substring(0,$CursorPosition) | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     __restic_debug "Truncated command: $Command" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     $ShellCompDirectiveError=1 | 
					
						
							|  |  |  |     $ShellCompDirectiveNoSpace=2 | 
					
						
							|  |  |  |     $ShellCompDirectiveNoFileComp=4 | 
					
						
							|  |  |  |     $ShellCompDirectiveFilterFileExt=8 | 
					
						
							|  |  |  |     $ShellCompDirectiveFilterDirs=16 | 
					
						
							| 
									
										
										
										
											2023-07-31 20:23:24 +02:00
										 |  |  |     $ShellCompDirectiveKeepOrder=32 | 
					
						
							| 
									
										
										
										
											2023-01-12 20:51:19 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # Prepare the command to request completions for the program. | 
					
						
							|  |  |  |     # Split the command at the first space to separate the program and arguments. | 
					
						
							|  |  |  |     $Program,$Arguments = $Command.Split(" ",2) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     $RequestComp="$Program __completeNoDesc $Arguments" | 
					
						
							|  |  |  |     __restic_debug "RequestComp: $RequestComp" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # we cannot use $WordToComplete because it | 
					
						
							|  |  |  |     # has the wrong values if the cursor was moved | 
					
						
							|  |  |  |     # so use the last argument | 
					
						
							|  |  |  |     if ($WordToComplete -ne "" ) { | 
					
						
							|  |  |  |         $WordToComplete = $Arguments.Split(" ")[-1] | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     __restic_debug "New WordToComplete: $WordToComplete" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Check for flag with equal sign | 
					
						
							|  |  |  |     $IsEqualFlag = ($WordToComplete -Like "--*=*" ) | 
					
						
							|  |  |  |     if ( $IsEqualFlag ) { | 
					
						
							|  |  |  |         __restic_debug "Completing equal sign flag" | 
					
						
							|  |  |  |         # Remove the flag part | 
					
						
							|  |  |  |         $Flag,$WordToComplete = $WordToComplete.Split("=",2) | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if ( $WordToComplete -eq "" -And ( -Not $IsEqualFlag )) { | 
					
						
							|  |  |  |         # If the last parameter is complete (there is a space following it) | 
					
						
							|  |  |  |         # We add an extra empty parameter so we can indicate this to the go method. | 
					
						
							|  |  |  |         __restic_debug "Adding extra empty parameter" | 
					
						
							| 
									
										
										
										
											2023-07-31 20:23:24 +02:00
										 |  |  |         # PowerShell 7.2+ changed the way how the arguments are passed to executables, | 
					
						
							|  |  |  |         # so for pre-7.2 or when Legacy argument passing is enabled we need to use | 
					
						
							|  |  |  |         # `"`" to pass an empty argument, a "" or '' does not work!!! | 
					
						
							|  |  |  |         if ($PSVersionTable.PsVersion -lt [version]'7.2.0' -or | 
					
						
							|  |  |  |             ($PSVersionTable.PsVersion -lt [version]'7.3.0' -and -not [ExperimentalFeature]::IsEnabled("PSNativeCommandArgumentPassing")) -or | 
					
						
							|  |  |  |             (($PSVersionTable.PsVersion -ge [version]'7.3.0' -or [ExperimentalFeature]::IsEnabled("PSNativeCommandArgumentPassing")) -and | 
					
						
							|  |  |  |               $PSNativeCommandArgumentPassing -eq 'Legacy')) { | 
					
						
							|  |  |  |              $RequestComp="$RequestComp" + ' `"`"' | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |              $RequestComp="$RequestComp" + ' ""' | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-01-12 20:51:19 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     __restic_debug "Calling $RequestComp" | 
					
						
							|  |  |  |     # First disable ActiveHelp which is not supported for Powershell | 
					
						
							| 
									
										
										
										
											2024-07-26 11:30:37 +02:00
										 |  |  |     ${env:RESTIC_ACTIVE_HELP}=0 | 
					
						
							| 
									
										
										
										
											2023-01-12 20:51:19 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     #call the command store the output in $out and redirect stderr and stdout to null | 
					
						
							|  |  |  |     # $Out is an array contains each line per element | 
					
						
							|  |  |  |     Invoke-Expression -OutVariable out "$RequestComp" 2>&1 | Out-Null | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # get directive from last line | 
					
						
							|  |  |  |     [int]$Directive = $Out[-1].TrimStart(':') | 
					
						
							|  |  |  |     if ($Directive -eq "") { | 
					
						
							|  |  |  |         # There is no directive specified | 
					
						
							|  |  |  |         $Directive = 0 | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     __restic_debug "The completion directive is: $Directive" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # remove directive (last element) from out | 
					
						
							|  |  |  |     $Out = $Out | Where-Object { $_ -ne $Out[-1] } | 
					
						
							|  |  |  |     __restic_debug "The completions are: $Out" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (($Directive -band $ShellCompDirectiveError) -ne 0 ) { | 
					
						
							|  |  |  |         # Error code.  No completion. | 
					
						
							|  |  |  |         __restic_debug "Received error from custom completion go code" | 
					
						
							|  |  |  |         return | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     $Longest = 0 | 
					
						
							| 
									
										
										
										
											2023-07-31 20:23:24 +02:00
										 |  |  |     [Array]$Values = $Out | ForEach-Object { | 
					
						
							| 
									
										
										
										
											2023-01-12 20:51:19 +01:00
										 |  |  |         #Split the output in name and description | 
					
						
							|  |  |  |         $Name, $Description = $_.Split("`t",2) | 
					
						
							|  |  |  |         __restic_debug "Name: $Name Description: $Description" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Look for the longest completion so that we can format things nicely | 
					
						
							|  |  |  |         if ($Longest -lt $Name.Length) { | 
					
						
							|  |  |  |             $Longest = $Name.Length | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Set the description to a one space string if there is none set. | 
					
						
							|  |  |  |         # This is needed because the CompletionResult does not accept an empty string as argument | 
					
						
							|  |  |  |         if (-Not $Description) { | 
					
						
							|  |  |  |             $Description = " " | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2025-03-27 20:16:56 +01:00
										 |  |  |         New-Object -TypeName PSCustomObject -Property @{ | 
					
						
							|  |  |  |             Name = "$Name" | 
					
						
							|  |  |  |             Description = "$Description" | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-01-12 20:51:19 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     $Space = " " | 
					
						
							|  |  |  |     if (($Directive -band $ShellCompDirectiveNoSpace) -ne 0 ) { | 
					
						
							|  |  |  |         # remove the space here | 
					
						
							|  |  |  |         __restic_debug "ShellCompDirectiveNoSpace is called" | 
					
						
							|  |  |  |         $Space = "" | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if ((($Directive -band $ShellCompDirectiveFilterFileExt) -ne 0 ) -or | 
					
						
							|  |  |  |        (($Directive -band $ShellCompDirectiveFilterDirs) -ne 0 ))  { | 
					
						
							|  |  |  |         __restic_debug "ShellCompDirectiveFilterFileExt ShellCompDirectiveFilterDirs are not supported" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # return here to prevent the completion of the extensions | 
					
						
							|  |  |  |         return | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     $Values = $Values | Where-Object { | 
					
						
							|  |  |  |         # filter the result | 
					
						
							|  |  |  |         $_.Name -like "$WordToComplete*" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Join the flag back if we have an equal sign flag | 
					
						
							|  |  |  |         if ( $IsEqualFlag ) { | 
					
						
							|  |  |  |             __restic_debug "Join the equal sign flag back to the completion value" | 
					
						
							|  |  |  |             $_.Name = $Flag + "=" + $_.Name | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-31 20:23:24 +02:00
										 |  |  |     # we sort the values in ascending order by name if keep order isn't passed | 
					
						
							|  |  |  |     if (($Directive -band $ShellCompDirectiveKeepOrder) -eq 0 ) { | 
					
						
							|  |  |  |         $Values = $Values | Sort-Object -Property Name | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-12 20:51:19 +01:00
										 |  |  |     if (($Directive -band $ShellCompDirectiveNoFileComp) -ne 0 ) { | 
					
						
							|  |  |  |         __restic_debug "ShellCompDirectiveNoFileComp is called" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if ($Values.Length -eq 0) { | 
					
						
							|  |  |  |             # Just print an empty string here so the | 
					
						
							|  |  |  |             # shell does not start to complete paths. | 
					
						
							|  |  |  |             # We cannot use CompletionResult here because | 
					
						
							|  |  |  |             # it does not accept an empty string as argument. | 
					
						
							|  |  |  |             "" | 
					
						
							|  |  |  |             return | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Get the current mode | 
					
						
							|  |  |  |     $Mode = (Get-PSReadLineKeyHandler | Where-Object {$_.Key -eq "Tab" }).Function | 
					
						
							|  |  |  |     __restic_debug "Mode: $Mode" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     $Values | ForEach-Object { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # store temporary because switch will overwrite $_ | 
					
						
							|  |  |  |         $comp = $_ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # PowerShell supports three different completion modes | 
					
						
							|  |  |  |         # - TabCompleteNext (default windows style - on each key press the next option is displayed) | 
					
						
							|  |  |  |         # - Complete (works like bash) | 
					
						
							|  |  |  |         # - MenuComplete (works like zsh) | 
					
						
							|  |  |  |         # You set the mode with Set-PSReadLineKeyHandler -Key Tab -Function <mode> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # CompletionResult Arguments: | 
					
						
							|  |  |  |         # 1) CompletionText text to be used as the auto completion result | 
					
						
							|  |  |  |         # 2) ListItemText   text to be displayed in the suggestion list | 
					
						
							|  |  |  |         # 3) ResultType     type of completion result | 
					
						
							|  |  |  |         # 4) ToolTip        text for the tooltip with details about the object | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         switch ($Mode) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             # bash like | 
					
						
							|  |  |  |             "Complete" { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 if ($Values.Length -eq 1) { | 
					
						
							|  |  |  |                     __restic_debug "Only one completion left" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     # insert space after value | 
					
						
							| 
									
										
										
										
											2025-03-27 20:16:56 +01:00
										 |  |  |                     $CompletionText = $($comp.Name | __restic_escapeStringWithSpecialChars) + $Space | 
					
						
							|  |  |  |                     if ($ExecutionContext.SessionState.LanguageMode -eq "FullLanguage"){ | 
					
						
							|  |  |  |                         [System.Management.Automation.CompletionResult]::new($CompletionText, "$($comp.Name)", 'ParameterValue', "$($comp.Description)") | 
					
						
							|  |  |  |                     } else { | 
					
						
							|  |  |  |                         $CompletionText | 
					
						
							|  |  |  |                     } | 
					
						
							| 
									
										
										
										
											2023-01-12 20:51:19 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |                 } else { | 
					
						
							|  |  |  |                     # Add the proper number of spaces to align the descriptions | 
					
						
							|  |  |  |                     while($comp.Name.Length -lt $Longest) { | 
					
						
							|  |  |  |                         $comp.Name = $comp.Name + " " | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     # Check for empty description and only add parentheses if needed | 
					
						
							|  |  |  |                     if ($($comp.Description) -eq " " ) { | 
					
						
							|  |  |  |                         $Description = "" | 
					
						
							|  |  |  |                     } else { | 
					
						
							|  |  |  |                         $Description = "  ($($comp.Description))" | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-27 20:16:56 +01:00
										 |  |  |                     $CompletionText = "$($comp.Name)$Description" | 
					
						
							|  |  |  |                     if ($ExecutionContext.SessionState.LanguageMode -eq "FullLanguage"){ | 
					
						
							|  |  |  |                         [System.Management.Automation.CompletionResult]::new($CompletionText, "$($comp.Name)$Description", 'ParameterValue', "$($comp.Description)") | 
					
						
							|  |  |  |                     } else { | 
					
						
							|  |  |  |                         $CompletionText | 
					
						
							|  |  |  |                     } | 
					
						
							| 
									
										
										
										
											2023-01-12 20:51:19 +01:00
										 |  |  |                 } | 
					
						
							|  |  |  |              } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             # zsh like | 
					
						
							|  |  |  |             "MenuComplete" { | 
					
						
							|  |  |  |                 # insert space after value | 
					
						
							|  |  |  |                 # MenuComplete will automatically show the ToolTip of | 
					
						
							|  |  |  |                 # the highlighted value at the bottom of the suggestions. | 
					
						
							| 
									
										
										
										
											2025-03-27 20:16:56 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |                 $CompletionText = $($comp.Name | __restic_escapeStringWithSpecialChars) + $Space | 
					
						
							|  |  |  |                 if ($ExecutionContext.SessionState.LanguageMode -eq "FullLanguage"){ | 
					
						
							|  |  |  |                     [System.Management.Automation.CompletionResult]::new($CompletionText, "$($comp.Name)", 'ParameterValue', "$($comp.Description)") | 
					
						
							|  |  |  |                 } else { | 
					
						
							|  |  |  |                     $CompletionText | 
					
						
							|  |  |  |                 } | 
					
						
							| 
									
										
										
										
											2023-01-12 20:51:19 +01:00
										 |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             # TabCompleteNext and in case we get something unknown | 
					
						
							|  |  |  |             Default { | 
					
						
							|  |  |  |                 # Like MenuComplete but we don't want to add a space here because | 
					
						
							|  |  |  |                 # the user need to press space anyway to get the completion. | 
					
						
							|  |  |  |                 # Description will not be shown because that's not possible with TabCompleteNext | 
					
						
							| 
									
										
										
										
											2025-03-27 20:16:56 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |                 $CompletionText = $($comp.Name | __restic_escapeStringWithSpecialChars) | 
					
						
							|  |  |  |                 if ($ExecutionContext.SessionState.LanguageMode -eq "FullLanguage"){ | 
					
						
							|  |  |  |                     [System.Management.Automation.CompletionResult]::new($CompletionText, "$($comp.Name)", 'ParameterValue', "$($comp.Description)") | 
					
						
							|  |  |  |                 } else { | 
					
						
							|  |  |  |                     $CompletionText | 
					
						
							|  |  |  |                 } | 
					
						
							| 
									
										
										
										
											2023-01-12 20:51:19 +01:00
										 |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-26 11:30:37 +02:00
										 |  |  | Register-ArgumentCompleter -CommandName 'restic' -ScriptBlock ${__resticCompleterBlock} |