Merge remote-tracking branch 'upstream/development' into development-dax

* upstream/development: (84 commits)
  update profile.ps1's debug output
  Make PowerShell profile.ps1's debug and verbose working correctly
  Redirect Verbose and Debug stream
  ignore git commit
  fix line endings
  adjust comments
  Update PowerShell test to set CMDER_DEBUG env variable inline
  improve CI PowrShell profile test debug/verbose output
  Modify PowerShell test command for debug output
  fix incorrect comment
  adjust comments
  adjust comments
  adjust comments
  Avoid duplicating cmd.exe prompt definition
  Consolidate terminal conditionals and add shell integration for bash and cmd.exe
  Add OSC 133;D support for command exit code tracking
  Initial plan
  adjust comments
  simplify regex
  adjust comments
  ...
This commit is contained in:
Dax T. Games
2025-11-27 14:08:51 +00:00
29 changed files with 649 additions and 392 deletions

15
.git-blame-ignore-revs Normal file
View File

@@ -0,0 +1,15 @@
# List of whitespace-only commits to ignore in the Git blame;
# to improve tracking changes and avoid noise
58db4e3419bf1e5cc1bb61fcd7ce2ebbca89243a
efb3338f5cf0eec21e8a75abc62ee14965cb4a7e
3859f6ffc088b2ae78748abc84986f4adcadcd41
d6569192fc91167f555c3eff58402ff01f1197ea
67de97a492c9389f95499db38f9474a1c20ec585
a0d085f93eaa69c22449d0217e8daf9eaea2b180
1cfba25beb46c74bb1debca2bcfe7ac470e96172
f6bc623284914489e891bbac923feb774c862b99
abbab3f8b477e917d0a175d0de23cce121096631
126347025f9cade241beff182738b2527da7535e
4740b836f300658b27e6ad4d79efac63c9c24c24
be44bac95670b1cbbc91bd657882d985989846f9
f67e5704eda60526d495be758572181f01a6cac8

View File

@@ -19,7 +19,7 @@ jobs:
# Steps represent a sequence of tasks that will be executed as part of the job # Steps represent a sequence of tasks that will be executed as part of the job
steps: steps:
# Checks-out the repository under $GITHUB_WORKSPACE, so the job can access it # Checks-out the repository under $GITHUB_WORKSPACE, so the job can access it
- uses: actions/checkout@v4 - uses: actions/checkout@v5
with: with:
fetch-depth: 0 # fetch all history for all branches and tags fetch-depth: 0 # fetch all history for all branches and tags

View File

@@ -35,7 +35,7 @@ jobs:
discussions: write discussions: write
steps: steps:
- name: Check out repository code (Action from GitHub) - name: Check out repository code (Action from GitHub)
uses: actions/checkout@v4 uses: actions/checkout@v5
with: with:
fetch-depth: 0 fetch-depth: 0
@@ -53,66 +53,66 @@ jobs:
run: .\pack.ps1 -verbose -terminal all run: .\pack.ps1 -verbose -terminal all
- name: Upload artifact (cmder_win_mini.zip) - name: Upload artifact (cmder_win_mini.zip)
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4
with: with:
path: build/cmder_win_mini.zip path: build/cmder_win_mini.zip
name: cmder_win_mini.zip name: cmder_win_mini.zip
if-no-files-found: error if-no-files-found: error
- name: Upload artifact (cmder_win.7z) - name: Upload artifact (cmder_win.7z)
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4
with: with:
path: build/cmder_win.7z path: build/cmder_win.7z
name: cmder_win.7z name: cmder_win.7z
if-no-files-found: error if-no-files-found: error
- name: Upload artifact (cmder_win.zip) - name: Upload artifact (cmder_win.zip)
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4
with: with:
path: build/cmder_win.zip path: build/cmder_win.zip
name: cmder_win.zip name: cmder_win.zip
if-no-files-found: error if-no-files-found: error
- name: Upload artifact (cmder_wt.zip) - name: Upload artifact (cmder_wt.zip)
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4
with: with:
path: build/cmder_wt.zip path: build/cmder_wt.zip
name: cmder_wt.zip name: cmder_wt.zip
if-no-files-found: error if-no-files-found: error
- name: Upload artifact (cmder_wt.7z) - name: Upload artifact (cmder_wt.7z)
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4
with: with:
path: build/cmder_wt.7z path: build/cmder_wt.7z
name: cmder_wt.7z name: cmder_wt.7z
- name: Upload artifact (cmder_wt_mini.zip) - name: Upload artifact (cmder_wt_mini.zip)
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4
with: with:
path: build/cmder_wt_mini.zip path: build/cmder_wt_mini.zip
name: cmder_wt_mini.zip name: cmder_wt_mini.zip
- name: Upload artifact (cmder.zip) - name: Upload artifact (cmder.zip)
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v5
with: with:
path: build/cmder.zip path: build/cmder.zip
name: cmder.zip name: cmder.zip
if-no-files-found: error if-no-files-found: error
- name: Upload artifact (cmder.7z) - name: Upload artifact (cmder.7z)
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v5
with: with:
path: build/cmder.7z path: build/cmder.7z
name: cmder.7z name: cmder.7z
- name: Upload artifact (cmder_mini.zip) - name: Upload artifact (cmder_mini.zip)
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v5
with: with:
path: build/cmder_mini.zip path: build/cmder_mini.zip
name: cmder_mini.zip name: cmder_mini.zip
- name: Upload artifact (hashes.txt) - name: Upload artifact (hashes.txt)
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v5
with: with:
path: build/hashes.txt path: build/hashes.txt
name: hashes.txt name: hashes.txt

View File

@@ -45,11 +45,11 @@ jobs:
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v5
# Initializes the CodeQL tools for scanning. # Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL - name: Initialize CodeQL
uses: github/codeql-action/init@v3 uses: github/codeql-action/init@v4
with: with:
languages: ${{ matrix.language }} languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file. # If you wish to specify custom queries, you can do so here or in a config file.
@@ -68,6 +68,6 @@ jobs:
run: .\build.ps1 -Compile -verbose run: .\build.ps1 -Compile -verbose
- name: Perform CodeQL Analysis - name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3 uses: github/codeql-action/analyze@v4
with: with:
category: "/language:${{matrix.language}}" category: "/language:${{matrix.language}}"

View File

@@ -38,7 +38,7 @@ jobs:
continue-on-error: false continue-on-error: false
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v5
- name: Initialize vendors - name: Initialize vendors
shell: pwsh shell: pwsh
working-directory: scripts working-directory: scripts
@@ -48,7 +48,7 @@ jobs:
cmd /c vendor\init.bat /v /d /t cmd /c vendor\init.bat /v /d /t
- name: Testing PowerShell - name: Testing PowerShell
run: | run: |
PowerShell.exe -ExecutionPolicy Bypass -NoLogo -NoProfile -Command "Invoke-Expression '. ''vendor\profile.ps1'''" PowerShell.exe -ExecutionPolicy Bypass -NoLogo -NoProfile -Command "$env:CMDER_DEBUG='1'; . 'vendor\profile.ps1'"
- name: Testing Bash - name: Testing Bash
run: | run: |
bash vendor/cmder.sh bash vendor/cmder.sh

View File

@@ -24,7 +24,7 @@ jobs:
pull-requests: write pull-requests: write
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v5
with: with:
fetch-depth: 0 fetch-depth: 0
@@ -51,7 +51,7 @@ jobs:
Set-GHVariable -Name LIST_UPDATED -Value $listUpdated.Trim(', ') Set-GHVariable -Name LIST_UPDATED -Value $listUpdated.Trim(', ')
echo "UPDATE_MESSAGE<<<EOF`n$updateMessage`n<EOF" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8 echo "UPDATE_MESSAGE<<<EOF`n$updateMessage`n<EOF" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8
- uses: peter-evans/create-pull-request@v6 - uses: peter-evans/create-pull-request@v7
if: env.COUNT_UPDATED > 0 if: env.COUNT_UPDATED > 0
with: with:
title: 'Updates to `${{ env.COUNT_UPDATED }}` vendored dependencies' title: 'Updates to `${{ env.COUNT_UPDATED }}` vendored dependencies'

View File

@@ -1,5 +1,17 @@
# Change Log # Change Log
## Unreleased
### Fixes
- Fixes #2940
### Adds
- Cmder for Windows
- Uses Windows and Git for Windows Native Terminals.
- Does not contain a terminal emulator like ConEmu or Windows Terminal.
## [1.3.20](https://github.com/cmderdev/cmder/tree/v1.3.20) (2022-03-18) ## [1.3.20](https://github.com/cmderdev/cmder/tree/v1.3.20) (2022-03-18)
### Changes ### Changes

View File

@@ -352,6 +352,10 @@ Cmder by default comes with a vendored ConEmu installation as the underlying ter
However, Cmder can in fact run in a variety of other terminal emulators, and even integrated IDEs. Assuming you have the latest version of Cmder, follow the following instructions to get Cmder working with your own terminal emulator. However, Cmder can in fact run in a variety of other terminal emulators, and even integrated IDEs. Assuming you have the latest version of Cmder, follow the following instructions to get Cmder working with your own terminal emulator.
⚠ *Note:* Cmder includes built-in support for Windows Terminal directory tracking via OSC 9;9 sequences. This enables "Duplicate Tab" and "Split Pane" features to preserve the current working directory for both `cmd.exe` and PowerShell sessions.
⚠ *Note:* Cmder also includes built-in support for [Windows Terminal shell integration](https://learn.microsoft.com/en-us/windows/terminal/tutorials/shell-integration) via OSC 133 sequences (A, B, C, D) for PowerShell sessions. This enables features like command navigation (jump between commands), command selection, visual command separators, command exit code tracking, and improved command history management in Windows Terminal.
For instructions on how to integrate Cmder with your IDE, please read our [Wiki section](https://github.com/cmderdev/cmder/wiki#cmder-integration). For instructions on how to integrate Cmder with your IDE, please read our [Wiki section](https://github.com/cmderdev/cmder/wiki#cmder-integration).
## Upgrading ## Upgrading

View File

@@ -188,6 +188,9 @@
<ItemGroup> <ItemGroup>
<ClCompile Include="src\CmderLauncher.cpp" /> <ClCompile Include="src\CmderLauncher.cpp" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Image Include="..\icons\cmder.ico" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets"> <ImportGroup Label="ExtensionTargets">
</ImportGroup> </ImportGroup>

View File

@@ -105,7 +105,7 @@ bool FileExists(const wchar_t * filePath)
return false; return false;
} }
void StartCmder(std::wstring path = L"", bool is_single_mode = false, std::wstring taskName = L"", std::wstring title = L"", std::wstring iconPath = L"", std::wstring cfgRoot = L"", bool use_user_cfg = true, std::wstring conemu_args = L"", bool admin = false) void StartCmder(std::wstring path = L"", bool is_single_mode = false, std::wstring taskName = L"", std::wstring title = L"", std::wstring iconPath = L"", std::wstring cfgRoot = L"", bool use_user_cfg = true, std::wstring conemu_args = L"")
{ {
#if USE_TASKBAR_API #if USE_TASKBAR_API
wchar_t appId[MAX_PATH] = { 0 }; wchar_t appId[MAX_PATH] = { 0 };
@@ -312,7 +312,7 @@ void StartCmder(std::wstring path = L"", bool is_single_mode = false, std::wstr
MessageBox(NULL, MessageBox(NULL,
(GetLastError() == ERROR_ACCESS_DENIED) (GetLastError() == ERROR_ACCESS_DENIED)
? L"Failed to copy vendor/windows-terminal/settings/settings.json file to config/windows_terminal_%COMPUTERNAME%_settings.json! Access Denied." ? L"Failed to copy vendor/windows-terminal/settings/settings.json file to config/windows_terminal_%COMPUTERNAME%_settings.json! Access Denied."
: L"Failed to copy vendor/windows-terminal/settings/settings.json file to config/windows_teerminal_%COMPUTERNAME%_settigns.json!", MB_TITLE, MB_ICONSTOP); : L"Failed to copy vendor/windows-terminal/settings/settings.json file to config/windows_terminal_%COMPUTERNAME%_settigns.json!", MB_TITLE, MB_ICONSTOP);
exit(1); exit(1);
} }
else if (PathFileExists(conEmuDir)) else if (PathFileExists(conEmuDir))
@@ -360,7 +360,7 @@ void StartCmder(std::wstring path = L"", bool is_single_mode = false, std::wstr
MessageBox(NULL, MessageBox(NULL,
(GetLastError() == ERROR_ACCESS_DENIED) (GetLastError() == ERROR_ACCESS_DENIED)
? L"Failed to copy vendor/windows-terminal/settings/settings.json file to config/windows_terminal_settings.json! Access Denied." ? L"Failed to copy vendor/windows-terminal/settings/settings.json file to config/windows_terminal_settings.json! Access Denied."
: L"Failed to copy vendor/windows-terminal/settings/settings.json file to config/windows_teerminal_settigns.json!", MB_TITLE, MB_ICONSTOP); : L"Failed to copy vendor/windows-terminal/settings/settings.json file to config/windows_terminal_settigns.json!", MB_TITLE, MB_ICONSTOP);
exit(1); exit(1);
} }
else if (PathFileExists(conEmuDir)) else if (PathFileExists(conEmuDir))
@@ -651,38 +651,7 @@ void StartCmder(std::wstring path = L"", bool is_single_mode = false, std::wstr
#endif #endif
PROCESS_INFORMATION pi; PROCESS_INFORMATION pi;
// MessageBox(NULL, terminalPath, _T("Error"), MB_OK); if (!CreateProcess(terminalPath, args, NULL, NULL, false, 0, NULL, NULL, &si, &pi))
// MessageBox(NULL, args, _T("Error"), MB_OK);
// Let's try to rerun as Administrator
SHELLEXECUTEINFO sei = { sizeof(sei) };
sei.fMask = SEE_MASK_NOASYNC | SEE_MASK_NOCLOSEPROCESS;
sei.lpVerb = L"runas";
sei.lpFile = terminalPath;
sei.lpParameters = args;
sei.nShow = SW_SHOWNORMAL;
if (admin && ShellExecuteEx(&sei))
{
if (!sei.hProcess)
{
Sleep(500);
_ASSERTE(sei.hProcess != nullptr);
}
if (sei.hProcess)
{
WaitForSingleObject(sei.hProcess, INFINITE);
}
// int nZone = 0;
// if (!HasZoneIdentifier(lsFile, nZone)
// || (nZone != 0 /*LocalComputer*/))
// {
// // Assuming that elevated copy has fixed all zone problems
// break;
// }
}
else if (!CreateProcess(terminalPath, args, NULL, NULL, false, 0, NULL, NULL, &si, &pi))
{ {
if (PathFileExists(windowsTerminalDir)) if (PathFileExists(windowsTerminalDir))
{ {
@@ -813,7 +782,6 @@ struct cmderOptions
std::wstring cmderIcon = L""; std::wstring cmderIcon = L"";
std::wstring cmderRegScope = L"USER"; std::wstring cmderRegScope = L"USER";
std::wstring cmderTerminalArgs = L""; std::wstring cmderTerminalArgs = L"";
bool cmderAdmin = false;
bool cmderSingle = false; bool cmderSingle = false;
bool cmderUserCfg = true; bool cmderUserCfg = true;
bool registerApp = false; bool registerApp = false;
@@ -902,10 +870,6 @@ cmderOptions GetOption()
{ {
cmderOptions.cmderUserCfg = false; cmderOptions.cmderUserCfg = false;
} }
else if (_wcsicmp(L"/a", szArgList[i]) == 0 && !PathFileExists(windowsTerminalDir) && !PathFileExists(conEmuDir))
{
cmderOptions.cmderAdmin = true;
}
else if (_wcsicmp(L"/register", szArgList[i]) == 0) else if (_wcsicmp(L"/register", szArgList[i]) == 0)
{ {
cmderOptions.registerApp = true; cmderOptions.registerApp = true;
@@ -1042,7 +1006,8 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
} }
else else
{ {
StartCmder(cmderOptions.cmderStart, cmderOptions.cmderSingle, cmderOptions.cmderTask, cmderOptions.cmderTitle, cmderOptions.cmderIcon, cmderOptions.cmderCfgRoot, cmderOptions.cmderUserCfg, cmderOptions.cmderTerminalArgs, cmderOptions.cmderAdmin); } StartCmder(cmderOptions.cmderStart, cmderOptions.cmderSingle, cmderOptions.cmderTask, cmderOptions.cmderTitle, cmderOptions.cmderIcon, cmderOptions.cmderCfgRoot, cmderOptions.cmderUserCfg, cmderOptions.cmderTerminalArgs);
}
return 0; return 0;
} }

View File

@@ -60,7 +60,7 @@ END
// Icon with lowest ID value placed first to ensure application icon // Icon with lowest ID value placed first to ensure application icon
// remains consistent on all systems. // remains consistent on all systems.
IDI_CMDER ICON "..\\..\\icons\\cmder.ico" IDI_CMDER ICON "..\\..\\icons\\cmder.ico"
#endif // English (United States) resources #endif // not APSTUDIO_INVOKED
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////

View File

@@ -6,7 +6,7 @@ STRINGTABLE
{ {
IDS_TITLE "Cmder Launcher" IDS_TITLE "Cmder Launcher"
IDS_SWITCHES L"Valid options:\n\n /a Admin - Native Terminals ONLY!\n /c [CMDER User Root Path]\n /task [Windows Terminal Profile/ConEmu Task Name]\n /icon [CMDER Icon Path] - ConEmu ONLY!\n [/start [Start in Path] | [Start in Path]]\n /single - ConEmu ONLY!\n /m\n -- [ConEmu/Windows Terminal extra arguments]\n\nNote: '-- [...]' must be the last argument!\n\nor, either:\n /register [USER | ALL]\n /unregister [USER | ALL]" IDS_SWITCHES L"Valid options:\n\n /c [CMDER User Root Path]\n /task [Windows Terminal Profile/ConEmu Task Name]\n /icon [CMDER Icon Path] - ConEmu ONLY!\n [/start [Start in Path] | [Start in Path]]\n /single - ConEmu ONLY!\n /m\n -- [ConEmu/Windows Terminal extra arguments]\n\nNote: '-- [...]' must be the last argument!\n\nor, either:\n /register [USER | ALL]\n /unregister [USER | ALL]"
} }
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////

View File

@@ -27,7 +27,7 @@
.EXAMPLE .EXAMPLE
.\build.ps1 -SourcesPath '~/custom/vendors.json' .\build.ps1 -SourcesPath '~/custom/vendors.json'
Build cmder with your own packages. See vendor/sources.json for the syntax you need to copy. Build Cmder with your own packages. See vendor/sources.json for the syntax you need to copy.
.NOTES .NOTES
AUTHORS AUTHORS
Samuel Vasko, Jack Bennett Samuel Vasko, Jack Bennett
@@ -129,8 +129,9 @@ if (-not $noVendor) {
else { $WinTermSettingsJson = "" } else { $WinTermSettingsJson = "" }
# Kill ssh-agent.exe if it is running from the $env:cmder_root we are building # Kill ssh-agent.exe if it is running from the $env:cmder_root we are building
$cmder_folder = $cmder_root.toString()
foreach ($ssh_agent in $(Get-Process ssh-agent -ErrorAction SilentlyContinue)) { foreach ($ssh_agent in $(Get-Process ssh-agent -ErrorAction SilentlyContinue)) {
if ([string]$($ssh_agent.path) -Match [string]$cmder_root.replace('\', '\\')) { if ([string]$($ssh_agent.path) -Match $cmder_folder.Replace('\', '\\')) {
Write-Verbose $("Stopping " + $ssh_agent.path + "!") Write-Verbose $("Stopping " + $ssh_agent.path + "!")
Stop-Process $ssh_agent.id Stop-Process $ssh_agent.id
} }
@@ -138,11 +139,11 @@ if (-not $noVendor) {
foreach ($s in $sources) { foreach ($s in $sources) {
if ($terminal -eq "none") { if ($terminal -eq "none") {
return continue
} elseif ($s.name -eq "conemu-maximus5" -and $terminal -eq "windows-terminal") { } elseif ($s.name -eq "conemu-maximus5" -and $terminal -eq "windows-terminal") {
return continue
} elseif ($s.name -eq "windows-terminal" -and $terminal -eq "conemu-maximus5") { } elseif ($s.name -eq "windows-terminal" -and $terminal -eq "conemu-maximus5") {
return continue
} }
Write-Verbose "Getting vendored $($s.name) $($s.version)..." Write-Verbose "Getting vendored $($s.name) $($s.version)..."
@@ -158,9 +159,9 @@ if (-not $noVendor) {
# Make Embedded Windows Terminal Portable # Make Embedded Windows Terminal Portable
if ($s.name -eq "windows-terminal") { if ($s.name -eq "windows-terminal") {
$windowTerminalFiles = resolve-path ($saveTo + "\" + $s.name + "\terminal*") $windowTerminalFiles = resolve-path ($saveTo + "\" + $s.name + "\terminal*")
move-item -ErrorAction SilentlyContinue $windowTerminalFiles\* $s.name >$null Move-Item -ErrorAction SilentlyContinue $windowTerminalFiles\* $s.name >$null
remove-item -ErrorAction SilentlyContinue $windowTerminalFiles >$null Remove-Item -ErrorAction SilentlyContinue $windowTerminalFiles >$null
write-verbose "Making Windows Terminal Portable..." Write-Verbose "Making Windows Terminal Portable..."
New-Item -Type Directory -Path (Join-Path $saveTo "/windows-terminal/settings") -ErrorAction SilentlyContinue >$null New-Item -Type Directory -Path (Join-Path $saveTo "/windows-terminal/settings") -ErrorAction SilentlyContinue >$null
New-Item -Type File -Path (Join-Path $saveTo "/windows-terminal/.portable") -ErrorAction SilentlyContinue >$null New-Item -Type File -Path (Join-Path $saveTo "/windows-terminal/.portable") -ErrorAction SilentlyContinue >$null
} }

View File

@@ -2,18 +2,18 @@
.Synopsis .Synopsis
Pack Cmder Pack Cmder
.DESCRIPTION .DESCRIPTION
Use this script to pack cmder into release archives Use this script to pack Cmder into release archives
You will need to make this script executable by setting your Powershell Execution Policy to Remote signed You will need to make this script executable by setting your Powershell Execution Policy to Remote signed
Then unblock the script for execution with UnblockFile .\pack.ps1 Then unblock the script for execution with UnblockFile .\pack.ps1
.EXAMPLE .EXAMPLE
.\pack.ps1 .\pack.ps1
Creates default archives for cmder Creates default archives for Cmder
.EXAMPLE .EXAMPLE
.\pack.ps1 -verbose .\pack.ps1 -verbose
Creates default archives for cmder with plenty of information Creates default archives for Cmder with plenty of information
.NOTES .NOTES
AUTHORS AUTHORS
Samuel Vasko, Jack Bennett, Martin Kemp Samuel Vasko, Jack Bennett, Martin Kemp

View File

@@ -32,7 +32,11 @@ Param(
# -whatif switch to not actually make changes # -whatif switch to not actually make changes
# Path to the vendor configuration source file # Path to the vendor configuration source file
[string]$sourcesPath = "$PSScriptRoot\..\vendor\sources.json" [string]$sourcesPath = "$PSScriptRoot\..\vendor\sources.json",
# Include pre-release versions (RC, beta, alpha, etc.)
# By default, only stable releases are considered
[switch]$IncludePrerelease = $false
) )
# Get the root directory of the cmder project. # Get the root directory of the cmder project.
@@ -79,11 +83,39 @@ function Match-Filenames {
return $position return $position
} }
# Checks if a release is a pre-release based on GitHub API flag and version tag keywords
# Pre-release keywords include: -rc (release candidate), -beta, -alpha, -preview, -pre
function Test-IsPrerelease {
param (
[Parameter(Mandatory = $true)]
$release
)
# Check if marked as pre-release by GitHub
if ($release.prerelease -eq $true) {
return $true
}
# Check for common pre-release keywords in tag name
# This catches versions like v2.50.0-rc, v1.0.0-beta, v1.0.0-alpha, etc.
$prereleaseKeywords = @('-rc', '-beta', '-alpha', '-preview', '-pre')
foreach ($keyword in $prereleaseKeywords) {
if ($release.tag_name -ilike "*$keyword*") {
return $true
}
}
return $false
}
# Uses the GitHub api in order to fetch the current download links for the latest releases of the repo. # Uses the GitHub api in order to fetch the current download links for the latest releases of the repo.
function Fetch-DownloadUrl { function Fetch-DownloadUrl {
param ( param (
[Parameter(Mandatory = $true)] [Parameter(Mandatory = $true)]
$urlStr $urlStr,
[Parameter(Mandatory = $false)]
[bool]$includePrerelease = $false
) )
$url = [uri] $urlStr $url = [uri] $urlStr
@@ -127,6 +159,13 @@ function Fetch-DownloadUrl {
} }
:loop foreach ($i in $info) { :loop foreach ($i in $info) {
# Skip pre-release versions unless explicitly included
# Pre-releases include RC (Release Candidate), beta, alpha, and other test versions
if (-not $includePrerelease -and (Test-IsPrerelease $i)) {
Write-Verbose "Skipping pre-release version: $($i.tag_name)"
continue
}
if (-not ($i.assets -is [array])) { if (-not ($i.assets -is [array])) {
continue continue
} }
@@ -164,15 +203,29 @@ function Fetch-DownloadUrl {
# Special case for archive downloads of repository # Special case for archive downloads of repository
if (($null -eq $downloadLinks) -or (-not $downloadLinks)) { if (($null -eq $downloadLinks) -or (-not $downloadLinks)) {
if ((($p | ForEach-Object { $_.Trim('/') }) -contains "archive") -and $info[0].tag_name) { if ((($p | ForEach-Object { $_.Trim('/') }) -contains "archive")) {
# Find the first release that matches our pre-release filtering criteria
$selectedRelease = $null
foreach ($release in $info) {
# Apply the same filtering logic
if (-not $includePrerelease -and (Test-IsPrerelease $release)) {
continue
}
# Use the first release that passes the filter
$selectedRelease = $release
break
}
if ($selectedRelease -and $selectedRelease.tag_name) {
for ($i = 0; $i -lt $p.Length; $i++) { for ($i = 0; $i -lt $p.Length; $i++) {
if ($p[$i].Trim('/') -eq "archive") { if ($p[$i].Trim('/') -eq "archive") {
$p[$i + 1] = $info[0].tag_name + ".zip" $p[$i + 1] = $selectedRelease.tag_name + ".zip"
$downloadLinks = $url.Scheme + "://" + $url.Host + ($p -join '') $downloadLinks = $url.Scheme + "://" + $url.Host + ($p -join '')
return $downloadLinks return $downloadLinks
} }
} }
} }
}
return '' return ''
} }
@@ -215,7 +268,7 @@ foreach ($s in $sources) {
Write-Verbose "Old Link: $($s.url)" Write-Verbose "Old Link: $($s.url)"
$downloadUrl = Fetch-DownloadUrl $s.url $downloadUrl = Fetch-DownloadUrl $s.url -includePrerelease $IncludePrerelease
if (($null -eq $downloadUrl) -or ($downloadUrl -eq '')) { if (($null -eq $downloadUrl) -or ($downloadUrl -eq '')) {
Write-Verbose "No new links were found" Write-Verbose "No new links were found"

View File

@@ -172,13 +172,13 @@ function Register-Cmder() {
# Text for the context menu item. # Text for the context menu item.
$MenuText = "Cmder Here" $MenuText = "Cmder Here"
, # Defaults to the current cmder directory when run from cmder. , # Defaults to the current Cmder directory when run from Cmder.
$PathToExe = (Join-Path $env:CMDER_ROOT "cmder.exe") $PathToExe = (Join-Path $env:CMDER_ROOT "cmder.exe")
, # Commands the context menu will execute. , # Commands the context menu will execute.
$Command = "%V" $Command = "%V"
, # Defaults to the icons folder in the cmder package. , # Defaults to the icons folder in the Cmder package.
$icon = (Split-Path $PathToExe | Join-Path -ChildPath 'icons/cmder.ico') $icon = (Split-Path $PathToExe | Join-Path -ChildPath 'icons/cmder.ico')
) )
Begin Begin

3
vendor/bin/create-cmdercfg.cmd vendored Normal file
View File

@@ -0,0 +1,3 @@
@echo off
powershell -executionpolicy bypass -f "%cmder_root%\vendor\bin\create-cmdercfg.ps1" -shell cmd -outfile "%CMDER_CONFIG_DIR%\user_init.cmd"

154
vendor/clink.lua vendored
View File

@@ -7,7 +7,7 @@
-- luacheck: globals uah_color cwd_color lamb_color clean_color dirty_color conflict_color unknown_color -- luacheck: globals uah_color cwd_color lamb_color clean_color dirty_color conflict_color unknown_color
-- luacheck: globals prompt_homeSymbol prompt_lambSymbol prompt_type prompt_useHomeSymbol prompt_useUserAtHost -- luacheck: globals prompt_homeSymbol prompt_lambSymbol prompt_type prompt_useHomeSymbol prompt_useUserAtHost
-- luacheck: globals prompt_singleLine prompt_includeVersionControl -- luacheck: globals prompt_singleLine prompt_includeVersionControl
-- luacheck: globals prompt_overrideGitStatusOptIn prompt_overrideSvnStatusOptIn -- luacheck: globals prompt_overrideGitStatusOptIn
-- luacheck: globals clink io.popenyield os.isdir settings.get -- luacheck: globals clink io.popenyield os.isdir settings.get
-- At first, load the original clink.lua file -- At first, load the original clink.lua file
@@ -350,13 +350,8 @@ end
-- @return {false|mercurial branch information} -- @return {false|mercurial branch information}
--- ---
local function get_hg_branch() local function get_hg_branch()
-- Return the branch information. The default is to get just the -- Return the branch information.
-- branch name, but you could e.g. use the "hg-prompt" extension to local file = io.popen("hg branch 2>nul")
-- get more information, such as any applied mq patches. Here's an
-- example of that:
-- local cmd = "hg prompt \"{branch}{status}{|{patch}}{update}\""
local cmd = "hg branch 2>nul"
local file = io.popen(cmd)
if not file then if not file then
return false return false
end end
@@ -424,12 +419,33 @@ local function get_git_status()
return { status = is_status, conflict = conflict_found } return { status = is_status, conflict = conflict_found }
end end
---
-- Get the status of working dir
-- @return {bool}
---
local function get_hg_status()
-- The default is to just use the branch name, but you could e.g. use the
-- "hg-prompt" extension to get more information, such as any applied mq
-- patches. Here's an example of that:
-- "hg prompt \"{branch}{status}{|{patch}}{update}\""
local pipe = io_popenyield("hg status -amrd 2>&1")
if not pipe then
return { error = true }
end
local output = pipe:read('*all')
pipe:close()
local dirty = (output ~= nil and output ~= "")
return { clean = not dirty }
end
--- ---
-- Get the status of working dir -- Get the status of working dir
-- @return {bool} -- @return {bool}
--- ---
local function get_svn_status() local function get_svn_status()
local file = io_popenyield("svn status -q") local file = io_popenyield("svn status -q 2>nul")
if not file then if not file then
return { error = true } return { error = true }
end end
@@ -520,14 +536,6 @@ local function git_prompt_filter()
return false return false
end end
-- Colors for git status
local colors = {
clean = get_clean_color(),
dirty = get_dirty_color(),
conflict = get_conflict_color(),
nostatus = get_unknown_color()
}
local git_dir = get_git_dir() local git_dir = get_git_dir()
local color local color
if git_dir then if git_dir then
@@ -547,18 +555,19 @@ local function git_prompt_filter()
local gitConflict = gitInfo.conflict local gitConflict = gitInfo.conflict
if gitStatus == nil then if gitStatus == nil then
color = colors.nostatus color = get_unknown_color()
elseif gitStatus then elseif gitStatus then
color = colors.clean color = get_clean_color()
else else
color = colors.dirty color = get_dirty_color()
end end
if gitConflict then if gitConflict then
color = colors.conflict color = get_conflict_color()
end end
clink.prompt.value = gsub_plain(clink.prompt.value, "{git}", " "..color.."("..branch..")") local result = " "..color.."("..branch..")"
clink.prompt.value = gsub_plain(clink.prompt.value, "{git}", result)
return false return false
end end
end end
@@ -568,6 +577,18 @@ local function git_prompt_filter()
return false return false
end end
local function get_hg_info_table()
local info = clink_promptcoroutine(function ()
return get_hg_status() or {}
end)
if not info then
info = cached_info.hg_info or {}
else
cached_info.hg_info = info
end
return info
end
local function hg_prompt_filter() local function hg_prompt_filter()
-- Don't do any hg processing if the prompt doesn't want to show hg info. -- Don't do any hg processing if the prompt doesn't want to show hg info.
@@ -577,33 +598,30 @@ local function hg_prompt_filter()
local hg_dir = get_hg_dir() local hg_dir = get_hg_dir()
if hg_dir then if hg_dir then
-- Colors for mercurial status local branch = get_hg_branch()
local colors = { if branch and
clean = get_clean_color(),
dirty = get_dirty_color(),
nostatus = get_unknown_color()
}
local output = get_hg_branch()
-- strip the trailing newline from the branch name
local n = #output
while n > 0 and output:find("^%s", n) do n = n - 1 end
local branch = output:sub(1, n)
if branch ~= nil and
string.sub(branch,1,7) ~= "abort: " and -- not an HG working copy string.sub(branch,1,7) ~= "abort: " and -- not an HG working copy
(not string.find(branch, "is not recognized")) then -- 'hg' not in path (not string.find(branch, "is not recognized")) then -- 'hg' not in path
local color = colors.clean -- If in a different repo or branch than last time, discard cached info
if cached_info.hg_dir ~= hg_dir or cached_info.hg_branch ~= branch then
local pipe = io.popen("hg status -amrd 2>&1") cached_info.hg_info = nil
if pipe then cached_info.hg_dir = hg_dir
output = pipe:read('*all') cached_info.hg_branch = branch
pipe:close()
if output ~= nil and output ~= "" then color = colors.dirty end
end end
local result = color .. "(" .. branch .. ")" local hgInfo = get_hg_info_table()
clink.prompt.value = gsub_plain(clink.prompt.value, "{hg}", " "..result)
local color
if not hgInfo or hgInfo.error then
color = get_unknown_color()
elseif hgInfo.clean then
color = get_clean_color()
else
color = get_dirty_color()
end
local result = " "..color.."("..branch..")"
clink.prompt.value = gsub_plain(clink.prompt.value, "{hg}", result)
return false return false
end end
end end
@@ -612,6 +630,18 @@ local function hg_prompt_filter()
clink.prompt.value = gsub_plain(clink.prompt.value, "{hg}", "") clink.prompt.value = gsub_plain(clink.prompt.value, "{hg}", "")
end end
local function get_svn_info_table()
local info = clink_promptcoroutine(function ()
return get_svn_status() or {}
end)
if not info then
info = cached_info.svn_info or {}
else
cached_info.svn_info = info
end
return info
end
local function svn_prompt_filter() local function svn_prompt_filter()
-- Don't do any svn processing if the prompt doesn't want to show svn info. -- Don't do any svn processing if the prompt doesn't want to show svn info.
@@ -619,13 +649,6 @@ local function svn_prompt_filter()
return false return false
end end
-- Colors for svn status
local colors = {
clean = get_clean_color(),
dirty = get_dirty_color(),
nostatus = get_unknown_color()
}
local svn_dir = get_svn_dir() local svn_dir = get_svn_dir()
if svn_dir then if svn_dir then
-- if we're inside of svn repo then try to detect current branch -- if we're inside of svn repo then try to detect current branch
@@ -637,29 +660,16 @@ local function svn_prompt_filter()
cached_info.svn_dir = svn_dir cached_info.svn_dir = svn_dir
cached_info.svn_branch = branch cached_info.svn_branch = branch
end end
-- Get the svn status using coroutine if available and option is enabled. Otherwise use a blocking call
local svnStatus local svnInfo = get_svn_info_table()
if clink.promptcoroutine and io.popenyield and settings.get("prompt.async") and prompt_overrideSvnStatusOptIn then -- luacheck: no max line length
svnStatus = clink_promptcoroutine(function ()
return get_svn_status()
end)
-- If the status result is pending, use the cached version instead, otherwise store it to the cache
if svnStatus == nil then
svnStatus = cached_info.svn_info
else
cached_info.svn_info = svnStatus
end
else
svnStatus = get_svn_status()
end
local color local color
if not svnStatus or svnStatus.error then if not svnInfo or svnInfo.error then
color = colors.nostatus color = get_unknown_color()
elseif svnStatus.clean then elseif svnInfo.clean then
color = colors.clean color = get_clean_color()
else else
color = colors.dirty color = get_dirty_color()
end end
clink.prompt.value = gsub_plain(clink.prompt.value, "{svn}", " "..color.."("..branch..")") clink.prompt.value = gsub_plain(clink.prompt.value, "{svn}", " "..color.."("..branch..")")

View File

@@ -38,25 +38,20 @@ prompt_includeVersionControl = true
-- NOTE: This only takes effect if using Clink v1.2.10 or higher. -- NOTE: This only takes effect if using Clink v1.2.10 or higher.
prompt_overrideGitStatusOptIn = false prompt_overrideGitStatusOptIn = false
-- OPTIONAL. If true then always ignore the cmder.status and cmder.cmdstatus svn config settings and run the svn prompt commands in the background.
-- default is false
-- NOTE: This only takes effect if using Clink v1.2.10 or higher.
prompt_overrideSvnStatusOptIn = false
-- Prompt Attributes -- Prompt Attributes
-- --
-- Colors: https://github.com/cmderdev/cmder/wiki/Customization#list-of-colors -- Colors: https://github.com/cmderdev/cmder/wiki/Customization#list-of-colors
-- Effects: https://github.com/cmderdev/cmder/wiki/Customization#list-of-effects -- Effects: https://github.com/cmderdev/cmder/wiki/Customization#list-of-effects
-- --
-- Green: "\x1b[1;33;49m" -- Green: "\x1b[1;32;49m"
-- Yellow: "\x1b[1;32;49m" -- Yellow: "\x1b[1;33;49m"
-- Light Grey: "\x1b[1;30;49m" -- Light Grey: "\x1b[1;30;49m"
-- Prompt Element Colors -- Prompt Element Colors
uah_color = "\x1b[1;33;49m" -- Green = uah = [user]@[hostname] uah_color = "\x1b[1;33;49m" -- Yellow uah = [user]@[hostname]
cwd_color = "\x1b[1;32;49m" -- Yellow cwd = Current Working Directory cwd_color = "\x1b[1;32;49m" -- Green cwd = Current Working Directory
lamb_color = "\x1b[1;30;49m" -- Light Grey = Lambda Color lamb_color = "\x1b[1;30;49m" -- Light Grey = Lambda Color
clean_color = "\x1b[37;1m" clean_color = "\x1b[37;1m"
dirty_color = "\x1b[33;3m" -- Yellow, Italic dirty_color = "\x1b[33;3m" -- Yellow, Italic
conflict_color = "\x1b[31;1m" -- Red, Bold conflict_color = "\x1b[31;1m" -- Red, Bold
unknown_color = "\x1b[37;1m" -- White = No VCS Status Branch Color unknown_color = "\x1b[37;1m" -- White, Bold = No VCS Status Branch Color

92
vendor/git-prompt.sh vendored
View File

@@ -1,24 +1,33 @@
# Returns 1 if git status for Cmder is disabled, otherwise returns 0
function getGitStatusSetting() { function getGitStatusSetting() {
gitStatusSetting=$(git --no-pager config -l 2>/dev/null) local gitConfig
if [[ -n ${gitStatusSetting} ]] && [[ ${gitStatusSetting} =~ cmder.status=false ]] || [[ ${gitStatusSetting} =~ cmder.shstatus=false ]] # Get all git config entries for the current repository without pager
gitConfig=$(git --no-pager config -l 2>/dev/null) || return 0 # treat failure as enabled
# Check if git status display for Cmder is disabled via config
# Matches: cmder.status=false or cmder.shstatus=false (Bash-specific)
if [[ $gitConfig =~ (^|$'\n')cmder\.(sh)?status=false($|$'\n') ]]
then then
echo false return 1 # disabled
else
echo true
fi fi
return 0
} }
# Prints current branch or detached HEAD short commit hash
function getSimpleGitBranch() { function getSimpleGitBranch() {
gitDir=$(git rev-parse --git-dir 2>/dev/null) local gitDir
if [ -z "$gitDir" ]; then gitDir=$(git rev-parse --git-dir 2>/dev/null) || return 0
return 0
fi
headContent=$(< "$gitDir/HEAD") local headFile="$gitDir/HEAD"
if [[ "$headContent" == "ref: refs/heads/"* ]] [ -f "$headFile" ] || return 0
local headContent
headContent=$(< "$headFile")
if [[ "$headContent" =~ ^ref:\ refs/heads/(.+)$ ]]
then then
echo " (${headContent:16})" echo " (${BASH_REMATCH[1]})"
else else
echo " (HEAD detached at ${headContent:0:7})" echo " (HEAD detached at ${headContent:0:7})"
fi fi
@@ -33,18 +42,50 @@ fi
if test -f ~/.config/git/git-prompt.sh if test -f ~/.config/git/git-prompt.sh
then then
if [[ $(getGitStatusSetting) == true ]] if getGitStatusSetting
then then
. ~/.config/git/git-prompt.sh . ~/.config/git/git-prompt.sh
fi fi
else else
PS1='\[\033]0;$TITLEPREFIX:$PWD\007\]' # set window title
# PS1="$PS1"'\n' # new line # Setup OSC 133 shell integration for Windows Terminal
PS1="$PS1"'\[\033[32m\]' # change to green if [ -n "$WT_SESSION" ]; then
__cmder_prompt_command() {
local exit_code=$?
# Emit OSC 133;D to mark the end of command execution with exit code
printf '\e]133;D;%s\a' "$exit_code"
return $exit_code
}
__cmder_preexec() {
# Emit OSC 133;C to mark the start of command execution
printf '\e]133;C\a'
}
# Append to PROMPT_COMMAND to emit sequences just before each prompt
if [ -z "$PROMPT_COMMAND" ]; then
PROMPT_COMMAND="__cmder_prompt_command"
else
PROMPT_COMMAND="__cmder_prompt_command;$PROMPT_COMMAND"
fi
# Use DEBUG trap to emit OSC 133;C before command execution
trap '__cmder_preexec' DEBUG
fi
# Source: github.com/git-for-windows/build-extra/blob/main/git-extra/git-prompt.sh
PS1='\[\033]0;${TITLEPREFIX:+$TITLEPREFIX:}${PWD//[^[:ascii:]]/?}\007\]' # set window title to TITLEPREFIX (if set) and current working directory
# PS1="$PS1"'\n' # new line (disabled)
if [ -n "$WT_SESSION" ]; then
# Emit OSC 133;A to mark the start of prompt
PS1="$PS1"'\e]133;A\a'
fi
PS1="$PS1"'\[\033[32m\]' # change to green and bold
PS1="$PS1"'\u@\h ' # user@host<space> PS1="$PS1"'\u@\h ' # user@host<space>
# PS1="$PS1"'\[\033[35m\]' # change to purple PS1="$PS1${MSYSTEM:+\[\033[35m\]$MSYSTEM }" # show MSYSTEM in purple (if set)
# PS1="$PS1"'$MSYSTEM ' # show MSYSTEM PS1="$PS1"'\[\033[1;33m\]' # change to dark yellow in bold
PS1="$PS1"'\[\033[33m\]' # change to brownish yellow
PS1="$PS1"'\w' # current working directory PS1="$PS1"'\w' # current working directory
if test -z "$WINELOADERNOEXEC" if test -z "$WINELOADERNOEXEC"
then then
@@ -55,7 +96,7 @@ else
if test -f "$COMPLETION_PATH/git-prompt.sh" if test -f "$COMPLETION_PATH/git-prompt.sh"
then then
. "$COMPLETION_PATH/git-completion.bash" . "$COMPLETION_PATH/git-completion.bash"
if [[ $(getGitStatusSetting) == true ]] if getGitStatusSetting
then then
. "$COMPLETION_PATH/git-prompt.sh" . "$COMPLETION_PATH/git-prompt.sh"
PS1="$PS1"'\[\033[36m\]' # change color to cyan PS1="$PS1"'\[\033[36m\]' # change color to cyan
@@ -66,9 +107,16 @@ else
fi fi
fi fi
fi fi
PS1="$PS1"'\[\033[0m\]' # change color PS1="$PS1"'\[\033[0m\]' # reset color
PS1="$PS1"'\n' # new line PS1="$PS1"'\n' # new line
PS1="$PS1"'λ ' # prompt: always λ PS1="$PS1"'\[\033[30;1m\]' # change color to grey in bold
PS1="$PS1"'λ ' # prompt: Cmder uses λ
PS1="$PS1"'\[\033[0m\]' # reset color
if [ -n "$WT_SESSION" ]; then
# Emit OSC 133;B to mark the end of prompt
PS1="$PS1"'\[\e]133;B\a\]'
fi
fi fi
MSYS2_PS1="$PS1" # for detection by MSYS2 SDK's bash.basrc MSYS2_PS1="$PS1" # for detection by MSYS2 SDK's bash.basrc

7
vendor/init.bat vendored
View File

@@ -239,6 +239,9 @@ goto :SKIP_CLINK
:: Revert back to plain cmd.exe prompt without clink :: Revert back to plain cmd.exe prompt without clink
prompt $E[1;32;49m$P$S$_$E[1;30;49mλ$S$E[0m prompt $E[1;32;49m$P$S$_$E[1;30;49mλ$S$E[0m
:: Add Windows Terminal shell integration support (OSC 133 sequences)
if defined WT_SESSION (prompt $e]133;D$e\$e]133;A$e\$e]9;9;$P$e\%PROMPT%$e]133;B$e\)
chcp %cp%>nul chcp %cp%>nul
:CLINK_FINISH :CLINK_FINISH
@@ -390,7 +393,7 @@ setlocal enabledelayedexpansion
if defined git_locale ( if defined git_locale (
REM %print_debug% init.bat "Env Var - git_locale=!git_locale!" REM %print_debug% init.bat "Env Var - git_locale=!git_locale!"
if not defined LANG ( if not defined LANG (
for /F "delims=" %%F in ('!git_locale! -uU 2') do ( for /F "delims=" %%F in ('"!git_locale!" -uU 2') do (
set "LANG=%%F" set "LANG=%%F"
) )
) )
@@ -540,7 +543,7 @@ if "%CMDER_ALIASES%" == "1" if exist "%CMDER_ROOT%\bin\alias.bat" if exist "%CMD
set initialConfig= set initialConfig=
if not exist "%CMDER_CONFIG_DIR%\user_init.cmd" ( if not exist "%CMDER_CONFIG_DIR%\user_init.cmd" (
powershell -f "%cmder_root%\vendor\bin\create-cmdercfg.ps1" -shell cmd -outfile "%CMDER_CONFIG_DIR%\user_init.cmd" powershell -executionpolicy bypass -f "%cmder_root%\vendor\bin\create-cmdercfg.ps1" -shell cmd -outfile "%CMDER_CONFIG_DIR%\user_init.cmd"
if not exist "%CMDER_ROOT%\config\user_init.cmd" ( if not exist "%CMDER_ROOT%\config\user_init.cmd" (
%print_error% "Failed to generate Cmder config" %print_error% "Failed to generate Cmder config"

View File

@@ -4,7 +4,7 @@ set lib_base=call "%~dp0lib_base.cmd"
if "%~1" == "/h" ( if "%~1" == "/h" (
%lib_base% help "%~0" %lib_base% help "%~0"
) else if "%1" neq "" ( ) else if "%~1" neq "" (
call :%* call :%*
) )

View File

@@ -14,7 +14,7 @@ if %fast_init% gtr %verbose_output% if %fast_init% gtr %debug_output% exit /b
if "%~1" == "/h" ( if "%~1" == "/h" (
%lib_base% help "%~0" %lib_base% help "%~0"
) else if "%1" neq "" ( ) else if "%~1" neq "" (
call :%* call :%*
) )

View File

@@ -1,12 +1,12 @@
@echo off @echo off
call "%~dp0lib_base.cmd" call "%~dp0lib_base.cmd"
call "%%~dp0lib_console.cmd" call "%~dp0lib_console.cmd"
set lib_git=call "%~dp0lib_git.cmd" set lib_git=call "%~dp0lib_git.cmd"
if "%~1" == "/h" ( if "%~1" == "/h" (
%lib_base% help "%~0" %lib_base% help "%~0"
) else if "%1" neq "" ( ) else if "%~1" neq "" (
call :%* call :%*
) )

View File

@@ -1,12 +1,12 @@
@echo off @echo off
call "%~dp0lib_base.cmd" call "%~dp0lib_base.cmd"
call "%%~dp0lib_console" call "%~dp0lib_console.cmd"
set lib_path=call "%~dp0lib_path.cmd" set lib_path=call "%~dp0lib_path.cmd"
if "%~1" == "/h" ( if "%~1" == "/h" (
%lib_base% help "%~0" %lib_base% help "%~0"
) else if "%1" neq "" ( ) else if "%~1" neq "" (
call :%* call :%*
) )
@@ -48,7 +48,7 @@ exit /b
set "add_path=%~1" set "add_path=%~1"
) else ( ) else (
%print_error% "You must specify a directory to add to the path!" %print_error% "You must specify a directory to add to the path!"
exit 1 exit /b 1
) )
if "%~2" neq "" if /i "%~2" == "append" ( if "%~2" neq "" if /i "%~2" == "append" (
@@ -72,7 +72,7 @@ exit /b
set "PATH=%add_to_path%;%PATH%" set "PATH=%add_to_path%;%PATH%"
) )
goto :end_enhance_path goto :end_enhance_path
) else if "add_to_path" equ "" ( ) else if "%add_to_path%" equ "" (
goto :end_enhance_path goto :end_enhance_path
) )
@@ -84,20 +84,20 @@ exit /b
setlocal enabledelayedexpansion setlocal enabledelayedexpansion
if "!found!" == "0" ( if "!found!" == "0" (
echo "!path!"|!WINDIR!\System32\findstr >nul /I /R /C:";!find_query!;" echo "!PATH!"|!WINDIR!\System32\findstr >nul /I /R /C:";!find_query!;"
call :set_found call :set_found
) )
%print_debug% :enhance_path "Env Var INSIDE PATH !find_query! - found=!found!" %print_debug% :enhance_path "Env Var INSIDE PATH !find_query! - found=!found!"
if /i "!position!" == "append" ( if /i "!position!" == "append" (
if "!found!" == "0" ( if "!found!" == "0" (
echo "!path!"|!WINDIR!\System32\findstr >nul /I /R /C:";!find_query!\"$" echo "!PATH!"|!WINDIR!\System32\findstr >nul /I /R /C:";!find_query!\"$"
call :set_found call :set_found
) )
%print_debug% :enhance_path "Env Var END PATH !find_query! - found=!found!" %print_debug% :enhance_path "Env Var END PATH !find_query! - found=!found!"
) else ( ) else (
if "!found!" == "0" ( if "!found!" == "0" (
echo "!path!"|!WINDIR!\System32\findstr >nul /I /R /C:"^\"!find_query!;" echo "!PATH!"|!WINDIR!\System32\findstr >nul /I /R /C:"^\"!find_query!;"
call :set_found call :set_found
) )
%print_debug% :enhance_path "Env Var BEGIN PATH !find_query! - found=!found!" %print_debug% :enhance_path "Env Var BEGIN PATH !find_query! - found=!found!"
@@ -119,7 +119,8 @@ exit /b
:end_enhance_path :end_enhance_path
set "PATH=%PATH:;;=;%" set "PATH=%PATH:;;=;%"
REM echo %path%|"C:\Users\dgames\cmder - dev\vendor\git-for-windows\usr\bin\wc" -c REM echo %path%|wc -c
if "%fast_init%" == "1" exit /b if "%fast_init%" == "1" exit /b
if not "%OLD_PATH:~0,3000%" == "%OLD_PATH:~0,3001%" goto :toolong if not "%OLD_PATH:~0,3000%" == "%OLD_PATH:~0,3001%" goto :toolong
@@ -127,15 +128,22 @@ exit /b
exit /b exit /b
:toolong :toolong
echo "%OLD_PATH%">"%temp%\cmder_lib_pathA" set "_rand=%RANDOM%"
echo "%PATH%">"%temp%\cmder_lib_pathB" if exist "%temp%\%_rand%_cmder_lib_pathA" del "%temp%\%_rand%_cmder_lib_pathA" 2>nul 1>nul
fc /b "%temp%\cmder_lib_pathA" "%temp%\cmder_lib_pathB" 2>nul 1>nul if exist "%temp%\%_rand%_cmder_lib_pathB" del "%temp%\%_rand%_cmder_lib_pathB" 2>nul 1>nul
if errorlevel 1 ( del "%temp%\cmder_lib_pathA" & del "%temp%\cmder_lib_pathB" & goto :changed ) if exist "%temp%\%_rand%_cmder_lib_pathA" goto :toolong
del "%temp%\cmder_lib_pathA" & del "%temp%\cmder_lib_pathB" if exist "%temp%\%_rand%_cmder_lib_pathB" goto :toolong
echo "%OLD_PATH%">"%temp%\%_rand%_cmder_lib_pathA"
if errorlevel 1 ( if exist "%temp%\%_rand%_cmder_lib_pathA" del "%temp%\%_rand%_cmder_lib_pathA" & goto :toolong )
echo "%PATH%">"%temp%\%_rand%_cmder_lib_pathB"
if errorlevel 1 ( if exist "%temp%\%_rand%_cmder_lib_pathA" del "%temp%\%_rand%_cmder_lib_pathA" & if exist "%temp%\%_rand%_cmder_lib_pathB" del "%temp%\%_rand%_cmder_lib_pathB" & goto :toolong )
fc /b "%temp%\%_rand%_cmder_lib_pathA" "%temp%\%_rand%_cmder_lib_pathB" 2>nul 1>nul
if errorlevel 1 ( del "%temp%\%_rand%_cmder_lib_pathA" & del "%temp%\%_rand%_cmder_lib_pathB" & set "_rand=" & goto :changed )
del "%temp%\%_rand%_cmder_lib_pathA" & del "%temp%\%_rand%_cmder_lib_pathB" & set "_rand="
exit /b exit /b
:changed :changed
%print_debug% :enhance_path "END Env Var - PATH=%path%" %print_debug% :enhance_path "END Env Var - PATH=%PATH%"
%print_debug% :enhance_path "Env Var %find_query% - found=%found%" %print_debug% :enhance_path "Env Var %find_query% - found=%found%"
exit /b exit /b
@@ -183,7 +191,7 @@ exit /b
set "add_path=%~1" set "add_path=%~1"
) else ( ) else (
%print_error% "You must specify a directory to add to the path!" %print_error% "You must specify a directory to add to the path!"
exit 1 exit /b 1
) )
set "depth=%~2" set "depth=%~2"

View File

@@ -1,12 +1,12 @@
@echo off @echo off
call "%~dp0lib_base.cmd" call "%~dp0lib_base.cmd"
call "%%~dp0lib_console" call "%~dp0lib_console.cmd"
set lib_profile=call "%~dp0lib_profile.cmd" set lib_profile=call "%~dp0lib_profile.cmd"
if "%~1" == "/h" ( if "%~1" == "/h" (
%lib_base% help "%~0" %lib_base% help "%~0"
) else if "%1" neq "" ( ) else if "%~1" neq "" (
call :%* call :%*
) )

105
vendor/profile.ps1 vendored
View File

@@ -7,15 +7,11 @@
$CMDER_INIT_START = Get-Date $CMDER_INIT_START = Get-Date
# Compatibility with PS major versions <= 2 # Determine the script root if not already set
if (!$PSScriptRoot) { if (!$PSScriptRoot) {
$PSScriptRoot = Split-Path $Script:MyInvocation.MyCommand.Path $PSScriptRoot = Split-Path $Script:MyInvocation.MyCommand.Path
} }
if ($ENV:CMDER_USER_CONFIG) {
Write-Verbose "CMDER IS ALSO USING INDIVIDUAL USER CONFIG FROM '$ENV:CMDER_USER_CONFIG'!"
}
# We do this for Powershell as Admin Sessions because CMDER_ROOT is not being set. # We do this for Powershell as Admin Sessions because CMDER_ROOT is not being set.
if (!$ENV:CMDER_ROOT) { if (!$ENV:CMDER_ROOT) {
if ($ENV:ConEmuDir) { if ($ENV:ConEmuDir) {
@@ -28,40 +24,61 @@ if (!$ENV:CMDER_ROOT) {
# Remove trailing '\' # Remove trailing '\'
$ENV:CMDER_ROOT = ($ENV:CMDER_ROOT).TrimEnd("\") $ENV:CMDER_ROOT = ($ENV:CMDER_ROOT).TrimEnd("\")
# -> recent PowerShell versions include PowerShellGet out of the box # Recent PowerShell versions include PowerShellGet out of the box
$moduleInstallerAvailable = [bool](Get-Command -Name 'Install-Module' -ErrorAction SilentlyContinue) $moduleInstallerAvailable = [bool](Get-Command -Name 'Install-Module' -ErrorAction SilentlyContinue)
# Enable Debug and Verbose output if CMDER_DEBUG environment variable is set to '1' or 'true'
if ($env:CMDER_DEBUG -and ($env:CMDER_DEBUG -match '^(1|true)$')) {
$DebugPreference = 'Continue'
$VerbosePreference = 'Continue'
}
# Add Cmder modules directory to the autoload path. # Add Cmder modules directory to the autoload path.
$CmderModulePath = Join-path $PSScriptRoot "psmodules/" $CmderModulePath = Join-path $PSScriptRoot "psmodules/"
# Import Cmder functions
$CmderFunctions = Join-Path $CmderModulePath "Cmder.ps1" $CmderFunctions = Join-Path $CmderModulePath "Cmder.ps1"
. $CmderFunctions . $CmderFunctions
# Configure PSModulePath to include Cmder modules if not already present
if (-not $moduleInstallerAvailable -and -not $env:PSModulePath.Contains($CmderModulePath) ) { if (-not $moduleInstallerAvailable -and -not $env:PSModulePath.Contains($CmderModulePath) ) {
$env:PSModulePath = $env:PSModulePath.Insert(0, "$CmderModulePath;") $env:PSModulePath = $env:PSModulePath.Insert(0, "$CmderModulePath;")
} }
$gitVersionVendor = (readVersion -gitPath "$ENV:CMDER_ROOT\vendor\git-for-windows\cmd") if ($env:CMDER_USER_CONFIG) {
Write-Verbose "CMDER IS ALSO USING INDIVIDUAL USER CONFIG FROM '$ENV:CMDER_USER_CONFIG'!"
}
# Read vendored Git Version
$gitVendorPath = Join-Path $ENV:CMDER_ROOT 'vendor\git-for-windows\cmd'
$gitVersionVendor = Get-GitVersion -GitPath $gitVendorPath
if (-not [string]::IsNullOrEmpty($gitVersionVendor)) {
Write-Debug "GIT VENDOR: ${gitVersionVendor}" Write-Debug "GIT VENDOR: ${gitVersionVendor}"
} else {
Write-Debug "GIT VENDOR is not present at '$gitVendorPath'"
}
# Get user installed Git Version[s] and Compare with vendored if found. # Get user installed Git version(s) if found, and compare them with vendored version.
foreach ($git in (Get-Command -ErrorAction SilentlyContinue 'git')) { foreach ($git in (Get-Command -ErrorAction SilentlyContinue 'git')) {
Write-Debug "GIT PATH: {$git.Path}" Write-Debug "GIT USER PATH: $($git.Path)"
$gitDir = Split-Path -Path $git.Path $gitDir = Split-Path -Path $git.Path
$gitDir = isGitShim -gitPath $gitDir $gitDir = Get-GitShimPath -GitPath $gitDir
$gitVersionUser = (readVersion -gitPath $gitDir) $gitVersionUser = Get-GitVersion -GitPath $gitDir
Write-Debug "GIT USER: ${gitVersionUser}" Write-Debug "GIT USER VERSION: ${gitVersionUser}"
$useGitVersion = compare_git_versions -userVersion $gitVersionUser -vendorVersion $gitVersionVendor $useGitVersion = Compare-GitVersion -UserVersion $gitVersionUser -VendorVersion $gitVersionVendor
Write-Debug "Using Git Version: ${useGitVersion}" Write-Debug "Using Git Version: ${useGitVersion}"
# Use user installed Git # Use user installed Git
if ($null -eq $gitPathUser) { if ($null -eq $gitPathUser) {
Write-Debug "Detected Git from mingw bin directory"
Write-Debug "Git Dir: ${gitDir}"
if ($gitDir -match '\\mingw32\\bin' -or $gitDir -match '\\mingw64\\bin') { if ($gitDir -match '\\mingw32\\bin' -or $gitDir -match '\\mingw64\\bin') {
$gitPathUser = ($gitDir.subString(0,$gitDir.Length - 12)) $gitPathUser = $gitDir.subString(0, $gitDir.Length - 12)
} else { } else {
$gitPathUser = ($gitDir.subString(0,$gitDir.Length - 4)) $gitPathUser = $gitDir.subString(0, $gitDir.Length - 4)
} }
Write-Debug "Git Path User: ${gitDir}"
} }
if ($useGitVersion -eq $gitVersionUser) { if ($useGitVersion -eq $gitVersionUser) {
@@ -72,7 +89,7 @@ foreach ($git in (Get-Command -ErrorAction SilentlyContinue 'git')) {
} }
} }
# User vendored Git. # Use vendored Git if no user Git found or user Git is older than vendored Git
if ($null -eq $ENV:GIT_INSTALL_ROOT -and $null -ne $gitVersionVendor) { if ($null -eq $ENV:GIT_INSTALL_ROOT -and $null -ne $gitVersionVendor) {
$ENV:GIT_INSTALL_ROOT = "$ENV:CMDER_ROOT\vendor\git-for-windows" $ENV:GIT_INSTALL_ROOT = "$ENV:CMDER_ROOT\vendor\git-for-windows"
$ENV:GIT_INSTALL_TYPE = 'VENDOR' $ENV:GIT_INSTALL_TYPE = 'VENDOR'
@@ -82,23 +99,43 @@ Write-Debug "GIT_INSTALL_ROOT: ${ENV:GIT_INSTALL_ROOT}"
Write-Debug "GIT_INSTALL_TYPE: ${ENV:GIT_INSTALL_TYPE}" Write-Debug "GIT_INSTALL_TYPE: ${ENV:GIT_INSTALL_TYPE}"
if ($null -ne $ENV:GIT_INSTALL_ROOT) { if ($null -ne $ENV:GIT_INSTALL_ROOT) {
$env:Path = Configure-Git -gitRoot "$ENV:GIT_INSTALL_ROOT" -gitType $ENV:GIT_INSTALL_TYPE -gitPathUser $gitPathUser $env:Path = Set-GitPath -GitRoot "$ENV:GIT_INSTALL_ROOT" -GitType $ENV:GIT_INSTALL_TYPE -GitPathUser $gitPathUser
} }
# Create 'vi' alias for 'vim' if vim is available
if (Get-Command -Name "vim" -ErrorAction SilentlyContinue) { if (Get-Command -Name "vim" -ErrorAction SilentlyContinue) {
New-Alias -name "vi" -value vim -errorAction SilentlyContinue New-Alias -name "vi" -value vim -errorAction SilentlyContinue
} }
# PSReadline configuration
if (Get-Module PSReadline -ErrorAction "SilentlyContinue") { if (Get-Module PSReadline -ErrorAction "SilentlyContinue") {
# Display an extra prompt line between the prompt and the command input
Set-PSReadlineOption -ExtraPromptLineCount 1 Set-PSReadlineOption -ExtraPromptLineCount 1
# Invoked when Enter is pressed to submit a command
if ($env:WT_SESSION) {
Set-PSReadLineKeyHandler -Key Enter -ScriptBlock {
# Get the current command line
$line = $null
$cursor = $null
[Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$line, [ref]$cursor)
# Accept the line first
[Microsoft.PowerShell.PSConsoleReadLine]::AcceptLine()
# Emit OSC 133;C to mark start of command output
# This is written directly to the console after the command is accepted
[Console]::Write("$([char]0x1B)]133;C$([char]7)")
}
}
} }
# Pre-assign default prompt hooks so the first run of cmder gets a working prompt. # Pre-assign default prompt hooks so the first run of Cmder gets a working prompt
$env:gitLoaded = $null $env:gitLoaded = $null
[ScriptBlock]$PrePrompt = {} [ScriptBlock]$PrePrompt = {}
[ScriptBlock]$PostPrompt = {} [ScriptBlock]$PostPrompt = {}
[ScriptBlock]$CmderPrompt = { [ScriptBlock]$CmderPrompt = {
# Check if we're currently running under Admin privileges. # Check if we're currently running under Admin privileges
$identity = [Security.Principal.WindowsIdentity]::GetCurrent() $identity = [Security.Principal.WindowsIdentity]::GetCurrent()
$principal = [Security.Principal.WindowsPrincipal] $identity $principal = [Security.Principal.WindowsPrincipal] $identity
$adminRole = [Security.Principal.WindowsBuiltInRole]::Administrator $adminRole = [Security.Principal.WindowsBuiltInRole]::Administrator
@@ -107,7 +144,7 @@ $env:gitLoaded = $null
$Host.UI.RawUI.ForegroundColor = "White" $Host.UI.RawUI.ForegroundColor = "White"
Microsoft.PowerShell.Utility\Write-Host "PS " -NoNewline -ForegroundColor $color Microsoft.PowerShell.Utility\Write-Host "PS " -NoNewline -ForegroundColor $color
Microsoft.PowerShell.Utility\Write-Host $pwd.ProviderPath -NoNewLine -ForegroundColor Green Microsoft.PowerShell.Utility\Write-Host $pwd.ProviderPath -NoNewLine -ForegroundColor Green
checkGit($pwd.ProviderPath) Show-GitStatus -Path $pwd.ProviderPath
Microsoft.PowerShell.Utility\Write-Host "`nλ" -NoNewLine -ForegroundColor "DarkGray" Microsoft.PowerShell.Utility\Write-Host "`nλ" -NoNewLine -ForegroundColor "DarkGray"
} }
@@ -196,6 +233,28 @@ if ( $(Get-Command prompt).Definition -match 'PS \$\(\$executionContext.SessionS
[ScriptBlock]$Prompt = { [ScriptBlock]$Prompt = {
$lastSUCCESS = $? $lastSUCCESS = $?
$realLastExitCode = $LastExitCode $realLastExitCode = $LastExitCode
# Terminal-specific escape sequences for Windows Terminal and ConEmu
if ($env:WT_SESSION -or $env:ConEmuPID) {
# Emit OSC 133;D to mark the end of command execution with exit code
if ($env:WT_SESSION) {
Microsoft.PowerShell.Utility\Write-Host -NoNewline "$([char]0x1B)]133;D;$realLastExitCode$([char]7)"
}
# Emit OSC 9;9 to enable directory tracking
# Enables "Duplicate Tab" and "Split Pane" to preserve the working directory
$loc = $executionContext.SessionState.Path.CurrentLocation
if ($loc.Provider.Name -eq "FileSystem") {
Microsoft.PowerShell.Utility\Write-Host -NoNewline "$([char]0x1B)]9;9;`"$($loc.ProviderPath)`"$([char]0x1B)\"
}
# Emit OSC 133;A to mark the start of the prompt
# Enables features like command navigation, selection, and visual separators
if ($env:WT_SESSION) {
Microsoft.PowerShell.Utility\Write-Host -NoNewline "$([char]0x1B)]133;A$([char]7)"
}
}
$host.UI.RawUI.WindowTitle = Microsoft.PowerShell.Management\Split-Path $pwd.ProviderPath -Leaf $host.UI.RawUI.WindowTitle = Microsoft.PowerShell.Management\Split-Path $pwd.ProviderPath -Leaf
Microsoft.PowerShell.Utility\Write-Host -NoNewline "$([char]0x200B)`r$([char]0x1B)[K" Microsoft.PowerShell.Utility\Write-Host -NoNewline "$([char]0x200B)`r$([char]0x1B)[K"
if ($lastSUCCESS -or ($LastExitCode -ne 0)) { if ($lastSUCCESS -or ($LastExitCode -ne 0)) {
@@ -204,6 +263,12 @@ if ( $(Get-Command prompt).Definition -match 'PS \$\(\$executionContext.SessionS
PrePrompt | Microsoft.PowerShell.Utility\Write-Host -NoNewline PrePrompt | Microsoft.PowerShell.Utility\Write-Host -NoNewline
CmderPrompt CmderPrompt
PostPrompt | Microsoft.PowerShell.Utility\Write-Host -NoNewline PostPrompt | Microsoft.PowerShell.Utility\Write-Host -NoNewline
# Emit OSC 133;B to mark the start of command input (after prompt, before user types)
if ($env:WT_SESSION) {
Microsoft.PowerShell.Utility\Write-Host -NoNewline "$([char]0x1B)]133;B$([char]7)"
}
$global:LastExitCode = $realLastExitCode $global:LastExitCode = $realLastExitCode
return " " return " "
} }

View File

@@ -1,179 +1,251 @@
function readVersion($gitPath) { function Get-GitVersion {
$gitExecutable = "${gitPath}\git.exe" param(
[Parameter(Mandatory = $true)]
[string]$GitPath
)
if (-not (Test-Path "$gitExecutable")) { $gitExecutable = Join-Path $GitPath "git.exe"
if (-not (Test-Path $gitExecutable)) {
return $null return $null
} }
$gitVersion = (cmd /c "${gitExecutable}" --version) # Execute 'git --version' and capture output
$gitVersion = & $gitExecutable --version 2>$null
if ($gitVersion -match 'git version\s+(\S+)') {
return $Matches[1]
}
Write-Debug "Git executable path: $gitExecutable"
Write-Error "'git --version' returned an improper version string!"
Write-Error "Unable to determine Git version from output: $gitVersion"
if ($gitVersion -match 'git version') {
($trash1, $trash2, $gitVersion) = $gitVersion.split(' ', 3)
} else {
pause
return $null return $null
} }
return $gitVersion.toString() function Get-GitShimPath {
} param(
[Parameter(Mandatory = $true)]
function isGitShim($gitPath) { [string]$GitPath
# check if there is a shim file - if yes, read the actual executable path )
# Check if there is a shim file - if yes, read the actual executable path
# See: github.com/ScoopInstaller/Shim # See: github.com/ScoopInstaller/Shim
if (Test-Path "${gitPath}\git.shim") { $shimFile = Join-Path $GitPath "git.shim"
$shim = (get-content "${gitPath}\git.shim") if (Test-Path $shimFile) {
($trash, $gitPath) = $shim.replace(' ', '').split('=') $shimContent = Get-Content $shimFile -Raw
if ($shimContent -match '^\s*path\s*=\s*(.+)\s*$') {
$gitPath = $gitPath.replace('\git.exe', '') $GitPath = $Matches[1].Trim().Replace('\git.exe', '')
}
} }
return $gitPath.toString() return $GitPath
} }
function compareVersions($userVersion, $vendorVersion) { function Compare-Version {
if ($null -ne $userVersion) { param(
($userMajor, $userMinor, $userPatch, $userBuild) = $userVersion.split('.', 4) [Parameter(Mandatory = $false)]
} else { [AllowNull()]
[string]$UserVersion,
[Parameter(Mandatory = $false)]
[AllowNull()]
[string]$VendorVersion
)
if ([string]::IsNullOrEmpty($UserVersion)) { return -1 }
if ([string]::IsNullOrEmpty($VendorVersion)) { return 1 }
# Split version strings by dots to compare segment by segment
# For "2.49.0.windows.1", we get: ["2", "49", "0", "windows", "1"]
$userParts = $UserVersion -split '\.'
$vendorParts = $VendorVersion -split '\.'
$maxLength = [Math]::Max($userParts.Count, $vendorParts.Count)
for ($i = 0; $i -lt $maxLength; $i++) {
$userPart = if ($i -lt $userParts.Count) { $userParts[$i] } else { '' }
$vendorPart = if ($i -lt $vendorParts.Count) { $vendorParts[$i] } else { '' }
# Check if both parts are purely numeric
$userIsNumeric = $userPart -match '^\d+$'
$vendorIsNumeric = $vendorPart -match '^\d+$'
if ($userIsNumeric -and $vendorIsNumeric) {
# Both numeric: compare as integers (so 49 > 5, not lexicographic)
$userNum = [int]$userPart
$vendorNum = [int]$vendorPart
if ($userNum -gt $vendorNum) { return 1 }
if ($userNum -lt $vendorNum) { return -1 }
}
elseif ($userIsNumeric -and -not $vendorIsNumeric) {
# Numeric segment comes before text segment (e.g., "2.0" < "2.0.rc1")
return -1 return -1
} }
elseif (-not $userIsNumeric -and $vendorIsNumeric) {
if ($null -ne $vendorVersion) { # Text segment comes after numeric segment
($vendorMajor, $vendorMinor, $vendorPatch, $vendorBuild) = $vendorVersion.split('.', 4)
} else {
return 1 return 1
} }
else {
if (($userMajor -eq $vendorMajor) -and ($userMinor -eq $vendorMinor) -and ($userPatch -eq $vendorPatch) -and ($userBuild -eq $vendorBuild)) { # Both are text: use case-insensitive lexicographic comparison
return 1 $cmp = [string]::Compare($userPart, $vendorPart, $true)
if ($cmp -ne 0) { return [Math]::Sign($cmp) }
}
} }
if ($userMajor -gt $vendorMajor) { return 1 }
if ($userMajor -lt $vendorMajor) { return -1 }
if ($userMinor -gt $vendorMinor) { return 1 }
if ($userMinor -lt $vendorMinor) { return -1 }
if ($userPatch -gt $vendorPatch) { return 1 }
if ($userPatch -lt $vendorPatch) { return -1 }
if ($userBuild -gt $vendorBuild) { return 1 }
if ($userBuild -lt $vendorBuild) { return -1 }
return 0 return 0
} }
function compare_git_versions($userVersion, $vendorVersion) { function Compare-GitVersion {
$result = compareVersions -userVersion $userVersion -vendorVersion $vendorVersion param(
[Parameter(Mandatory = $false)]
[AllowNull()]
[string]$UserVersion,
[Parameter(Mandatory = $false)]
[AllowNull()]
[string]$VendorVersion
)
Write-Debug "Compare Versions Result: ${result}" $result = Compare-Version -UserVersion $UserVersion -VendorVersion $VendorVersion
Write-Debug "Compare Versions Result: $result"
if ($result -ge 0) { if ($result -ge 0) {
return $userVersion return $UserVersion
}
else {
return $vendorVersion
} }
return $VendorVersion
} }
function Configure-Git($gitRoot, $gitType, $gitPathUser) { function Set-GitPath {
param(
[Parameter(Mandatory = $true)]
[string]$GitRoot,
[Parameter(Mandatory = $true)]
[string]$GitType,
[Parameter(Mandatory = $false)]
[string]$GitPathUser
)
# Proposed Behavior # Proposed Behavior
# Modify the path if we are using VENDORED Git, do nothing if using USER Git. # Modify the path if we are using VENDORED Git, do nothing if using USER Git.
# If User Git is installed but is older, match its path config adding paths # If User Git is installed but is older, match its path config adding paths
# in the same path positions allowing a user to configure Cmder Git path # in the same path positions allowing a user to configure Cmder Git path
# using locally installed Git Path Config. # using locally installed Git Path Config.
if ($gitType -eq 'VENDOR') {
# If User Git is installed replace its path config with Newer Vendored Git Path
if (($null -ne $gitPathUser) -and ($gitPathUser -ne '')) {
Write-Verbose "Cmder 'profile.ps1': Replacing older user Git path '$gitPathUser' with newer vendored Git path '$gitRoot' in the system path..."
$newPath = ($env:path -ireplace [regex]::Escape($gitPathUser), $gitRoot) if ($GitType -ne 'VENDOR') {
} return $env:Path
else {
if (-not ($env:Path -match [regex]::Escape("$gitRoot\cmd"))) {
Write-Debug "Adding $gitRoot\cmd to the path"
$newPath = $($gitRoot + "\cmd" + ";" + $env:Path)
} }
# Add "$gitRoot\mingw[32|64]\bin" to the path if exists and not done already $newPath = $env:Path
if ((Test-Path "$gitRoot\mingw32\bin") -and -not ($env:path -match [regex]::Escape("$gitRoot\mingw32\bin"))) {
Write-Debug "Adding $gitRoot\mingw32\bin to the path" # Replace user Git path with vendored Git if user path exists
$newPath = "$newPath;$gitRoot\mingw32\bin" if ($GitPathUser) {
} Write-Verbose "Cmder 'profile.ps1': Replacing older user Git path '$GitPathUser' with newer vendored Git path '$GitRoot' in the system path..."
elseif ((Test-Path "$gitRoot\mingw64\bin") -and -not ($env:path -match [regex]::Escape("$gitRoot\mingw64\bin"))) { $newPath = $newPath -ireplace [regex]::Escape($GitPathUser), $GitRoot
Write-Debug "Adding $gitRoot\mingw64\bin to the path" } else {
$newPath = "$newPath;$gitRoot\mingw64\bin" # Add Git cmd directory to the path
$gitCmd = Join-Path $GitRoot "cmd"
if (-not ($newPath -match [regex]::Escape($gitCmd))) {
Write-Debug "Adding $gitCmd to the path"
$newPath = "$gitCmd;$newPath"
} }
# Add "$gitRoot\usr\bin" to the path if exists and not done already # Add mingw[32|64]\bin directories to the path, if they exist and not already present
if ((Test-Path "$gitRoot\usr\bin") -and -not ($env:path -match [regex]::Escape("$gitRoot\usr\bin"))) { # Prefer mingw64 on 64-bit systems, mingw32 on 32-bit systems
Write-Debug "Adding $gitRoot\usr\bin to the path" $is64Bit = [Environment]::Is64BitOperatingSystem
$newPath = "$newPath;$gitRoot\usr\bin" $mingwDirs = if ($is64Bit) { @('mingw64', 'mingw32') } else { @('mingw32') }
foreach ($mingw in $mingwDirs) {
$mingwBin = Join-Path $GitRoot "$mingw\bin"
if ((Test-Path $mingwBin) -and -not ($newPath -match [regex]::Escape($mingwBin))) {
Write-Debug "Adding $mingwBin to the path"
$newPath = "$newPath;$mingwBin"
break
}
}
# Add usr\bin directory to the path
$usrBin = Join-Path $GitRoot "usr\bin"
if ((Test-Path $usrBin) -and -not ($newPath -match [regex]::Escape($usrBin))) {
Write-Debug "Adding $usrBin to the path"
$newPath = "$newPath;$usrBin"
} }
} }
return $newPath return $newPath
} }
return $env:path function Import-Git {
} $gitModule = Get-Module -Name Posh-Git -ListAvailable
function Import-Git() { if (-not $gitModule) {
$GitModule = Get-Module -Name Posh-Git -ListAvailable Microsoft.PowerShell.Utility\Write-Host -NoNewline "`r`n"
if ($GitModule | Select-Object version | Where-Object version -le ([version]"0.6.1.20160330")) {
Import-Module Posh-Git > $null
}
if ($GitModule | Select-Object version | Where-Object version -ge ([version]"1.0.0")) {
Import-Module Posh-Git > $null
$GitPromptSettings.AnsiConsole = $false
}
if (-not $GitModule) {
Write-Host -NoNewline "`r`n"
Write-Warning "Missing git support, install posh-git with 'Install-Module posh-git' and restart Cmder." Write-Warning "Missing git support, install posh-git with 'Install-Module posh-git' and restart Cmder."
Write-Host -NoNewline "`r$([char]0x1B)[A" Microsoft.PowerShell.Utility\Write-Host -NoNewline "`r$([char]0x1B)[A"
return $false return $false
} }
# Make sure we only run once by always returning true
# Import posh-git module (works for all versions)
Import-Module Posh-Git -ErrorAction SilentlyContinue | Out-Null
# Apply version-specific settings for posh-git 1.0.0+
if (($gitModule.Version -ge [version]"1.0.0") -and (Get-Variable -Name GitPromptSettings -ErrorAction SilentlyContinue)) {
$GitPromptSettings.AnsiConsole = $false
}
return $true return $true
} }
function checkGit($Path) { function Show-GitStatus {
param(
[Parameter(Mandatory = $true)]
[string]$Path
)
if (-not (Get-Command git -ErrorAction SilentlyContinue)) { if (-not (Get-Command git -ErrorAction SilentlyContinue)) {
return return
} }
if (-not (Test-Path -Path (Join-Path $Path '.git'))) {
$SplitPath = Split-Path $path $gitDir = Join-Path $Path '.git'
if ($SplitPath) { checkGit($SplitPath) } if (-not (Test-Path $gitDir)) {
$parentPath = Split-Path $Path
if ($parentPath) {
Show-GitStatus -Path $parentPath
}
return return
} }
if (getGitStatusSetting -eq $true) {
if (Get-GitStatusSetting) {
if ($null -eq $env:gitLoaded) { if ($null -eq $env:gitLoaded) {
$env:gitLoaded = Import-Git $env:gitLoaded = Import-Git
} }
if ($env:gitLoaded -eq $true) { if ($env:gitLoaded -eq $true) {
Write-VcsStatus Write-VcsStatus
} }
} else {
$headFile = Join-Path $gitDir 'HEAD'
if (Test-Path $headFile) {
$headContent = Get-Content $headFile -Raw
if ($headContent -match 'ref: refs/heads/(.+)') {
$branchName = $Matches[1].Trim()
} else {
$shortHash = $headContent.Substring(0, [Math]::Min(7, $headContent.Length))
$branchName = "HEAD detached at $shortHash"
} }
else { Microsoft.PowerShell.Utility\Write-Host " [$branchName]" -NoNewline -ForegroundColor White
$headContent = Get-Content (Join-Path $Path '.git/HEAD')
if ($headContent -like "ref: refs/heads/*") {
$branchName = $headContent.Substring(16)
} }
else {
$branchName = "HEAD detached at $($headContent.Substring(0, 7))"
}
Write-Host " [$branchName]" -NoNewline -ForegroundColor White
} }
} }
function getGitStatusSetting() { function Get-GitStatusSetting {
$gitStatus = (git --no-pager config -l) | Out-String $gitConfig = git --no-pager config -l 2>$null | Out-String
foreach ($line in $($gitStatus -split "`r`n")) { # Check if git status display is disabled via config
if (($line -match 'cmder.status=false') -or ($line -match 'cmder.psstatus=false')) { # Matches: cmder.status=false or cmder.psstatus=false (PowerShell-specific)
if ($gitConfig -match '(?m)^cmder\.(ps)?status=false$') {
return $false return $false
} }
}
return $true return $true
} }

24
vendor/sources.json vendored
View File

@@ -1,27 +1,27 @@
[ [
{
"name": "windows-terminal",
"version": "1.20.11271.0",
"url": "https://github.com/microsoft/terminal/releases/download/v1.20.11271.0/Microsoft.WindowsTerminal_1.20.11271.0_x64.zip"
},
{ {
"name": "git-for-windows", "name": "git-for-windows",
"version": "2.47.0.windows.1", "version": "2.51.2.windows.1",
"url": "https://github.com/git-for-windows/git/releases/download/v2.47.0.windows.1/PortableGit-2.47.0-64-bit.7z.exe" "url": "https://github.com/git-for-windows/git/releases/download/v2.51.2.windows.1/PortableGit-2.51.2-64-bit.7z.exe"
}, },
{ {
"name": "clink", "name": "clink",
"version": "1.7.3", "version": "1.8.8",
"url": "https://github.com/chrisant996/clink/releases/download/v1.7.3/clink.1.7.3.f8fb96.zip" "url": "https://github.com/chrisant996/clink/releases/download/v1.8.8/clink.1.8.8.a63364.zip"
}, },
{ {
"name": "conemu-maximus5", "name": "conemu-maximus5",
"version": "23.07.24", "version": "23.07.24",
"url": "https://github.com/Maximus5/ConEmu/releases/download/v23.07.24/ConEmuPack.230724.7z" "url": "https://github.com/ConEmu/ConEmu/releases/download/v23.07.24/ConEmuPack.230724.7z"
},
{
"name": "windows-terminal",
"version": "1.23.12811.0",
"url": "https://github.com/microsoft/terminal/releases/download/v1.23.12811.0/Microsoft.WindowsTerminal_1.23.12811.0_x64.zip"
}, },
{ {
"name": "clink-completions", "name": "clink-completions",
"version": "0.6.0", "version": "0.6.6",
"url": "https://github.com/vladimir-kotikov/clink-completions/archive/v0.6.0.zip" "url": "https://github.com/vladimir-kotikov/clink-completions/archive/v0.6.6.zip"
} }
] ]