在按下 ENTER 之前获取 powershell 当前行
Getting powershell current line before ENTER is pressed
我有一个想法,就是编写一个可视化工具,在您键入时显示 PowerShell 行的 AST。但是要做到这一点,第一步是获取当前行的文本,在提交之前(在按下 ENTER 之前),但我找不到 API 函数或钩子来做到这一点。有吗?
我在新的 Windows 终端上使用 PowerShell Core 7.1.0。
预测源
PSReadLine 的 PredictiveSource 选项似乎可以用于此目的,前提是它可以在每个字母输入上调用,而不仅仅是在 TAB 上调用,但我找不到关于 3rd 类型合同的任何信息- 深入研究文档和 C# 代码后的派对插件...
设置-PSReadLineKeyHandler
正如传说中的@mklement0 所建议的那样,也许可以使用 Set-PSReadLineKeyHandler
。它似乎旨在用于键绑定,但我仍在思考如何将其用于此目的。
虽然没有官方机制来响应每个击键,但您可以通过为每个可打印字符和 select 几个控制字符设置一个 键处理程序来实现自己的机制,通过Set-PSReadLineKeyHandler
cmdlet。
在键处理程序中,您可以在输入行下方显示有关输入缓冲区当前状态的信息。
tl;博士:
通过修改 $metaInfo = ...
行来调整下面的代码,以确定要在其下方实时显示有关正在编辑的命令行的哪些信息.
阅读下一节中的限制。
注:
它是设置密钥处理程序的前 256 个 Unicode 代码点中的可打印字符,实际上是构成 ISO-8859-1 encoding, itself a subset of Windows-1252[=78 的字符集=][1]。因此,所有 ASCII 范围的字母加上一些带重音的字母都被覆盖了,但例如西里尔字母就不会被覆盖。但是,您可以根据需要定制列表。
为了便于说明,下面的代码并没有尝试将 AST 可视化,而是以 [=26= 的格式化表示形式打印有关刚刚按下的键的信息]实例。
在下面的代码中找到以$metaInfo =
开头的行来自定义显示的内容。
获取表示缓冲区内容的AST(抽象语法树)的命令为:
$ast = $tokens = $errors = $cursor = $null
[Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref] $ast, [ref] $tokens, [ref] $errors, [ref] $cursor)
该代码在常规 Windows 控制台中效果最佳 windows (conhost.exe
); 限制:
在 Windows 终端和 Unix 终端中,如果当前输入行太靠近 window 的底部边缘而无法容纳所需的行,则需要解决方法显示自定义信息:清屏,提示出现在window的第一行
- 但是,回滚缓冲区的内容已完全保留,因此如果需要,您只需向上滚动即可查看滚出视图的屏幕内容。
粘贴命令通过模拟输入,而密钥处理程序有效似乎得到PSReadLine
模块对当前终端行是什么感到困惑,因此自定义信息的多次打印操作最终会堆叠在一起,而不是在原地相互覆盖。
- 这只能在 Windows 上避免 - 在常规控制台 windows 和 Windows 终端中 - 使用 Ctrl-V 粘贴,在这种情况下,文本会真正粘贴到命令行上,但不会触发密钥处理程序(您必须随后键入另一个(虚拟)字符以根据粘贴的内容触发密钥处理程序)。
- 相比之下,执行触发所述问题的模拟输入:
- 总是在 Unix 终端中
- 在 Windows 上右键单击 粘贴
# The printable characters to respond to.
$printableChars = [char[]] (0x20..0x7e + 0xa0..0xff)
# The control characters to respond to.
$controlChars = 'Enter', 'Escape', 'Backspace', 'Delete'
# Set up the key handler for all specified characters.
$printableChars + $controlChars | ForEach-Object {
Set-PSReadLineKeyHandler $_ {
param($key, $arg)
$line = $cursor = $null
[Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref] $null, [ref] $cursor)
# Handle the key at hand.
switch ($key.Key) {
'Backspace' { [Microsoft.PowerShell.PSConsoleReadLine]::BackwardDeleteChar(); break }
'Delete' { try { [Microsoft.PowerShell.PSConsoleReadLine]::Delete($cursor, 1) } catch { }; break } # ignore error with empty buffer
'Escape' {
[Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref] $line, [ref] $null)
[Microsoft.PowerShell.PSConsoleReadLine]::Delete([=11=], $line.Length)
break
}
'Enter' {
# Clear any previous meta-information output, so that it doesn't linger and get mixed with command output.
try {
# !! On conhost.exe (regular console) windows on Windows, [Console]::CursorTop and [Console]::WindowTop are *relative to the scrollback buffer*.
# !! In Windows Terminal and on Unix, [Console]::WindowTop is always 0, and [Console]::CursorTop is relative to the screen height - even in the presence of a scrollback buffer.
Write-Host -NoNewLine (, (' ' * [Console]::WindowWidth) * ([Console]::WindowTop + [Console]::WindowHeight - [Console]::CursorTop - 1) -join "`n")
}
catch { Write-Warning "`nClearing the screen below the current line failed: $_" } # This shouldn't happen.
# !! Workaround for a display bug: If the cursor isn't at the very end of the line, everything to the
# !! right is inexplicably *erased* on submission, even though the submission itself still works fine.
# !! We detect that case and simply fill the entire buffer again, which leaves it drawn correctly on submission.
# !! (Note that [Microsoft.PowerShell.PSConsoleReadLine]::SetCursorPosition($line.Length) does *not* work.)
[Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref] $line, [ref] $cursor)
if ($cursor -ne $line.length) {
[Microsoft.PowerShell.PSConsoleReadLine]::Delete(0, $line.Length)
[Microsoft.PowerShell.PSConsoleReadLine]::Insert($line)
}
# Submit the command.
[Microsoft.PowerShell.PSConsoleReadLine]::AcceptLine()
return # We're done.
}
Default { [Microsoft.PowerShell.PSConsoleReadLine]::Insert($key.KeyChar) }
}
# Get the updated buffer content and cursor position.
[Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$line, [ref] $cursor)
# Note: To get the *AST* (too), use the following:
# $ast = $tokens = $errors = $cursor = $null
# [Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref] $ast, [ref] $tokens, [ref] $errors, [ref] $cursor)
# Determine the meta-informaton to print:
$metaInfo = $key | Out-String
if ($env:OS -ne 'Windows_NT' -or $env:WT_SESSION) {
# Workaround for all terminals except conhost.exe
# See comments at the top of the answer.
if ([Console]::CursorTop + $metaInfo.Count -gt [Console]::WindowTop + [Console]::WindowHeight) {
[Microsoft.PowerShell.PSConsoleReadLine]::ClearScreen()
}
}
# Print the desired information below the line being edited.
# Note:
# * The .PadRight() calls ensure that all lines are fully filled (padded with spaces),
# in order to erase potential remnants from previously displayed information.
# * This is NOT sufficient to deal with *varying line counts* being displayed, however.
Write-Host # blank line
Write-Host -NoNewLine -ForegroundColor Yellow ($metaInfo -split '\r?\n' | ForEach-Object PadRight ([Console]::WindowWidth-1), ' ')
# Set the new cursor position.
[Microsoft.PowerShell.PSConsoleReadLine]::SetCursorPosition($cursor)
}
}
密钥处理程序的示例屏幕截图:
请注意正在编辑的命令行下方的信息如何反映有关最近按下的键(大写字母 D)的信息。
[1] ISO-8859-1 是 Windows-1252 关于 可打印字符 的子集,因为它的 0x80-0x9f
范围被所谓的 C1 控制字符占用,而 Windows-1252 包含此范围内的可打印字符(除了代码点 0x81
、0x8d
、0x8f
、0x90
和 0x9d
,它们是未定义的)。
我有一个想法,就是编写一个可视化工具,在您键入时显示 PowerShell 行的 AST。但是要做到这一点,第一步是获取当前行的文本,在提交之前(在按下 ENTER 之前),但我找不到 API 函数或钩子来做到这一点。有吗?
我在新的 Windows 终端上使用 PowerShell Core 7.1.0。
预测源
PSReadLine 的 PredictiveSource 选项似乎可以用于此目的,前提是它可以在每个字母输入上调用,而不仅仅是在 TAB 上调用,但我找不到关于 3rd 类型合同的任何信息- 深入研究文档和 C# 代码后的派对插件...
设置-PSReadLineKeyHandler
正如传说中的@mklement0 所建议的那样,也许可以使用 Set-PSReadLineKeyHandler
。它似乎旨在用于键绑定,但我仍在思考如何将其用于此目的。
虽然没有官方机制来响应每个击键,但您可以通过为每个可打印字符和 select 几个控制字符设置一个 键处理程序来实现自己的机制,通过Set-PSReadLineKeyHandler
cmdlet。
在键处理程序中,您可以在输入行下方显示有关输入缓冲区当前状态的信息。
tl;博士:
通过修改
$metaInfo = ...
行来调整下面的代码,以确定要在其下方实时显示有关正在编辑的命令行的哪些信息.阅读下一节中的限制。
注:
它是设置密钥处理程序的前 256 个 Unicode 代码点中的可打印字符,实际上是构成 ISO-8859-1 encoding, itself a subset of Windows-1252[=78 的字符集=][1]。因此,所有 ASCII 范围的字母加上一些带重音的字母都被覆盖了,但例如西里尔字母就不会被覆盖。但是,您可以根据需要定制列表。
为了便于说明,下面的代码并没有尝试将 AST 可视化,而是以 [=26= 的格式化表示形式打印有关刚刚按下的键的信息]实例。
在下面的代码中找到以
$metaInfo =
开头的行来自定义显示的内容。获取表示缓冲区内容的AST(抽象语法树)的命令为:
$ast = $tokens = $errors = $cursor = $null [Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref] $ast, [ref] $tokens, [ref] $errors, [ref] $cursor)
该代码在常规 Windows 控制台中效果最佳 windows (
conhost.exe
); 限制:在 Windows 终端和 Unix 终端中,如果当前输入行太靠近 window 的底部边缘而无法容纳所需的行,则需要解决方法显示自定义信息:清屏,提示出现在window的第一行
- 但是,回滚缓冲区的内容已完全保留,因此如果需要,您只需向上滚动即可查看滚出视图的屏幕内容。
粘贴命令通过模拟输入,而密钥处理程序有效似乎得到
PSReadLine
模块对当前终端行是什么感到困惑,因此自定义信息的多次打印操作最终会堆叠在一起,而不是在原地相互覆盖。- 这只能在 Windows 上避免 - 在常规控制台 windows 和 Windows 终端中 - 使用 Ctrl-V 粘贴,在这种情况下,文本会真正粘贴到命令行上,但不会触发密钥处理程序(您必须随后键入另一个(虚拟)字符以根据粘贴的内容触发密钥处理程序)。
- 相比之下,执行触发所述问题的模拟输入:
- 总是在 Unix 终端中
- 在 Windows 上右键单击 粘贴
# The printable characters to respond to.
$printableChars = [char[]] (0x20..0x7e + 0xa0..0xff)
# The control characters to respond to.
$controlChars = 'Enter', 'Escape', 'Backspace', 'Delete'
# Set up the key handler for all specified characters.
$printableChars + $controlChars | ForEach-Object {
Set-PSReadLineKeyHandler $_ {
param($key, $arg)
$line = $cursor = $null
[Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref] $null, [ref] $cursor)
# Handle the key at hand.
switch ($key.Key) {
'Backspace' { [Microsoft.PowerShell.PSConsoleReadLine]::BackwardDeleteChar(); break }
'Delete' { try { [Microsoft.PowerShell.PSConsoleReadLine]::Delete($cursor, 1) } catch { }; break } # ignore error with empty buffer
'Escape' {
[Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref] $line, [ref] $null)
[Microsoft.PowerShell.PSConsoleReadLine]::Delete([=11=], $line.Length)
break
}
'Enter' {
# Clear any previous meta-information output, so that it doesn't linger and get mixed with command output.
try {
# !! On conhost.exe (regular console) windows on Windows, [Console]::CursorTop and [Console]::WindowTop are *relative to the scrollback buffer*.
# !! In Windows Terminal and on Unix, [Console]::WindowTop is always 0, and [Console]::CursorTop is relative to the screen height - even in the presence of a scrollback buffer.
Write-Host -NoNewLine (, (' ' * [Console]::WindowWidth) * ([Console]::WindowTop + [Console]::WindowHeight - [Console]::CursorTop - 1) -join "`n")
}
catch { Write-Warning "`nClearing the screen below the current line failed: $_" } # This shouldn't happen.
# !! Workaround for a display bug: If the cursor isn't at the very end of the line, everything to the
# !! right is inexplicably *erased* on submission, even though the submission itself still works fine.
# !! We detect that case and simply fill the entire buffer again, which leaves it drawn correctly on submission.
# !! (Note that [Microsoft.PowerShell.PSConsoleReadLine]::SetCursorPosition($line.Length) does *not* work.)
[Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref] $line, [ref] $cursor)
if ($cursor -ne $line.length) {
[Microsoft.PowerShell.PSConsoleReadLine]::Delete(0, $line.Length)
[Microsoft.PowerShell.PSConsoleReadLine]::Insert($line)
}
# Submit the command.
[Microsoft.PowerShell.PSConsoleReadLine]::AcceptLine()
return # We're done.
}
Default { [Microsoft.PowerShell.PSConsoleReadLine]::Insert($key.KeyChar) }
}
# Get the updated buffer content and cursor position.
[Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$line, [ref] $cursor)
# Note: To get the *AST* (too), use the following:
# $ast = $tokens = $errors = $cursor = $null
# [Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref] $ast, [ref] $tokens, [ref] $errors, [ref] $cursor)
# Determine the meta-informaton to print:
$metaInfo = $key | Out-String
if ($env:OS -ne 'Windows_NT' -or $env:WT_SESSION) {
# Workaround for all terminals except conhost.exe
# See comments at the top of the answer.
if ([Console]::CursorTop + $metaInfo.Count -gt [Console]::WindowTop + [Console]::WindowHeight) {
[Microsoft.PowerShell.PSConsoleReadLine]::ClearScreen()
}
}
# Print the desired information below the line being edited.
# Note:
# * The .PadRight() calls ensure that all lines are fully filled (padded with spaces),
# in order to erase potential remnants from previously displayed information.
# * This is NOT sufficient to deal with *varying line counts* being displayed, however.
Write-Host # blank line
Write-Host -NoNewLine -ForegroundColor Yellow ($metaInfo -split '\r?\n' | ForEach-Object PadRight ([Console]::WindowWidth-1), ' ')
# Set the new cursor position.
[Microsoft.PowerShell.PSConsoleReadLine]::SetCursorPosition($cursor)
}
}
密钥处理程序的示例屏幕截图:
请注意正在编辑的命令行下方的信息如何反映有关最近按下的键(大写字母 D)的信息。
[1] ISO-8859-1 是 Windows-1252 关于 可打印字符 的子集,因为它的 0x80-0x9f
范围被所谓的 C1 控制字符占用,而 Windows-1252 包含此范围内的可打印字符(除了代码点 0x81
、0x8d
、0x8f
、0x90
和 0x9d
,它们是未定义的)。