en-US/about_CompleterActions_Migration.help.txt
|
TOPIC about_CompleterActions_Migration SYNOPSIS Explains what changed in CompleterActions 3.0.0 and how to move a 2.x profile or script to it: the removed 1.x names, the public .NET record types, the PowerShell 7.4 engine floor, and the strict grammar at registration and import. A closing section lists what changed in CompleterActions 2.0.0 for a reader still moving from 1.x. LONG DESCRIPTION CompleterActions 3.0.0 is the breaking release the 2.x line announced. Four things change for a caller: - The 1.x names are removed. Get-CompleterRegistration, Register-CompleterRegistration, and Unregister-CompleterRegistration, which 2.x kept as deprecated aliases, are gone, together with the three legacy wrappers that carried them, the -ManagedOnly and -DiscoveredOnly switches of the Get wrapper, and the once-per-process deprecation warning. The module exports eleven functions and no aliases. - The records are public .NET types. CompleterRegistration, ImportedCompleterRegistration, CompleterScriptFinding, CompletionMatch, and the CompleterState and CompleterType enums live in the CompleterActions namespace of a compiled assembly, CompleterActions.Core.dll, that the manifest loads. A type literal such as [CompleterActions.CompleterRegistration] works from any script. - The minimum engine is PowerShell 7.4. The manifest declares PowerShellVersion = '7.4'. - The strict grammar also runs at registration and import. A strict script that does not conform is refused by Register-Completer -Lazy and reported as an invalid entry by Import-CompleterSet, instead of registering as Pending and failing on its first tab press. Two more things move into the compiled assembly without changing what a caller sees: the import-time engine probe with the reflection into the engine's completer tables, and the walk of the strict grammar. The probe's message, every finding of Test-CompleterScript, and every warning, error, and verbose line of the eleven commands are the same as in 2.2.0, apart from the two grammar errors described below. Nothing else changes. Every command keeps its name, parameters, and parameter sets. Completer set files keep their schema: a set written by 2.2.0 imports on 3.0.0 with the same records, and a set written by 3.0.0 imports on 2.2.0. Completer scripts need no change. REMOVED COMMANDS | 2.x | 3.0 | | ---------------------------------------- | ----------------------------- | | Get-CompleterRegistration (alias) | Get-Completer | | Register-CompleterRegistration (alias) | Register-Completer | | Unregister-CompleterRegistration (alias) | Unregister-Completer | | Get-CompleterRegistrationLegacy | Get-Completer | | Register-CompleterRegistrationLegacy | Register-Completer | | Unregister-CompleterRegistrationLegacy | Unregister-Completer | | -ManagedOnly | -State Active, Pending, | | | Failed, Stale | | -DiscoveredOnly | -State Discovered, Conflicted | | the deprecation warning | nothing is written | Calling an old name fails with PowerShell's own CommandNotFoundException: The term 'Get-CompleterRegistration' is not recognized as a name of a cmdlet, function, script file, or executable program. That is what a session sees once 3.0 is the only CompleterActions on PSModulePath. While a 2.x copy is still installed beside it, command discovery autoloads that 2.x module for the old name instead, so the call succeeds against 2.x and the session then holds both versions. Uninstall the 2.x copies you no longer pin before relying on the error: Get-InstalledPSResource CompleterActions | Where-Object Version -lt 3.0.0 | Uninstall-PSResource The module adds no message of its own, because a module cannot intercept a name it does not export. The three *Legacy functions existed only so the aliases had an exported target, and they go with them. -ManagedOnly and -DiscoveredOnly were accepted only by the legacy Get wrapper; Get-Completer never had them. Before: Import-CompleterScript -Path .\git_completer.ps1 | Register-CompleterRegistration Get-CompleterRegistration -ManagedOnly Get-CompleterRegistration -DiscoveredOnly Unregister-CompleterRegistration -CommandName git -Native After: Import-CompleterScript -Path .\git_completer.ps1 | Register-Completer Get-Completer -State Active, Pending, Failed, Stale Get-Completer -State Discovered, Conflicted Unregister-Completer -CommandName git -Native The new names take the parameters the aliases forwarded, so the rename is the whole change. A 2.x profile that already uses the new names needs nothing from this section. PUBLIC TYPES The four records and the two enums are public .NET types in the CompleterActions namespace: - [CompleterActions.CompleterRegistration] - [CompleterActions.ImportedCompleterRegistration] - [CompleterActions.CompleterScriptFinding] - [CompleterActions.CompletionMatch] - [CompleterActions.CompleterState]: Active, Stale, Conflicted, Pending, Failed, Discovered - [CompleterActions.CompleterType]: Native, Parameter Property names, property order, enum names, and enum values are the ones 2.x declared, so Select-Object *, Format-List, and ConvertTo-Csv show the same columns in the same order. In 2.x the records were PowerShell classes private to the module. Eight things differ for a caller: 1. -is and ::new() work everywhere. Once the module is imported, `$record -is [CompleterActions.CompleterRegistration]` is $true at the prompt and in any script, and `[CompleterActions.CompleterRegistration]::new()` builds a record. In 2.x both resolved only inside the module. The type literal resolves as long as the assembly is loaded in the process, so it keeps working after Remove-Module. 2. PSTypeNames loses the bare class name. A record's PSTypeNames is CompleterActions.CompleterRegistration, System.Object. In 2.x it was CompleterActions.CompleterRegistration, CompleterRegistration, System.Object, because the PowerShell class carried its own bare name and inserted the dotted one in front. The dotted name is still first, so the format views apply unchanged, `$record.PSTypeNames[0] -eq 'CompleterActions.CompleterRegistration'` is still $true, and a deserialized record is still Deserialized.CompleterActions.CompleterRegistration. Only a check for the bare name 'CompleterRegistration' in PSTypeNames stops matching. 3. String properties are '' when they have no value. Every string property of the four types starts as '' and stores '' when $null is assigned, so ParameterName on a native record, ScriptPath and LoadError on a record without a script, and ToolTip on a completion match are '' rather than $null. 2.x gave the same '' on the records the module returned; 3.0 extends it to an instance you build with ::new(), whose strings were $null in 2.x. ImportModule and ScriptBlock stay $null until they are set. 4. Enum values serialize as integers, as they did in 2.x. ConvertTo-Json writes "State": 3 for Pending and Export-Clixml writes the integer. Pass -EnumsAsStrings to ConvertTo-Json, or store `[string] $_.State`, when a script persists records. Comparisons with strings keep working: `$_.State -eq 'Pending'`, `Where-Object State -eq Pending`, `-State Pending`, and tab completion of -State values all work, because the parameter is typed [CompleterActions.CompleterState[]]. 5. The enums are usable from a script. `[enum]::GetNames([CompleterActions.CompleterState])` and `[CompleterActions.CompleterState]::Failed` work anywhere. In 2.x they needed `& (Get-Module CompleterActions) { [CompleterState] }`, and that form no longer resolves: the bare names are gone inside the module too. 6. Hashtable conversion works. `[CompleterActions.CompleterScriptFinding] @{ Path = 'x'; Line = 1 }` builds a finding through its property setters, as the module's own helpers do. 7. OutputType names are unchanged. `(Get-Command Get-Completer).OutputType.Name` is still CompleterActions.CompleterRegistration, and the other commands keep their 2.x output type names. 8. Remoting and jobs are unchanged. Across a remoting or job boundary a record arrives deserialized, as in 2.x: its type name is Deserialized.CompleterActions.CompleterRegistration and State is a plain string such as 'Pending', so `$_.State -eq 'Pending'` still holds on the receiving side. The assembly is CompleterActions.Core.dll, in the lib folder of the module, and the manifest loads it through RequiredAssemblies before the module's script runs. It exports no cmdlet. Its AssemblyVersion is 3.0.0.0 for every 3.x release, so a type keeps its identity across minor releases; the file version and informational version carry the module version. An assembly cannot be unloaded from a process: after an update, start a new pwsh session to load the new CompleterActions.Core.dll. The loaded version is `[CompleterActions.CompleterRegistration].Assembly.GetName().Version`. The assembly also holds the engine access and the strict grammar, in the CompleterActions.Internal namespace. Those types are public only because PowerShell cannot call internal members. They are not a contract and may change in any 3.x release; do not call them from a script. ENGINE FLOOR The manifest declares PowerShellVersion = '7.4', and CompatiblePSEditions stays @('Core'). On PowerShell 7.0 to 7.3, Import-Module CompleterActions fails with PowerShell's own minimum version error before any module file runs. The CI matrix has started at 7.4 since 1.3.0, so a tested engine sees no change. A machine that must stay on an older engine keeps CompleterActions 2.2.0, for example with `Install-PSResource CompleterActions -Version 2.2.0` and `Import-Module CompleterActions -RequiredVersion 2.2.0`. The engine probe that runs at import keeps its message. On an engine whose internals changed, Import-Module still fails with one error that names the running PowerShell version and the members it could not resolve: CompleterActions cannot run on PowerShell <version>: the required runtime member(s) '<list>' could not be resolved. Completer discovery depends on PowerShell internals; check for a module update that supports this engine version. What moved is where that check lives. The compiled layer resolves each engine member once at import and keeps it, so a member that resolved at import cannot fail to resolve later in the session. THE GRAMMAR AT REGISTRATION AND IMPORT In 2.x the strict grammar ran only when a script loaded, on the first tab press for one of its targets, because walking every script at import cost about 20 ms each. The compiled walk is cheap enough to run at import, so 3.0 also runs it wherever a strict script is already parsed for its targets. The first-tab check stays exactly as it was: a script that changes between registration and its first tab press is still checked in its changed form before it runs. Register-Completer -Lazy. Without -Trusted, the targets are read from the parsed script, and the grammar now runs on that parse. A script that does not conform fails the call with the error Import-CompleterScript gives, one line per finding, and nothing is registered for any of its targets. The check runs before -WhatIf is honoured, so -WhatIf fails the same way: Completer script 'C:\Completers\tool_completer.ps1' does not conform to the strict import grammar. Run Test-CompleterScript to work through the findings, or import with -Trusted to run the script as-is. Line 1, column 1 (CommandAst): The script uses unsupported top-level command 'Get-Date'. ... Import-CompleterSet. A strict entry that does not take the hash fast path is parsed for its targets, as in 2.x, and the grammar now runs on that parse. A script that does not conform is an invalid entry, reported from its first finding: Entry 4 ('tool_completer/tool_completer.ps1'): The script does not conform to the strict import grammar: line 1, column 1 (CommandAst): The script uses unsupported top-level command 'Get-Date'. Run Test-CompleterScript to work through the findings, or mark the entry Trusted to run it as-is. It takes the path of every invalid entry. Without -SkipInvalid the set fails and nothing is registered. With -SkipInvalid the entry is skipped with the usual "Completer set '<path>' skipped Entry <n> ..." warning and the other entries register. When the same entry also declares Targets that the script does not match, both problems are reported, the grammar first. What does not change: - An entry whose Hash matches its script takes the fast path. It is neither parsed nor walked at import, because the export that wrote the Hash derived its targets from the same text, and the first tab press runs the grammar before the script executes. A fully hashed set of unchanged scripts imports with exactly the work it did in 2.2.0; only a new or changed strict script pays for the walk. - Trusted entries and -Trusted registrations are never walked. - Test-CompleterSet reports drift, not grammar, and finds what it found in 2.2.0. Test-CompleterScript stays the grammar gate for a repository. - Export-CompleterSet does not run the grammar. What a profile sees: a set that in 2.x imported a non-conforming strict script as a Pending record, which turned Failed on its first tab press, now reports that entry when the profile runs. A profile that imports with -SkipInvalid gets one warning for the entry at session start and every other entry registers as before. A profile that imports without -SkipInvalid fails the whole set until the script is fixed, marked Trusted in the set, or removed from it. A repository whose scripts all pass Test-CompleterScript sees no difference. CHECKLIST FOR A PROFILE 1. Run PowerShell 7.4 or later, or pin CompleterActions 2.2.0. Uninstall the 2.x copies you no longer pin; while one is installed, an old name autoloads it instead of failing. 2. Replace Get-CompleterRegistration, Register-CompleterRegistration, and Unregister-CompleterRegistration with Get-Completer, Register-Completer, and Unregister-Completer, and any *Legacy function call with the same new name. 3. Replace -ManagedOnly with -State Active, Pending, Failed, Stale and -DiscoveredOnly with -State Discovered, Conflicted, or with the narrower state you meant. 4. Replace PSTypeNames checks for the bare name 'CompleterRegistration' with the dotted name or with `-is [CompleterActions.CompleterRegistration]`, and replace `& (Get-Module CompleterActions) { [CompleterState] }` with [CompleterActions.CompleterState]. 5. Run Test-CompleterScript over the completer repository and fix every Error finding, or mark the script Trusted in the set. A non-conforming strict script is now reported at import instead of failing at its first tab press. 6. Keep -SkipInvalid on the Import-CompleterSet line if one bad entry should not stop the rest of the set from registering. 7. Start a new pwsh session after updating, so the new CompleterActions.Core.dll is the one loaded. Completer set files and conforming completer scripts need no change. MOVING FROM 1.x A 1.x profile moves through 2.0 first. 2.0.0 renamed the three commands with the CompleterRegistration noun, and 3.0 removed the old names: | 1.x | 2.0 and 3.0 | | -------------------------------- | ---------------------- | | Get-CompleterRegistration | Get-Completer | | Register-CompleterRegistration | Register-Completer | | Unregister-CompleterRegistration | Unregister-Completer | Three more changes from 2.0.0 apply to a 1.x profile. The 2.0 edition of this topic, shipped with every 2.x release, covered them in full, so a profile that already runs on 2.x was migrated by it: - Targets are explicit. -Key is gone; name a target with -CommandName plus -Native or -ParameterName, or pipe a record that carries IsNative. Keys are output-only identifiers. - One -State filter replaced -ManagedOnly and -DiscoveredOnly, and a runtime registration that no managed record describes reports State 'Discovered' instead of 'Active'. - The records are typed: State and CompleterType are enums, empty string properties are '' instead of $null, and Get-Completer sorts by CompleterType, CommandName, and ParameterName before paging. SEE ALSO Get-Help Get-Completer -Full Get-Help Register-Completer -Full Get-Help Unregister-Completer -Full about_Import_Completers about_Completer_Sets |