-
-
Save BbsonLin/ca340f2c178b68b0d1dcf066a8b36b19 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using namespace System.Management.Automation | |
using namespace System.Management.Automation.Language | |
if ($host.Name -eq 'ConsoleHost') | |
{ | |
Import-Module PSReadLine | |
} | |
Set-PSReadLineOption -PredictionSource History | |
Set-PSReadLineOption -PredictionViewStyle ListView | |
Set-PSReadLineOption -EditMode Windows | |
# 設定按下 Ctrl+d 可以退出 PowerShell 執行環境 | |
Set-PSReadlineKeyHandler -Chord ctrl+d -Function ViExit | |
# 設定按下 Ctrl+w 可以刪除一個單字 | |
Set-PSReadlineKeyHandler -Chord ctrl+w -Function BackwardDeleteWord | |
# 設定按下 Ctrl+e 可以移動游標到最後面(End) | |
Set-PSReadlineKeyHandler -Chord ctrl+e -Function EndOfLine | |
# 設定按下 Ctrl+a 可以移動游標到最前面(Begin) | |
Set-PSReadlineKeyHandler -Chord ctrl+a -Function BeginningOfLine | |
# 列出命令 Intellisense 說明 | |
Set-PSReadlineKeyHandler -Chord ctrl+\ -Function MenuComplete | |
#Set-PSReadLineKeyHandler -Key UpArrow -Function HistorySearchBackward | |
#Set-PSReadLineKeyHandler -Key DownArrow -Function HistorySearchForward | |
# This key handler shows the entire or filtered history using Out-GridView. The | |
# typed text is used as the substring pattern for filtering. A selected command | |
# is inserted to the command line without invoking. Multiple command selection | |
# is supported, e.g. selected by Ctrl + Click. | |
Set-PSReadLineKeyHandler -Key F7 ` | |
-BriefDescription History ` | |
-LongDescription 'Show command history' ` | |
-ScriptBlock { | |
$pattern = $null | |
[Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$pattern, [ref]$null) | |
if ($pattern) | |
{ | |
$pattern = [regex]::Escape($pattern) | |
} | |
$history = [System.Collections.ArrayList]@( | |
$last = '' | |
$lines = '' | |
foreach ($line in [System.IO.File]::ReadLines((Get-PSReadLineOption).HistorySavePath)) | |
{ | |
if ($line.EndsWith('`')) | |
{ | |
$line = $line.Substring(0, $line.Length - 1) | |
$lines = if ($lines) | |
{ | |
"$lines`n$line" | |
} | |
else | |
{ | |
$line | |
} | |
continue | |
} | |
if ($lines) | |
{ | |
$line = "$lines`n$line" | |
$lines = '' | |
} | |
if (($line -cne $last) -and (!$pattern -or ($line -match $pattern))) | |
{ | |
$last = $line | |
$line | |
} | |
} | |
) | |
$history.Reverse() | |
$command = $history | Out-GridView -Title History -PassThru | |
if ($command) | |
{ | |
[Microsoft.PowerShell.PSConsoleReadLine]::RevertLine() | |
[Microsoft.PowerShell.PSConsoleReadLine]::Insert(($command -join "`n")) | |
} | |
} | |
# F1 for help on the command line - naturally | |
Set-PSReadLineKeyHandler -Key F1 ` | |
-BriefDescription CommandHelp ` | |
-LongDescription "Open the help window for the current command" ` | |
-ScriptBlock { | |
param($key, $arg) | |
$ast = $null | |
$tokens = $null | |
$errors = $null | |
$cursor = $null | |
[Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$ast, [ref]$tokens, [ref]$errors, [ref]$cursor) | |
$commandAst = $ast.FindAll( { | |
$node = $args[0] | |
$node -is [CommandAst] -and | |
$node.Extent.StartOffset -le $cursor -and | |
$node.Extent.EndOffset -ge $cursor | |
}, $true) | Select-Object -Last 1 | |
if ($commandAst -ne $null) | |
{ | |
$commandName = $commandAst.GetCommandName() | |
if ($commandName -ne $null) | |
{ | |
$command = $ExecutionContext.InvokeCommand.GetCommand($commandName, 'All') | |
if ($command -is [AliasInfo]) | |
{ | |
$commandName = $command.ResolvedCommandName | |
} | |
if ($commandName -ne $null) | |
{ | |
Get-Help $commandName -ShowWindow | |
} | |
} | |
} | |
} | |
Set-PSReadLineKeyHandler -Key '"',"'" ` | |
-BriefDescription SmartInsertQuote ` | |
-LongDescription "Insert paired quotes if not already on a quote" ` | |
-ScriptBlock { | |
param($key, $arg) | |
$quote = $key.KeyChar | |
$selectionStart = $null | |
$selectionLength = $null | |
[Microsoft.PowerShell.PSConsoleReadLine]::GetSelectionState([ref]$selectionStart, [ref]$selectionLength) | |
$line = $null | |
$cursor = $null | |
[Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$line, [ref]$cursor) | |
# If text is selected, just quote it without any smarts | |
if ($selectionStart -ne -1) | |
{ | |
[Microsoft.PowerShell.PSConsoleReadLine]::Replace($selectionStart, $selectionLength, $quote + $line.SubString($selectionStart, $selectionLength) + $quote) | |
[Microsoft.PowerShell.PSConsoleReadLine]::SetCursorPosition($selectionStart + $selectionLength + 2) | |
return | |
} | |
$ast = $null | |
$tokens = $null | |
$parseErrors = $null | |
[Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$ast, [ref]$tokens, [ref]$parseErrors, [ref]$null) | |
function FindToken | |
{ | |
param($tokens, $cursor) | |
foreach ($token in $tokens) | |
{ | |
if ($cursor -lt $token.Extent.StartOffset) { continue } | |
if ($cursor -lt $token.Extent.EndOffset) { | |
$result = $token | |
$token = $token -as [StringExpandableToken] | |
if ($token) { | |
$nested = FindToken $token.NestedTokens $cursor | |
if ($nested) { $result = $nested } | |
} | |
return $result | |
} | |
} | |
return $null | |
} | |
$token = FindToken $tokens $cursor | |
# If we're on or inside a **quoted** string token (so not generic), we need to be smarter | |
if ($token -is [StringToken] -and $token.Kind -ne [TokenKind]::Generic) { | |
# If we're at the start of the string, assume we're inserting a new string | |
if ($token.Extent.StartOffset -eq $cursor) { | |
[Microsoft.PowerShell.PSConsoleReadLine]::Insert("$quote$quote ") | |
[Microsoft.PowerShell.PSConsoleReadLine]::SetCursorPosition($cursor + 1) | |
return | |
} | |
# If we're at the end of the string, move over the closing quote if present. | |
if ($token.Extent.EndOffset -eq ($cursor + 1) -and $line[$cursor] -eq $quote) { | |
[Microsoft.PowerShell.PSConsoleReadLine]::SetCursorPosition($cursor + 1) | |
return | |
} | |
} | |
if ($null -eq $token -or | |
$token.Kind -eq [TokenKind]::RParen -or $token.Kind -eq [TokenKind]::RCurly -or $token.Kind -eq [TokenKind]::RBracket) { | |
if ($line[0..$cursor].Where{$_ -eq $quote}.Count % 2 -eq 1) { | |
# Odd number of quotes before the cursor, insert a single quote | |
[Microsoft.PowerShell.PSConsoleReadLine]::Insert($quote) | |
} | |
else { | |
# Insert matching quotes, move cursor to be in between the quotes | |
[Microsoft.PowerShell.PSConsoleReadLine]::Insert("$quote$quote") | |
[Microsoft.PowerShell.PSConsoleReadLine]::SetCursorPosition($cursor + 1) | |
} | |
return | |
} | |
# If cursor is at the start of a token, enclose it in quotes. | |
if ($token.Extent.StartOffset -eq $cursor) { | |
if ($token.Kind -eq [TokenKind]::Generic -or $token.Kind -eq [TokenKind]::Identifier -or | |
$token.Kind -eq [TokenKind]::Variable -or $token.TokenFlags.hasFlag([TokenFlags]::Keyword)) { | |
$end = $token.Extent.EndOffset | |
$len = $end - $cursor | |
[Microsoft.PowerShell.PSConsoleReadLine]::Replace($cursor, $len, $quote + $line.SubString($cursor, $len) + $quote) | |
[Microsoft.PowerShell.PSConsoleReadLine]::SetCursorPosition($end + 2) | |
return | |
} | |
} | |
# We failed to be smart, so just insert a single quote | |
[Microsoft.PowerShell.PSConsoleReadLine]::Insert($quote) | |
} | |
Set-PSReadLineKeyHandler -Key '(','{','[' ` | |
-BriefDescription InsertPairedBraces ` | |
-LongDescription "Insert matching braces" ` | |
-ScriptBlock { | |
param($key, $arg) | |
$closeChar = switch ($key.KeyChar) | |
{ | |
<#case#> '(' { [char]')'; break } | |
<#case#> '{' { [char]'}'; break } | |
<#case#> '[' { [char]']'; break } | |
} | |
$selectionStart = $null | |
$selectionLength = $null | |
[Microsoft.PowerShell.PSConsoleReadLine]::GetSelectionState([ref]$selectionStart, [ref]$selectionLength) | |
$line = $null | |
$cursor = $null | |
[Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$line, [ref]$cursor) | |
if ($selectionStart -ne -1) | |
{ | |
# Text is selected, wrap it in brackets | |
[Microsoft.PowerShell.PSConsoleReadLine]::Replace($selectionStart, $selectionLength, $key.KeyChar + $line.SubString($selectionStart, $selectionLength) + $closeChar) | |
[Microsoft.PowerShell.PSConsoleReadLine]::SetCursorPosition($selectionStart + $selectionLength + 2) | |
} else { | |
# No text is selected, insert a pair | |
[Microsoft.PowerShell.PSConsoleReadLine]::Insert("$($key.KeyChar)$closeChar") | |
[Microsoft.PowerShell.PSConsoleReadLine]::SetCursorPosition($cursor + 1) | |
} | |
} | |
Set-PSReadLineKeyHandler -Key ')',']','}' ` | |
-BriefDescription SmartCloseBraces ` | |
-LongDescription "Insert closing brace or skip" ` | |
-ScriptBlock { | |
param($key, $arg) | |
$line = $null | |
$cursor = $null | |
[Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$line, [ref]$cursor) | |
if ($line[$cursor] -eq $key.KeyChar) | |
{ | |
[Microsoft.PowerShell.PSConsoleReadLine]::SetCursorPosition($cursor + 1) | |
} | |
else | |
{ | |
[Microsoft.PowerShell.PSConsoleReadLine]::Insert("$($key.KeyChar)") | |
} | |
} | |
Set-PSReadLineKeyHandler -Key Backspace ` | |
-BriefDescription SmartBackspace ` | |
-LongDescription "Delete previous character or matching quotes/parens/braces" ` | |
-ScriptBlock { | |
param($key, $arg) | |
$line = $null | |
$cursor = $null | |
[Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$line, [ref]$cursor) | |
if ($cursor -gt 0) | |
{ | |
$toMatch = $null | |
if ($cursor -lt $line.Length) | |
{ | |
switch ($line[$cursor]) | |
{ | |
<#case#> '"' { $toMatch = '"'; break } | |
<#case#> "'" { $toMatch = "'"; break } | |
<#case#> ')' { $toMatch = '('; break } | |
<#case#> ']' { $toMatch = '['; break } | |
<#case#> '}' { $toMatch = '{'; break } | |
} | |
} | |
if ($toMatch -ne $null -and $line[$cursor-1] -eq $toMatch) | |
{ | |
[Microsoft.PowerShell.PSConsoleReadLine]::Delete($cursor - 1, 2) | |
} | |
else | |
{ | |
[Microsoft.PowerShell.PSConsoleReadLine]::BackwardDeleteChar($key, $arg) | |
} | |
} | |
} | |
###################################################################################################################### | |
Clear-Host | |
###################################################################################################################### | |
Import-Module oh-my-posh | |
Set-PoshPrompt -Theme ~/bbsonlin.omp.json | |
###################################################################################################################### | |
Import-Module -Name Terminal-Icons | |
###################################################################################################################### | |
# winget parameter completion | |
Register-ArgumentCompleter -Native -CommandName winget -ScriptBlock { | |
param($wordToComplete, $commandAst, $cursorPosition) | |
[Console]::InputEncoding = [Console]::OutputEncoding = $OutputEncoding = [System.Text.Utf8Encoding]::new() | |
$Local:word = $wordToComplete.Replace('"', '""') | |
$Local:ast = $commandAst.ToString().Replace('"', '""') | |
winget complete --word="$Local:word" --commandline "$Local:ast" --position $cursorPosition | ForEach-Object { | |
[System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) | |
} | |
} | |
# PowerShell parameter completion shim for the dotnet CLI | |
Register-ArgumentCompleter -Native -CommandName dotnet -ScriptBlock { | |
param($commandName, $wordToComplete, $cursorPosition) | |
dotnet complete --position $cursorPosition "$wordToComplete" | ForEach-Object { | |
[System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) | |
} | |
} | |
###################################################################################################################### | |
function New-RandomPassword { | |
param( | |
[Parameter()] | |
[int]$MinimumPasswordLength = 8, | |
[Parameter()] | |
[int]$MaximumPasswordLength = 12, | |
[Parameter()] | |
[switch]$ConvertToSecureString | |
) | |
$length = Get-Random -Minimum $MinimumPasswordLength -Maximum $MaximumPasswordLength | |
$password = ("!@#$%^&*0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz".ToCharArray() | sort {Get-Random})[0..$length] -join '' | |
if ($ConvertToSecureString.IsPresent) { | |
ConvertTo-SecureString -String $password -AsPlainText -Force | |
} else { | |
$password | |
} | |
} | |
function GeneratePassword { | |
param( | |
[Parameter()] | |
[int]$MinimumPasswordLength = 8, | |
[Parameter()] | |
[int]$MaximumPasswordLength = 12, | |
[Parameter()] | |
[int]$NumberOfAlphaNumericCharacters = 5, | |
[Parameter()] | |
[switch]$ConvertToSecureString | |
) | |
Add-Type -AssemblyName 'System.Web' | |
$length = Get-Random -Minimum $MinimumPasswordLength -Maximum $MaximumPasswordLength | |
$password = [System.Web.Security.Membership]::GeneratePassword($length,$NumberOfAlphaNumericCharacters) | |
if ($ConvertToSecureString.IsPresent) { | |
ConvertTo-SecureString -String $password -AsPlainText -Force | |
} else { | |
$password | |
} | |
} | |
function New-Password { | |
<# | |
.SYNOPSIS | |
Generate a random password. | |
.DESCRIPTION | |
Generate a random password. | |
.NOTES | |
Change log: | |
27/11/2017 - faustonascimento - Swapped Get-Random for System.Random. | |
Swapped Sort-Object for Fisher-Yates shuffle. | |
17/03/2017 - Chris Dent - Created. | |
#> | |
[CmdletBinding()] | |
[OutputType([String])] | |
param ( | |
# The length of the password which should be created. | |
[Parameter(ValueFromPipeline)] | |
[ValidateRange(8, 255)] | |
[Int32]$Length = 10, | |
# The character sets the password may contain. A password will contain at least one of each of the characters. | |
[String[]]$CharacterSet = ('abcdefghijklmnopqrstuvwxyz', | |
'ABCDEFGHIJKLMNOPQRSTUVWXYZ', | |
'0123456789', | |
'!$%&^.#;'), | |
# The number of characters to select from each character set. | |
[Int32[]]$CharacterSetCount = (@(1) * $CharacterSet.Count), | |
[Parameter()] | |
[switch]$ConvertToSecureString | |
) | |
begin { | |
$bytes = [Byte[]]::new(4) | |
$rng = [System.Security.Cryptography.RandomNumberGenerator]::Create() | |
$rng.GetBytes($bytes) | |
$seed = [System.BitConverter]::ToInt32($bytes, 0) | |
$rnd = [Random]::new($seed) | |
if ($CharacterSet.Count -ne $CharacterSetCount.Count) { | |
throw "The number of items in -CharacterSet needs to match the number of items in -CharacterSetCount" | |
} | |
$allCharacterSets = [String]::Concat($CharacterSet) | |
} | |
process { | |
try { | |
$requiredCharLength = 0 | |
foreach ($i in $CharacterSetCount) { | |
$requiredCharLength += $i | |
} | |
if ($requiredCharLength -gt $Length) { | |
throw "The sum of characters specified by CharacterSetCount is higher than the desired password length" | |
} | |
$password = [Char[]]::new($Length) | |
$index = 0 | |
for ($i = 0; $i -lt $CharacterSet.Count; $i++) { | |
for ($j = 0; $j -lt $CharacterSetCount[$i]; $j++) { | |
$password[$index++] = $CharacterSet[$i][$rnd.Next($CharacterSet[$i].Length)] | |
} | |
} | |
for ($i = $index; $i -lt $Length; $i++) { | |
$password[$index++] = $allCharacterSets[$rnd.Next($allCharacterSets.Length)] | |
} | |
# Fisher-Yates shuffle | |
for ($i = $Length; $i -gt 0; $i--) { | |
$n = $i - 1 | |
$m = $rnd.Next($i) | |
$j = $password[$m] | |
$password[$m] = $password[$n] | |
$password[$n] = $j | |
} | |
$password = [String]::new($password) | |
if ($ConvertToSecureString.IsPresent) { | |
ConvertTo-SecureString -String $password -AsPlainText -Force | |
} else { | |
$password | |
} | |
} catch { | |
Write-Error -ErrorRecord $_ | |
} | |
} | |
} | |
###################################################################################################################### | |
# 快速開啟 c:\windows\system32\drivers\etc\hosts 檔案 | |
function hosts { notepad c:\windows\system32\drivers\etc\hosts } | |
If (Test-Path Alias:curl) {Remove-Item Alias:curl} | |
If (Test-Path Alias:wget) {Remove-Item Alias:wget} | |
# Alias | |
# Workspace | |
function wksp { cd D:\Code\ } | |
# Python PDM | |
function pr { pdm run @Args } | |
# NVim | |
function nvim-config { cd ~\AppData\Local\nvim\ } | |
function lvim { C:\Users\bobson.lin\.local\bin\lvim.ps1 } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Ref
https://fb.watch/ctbP-M2QfY/
https://blog.miniasp.com/post/2021/11/24/PowerShell-prompt-with-Oh-My-Posh-and-Windows-Terminal
https://blog.miniasp.com/post/2019/12/11/Windows-PowerShell-PSReadline
https://docs.microsoft.com/zh-tw/powershell/scripting/windows-powershell/ise/keyboard-shortcuts-for-the-windows-powershell-ise?view=powershell-7.2