Change:
Testing on Windows Here's what's happening in the following gif: on another cell (not the one who's ... I'm about to click. on the ... of the output cell. menu shows for a split second then goes away. on the same ... menu now shows properly. !
The mouse click on the should not change the editor focus, like the run button.
I think we have a workaround for this on the cell toolbar, but I didn't think it would be necessary here because none of those actions depend on focus. Also maybe it should respect
Still repros for me. It actually repros each time I click the ... now, instead of only the first time.
Only on Windows it seems...
This is like but it seems that issue can have an impact outside of just focus-related context keys. Basically, a context key (cell type, "cell has outputs", etc) changes in the parent CKS, but is overridden in the scoped CKS. So the effective value in the scoped CKS did not change, but the menu still tries to refresh itself and closes. We have a hacky workaround for the original focus issue for the cell title toolbar But actually this can impact any toolbar. I can copy the workaround for the output toolbar for now. Or we can disable the consolidated output toolbar by default. I still have no clue why it works on Linux but not Windows, since they should be using the same custom toolbar.
In the sync today, the vote was to leave this alone since it isn't totally blocking and wasn't noticed in the past 2 or 3 weeks. I think we should either leave it for next month or disable .
The issue is described in and is really about menus in general. It's really annoying and there are some things I can do to fix it. Any one of these would be fine. Child CKS fires an onDidChange event when a value changes in its parent, even when that key is overridden by the child We could check for this and not fire the event in this case at Would probably reduce a lot of noise/unnecessary updating in the workbench. Would take a little refactoring - the doesn't contain enough info to do this. We can expand the event to contain a full list of types that are only exposed internally, not to consumers. The Menu fires its onDidChange because some context keys were updated, but it doesn't check whether the actual list of menu items changed Maybe it could diff its list of menu items to avoid this. But the Menu doesn't cache a list of menu items, so that isn't currently possible. Calling setActions on the toolbar with an identical list of actions still causes it to hide We could diff the actions to check that here, but that seems too late for that sort of check After writing all this out, I think the first makes sense, and I could take a crack at a PR, lmk what you think
I like that. Also, I do believe the knows enough, the child CKS service would need to know its keys and ask the event if any of those are affected. Otherwise the CKS should have access to all possible implementations of Careful - context keys for menus control not just inclusion into the menu but also enablement and toggle-states. See how is used
I'm thinking the check has to be, whether all of the keys in the event are contained within the child CKS, not some. The method isn't enough for that but I think you are pointing out that I can just check all the implementations, and for example I can see the full list of keys on .

Discussion:
export class Context implements IContext { protected _parent: Context | null; <del> protected _value: { [key: string]: any }; </del> <ins> protected _value: Record<string, any>; </ins> protected _id: number; constructor(id: number, parent: Context | null) {

this._value['_contextId'] = id; } <ins> public get value(): Record<string, any> { return { ...this._value }; } </ins> public setValue(key: string, value: any): boolean { // console.log('SET ' + key + ' = ' + value + ' ON ' + this._id); if (this._value[key] !== value) {

this._parent = parent; } <del> public collectAllValues(): { [key: string]: any } { </del> <ins> public collectAllValues(): Record<string, any> { </ins> let result = this._parent ? this._parent.collectAllValues() : Object.create(null); result = { ...result, ...this._value }; delete result['_contextId'];

} } <ins> function allEventKeysInContext(event: IContextKeyChangeEvent, context: Record<string, any>): boolean { return (event instanceof ArrayContextKeyChangeEvent && event.keys.every(key => key in context)) || (event instanceof SimpleContextKeyChangeEvent && event.key in context) || (event instanceof CompositeContextKeyChangeEvent && event.events.every(e => allEventKeysInContext(e, context))); } </ins> export abstract class AbstractContextKeyService implements IContextKeyService { declare _serviceBrand: undefined;

private _updateParentChangeListener(): void { // Forward parent events to this listener. Parent will change. <del> this._parentChangeListener.value = this._parent.onDidChangeContext(this._onDidChangeContext.fire, this._onDidChangeContext); </del> <ins> this._parentChangeListener.value = this._parent.onDidChangeContext(e => { const thisContainer = this._parent.getContextValuesContainer(this._myContextId); const thisContextValues = thisContainer.value; if (!allEventKeysInContext(e, thisContextValues)) { this._onDidChangeContext.fire(e); } }); </ins> } public dispose(): void {

*  Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; <ins> import { DeferredPromise } from 'vs/base/common/async'; </ins> import { DisposableStore } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration';

const expr = ContextKeyExpr.in('notebookCellResource', 'jupyter.runByLineCells'); assert.deepStrictEqual(contextKeyService.contextMatchesRules(expr), true); }); <ins> test('suppress update event from parent when one key is overridden by child', () => { const root = new ContextKeyService(new TestConfigurationService()); const child = root.createScoped(document.createElement('div')); root.createKey('testA', 1); child.createKey('testA', 4); let fired = false; const event = child.onDidChangeContext(e => fired = true); root.setContext('testA', 10); assert.strictEqual(fired, false, 'Should not fire event when overridden key is updated in parent'); event.dispose(); }); test('suppress update event from parent when all keys are overridden by child', () => { const root = new ContextKeyService(new TestConfigurationService()); const child = root.createScoped(document.createElement('div')); root.createKey('testA', 1); root.createKey('testB', 2); root.createKey('testC', 3); child.createKey('testA', 4); child.createKey('testB', 5); child.createKey('testD', 6); let fired = false; const event = child.onDidChangeContext(e => fired = true); root.bufferChangeEvents(() => { root.setContext('testA', 10); root.setContext('testB', 20); root.setContext('testD', 30); }); assert.strictEqual(fired, false, 'Should not fire event when overridden key is updated in parent'); event.dispose(); }); test('pass through update event from parent when one key is not overridden by child', () => { const root = new ContextKeyService(new TestConfigurationService()); const child = root.createScoped(document.createElement('div')); root.createKey('testA', 1); root.createKey('testB', 2); root.createKey('testC', 3); child.createKey('testA', 4); child.createKey('testB', 5); child.createKey('testD', 6); const def = new DeferredPromise(); child.onDidChangeContext(e => { try { assert.ok(e.affectsSome(new Set(['testA'])), 'testA changed'); assert.ok(e.affectsSome(new Set(['testB'])), 'testB changed'); assert.ok(e.affectsSome(new Set(['testC'])), 'testC changed'); } catch (err) { def.error(err); return; } def.complete(undefined); }); root.bufferChangeEvents(() => { root.setContext('testA', 10); root.setContext('testB', 20); root.setContext('testC', 30); }); return def.p; }); </ins> });