ShowDemo.types.ps1xml
<?xml version="1.0" encoding="utf-16"?> <!-- Generated with EZOut 2.0.5: Install-Module EZOut or https://github.com/StartAutomating/EZOut --> <Types> <Type> <Name>Demo</Name> <Members> <ScriptMethod> <Name>Dump</Name> <Script> $demoContent = @(foreach ($chapter in $this.Chapters) { "# $($chapter.Number) $($chapter.Name)" $stepIndex = 0 foreach ($step in $chapter.Steps) { $stepIndex++ if ($this.CurrentChapter -eq $chapter -and $this.CurrentStep -eq $stepIndex) { " <# *** You Are Here! *** #>" } $step } }) -join [Environment]::NewLine $demoContent </Script> </ScriptMethod> <ScriptMethod> <Name>NextChapter</Name> <Script> <# .SYNOPSIS Go to the Next Chapter in a Demo .DESCRIPTION Advances a demo to the next chapter. #> $demo = $this $chapterIndex = $demo.Chapters.IndexOf($demo.CurrentChapter) $chapterIndex++ if (-not $demo.Chapters[$chapterIndex]) { $demo | Add-Member NoteProperty DemoFinished ([datetime]::Now) -Force $null = New-Event -SourceIdentifier Demo.Complete -Sender $this } else { $null = New-Event -SourceIdentifier Demo.NextChapter -Sender $this -EventArguments $demo.Chapters[$chapterIndex].Name -MessageData $demo.Chapters[$chapterIndex] $demo | Add-Member NoteProperty CurrentChapter $demo.Chapters[$chapterIndex] -Force $demo | Add-Member NoteProperty CurrentStep 0 -Force } </Script> </ScriptMethod> <ScriptMethod> <Name>NextStep</Name> <Script> <# .SYNOPSIS Go to the next demo step .DESCRIPTION Advances a demo to the next step. #> $demo = $this $demo.CurrentStep++ # No more steps in this chapter if (-not $demo.CurrentChapter.Steps[$demo.CurrentStep - 1]) { $demo.NextChapter() } $null = New-Event -SourceIdentifier Demo.NextStep -Sender $this -EventArguments $args </Script> </ScriptMethod> <ScriptMethod> <Name>ProcessInput</Name> <Script> <# .SYNOPSIS Processes demo input .DESCRIPTION Processes user input to a demo file. #> param($hostInput) $demo = $this $null = New-Event -SourceIdentifier Demo.ProcessInput -Sender $this -EventArguments @($hostinput) switch ($hostInput) { '?' { @{ ForegroundColor = 'Cyan' InputObject = @" Running demo: $($demo.Name) (q) Quit (# ...) Goto Chapter/Step (f ...) Find cmds using X (t) Timecheck (s) Skip (!) Debug Demo (d) Dump demo "@ } } 'q' { $demo.Stop() } 'd' { $demoDump = $demo.Dump() $demoDump | Out-Host if (Get-Command Set-Clipboard -ErrorAction SilentlyContinue) { $demoDump | Set-Clipboard } } 's' { $demo | Add-Member NoteProperty StepToRun $null -Force } 't' { $duration = [Datetime]::now - $demo.DemoStarted @{ ForegroundColor = 'Warning' Italic = $true InputObject = "{0} {1} [{2}m, {3}s]" -f $demo.Name, $demo.Status, [int]$Duration.TotalMinutes, [int]$Duration.Seconds } } '!' { Write-Warning "Debugging Demo: Use Resume-Demo to resume." $host.EnterNestedPrompt() } default { if ($hostInput -match '^\s{0,}\#\s{0,}\d') { $demo | Add-Member NoteProperty StepToRun $null -Force $demo.SetChapter($hostInput -replace '^\s{0,}\#\s{0,}') } elseif ($hostInput -match '^\s{0,}(?>f|/)\s{0,}\S') { $toFind = $hostInput -replace '^\s{0,}(?>f|/)\s{0,}' Select-String -Path $demo.DemoFile -Pattern $toFind | Out-Host {} } } } </Script> </ScriptMethod> <ScriptMethod> <Name>Reset</Name> <Script> <# .SYNOPSIS Resets a demo .DESCRIPTION Resets a demo file, so that it can be replayed. #> $this | Add-Member NoteProperty Status "NotStarted" -Force $this | Add-Member NoteProperty StepToRun $null -Force $demo | Add-Member NoteProperty DemoFinished $null -Force $demo | Add-Member NoteProperty DemoStarted $null -Force $demo | Add-Member NoteProperty CurrentChapter $null -Force $demo | Add-Member NoteProperty CurrentStep $null -Force $null = New-Event -SourceIdentifier Demo.Reset -Sender $this -EventArguments $args </Script> </ScriptMethod> <ScriptMethod> <Name>SetChapter</Name> <Script> param([string]$Chapter) if (-not $this.Chapters) { throw "No Chapters" } $chapterParts = @($chapter -split '\.') $potentialChapterNumbers = @( if ($chapterParts.Length -gt 1) { ($chapterParts[0..($chapterParts.Length - 2)] -join '\.') + '*' } $chapter + '*' ) $newChapter, $newStep = :nextChapter foreach ($chap in $this.Chapters) { foreach ($potential in $potentialChapterNumbers) { if ($chap.Number -like $potential) { if ($potential -ne ($Chapter + '*')) { # Setting chapter and step number $chap, $chapterParts[-1] -as [int] break nextChapter } else { $chap, 1 } } } } if (-not $newChapter) { throw "Could not find chapter '$chapter'" } $this | Add-Member NoteProperty CurrentChapter $newChapter -Force $this | Add-Member NoteProperty CurrentStep ($newStep - 1) -Force </Script> </ScriptMethod> <ScriptMethod> <Name>SetStatus</Name> <Script> <# .SYNOPSIS Sets demo status .DESCRIPTION Sets the status of a demo. #> param([string]$Status) $this | Add-Member Status $status -Force $null = New-Event -SourceIdentifier Demo.SetStatus -Sender $this -EventArguments @($Status) </Script> </ScriptMethod> <ScriptMethod> <Name>ShowStep</Name> <Script> param($step) $null = New-Event -SourceIdentifier Demo.ShowStep -Sender $this -EventArguments @($step) $stepTokens = [Management.Automation.PSParser]::Tokenize($step, [ref]$null) $PreviousToken = $null foreach ($_ in $stepTokens) { $content = $_.Content if ($_.Type -in 'Variable', 'String') { $content = $step.Substring($_.Start, $_.Length) } if ($PreviousToken) { $token = $_ $prevEnd = $PreviousToken.Start + $PreviousToken.Length $substring = $step.Substring($prevEnd, $token.Start - $prevEnd) if ($substring) { @{InputObject=$substring} } } if ($_.Type -eq 'Comment') { @{ ForegroundColor='Success' InputObject = $content } } elseif ($_.Type -in 'Keyword', 'String', 'CommandArgument') { @{ ForegroundColor='Verbose' InputObject = $Content } } elseif ($_.Type -in 'Variable', 'Command') { @{ ForegroundColor='Warning' InputObject = $Content } } elseif ($_.Type -in 'CommandParameter') { @{ ForegroundColor='Magenta' InputObject = $Content } } elseif ( $_.Type -in 'Operator','GroupStart', 'GroupEnd' ) { @{ ForegroundColor='Magenta' InputObject = $Content } } elseif ( $_.Type -notin 'Comment', 'Keyword', 'String', 'CommandArgument', 'Variable', 'Command', 'CommandParameter', 'Operator','GroupStart', 'GroupEnd' ) { @{ ForegroundColor='White' InputObject=$Content } } else { @{ ForegroundColor='White' InputObject=$Content } } $PreviousToken = $_ } </Script> </ScriptMethod> <ScriptMethod> <Name>Start</Name> <Script> <# .SYNOPSIS Starts a Demo .DESCRIPTION Starts a Demo file. #> $this | Add-Member NoteProperty Status Running -Force $this | Add-Member NoteProperty DemoStarted ([DateTime]::Now) -Force $null = New-Event -SourceIdentifier Demo.Start -Sender $this -EventArguments $args </Script> </ScriptMethod> <ScriptMethod> <Name>StartChapter</Name> <Script> $this | Add-Member NoteProperty CurrentStep 1 -Force </Script> </ScriptMethod> <ScriptMethod> <Name>Stop</Name> <Script> <# .SYNOPSIS Stops a demo .DESCRIPTION Stops a demo that is currently running #> $this | Add-Member NoteProperty Status Stopped -Force $this | Add-Member NoteProperty StepToRun $null -Force $this | Add-Member NoteProperty DemoFinished ([datetime]::Now) -Force $demo | Add-Member NoteProperty CurrentChapter $null -Force $demo | Add-Member NoteProperty CurrentStep $null -Force $null = New-Event -SourceIdentifier Demo.Stop -Sender $this -EventArguments $args </Script> </ScriptMethod> <ScriptMethod> <Name>ToMarkdown</Name> <Script> # We don't want to modify this object $demoCopy = # so create a copy if ($this.DemoFile) { # by importing the file Import-Demo -DemoPath $this.DemoFile } elseif ($this.DemoScript) { # or the script block. Import-Demo -DemoPath $this.DemoScript } # We need Write-Host to be overridden in the same way as Export-Demo does. # So find Export-Demo's Abstract Syntax Tree $exportDemoAst = $ExecutionContext.SessionState.InvokeCommand.GetCommand('Export-Demo','Function').ScriptBlock.Ast.Body # and find our inner function $writeHost = $exportDemoAst.Find({param($ast) $ast.Name -eq 'Write-Host' -and $ast.IsFilter -eq $false }, $false) # And override it here ${function:Write-Host} = $writeHost.Body.GetScriptBlock() # Now, modify our demo copy to make it non-interactive $demoCopy | Add-Member NoteProperty Interactive $false -Force # and markdown $demoCopy | Add-Member NoteProperty Markdown $true -Force # then use the formatter to get the markdown as a string. $demoCopy | Format-Custom | Out-String -Width 1mb </Script> </ScriptMethod> <ScriptProperty> <Name>TotalSteps</Name> <GetScriptBlock> $stepCount = 0 foreach ($chapter in $this.Chapters) { $stepCount += $chapter.Steps.Length } $stepCount </GetScriptBlock> </ScriptProperty> </Members> </Type> <Type> <Name>Demo.Chapter</Name> <Members> <ScriptProperty> <Name>Steps</Name> <GetScriptBlock> $text = $this.Text $step = @() $ThisChapterSteps = @() # We want every step to be able to run independently. # This would be untrue if the code is unbalanced when a chapter would start # Thus, while we're primarily looking for comments, we also need to track groups $groupDepth = 0 for ($tokenNumber =0 ; $tokenNumber -lt $this.Tokens.Length; $tokenNumber++) { $token = $this.tokens[$tokenNumber] # If the token is a group start if ($token.Type -eq 'GroupStart') { $groupDepth++ # increment depth. } # If the token was a group end elseif ($token.Type -eq 'GroupEnd') { $groupDepth-- # decrement depth. } # and elseif ( (-not $groupDepth) -and # If there was no depth and $token.StartColumn -le 1 -and # the token was a comment starting in the first column $token[-1].Type -ne 'Keyword' -and # and it wasn't preceeded by a keyword $token[0].Type -ne 'Keyword' -and # and it wasn't a keyword $token[0].Type -ne 'Newline' # and it wasn't a newline ) { # Then it's the start of a new step if ($step) { $stepEnd = $step[-1].Start + $step[-1].Length $stepStart = $step[0].Start # Get the content of the last step $stepScript = $text.Substring($stepStart, $stepEnd - $stepStart) -replace '^\s{0,}$' if ($stepScript) { # and make it into a PSObject $stepScript = [PSObject]::new($stepScript) # with the PSTypeName 'Demo.Step' $stepScript.pstypenames.add('Demo.Step') # and add the .Chapter property, pointing to $this $stepScript.psobject.properties.add([psnoteproperty]::new( 'Chapter',$this )) $ThisChapterSteps += $stepScript } # then reset the collection of tokens in the current step. $step = @() } } # Add any token we see into the current step. $step += $token } # If there were any steps remaining if ($step) { $stepEnd = $step[-1].Start + $step[-1].Length $stepStart = $step[0].Start $stepScript = $text.Substring($stepStart, $stepEnd - $stepStart) -replace '^\s{0,}$' if ($stepScript) { # make them into 'Demo.Step' objects $stepScript = [PSObject]::new($stepScript) $stepScript.pstypenames.add('Demo.Step') $stepScript.psobject.properties.add([psnoteproperty]::new( 'Chapter',$this )) # and add the chapter. $ThisChapterSteps += $stepScript } } # Force steps to be returned as a list. ,$ThisChapterSteps </GetScriptBlock> </ScriptProperty> </Members> </Type> <Type> <Name>Demo.Step</Name> <Members> <ScriptMethod> <Name>HidePrompt</Name> <Script> <# .SYNOPSIS Hides the prompt .DESCRIPTION Hides the prompt within a demo. .EXAMPLE #.HidePrompt #> param( # Any additional parameters for the step. # This is ignored when hiding prompts. $step ) $this.Chapter.Demo | Add-Member NoteProperty ShowPrompt $false $null = New-Event -SourceIdentifier Demo.HidePrompt -Sender $this.Chapter.Demo -EventArguments @($step) </Script> </ScriptMethod> <ScriptMethod> <Name>Invoke</Name> <Script> <# .SYNOPSIS Invokes a demo step .DESCRIPTION Invokes a step in a demo file. #> $hiddenStep = $this.HiddenStep $invokeResults = if (-not $hiddenStep) { Invoke-Expression $this } elseif ($this.$($hiddenStep.StepType).Invoke) { $this.$($hiddenStep.StepType).Invoke($hiddenStep.Arguments) } $invokeResults $null = New-Event -SourceIdentifier Demo.Step.Invoke -Sender $this -EventArguments $args -MessageData $invokeResults </Script> </ScriptMethod> <ScriptMethod> <Name>ShowPrompt</Name> <Script> <# .SYNOPSIS Shows the prompt .DESCRIPTION Show the prompt within a demo. .EXAMPLE #.ShowPrompt #> param( # Any additional parameters for the step. # This is ignored when showing prompts. $step ) $this.Chapter.Demo | Add-Member NoteProperty ShowPrompt $true $null = New-Event -SourceIdentifier Demo.ShowPrompt -Sender $this.Chapter.Demo -EventArguments @($step) </Script> </ScriptMethod> <ScriptMethod> <Name>Silent</Name> <Script> <# .SYNOPSIS Run a silent step .DESCRIPTION Run a silent step of a demo. Silent steps do not display their results. #> param($silentStep) Invoke-Expression $silentStep | Out-Null $null = New-Event -SourceIdentifier Demo.Step.Silent -Sender $this -EventArguments @($silentStep) </Script> </ScriptMethod> <ScriptProperty> <Name>HiddenStep</Name> <GetScriptBlock> $specialStepNameRegex = '^\s{0,}\<{0,1}\#\s{0,}\.(?<st>[\S]+)' if ($this -notmatch $specialStepNameRegex) { return $null } [PSCustomObject]@{ StepType = $matches.st Arguments = $this -replace $specialStepNameRegex } </GetScriptBlock> </ScriptProperty> <ScriptProperty> <Name>IsComment</Name> <GetScriptBlock> $stepTokens = [Management.Automation.PSParser]::Tokenize($this, [ref]$null) foreach ($token in $stepTokens) { if ($token.Type -notin 'Comment', 'Newline') { return $false } } return $true </GetScriptBlock> </ScriptProperty> </Members> </Type> </Types> |