mirror of
https://github.com/microsoft/vscode.git
synced 2026-02-15 07:28:05 +00:00
This commit is contained in:
@@ -6,6 +6,7 @@
|
||||
import { localize } from '../../../nls.js';
|
||||
import { Registry } from '../../registry/common/platform.js';
|
||||
import { DisposableStore, IDisposable } from '../../../base/common/lifecycle.js';
|
||||
import { IContextKeyService } from '../../contextkey/common/contextkey.js';
|
||||
import { IKeybindingService } from '../../keybinding/common/keybinding.js';
|
||||
import { Extensions, IQuickAccessProvider, IQuickAccessProviderDescriptor, IQuickAccessRegistry } from '../common/quickAccess.js';
|
||||
import { IQuickInputService, IQuickPick, IQuickPickItem } from '../common/quickInput.js';
|
||||
@@ -22,7 +23,8 @@ export class HelpQuickAccessProvider implements IQuickAccessProvider {
|
||||
|
||||
constructor(
|
||||
@IQuickInputService private readonly quickInputService: IQuickInputService,
|
||||
@IKeybindingService private readonly keybindingService: IKeybindingService
|
||||
@IKeybindingService private readonly keybindingService: IKeybindingService,
|
||||
@IContextKeyService private readonly contextKeyService: IContextKeyService
|
||||
) { }
|
||||
|
||||
provide(picker: IQuickPick<IHelpQuickAccessPickItem, { useSeparators: true }>): IDisposable {
|
||||
@@ -39,8 +41,8 @@ export class HelpQuickAccessProvider implements IQuickAccessProvider {
|
||||
// Also open a picker when we detect the user typed the exact
|
||||
// name of a provider (e.g. `?term` for terminals)
|
||||
disposables.add(picker.onDidChangeValue(value => {
|
||||
const providerDescriptor = this.registry.getQuickAccessProvider(value.substr(HelpQuickAccessProvider.PREFIX.length));
|
||||
if (providerDescriptor && providerDescriptor.prefix && providerDescriptor.prefix !== HelpQuickAccessProvider.PREFIX) {
|
||||
const providerDescriptor = this.registry.getQuickAccessProvider(value.substr(HelpQuickAccessProvider.PREFIX.length), this.contextKeyService);
|
||||
if (providerDescriptor?.prefix && providerDescriptor.prefix !== HelpQuickAccessProvider.PREFIX) {
|
||||
this.quickInputService.quickAccess.show(providerDescriptor.prefix, { preserveValue: true });
|
||||
}
|
||||
}));
|
||||
@@ -53,7 +55,7 @@ export class HelpQuickAccessProvider implements IQuickAccessProvider {
|
||||
|
||||
getQuickAccessProviders(): IHelpQuickAccessPickItem[] {
|
||||
const providers: IHelpQuickAccessPickItem[] = this.registry
|
||||
.getQuickAccessProviders()
|
||||
.getQuickAccessProviders(this.contextKeyService)
|
||||
.sort((providerA, providerB) => providerA.prefix.localeCompare(providerB.prefix))
|
||||
.flatMap(provider => this.createPicks(provider));
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import { DeferredPromise } from '../../../base/common/async.js';
|
||||
import { CancellationTokenSource } from '../../../base/common/cancellation.js';
|
||||
import { Event } from '../../../base/common/event.js';
|
||||
import { Disposable, DisposableStore, IDisposable, isDisposable, toDisposable } from '../../../base/common/lifecycle.js';
|
||||
import { IContextKeyService } from '../../contextkey/common/contextkey.js';
|
||||
import { IInstantiationService } from '../../instantiation/common/instantiation.js';
|
||||
import { DefaultQuickAccessFilterValue, Extensions, IQuickAccessController, IQuickAccessOptions, IQuickAccessProvider, IQuickAccessProviderDescriptor, IQuickAccessRegistry } from '../common/quickAccess.js';
|
||||
import { IQuickInputService, IQuickPick, IQuickPickItem, ItemActivation } from '../common/quickInput.js';
|
||||
@@ -27,7 +28,8 @@ export class QuickAccessController extends Disposable implements IQuickAccessCon
|
||||
|
||||
constructor(
|
||||
@IQuickInputService private readonly quickInputService: IQuickInputService,
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
@IContextKeyService private readonly contextKeyService: IContextKeyService
|
||||
) {
|
||||
super();
|
||||
this._register(toDisposable(() => {
|
||||
@@ -233,7 +235,7 @@ export class QuickAccessController extends Disposable implements IQuickAccessCon
|
||||
}
|
||||
|
||||
private getOrInstantiateProvider(value: string, enabledProviderPrefixes?: string[]): [IQuickAccessProvider | undefined, IQuickAccessProviderDescriptor | undefined] {
|
||||
const providerDescriptor = this.registry.getQuickAccessProvider(value);
|
||||
const providerDescriptor = this.registry.getQuickAccessProvider(value, this.contextKeyService);
|
||||
if (!providerDescriptor || enabledProviderPrefixes && !enabledProviderPrefixes?.includes(providerDescriptor.prefix)) {
|
||||
return [undefined, undefined];
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
import { coalesce } from '../../../base/common/arrays.js';
|
||||
import { CancellationToken } from '../../../base/common/cancellation.js';
|
||||
import { IDisposable, toDisposable } from '../../../base/common/lifecycle.js';
|
||||
import { ContextKeyExpression, IContextKeyService } from '../../contextkey/common/contextkey.js';
|
||||
import { ItemActivation, IQuickNavigateConfiguration, IQuickPick, IQuickPickItem, QuickPickItem, IQuickPickSeparator } from './quickInput.js';
|
||||
import { Registry } from '../../registry/common/platform.js';
|
||||
|
||||
@@ -194,6 +195,12 @@ export interface IQuickAccessProviderDescriptor {
|
||||
* picker for the provider is showing.
|
||||
*/
|
||||
readonly contextKey?: string;
|
||||
|
||||
/**
|
||||
* A context key expression that must evaluate to true for the
|
||||
* provider to be considered in the registry.
|
||||
*/
|
||||
readonly when?: ContextKeyExpression;
|
||||
}
|
||||
|
||||
export const Extensions = {
|
||||
@@ -210,12 +217,12 @@ export interface IQuickAccessRegistry {
|
||||
/**
|
||||
* Get all registered quick access providers.
|
||||
*/
|
||||
getQuickAccessProviders(): IQuickAccessProviderDescriptor[];
|
||||
getQuickAccessProviders(contextKeyService: IContextKeyService): IQuickAccessProviderDescriptor[];
|
||||
|
||||
/**
|
||||
* Get a specific quick access provider for a given prefix.
|
||||
*/
|
||||
getQuickAccessProvider(prefix: string): IQuickAccessProviderDescriptor | undefined;
|
||||
getQuickAccessProvider(prefix: string, contextKeyService: IContextKeyService): IQuickAccessProviderDescriptor | undefined;
|
||||
}
|
||||
|
||||
export class QuickAccessRegistry implements IQuickAccessRegistry {
|
||||
@@ -245,12 +252,15 @@ export class QuickAccessRegistry implements IQuickAccessRegistry {
|
||||
});
|
||||
}
|
||||
|
||||
getQuickAccessProviders(): IQuickAccessProviderDescriptor[] {
|
||||
return coalesce([this.defaultProvider, ...this.providers]);
|
||||
getQuickAccessProviders(contextKeyService: IContextKeyService): IQuickAccessProviderDescriptor[] {
|
||||
return coalesce([this.defaultProvider, ...this.providers])
|
||||
.filter(provider => !provider.when || contextKeyService.contextMatchesRules(provider.when));
|
||||
}
|
||||
|
||||
getQuickAccessProvider(prefix: string): IQuickAccessProviderDescriptor | undefined {
|
||||
const result = prefix ? (this.providers.find(provider => prefix.startsWith(provider.prefix)) || undefined) : undefined;
|
||||
getQuickAccessProvider(prefix: string, contextKeyService: IContextKeyService): IQuickAccessProviderDescriptor | undefined {
|
||||
const result = prefix
|
||||
? this.providers.find(provider => prefix.startsWith(provider.prefix) && (!provider.when || contextKeyService.contextMatchesRules(provider.when)))
|
||||
: undefined;
|
||||
|
||||
return result || this.defaultProvider;
|
||||
}
|
||||
|
||||
@@ -154,6 +154,7 @@ Registry.as<IQuickAccessRegistry>(QuickAccessExtensions.Quickaccess).registerQui
|
||||
ctor: AgentSessionsQuickAccessProvider,
|
||||
prefix: AGENT_SESSIONS_QUICK_ACCESS_PREFIX,
|
||||
contextKey: 'inAgentSessionsPicker',
|
||||
when: ChatContextKeys.enabled,
|
||||
placeholder: localize('agentSessionsQuickAccessPlaceholder', "Search agent sessions by name"),
|
||||
helpEntries: [{
|
||||
description: localize('agentSessionsQuickAccessHelp', "Show All Agent Sessions"),
|
||||
|
||||
@@ -16,6 +16,7 @@ import { IConfigurationMigrationRegistry, Extensions as ConfigurationMigrationEx
|
||||
import { registerWorkbenchContribution2, WorkbenchPhase } from '../../../common/contributions.js';
|
||||
import { EditorExtensions } from '../../../common/editor.js';
|
||||
import { mcpSchemaId } from '../../../services/configuration/common/configuration.js';
|
||||
import { ChatContextKeys } from '../../chat/common/actions/chatContextKeys.js';
|
||||
import { ExtensionMcpDiscovery } from '../common/discovery/extensionMcpDiscovery.js';
|
||||
import { InstalledMcpServersDiscovery } from '../common/discovery/installedMcpServersDiscovery.js';
|
||||
import { mcpDiscoveryRegistry } from '../common/discovery/mcpDiscovery.js';
|
||||
@@ -108,6 +109,7 @@ Registry.as<IEditorPaneRegistry>(EditorExtensions.EditorPane).registerEditorPane
|
||||
Registry.as<IQuickAccessRegistry>(QuickAccessExtensions.Quickaccess).registerQuickAccessProvider({
|
||||
ctor: McpResourceQuickAccess,
|
||||
prefix: McpResourceQuickAccess.PREFIX,
|
||||
when: ChatContextKeys.enabled,
|
||||
placeholder: localize('mcp.quickaccess.placeholder', "Filter to an MCP resource"),
|
||||
helpEntries: [{
|
||||
description: localize('mcp.quickaccess.add', "MCP Server Resources"),
|
||||
|
||||
@@ -52,6 +52,7 @@ import { IUriIdentityService } from '../../../../platform/uriIdentity/common/uri
|
||||
import { stripIcons } from '../../../../base/common/iconLabels.js';
|
||||
import { Lazy } from '../../../../base/common/lazy.js';
|
||||
import { IKeybindingService } from '../../../../platform/keybinding/common/keybinding.js';
|
||||
import { IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js';
|
||||
import { Registry } from '../../../../platform/registry/common/platform.js';
|
||||
import { ASK_QUICK_QUESTION_ACTION_ID } from '../../chat/browser/actions/chatQuickInputActions.js';
|
||||
import { IQuickChatService } from '../../chat/browser/chat.js';
|
||||
@@ -136,6 +137,7 @@ export class AnythingQuickAccessProvider extends PickerQuickAccessProvider<IAnyt
|
||||
@IUriIdentityService private readonly uriIdentityService: IUriIdentityService,
|
||||
@IQuickInputService private readonly quickInputService: IQuickInputService,
|
||||
@IKeybindingService private readonly keybindingService: IKeybindingService,
|
||||
@IContextKeyService private readonly contextKeyService: IContextKeyService,
|
||||
@IQuickChatService private readonly quickChatService: IQuickChatService,
|
||||
@ILogService private readonly logService: ILogService,
|
||||
@ICustomEditorLabelService private readonly customEditorLabelService: ICustomEditorLabelService
|
||||
@@ -827,7 +829,7 @@ export class AnythingQuickAccessProvider extends PickerQuickAccessProvider<IAnyt
|
||||
}
|
||||
|
||||
type IHelpAnythingQuickPickItem = IAnythingQuickPickItem & { commandCenterOrder: number };
|
||||
const providers: IHelpAnythingQuickPickItem[] = this.lazyRegistry.value.getQuickAccessProviders()
|
||||
const providers: IHelpAnythingQuickPickItem[] = this.lazyRegistry.value.getQuickAccessProviders(this.contextKeyService)
|
||||
.filter(p => p.helpEntries.some(h => h.commandCenterOrder !== undefined))
|
||||
.flatMap(provider => provider.helpEntries
|
||||
.filter(h => h.commandCenterOrder !== undefined)
|
||||
|
||||
@@ -21,6 +21,9 @@ import { EditorsOrder } from '../../common/editor.js';
|
||||
import { Range } from '../../../editor/common/core/range.js';
|
||||
import { TestInstantiationService } from '../../../platform/instantiation/test/common/instantiationServiceMock.js';
|
||||
import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../base/test/common/utils.js';
|
||||
import { IContextKeyService, ContextKeyExpr } from '../../../platform/contextkey/common/contextkey.js';
|
||||
import { ContextKeyService } from '../../../platform/contextkey/browser/contextKeyService.js';
|
||||
import { TestConfigurationService } from '../../../platform/configuration/test/common/testConfigurationService.js';
|
||||
|
||||
suite('QuickAccess', () => {
|
||||
|
||||
@@ -114,26 +117,81 @@ suite('QuickAccess', () => {
|
||||
test('registry', () => {
|
||||
const registry = (Registry.as<IQuickAccessRegistry>(Extensions.Quickaccess));
|
||||
const restore = (registry as QuickAccessRegistry).clear();
|
||||
const contextKeyService = instantiationService.get(IContextKeyService);
|
||||
|
||||
assert.ok(!registry.getQuickAccessProvider('test'));
|
||||
assert.ok(!registry.getQuickAccessProvider('test', contextKeyService));
|
||||
|
||||
const disposables = new DisposableStore();
|
||||
|
||||
disposables.add(registry.registerQuickAccessProvider(providerDescriptorDefault));
|
||||
assert(registry.getQuickAccessProvider('') === providerDescriptorDefault);
|
||||
assert(registry.getQuickAccessProvider('test') === providerDescriptorDefault);
|
||||
assert(registry.getQuickAccessProvider('', contextKeyService) === providerDescriptorDefault);
|
||||
assert(registry.getQuickAccessProvider('test', contextKeyService) === providerDescriptorDefault);
|
||||
|
||||
const disposable = disposables.add(registry.registerQuickAccessProvider(providerDescriptor1));
|
||||
assert(registry.getQuickAccessProvider('test') === providerDescriptor1);
|
||||
assert(registry.getQuickAccessProvider('test', contextKeyService) === providerDescriptor1);
|
||||
|
||||
const providers = registry.getQuickAccessProviders();
|
||||
const providers = registry.getQuickAccessProviders(contextKeyService);
|
||||
assert(providers.some(provider => provider.prefix === 'test'));
|
||||
|
||||
disposable.dispose();
|
||||
assert(registry.getQuickAccessProvider('test') === providerDescriptorDefault);
|
||||
assert(registry.getQuickAccessProvider('test', contextKeyService) === providerDescriptorDefault);
|
||||
|
||||
disposables.dispose();
|
||||
assert.ok(!registry.getQuickAccessProvider('test'));
|
||||
assert.ok(!registry.getQuickAccessProvider('test', contextKeyService));
|
||||
|
||||
restore();
|
||||
});
|
||||
|
||||
test('registry - when condition', () => {
|
||||
const registry = (Registry.as<IQuickAccessRegistry>(Extensions.Quickaccess));
|
||||
const restore = (registry as QuickAccessRegistry).clear();
|
||||
|
||||
// Use real ContextKeyService that properly evaluates rules
|
||||
const contextKeyService = disposables.add(new ContextKeyService(new TestConfigurationService()));
|
||||
const localDisposables = new DisposableStore();
|
||||
|
||||
// Create a context key that starts as undefined (falsy)
|
||||
const contextKey = contextKeyService.createKey<boolean | undefined>('testQuickAccessContextKey', undefined);
|
||||
|
||||
// Register a provider with a when condition that requires testQuickAccessContextKey to be truthy
|
||||
const providerWithWhen = {
|
||||
ctor: TestProvider1,
|
||||
prefix: 'whentest',
|
||||
helpEntries: [],
|
||||
when: ContextKeyExpr.has('testQuickAccessContextKey')
|
||||
};
|
||||
localDisposables.add(registry.registerQuickAccessProvider(providerWithWhen));
|
||||
|
||||
// Verify the expression works with the context key service
|
||||
assert.strictEqual(contextKeyService.contextMatchesRules(providerWithWhen.when), false);
|
||||
|
||||
// Provider with false when condition should not be found
|
||||
assert.strictEqual(registry.getQuickAccessProvider('whentest', contextKeyService), undefined);
|
||||
|
||||
// Should not appear in the list of providers
|
||||
let providers = registry.getQuickAccessProviders(contextKeyService);
|
||||
assert.ok(!providers.some(p => p.prefix === 'whentest'));
|
||||
|
||||
// Set the context key to true
|
||||
contextKey.set(true);
|
||||
|
||||
// Verify the expression now matches
|
||||
assert.strictEqual(contextKeyService.contextMatchesRules(providerWithWhen.when), true);
|
||||
|
||||
// Now the provider should be found
|
||||
assert.strictEqual(registry.getQuickAccessProvider('whentest', contextKeyService), providerWithWhen);
|
||||
|
||||
// Should appear in the list of providers
|
||||
providers = registry.getQuickAccessProviders(contextKeyService);
|
||||
assert.ok(providers.some(p => p.prefix === 'whentest'));
|
||||
|
||||
// Set context key back to undefined (falsy)
|
||||
contextKey.set(undefined);
|
||||
|
||||
// Provider should not be found again
|
||||
assert.strictEqual(registry.getQuickAccessProvider('whentest', contextKeyService), undefined);
|
||||
|
||||
localDisposables.dispose();
|
||||
|
||||
restore();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user