Public/Add-ModuleQualification.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
using namespace Microsoft.PowerShell.EditorServices.Extensions
using namespace System.Management.Automation.Language

function Add-ModuleQualification {
    <#
    .EXTERNALHELP EditorServicesCommandSuite-help.xml
    #>

    [EditorCommand(DisplayName='Add Module Name to Closest Command')]
    [CmdletBinding()]
    param(
        [System.Management.Automation.Language.Ast]
        $Ast
    )
    begin {
        function InferCommandInfo([string]$commandName) {
            # HACK: If someone knows a reliable way to perform command lookup outside the module,
            # without reflection, please let me know or send a PR.
            $flags = [Reflection.BindingFlags]'Instance, NonPublic'
            $context = $ExecutionContext.
                GetType().
                GetField('_context', $flags).
                GetValue($ExecutionContext)
            $globalState = $context.
                GetType().
                GetProperty('TopLevelSessionState', $flags).
                GetValue($context)

            $getCommand = { $ExecutionContext.InvokeCommand.GetCommand($args[0], 'All') }

            $null = [scriptblock].
                GetProperty('SessionStateInternal', $flags).
                SetValue($getCommand, $globalState)

            $PSCmdlet.WriteVerbose($Strings.InferringFromSession)

            $command = $getCommand.InvokeReturnAsIs($commandName)

            if ($command) { return $command }

            $PSCmdlet.WriteVerbose($Strings.InferringFromWorkspace)
            try {
                $manifest = GetInferredManifest
                if (($moduleInfo = Get-Module $manifest.Name -ErrorAction Ignore)) {
                    # Retrieve command info from the first returned module incase multiple versions
                    # are loaded into the session.
                    return $moduleInfo[0].Invoke($getCommand, $commandName)
                }
                $isExport = $manifest.FunctionsToExport -contains $commandName -or
                            $manifest.CmdletsToExport   -contains $commandName
                # If it's exported in the manifest but not loaded we can't actually get CommandInfo,
                # but we can return the properties we expect anyway.
                if ($isExport) {
                    return @{
                        ModuleName = $manifest.Name
                        Name       = $commandName
                    }
                }
            } catch {
                $PSCmdlet.WriteVerbose($Strings.VerboseInvalidManifest)
            }
            $PSCmdlet.WriteVerbose('Unable to find command "{0}".' -f $commandName)
        }
    }
    end {
        $Ast = GetAncestorOrThrow $Ast -AstType CommandAst

        $command = InferCommandInfo $Ast.GetCommandName()
        if (-not $command) {
            ThrowError -Exception ([ArgumentException]::new($Strings.CannotInferModule)) `
                       -Id        CannotInferModule `
                       -Category  InvalidArgument `
                       -Target    $Ast.GetCommandName() `
                       -Show
        }

        if (-not $command.ModuleName) {
            $PSCmdlet.WriteVerbose($Strings.CommandNotInModule)
            return
        }

        $newExpression = '{0}\{1}' -f $command.ModuleName, $command.Name
        $Ast.CommandElements[0] | Set-ScriptExtent -Text $newExpression
    }
}