Skip to content

Commit eb5518f

Browse files
StartAutomatingStartAutomating
authored andcommitted
PipeScript Module: Handling CommandNotFound (Fixes #281)
1 parent dcd9b19 commit eb5518f

1 file changed

Lines changed: 87 additions & 0 deletions

File tree

PipeScript.psm1

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,3 +59,90 @@ $aliasList +=
5959

6060

6161
Export-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

Comments
 (0)