Bug
When multiple SCM providers are registered, inline toolbar actions from one provider's scm/resourceState/context menu may appear on another provider's resource items. For example, git's Stage Changes / Discard Changes / Open File buttons appear on a third-party extension's SCM resources, if those resources don't contribute any actions themselves. This happens even though git's contributions are properly guarded with scmProvider == git.
Reproduction
- Install an extension that registers a custom SCM provider with resource groups and resources
- Open the Source Control view so both the custom provider and git are visible
- Expand git's working tree to render its resources (inline actions appear correctly)
- Switch to the custom provider's resources
- Hover over a resource item -- git's inline actions appear despite the
scmProvider == git when-clause guard
The bug requires that the custom provider's resource items contribute zero inline actions to scm/resourceState/context.
Root cause
connectPrimaryMenu in src/vs/workbench/contrib/scm/browser/util.ts initializes its action cache as empty arrays:
let cachedPrimary: IAction[] = [];
let cachedSecondary: IAction[] = [];
When a ResourceRenderer template is recycled from a git resource to another provider's resource, _renderActionBar correctly detects the menu change and creates a new connectPrimaryMenu subscription. The new menu is correctly evaluated through the OverlayContextKeyService chain, and all git when-clauses correctly return false, producing an empty action set.
However, equals([], []) returns true, so the callback is never invoked:
if (equals(cachedPrimary, primary, compareActions) && equals(cachedSecondary, secondary, compareActions)) {
return; // skipped -- both cached and new are empty
}
Since setActions is never called, the toolbar retains the stale inline actions from the previous template occupant.
Fix
Initialize the cache to undefined instead of []. The equals function in base/common/arrays.ts already handles undefined safely (line 33: if (!one || !other) return false), so the first invocation always falls through to the callback:
let cachedPrimary: IAction[] = undefined!;
let cachedSecondary: IAction[] = undefined!;
Verification
Confirmed by patching the bundled workbench.desktop.main.js with this change. After the fix, switching between providers correctly clears the inline toolbar when the new provider has no matching scm/resourceState/context contributions.
Bug
When multiple SCM providers are registered, inline toolbar actions from one provider's
scm/resourceState/contextmenu may appear on another provider's resource items. For example, git's Stage Changes / Discard Changes / Open File buttons appear on a third-party extension's SCM resources, if those resources don't contribute any actions themselves. This happens even though git's contributions are properly guarded withscmProvider == git.Reproduction
scmProvider == gitwhen-clause guardThe bug requires that the custom provider's resource items contribute zero inline actions to
scm/resourceState/context.Root cause
connectPrimaryMenuinsrc/vs/workbench/contrib/scm/browser/util.tsinitializes its action cache as empty arrays:When a
ResourceRenderertemplate is recycled from a git resource to another provider's resource,_renderActionBarcorrectly detects the menu change and creates a newconnectPrimaryMenusubscription. The new menu is correctly evaluated through theOverlayContextKeyServicechain, and all git when-clauses correctly returnfalse, producing an empty action set.However,
equals([], [])returnstrue, so the callback is never invoked:Since
setActionsis never called, the toolbar retains the stale inline actions from the previous template occupant.Fix
Initialize the cache to
undefinedinstead of[]. Theequalsfunction inbase/common/arrays.tsalready handlesundefinedsafely (line 33:if (!one || !other) return false), so the first invocation always falls through to the callback:Verification
Confirmed by patching the bundled
workbench.desktop.main.jswith this change. After the fix, switching between providers correctly clears the inline toolbar when the new provider has no matchingscm/resourceState/contextcontributions.