@@ -59,3 +59,90 @@ $aliasList +=
5959
6060
6161Export-ModuleMember - Function * - Alias $aliasList
62+
63+ $global :ExecutionContext.SessionState.InvokeCommand.CommandNotFoundAction = {
64+ param ($sender , $eventArgs )
65+
66+ # Rather than be the only thing that can handle command not found, we start by broadcasting an event.
67+ New-Event - SourceIdentifier " PowerShell.CommandNotFound" - MessageData $notFoundArgs - Sender $global :ExecutionContext - EventArguments $notFoundArgs
68+
69+ # Then, we do a bit of callstack peeking
70+ $callstack = @ (Get-PSCallStack )
71+ $callstackPeek = $callstack [-1 ]
72+ # When peeking in on a dynamic script block, the offsets may lie.
73+ $column = [Math ]::Max($callstackPeek.InvocationInfo.OffsetInLine , 1 )
74+ $line = [Math ]::Max($callstackPeek.InvocationInfo.ScriptLineNumber , 1 )
75+ $callingScriptBlock = $callstackPeek.InvocationInfo.MyCommand.ScriptBlock
76+ # Now find all of the AST elements at this location.
77+ $astFound = @ ($callingScriptBlock.Ast.FindAll ({
78+ param ($ast )
79+ $ast.Extent.StartLineNumber -eq $line -and
80+ $ast.Extent.StartColumnNumber -eq $column
81+ }, $true ))
82+ if (-not $script :LastCommandNotFoundScript ) {
83+ $script :LastCommandNotFoundScript = $callingScriptBlock
84+ } elseif ($script :LastCommandNotFoundScript -eq $callingScriptBlock ) {
85+ return
86+ } else {
87+ $script :LastCommandNotFoundScript = $callingScriptBlock
88+ }
89+
90+ if (-not $callingScriptBlock ) {
91+ return
92+ }
93+
94+
95+ $transpiledScriptBlock =
96+ try {
97+ $callingScriptBlock.Transpile ()
98+ } catch {
99+ Write-Error $_
100+ return
101+ }
102+ if ($transpiledScriptBlock -and
103+ ($transpiledScriptBlock.ToString ().Length -ne $callingScriptBlock.ToString ().Length)) {
104+
105+ $endStatements = $transpiledScriptBlock.Ast.EndBlock.Statements
106+ $FirstExpression =
107+ if ($endStatements -and (
108+ $endStatements [0 ] -is
109+ [Management.Automation.Language.PipelineAst ]
110+ ) -and (
111+ $endStatements [0 ].PipelineElements[0 ] -is
112+ [Management.Automation.Language.CommandExpressionAst ]
113+ )
114+ ) {
115+ $endStatements [0 ].PipelineElements[0 ].Expression
116+ } else { $null }
117+
118+ if ($astFound -and
119+ $astFound [-1 ].Parent -is [Management.Automation.Language.AssignmentStatementAst ] -and
120+ (
121+ $FirstExpression -is [Management.Automation.Language.BinaryExpressionAst ] -or
122+ $FirstExpression -is [Management.Automation.Language.ParenExpressionAst ]
123+ )
124+ ) {
125+ Write-Error "
126+ Will not interactively transpile {$callingScriptBlock } ( because it would overwrite $ ( $astFound [-1 ].Parent.Left.Extent) )"
127+ return
128+ }
129+
130+ if ($astFound -and
131+ $astFound [-1 ].Parent -is [Management.Automation.Language.AssignmentStatementAst ] -and
132+ $endStatements -and
133+ $endStatements [0 ] -is [Management.Automation.Language.AssignmentStatementAst ] -and
134+ $astFound [-1 ].Parent.Left.ToString() -eq $endStatements [0 ].Left.ToString()) {
135+ $eventArgs.CommandScriptBlock = [ScriptBlock ]::Create($endStatements [0 ].Right.ToString())
136+ $eventArgs.StopSearch = $true
137+ } else {
138+ $eventArgs.CommandScriptBlock = $transpiledScriptBlock
139+ $eventArgs.StopSearch = $true
140+ }
141+ }
142+
143+ return
144+ }
145+
146+ $MyInvocation.MyCommand.Module.OnRemove = {
147+ $global :ExecutionContext.SessionState.InvokeCommand.CommandNotFoundAction = $null
148+ }
0 commit comments