DockerCompletion.psm1

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
139
140
141
Param(
    [string[]]$CustomScriptPath
)

function Select-CompletionResult {
    Param(
        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [NativeCommandCompletionResult[]]$CompletionResult,
        [switch]$OptionWithArg,
        [switch]$LegacyOrTopLevelCommand,
        [switch]$SubCommand,
        [switch]$ManagementCommand
    )

    Process {
        if ($OptionWithArg) {
            $CompletionResult = $CompletionResult | Where-Object { $_.CompletionText -Like '-*' -and $_.TextType -NE 'Switch' }
        }
        if ($LegacyOrTopLevelCommand) {
            $CompletionResult = $CompletionResult | Where-Object { $_.TextType -eq 'LegacyCommand' -or $_.TextType -eq 'TopLevelCommand' }
        }
        if ($SubCommand) {
            $CompletionResult = $CompletionResult | Where-Object TextType -EQ SubCommand
        }
        if ($ManagementCommand) {
            $CompletionResult = $CompletionResult | Where-Object TextType -EQ ManagementCommand
        }
        $CompletionResult
    }
}

function Invoke-CompletionCustomScript {
    Param(
        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [string[]]$Path
    )

    Process {
        foreach ($p in $Path) {
            . $p
        }
    }
}

Invoke-CompletionCustomScript $PSScriptRoot\completers.ps1, $PSScriptRoot\completers4arguments.ps1
if ($CustomScriptPath) {
    Invoke-CompletionCustomScript $CustomScriptPath
}

$argumentCompleter = {
    Param([string]$wordToComplete, $commandAst, $cursorPosition)

    $completerName = 'docker'
    $optionWithArg = $null
    $managementCommand = $null
    $subCommand = $null
    $legacyOrTopLevelCommand = $null
    $indexOfFirstArg = -1
    $counter = 1

    for (; $counter -lt $commandAst.CommandElements.Count; $counter++) {
        $ce = $commandAst.CommandElements[$counter]
        if ($cursorPosition -lt $ce.Extent.EndColumnNumber) {
            break
        }

        if ($optionWithArg) {
            # The argument of $optionWithArg is completed by this $ce
            $optionWithArg = $null
            continue
        }

        $text = $ce.Extent.Text
        if ($text.StartsWith('-')) {
            if ($text -in (Invoke-Completer $completerName -Option -ArgumentList $wordToComplete, $commandAst, $cursorPosition |
                        Select-CompletionResult -OptionWithArg).CompletionText) {
                $optionWithArg = $text
            }
        } elseif (!$managementCommand -and !$legacyOrTopLevelCommand) {
            if ($text -in (Invoke-Completer $completerName -Completer -ArgumentList $wordToComplete, $commandAst, $cursorPosition |
                        Select-CompletionResult -LegacyOrTopLevelCommand).CompletionText) {
                $legacyOrTopLevelCommand = $text
                $completerName += "_$legacyOrTopLevelCommand"
            } else {
                $managementCommand = $text
                $completerName += "_$managementCommand"
            }
        } elseif ($managementCommand -and !$subCommand) {
            $results = Invoke-Completer $completerName -Completer -ArgumentList $wordToComplete, $commandAst, $cursorPosition
            if ($text -in ($results | Select-CompletionResult -SubCommand).CompletionText) {
                $subCommand = $text
                $completerName += "_$subCommand"
            } elseif ($text -in ($results | Select-CompletionResult -ManagementCommand).CompletionText) {
                $managementCommand = $text
                $completerName += "_$managementCommand"
            }
        } elseif ($indexOfFirstArg -lt 0) {
            $indexOfFirstArg = $counter
        }
    }

    if ($optionWithArg) {
        $completerName += "_$optionWithArg"
    }

    # At this point, $completerName is any of the following:
    # 'docker'
    # 'docker_optionWithArg'
    # 'docker_legacyOrTopLevelCommand'
    # 'docker_legacyOrTopLevelCommand_optionWithArg'
    # 'docker_managementCommand'
    # 'docker_managementCommand_subCommand'
    # 'docker_managementCommand_subCommand_optionWithArg'
    # These managementCommand can be followed by managementCommand (`trust` command)

    if ($wordToComplete) {
        $ceElements = $commandAst.CommandElements[$counter].Elements
        if ($ceElements) {
            # comma-separated args (e.g. `--mount type=bind,src=/c/Users/matt9ucci,dst=/root`)
            foreach ($cee in $ceElements) {
                if (($wordToComplete -eq $cee.Value) -and ($cursorPosition -ge $cee.Extent.StartOffset)) {
                    $wordToCompleteSubstring = $wordToComplete.Substring(0, $cursorPosition - $cee.Extent.StartOffset)
                }
            }
            $wordToComplete = $wordToCompleteSubstring
        } else {
            $wordToCompleteSubstring = $wordToComplete.Substring(0, $cursorPosition - $commandAst.CommandElements[$counter].Extent.StartOffset)
        }
    }

    if ($wordToComplete.StartsWith('-')) {
        $completionResult = Invoke-Completer $completerName -Option -ArgumentList $wordToComplete, $commandAst, $cursorPosition
    } else {
        $completionResult = Invoke-Completer $completerName -Completer -ArgumentList $wordToComplete, $commandAst, $cursorPosition, $indexOfFirstArg
    }

    $completionResult | Where-Object CompletionText -Like "$wordToCompleteSubstring*"
}

Register-NativeCommandArgumentCompleter docker $argumentCompleter