completers/procdump_completer/procdump_completer.ps1
|
# procdump.exe tab completion for PowerShell # Static-first native completer for ProcDump with context-sensitive placeholders and safe local process hints. Set-StrictMode -Version 2.0 if (-not (Get-Variable -Name ProcDumpCompletionCatalog -Scope Script -ErrorAction Ignore)) { $script:ProcDumpCompletionCatalog = @{ SwitchOrder = @( '-mm', '-ma', '-mac', '-mt', '-mp', '-mc', '-md', '-mk', '-n', '-s', '-c', '-cl', '-u', '-cp', '-m', '-ml', '-p', '-pl', '-h', '-e', '-g', '-b', '-ld', '-ud', '-ct', '-et', '-l', '-t', '-f', '-fx', '-dc', '-o', '-r', '-a', '-at', '-pt', '-wer', '-64', '-w', '-x', '-i', '-k', '-cancel', '-accepteula', '-?', '/?' ) # v12.01 Install Usage lists exactly these alongside -i; Uninstall Usage is # literally 'procdump.exe -u' with no additional options. InstallSwitches = @( '-mm', '-ma', '-mac', '-mt', '-mp', '-mc', '-md', '-mk', '-r', '-cp', '-at', '-pt', '-k', '-wer', '-accepteula' ) SwitchInfo = @{ '-mm' = 'Write a Mini dump file.' '-ma' = 'Write a Full dump file.' '-mac' = 'Write a Full dump file with memory compression.' '-mt' = 'Write a Triage dump file.' '-mp' = 'Write a MiniPlus dump file.' '-mc' = 'Write a Custom dump using a MINIDUMP_TYPE mask.' '-md' = 'Write a Callback dump using the specified callback DLL.' '-mk' = 'Also write a Kernel dump.' '-n' = 'Number of dumps to write before exiting.' '-s' = 'Consecutive seconds before dump is written.' '-c' = 'CPU threshold above which to create a dump.' '-cl' = 'CPU threshold below which to create a dump.' '-u' = 'Treat CPU usage relative to a single core, or uninstall in AeDebug mode when used alone.' '-cp' = 'Number of compression workers to use.' '-m' = 'Memory commit threshold in MB.' '-ml' = 'Trigger when memory commit drops below the specified MB value.' '-p' = 'Trigger when a performance counter meets or exceeds the threshold.' '-pl' = 'Trigger when a performance counter falls below the threshold.' '-h' = 'Write a dump if the process has a hung window.' '-e' = 'Write a dump when the process encounters an exception.' '-g' = 'Run as a native debugger in a managed process.' '-b' = 'Treat debug breakpoints as exceptions.' '-ld' = 'Trigger on module load. Valid with -e.' '-ud' = 'Trigger on module unload. Valid with -e.' '-ct' = 'Trigger on thread creation. Valid with -e.' '-et' = 'Trigger on thread exit. Valid with -e.' '-l' = 'Display the debug logging of the process.' '-t' = 'Write a dump when the process terminates.' '-f' = 'Include filter for exception, debug log, or DLL event text.' '-fx' = 'Exclude filter for exception, debug log, or DLL event text.' '-dc' = 'Add the specified string to the generated dump comment.' '-o' = 'Overwrite an existing dump file.' '-r' = 'Dump using a clone. Optional concurrent clone limit 1..5.' '-a' = 'Avoid outage. Requires -r.' '-at' = 'Avoid outage timeout in seconds.' '-pt' = 'Include a Process Tree stream in the dump file.' '-wer' = 'Queue the largest dump to Windows Error Reporting.' '-64' = 'Capture a 64-bit dump for a WOW64 process.' '-w' = 'Wait for the specified process to launch if it is not running.' '-x' = 'Launch the specified image with optional arguments.' '-i' = 'Install ProcDump as the AeDebug postmortem debugger.' '-k' = 'Kill the process after cloning or after dump collection.' '-cancel' = 'Gracefully terminate ProcDump monitoring for the target process PID.' '-accepteula' = 'Automatically accept the Sysinternals license agreement.' '-?' = 'Display ProcDump help.' '/?' = 'Display ProcDump help.' } ProcessEntries = @() ProcessCacheUpdated = $null ProcessCacheTtlSeconds = 2 } } function New-ProcDumpCompletionResult { param( [string]$CompletionText, [string]$ResultType, [string]$ToolTip ) if ([string]::IsNullOrWhiteSpace($ToolTip)) { $ToolTip = $CompletionText } [System.Management.Automation.CompletionResult]::new( $CompletionText, $CompletionText, $ResultType, $ToolTip ) } function Get-ProcDumpCurrentToken { param( [string]$Line, [int]$CursorPosition, [string]$Fallback ) if ([string]::IsNullOrWhiteSpace($Line)) { return $Fallback } if ($CursorPosition -gt $Line.Length) { return '' } $safeCursor = [Math]::Min([Math]::Max($CursorPosition, 0), $Line.Length) $prefix = $Line.Substring(0, $safeCursor) if ($prefix -match '\s$') { return '' } $parts = @([regex]::Matches($prefix, '"[^"]*"|\S+') | ForEach-Object { $_.Value }) if ($parts.Count -gt 0) { return $parts[-1] } $Fallback } function Remove-ProcDumpOuterQuotes { param([string]$Value) if ([string]::IsNullOrEmpty($Value)) { return '' } if ($Value.Length -ge 2 -and $Value.StartsWith('"') -and $Value.EndsWith('"')) { return $Value.Substring(1, $Value.Length - 2) } $Value.TrimStart('"') } function Update-ProcDumpProcessCache { $lastUpdated = $script:ProcDumpCompletionCatalog.ProcessCacheUpdated if ($null -ne $lastUpdated) { $cacheAge = (Get-Date) - $lastUpdated if ($cacheAge.TotalSeconds -lt $script:ProcDumpCompletionCatalog.ProcessCacheTtlSeconds) { return } } $nameSet = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase) $idSet = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase) $entries = [System.Collections.Generic.List[object]]::new() foreach ($process in @(Get-Process -ErrorAction SilentlyContinue)) { if ($process.ProcessName -and $nameSet.Add($process.ProcessName)) { $entries.Add([pscustomobject]@{ CompletionText = $process.ProcessName ResultType = 'ParameterValue' ToolTip = "Process name $($process.ProcessName)" }) } $processIdText = [string]$process.Id if ($processIdText -and $idSet.Add($processIdText)) { $entries.Add([pscustomobject]@{ CompletionText = $processIdText ResultType = 'ParameterValue' ToolTip = "Process ID $processIdText" }) } } $script:ProcDumpCompletionCatalog.ProcessEntries = @( $entries | Sort-Object -Property CompletionText ) $script:ProcDumpCompletionCatalog.ProcessCacheUpdated = Get-Date } function New-ProcDumpLiteralValueResults { param( [string]$CurrentValue, [string]$Placeholder, [string]$ToolTip ) if ([string]::IsNullOrWhiteSpace($CurrentValue)) { return @( New-ProcDumpCompletionResult -CompletionText $Placeholder -ResultType 'ParameterValue' -ToolTip $ToolTip ) } @( New-ProcDumpCompletionResult -CompletionText $CurrentValue -ResultType 'ParameterValue' -ToolTip $ToolTip ) } function Get-ProcDumpPathCompletion { param( [string]$CurrentValue, [string]$Placeholder, [string]$ToolTip, [string[]]$FileExtension = @(), [bool]$DirectoriesOnly = $false ) $results = [System.Collections.Generic.List[object]]::new() foreach ($match in @([System.Management.Automation.CompletionCompleters]::CompleteFilename($CurrentValue))) { if ($match.ResultType -ne [System.Management.Automation.CompletionResultType]::ProviderContainer) { if ($DirectoriesOnly) { continue } if ($FileExtension.Count -gt 0) { $text = Remove-ProcDumpOuterQuotes -Value $match.CompletionText $keep = $false foreach ($extension in $FileExtension) { if ($text.TrimEnd([char]34).EndsWith($extension, [System.StringComparison]::OrdinalIgnoreCase)) { $keep = $true break } } if (-not $keep) { continue } } } $results.Add((New-ProcDumpCompletionResult -CompletionText $match.CompletionText -ResultType $match.ResultType -ToolTip $ToolTip)) } if ($results.Count -gt 0) { return @($results.ToArray()) } @(New-ProcDumpLiteralValueResults -CurrentValue $CurrentValue -Placeholder $Placeholder -ToolTip $ToolTip) } function Get-ProcDumpSampleValueResults { param( [string]$CurrentValue, [string[]]$Samples, [string]$Placeholder, [string]$ToolTip ) $typedValue = Remove-ProcDumpOuterQuotes -Value $CurrentValue $results = [System.Collections.Generic.List[object]]::new() foreach ($sample in $Samples) { if (-not [string]::IsNullOrWhiteSpace($typedValue) -and -not $sample.StartsWith($typedValue, [System.StringComparison]::OrdinalIgnoreCase)) { continue } $results.Add((New-ProcDumpCompletionResult -CompletionText $sample -ResultType 'ParameterValue' -ToolTip $ToolTip)) } if (($Placeholder -notin $Samples) -and ([string]::IsNullOrWhiteSpace($typedValue) -or $Placeholder.StartsWith($typedValue, [System.StringComparison]::OrdinalIgnoreCase))) { $results.Add((New-ProcDumpCompletionResult -CompletionText $Placeholder -ResultType 'ParameterValue' -ToolTip $ToolTip)) } if ($results.Count -eq 0) { return @(New-ProcDumpLiteralValueResults -CurrentValue $CurrentValue -Placeholder $Placeholder -ToolTip $ToolTip) } @($results.ToArray()) } function Get-ProcDumpProcessCompletions { param( [string]$CurrentWord, [bool]$IdsOnly = $false ) Update-ProcDumpProcessCache $typedValue = Remove-ProcDumpOuterQuotes -Value $CurrentWord $results = $script:ProcDumpCompletionCatalog.ProcessEntries | Where-Object { (-not $IdsOnly -or $_.CompletionText -match '^\d+$') -and ([string]::IsNullOrWhiteSpace($typedValue) -or $_.CompletionText.StartsWith($typedValue, [System.StringComparison]::OrdinalIgnoreCase)) } | ForEach-Object { New-ProcDumpCompletionResult -CompletionText $_.CompletionText -ResultType $_.ResultType -ToolTip $_.ToolTip } if (@($results).Count -gt 0) { return @($results) } if ($IdsOnly) { return @(New-ProcDumpLiteralValueResults -CurrentValue $CurrentWord -Placeholder '<pid>' -ToolTip 'Process ID.') } @(New-ProcDumpLiteralValueResults -CurrentValue $CurrentWord -Placeholder '<process-name-or-pid>' -ToolTip 'Process name or PID.') } function Get-ProcDumpCommandState { param([object[]]$TokensBeforeCurrent) $TokensBeforeCurrent = @($TokensBeforeCurrent) $usedSwitchLookup = @{} $valueContext = $null $positionals = [System.Collections.Generic.List[string]]::new() $helpRequested = $false $mode = 'capture' for ($index = 0; $index -lt $TokensBeforeCurrent.Count; $index++) { $token = [string]$TokensBeforeCurrent[$index] if ([string]::IsNullOrWhiteSpace($token)) { continue } $lookup = $token.ToLowerInvariant() if ($lookup -in @('-?', '/?')) { $helpRequested = $true $usedSwitchLookup[$lookup] = $true continue } if ($lookup -eq '-u' -and $TokensBeforeCurrent.Count -eq 1) { $usedSwitchLookup[$lookup] = $true $mode = 'uninstall' continue } if ($lookup -eq '-x') { $usedSwitchLookup[$lookup] = $true $mode = 'launch' $remaining = ($TokensBeforeCurrent.Count - 1) - $index if ($remaining -eq 0) { $valueContext = [pscustomobject]@{ Switch = '-x'; Position = 1 } break } $positionals.Add([string]$TokensBeforeCurrent[$index + 1]) if ($remaining -eq 1) { $valueContext = [pscustomobject]@{ Switch = '-x'; Position = 2 } break } $positionals.Add([string]$TokensBeforeCurrent[$index + 2]) for ($extra = $index + 3; $extra -lt $TokensBeforeCurrent.Count; $extra++) { $positionals.Add([string]$TokensBeforeCurrent[$extra]) } break } if ($lookup -eq '-i') { $usedSwitchLookup[$lookup] = $true $mode = 'install' if ($index -eq ($TokensBeforeCurrent.Count - 1)) { $valueContext = [pscustomobject]@{ Switch = '-i'; Position = 1 } break } $nextToken = [string]$TokensBeforeCurrent[$index + 1] if (-not [string]::IsNullOrWhiteSpace($nextToken) -and -not $nextToken.StartsWith('-')) { $positionals.Add($nextToken) $index++ } continue } if ($lookup -eq '-cancel') { $usedSwitchLookup[$lookup] = $true if ($index -eq ($TokensBeforeCurrent.Count - 1)) { $valueContext = [pscustomobject]@{ Switch = '-cancel'; Position = 1 } break } $index++ continue } if ($lookup -in @('-mc', '-md', '-n', '-s', '-c', '-cl', '-cp', '-m', '-ml', '-f', '-fx', '-dc', '-at')) { $usedSwitchLookup[$lookup] = $true if ($index -eq ($TokensBeforeCurrent.Count - 1)) { $valueContext = [pscustomobject]@{ Switch = $lookup; Position = 1 } break } $index++ continue } if ($lookup -in @('-p', '-pl')) { $usedSwitchLookup[$lookup] = $true $remaining = ($TokensBeforeCurrent.Count - 1) - $index if ($remaining -eq 0) { $valueContext = [pscustomobject]@{ Switch = $lookup; Position = 1 } break } if ($remaining -eq 1) { $valueContext = [pscustomobject]@{ Switch = $lookup; Position = 2 } break } $index += 2 continue } if ($lookup -eq '-r') { $usedSwitchLookup[$lookup] = $true if ($index -eq ($TokensBeforeCurrent.Count - 1)) { $valueContext = [pscustomobject]@{ Switch = '-r'; Position = 1 } break } $nextToken = [string]$TokensBeforeCurrent[$index + 1] if (-not [string]::IsNullOrWhiteSpace($nextToken) -and -not $nextToken.StartsWith('-')) { $index++ } continue } if ($lookup -eq '-e') { $usedSwitchLookup[$lookup] = $true if ($index -eq ($TokensBeforeCurrent.Count - 1)) { $valueContext = [pscustomobject]@{ Switch = '-e'; Position = 1 } break } $nextToken = [string]$TokensBeforeCurrent[$index + 1] if ($nextToken -eq '1') { $index++ } continue } if ($lookup.StartsWith('-')) { $usedSwitchLookup[$lookup] = $true continue } $positionals.Add($token) } [pscustomobject]@{ UsedSwitchLookup = $usedSwitchLookup ValueContext = $valueContext Positionals = @($positionals) HelpRequested = $helpRequested Mode = $mode } } function Get-ProcDumpSwitchCompletions { param( [string]$CurrentWord, [pscustomobject]$State, [bool]$NoArgumentsYet ) $prefix = if ([string]::IsNullOrWhiteSpace($CurrentWord)) { '' } else { $CurrentWord } $results = [System.Collections.Generic.List[object]]::new() foreach ($token in $script:ProcDumpCompletionCatalog.SwitchOrder) { if (-not [string]::IsNullOrWhiteSpace($prefix) -and -not $token.StartsWith($prefix, [System.StringComparison]::OrdinalIgnoreCase)) { continue } if ($token -in @('-?', '/?')) { if (-not $NoArgumentsYet) { continue } } elseif ($State.UsedSwitchLookup.ContainsKey($token) -and $token -notin @('-f', '-fx', '-dc')) { continue } if ($State.Mode -eq 'uninstall') { continue } if ($State.Mode -eq 'install' -and $token -notin $script:ProcDumpCompletionCatalog.InstallSwitches) { continue } if ($token -eq '-a' -and -not $State.UsedSwitchLookup.ContainsKey('-r')) { continue } if ($token -in @('-ld', '-ud', '-ct', '-et', '-b', '-g') -and -not $State.UsedSwitchLookup.ContainsKey('-e')) { if ($token -ne '-g') { continue } } $results.Add((New-ProcDumpCompletionResult -CompletionText $token -ResultType 'ParameterName' -ToolTip $script:ProcDumpCompletionCatalog.SwitchInfo[$token])) } @($results.ToArray()) } function Complete-ProcDump { param( [string]$wordToComplete, [System.Management.Automation.Language.CommandAst]$commandAst, [int]$cursorPosition ) $line = $commandAst.ToString() $safeCursor = [Math]::Min([Math]::Max($cursorPosition - $commandAst.Extent.StartOffset, 0), $line.Length) $linePrefix = $line.Substring(0, $safeCursor) $commandTokens = @([regex]::Matches($linePrefix, '"[^"]*"|\S+') | ForEach-Object { $_.Value }) # Wrap the whole if-expression in @(): an [object[]]-constrained assignment from a # branch that yields nothing binds $null, and re-wrapping that $null manufactures a # one-element array holding $null, which would make every Count -eq 0 test false. $argumentTokens = @(if ($commandTokens.Count -gt 1) { $commandTokens | Select-Object -Skip 1 }) $currentWord = if ([string]::IsNullOrEmpty($wordToComplete)) { Get-ProcDumpCurrentToken -Line $line -CursorPosition ($cursorPosition - $commandAst.Extent.StartOffset) -Fallback $wordToComplete } else { $wordToComplete } $hasTrailingSpace = [string]::IsNullOrEmpty($currentWord) -and (($linePrefix -match '\s$') -or (($cursorPosition - $commandAst.Extent.StartOffset) -gt $line.Length)) $tokensBeforeCurrent = @(if ($hasTrailingSpace) { $argumentTokens } elseif ($argumentTokens.Count -gt 0) { $argumentTokens | Select-Object -First ($argumentTokens.Count - 1) }) $state = Get-ProcDumpCommandState -TokensBeforeCurrent $tokensBeforeCurrent if ($state.HelpRequested) { return @( New-ProcDumpCompletionResult -CompletionText '-?' -ResultType 'ParameterName' -ToolTip 'Display ProcDump help.' ) } $typedDash = -not [string]::IsNullOrEmpty($currentWord) -and $currentWord.StartsWith('-') # -r and -i take an optional value, so a typed dash is the next switch rather than a # value; without this the sample helper echoes the bare '-' and hides every switch. if ($state.ValueContext -and $typedDash -and ([string]$state.ValueContext.Switch) -in @('-r', '-i')) { return @(Get-ProcDumpSwitchCompletions -CurrentWord $currentWord -State $state -NoArgumentsYet:($tokensBeforeCurrent.Count -eq 0)) } if ($state.ValueContext) { $switchName = [string]$state.ValueContext.Switch $position = [int]$state.ValueContext.Position switch ($switchName) { '-mc' { return @(Get-ProcDumpSampleValueResults -CurrentValue $currentWord -Samples @('0x00061907', '0x00000002') -Placeholder '<hex-mask>' -ToolTip 'MINIDUMP_TYPE mask in hexadecimal.') } '-md' { return @(Get-ProcDumpPathCompletion -CurrentValue $currentWord -Placeholder '"C:\path\MiniDumpCallback.dll"' -ToolTip 'Callback DLL path.' -FileExtension @('.dll')) } '-n' { return @(Get-ProcDumpSampleValueResults -CurrentValue $currentWord -Samples @('1', '3', '5') -Placeholder '<count>' -ToolTip 'Number of dumps to write.') } '-s' { return @(Get-ProcDumpSampleValueResults -CurrentValue $currentWord -Samples @('5', '10', '30') -Placeholder '<seconds>' -ToolTip 'Consecutive seconds before dump is written.') } '-c' { return @(Get-ProcDumpSampleValueResults -CurrentValue $currentWord -Samples @('20', '50', '80') -Placeholder '<cpu-percent>' -ToolTip 'CPU threshold percent.') } '-cl' { return @(Get-ProcDumpSampleValueResults -CurrentValue $currentWord -Samples @('5', '10', '20') -Placeholder '<cpu-percent>' -ToolTip 'CPU threshold percent.') } '-cp' { return @(Get-ProcDumpSampleValueResults -CurrentValue $currentWord -Samples @('1', '2', '4') -Placeholder '<workers>' -ToolTip 'Compression worker count.') } '-m' { return @(Get-ProcDumpSampleValueResults -CurrentValue $currentWord -Samples @('512', '1024', '2048') -Placeholder '<commit-mb>' -ToolTip 'Commit threshold in MB.') } '-ml' { return @(Get-ProcDumpSampleValueResults -CurrentValue $currentWord -Samples @('256', '512', '1024') -Placeholder '<commit-mb>' -ToolTip 'Commit threshold in MB.') } '-p' { if ($position -eq 1) { return @(Get-ProcDumpSampleValueResults -CurrentValue $currentWord -Samples @('\Processor(_Total)\% Processor Time', '\Process(*)\Handle Count') -Placeholder '<counter>' -ToolTip 'Performance counter path.') } return @(Get-ProcDumpSampleValueResults -CurrentValue $currentWord -Samples @('10', '50', '100') -Placeholder '<threshold>' -ToolTip 'Counter threshold.') } '-pl' { if ($position -eq 1) { return @(Get-ProcDumpSampleValueResults -CurrentValue $currentWord -Samples @('\Processor(_Total)\% Processor Time', '\Process(*)\Handle Count') -Placeholder '<counter>' -ToolTip 'Performance counter path.') } return @(Get-ProcDumpSampleValueResults -CurrentValue $currentWord -Samples @('10', '50', '100') -Placeholder '<threshold>' -ToolTip 'Counter threshold.') } '-f' { return @(New-ProcDumpLiteralValueResults -CurrentValue $currentWord -Placeholder '<include-filter>' -ToolTip 'Include filter text; wildcards are supported.') } '-fx' { return @(New-ProcDumpLiteralValueResults -CurrentValue $currentWord -Placeholder '<exclude-filter>' -ToolTip 'Exclude filter text; wildcards are supported.') } '-dc' { return @(New-ProcDumpLiteralValueResults -CurrentValue $currentWord -Placeholder '"<comment>"' -ToolTip 'Dump comment string.') } '-r' { return @(Get-ProcDumpSampleValueResults -CurrentValue $currentWord -Samples @('1', '2', '3', '5') -Placeholder '<concurrency>' -ToolTip 'Optional clone concurrency limit.') } '-at' { return @(Get-ProcDumpSampleValueResults -CurrentValue $currentWord -Samples @('10', '30', '60') -Placeholder '<timeout-seconds>' -ToolTip 'Timeout for avoid-outage collection.') } '-cancel' { return @(Get-ProcDumpProcessCompletions -CurrentWord $currentWord -IdsOnly:$true) } '-i' { return @(Get-ProcDumpPathCompletion -CurrentValue $currentWord -Placeholder '"C:\Dumps"' -ToolTip 'Optional AeDebug dump folder.' -DirectoriesOnly $true) } '-e' { $results = [System.Collections.Generic.List[object]]::new() if (-not $typedDash) { foreach ($item in @(Get-ProcDumpSampleValueResults -CurrentValue $currentWord -Samples @('1') -Placeholder '1' -ToolTip 'Optional first-chance exception value.')) { $results.Add($item) } } foreach ($item in @(Get-ProcDumpSwitchCompletions -CurrentWord '-' -State $state -NoArgumentsYet:$false | Where-Object { $_.CompletionText -in @('-g', '-b', '-ld', '-ud', '-ct', '-et') })) { $results.Add($item) } return @($results.ToArray()) } '-x' { if ($position -eq 1) { return @(Get-ProcDumpPathCompletion -CurrentValue $currentWord -Placeholder '"C:\Dumps"' -ToolTip 'Dump folder for -x launch mode.' -DirectoriesOnly $true) } return @(Get-ProcDumpPathCompletion -CurrentValue $currentWord -Placeholder '"C:\path\image.exe"' -ToolTip 'Image path for -x launch mode.' -FileExtension @('.exe')) } } } if (-not [string]::IsNullOrWhiteSpace($currentWord) -and $currentWord.StartsWith('-')) { return @(Get-ProcDumpSwitchCompletions -CurrentWord $currentWord -State $state -NoArgumentsYet:($tokensBeforeCurrent.Count -eq 0)) } if ($state.Mode -eq 'launch') { if ($state.Positionals.Count -lt 2) { if ($state.Positionals.Count -eq 0) { return @(Get-ProcDumpPathCompletion -CurrentValue $currentWord -Placeholder '"C:\Dumps"' -ToolTip 'Dump folder for -x launch mode.' -DirectoriesOnly $true) } return @(Get-ProcDumpPathCompletion -CurrentValue $currentWord -Placeholder '"C:\path\image.exe"' -ToolTip 'Image path for -x launch mode.' -FileExtension @('.exe')) } return @(New-ProcDumpLiteralValueResults -CurrentValue $currentWord -Placeholder '<argument>' -ToolTip 'Argument passed to the launched image.') } if ([string]::IsNullOrWhiteSpace($currentWord)) { $results = [System.Collections.Generic.List[object]]::new() foreach ($item in @(Get-ProcDumpSwitchCompletions -CurrentWord $currentWord -State $state -NoArgumentsYet:($tokensBeforeCurrent.Count -eq 0))) { $results.Add($item) } switch ($state.Mode) { 'uninstall' { $results.Add((New-ProcDumpCompletionResult -CompletionText '<no-more-arguments>' -ResultType 'ParameterValue' -ToolTip 'Uninstall usage is "procdump.exe -u" with no additional options.')) } 'install' { if ($state.Positionals.Count -eq 0) { $results.Add((New-ProcDumpCompletionResult -CompletionText '"C:\Dumps"' -ResultType 'ParameterValue' -ToolTip 'Optional AeDebug dump folder.')) } } 'capture' { if ($state.Positionals.Count -eq 0) { foreach ($item in @(Get-ProcDumpProcessCompletions -CurrentWord '')) { $results.Add($item) } $results.Add((New-ProcDumpCompletionResult -CompletionText '<service-name>' -ResultType 'ParameterValue' -ToolTip 'Service name target.')) } elseif ($state.Positionals.Count -eq 1) { $results.Add((New-ProcDumpCompletionResult -CompletionText '<dump-file-or-folder>' -ResultType 'ParameterValue' -ToolTip 'Dump file or dump folder path.')) } } } return @($results.ToArray()) } switch ($state.Mode) { 'install' { if ($state.Positionals.Count -eq 0) { return @(Get-ProcDumpPathCompletion -CurrentValue $currentWord -Placeholder '"C:\Dumps"' -ToolTip 'Optional AeDebug dump folder.' -DirectoriesOnly $true) } } 'capture' { if ($state.Positionals.Count -eq 0) { return @(Get-ProcDumpProcessCompletions -CurrentWord $currentWord) } if ($state.Positionals.Count -eq 1) { return @(Get-ProcDumpPathCompletion -CurrentValue $currentWord -Placeholder '<dump-file-or-folder>' -ToolTip 'Dump file or dump folder path.' -FileExtension @('.dmp')) } } } @() } Register-ArgumentCompleter -Native -CommandName @('procdump', 'procdump.exe') -ScriptBlock { param($wordToComplete, $commandAst, $cursorPosition) Complete-ProcDump -wordToComplete $wordToComplete -commandAst $commandAst -cursorPosition $cursorPosition } |