New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature Request - "pre" execution functionality #14484
Comments
If this is primarily about automatic reflecting the execution duration of commands in the prompt string, you can define your function Prompt {
# Calculate the previous command's execution duration (time span).
$durationInfo = if ($he = Get-History -Count 1) {
# Use a '0.00s' format: duration in *seconds*, with two decimal places.
' [{0:N2}s]' -f ($he.EndExecutionTime - $he.StartExecutionTime).TotalSeconds
}
# Insert the information into the default prompt string; e.g.:
# 'PS C:\foo> ' becomes 'PS C:\foo [0.23s]> '
"PS $($executionContext.SessionState.Path.CurrentLocation)${durationInfo}$('>' * ($nestedPromptLevel + 1)) "
} |
@mklement0 I already have something similar in my profile, like many others do, but your suggestion doesn't address the ask of Whilst I can technically get round this by the below, this really would be better built in to the engine and would mitigate needing to pass to this function a scriptblock to run function global:preprompt {
[CmdletBinding()]
[alias('pp')]
param (
[Parameter()]
[scriptblock]
$Scriptblock
)
$RunningAction = if ($Scriptblock.ToString().Length -gt 25) {$Scriptblock.ToString().Substring(0,25)} else {$Scriptblock.ToString().Substring(0,($Scriptblock.ToString().Length)) }
if ($PSVersionTable.PSEdition -match 'Desktop' -or $isWindows) {
$admin = ((New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent())).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator))
if ($admin -eq $true) {
Write-Host "[" -nonewline -foregroundcolor DarkGray ; Write-Host "Admin" -nonewline -foregroundcolor Red ; Write-Host "] " -nonewline -foregroundcolor DarkGray
$Host.UI.RawUI.WindowTitle = "[Admin] " + $WindowTitle + ' - ' + (Get-Date -Format HH:mm:ss) + ' - ' + $RunningAction
}
else {
$host.UI.RawUI.WindowTitle = $WindowTitle + ' - ' + (Get-Date -Format HH:mm:ss) + ' - ' + $RunningAction
}
}
Write-Host "[" -NoNewline ; Write-Host (Get-Date -Format "HH:mm:ss") -ForegroundColor Gray -NoNewline ; Write-Host "] [" -NoNewline ; Write-Host $RunningAction -NoNewline ; Write-Host "]" -NoNewline; Write-Host ''
$Scriptblock.Invoke()
} |
This comment has been minimized.
This comment has been minimized.
Thinking about this some more: Since the prompt by definition has already printed when you submit your command, the desired functionality isn't prompt-related as such. I wonder if what you're looking for is better implemented as event hooks exposed via For instance, You can - awkwardly - emulate this behavior with $ExecutionContext.InvokeCommand.PostCommandLookupAction = {
if ($global:_preExecHandled) { return }
$cmdLine = $MyInvocation.Line
if ($args[1].CommandOrigin -ne 'Runspace' -or $cmdLine -match 'PostCommandLookupAction|^prompt$') { return }
$global:_preExecHandled = $true; $global:_prevTitle = $host.UI.RawUI.WindowTitle
$info = "Submitting at $(Get-Date): $cmdLine"
Write-Host -Foreground Yellow $info
$host.UI.RawUI.WindowTitle = $info
}
$function:prompt = "$function:prompt; `$global:_preExecHandled = `$false; if (`$global:_prevTitle) { `$host.UI.RawUI.WindowTitle = `$global:_prevTitle }" Note: Restoring the window title doesn't work on Unix-like platforms, because you can only set the title there. |
You can get there today by doing this: function PSConsoleHostReadLine {
[Microsoft.PowerShell.PSConsoleReadLine]::ReadLine($Host.Runspace, $ExecutionContext)
preprompt | Out-Null
} That'll have the same limitations that in-engine support would have. Aside from changing the window title, I'm not sure how this could be used effectively. A real world example using the technique above would go a long way. |
That's a much simpler workaround, @SeeminglyScience, thanks. In the absence of a dedicated event such as Adapted to a simplified version of @kilasuit's workaround: function PSConsoleHostReadLine {
# Prompt the user for a command line to submit, save it in a variable and
# pass it through, by enclosing it in (...)
($line = [Microsoft.PowerShell.PSConsoleReadLine]::ReadLine($Host.Runspace, $ExecutionContext))
if ($line.Trim()) { # Only react to non-blank lines.
# Synthesize status info.
$info = "Launched [$line] at: $(get-date)"
# Set the window title to the status info...
$host.UI.RawUI.WindowTitle = $info
# ... and print the same information to the host (only) ("`e[33m" is yellow).
# Note: We use Out-Host to strictly print to the host.
# Using Write-Host would pollute the information stream (#6).
$host.UI.SupportsVirtualTerminal ? "`e[033m$info`e[m" : $info | Out-Host
}
} By using It still amounts to visual pollution, however - though that may be desired. Example screenshot (note the window title and the info printed in yellow): A way to lessen this pollution would be to offer a way to redraw the prompt string in place: @kilasuit, is that what you had in mind? If so, the question is: is this technically feasible, in a manner that doesn't disrupt transcripts? |
That's not impossible, but that would be a very challenging work item to make consistent. You'd have to keep track of exactly how much the prompt initially wrote, clear it, re-write it, and then force PSRL to re-render. Also every custom
I think the only feasible option transcript wise would be to just write the re-rendered prompt normally. |
See also: #15271, which also contains a more complete change-the-window-title-while-a-command-is-running workaround. |
The ask is a function to run before an accepted command line gets executed, so change the title to differentiate from #15104, which asks for a way to call custom functions before evaluating the 'Prompt' function. |
Any updates on this? As a former Windows user and admin who now works on unix, I'd love to get back to using PowerShell. However, not having the ability to run hooks in a language-supported way prevents me from using tools that the rest of my team uses like direnv and rtx. It would be great to run N preprompt functions to support entire toolchains. See |
FYI there is already an action that is invoked when the location changes, that may be preferable for those tool chains as they could even take effect mid-script using namespace System
using namespace System.Management.Automation
$handler = [EventHandler[LocationChangedEventArgs]]{
param([object] $source, [LocationChangedEventArgs] $eventArgs)
end {
Write-Host "Old '$($eventArgs.OldPath)' New '$($eventArgs.NewPath)'"
}
}
$currentAction = $ExecutionContext.SessionState.InvokeCommand.LocationChangedAction
if ($currentAction) {
$ExecutionContext.SessionState.InvokeCommand.LocationChangedAction = [Delegate]::Combine($currentAction, $handler)
} else {
$ExecutionContext.SessionState.InvokeCommand.LocationChangedAction = $handler
}
cd \ |
This is exactly what I was looking for, much appreciated @SeeminglyScience! |
This issue has not had any activity in 6 months, if there is no further activity in 7 days, the issue will be closed automatically. Activity in this case refers only to comments on the issue. If the issue is closed and you are the author, you can re-open the issue using the button below. Please add more information to be considered during retriage. If you are not the author but the issue is impacting you after it has been closed, please submit a new issue with updated details and a link to this issue and the original. |
Summary of the new feature/enhancement
Ability to have a version of the prompt function that can run prior to each command being run in the command line & allow changing of the prompt within it
This would allow for example for a series of actions to set items in the prompt and host prior to execution (like when you kicked off a command, particularly any long running command)
Proposed technical implementation details (optional)
a mirroring of the prompt function but instead runs before the command issued at the terminal is actually run.
The above would on each execution update the prompt with the time that the command was executed at
The same code used in the prompt function below update the prompt with the time that the command was completed at
Having both is useful for transcriptions/interactive glances/ long running processes & would be a useful improvement to the engine
The text was updated successfully, but these errors were encountered: