@@ -18,17 +18,12 @@ function Get-Turtle {
18
18
if (-not $script :TurtleTypeData ) {
19
19
$script :TurtleTypeData = Get-TypeData - TypeName Turtle
20
20
}
21
- $methodNames = @ (foreach ($memberName in $script :TurtleTypeData.Members.Keys ) {
22
- if ($script :TurtleTypeData.Members [$memberName ] -is
23
- [Management.Automation.Runspaces.ScriptMethodData ]) {
24
- $memberName
25
- }
26
- })
21
+ $memberNames = @ ($script :TurtleTypeData.Members.Keys )
27
22
28
23
if ($wordToComplete ) {
29
- return $methodNames -like " $wordToComplete *"
24
+ return $memberNames -like " $wordToComplete *"
30
25
} else {
31
- return $methodNames
26
+ return $memberNames
32
27
}
33
28
})]
34
29
[Parameter (ValueFromRemainingArguments )]
@@ -43,68 +38,133 @@ function Get-Turtle {
43
38
$InputObject
44
39
)
45
40
46
- begin {
47
- $turtleType = Get-TypeData - TypeName Turtle
48
- $memberNames = @ (foreach ($memberName in $turtleType.Members.Keys ) {
49
- if (
50
- ($turtleType.Members [$memberName ] -is [Management.Automation.Runspaces.ScriptMethodData ]) -or
51
- (
52
- $turtleType.Members [$memberName ] -is
53
- [Management.Automation.Runspaces.AliasPropertyData ] -and
54
- $turtleType.Members [
55
- $turtleType.Members [$memberName ].ReferencedMemberName
56
- ] -is [Management.Automation.Runspaces.ScriptMethodData ]
57
- )
58
- ) {
59
- $memberName
60
- }
61
- })
41
+ begin {
42
+ # Get information about our turtle pseudo-type.
43
+ $turtleType = Get-TypeData - TypeName Turtle
44
+ # any member name is a potential command
45
+ $memberNames = $turtleType.Members.Keys
62
46
63
- $memberNames = $memberNames | Sort-Object @ {Expression = { $_.Length };Descending = $true }, Name
47
+ # We want to sort the member names by length, in case we need them in a pattern or want to sort quickly.
48
+ $memberNames = $memberNames | Sort-Object @ {Expression = { $_.Length };Descending = $true }, name
49
+ # Create a new turtle object in case we have no turtle input.
64
50
$currentTurtle = [PSCustomObject ]@ {PSTypeName = ' Turtle' }
65
51
}
66
52
67
- process {
68
-
53
+ process {
69
54
if ($PSBoundParameters.InputObject -and
70
55
$PSBoundParameters.InputObject.pstypenames -eq ' Turtle' ) {
71
56
$currentTurtle = $PSBoundParameters.InputObject
57
+ } elseif ($PSBoundParameters.InputObject ) {
58
+ # If input was passed, and it was not a turtle, pass it through.
59
+ return $PSBoundParameters.InputObject
72
60
}
73
61
74
- $currentMethod = $null
75
62
76
- $wordsAndArguments = foreach ($arg in $ArgumentList ) {
63
+ # First we want to split each argument into words.
64
+ # This way, it is roughly the same if you say:
65
+ # * `turtle 'forward 10'`
66
+ # * `turtle forward 10`
67
+ # * `turtle 'forward', 10`
68
+ $wordsAndArguments = @ (foreach ($arg in $ArgumentList ) {
69
+ # If the argument is a string, split it by whitespace.
77
70
if ($arg -is [string ]) {
78
71
$arg -split ' \s{1,}'
79
72
} else {
73
+ # otherwise, leave the argument alone.
80
74
$arg
81
75
}
82
- }
76
+ })
77
+
78
+ # Now that we have a series of words, we can process them.
79
+ # We want to keep track of the current member,
80
+ # and continue to the next word until we find a member name.
81
+ $currentMember = $null
83
82
84
- :findCommand for ($argIndex = 0 ; $argIndex -lt $wordsAndArguments.Length ; $argIndex ++ ) {
83
+ # To do this in one pass, we will iterate through the words and arguments.
84
+ # We use an indexed loop so we can skip past claimed arguments.
85
+ for ($argIndex = 0 ; $argIndex -lt $wordsAndArguments.Length ; $argIndex ++ ) {
85
86
$arg = $wordsAndArguments [$argIndex ]
86
- if ($arg -in $memberNames ) {
87
- $currentMethod = $arg
88
- for (
89
- $methodArgIndex = $argIndex + 1 ;
90
- $methodArgIndex -lt $wordsAndArguments.Length -and
91
- $wordsAndArguments [$methodArgIndex ] -notin $memberNames ;
92
- $methodArgIndex ++ ) {
87
+ # If the argument is not in the member names list, we can complain about it.
88
+ if ($arg -notin $memberNames ) {
89
+ if (-not $currentMember -and $arg -is [string ]) {
90
+ Write-Warning " Unknown command '$arg '."
93
91
}
94
- # Command without parameters
95
- if ($methodArgIndex -eq $argIndex ) {
96
- $argList = @ ()
97
- $currentTurtle = $currentTurtle .$currentMethod.Invoke ()
92
+ continue
93
+ }
94
+
95
+
96
+ # If we have a current member, we can invoke it or get it.
97
+ $currentMember = $arg
98
+ # We can also begin looking for arguments
99
+ for (
100
+ # at the next index.
101
+ $methodArgIndex = $argIndex + 1 ;
102
+ # We will continue until we reach the end of the words and arguments,
103
+ $methodArgIndex -lt $wordsAndArguments.Length -and
104
+ $wordsAndArguments [$methodArgIndex ] -notin $memberNames ;
105
+ $methodArgIndex ++ ) {
106
+ }
107
+ # Now we know how long it took to get to the next member name.
108
+
109
+ # And we can determine if we have any parameters
110
+ $argList =
111
+ if ($methodArgIndex -eq ($argIndex + 1 )) {
112
+ @ ()
98
113
}
99
114
else {
100
- $argList = $wordsAndArguments [($argIndex + 1 ).. ($methodArgIndex - 1 )]
101
- $currentTurtle = $currentTurtle .$currentMethod.Invoke ($argList )
102
- # "$($currentMethod) $($argList -join ' ')"
115
+ $wordsAndArguments [($argIndex + 1 ).. ($methodArgIndex - 1 )]
103
116
$argIndex = $methodArgIndex - 1
104
117
}
118
+
119
+ # Look up the member information for the current member.
120
+ $memberInfo = $turtleType.Members [$currentMember ]
121
+ # If it's an alias
122
+ if ($memberInfo.ReferencedMemberName ) {
123
+ # try to resolve it.
124
+ $currentMember = $memberInfo.ReferencedMemberName
125
+ $memberInfo = $turtleType.Members [$currentMember ]
126
+ }
127
+
128
+
129
+ # Now we want to get the output from the step.
130
+ $stepOutput =
131
+ if (
132
+ # If the member is a method, let's invoke it.
133
+ $memberInfo -is [Management.Automation.Runspaces.ScriptMethodData ] -or
134
+ $memberInfo -is [Management.Automation.PSMethod ]
135
+ ) {
136
+ # If we have arguments,
137
+ if ($argList ) {
138
+ # pass them to the method.
139
+ $currentTurtle .$currentMember.Invoke ($argList )
140
+ } else {
141
+ # otherwise, just invoke the method with no arguments.
142
+ $currentTurtle .$currentMember.Invoke ()
143
+ }
144
+ } else {
145
+ # If the member is a property, we can get it or set it.
146
+
147
+ # If we have any arguments,
148
+ if ($argList ) {
149
+ # lets try to set it.
150
+ $currentTurtle .$currentMember = $argList
151
+ } else {
152
+ # otherwise, lets get the property
153
+ $currentTurtle .$currentMember
154
+ }
155
+ }
156
+
157
+ # If the output is not a turtle object, we can output it.
158
+ # NOTE: This will lead to multiple types of output in the pipeline.
159
+ # Luckily, this should be one of the few cases where this does not annoy too much.
160
+ # Properties being returned will largely be strings or numbers.
161
+ if (-not ($stepOutput.pstypenames -eq ' Turtle' )) {
162
+ $stepOutput
163
+ } else {
164
+ $currentTurtle = $stepOutput
105
165
}
106
166
}
107
-
167
+
108
168
return $currentTurtle
109
169
}
110
170
}
0 commit comments