diff --git a/.github/workflows/CI.ps1 b/.github/workflows/CI.ps1 index ec0dd3c..17736a8 100644 --- a/.github/workflows/CI.ps1 +++ b/.github/workflows/CI.ps1 @@ -1,10 +1,14 @@ -. $PSScriptRoot/../../src/main.ps1 - try{ + . $PSScriptRoot/../../opt/install.ps1 -Force -StartEsh no + + . $PSScriptRoot/../../src/main.ps1 $EshellUI.Init($MyInvocation) $EshellUI.Start() -} -catch{ + + . $PSScriptRoot/../../opt/uninstall.ps1 -Force -RemoveDir no +}catch{} + +if($error){ $error | ForEach-Object { Write-Output "::error file=$($_.InvocationInfo.ScriptName),line=$($_.InvocationInfo.ScriptLineNumber),col=$($_.InvocationInfo.OffsetInLine),endColumn=$($_.InvocationInfo.OffsetInLine),tittle=error::script error" Write-Output "::group::script stack trace" diff --git a/README.md b/README.md index 1e65a70..c7c5756 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,10 @@ ## 快速开始 -运行`opt/install`以快速开始(你甚至不需要clone这个项目): +运行[`esh.exe`](https://github.com/steve02081504/esh/releases/latest/download/esh.exe)以在不进行安装的情况下开始 +对于单文件exe 你可以通过`esh -Command $EshellUI.RunInstall()`来安装esh到系统环境中 + +或者运行`opt/install`以快速开始(你甚至不需要clone这个项目): ```powershell & { (Invoke-WebRequest https://bit.ly/EshInstall).Content | Invoke-Expression } @@ -116,6 +119,7 @@ rm -rf superhavyrock │ │ └───special #特殊命令脚本 │ ├───opt #安装 卸载 启动脚本 基础文件 │ └───scripts #收录脚本工具 + ├───runner #exe文件编译文件夹 包含源码和构建脚本 ├───img #图片资源 ├───opt #安装 卸载 启动脚本 └───path #用于加入环境变量的文件夹 diff --git a/data/git_icon.ini b/data/git_icon.ini new file mode 100644 index 0000000..724ec48 --- /dev/null +++ b/data/git_icon.ini @@ -0,0 +1,2 @@ +[.ShellClassInfo] +IconResource=..\img\git.ico,0 diff --git a/desktop.ini b/desktop.ini index b36bcfc..0f83c62 100644 --- a/desktop.ini +++ b/desktop.ini @@ -1,2 +1,2 @@ [.ShellClassInfo] -IconResource=C:\WINDOWS\System32\SHELL32.dll,267 +IconResource=.\img\esh.ico,0 diff --git a/img/cmd.ico b/img/cmd.ico deleted file mode 100644 index f0394e5..0000000 Binary files a/img/cmd.ico and /dev/null differ diff --git a/img/esh.ico b/img/esh.ico new file mode 100644 index 0000000..83c0dd3 Binary files /dev/null and b/img/esh.ico differ diff --git a/img/git.ico b/img/git.ico new file mode 100644 index 0000000..65b9b54 Binary files /dev/null and b/img/git.ico differ diff --git a/opt/install.ps1 b/opt/install.ps1 index 185773d..e3cd1db 100644 --- a/opt/install.ps1 +++ b/opt/install.ps1 @@ -1,5 +1,9 @@ #!/usr/bin/env pwsh -[CmdletBinding()]param([switch]$FromScript=$false) +[CmdletBinding()]param( + [switch]$Force=$false, + [ValidateSet('yes', 'no', 'ask', 'auto')][string]$StartEsh='auto', + [parameter(DontShow)][switch]$FromScript=$false +) function illusionlimb($path) { Invoke-Expression $(if (Test-Path $PSScriptRoot/../path/esh) { Get-Content "$PSScriptRoot/$path" -Raw } @@ -31,15 +35,4 @@ else { if ($EshellUI) { Write-Host "(并且你正在使用它 :))" } } -. $eshDir/src/opt/install.ps1 - -if (-not (Get-Command pwsh -ErrorAction Ignore)) { - $Host.UI.WriteErrorLine("esh的运行需要PowerShell 6或以上`n访问 https://aka.ms/pscore6 来获取PowerShell 6+ 并使得``pwsh``命令在环境中可用以使得esh能够正常工作") -} -elseif ((-not $EshellUI) -and (YorN "要使用 Eshell 吗?")) { - if ($FromScript -or ($PSVersionTable.PSVersion.Major -lt 6)) { - . $eshDir/opt/run - [System.Environment]::Exit($LastExitCode) - } - else { . $eshDir/opt/run.ps1 -Invocation $MyInvocation } -} +. $eshDir/src/opt/install.ps1 -Force:$Force -StartEsh:$StartEsh -FromScript:$FromScript diff --git a/opt/run b/opt/run index ce191b2..ad7be36 100644 --- a/opt/run +++ b/opt/run @@ -9,7 +9,16 @@ while [[ $# -gt 0 ]]; do command="$2" shift 2 else - echo "Error: Missing argument after -Command" + echo "Error: Missing argument after -Command" >&2 + exit 1 + fi + ;; + -File) + if [[ $# -gt 1 ]]; then + File="$2" + shift 2 + else + echo "Error: Missing argument after -File" >&2 exit 1 fi ;; @@ -29,9 +38,19 @@ if [[ -d "/c/Windows" ]]; then fi if [[ -z "$command" ]]; then - pwsh $parsed_args -nologo -NoExit -File "$SCRIPT_DIR/run.ps1" + if [[ -z "$File" ]]; then + pwsh $parsed_args -nologo -NoExit -File "$SCRIPT_DIR/run.ps1" + else + File=$(echo "$File" | sed -e 's/"/`"/g') + pwsh $parsed_args -nologo -NoExit -Command ". $SCRIPT_DIR/run.ps1 ; . $File" + fi else command=$(echo "$command" | sed -e 's/"/`"/g') - pwsh $parsed_args -nologo -Command ". $SCRIPT_DIR/run.ps1 ; Invoke-Expression $command" + if [[ -z "$File" ]]; then + pwsh $parsed_args -nologo -Command ". $SCRIPT_DIR/run.ps1 ; Invoke-Expression $command" + else + File=$(echo "$File" | sed -e 's/"/`"/g') + pwsh $parsed_args -nologo -Command ". $SCRIPT_DIR/run.ps1 ; . $File ; Invoke-Expression $command" + fi fi exit $? diff --git a/opt/run.cmd b/opt/run.cmd index 86281ef..921c106 100644 --- a/opt/run.cmd +++ b/opt/run.cmd @@ -9,19 +9,42 @@ for %%i in (%*) do ( set "command=%%i" set "hasCommand=" ) else ( - if "%%i"=="-Command" ( - set "hasCommand=true" - ) else ( + if defined hasFile ( + set "hasFile=" set "remainingArgs=!remainingArgs! %%i" + ) else ( + if "%%i"=="-Command" ( + set "hasCommand=true" + ) else ( + if "%%i"=="-File" ( + set "hasFile=true" + ) else ( + set "remainingArgs=!remainingArgs! %%i" + ) + ) ) ) ) if defined command ( set "command=!command:"=""!" - pwsh !remainingArgs! -nologo -Command ". %~dp0\run.ps1; Invoke-Expression !command!" +) +if defined File ( + set "File=!File:"=""!" +) + +if defined command ( + if defined File ( + pwsh !remainingArgs! -nologo -Command ". %~dp0\run.ps1; . !File!; Invoke-Expression !command!" + ) else ( + pwsh !remainingArgs! -nologo -Command ". %~dp0\run.ps1; Invoke-Expression !command!" + ) ) else ( - pwsh !remainingArgs! -nologo -NoExit -File "%~dp0\run.ps1" + if defined File ( + pwsh !remainingArgs! -nologo -Command ". %~dp0\run.ps1; . !File!" + ) else ( + pwsh !remainingArgs! -nologo -NoExit -File "%~dp0\run.ps1" + ) ) @echo on diff --git a/opt/uninstall.ps1 b/opt/uninstall.ps1 index 8858050..c52e597 100644 --- a/opt/uninstall.ps1 +++ b/opt/uninstall.ps1 @@ -1,5 +1,8 @@ #!/usr/bin/env pwsh - +[CmdletBinding()]param( + [switch]$Force=$false, + [ValidateSet('no', 'yes', 'ask', 'auto')][string]$RemoveDir='auto' +) function illusionlimb($path) { Invoke-Expression $(if (Test-Path $PSScriptRoot/../path/esh) { Get-Content "$PSScriptRoot/$path" -Raw } else { (Invoke-WebRequest "https://github.com/steve02081504/esh/raw/master/opt/$path").Content }) @@ -17,4 +20,4 @@ else { if ($EshellUI) { Write-Host "(并且你正在使用它 :()" } } -. $eshDir/src/opt/uninstall.ps1 +. $eshDir/src/opt/uninstall.ps1 -Force:$Force -RemoveDir:$RemoveDir diff --git a/runner/.gitignore b/runner/.gitignore new file mode 100644 index 0000000..b0ad2a1 --- /dev/null +++ b/runner/.gitignore @@ -0,0 +1 @@ +/esh.exe diff --git a/runner/build.ps1 b/runner/build.ps1 new file mode 100644 index 0000000..91defea --- /dev/null +++ b/runner/build.ps1 @@ -0,0 +1,6 @@ +[CmdletBinding()]param ($OutputFile = "$PSScriptRoot/esh.exe") + +if (-not (Get-Module -ListAvailable ps2exe)) { + Install-Module ps2exe +} +Invoke-ps2exe $PSScriptRoot/main.ps1 $OutputFile -NoConsole -iconFile $PSScriptRoot/../img/esh.ico -title 'E-Shell' -description 'E-Shell' -version '1960.7.17.13' -company 'E-tek' -product 'E-Sh' -copyright '(c) E-tek Corporation. All rights reserved.' diff --git a/runner/desktop.ini b/runner/desktop.ini new file mode 100644 index 0000000..5787752 --- /dev/null +++ b/runner/desktop.ini @@ -0,0 +1,2 @@ +[.ShellClassInfo] +IconResource=C:\WINDOWS\System32\SHELL32.dll,214 diff --git a/runner/main.ps1 b/runner/main.ps1 new file mode 100644 index 0000000..e279c94 --- /dev/null +++ b/runner/main.ps1 @@ -0,0 +1,66 @@ +param( + [switch]$RunInstall=$false, + [Parameter(ValueFromRemainingArguments = $true)] + $RemainingArguments +) +if((Get-ExecutionPolicy) -eq 'Restricted'){ Set-ExecutionPolicy RemoteSigned -Scope CurrentUser -Force } +$env:Path.Split(";") | ForEach-Object { + if ($_ -and (-not (Test-Path $_ -PathType Container))) { + Write-Warning "检测到无效的环境变量于$_,请考虑删除" + } + elseif ($_ -like "*[\\/]esh[\\/]path*") { + $eshDir = $_ -replace "[\\/]path[\\/]*$", '' + $eshDirFromEnv = $true + } +} +if (-not $eshDir) { + $eshDir = if (Test-Path $env:LOCALAPPDATA/esh) { "$env:LOCALAPPDATA/esh" } +} + +if (-not $eshDir) { + Remove-Item $env:LOCALAPPDATA/esh -Confirm -ErrorAction Ignore -Recurse + Remove-Item $env:TEMP/esh-master -Force -ErrorAction Ignore -Confirm:$false -Recurse + try { Invoke-WebRequest https://bit.ly/Esh-zip -OutFile $env:TEMP/Eshell.zip } + catch { + $Host.UI.WriteErrorLine("下载错误 终止程序") + exit 1 + } + Expand-Archive $env:TEMP/Eshell.zip $env:TEMP -Force + Remove-Item $env:TEMP/Eshell.zip -Force + Move-Item $env:TEMP/esh-master $env:LOCALAPPDATA/esh -Force + $eshDir = "$env:LOCALAPPDATA/esh" + try { Invoke-WebRequest 'https://bit.ly/SAO-lib' -OutFile "$eshDir/data/SAO-lib.txt" } + catch { + Write-Host "啊哦 SAO-lib下载失败了`n这不会影响什么,不过你可以在Esh启动后使用``Update-SAO-lib``来让Esh有机会显示更多骚话" + } +} + +if ($RunInstall){ + . $eshDir/src/opt/install.ps1 $RemainingArguments + exit +} +if (-not (Get-Command pwsh -ErrorAction Ignore)) { + $Host.UI.WriteErrorLine("esh的运行需要PowerShell 6或以上`n访问 https://aka.ms/pscore6 来获取PowerShell 6+ 并使得``pwsh``命令在环境中可用以使得esh能够正常工作") + do { + $response = $Host.UI.PromptForChoice("未找到可用的pwsh", "尝试自动安装PowerShell吗?", @("自动安装","带我到下载页面","退出"), 0) + } until ($response -ne -1) + switch ($response) { + 0 { + if (-not (Get-Command winget -ErrorAction Ignore)) { + Import-Module Appx + Add-AppxPackage -RegisterByFamilyName -MainPackage Microsoft.DesktopAppInstaller_8wekyb3d8bbwe + } + Invoke-Expression "winget install --id Microsoft.Powershell $( + if([System.Environment]::OSVersion.Version.Major -le 7){'-v 7.2.15'} + )" + } + 1 { Start-Process https://aka.ms/pscore6 } + 2 { exit 1 } + } +} +if (Get-Command pwsh -ErrorAction Ignore) { + if (Get-Command wt -ErrorAction Ignore) { + wt $eshDir/path/esh.cmd $RemainingArguments + } + else{ & $eshDir/path/esh $RemainingArguments } +} diff --git a/src/commands/copilot.ps1 b/src/commands/copilot.ps1 index d23e970..50781b7 100644 --- a/src/commands/copilot.ps1 +++ b/src/commands/copilot.ps1 @@ -2,9 +2,7 @@ function global:Install-Copilot { if (-not (Test-Command gh)) { #github cli not found if (Test-Command winget) { - try { - winget install GitHub.cli - } + try { winget install GitHub.cli } catch { Out-Error 'Install github cli failed.' throw diff --git a/src/commands/special/linux_bins.ps1 b/src/commands/special/linux_bins.ps1 index 07f2ea7..e3364fe 100644 --- a/src/commands/special/linux_bins.ps1 +++ b/src/commands/special/linux_bins.ps1 @@ -38,10 +38,8 @@ function global:cd { #所以我们只能通过Set-Location来模拟cd的行为 $IsLinuxBin = $Path.Length -eq 0 function baseCD ($Path, [switch]$IsFollowSymbolicLink = $true) { - if ($Path.Length -eq 0) { - Set-Location ~ - } - elseif (-not $IsFollowSymbolicLink) { + $Path ??= '~' + if (-not $IsFollowSymbolicLink) { #循环分割路径,检查每一级路径是否是符号链接 $PreviousPath = "" while ($Path) { diff --git a/src/commands/update.ps1 b/src/commands/update.ps1 index 9133c63..a5a5048 100644 --- a/src/commands/update.ps1 +++ b/src/commands/update.ps1 @@ -31,7 +31,7 @@ function global:Update-EShell { if (Test-Path "$espath/.git/config") { $pathNow = $PWD Set-Location $espath - git pull + git pull --rebase Set-Location $pathNow reload return @@ -41,8 +41,8 @@ function global:Update-EShell { try { #下载最新的EShell Invoke-WebRequest 'https://github.com/steve02081504/esh/archive/refs/heads/master.zip' -OutFile "$datapath/master.zip" - #删除旧的src以确保干净 - Remove-Item "$espath/src" -Recurse -Force + #删除除了data和desktop.ini文件夹以外的所有文件 + Get-ChildItem "$espath" -Force | Where-Object { $_.Name -notin @('data', 'desktop.ini') } | Remove-Item -Recurse -Force #更新文件 Rename-Item "$espath" "$praentpath/esh-master" Expand-Archive "$praentpath/esh-master/data/master.zip" "$praentpath" -Force diff --git a/src/fixers/CodePageFixer.ps1 b/src/fixers/CodePageFixer.ps1 index a646ad4..9e16e40 100644 --- a/src/fixers/CodePageFixer.ps1 +++ b/src/fixers/CodePageFixer.ps1 @@ -1,17 +1,12 @@ -# 假如在win8以下的系统上运行,那么我们需要检查和修复输出编码 -if ($IsWindows -and ([System.Environment]::OSVersion.Version.Major -le 7)) { +if ($IsWindows -and ([Console]::OutputEncoding -ne [System.Text.Encoding]::UTF8)) { $CursorPosBackUp = $host.UI.RawUI.CursorPosition - $CodingBackUp = [Console]::OutputEncoding $TestText = '中文测试你好小笼包我是冰激凌' - function TestAndSet ($Encoding) { - try { Write-Host $TestText } - catch { $error.RemoveAt(0); [Console]::OutputEncoding = $Encoding } - $host.UI.RawUI.CursorPosition = $CursorPosBackUp + foreach ($Encoding in [System.Text.Encoding]::GetEncodings()) { + try { Write-Host $TestText;break } + catch { $error.RemoveAt(0);[Console]::OutputEncoding = $Encoding } + try{ $host.UI.RawUI.CursorPosition = $CursorPosBackUp } catch { $Error.RemoveAt(0) } } - TestAndSet ([System.Text.Encoding]::GetEncoding(936)) - TestAndSet $CodingBackUp Write-Host $(' ' * $TestText.Length * 2) - $host.UI.RawUI.CursorPosition = $CursorPosBackUp - Remove-Variable @('CursorPosBackUp', 'CodingBackUp', 'TestText') - Remove-Item function:TestAndSet + try{ $host.UI.RawUI.CursorPosition = $CursorPosBackUp } catch { $Error.RemoveAt(0) } + Remove-Variable @('CursorPosBackUp', 'TestText') } diff --git a/src/fixers/VSCodeBackgroundJobsDisAbler.ps1 b/src/fixers/VSCodeBackgroundJobsDisAbler.ps1 index 4ff6533..ecfc0f0 100644 --- a/src/fixers/VSCodeBackgroundJobsDisAbler.ps1 +++ b/src/fixers/VSCodeBackgroundJobsDisAbler.ps1 @@ -1,8 +1,8 @@ if ($EshellUI.Im.VSCodeExtension -and ($host.Version -le [version]'2023.8.0')) { - $EshellUI.BackgroundJobs.Add({ - Unregister-Event -SubscriptionId $EshellUI.OtherData.IdleEvent.SubscriptionId -Force - $EshellUI.OtherData.Remove('IdleEvent') - }) | Out-Null + $EshellUI.BackgroundJobs | ForEach-Object { & $_ } + $EshellUI.BackgroundJobs.Clear() + Unregister-Event -SubscriptionId $EshellUI.RegisteredEvents.IdleEvent.RawData.SubscriptionId -Force + $EshellUI.RegisteredEvents.Remove('IdleEvent') $EshellUI.LoadingLog.AddWarning( "EshellUI's BackgroundJobs has been disabled due to a bug of PowerShell VSCode Extension. See $( diff --git a/src/main.ps1 b/src/main.ps1 index 3484ea7..875c3b2 100644 --- a/src/main.ps1 +++ b/src/main.ps1 @@ -10,7 +10,7 @@ $EshellUI = ValueEx @{ Path = Split-Path $PSScriptRoot } Im = ValueEx @{ - Sudo = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]“Administrator”) + Sudo = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]'Administrator') VSCodeExtension = [bool]($psEditor) -and ($Host.Name -eq 'Visual Studio Code Host') Editor = [bool]($psEditor) WindowsPowerShell = (Split-Path $(Split-Path $PROFILE) -Leaf) -eq 'WindowsPowerShell' @@ -144,20 +144,25 @@ $EshellUI = ValueEx @{ TabHandler = (Get-PSReadLineKeyHandler Tab).Function EnterHandler = (Get-PSReadLineKeyHandler Enter).Function } - #注册事件以在退出时保存数据 - Register-EngineEvent PowerShell.Exiting -SupportEvent -Action { - $EshellUI.SaveVariables() - } - #注册事件以在空闲时执行后台任务 - Register-EngineEvent PowerShell.OnIdle -SupportEvent -Action { - if ($EshellUI.BackgroundJobs.Count) { - $EshellUI.BackgroundJobs.PopAndRun() + $this.RegisteredEvents=@{ + ExitingEvent = @{ + ID = 'PowerShell.Exiting' + Action = {$EshellUI.SaveVariables()} + } + IdleEvent = @{ + ID = 'PowerShell.OnIdle' + Action = { + if ($EshellUI.BackgroundJobs.Count) { + $EshellUI.BackgroundJobs.PopAndRun() + } + } } } - $EventList = Get-EventSubscriber -Force - $this.OtherData.ExitingEvent = $EventList[$EventList.Count-2] - $this.OtherData.IdleEvent = $EventList[$EventList.Count-1] - Remove-Variable EventList + $this.RegisteredEvents.GetEnumerator() | ForEach-Object { + $_ = $_.Value + Register-EngineEvent $_.ID -SupportEvent -Action $_.Action + $_.RawData = (Get-EventSubscriber -Force)[-1] + } . $PSScriptRoot/system/base.ps1 @@ -165,6 +170,7 @@ $EshellUI = ValueEx @{ . $PSScriptRoot/system/UI/title.ps1 . $PSScriptRoot/system/UI/icon.ps1 + Set-PSReadLineKeyHandler -Key Tab -Function MenuComplete . $PSScriptRoot/system/linux.ps1 . $PSScriptRoot/system/UI/prompt/main.ps1 @@ -187,6 +193,7 @@ $EshellUI = ValueEx @{ $this.SaveVariables() $this.Remove() . "$($this.Sources.Path)/src/main.ps1" + $EshellUI.Init($this.Invocation) $EshellUI.LoadVariables() $EshellUI.Start() } @@ -222,8 +229,9 @@ $EshellUI = ValueEx @{ } $this.SaveVariables() $function:prompt = $this.OtherData.BeforeEshLoaded.promptBackup - Unregister-Event -SubscriptionId $($this.OtherData.ExitingEvent.SubscriptionId ?? 0) -Force - Unregister-Event -SubscriptionId $($this.OtherData.IdleEvent.SubscriptionId ?? 0) -Force + $this.RegisteredEvents.GetEnumerator() | Where-Object { $_.Value.RawData } | ForEach-Object { + Unregister-Event -SubscriptionId $_.Value.RawData.SubscriptionId -Force + } $this.ProvidedFunctions() | ForEach-Object { Remove-Item function:\$($_.Name) } $this.ProvidedVariables() | ForEach-Object { Remove-Item variable:\$($_.Name) } $this.ProvidedAliases() | ForEach-Object { Remove-Item alias:\$($_.Name) } @@ -290,4 +298,21 @@ $EshellUI = ValueEx @{ throw $_ } } + 'method:CompileExeFile' = { + param($OutputFile) + if(IsLinuxPath $OutputFile){ + $OutputFile = LinuxPathToWindowsPath $OutputFile + } + if (Test-Path $OutputFile -PathType Container) { + $OutputFile = Join-Path $OutputFile 'esh.exe' + } + if (Test-Path $OutputFile) { + try{ Remove-Item $OutputFile -Force -ErrorAction Stop } + catch{ + Write-Error "Failed to remove $OutputFile" + return + } + } + . "$($this.Sources.Path)/runner/build.ps1" -OutputFile $OutputFile | Out-Null + } } diff --git a/src/opt/base.ps1 b/src/opt/base.ps1 index 2b3d328..4a5cfd9 100644 --- a/src/opt/base.ps1 +++ b/src/opt/base.ps1 @@ -1,5 +1,6 @@ using namespace System.Management.Automation.Host -function YorN($message, $helpMessageY = "", $helpMessageN = "", [switch]$defaultN = $false) { +function YorN($message, $helpMessageY = "", $helpMessageN = "", [switch]$defaultN = $false, [switch]$SkipAsDefault=$false) { + if($SkipAsDefault) { return -not $defaultN } do { $response = $Host.UI.PromptForChoice("", $message, @( [ChoiceDescription]::new('&Yes', $helpMessageY), [ChoiceDescription]::new('&No', $helpMessageN) diff --git a/src/opt/install.ps1 b/src/opt/install.ps1 index b4f4391..d434f85 100644 --- a/src/opt/install.ps1 +++ b/src/opt/install.ps1 @@ -1,27 +1,51 @@ using namespace System.Management.Automation.Host +param( + [switch]$Force=$false, + [ValidateSet('yes', 'no', 'ask', 'auto')][string]$StartEsh='auto', + [parameter(DontShow)][switch]$FromScript=$false +) . $PSScriptRoot/base.ps1 -# 运行环境中完全可能没有pwsh(用户用winpwsh运行该脚本),所以我们需要进行一些额外的检查 -try { - if (-not (Get-Command pwsh -ErrorAction Ignore)) { - # tiny10或tiny11完全可能没有winget,额外的代码来修复它 - if (-not (Get-Command winget -ErrorAction Ignore)) { - # 这段if的大前提已经是在winpwsh中了 不需要额外的判断来确定是否使用-UseWindowsPowerShell flag导入Appx - Import-Module Appx - Add-AppxPackage -RegisterByFamilyName -MainPackage Microsoft.DesktopAppInstaller_8wekyb3d8bbwe +if ($IsWindows) { + # 运行环境中完全可能没有pwsh(用户用winpwsh运行该脚本),所以我们需要进行一些额外的检查 + try { + if (-not (Get-Command pwsh -ErrorAction Ignore)) { + # tiny10或tiny11完全可能没有winget,额外的代码来修复它 + if (-not (Get-Command winget -ErrorAction Ignore)) { + # 这段if的大前提已经是在winpwsh中了 不需要额外的判断来确定是否使用-UseWindowsPowerShell flag导入Appx + Import-Module Appx + Add-AppxPackage -RegisterByFamilyName -MainPackage Microsoft.DesktopAppInstaller_8wekyb3d8bbwe + } + # 额外的判断:在win8以下的系统中,最后一个能运行的pwsh版本是7.2.15 + Invoke-Expression "winget install --id Microsoft.Powershell $( + if([System.Environment]::OSVersion.Version.Major -le 7){'-v 7.2.15'} + )" + } + }catch{} + if ((Test-Path "$eshDir/.git") -and (-not (Test-Path "$eshDir/.git/desktop.ini"))) { + Copy-Item "$eshDir/data/git_icon.ini" "$eshDir/.git/desktop.ini" -Force + } + Get-ChildItem $eshDir -Recurse -Filter desktop.ini -Force | ForEach-Object { + $Dir = Get-Item $(Split-Path $_.FullName) -Force + $Dir.Attributes = $Dir.Attributes -bor [System.IO.FileAttributes]::ReadOnly -bor [System.IO.FileAttributes]::Directory + $_.Attributes = $_.Attributes -bor [System.IO.FileAttributes]::Hidden -bor [System.IO.FileAttributes]::System + } + Get-ChildItem $eshDir -Recurse -Filter .* | ForEach-Object { + $_.Attributes = $_.Attributes -bor [System.IO.FileAttributes]::Hidden + } + if ((Test-Path "$eshDir/../SAO-lib/SAO-lib.txt") -and (-not (Test-Path "$eshDir/data/SAO-lib.txt"))) { + if (([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]'Administrator')) { + New-Item -ItemType SymbolicLink -Path "$eshDir/data/SAO-lib.txt" -Target "$eshDir/../SAO-lib/SAO-lib.txt" -Force + } + else{ + try{ + Start-Process -Wait -FilePath cmd.exe -ArgumentList "/c mklink `"$eshDir/data/SAO-lib.txt`" `"$eshDir/../SAO-lib/SAO-lib.txt`"" -Verb runas + }catch{} } - # 额外的判断:在win8以下的系统中,最后一个能运行的pwsh版本是7.2.15 - Invoke-Expression "winget install --id Microsoft.Powershell $( - if([System.Environment]::OSVersion.Version.Major -le 7){'-v 7.2.15'} - )" } -}catch{} -#对于每个desktop.ini进行+s +h操作 -Get-ChildItem $eshDir -Recurse -Filter desktop.ini | ForEach-Object { - $_.Attributes = 'Hidden', 'System' } -if ((-not $eshDirFromEnv) -and (YorN "要安装 Esh 到环境变量吗?" -helpMessageY "将可以在任何地方使用``esh``或``EShell``命令")) { +if ((-not $eshDirFromEnv) -and (YorN "要安装 Esh 到环境变量吗?" -helpMessageY "将可以在任何地方使用``esh``或``EShell``命令" -SkipAsDefault:$Force)) { $env:Path += "`;$eshDir/path" $UserPath = [Environment]::GetEnvironmentVariable("Path", "User") + "`;$eshDir/path" [Environment]::SetEnvironmentVariable("Path", $UserPath, "User") @@ -46,7 +70,7 @@ else { $added = $true } } - if ((-not $added) -and (YorN "要添加 Eshell 到 PowerShell 配置文件吗?" -defaultN:($Host.Name -ne 'Visual Studio Code Host') -helpMessageY "powershell将表现得与Esh相同" -helpMessageN "让powershell保持原样,你仍然可以通过``esh``命令来使用Esh(推荐)")) { + if ((-not $added) -and (YorN "要添加 Eshell 到 PowerShell 配置文件吗?" -defaultN:($Host.Name -ne 'Visual Studio Code Host') -helpMessageY "powershell将表现得与Esh相同" -helpMessageN "让powershell保持原样,你仍然可以通过``esh``命令来使用Esh(推荐)" -SkipAsDefault:$Force)) { @( $universalProfile $profile @@ -58,12 +82,14 @@ else { } } if (-not $added) { - do { - $response = $Host.UI.PromptForChoice("未找到可用的profile文件", "选择你想要新建并添加Esh加载的配置文件", @( - [ChoiceDescription]::new("&0`b通用配置文件", $universalProfile), - [ChoiceDescription]::new("&1`b当前应用针对性配置文件", $profile) - ), 1) - } until ($response -ne -1) + if(-not $Force){ + do { + $response = $Host.UI.PromptForChoice("未找到可用的profile文件", "选择你想要新建并添加Esh加载的配置文件", @( + [ChoiceDescription]::new("&0`b当前应用针对性配置文件", $profile), + [ChoiceDescription]::new("&1`b通用配置文件", $universalProfile) + ), 0) + } until ($response -ne -1) + } $theProfile = @($universalProfile, $profile)[$response] New-Item -ItemType Directory -Force -Path $profilesDir | Out-Null Write-Information "在${theProfile}中添加了esh加载语句" @@ -74,18 +100,19 @@ else { if ($IsWindows) { $EshCmd = @("$eshDir/path/")[$eshDirFromEnv] + "esh.cmd" $wtFragmentDir = "$env:LOCALAPPDATA\Microsoft\Windows Terminal\Fragments\esh" - $WtProfileBase = @{ + $WtProfileBase = [ordered]@{ commandline = $EshCmd; startingDirectory = "~" - icon = "ms-appx:///ProfileIcons/{0caa0dad-35be-5f56-a8ff-afceeeaa6101}.png" + icon = "$eshDir/img/esh.ico" } - $wtFragment = @{ - schema = "https://aka.ms/terminal-profiles-schema" + $wtFragment = [ordered]@{ + '$help' = "https://aka.ms/terminal-documentation" + '$schema' = "https://aka.ms/terminal-profiles-schema" profiles = @( - @{name = "Eshell" } + $WtProfileBase - @{name = "Eshell (Root)" ; elevate = $true } + $WtProfileBase + [ordered]@{name = "Eshell" } + $WtProfileBase + [ordered]@{name = "Eshell (Root)" ; elevate = $true } + $WtProfileBase ) } - $wtFragmentJson = ($wtFragment | ConvertTo-Json).Replace("`r`n", "`n") + $wtFragmentJson = ($wtFragment | ConvertTo-Json).Replace("`r`n", "`n").Replace(" ", "`t")+ "`n" if (Test-Path $wtFragmentDir/esh.json) { if ((Get-Content $wtFragmentDir/esh.json -Raw) -ne $wtFragmentJson) { Set-Content $wtFragmentDir/esh.json $wtFragmentJson -NoNewline @@ -109,3 +136,21 @@ if ($IsWindows) { Set-Content $LoaderPath $startScript -NoNewline } } + +if (-not (Get-Command pwsh -ErrorAction Ignore)) { + $Host.UI.WriteErrorLine("esh的运行需要PowerShell 6或以上`n访问 https://aka.ms/pscore6 来获取PowerShell 6+ 并使得``pwsh``命令在环境中可用以使得esh能够正常工作") +} +else{ + $StartEsh = (-not $EshellUI) -and $(switch ($StartEsh) { + no { $false } + yes { $true } + default { YorN "要使用 Eshell 吗?" -SkipAsDefault:($Force -and $StartEsh -eq 'auto') } + }) +} +if ($StartEsh) { + if ($FromScript -or ($PSVersionTable.PSVersion.Major -lt 6)) { + . $eshDir/opt/run + [System.Environment]::Exit($LastExitCode) + } + else { . $eshDir/opt/run.ps1 -Invocation $MyInvocation } +} diff --git a/src/opt/uninstall.ps1 b/src/opt/uninstall.ps1 index bdbc5fc..70b0fb1 100644 --- a/src/opt/uninstall.ps1 +++ b/src/opt/uninstall.ps1 @@ -1,3 +1,8 @@ +param( + [switch]$Force=$false, + [ValidateSet('no', 'yes', 'ask', 'auto')][string]$RemoveDir='auto' +) + . $PSScriptRoot/base.ps1 # 移除环境变量 @@ -13,7 +18,7 @@ if ($eshDirFromEnv) { $profilesDir = Split-Path $PROFILE $startScript = ". $eshDir/opt/run.ps1" -Get-ChildItem $profilesDir -Filter *profile.ps1 | ForEach-Object { +Get-ChildItem $profilesDir -Filter *profile.ps1 -ErrorAction Ignore | ForEach-Object { $theprofile = $_.FullName $profileContent = Get-Content $theprofile if ($profileContent -contains $startScript) { @@ -50,7 +55,12 @@ if ($EshellUI) { Remove-Variable EshellUI -Scope Global } -if (YorN "要删除 Esh 安装目录吗?" -helpMessageY "将会删除 $eshDir" -defaultN:($eshDir -match "workspace|workstation")) { +$RemoveEshDir = switch ($RemoveDir) { + yes { $true } + no { $false } + default { YorN "要删除 Esh 安装目录吗?" -helpMessageY "将会删除 $eshDir" -defaultN:($eshDir -match "workspace|workstation") -SkipAsDefault:($Force -and $RemoveDir -eq 'auto') } +} +if ($RemoveEshDir) { Remove-Item $eshDir -Recurse -Force Write-Host "已删除 Esh 安装目录。" } diff --git a/src/scripts/CHT2CHS.ps1 b/src/scripts/CHT2CHS.ps1 index 1545e31..902a12f 100644 --- a/src/scripts/CHT2CHS.ps1 +++ b/src/scripts/CHT2CHS.ps1 @@ -8,16 +8,12 @@ function global:CHS2CHT.base { param([string]$str) - [string]$Result = "" - for ($i = 0; $i -lt $str.Length; $i++) { - if (([int]$str[$i] -gt 255) -and ${CHS2CHT.data.alphas}.Contains($str[$i])) { - $Result += ${CHS2CHT.data.alphas}[$str[$i]] + ($str.ToCharArray() | ForEach-Object { + if (($_ -ge 256) -and ${CHS2CHT.data.alphas}.Contains($_)) { + ${CHS2CHT.data.alphas}[$_] } - else { - $Result += $str[$i] - } - } - return $Result + else { $_ } + }) -join '' } function global:CHS2CHT { param([string]$str) @@ -32,16 +28,12 @@ function global:CHS2CHT { function global:CHT2CHS.base { param([string]$str) - [string]$Result = "" - for ($i = 0; $i -lt $str.Length; $i++) { - if (([int]$str[$i] -gt 255) -and ${CHT2CHS.data.alphas}.Contains($str[$i])) { - $Result += ${CHT2CHS.data.alphas}[$str[$i]] + ($str.ToCharArray() | ForEach-Object { + if (($_ -ge 256) -and ${CHT2CHS.data.alphas}.Contains($_)) { + ${CHT2CHS.data.alphas}[$_] } - else { - $Result += $str[$i] - } - } - return $Result + else { $_ } + }) -join '' } function global:CHT2CHS { param([string]$str) diff --git a/src/scripts/DiggingPath.ps1 b/src/scripts/DiggingPath.ps1 new file mode 100644 index 0000000..33d8e38 --- /dev/null +++ b/src/scripts/DiggingPath.ps1 @@ -0,0 +1,18 @@ +function global:DiggingPath { + param ( + [Parameter(Mandatory = $true)] + [scriptblock]$Runner, + $Path = $PWD.Path, + $CheckPath = '' + ) + if($Input) { if($Path){$CheckPath = $Path};$Path = $Input } + if(-not $Path) { return } + $DescriptionPath = if($CheckPath){ Join-Path $Path $CheckPath } else { $Path } + if (Test-Path $DescriptionPath) { + if($x=$Runner.Invoke(($_=$DescriptionPath))) { + return $x + } + } + $ParentPath = Split-Path $Path + DiggingPath $Runner $ParentPath $CheckPath +} diff --git a/src/scripts/Out.ps1 b/src/scripts/Out.ps1 index f47cddc..0061285 100644 --- a/src/scripts/Out.ps1 +++ b/src/scripts/Out.ps1 @@ -10,8 +10,7 @@ ${global:Out-Performance} = @{ } } -function global:Out-Error { - param ($Value) +function global:Out-Error($Value) { $Host.UI.WriteErrorLine( $VirtualTerminal.Colors[ ${global:Out-Performance}.Error.Color ?? @@ -22,8 +21,7 @@ function global:Out-Error { ) } -function global:Out-Warning { - param ($Value) +function global:Out-Warning($Value) { $Host.UI.WriteWarningLine( $VirtualTerminal.Colors[ ${global:Out-Performance}.Warning.Color ?? @@ -34,8 +32,7 @@ function global:Out-Warning { ) } -function global:Out-Info { - param ($Value) +function global:Out-Info($Value) { $Host.UI.WriteLine( $VirtualTerminal.Colors[ ${global:Out-Performance}.Info.Color ?? 'Default' diff --git a/src/scripts/Ukagaka.ps1 b/src/scripts/Ukagaka.ps1 index 116d2d2..08c7e09 100644 --- a/src/scripts/Ukagaka.ps1 +++ b/src/scripts/Ukagaka.ps1 @@ -1,20 +1,8 @@ -function global:Get-Ukagaka-Description-File-HashTable { - param( - [Parameter(Mandatory = $true)] - $Content - ) - #首先以换行符分割 - $Content = $Content -split "`n" - #去除末尾可能的`r - $Content = $Content -replace "`r$" - #去除空行 - $Content = $Content -ne "" +function global:Get-Ukagaka-Description-File-HashTable($Content) { $Description = @{} - foreach ($Line in $Content) { - $LineArray = $Line -split ',' - $Key = $LineArray[0].Trim() - $Value = $Line.Substring($LineArray[0].Length + 1).Trim() - $Description.Add($Key, $Value) + $Content ?? $Input -split '\r?\n' -ne '' | ForEach-Object { + $Key,$Value = $_ -split ',' + $Description.Add($Key.Trim(), $Value -join ',') } $Description } @@ -23,53 +11,33 @@ function global:Read-Ukagaka-Description-File { [Parameter(Mandatory = $true)] [string]$Path ) - $Content = Get-Content $Path -Encoding UTF8 - $Description = Get-Ukagaka-Description-File-HashTable -Content $Content + . ($Read={$Description = Get-Content $Path -Encoding UTF8 | Get-Ukagaka-Description-File-HashTable}) #若charset不是UTF-8或其大小写变体,则重新读取 - if (($Description['charset']) -and ($Description['charset'] -notmatch 'UTF-?8')) { - $Content = Get-Content $Path -Encoding $Description['charset'] - $Description = Get-Ukagaka-Description-File-HashTable -Content $Content - } + if ($Description.charset -and $Description.charset -notmatch 'UTF-?8' ) { . $Read } $Description } -function global:Test-Ukagaka-Directory-Base { +. "$($EshellUI.Sources.Path)/src/scripts/DiggingPath.ps1" +function global:Test-Ukagaka-Common-Directory { param( [Parameter(Mandatory = $true)] [string]$Path, [string]$CheckPath = 'descript.txt' ) - $DescriptionPath = Join-Path $Path $CheckPath - if (Test-Path $DescriptionPath) { - Read-Ukagaka-Description-File $DescriptionPath - } - else { - #测试父目录直至根目录 - $ParentPath = Split-Path $Path - if ($ParentPath) { - Test-Ukagaka-Directory-Base $ParentPath $CheckPath - } - else { - $null - } - } + DiggingPath { Read-Ukagaka-Description-File $_ } $Path $CheckPath } function global:Test-Ukagaka-Ghost-Directory { param( [Parameter(Mandatory = $true)] [string]$Path ) - Test-Ukagaka-Directory-Base $Path 'ghost/master/descript.txt' + Test-Ukagaka-Common-Directory $Path 'ghost/master/descript.txt' } function global:Test-Ukagaka-Directory { param( [Parameter(Mandatory = $true)] [string]$Path ) - $result = Test-Ukagaka-Directory-Base $Path - if (-not $result) { - Test-Ukagaka-Ghost-Directory $Path - } - else { - $result - } + + if ($result = Test-Ukagaka-Common-Directory $Path) { $result } + else { Test-Ukagaka-Ghost-Directory $Path } } diff --git a/src/scripts/ValueEx.ps1 b/src/scripts/ValueEx.ps1 index 42d360e..9dc330d 100644 --- a/src/scripts/ValueEx.ps1 +++ b/src/scripts/ValueEx.ps1 @@ -31,12 +31,11 @@ function global:ValueEx ($ValueAndMethods) { function global:IndexEx ($Value, $Index, [switch]$Set = $false, $ValueToSet) { if (-not $Index) { return , $Value } if ($Index.Contains('.')) { - while ($Index.Contains('.')) { - $Pos = $Index.IndexOf('.') - $SubIndex = $Index.Substring(0, $Pos) - $Index = $Index.Substring($Pos + 1) - $Value = IndexEx $Value $SubIndex + $Index = $Index -split '\.' + $Index | Select-Object -SkipLast 1 | ForEach-Object { + $Value = IndexEx $Value $_ } + $Index = $Index[-1] } if ($Set) { if ($Value.__arec_set__) { $Value.__arec_set__($Index, $ValueToSet) } diff --git a/src/system/UI/hints.ps1 b/src/system/UI/hints.ps1 index 164cddb..27523d7 100644 --- a/src/system/UI/hints.ps1 +++ b/src/system/UI/hints.ps1 @@ -19,9 +19,11 @@ $EshellUI.Hints = ValueEx @{ } } -$EshellUI.Hints.AddWithCommand('fk', 'fuck typos', 'thefuck') | Out-Null -$EshellUI.Hints.AddWithCommand('coffee', 'get a cup of coffee') | Out-Null -$EshellUI.Hints.AddWithCommand('poweron', 'turn on this computer') | Out-Null -$EshellUI.Hints.Add($(coffee)) | Out-Null +&{ + $EshellUI.Hints.AddWithCommand('fk', 'fuck typos', 'thefuck') + $EshellUI.Hints.AddWithCommand('coffee', 'get a cup of coffee') + $EshellUI.Hints.AddWithCommand('poweron', 'turn on this computer') + $EshellUI.Hints.Add($(coffee)) -Get-Content "$($EshellUI.Sources.Path)/data/SAO-lib.txt" -ErrorAction Ignore -Encoding utf-8 | ForEach-Object { $EshellUI.Hints.Add($_) } | Out-Null + Get-Content "$($EshellUI.Sources.Path)/data/SAO-lib.txt" -ErrorAction Ignore -Encoding utf-8 | ForEach-Object { $EshellUI.Hints.Add($_) } +} | Out-Null diff --git a/src/system/UI/icon.ps1 b/src/system/UI/icon.ps1 index 1867e4c..4cb844c 100644 --- a/src/system/UI/icon.ps1 +++ b/src/system/UI/icon.ps1 @@ -1,4 +1,3 @@ . "$($EshellUI.Sources.Path)/src/scripts/Console.ps1" -Set-ConsoleIcon "$($EshellUI.Sources.Path)/img/cmd.ico" -Remove-Item function:Set-ConsoleIcon -Remove-Item function:Set-WindowIcon +Set-ConsoleIcon "$($EshellUI.Sources.Path)/img/esh.ico" +Remove-Item @('function:Set-ConsoleIcon','function:Set-WindowIcon') diff --git a/src/system/UI/logo.ps1 b/src/system/UI/logo.ps1 index 0f71388..ecc8824 100644 --- a/src/system/UI/logo.ps1 +++ b/src/system/UI/logo.ps1 @@ -11,12 +11,12 @@ $EshellUI.Logo = ValueEx @{ if ($EshellUI.Im.VSCodeExtension) { Write-Host -NoNewline "$($VirtualTerminal.Colors.Magenta) For $($VirtualTerminal.Styles.Italic)VSCode PowerShell Extension $($VirtualTerminal.Styles.NoItalic)v$($host.Version.ToString())" } - <#elseif ($EshellUI.Im.WindowsTerminal) { + elseif ($EshellUI.Im.WindowsTerminal) { function Get-WindowsTerminalVersion { [regex]::match((Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\wt.exe").Path, "_(.*?)_").Groups[1].Value } - Write-Host -NoNewline "$($VirtualTerminal.Colors.Magenta) For $($VirtualTerminal.Styles.Italic)Windows Terminal $($VirtualTerminal.Styles.NoItalic)v$(Get-WindowsTerminalVersion)" - }#> + Write-Host -NoNewline "$($VirtualTerminal.Colors.Magenta) In $($VirtualTerminal.Styles.Italic)Windows Terminal $($VirtualTerminal.Styles.NoItalic)v$(Get-WindowsTerminalVersion)" + } Write-Host Write-Host "$($VirtualTerminal.Styles.Italic)$($VirtualTerminal.Colors.BrightMagenta)(c)$($VirtualTerminal.Colors.Reset) E-tek Corporation.$($VirtualTerminal.Styles.NoItalic) $($VirtualTerminal.Styles.Underline)All rights reserved$($VirtualTerminal.Styles.NoUnderline)." diff --git a/src/system/UI/prompt/builders/git.ps1 b/src/system/UI/prompt/builders/git.ps1 index ba94c1a..b59dc28 100644 --- a/src/system/UI/prompt/builders/git.ps1 +++ b/src/system/UI/prompt/builders/git.ps1 @@ -1,29 +1,13 @@ $EshellUI.Prompt.Builders['git'] = { - param( - [Parameter(Mandatory = $true)] - [string]$prompt_str, - [Parameter(Mandatory = $true)] - [HashTable]$BuildMethods - ) if (Test-Command git) { $gitRepoUid = git rev-parse --short HEAD 2>$null $gitRepoBranch = git rev-parse --abbrev-ref HEAD 2>$null $gitChangedFileNum = git status --porcelain 2>$null | Measure-Object -Line | Select-Object -ExpandProperty Lines - if ($null -ne $gitRepoUid) { - $git_prompt_str = " $($VirtualTerminal.Colors.Cyan)$gitRepoUid" - if ($null -ne $gitRepoBranch) { - $git_prompt_str += "@$gitRepoBranch" - } + if($gitRepoUid -or $gitRepoBranch) { + $VirtualTerminal.Colors.Cyan + '' + (@($gitRepoBranch,$gitRepoUid) -ne $null -join '@') } if ($gitChangedFileNum -gt 0) { - $git_prompt_str = "$git_prompt_str $gitChangedFileNum file" - if ($gitChangedFileNum -gt 1) { - $git_prompt_str += 's' - } - } - if ($git_prompt_str) { - $prompt_str = $BuildMethods.AddBlock($prompt_str, $git_prompt_str) + "$gitChangedFileNum file"+@('s')[$gitChangedFileNum -eq 1] } } - $prompt_str } diff --git a/src/system/UI/prompt/builders/npm.ps1 b/src/system/UI/prompt/builders/npm.ps1 index 137cef4..ec93147 100644 --- a/src/system/UI/prompt/builders/npm.ps1 +++ b/src/system/UI/prompt/builders/npm.ps1 @@ -1,33 +1,7 @@ +. "$($EshellUI.Sources.Path)/src/scripts/DiggingPath.ps1" $EshellUI.Prompt.Builders['npm'] = { - param( - [Parameter(Mandatory = $true)] - [string]$prompt_str, - [Parameter(Mandatory = $true)] - [HashTable]$BuildMethods - ) - if (Test-Path package.json) { - $packageJson = Get-Content package.json -Raw -ErrorAction SilentlyContinue + DiggingPath { Get-Content $_ -Raw | ConvertFrom-Json } $PWD.Path 'package.json' | Set-Variable -Name packageJson + if ($npminfo = @($packageJson.Name,$packageJson.Version) -ne $null -join '@') { + $VirtualTerminal.Colors.Red+" "+$npminfo } - elseif (Test-Command git) { - $gitRepoRoot = git rev-parse --show-toplevel 2>$null - if (($null -ne $gitRepoRoot) -and (Test-Path "$gitRepoRoot/package.json")) { - $packageJson = Get-Content "$gitRepoRoot/package.json" -Raw -ErrorAction SilentlyContinue - } - } - $npm_prompt_str = $null - if ($null -ne $packageJson) { - $packageJson = ConvertFrom-Json $packageJson - $npmRepoName = $packageJson.Name - $npmRepoVersion = $packageJson.Version - } - if ($null -ne $npmRepoName) { - $npm_prompt_str = " $($VirtualTerminal.Colors.Red) $npmRepoName" - if ($null -ne $npmRepoVersion) { - $npm_prompt_str += "@$npmRepoVersion" - } - } - if ($npm_prompt_str) { - $prompt_str = $BuildMethods.AddBlock($prompt_str, $npm_prompt_str) - } - $prompt_str } diff --git a/src/system/UI/prompt/builders/ukagaka.ps1 b/src/system/UI/prompt/builders/ukagaka.ps1 index 4faaca4..8354c24 100644 --- a/src/system/UI/prompt/builders/ukagaka.ps1 +++ b/src/system/UI/prompt/builders/ukagaka.ps1 @@ -1,65 +1,17 @@ . "$($EshellUI.Sources.Path)/src/scripts/Ukagaka.ps1" $EshellUI.Prompt.Builders['ukagaka'] = { - param( - [Parameter(Mandatory = $true)] - [string]$prompt_str, - [Parameter(Mandatory = $true)] - [HashTable]$BuildMethods - ) - $ukagaka_prompt_str = $null $ukagakaDescription = Test-Ukagaka-Directory $PWD.Path if ($ukagakaDescription.Count -gt 0) { - switch ($x = $ukagakaDescription['type']) { - 'ghost' { - $ukagaka_prompt_str = " $($VirtualTerminal.Colors.Green)󰀆 $x" - if ($ukagakaDescription['name']) { - $ukagaka_prompt_str += " $($ukagakaDescription[`"name`"])" - if ($ukagakaDescription['sakura.name']) { - $ukagaka_prompt_str += "($($ukagakaDescription[`"sakura.name`"])" - if ($ukagakaDescription['kero.name']) { - $ukagaka_prompt_str += "&$($ukagakaDescription[`"kero.name`"])" - } - $ukagaka_prompt_str += ')' - } - } - elseif ($ukagakaDescription['sakura.name']) { - $ukagaka_prompt_str += " $($ukagakaDescription[`"sakura.name`"])" - if ($ukagakaDescription['kero.name']) { - $ukagaka_prompt_str += "&$($ukagakaDescription[`"kero.name`"])" - } - } + $detalname = @($ukagakaDescription['sakura.name'],$ukagakaDescription['kero.name']) -ne $null -join '&' + $VirtualTerminal.Colors.Green+"$( + switch ($x = $ukagakaDescription.type) { + 'ghost' { "󰀆" };'shell' { "󱓨" };'balloon' { "󰍡" };default { "" } } - 'shell' { - $ukagaka_prompt_str = " $($VirtualTerminal.Colors.Green)󱓨 $x" - if ($ukagakaDescription['name']) { - $ukagaka_prompt_str += " $($ukagakaDescription[`"name`"])" - } - } - 'balloon' { - $ukagaka_prompt_str = " $($VirtualTerminal.Colors.Green)󰍡 $x" - if ($ukagakaDescription['name']) { - $ukagaka_prompt_str += " $($ukagakaDescription[`"name`"])" - } - } - default { - $ukagaka_prompt_str = " $($VirtualTerminal.Colors.Green) $x" - if ($ukagakaDescription['name']) { - $ukagaka_prompt_str += " $($ukagakaDescription[`"name`"])" - } - } - } - if ($ukagakaDescription['craftman']) { - $ukagaka_prompt_str = $BuildMethods.AddBlock($ukagaka_prompt_str, " by $($ukagakaDescription[`"craftman`"])") + ) $x" + if ($name=$ukagakaDescription.name ?? $detalname) { + "$name$(if ($name -ne $detalname) { "($detalname)" })" } - if ($ukagakaDescription['githubrepo']) { - $ukagaka_prompt_str = $BuildMethods.AddBlock($ukagaka_prompt_str, " @ <$($ukagakaDescription[`"githubrepo`"])>") - } - elseif ($ukagakaDescription['craftmanurl']) { - $ukagaka_prompt_str = $BuildMethods.AddBlock($ukagaka_prompt_str, " @ <$($ukagakaDescription[`"craftmanurl`"])>") - } - } - if ($ukagaka_prompt_str) { - $prompt_str = $BuildMethods.AddBlock($prompt_str, $ukagaka_prompt_str) + if ($x=$ukagakaDescription.craftman) { "by $x" } + if ($x=$ukagakaDescription.githubrepo ?? $ukagakaDescription.craftmanurl){ "@ <$x>" } } - $prompt_str } diff --git a/src/system/UI/prompt/main.ps1 b/src/system/UI/prompt/main.ps1 index a0697b4..218823d 100644 --- a/src/system/UI/prompt/main.ps1 +++ b/src/system/UI/prompt/main.ps1 @@ -1,47 +1,54 @@ $EshellUI.Prompt = ValueEx @{ Parent = $EshellUI - Builders = @{} + Builders = [ordered]@{} BuildMethods = ValueEx @{ + 'method:BaseNewlineCheck' = { + param($LastLineLength, $scale, $threshold_base = $Host.UI.RawUI.WindowSize.Width) + if ($LastLineLength -gt $threshold_base*$scale) { "`n" } + } 'method:NewlineCheck' = { - param( - [Parameter(Mandatory = $true)] - [string]$prompt_str - ) - $LastLineIndex = [Math]::Max($prompt_str.LastIndexOf('`n'), 0) - $LastLine = $prompt_str.Substring($LastLineIndex) - #如果$prompt_str最后一行长度大于$Host.UI.RawUI.WindowSize.Width/2则换行 - if ($LastLine.Length -gt ($Host.UI.RawUI.WindowSize.Width / 2)) { - $prompt_str += "`n" - } - return $prompt_str + param($prompt_str) + $LastLine = $prompt_str -split "`n" | Select-Object -Last 1 + $prompt_str+$this.BaseNewlineCheck($LastLine.Length, 0.42) } - 'method:AddBlock' = { - param($prompt_str, $block_str) - $LastLineIndex = [Math]::Max($prompt_str.LastIndexOf('`n'), 0) - $LastLine = $prompt_str.Substring($LastLineIndex) - #如果$LastLine + $block_str长度大于$Host.UI.RawUI.WindowSize.Width则换行 - if (($LastLine + $block_str).Length -gt $Host.UI.RawUI.WindowSize.Width) { - $prompt_str += "`n" - } - return $prompt_str + $block_str + 'method:MargeBlock' = { + param($blocks) + ( $blocks | ForEach-Object{ + if($x=$this.BaseNewlineCheck(($LastLineLength+=$_.Length), 0.66)){ + $LastLineLength = 0;$x + } else{ ' ' } + $_ + } | Select-Object -Skip 1 ) -join '' } + # 关于0.42和0.66的解释:0.66+0.42=1.08,这象征着108好汉于三国起义 杀死了汉朝的最后一位皇帝,从而结束了原神 } 'method:Get' = { $local:EshellUI = $this.Parent - $prompt_str = $PWD.Path - if (($prompt_str.StartsWith($HOME)) -or ($prompt_str.StartsWith($EshellUI.MSYS.RootPath))) { - $prompt_str = WindowsPathToLinuxPath $prompt_str + $path_str = $PWD.Path + if ($path_str.StartsWith($HOME) -or $path_str.StartsWith($EshellUI.MSYS.RootPath)) { + $path_str = WindowsPathToLinuxPath $path_str } - - $this.Builders.Keys | ForEach-Object { - $prompt_str = $this.Builders[$_].Invoke($prompt_str, $this.BuildMethods) + $this.LastBuild=@{ + Tokens=@($VirtualTerminal.Colors.Reset+$path_str) + BuilderOutput=@{} + } + $this.Builders.GetEnumerator() | ForEach-Object { + $this.LastBuild.Tokens += $this.LastBuild.BuilderOutput[$_.Key] = & $_.Value } - $prompt_str = $this.BuildMethods.NewlineCheck("$prompt_str $($VirtualTerminal.Colors.Reset)") - $prompt_str += '>' * ($NestedPromptLevel+1) - return $prompt_str + return $this.BuildMethods.NewlineCheck( + $this.BuildMethods.MargeBlock($this.LastBuild.Tokens) + ) + ' ' + $VirtualTerminal.Colors.Reset + '>' * ($NestedPromptLevel+1) } } #遍历脚本所在文件夹 -Get-ChildItem $PSScriptRoot/builders *.ps1 | ForEach-Object { . $_.FullName } +Get-ChildItem $PSScriptRoot/builders *.ps1 | Sort-Object -Property Name | ForEach-Object { . $_.FullName } -function global:prompt { $EshellUI.Prompt.Get() } +function global:prompt { + try{ + $EshellUI.Prompt.Get() + } + catch { + $_ | Out-Error + "err >" + } +} diff --git a/src/system/UI/title.ps1 b/src/system/UI/title.ps1 index 5ed7605..b820aff 100644 --- a/src/system/UI/title.ps1 +++ b/src/system/UI/title.ps1 @@ -1,6 +1,2 @@ #set the title same as cmd -$host.UI.RawUI.WindowTitle = '命令提示符' -#if as root -if ($EshellUI.Im.Sudo) { - $host.UI.RawUI.WindowTitle += '(root)' -} +$host.UI.RawUI.WindowTitle = '命令提示符' + $(if ($EshellUI.Im.Sudo) { '(root)' }) diff --git a/src/system/linux.ps1 b/src/system/linux.ps1 index d4c5b2c..05b0c49 100644 --- a/src/system/linux.ps1 +++ b/src/system/linux.ps1 @@ -1,30 +1,47 @@ #查找$EshellUI.MSYS.RootPath是否是可用的msys路径 -if (-not $EshellUI.MSYS.RootPath -or -not (Test-Path $EshellUI.MSYS.RootPath)) { - #若不是,则遍历所有盘符 - $DriveLetters = Get-PSDrive -PSProvider FileSystem | ForEach-Object { $_.Name } - $DriveLetters | ForEach-Object { +if (-not (Test-Path $EshellUI.MSYS.RootPath)) { + if(Test-Command rm.exe) { + $Path = Split-Path (Get-Command rm.exe).Source + $EshellUI.MSYS.RootPath = $Path -replace "([\\/]usr)[\\/]?bin[\\/]?$" + } + Get-PSDrive -PSProvider FileSystem | ForEach-Object { $_.Name } | ForEach-Object { #若该盘符下存在msys路径 $EshellUI.MSYS.RootPath ??= if (Test-Path "${_}:\msys64") { "${_}:\msys64" } elseif (Test-Path "${_}:\msys") { "${_}:\msys" } } + if (Test-Path $EshellUI.MSYS.RootPath) { + $EshellUI.LoadingLog.AddInfo("Now MSYS RootPath is auto set to $($VirtualTerminal.Colors.Green)$($EshellUI.MSYS.RootPath)") + $EshellUI.SaveVariables() + } + else{ + $EshellUI.LoadingLog.AddWarning( +"Auto set MSYS RootPath failed. +Set it manually by $( + $VirtualTerminal.Colors.Green +)`$EshellUI.MSYS.RootPath $($VirtualTerminal.Colors.White)= $($VirtualTerminal.Colors.Blue)'path'$( + $VirtualTerminal.Colors[${global:Out-Performance}.Warning.Color] +) then run $( + $VirtualTerminal.Colors.Green +)`$EshellUI.$($VirtualTerminal.Colors.Magenta)SaveVariables()$( + $VirtualTerminal.Colors[${global:Out-Performance}.Warning.Color] +) or $( + $VirtualTerminal.Colors.Green +)exit$( + $VirtualTerminal.Colors[${global:Out-Performance}.Warning.Color] +) to save it.") + } } if (Test-Command locale) { $env:LANG ??= $env:LANGUAGE ??= $env:LC_ALL ??= $(locale -uU) } -function global:Test-PathEx { - param( - [string]$Path - ) +function global:Test-PathEx($Path) { if (IsLinuxPath $Path) { $Path = LinuxPathToWindowsPath $Path } return Test-Path $Path } -function global:IsLinuxPath { - param( - [string]$Path - ) +function global:IsLinuxPath([string]$Path) { if ($Path.StartsWith("/") -or $Path.StartsWith("~")) { return $true } @@ -35,10 +52,7 @@ function global:IsLinuxPath { } #一个函数以处理linux路径到windows路径的转换 -function global:LinuxPathToWindowsPath { - param( - [string]$Path - ) +function global:LinuxPathToWindowsPath($Path) { if (($PWD.Path -eq $env:USERPROFILE) -and $Path.StartsWith("./")) { $Path = "~" + $Path.Substring(1) } @@ -69,10 +83,7 @@ function global:LinuxPathToWindowsPath { return Join-Path $EshellUI.MSYS.RootPath $Path } } -function global:WindowsPathToLinuxPath { - param( - [string]$Path - ) +function global:WindowsPathToLinuxPath($Path) { #若path是rootpath的子目录 if ($Path.StartsWith($EshellUI.MSYS.RootPath)) { #则转换为linux路径 @@ -97,150 +108,151 @@ function global:WindowsPathToLinuxPath { return $Path } -#一个补全提供器用于补全linux路径 -$LinuxPathCompleter = { - param( - [string]$commandName, - [string]$parameterName, - [string]$wordToComplete, - [string]$commandAst, - [string]$fakeBoundParameter - ) - #补全的前提是要补全的词语为linux路径 - if (-not (IsLinuxPath $wordToComplete)) { - return - } - #获取对应的windows路径 - $WindowsPath = LinuxPathToWindowsPath $wordToComplete - #若windows路径不存在 - if (-not (Test-Path $WindowsPath)) { - #测试其父目录是否存在 - $ParentPath = Split-Path $WindowsPath - if (-not (Test-Path $ParentPath)) { - #若父目录不存在,则不补全 +if (Test-Command rm.exe) { + . "$($EshellUI.Sources.Path)/src/commands/special/linux_bins.ps1" +} + + +if (Test-Path $EshellUI.MSYS.RootPath) { + #一个补全提供器用于补全linux路径 + $LinuxPathCompleter = { + param( + [string]$commandName, + [string]$parameterName, + [string]$wordToComplete, + [string]$commandAst, + [string]$fakeBoundParameter + ) + #补全的前提是要补全的词语为linux路径 + if (-not (IsLinuxPath $wordToComplete)) { return } - #若父目录存在,则根据后半段路径补全 - $WordToComplete = Split-Path $WindowsPath -Leaf - #遍历父目录下的所有文件和目录 - Get-ChildItem $ParentPath | ForEach-Object { - #若文件或目录名以$WordToComplete开头 - if ($_.Name -like "${WordToComplete}*") { + #获取对应的windows路径 + $WindowsPath = LinuxPathToWindowsPath $wordToComplete + #若windows路径不存在 + if (-not (Test-Path $WindowsPath)) { + #测试其父目录是否存在 + $ParentPath = Split-Path $WindowsPath + if (-not (Test-Path $ParentPath)) { + #若父目录不存在,则不补全 + return + } + #若父目录存在,则根据后半段路径补全 + $WordToComplete = Split-Path $WindowsPath -Leaf + #遍历父目录下的所有文件和目录 + Get-ChildItem $ParentPath | ForEach-Object { + #若文件或目录名以$WordToComplete开头 + if ($_.Name -like "${WordToComplete}*") { + #输出其linux路径 + WindowsPathToLinuxPath $_.FullName + } + } + } + else { + #若windows路径存在,则遍历其下的所有文件和目录 + Get-ChildItem $WindowsPath | ForEach-Object { #输出其linux路径 WindowsPathToLinuxPath $_.FullName } } } - else { - #若windows路径存在,则遍历其下的所有文件和目录 - Get-ChildItem $WindowsPath | ForEach-Object { - #输出其linux路径 - WindowsPathToLinuxPath $_.FullName - } - } -} -Register-ArgumentCompleter -ParameterName "Path" -ScriptBlock $LinuxPathCompleter -Register-ArgumentCompleter -ParameterName "Destination" -ScriptBlock $LinuxPathCompleter -Remove-Variable LinuxPathCompleter - -Set-PSReadLineKeyHandler -Key Tab -Function MenuComplete - -if (Test-Command rm.exe) { - . "$($EshellUI.Sources.Path)/src/commands/special/linux_bins.ps1" -} + Register-ArgumentCompleter -ParameterName "Path" -ScriptBlock $LinuxPathCompleter + Register-ArgumentCompleter -ParameterName "Destination" -ScriptBlock $LinuxPathCompleter + Remove-Variable LinuxPathCompleter -#设置一个函数用于在powershell执行以/开头的命令时,自动转换为windows路径 -#设置触发器 -Set-PSReadLineKeyHandler -Key Enter -ScriptBlock { - #获取当前行 - $OriLine = $null - $Cursor = $null - [Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$OriLine, [ref]$Cursor) - $Line = $OriLine.Trim() - #自行首获取可执行文件路径 - $Executable = $Line.Split(" ")[0] - $Rest = $Line.Substring($Executable.Length).Trim() - if ($Executable.StartsWith('"')) { - while ((-not $Executable.EndsWith('"')) -and ($Executable.Length -lt $Line.Length)) { - $Executable = $Executable + " " + $Line.Substring($Executable.Length + 1).Split(" ")[0] - } + #设置一个函数用于在powershell执行以/开头的命令时,自动转换为windows路径 + #设置触发器 + Set-PSReadLineKeyHandler -Key Enter -ScriptBlock { + #获取当前行 + $OriLine = $null + $Cursor = $null + [Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$OriLine, [ref]$Cursor) + $Line = $OriLine.Trim() + #自行首获取可执行文件路径 + $Executable = $Line.Split(" ")[0] $Rest = $Line.Substring($Executable.Length).Trim() - } - #若当前行以/开头 - if ($Executable.StartsWith("/") -or $Executable.StartsWith("~")) { - Write-Host - [Microsoft.PowerShell.PSConsoleReadLine]::CancelLine() - Write-Host "`b`b " - #则转换为windows路径 - $Executable = LinuxPathToWindowsPath $Executable - #求值并输出 - $StartExecutionTime = Get-Date - try { Invoke-Expression "$Executable $Rest *>&1" | Out-Default } - catch { Out-Error $_ } - $EndExecutionTime = Get-Date - [Microsoft.PowerShell.PSConsoleReadLine]::AddToHistory($OriLine) - [PSCustomObject](@{ - CommandLine = "$Executable $Rest" - ExecutionStatus = "Completed" - StartExecutionTime = $StartExecutionTime - EndExecutionTime = $EndExecutionTime - }) | Add-History - } - else { - if ($EshellUI.Im.VSCodeExtension -and ($NestedPromptLevel -eq 0)) { - if ($Line.StartsWith("exit")) { - #若当前行以exit开头,则退出vscode - Invoke-Expression "global:$Line" - return + if ($Executable.StartsWith('"')) { + while ((-not $Executable.EndsWith('"')) -and ($Executable.Length -lt $Line.Length)) { + $Executable = $Executable + " " + $Line.Substring($Executable.Length + 1).Split(" ")[0] } + $Rest = $Line.Substring($Executable.Length).Trim() } - [Microsoft.PowerShell.PSConsoleReadLine]::AcceptLine() - } -} -Set-PSReadLineKeyHandler -Key Tab -ScriptBlock { - #获取当前行 - $OriLine = $null - $Cursor = $null - [Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$OriLine, [ref]$Cursor) - $Line = $OriLine - #自光标位置分隔出当前单词 - $BeforeCursor = $Line.Substring(0, $Cursor) - $Rest = $Line.Substring($Cursor) - $WordToComplete = $BeforeCursor.Split(" ")[-1] - #处理" - $HasQuote = $false - if ($WordToComplete.EndsWith('"')) { - $HasQuote = $true - while (-not $WordToComplete.StartsWith('"')) { - $WordToComplete = $BeforeCursor.Substring(0, $BeforeCursor.Length - $WordToComplete.Length - 1).Split(" ")[-1] + " " + $WordToComplete + #若当前行以/开头 + if ($Executable.StartsWith("/") -or $Executable.StartsWith("~")) { + Write-Host + [Microsoft.PowerShell.PSConsoleReadLine]::CancelLine() + Write-Host "`b`b " + #则转换为windows路径 + $Executable = LinuxPathToWindowsPath $Executable + #求值并输出 + $StartExecutionTime = Get-Date + try { Invoke-Expression "$Executable $Rest *>&1" | Out-Default } + catch { Out-Error $_ } + $EndExecutionTime = Get-Date + [Microsoft.PowerShell.PSConsoleReadLine]::AddToHistory($OriLine) + [PSCustomObject](@{ + CommandLine = "$Executable $Rest" + ExecutionStatus = "Completed" + StartExecutionTime = $StartExecutionTime + EndExecutionTime = $EndExecutionTime + }) | Add-History } - $WordToComplete = $WordToComplete.Substring(1, $WordToComplete.Length - 2) - } - #若当前单词以/开头 - if ($WordToComplete.StartsWith("/") -or $WordToComplete.StartsWith("~")) { - #则转换为windows路径 - $WordAfterComplete = LinuxPathToWindowsPath $WordToComplete + '/' - if ($HasQuote) { - $WordAfterComplete = '"' + $WordAfterComplete + '"' + else { + if ($EshellUI.Im.VSCodeExtension -and ($NestedPromptLevel -eq 0)) { + if ($Line.StartsWith("exit")) { + #若当前行以exit开头,则退出vscode + Invoke-Expression "global:$Line" + return + } + } + [Microsoft.PowerShell.PSConsoleReadLine]::AcceptLine() } - $CursorOfBegin = $Cursor - $WordToComplete.Length - $CursorOfEnd = $Cursor - $CursorOfBegin + $WordAfterComplete.Length - [Microsoft.PowerShell.PSConsoleReadLine]::Replace($CursorOfBegin, $WordToComplete.Length, $WordAfterComplete) - [Microsoft.PowerShell.PSConsoleReadLine]::SetCursorPosition($CursorOfEnd) - [Microsoft.PowerShell.PSConsoleReadLine]::MenuComplete() + } + Set-PSReadLineKeyHandler -Key Tab -ScriptBlock { + #获取当前行 + $OriLine = $null + $Cursor = $null [Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$OriLine, [ref]$Cursor) - $BeginIndex = $OriLine.IndexOf($WordAfterComplete) - if ($BeginIndex -ne -1) { - $CursorOfBegin = $BeginIndex + $Line = $OriLine + #自光标位置分隔出当前单词 + $BeforeCursor = $Line.Substring(0, $Cursor) + $Rest = $Line.Substring($Cursor) + $WordToComplete = $BeforeCursor.Split(" ")[-1] + #处理" + $HasQuote = $false + if ($WordToComplete.EndsWith('"')) { + $HasQuote = $true + while (-not $WordToComplete.StartsWith('"')) { + $WordToComplete = $BeforeCursor.Substring(0, $BeforeCursor.Length - $WordToComplete.Length - 1).Split(" ")[-1] + " " + $WordToComplete + } + $WordToComplete = $WordToComplete.Substring(1, $WordToComplete.Length - 2) + } + #若当前单词以/开头 + if ($WordToComplete.StartsWith("/") -or $WordToComplete.StartsWith("~")) { + #则转换为windows路径 + $WordAfterComplete = LinuxPathToWindowsPath $WordToComplete + '/' + if ($HasQuote) { + $WordAfterComplete = '"' + $WordAfterComplete + '"' + } + $CursorOfBegin = $Cursor - $WordToComplete.Length + $CursorOfEnd = $Cursor - $CursorOfBegin + $WordAfterComplete.Length + [Microsoft.PowerShell.PSConsoleReadLine]::Replace($CursorOfBegin, $WordToComplete.Length, $WordAfterComplete) + [Microsoft.PowerShell.PSConsoleReadLine]::SetCursorPosition($CursorOfEnd) + [Microsoft.PowerShell.PSConsoleReadLine]::MenuComplete() + [Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$OriLine, [ref]$Cursor) + $BeginIndex = $OriLine.IndexOf($WordAfterComplete) + if ($BeginIndex -ne -1) { + $CursorOfBegin = $BeginIndex + } + $CursorOfEnd = $OriLine.Length - $CursorOfBegin - $Rest.Length + $WordToComplete = $OriLine.Substring($CursorOfBegin, $CursorOfEnd) + $WordAfterComplete = WindowsPathToLinuxPath $WordToComplete + [Microsoft.PowerShell.PSConsoleReadLine]::Replace($CursorOfBegin, $CursorOfEnd, $WordAfterComplete) + [Microsoft.PowerShell.PSConsoleReadLine]::SetCursorPosition($CursorOfBegin + $WordAfterComplete.Length) + } + else { + #否则调用默认的补全 + [Microsoft.PowerShell.PSConsoleReadLine]::MenuComplete() } - $CursorOfEnd = $OriLine.Length - $CursorOfBegin - $Rest.Length - $WordToComplete = $OriLine.Substring($CursorOfBegin, $CursorOfEnd) - $WordAfterComplete = WindowsPathToLinuxPath $WordToComplete - [Microsoft.PowerShell.PSConsoleReadLine]::Replace($CursorOfBegin, $CursorOfEnd, $WordAfterComplete) - [Microsoft.PowerShell.PSConsoleReadLine]::SetCursorPosition($CursorOfBegin + $WordAfterComplete.Length) - } - else { - #否则调用默认的补全 - [Microsoft.PowerShell.PSConsoleReadLine]::MenuComplete() } }