@@ -18,17 +18,12 @@ function Get-Turtle {
1818 if (-not $script :TurtleTypeData ) {
1919 $script :TurtleTypeData = Get-TypeData - TypeName Turtle
2020 }
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 )
2722
2823 if ($wordToComplete ) {
29- return $methodNames -like " $wordToComplete *"
24+ return $memberNames -like " $wordToComplete *"
3025 } else {
31- return $methodNames
26+ return $memberNames
3227 }
3328 })]
3429 [Parameter (ValueFromRemainingArguments )]
@@ -43,68 +38,133 @@ function Get-Turtle {
4338 $InputObject
4439 )
4540
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
6246
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.
6450 $currentTurtle = [PSCustomObject ]@ {PSTypeName = ' Turtle' }
6551 }
6652
67- process {
68-
53+ process {
6954 if ($PSBoundParameters.InputObject -and
7055 $PSBoundParameters.InputObject.pstypenames -eq ' Turtle' ) {
7156 $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
7260 }
7361
74- $currentMethod = $null
7562
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.
7770 if ($arg -is [string ]) {
7871 $arg -split ' \s{1,}'
7972 } else {
73+ # otherwise, leave the argument alone.
8074 $arg
8175 }
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
8382
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 ++ ) {
8586 $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 '."
9391 }
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+ @ ()
98113 }
99114 else {
100- $argList = $wordsAndArguments [($argIndex + 1 ).. ($methodArgIndex - 1 )]
101- $currentTurtle = $currentTurtle .$currentMethod.Invoke ($argList )
102- # "$($currentMethod) $($argList -join ' ')"
115+ $wordsAndArguments [($argIndex + 1 ).. ($methodArgIndex - 1 )]
103116 $argIndex = $methodArgIndex - 1
104117 }
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
105165 }
106166 }
107-
167+
108168 return $currentTurtle
109169 }
110170}
0 commit comments