-
Notifications
You must be signed in to change notification settings - Fork 125
Description
Description
When using Match File → Extension (exact extension list), the menu is not shown if one extension is a prefix of another extension that appears earlier in the list.
Example: .ps1 fails to match when .ps1xml exists before it in AcceptExts.
Steps to reproduce
Create a custom menu item
Enable Match File
Set mode to Extension
Set AcceptExts to:
.ps1xml|.ps1
Right-click a .ps1 file
Expected behavior
The menu should be shown for .ps1 files, since .ps1 is explicitly listed.
Actual behavior
The menu is not shown for .ps1 files.
Minimal reproduction (logic simulation)
This reproduces the exact behavior in PowerShell, mimicking the current C++ logic:
function Test-CCM_ExtensionList {
param(
[string] $AcceptExts,
[string] $Ext
)
$pos = $AcceptExts.IndexOf($Ext)
if ($pos -lt 0) { return $false }
$isStart = ($pos -eq 0)
$isEnd = ($pos + $Ext.Length -eq $AcceptExts.Length)
$prevOk = (-not $isStart) -and ($AcceptExts[$pos - 1] -eq '|')
$nextOk = (-not $isEnd) -and ($AcceptExts[$pos + $Ext.Length] -eq '|')
return (($isStart -or $prevOk) -and ($isEnd -or $nextOk))
}
Test-CCM_ExtensionList ".ps1xml|.ps1" ".ps1" # False (bug)
Test-CCM_ExtensionList ".ps1|.ps1xml" ".ps1" # True
Root cause (analysis)
In CustomSubExplorerCommand::Accept(...), the Extension List logic:
Uses find(ext) to locate the first occurrence of the extension substring
Performs boundary checks (| before/after)
Returns false immediately if the first match fails
Does not search for subsequent valid matches
As a result, .ps1 matches the substring inside .ps1xml first, fails the boundary check, and the function exits without checking the correct .ps1 token later in the list.
Workaround
Reorder the list so shorter extensions appear before longer ones:
.ps1|.ps1xml
Proposed fixes
✅ Option A (recommended – simplest & most robust)
Tokenize the extension list and compare tokens exactly:
// Pseudocode
split acceptExts by '|'
trim whitespace
lowercase tokens and ext
if (token == ext) return true
Benefits:
True exact matching
No prefix issues
Robust against whitespace and case
Simpler logic
🔧 Option B (minimal patch)
Keep current logic, but iterate over all occurrences:
size_t pos = 0;
while ((pos = acceptExts.find(ext, pos)) != std::wstring::npos) {
bool isStart = (pos == 0);
bool isEnd = (pos + ext.size() == acceptExts.size());
bool prevOk = isStart || acceptExts[pos - 1] == L'|';
bool nextOk = isEnd || acceptExts[pos + ext.size()] == L'|';
if (prevOk && nextOk) return true;
pos += 1;
}
return false;
Additional suggestions
Normalize acceptExts to lowercase before matching (currently ext is lowercased but acceptExts is not)
Trim whitespace around tokens
Notes
“Extension Like” works as expected since it uses substring matching
This issue only affects Extension (exact) mode
The bug becomes more likely with long extension lists
👍 Happy to help test or validate a fix.