Skip to content

Commit 2e8b740

Browse files
author
James Brundage
committed
PipeScript Module: Handling CommandNotFound (Fixes #281)
1 parent 878edda commit 2e8b740

1 file changed

Lines changed: 84 additions & 1 deletion

File tree

PipeScript.ps1.psm1

Lines changed: 84 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,87 @@ $MyModule = $MyInvocation.MyCommand.ScriptBlock.Module
1919
$aliasList +=
2020
[GetExports("Alias")]$MyModule
2121

22-
Export-ModuleMember -Function * -Alias $aliasList
22+
Export-ModuleMember -Function * -Alias $aliasList
23+
24+
$global:ExecutionContext.SessionState.InvokeCommand.CommandNotFoundAction = {
25+
param($sender, $eventArgs)
26+
27+
# Rather than be the only thing that can handle command not found, we start by broadcasting an event.
28+
New-Event -SourceIdentifier "PowerShell.CommandNotFound" -MessageData $notFoundArgs -Sender $global:ExecutionContext -EventArguments $notFoundArgs
29+
30+
# Then, we do a bit of callstack peeking
31+
$callstack = @(Get-PSCallStack)
32+
$callstackPeek = $callstack[-1]
33+
# When peeking in on a dynamic script block, the offsets may lie.
34+
$column = [Math]::Max($callstackPeek.InvocationInfo.OffsetInLine, 1)
35+
$line = [Math]::Max($callstackPeek.InvocationInfo.ScriptLineNumber, 1)
36+
$callingScriptBlock = $callstackPeek.InvocationInfo.MyCommand.ScriptBlock
37+
# Now find all of the AST elements at this location.
38+
$astFound = @($callingScriptBlock.Ast.FindAll({
39+
param($ast)
40+
$ast.Extent.StartLineNumber -eq $line -and
41+
$ast.Extent.StartColumnNumber -eq $column
42+
}, $true))
43+
if (-not $script:LastCommandNotFoundScript) {
44+
$script:LastCommandNotFoundScript = $callingScriptBlock
45+
} elseif ($script:LastCommandNotFoundScript -eq $callingScriptBlock) {
46+
return
47+
} else {
48+
$script:LastCommandNotFoundScript = $callingScriptBlock
49+
}
50+
51+
if (-not $callingScriptBlock) {
52+
return
53+
}
54+
55+
56+
$transpiledScriptBlock =
57+
try {
58+
$callingScriptBlock.Transpile()
59+
} catch {
60+
Write-Error $_
61+
return
62+
}
63+
if ($transpiledScriptBlock -and
64+
($transpiledScriptBlock.ToString().Length -ne $callingScriptBlock.ToString().Length)) {
65+
66+
$endStatements = $transpiledScriptBlock.Ast.EndBlock.Statements
67+
$FirstExpression =
68+
if ($endStatements -and (
69+
$endStatements[0] -is
70+
[Management.Automation.Language.PipelineAst]
71+
) -and (
72+
$endStatements[0].PipelineElements[0] -is
73+
[Management.Automation.Language.CommandExpressionAst]
74+
)
75+
) {
76+
$endStatements[0].PipelineElements[0].Expression
77+
} else { $null }
78+
79+
if ($astFound -and
80+
$astFound[-1].Parent -is [Management.Automation.Language.AssignmentStatementAst] -and
81+
(
82+
$FirstExpression -is [Management.Automation.Language.BinaryExpressionAst] -or
83+
$FirstExpression -is [Management.Automation.Language.ParenExpressionAst]
84+
)
85+
) {
86+
Write-Error "
87+
Will not interactively transpile {$callingScriptBlock} ( because it would overwrite $($astFound[-1].Parent.Left.Extent) )"
88+
return
89+
}
90+
91+
if ($astFound -and
92+
$astFound[-1].Parent -is [Management.Automation.Language.AssignmentStatementAst] -and
93+
$endStatements -and
94+
$endStatements[0] -is [Management.Automation.Language.AssignmentStatementAst] -and
95+
$astFound[-1].Parent.Left.ToString() -eq $endStatements[0].Left.ToString()) {
96+
$eventArgs.CommandScriptBlock = [ScriptBlock]::Create($endStatements[0].Right.ToString())
97+
$eventArgs.StopSearch = $true
98+
} else {
99+
$eventArgs.CommandScriptBlock = $transpiledScriptBlock
100+
$eventArgs.StopSearch = $true
101+
}
102+
}
103+
104+
return
105+
}

0 commit comments

Comments
 (0)