awa
This commit is contained in:
@@ -0,0 +1,354 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
||||
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
||||
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
||||
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||
};
|
||||
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
||||
return function (target, key) { decorator(target, key, paramIndex); }
|
||||
};
|
||||
var AbstractCommandsQuickAccessProvider_1, CommandsHistory_1;
|
||||
import { toErrorMessage } from '../../../base/common/errorMessage.js';
|
||||
import { isCancellationError } from '../../../base/common/errors.js';
|
||||
import { matchesContiguousSubString, matchesPrefix, matchesWords, or } from '../../../base/common/filters.js';
|
||||
import { createSingleCallFunction } from '../../../base/common/functional.js';
|
||||
import { Disposable } from '../../../base/common/lifecycle.js';
|
||||
import { LRUCache } from '../../../base/common/map.js';
|
||||
import { TfIdfCalculator, normalizeTfIdfScores } from '../../../base/common/tfIdf.js';
|
||||
import { localize } from '../../../nls.js';
|
||||
import { ICommandService } from '../../commands/common/commands.js';
|
||||
import { IConfigurationService } from '../../configuration/common/configuration.js';
|
||||
import { IDialogService } from '../../dialogs/common/dialogs.js';
|
||||
import { IInstantiationService } from '../../instantiation/common/instantiation.js';
|
||||
import { IKeybindingService } from '../../keybinding/common/keybinding.js';
|
||||
import { ILogService } from '../../log/common/log.js';
|
||||
import { PickerQuickAccessProvider } from './pickerQuickAccess.js';
|
||||
import { IStorageService, WillSaveStateReason } from '../../storage/common/storage.js';
|
||||
import { ITelemetryService } from '../../telemetry/common/telemetry.js';
|
||||
let AbstractCommandsQuickAccessProvider = class AbstractCommandsQuickAccessProvider extends PickerQuickAccessProvider {
|
||||
static { AbstractCommandsQuickAccessProvider_1 = this; }
|
||||
static { this.PREFIX = '>'; }
|
||||
static { this.TFIDF_THRESHOLD = 0.5; }
|
||||
static { this.TFIDF_MAX_RESULTS = 5; }
|
||||
static { this.WORD_FILTER = or(matchesPrefix, matchesWords, matchesContiguousSubString); }
|
||||
constructor(options, instantiationService, keybindingService, commandService, telemetryService, dialogService) {
|
||||
super(AbstractCommandsQuickAccessProvider_1.PREFIX, options);
|
||||
this.keybindingService = keybindingService;
|
||||
this.commandService = commandService;
|
||||
this.telemetryService = telemetryService;
|
||||
this.dialogService = dialogService;
|
||||
this.commandsHistory = this._register(instantiationService.createInstance(CommandsHistory));
|
||||
this.options = options;
|
||||
}
|
||||
async _getPicks(filter, _disposables, token, runOptions) {
|
||||
// Ask subclass for all command picks
|
||||
const allCommandPicks = await this.getCommandPicks(token);
|
||||
if (token.isCancellationRequested) {
|
||||
return [];
|
||||
}
|
||||
const runTfidf = createSingleCallFunction(() => {
|
||||
const tfidf = new TfIdfCalculator();
|
||||
tfidf.updateDocuments(allCommandPicks.map(commandPick => ({
|
||||
key: commandPick.commandId,
|
||||
textChunks: [this.getTfIdfChunk(commandPick)]
|
||||
})));
|
||||
const result = tfidf.calculateScores(filter, token);
|
||||
return normalizeTfIdfScores(result)
|
||||
.filter(score => score.score > AbstractCommandsQuickAccessProvider_1.TFIDF_THRESHOLD)
|
||||
.slice(0, AbstractCommandsQuickAccessProvider_1.TFIDF_MAX_RESULTS);
|
||||
});
|
||||
// Filter
|
||||
const filteredCommandPicks = [];
|
||||
for (const commandPick of allCommandPicks) {
|
||||
const labelHighlights = AbstractCommandsQuickAccessProvider_1.WORD_FILTER(filter, commandPick.label) ?? undefined;
|
||||
const aliasHighlights = commandPick.commandAlias ? AbstractCommandsQuickAccessProvider_1.WORD_FILTER(filter, commandPick.commandAlias) ?? undefined : undefined;
|
||||
// Add if matching in label or alias
|
||||
if (labelHighlights || aliasHighlights) {
|
||||
commandPick.highlights = {
|
||||
label: labelHighlights,
|
||||
detail: this.options.showAlias ? aliasHighlights : undefined
|
||||
};
|
||||
filteredCommandPicks.push(commandPick);
|
||||
}
|
||||
// Also add if we have a 100% command ID match
|
||||
else if (filter === commandPick.commandId) {
|
||||
filteredCommandPicks.push(commandPick);
|
||||
}
|
||||
// Handle tf-idf scoring for the rest if there's a filter
|
||||
else if (filter.length >= 3) {
|
||||
const tfidf = runTfidf();
|
||||
if (token.isCancellationRequested) {
|
||||
return [];
|
||||
}
|
||||
// Add if we have a tf-idf score
|
||||
const tfidfScore = tfidf.find(score => score.key === commandPick.commandId);
|
||||
if (tfidfScore) {
|
||||
commandPick.tfIdfScore = tfidfScore.score;
|
||||
filteredCommandPicks.push(commandPick);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Add description to commands that have duplicate labels
|
||||
const mapLabelToCommand = new Map();
|
||||
for (const commandPick of filteredCommandPicks) {
|
||||
const existingCommandForLabel = mapLabelToCommand.get(commandPick.label);
|
||||
if (existingCommandForLabel) {
|
||||
commandPick.description = commandPick.commandId;
|
||||
existingCommandForLabel.description = existingCommandForLabel.commandId;
|
||||
}
|
||||
else {
|
||||
mapLabelToCommand.set(commandPick.label, commandPick);
|
||||
}
|
||||
}
|
||||
// Sort by MRU order and fallback to name otherwise
|
||||
filteredCommandPicks.sort((commandPickA, commandPickB) => {
|
||||
// If a result came from tf-idf, we want to put that towards the bottom
|
||||
if (commandPickA.tfIdfScore && commandPickB.tfIdfScore) {
|
||||
if (commandPickA.tfIdfScore === commandPickB.tfIdfScore) {
|
||||
return commandPickA.label.localeCompare(commandPickB.label); // prefer lexicographically smaller command
|
||||
}
|
||||
return commandPickB.tfIdfScore - commandPickA.tfIdfScore; // prefer higher tf-idf score
|
||||
}
|
||||
else if (commandPickA.tfIdfScore) {
|
||||
return 1; // first command has a score but other doesn't so other wins
|
||||
}
|
||||
else if (commandPickB.tfIdfScore) {
|
||||
return -1; // other command has a score but first doesn't so first wins
|
||||
}
|
||||
const commandACounter = this.commandsHistory.peek(commandPickA.commandId);
|
||||
const commandBCounter = this.commandsHistory.peek(commandPickB.commandId);
|
||||
if (commandACounter && commandBCounter) {
|
||||
return commandACounter > commandBCounter ? -1 : 1; // use more recently used command before older
|
||||
}
|
||||
if (commandACounter) {
|
||||
return -1; // first command was used, so it wins over the non used one
|
||||
}
|
||||
if (commandBCounter) {
|
||||
return 1; // other command was used so it wins over the command
|
||||
}
|
||||
if (this.options.suggestedCommandIds) {
|
||||
const commandASuggestion = this.options.suggestedCommandIds.has(commandPickA.commandId);
|
||||
const commandBSuggestion = this.options.suggestedCommandIds.has(commandPickB.commandId);
|
||||
if (commandASuggestion && commandBSuggestion) {
|
||||
return 0; // honor the order of the array
|
||||
}
|
||||
if (commandASuggestion) {
|
||||
return -1; // first command was suggested, so it wins over the non suggested one
|
||||
}
|
||||
if (commandBSuggestion) {
|
||||
return 1; // other command was suggested so it wins over the command
|
||||
}
|
||||
}
|
||||
// both commands were never used, so we sort by name
|
||||
return commandPickA.label.localeCompare(commandPickB.label);
|
||||
});
|
||||
const commandPicks = [];
|
||||
let addOtherSeparator = false;
|
||||
let addSuggestedSeparator = true;
|
||||
let addCommonlyUsedSeparator = !!this.options.suggestedCommandIds;
|
||||
for (let i = 0; i < filteredCommandPicks.length; i++) {
|
||||
const commandPick = filteredCommandPicks[i];
|
||||
// Separator: recently used
|
||||
if (i === 0 && this.commandsHistory.peek(commandPick.commandId)) {
|
||||
commandPicks.push({ type: 'separator', label: localize(1723, "recently used") });
|
||||
addOtherSeparator = true;
|
||||
}
|
||||
if (addSuggestedSeparator && commandPick.tfIdfScore !== undefined) {
|
||||
commandPicks.push({ type: 'separator', label: localize(1724, "similar commands") });
|
||||
addSuggestedSeparator = false;
|
||||
}
|
||||
// Separator: commonly used
|
||||
if (addCommonlyUsedSeparator && commandPick.tfIdfScore === undefined && !this.commandsHistory.peek(commandPick.commandId) && this.options.suggestedCommandIds?.has(commandPick.commandId)) {
|
||||
commandPicks.push({ type: 'separator', label: localize(1725, "commonly used") });
|
||||
addOtherSeparator = true;
|
||||
addCommonlyUsedSeparator = false;
|
||||
}
|
||||
// Separator: other commands
|
||||
if (addOtherSeparator && commandPick.tfIdfScore === undefined && !this.commandsHistory.peek(commandPick.commandId) && !this.options.suggestedCommandIds?.has(commandPick.commandId)) {
|
||||
commandPicks.push({ type: 'separator', label: localize(1726, "other commands") });
|
||||
addOtherSeparator = false;
|
||||
}
|
||||
// Command
|
||||
commandPicks.push(this.toCommandPick(commandPick, runOptions));
|
||||
}
|
||||
if (!this.hasAdditionalCommandPicks(filter, token)) {
|
||||
return commandPicks;
|
||||
}
|
||||
return {
|
||||
picks: commandPicks,
|
||||
additionalPicks: (async () => {
|
||||
const additionalCommandPicks = await this.getAdditionalCommandPicks(allCommandPicks, filteredCommandPicks, filter, token);
|
||||
if (token.isCancellationRequested) {
|
||||
return [];
|
||||
}
|
||||
const commandPicks = additionalCommandPicks.map(commandPick => this.toCommandPick(commandPick, runOptions));
|
||||
// Basically, if we haven't already added a separator, we add one before the additional picks so long
|
||||
// as one hasn't been added to the start of the array.
|
||||
if (addSuggestedSeparator && commandPicks[0]?.type !== 'separator') {
|
||||
commandPicks.unshift({ type: 'separator', label: localize(1727, "similar commands") });
|
||||
}
|
||||
return commandPicks;
|
||||
})()
|
||||
};
|
||||
}
|
||||
toCommandPick(commandPick, runOptions) {
|
||||
if (commandPick.type === 'separator') {
|
||||
return commandPick;
|
||||
}
|
||||
const keybinding = this.keybindingService.lookupKeybinding(commandPick.commandId);
|
||||
const ariaLabel = keybinding ?
|
||||
localize(1728, "{0}, {1}", commandPick.label, keybinding.getAriaLabel()) :
|
||||
commandPick.label;
|
||||
return {
|
||||
...commandPick,
|
||||
ariaLabel,
|
||||
detail: this.options.showAlias && commandPick.commandAlias !== commandPick.label ? commandPick.commandAlias : undefined,
|
||||
keybinding,
|
||||
accept: async () => {
|
||||
// Add to history
|
||||
this.commandsHistory.push(commandPick.commandId);
|
||||
// Telementry
|
||||
this.telemetryService.publicLog2('workbenchActionExecuted', {
|
||||
id: commandPick.commandId,
|
||||
from: runOptions?.from ?? 'quick open'
|
||||
});
|
||||
// Run
|
||||
try {
|
||||
commandPick.args?.length
|
||||
? await this.commandService.executeCommand(commandPick.commandId, ...commandPick.args)
|
||||
: await this.commandService.executeCommand(commandPick.commandId);
|
||||
}
|
||||
catch (error) {
|
||||
if (!isCancellationError(error)) {
|
||||
this.dialogService.error(localize(1729, "Command '{0}' resulted in an error", commandPick.label), toErrorMessage(error));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
// TF-IDF string to be indexed
|
||||
getTfIdfChunk({ label, commandAlias, commandDescription }) {
|
||||
let chunk = label;
|
||||
if (commandAlias && commandAlias !== label) {
|
||||
chunk += ` - ${commandAlias}`;
|
||||
}
|
||||
if (commandDescription && commandDescription.value !== label) {
|
||||
// If the original is the same as the value, don't add it
|
||||
chunk += ` - ${commandDescription.value === commandDescription.original ? commandDescription.value : `${commandDescription.value} (${commandDescription.original})`}`;
|
||||
}
|
||||
return chunk;
|
||||
}
|
||||
};
|
||||
AbstractCommandsQuickAccessProvider = AbstractCommandsQuickAccessProvider_1 = __decorate([
|
||||
__param(1, IInstantiationService),
|
||||
__param(2, IKeybindingService),
|
||||
__param(3, ICommandService),
|
||||
__param(4, ITelemetryService),
|
||||
__param(5, IDialogService)
|
||||
], AbstractCommandsQuickAccessProvider);
|
||||
export { AbstractCommandsQuickAccessProvider };
|
||||
let CommandsHistory = class CommandsHistory extends Disposable {
|
||||
static { CommandsHistory_1 = this; }
|
||||
static { this.DEFAULT_COMMANDS_HISTORY_LENGTH = 50; }
|
||||
static { this.PREF_KEY_CACHE = 'commandPalette.mru.cache'; }
|
||||
static { this.PREF_KEY_COUNTER = 'commandPalette.mru.counter'; }
|
||||
static { this.counter = 1; }
|
||||
static { this.hasChanges = false; }
|
||||
constructor(storageService, configurationService, logService) {
|
||||
super();
|
||||
this.storageService = storageService;
|
||||
this.configurationService = configurationService;
|
||||
this.logService = logService;
|
||||
this.configuredCommandsHistoryLength = 0;
|
||||
this.updateConfiguration();
|
||||
this.load();
|
||||
this.registerListeners();
|
||||
}
|
||||
registerListeners() {
|
||||
this._register(this.configurationService.onDidChangeConfiguration(e => this.updateConfiguration(e)));
|
||||
this._register(this.storageService.onWillSaveState(e => {
|
||||
if (e.reason === WillSaveStateReason.SHUTDOWN) {
|
||||
// Commands history is very dynamic and so we limit impact
|
||||
// on storage to only save on shutdown. This helps reduce
|
||||
// the overhead of syncing this data across machines.
|
||||
this.saveState();
|
||||
}
|
||||
}));
|
||||
}
|
||||
updateConfiguration(e) {
|
||||
if (e && !e.affectsConfiguration('workbench.commandPalette.history')) {
|
||||
return;
|
||||
}
|
||||
this.configuredCommandsHistoryLength = CommandsHistory_1.getConfiguredCommandHistoryLength(this.configurationService);
|
||||
if (CommandsHistory_1.cache && CommandsHistory_1.cache.limit !== this.configuredCommandsHistoryLength) {
|
||||
CommandsHistory_1.cache.limit = this.configuredCommandsHistoryLength;
|
||||
CommandsHistory_1.hasChanges = true;
|
||||
}
|
||||
}
|
||||
load() {
|
||||
const raw = this.storageService.get(CommandsHistory_1.PREF_KEY_CACHE, 0 /* StorageScope.PROFILE */);
|
||||
let serializedCache;
|
||||
if (raw) {
|
||||
try {
|
||||
serializedCache = JSON.parse(raw);
|
||||
}
|
||||
catch (error) {
|
||||
this.logService.error(`[CommandsHistory] invalid data: ${error}`);
|
||||
}
|
||||
}
|
||||
const cache = CommandsHistory_1.cache = new LRUCache(this.configuredCommandsHistoryLength, 1);
|
||||
if (serializedCache) {
|
||||
let entries;
|
||||
if (serializedCache.usesLRU) {
|
||||
entries = serializedCache.entries;
|
||||
}
|
||||
else {
|
||||
entries = serializedCache.entries.sort((a, b) => a.value - b.value);
|
||||
}
|
||||
entries.forEach(entry => cache.set(entry.key, entry.value));
|
||||
}
|
||||
CommandsHistory_1.counter = this.storageService.getNumber(CommandsHistory_1.PREF_KEY_COUNTER, 0 /* StorageScope.PROFILE */, CommandsHistory_1.counter);
|
||||
}
|
||||
push(commandId) {
|
||||
if (!CommandsHistory_1.cache) {
|
||||
return;
|
||||
}
|
||||
CommandsHistory_1.cache.set(commandId, CommandsHistory_1.counter++); // set counter to command
|
||||
CommandsHistory_1.hasChanges = true;
|
||||
}
|
||||
peek(commandId) {
|
||||
return CommandsHistory_1.cache?.peek(commandId);
|
||||
}
|
||||
saveState() {
|
||||
if (!CommandsHistory_1.cache) {
|
||||
return;
|
||||
}
|
||||
if (!CommandsHistory_1.hasChanges) {
|
||||
return;
|
||||
}
|
||||
const serializedCache = { usesLRU: true, entries: [] };
|
||||
CommandsHistory_1.cache.forEach((value, key) => serializedCache.entries.push({ key, value }));
|
||||
this.storageService.store(CommandsHistory_1.PREF_KEY_CACHE, JSON.stringify(serializedCache), 0 /* StorageScope.PROFILE */, 0 /* StorageTarget.USER */);
|
||||
this.storageService.store(CommandsHistory_1.PREF_KEY_COUNTER, CommandsHistory_1.counter, 0 /* StorageScope.PROFILE */, 0 /* StorageTarget.USER */);
|
||||
CommandsHistory_1.hasChanges = false;
|
||||
}
|
||||
static getConfiguredCommandHistoryLength(configurationService) {
|
||||
const config = configurationService.getValue();
|
||||
const configuredCommandHistoryLength = config.workbench?.commandPalette?.history;
|
||||
if (typeof configuredCommandHistoryLength === 'number') {
|
||||
return configuredCommandHistoryLength;
|
||||
}
|
||||
return CommandsHistory_1.DEFAULT_COMMANDS_HISTORY_LENGTH;
|
||||
}
|
||||
};
|
||||
CommandsHistory = CommandsHistory_1 = __decorate([
|
||||
__param(0, IStorageService),
|
||||
__param(1, IConfigurationService),
|
||||
__param(2, ILogService)
|
||||
], CommandsHistory);
|
||||
export { CommandsHistory };
|
||||
//# sourceMappingURL=commandsQuickAccess.js.map
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,76 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
||||
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
||||
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
||||
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||
};
|
||||
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
||||
return function (target, key) { decorator(target, key, paramIndex); }
|
||||
};
|
||||
var HelpQuickAccessProvider_1;
|
||||
import { localize } from '../../../nls.js';
|
||||
import { Registry } from '../../registry/common/platform.js';
|
||||
import { DisposableStore } from '../../../base/common/lifecycle.js';
|
||||
import { IKeybindingService } from '../../keybinding/common/keybinding.js';
|
||||
import { Extensions } from '../common/quickAccess.js';
|
||||
import { IQuickInputService } from '../common/quickInput.js';
|
||||
let HelpQuickAccessProvider = class HelpQuickAccessProvider {
|
||||
static { HelpQuickAccessProvider_1 = this; }
|
||||
static { this.PREFIX = '?'; }
|
||||
constructor(quickInputService, keybindingService) {
|
||||
this.quickInputService = quickInputService;
|
||||
this.keybindingService = keybindingService;
|
||||
this.registry = Registry.as(Extensions.Quickaccess);
|
||||
}
|
||||
provide(picker) {
|
||||
const disposables = new DisposableStore();
|
||||
// Open a picker with the selected value if picked
|
||||
disposables.add(picker.onDidAccept(() => {
|
||||
const [item] = picker.selectedItems;
|
||||
if (item) {
|
||||
this.quickInputService.quickAccess.show(item.prefix, { preserveValue: true });
|
||||
}
|
||||
}));
|
||||
// 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_1.PREFIX.length));
|
||||
if (providerDescriptor && providerDescriptor.prefix && providerDescriptor.prefix !== HelpQuickAccessProvider_1.PREFIX) {
|
||||
this.quickInputService.quickAccess.show(providerDescriptor.prefix, { preserveValue: true });
|
||||
}
|
||||
}));
|
||||
// Fill in all providers
|
||||
picker.items = this.getQuickAccessProviders().filter(p => p.prefix !== HelpQuickAccessProvider_1.PREFIX);
|
||||
return disposables;
|
||||
}
|
||||
getQuickAccessProviders() {
|
||||
const providers = this.registry
|
||||
.getQuickAccessProviders()
|
||||
.sort((providerA, providerB) => providerA.prefix.localeCompare(providerB.prefix))
|
||||
.flatMap(provider => this.createPicks(provider));
|
||||
return providers;
|
||||
}
|
||||
createPicks(provider) {
|
||||
return provider.helpEntries.map(helpEntry => {
|
||||
const prefix = helpEntry.prefix || provider.prefix;
|
||||
const label = prefix || '\u2026' /* ... */;
|
||||
return {
|
||||
prefix,
|
||||
label,
|
||||
keybinding: helpEntry.commandId ? this.keybindingService.lookupKeybinding(helpEntry.commandId) : undefined,
|
||||
ariaLabel: localize(1730, "{0}, {1}", label, helpEntry.description),
|
||||
description: helpEntry.description
|
||||
};
|
||||
});
|
||||
}
|
||||
};
|
||||
HelpQuickAccessProvider = HelpQuickAccessProvider_1 = __decorate([
|
||||
__param(0, IQuickInputService),
|
||||
__param(1, IKeybindingService)
|
||||
], HelpQuickAccessProvider);
|
||||
export { HelpQuickAccessProvider };
|
||||
//# sourceMappingURL=helpQuickAccess.js.map
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,486 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.quick-input-widget {
|
||||
position: absolute;
|
||||
width: 600px;
|
||||
z-index: 2550;
|
||||
left: 50%;
|
||||
-webkit-app-region: no-drag;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.quick-input-titlebar {
|
||||
cursor: grab;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-top-right-radius: 5px;
|
||||
border-top-left-radius: 5px;
|
||||
}
|
||||
|
||||
.quick-input-left-action-bar {
|
||||
display: flex;
|
||||
margin-left: 4px;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
/* give some space between input and action bar */
|
||||
.quick-input-inline-action-bar > .actions-container > .action-item:first-child {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
/* center horizontally */
|
||||
.quick-input-inline-action-bar > .actions-container > .action-item {
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.quick-input-title {
|
||||
cursor: grab;
|
||||
padding: 3px 0px;
|
||||
text-align: center;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.quick-input-right-action-bar {
|
||||
display: flex;
|
||||
margin-right: 4px;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.quick-input-right-action-bar > .actions-container {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.quick-input-right-action-bar > .actions-container > .action-item {
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
.quick-input-titlebar .monaco-action-bar .action-label.codicon {
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
.quick-input-description {
|
||||
margin: 6px 6px 6px 11px;
|
||||
}
|
||||
|
||||
.quick-input-header .quick-input-description {
|
||||
margin: 4px 2px;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.quick-input-header {
|
||||
cursor: grab;
|
||||
display: flex;
|
||||
padding: 6px 6px 2px 6px;
|
||||
}
|
||||
|
||||
.quick-input-widget.hidden-input .quick-input-header {
|
||||
/* reduce margins and paddings when input box hidden */
|
||||
padding: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.quick-input-and-message {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-grow: 1;
|
||||
min-width: 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.quick-input-check-all {
|
||||
align-self: center;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.quick-input-widget .quick-input-header .monaco-checkbox {
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.quick-input-filter {
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.quick-input-box {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.quick-input-widget.show-checkboxes .quick-input-box,
|
||||
.quick-input-widget.show-checkboxes .quick-input-message {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.quick-input-visible-count {
|
||||
position: absolute;
|
||||
left: -10000px;
|
||||
}
|
||||
|
||||
.quick-input-count {
|
||||
align-self: center;
|
||||
position: absolute;
|
||||
right: 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.quick-input-count .monaco-count-badge {
|
||||
vertical-align: middle;
|
||||
padding: 2px 4px;
|
||||
border-radius: 2px;
|
||||
min-height: auto;
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
.quick-input-action {
|
||||
margin-left: 6px;
|
||||
}
|
||||
|
||||
.quick-input-action .monaco-text-button {
|
||||
font-size: 11px;
|
||||
padding: 0 6px;
|
||||
display: flex;
|
||||
height: 25px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.quick-input-message {
|
||||
margin-top: -1px;
|
||||
padding: 5px;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
.quick-input-message > .codicon {
|
||||
margin: 0 0.2em;
|
||||
vertical-align: text-bottom;
|
||||
}
|
||||
|
||||
/* Links in descriptions & validations */
|
||||
.quick-input-message a {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.quick-input-progress.monaco-progress-container {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.quick-input-list {
|
||||
line-height: 22px;
|
||||
}
|
||||
|
||||
.quick-input-widget.hidden-input .quick-input-list {
|
||||
margin-top: 4px;
|
||||
/* reduce margins when input box hidden */
|
||||
padding-bottom: 4px;
|
||||
}
|
||||
|
||||
.quick-input-list .monaco-list {
|
||||
overflow: hidden;
|
||||
max-height: calc(20 * 22px);
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
.quick-input-list .monaco-scrollable-element {
|
||||
padding: 0px 6px;
|
||||
}
|
||||
|
||||
.quick-input-list .quick-input-list-entry {
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
padding: 0 6px;
|
||||
}
|
||||
|
||||
.quick-input-list .quick-input-list-entry.quick-input-list-separator-border {
|
||||
border-top-width: 1px;
|
||||
border-top-style: solid;
|
||||
}
|
||||
|
||||
.quick-input-list .monaco-list-row {
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.quick-input-list .monaco-list-row[data-index="0"] .quick-input-list-entry.quick-input-list-separator-border {
|
||||
border-top-style: none;
|
||||
}
|
||||
|
||||
.quick-input-list .quick-input-list-label {
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
height: 100%;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.quick-input-widget .monaco-checkbox {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.quick-input-widget .quick-input-list .monaco-checkbox,
|
||||
.quick-input-widget .quick-input-tree .monaco-checkbox {
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.quick-input-list .quick-input-list-icon {
|
||||
background-size: 16px;
|
||||
background-position: left center;
|
||||
background-repeat: no-repeat;
|
||||
padding-right: 6px;
|
||||
width: 16px;
|
||||
height: 22px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.quick-input-list .quick-input-list-rows {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
flex: 1;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.quick-input-list .quick-input-list-rows > .quick-input-list-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.quick-input-list .quick-input-list-rows > .quick-input-list-row .monaco-icon-label,
|
||||
.quick-input-list .quick-input-list-rows > .quick-input-list-row .monaco-icon-label .monaco-icon-label-container > .monaco-icon-name-container {
|
||||
flex: 1;
|
||||
/* make sure the icon label grows within the row */
|
||||
}
|
||||
|
||||
.quick-input-list .quick-input-list-rows > .quick-input-list-row .codicon[class*='codicon-'] {
|
||||
vertical-align: text-bottom;
|
||||
}
|
||||
|
||||
.quick-input-list .quick-input-list-rows .monaco-highlighted-label > span {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.quick-input-list .quick-input-list-entry .quick-input-list-entry-keybinding {
|
||||
margin-right: 8px;
|
||||
/* separate from the separator label or scrollbar if any */
|
||||
}
|
||||
|
||||
.quick-input-list .quick-input-list-label-meta {
|
||||
opacity: 0.7;
|
||||
line-height: normal;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* preserve list-like styling instead of tree-like styling */
|
||||
.quick-input-list .monaco-list .monaco-list-row .monaco-highlighted-label .highlight {
|
||||
font-weight: bold;
|
||||
background-color: unset;
|
||||
color: var(--vscode-list-highlightForeground) !important;
|
||||
}
|
||||
|
||||
/* preserve list-like styling instead of tree-like styling */
|
||||
.quick-input-list .monaco-list .monaco-list-row.focused .monaco-highlighted-label .highlight {
|
||||
color: var(--vscode-list-focusHighlightForeground) !important;
|
||||
}
|
||||
|
||||
.quick-input-list .quick-input-list-entry .quick-input-list-separator {
|
||||
margin-right: 4px;
|
||||
/* separate from keybindings or actions */
|
||||
}
|
||||
|
||||
.quick-input-list .quick-input-list-entry-action-bar {
|
||||
display: flex;
|
||||
flex: 0;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.quick-input-list .quick-input-list-entry-action-bar .action-label {
|
||||
/*
|
||||
* By default, actions in the quick input action bar are hidden
|
||||
* until hovered over them or selected.
|
||||
*/
|
||||
display: none;
|
||||
}
|
||||
|
||||
.quick-input-list .quick-input-list-entry-action-bar .action-label.codicon {
|
||||
margin-right: 4px;
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
.quick-input-list .quick-input-list-entry-action-bar {
|
||||
margin-top: 1px;
|
||||
}
|
||||
|
||||
.quick-input-list .quick-input-list-entry-action-bar {
|
||||
margin-right: 4px;
|
||||
/* separate from scrollbar */
|
||||
}
|
||||
|
||||
.quick-input-list .quick-input-list-entry .quick-input-list-entry-action-bar .action-label.always-visible,
|
||||
.quick-input-list .quick-input-list-entry:hover .quick-input-list-entry-action-bar .action-label,
|
||||
.quick-input-list .quick-input-list-entry.focus-inside .quick-input-list-entry-action-bar .action-label,
|
||||
.quick-input-list .monaco-list-row.focused .quick-input-list-entry-action-bar .action-label,
|
||||
.quick-input-list .monaco-list-row.passive-focused .quick-input-list-entry-action-bar .action-label {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.quick-input-list > .monaco-list:focus .monaco-list-row.focused {
|
||||
outline: 1px solid var(--vscode-list-focusOutline) !important;
|
||||
outline-offset: -1px;
|
||||
}
|
||||
|
||||
.quick-input-list > .monaco-list:focus .monaco-list-row.focused .quick-input-list-entry.quick-input-list-separator-border {
|
||||
border-color: transparent;
|
||||
}
|
||||
|
||||
/* focused items in quick pick */
|
||||
.quick-input-list .monaco-list-row.focused .monaco-keybinding-key,
|
||||
.quick-input-list .monaco-list-row.focused .quick-input-list-entry .quick-input-list-separator {
|
||||
color: inherit
|
||||
}
|
||||
|
||||
.quick-input-list .monaco-list-row.focused .monaco-keybinding-key {
|
||||
background: none;
|
||||
}
|
||||
|
||||
.quick-input-list .quick-input-list-separator-as-item {
|
||||
padding: 4px 6px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
/* Quick input separators as full-row item */
|
||||
.quick-input-list .quick-input-list-separator-as-item .label-name {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.quick-input-list .quick-input-list-separator-as-item .label-description {
|
||||
/* Override default description opacity so we don't have a contrast ratio issue. */
|
||||
opacity: 1 !important;
|
||||
}
|
||||
|
||||
/* Hide border when the item becomes the sticky one */
|
||||
.quick-input-list .monaco-tree-sticky-row .quick-input-list-entry.quick-input-list-separator-as-item.quick-input-list-separator-border {
|
||||
border-top-style: none;
|
||||
}
|
||||
|
||||
/* Give sticky row the same padding as the scrollable list */
|
||||
.quick-input-list .monaco-tree-sticky-row {
|
||||
padding: 0 5px;
|
||||
}
|
||||
|
||||
/* Hide the twistie containers so that there isn't blank indent */
|
||||
.quick-input-list .monaco-tl-twistie {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* Tree */
|
||||
|
||||
.quick-input-tree .monaco-list {
|
||||
overflow: hidden;
|
||||
max-height: calc(20 * 22px);
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
.quick-input-tree .quick-input-tree-entry {
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
padding: 0 6px;
|
||||
}
|
||||
|
||||
.quick-input-tree .quick-input-tree-label {
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
height: 100%;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.quick-input-tree .quick-input-tree-icon {
|
||||
background-size: 16px;
|
||||
background-position: left center;
|
||||
background-repeat: no-repeat;
|
||||
padding-right: 6px;
|
||||
width: 16px;
|
||||
height: 22px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.quick-input-tree .quick-input-tree-rows {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
flex: 1;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.quick-input-tree .quick-input-tree-rows > .quick-input-tree-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.quick-input-tree .quick-input-tree-rows > .quick-input-tree-row .monaco-icon-label,
|
||||
.quick-input-tree .quick-input-tree-rows > .quick-input-tree-row .monaco-icon-label .monaco-icon-label-container > .monaco-icon-name-container {
|
||||
flex: 1;
|
||||
/* make sure the icon label grows within the row */
|
||||
}
|
||||
|
||||
.quick-input-tree .quick-input-tree-rows > .quick-input-tree-row .codicon[class*='codicon-'] {
|
||||
vertical-align: text-bottom;
|
||||
}
|
||||
|
||||
.quick-input-tree .quick-input-tree-rows .monaco-highlighted-label > span {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.quick-input-tree .quick-input-tree-entry-action-bar {
|
||||
display: flex;
|
||||
flex: 0;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.quick-input-tree .quick-input-tree-entry-action-bar .action-label {
|
||||
/*
|
||||
* By default, actions in the quick input action bar are hidden
|
||||
* until hovered over them or selected.
|
||||
*/
|
||||
display: none;
|
||||
}
|
||||
|
||||
.quick-input-tree .quick-input-tree-entry-action-bar .action-label.codicon {
|
||||
margin-right: 4px;
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
.quick-input-tree .quick-input-tree-entry-action-bar {
|
||||
margin-top: 1px;
|
||||
}
|
||||
|
||||
.quick-input-tree .quick-input-tree-entry-action-bar {
|
||||
margin-right: 4px;
|
||||
/* separate from scrollbar */
|
||||
}
|
||||
|
||||
.quick-input-tree .quick-input-tree-entry .quick-input-tree-entry-action-bar .action-label.always-visible,
|
||||
.quick-input-tree .quick-input-tree-entry:hover .quick-input-tree-entry-action-bar .action-label,
|
||||
.quick-input-tree .quick-input-tree-entry.focus-inside .quick-input-tree-entry-action-bar .action-label,
|
||||
.quick-input-tree .monaco-list-row.focused .quick-input-tree-entry-action-bar .action-label,
|
||||
.quick-input-tree .monaco-list-row.passive-focused .quick-input-tree-entry-action-bar .action-label {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.quick-input-tree > .monaco-list:focus .monaco-list-row.focused {
|
||||
outline: 1px solid var(--vscode-list-focusOutline) !important;
|
||||
outline-offset: -1px;
|
||||
}
|
||||
@@ -0,0 +1,271 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import { timeout } from '../../../base/common/async.js';
|
||||
import { CancellationTokenSource } from '../../../base/common/cancellation.js';
|
||||
import { Disposable, DisposableStore, MutableDisposable } from '../../../base/common/lifecycle.js';
|
||||
import { isFunction } from '../../../base/common/types.js';
|
||||
export var TriggerAction;
|
||||
(function (TriggerAction) {
|
||||
/**
|
||||
* Do nothing after the button was clicked.
|
||||
*/
|
||||
TriggerAction[TriggerAction["NO_ACTION"] = 0] = "NO_ACTION";
|
||||
/**
|
||||
* Close the picker.
|
||||
*/
|
||||
TriggerAction[TriggerAction["CLOSE_PICKER"] = 1] = "CLOSE_PICKER";
|
||||
/**
|
||||
* Update the results of the picker.
|
||||
*/
|
||||
TriggerAction[TriggerAction["REFRESH_PICKER"] = 2] = "REFRESH_PICKER";
|
||||
/**
|
||||
* Remove the item from the picker.
|
||||
*/
|
||||
TriggerAction[TriggerAction["REMOVE_ITEM"] = 3] = "REMOVE_ITEM";
|
||||
})(TriggerAction || (TriggerAction = {}));
|
||||
function isPicksWithActive(obj) {
|
||||
const candidate = obj;
|
||||
return Array.isArray(candidate.items);
|
||||
}
|
||||
function isFastAndSlowPicks(obj) {
|
||||
const candidate = obj;
|
||||
return !!candidate.picks && candidate.additionalPicks instanceof Promise;
|
||||
}
|
||||
export class PickerQuickAccessProvider extends Disposable {
|
||||
constructor(prefix, options) {
|
||||
super();
|
||||
this.prefix = prefix;
|
||||
this.options = options;
|
||||
}
|
||||
provide(picker, token, runOptions) {
|
||||
const disposables = new DisposableStore();
|
||||
// Apply options if any
|
||||
picker.canAcceptInBackground = !!this.options?.canAcceptInBackground;
|
||||
// Disable filtering & sorting, we control the results
|
||||
picker.matchOnLabel = picker.matchOnDescription = picker.matchOnDetail = picker.sortByLabel = false;
|
||||
// Set initial picks and update on type
|
||||
let picksCts = undefined;
|
||||
const picksDisposable = disposables.add(new MutableDisposable());
|
||||
const updatePickerItems = async () => {
|
||||
// Cancel any previous ask for picks and busy
|
||||
picksCts?.dispose(true);
|
||||
picker.busy = false;
|
||||
// Setting the .value will call dispose() on the previous value, so we need to do this AFTER cancelling with dispose(true).
|
||||
const picksDisposables = picksDisposable.value = new DisposableStore();
|
||||
// Create new cancellation source for this run
|
||||
picksCts = picksDisposables.add(new CancellationTokenSource(token));
|
||||
// Collect picks and support both long running and short or combined
|
||||
const picksToken = picksCts.token;
|
||||
let picksFilter = picker.value.substring(this.prefix.length);
|
||||
if (!this.options?.shouldSkipTrimPickFilter) {
|
||||
picksFilter = picksFilter.trim();
|
||||
}
|
||||
const providedPicks = this._getPicks(picksFilter, picksDisposables, picksToken, runOptions);
|
||||
const applyPicks = (picks, skipEmpty) => {
|
||||
let items;
|
||||
let activeItem = undefined;
|
||||
if (isPicksWithActive(picks)) {
|
||||
items = picks.items;
|
||||
activeItem = picks.active;
|
||||
}
|
||||
else {
|
||||
items = picks;
|
||||
}
|
||||
if (items.length === 0) {
|
||||
if (skipEmpty) {
|
||||
return false;
|
||||
}
|
||||
// We show the no results pick if we have no input to prevent completely empty pickers #172613
|
||||
if ((picksFilter.length > 0 || picker.hideInput) && this.options?.noResultsPick) {
|
||||
if (isFunction(this.options.noResultsPick)) {
|
||||
items = [this.options.noResultsPick(picksFilter)];
|
||||
}
|
||||
else {
|
||||
items = [this.options.noResultsPick];
|
||||
}
|
||||
}
|
||||
}
|
||||
picker.items = items;
|
||||
if (activeItem) {
|
||||
picker.activeItems = [activeItem];
|
||||
}
|
||||
return true;
|
||||
};
|
||||
const applyFastAndSlowPicks = async (fastAndSlowPicks) => {
|
||||
let fastPicksApplied = false;
|
||||
let slowPicksApplied = false;
|
||||
await Promise.all([
|
||||
// Fast Picks: if `mergeDelay` is configured, in order to reduce
|
||||
// amount of flicker, we race against the slow picks over some delay
|
||||
// and then set the fast picks.
|
||||
// If the slow picks are faster, we reduce the flicker by only
|
||||
// setting the items once.
|
||||
(async () => {
|
||||
if (typeof fastAndSlowPicks.mergeDelay === 'number') {
|
||||
await timeout(fastAndSlowPicks.mergeDelay);
|
||||
if (picksToken.isCancellationRequested) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!slowPicksApplied) {
|
||||
fastPicksApplied = applyPicks(fastAndSlowPicks.picks, true /* skip over empty to reduce flicker */);
|
||||
}
|
||||
})(),
|
||||
// Slow Picks: we await the slow picks and then set them at
|
||||
// once together with the fast picks, but only if we actually
|
||||
// have additional results.
|
||||
(async () => {
|
||||
picker.busy = true;
|
||||
try {
|
||||
const awaitedAdditionalPicks = await fastAndSlowPicks.additionalPicks;
|
||||
if (picksToken.isCancellationRequested) {
|
||||
return;
|
||||
}
|
||||
let picks;
|
||||
let activePick = undefined;
|
||||
if (isPicksWithActive(fastAndSlowPicks.picks)) {
|
||||
picks = fastAndSlowPicks.picks.items;
|
||||
activePick = fastAndSlowPicks.picks.active;
|
||||
}
|
||||
else {
|
||||
picks = fastAndSlowPicks.picks;
|
||||
}
|
||||
let additionalPicks;
|
||||
let additionalActivePick = undefined;
|
||||
if (isPicksWithActive(awaitedAdditionalPicks)) {
|
||||
additionalPicks = awaitedAdditionalPicks.items;
|
||||
additionalActivePick = awaitedAdditionalPicks.active;
|
||||
}
|
||||
else {
|
||||
additionalPicks = awaitedAdditionalPicks;
|
||||
}
|
||||
if (additionalPicks.length > 0 || !fastPicksApplied) {
|
||||
// If we do not have any activePick or additionalActivePick
|
||||
// we try to preserve the currently active pick from the
|
||||
// fast results. This fixes an issue where the user might
|
||||
// have made a pick active before the additional results
|
||||
// kick in.
|
||||
// See https://github.com/microsoft/vscode/issues/102480
|
||||
let fallbackActivePick = undefined;
|
||||
if (!activePick && !additionalActivePick) {
|
||||
const fallbackActivePickCandidate = picker.activeItems[0];
|
||||
if (fallbackActivePickCandidate && picks.indexOf(fallbackActivePickCandidate) !== -1) {
|
||||
fallbackActivePick = fallbackActivePickCandidate;
|
||||
}
|
||||
}
|
||||
applyPicks({
|
||||
items: [...picks, ...additionalPicks],
|
||||
active: activePick || additionalActivePick || fallbackActivePick
|
||||
});
|
||||
}
|
||||
}
|
||||
finally {
|
||||
if (!picksToken.isCancellationRequested) {
|
||||
picker.busy = false;
|
||||
}
|
||||
slowPicksApplied = true;
|
||||
}
|
||||
})()
|
||||
]);
|
||||
};
|
||||
// No Picks
|
||||
if (providedPicks === null) {
|
||||
// Ignore
|
||||
}
|
||||
// Fast and Slow Picks
|
||||
else if (isFastAndSlowPicks(providedPicks)) {
|
||||
await applyFastAndSlowPicks(providedPicks);
|
||||
}
|
||||
// Fast Picks
|
||||
else if (!(providedPicks instanceof Promise)) {
|
||||
applyPicks(providedPicks);
|
||||
}
|
||||
// Slow Picks
|
||||
else {
|
||||
picker.busy = true;
|
||||
try {
|
||||
const awaitedPicks = await providedPicks;
|
||||
if (picksToken.isCancellationRequested) {
|
||||
return;
|
||||
}
|
||||
if (isFastAndSlowPicks(awaitedPicks)) {
|
||||
await applyFastAndSlowPicks(awaitedPicks);
|
||||
}
|
||||
else {
|
||||
applyPicks(awaitedPicks);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
if (!picksToken.isCancellationRequested) {
|
||||
picker.busy = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
disposables.add(picker.onDidChangeValue(() => updatePickerItems()));
|
||||
updatePickerItems();
|
||||
// Accept the pick on accept and hide picker
|
||||
disposables.add(picker.onDidAccept(event => {
|
||||
if (runOptions?.handleAccept) {
|
||||
if (!event.inBackground) {
|
||||
picker.hide(); // hide picker unless we accept in background
|
||||
}
|
||||
runOptions.handleAccept?.(picker.activeItems[0], event.inBackground);
|
||||
return;
|
||||
}
|
||||
const [item] = picker.selectedItems;
|
||||
if (typeof item?.accept === 'function') {
|
||||
if (!event.inBackground) {
|
||||
picker.hide(); // hide picker unless we accept in background
|
||||
}
|
||||
item.accept(picker.keyMods, event);
|
||||
}
|
||||
}));
|
||||
const buttonTrigger = async (button, item) => {
|
||||
if (typeof item.trigger !== 'function') {
|
||||
return;
|
||||
}
|
||||
const buttonIndex = item.buttons?.indexOf(button) ?? -1;
|
||||
if (buttonIndex >= 0) {
|
||||
const result = item.trigger(buttonIndex, picker.keyMods);
|
||||
const action = (typeof result === 'number') ? result : await result;
|
||||
if (token.isCancellationRequested) {
|
||||
return;
|
||||
}
|
||||
switch (action) {
|
||||
case TriggerAction.NO_ACTION:
|
||||
break;
|
||||
case TriggerAction.CLOSE_PICKER:
|
||||
picker.hide();
|
||||
break;
|
||||
case TriggerAction.REFRESH_PICKER:
|
||||
updatePickerItems();
|
||||
break;
|
||||
case TriggerAction.REMOVE_ITEM: {
|
||||
const index = picker.items.indexOf(item);
|
||||
if (index !== -1) {
|
||||
const items = picker.items.slice();
|
||||
const removed = items.splice(index, 1);
|
||||
const activeItems = picker.activeItems.filter(activeItem => activeItem !== removed[0]);
|
||||
const keepScrollPositionBefore = picker.keepScrollPosition;
|
||||
picker.keepScrollPosition = true;
|
||||
picker.items = items;
|
||||
if (activeItems) {
|
||||
picker.activeItems = activeItems;
|
||||
}
|
||||
picker.keepScrollPosition = keepScrollPositionBefore;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
// Trigger the pick with button index if button triggered
|
||||
disposables.add(picker.onDidTriggerItemButton(({ button, item }) => buttonTrigger(button, item)));
|
||||
disposables.add(picker.onDidTriggerSeparatorButton(({ button, separator }) => buttonTrigger(button, separator)));
|
||||
return disposables;
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=pickerQuickAccess.js.map
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,208 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
||||
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
||||
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
||||
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||
};
|
||||
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
||||
return function (target, key) { decorator(target, key, paramIndex); }
|
||||
};
|
||||
import { DeferredPromise } from '../../../base/common/async.js';
|
||||
import { CancellationTokenSource } from '../../../base/common/cancellation.js';
|
||||
import { Event } from '../../../base/common/event.js';
|
||||
import { Disposable, DisposableStore, isDisposable, toDisposable } from '../../../base/common/lifecycle.js';
|
||||
import { IInstantiationService } from '../../instantiation/common/instantiation.js';
|
||||
import { DefaultQuickAccessFilterValue, Extensions } from '../common/quickAccess.js';
|
||||
import { IQuickInputService, ItemActivation } from '../common/quickInput.js';
|
||||
import { Registry } from '../../registry/common/platform.js';
|
||||
let QuickAccessController = class QuickAccessController extends Disposable {
|
||||
constructor(quickInputService, instantiationService) {
|
||||
super();
|
||||
this.quickInputService = quickInputService;
|
||||
this.instantiationService = instantiationService;
|
||||
this.registry = Registry.as(Extensions.Quickaccess);
|
||||
this.mapProviderToDescriptor = new Map();
|
||||
this.lastAcceptedPickerValues = new Map();
|
||||
this.visibleQuickAccess = undefined;
|
||||
this._register(toDisposable(() => {
|
||||
for (const provider of this.mapProviderToDescriptor.values()) {
|
||||
if (isDisposable(provider)) {
|
||||
provider.dispose();
|
||||
}
|
||||
}
|
||||
this.visibleQuickAccess?.picker.dispose();
|
||||
}));
|
||||
}
|
||||
show(value = '', options) {
|
||||
this.doShowOrPick(value, false, options);
|
||||
}
|
||||
doShowOrPick(value, pick, options) {
|
||||
// Find provider for the value to show
|
||||
const [provider, descriptor] = this.getOrInstantiateProvider(value, options?.enabledProviderPrefixes);
|
||||
// Return early if quick access is already showing on that same prefix
|
||||
const visibleQuickAccess = this.visibleQuickAccess;
|
||||
const visibleDescriptor = visibleQuickAccess?.descriptor;
|
||||
if (visibleQuickAccess && descriptor && visibleDescriptor === descriptor) {
|
||||
// Apply value only if it is more specific than the prefix
|
||||
// from the provider and we are not instructed to preserve
|
||||
if (value !== descriptor.prefix && !options?.preserveValue) {
|
||||
visibleQuickAccess.picker.value = value;
|
||||
}
|
||||
// Always adjust selection
|
||||
this.adjustValueSelection(visibleQuickAccess.picker, descriptor, options);
|
||||
return;
|
||||
}
|
||||
// Rewrite the filter value based on certain rules unless disabled
|
||||
if (descriptor && !options?.preserveValue) {
|
||||
let newValue = undefined;
|
||||
// If we have a visible provider with a value, take it's filter value but
|
||||
// rewrite to new provider prefix in case they differ
|
||||
if (visibleQuickAccess && visibleDescriptor && visibleDescriptor !== descriptor) {
|
||||
const newValueCandidateWithoutPrefix = visibleQuickAccess.value.substr(visibleDescriptor.prefix.length);
|
||||
if (newValueCandidateWithoutPrefix) {
|
||||
newValue = `${descriptor.prefix}${newValueCandidateWithoutPrefix}`;
|
||||
}
|
||||
}
|
||||
// Otherwise, take a default value as instructed
|
||||
if (!newValue) {
|
||||
const defaultFilterValue = provider?.defaultFilterValue;
|
||||
if (defaultFilterValue === DefaultQuickAccessFilterValue.LAST) {
|
||||
newValue = this.lastAcceptedPickerValues.get(descriptor);
|
||||
}
|
||||
else if (typeof defaultFilterValue === 'string') {
|
||||
newValue = `${descriptor.prefix}${defaultFilterValue}`;
|
||||
}
|
||||
}
|
||||
if (typeof newValue === 'string') {
|
||||
value = newValue;
|
||||
}
|
||||
}
|
||||
// Store the existing selection if there was one.
|
||||
const visibleSelection = visibleQuickAccess?.picker?.valueSelection;
|
||||
const visibleValue = visibleQuickAccess?.picker?.value;
|
||||
// Create a picker for the provider to use with the initial value
|
||||
// and adjust the filtering to exclude the prefix from filtering
|
||||
const disposables = new DisposableStore();
|
||||
const picker = disposables.add(this.quickInputService.createQuickPick({ useSeparators: true }));
|
||||
picker.value = value;
|
||||
this.adjustValueSelection(picker, descriptor, options);
|
||||
picker.placeholder = options?.placeholder ?? descriptor?.placeholder;
|
||||
picker.quickNavigate = options?.quickNavigateConfiguration;
|
||||
picker.hideInput = !!picker.quickNavigate && !visibleQuickAccess; // only hide input if there was no picker opened already
|
||||
if (typeof options?.itemActivation === 'number' || options?.quickNavigateConfiguration) {
|
||||
picker.itemActivation = options?.itemActivation ?? ItemActivation.SECOND /* quick nav is always second */;
|
||||
}
|
||||
picker.contextKey = descriptor?.contextKey;
|
||||
picker.filterValue = (value) => value.substring(descriptor ? descriptor.prefix.length : 0);
|
||||
// Pick mode: setup a promise that can be resolved
|
||||
// with the selected items and prevent execution
|
||||
let pickPromise = undefined;
|
||||
if (pick) {
|
||||
pickPromise = new DeferredPromise();
|
||||
disposables.add(Event.once(picker.onWillAccept)(e => {
|
||||
e.veto();
|
||||
picker.hide();
|
||||
}));
|
||||
}
|
||||
// Register listeners
|
||||
disposables.add(this.registerPickerListeners(picker, provider, descriptor, value, options));
|
||||
// Ask provider to fill the picker as needed if we have one
|
||||
// and pass over a cancellation token that will indicate when
|
||||
// the picker is hiding without a pick being made.
|
||||
const cts = disposables.add(new CancellationTokenSource());
|
||||
if (provider) {
|
||||
disposables.add(provider.provide(picker, cts.token, options?.providerOptions));
|
||||
}
|
||||
// Finally, trigger disposal and cancellation when the picker
|
||||
// hides depending on items selected or not.
|
||||
Event.once(picker.onDidHide)(() => {
|
||||
if (picker.selectedItems.length === 0) {
|
||||
cts.cancel();
|
||||
}
|
||||
// Start to dispose once picker hides
|
||||
disposables.dispose();
|
||||
// Resolve pick promise with selected items
|
||||
pickPromise?.complete(picker.selectedItems.slice(0));
|
||||
});
|
||||
// Finally, show the picker. This is important because a provider
|
||||
// may not call this and then our disposables would leak that rely
|
||||
// on the onDidHide event.
|
||||
picker.show();
|
||||
// If the previous picker had a selection and the value is unchanged, we should set that in the new picker.
|
||||
if (visibleSelection && visibleValue === value) {
|
||||
picker.valueSelection = visibleSelection;
|
||||
}
|
||||
// Pick mode: return with promise
|
||||
if (pick) {
|
||||
return pickPromise?.p;
|
||||
}
|
||||
}
|
||||
adjustValueSelection(picker, descriptor, options) {
|
||||
let valueSelection;
|
||||
// Preserve: just always put the cursor at the end
|
||||
if (options?.preserveValue) {
|
||||
valueSelection = [picker.value.length, picker.value.length];
|
||||
}
|
||||
// Otherwise: select the value up until the prefix
|
||||
else {
|
||||
valueSelection = [descriptor?.prefix.length ?? 0, picker.value.length];
|
||||
}
|
||||
picker.valueSelection = valueSelection;
|
||||
}
|
||||
registerPickerListeners(picker, provider, descriptor, value, options) {
|
||||
const disposables = new DisposableStore();
|
||||
// Remember as last visible picker and clean up once picker get's disposed
|
||||
const visibleQuickAccess = this.visibleQuickAccess = { picker, descriptor, value };
|
||||
disposables.add(toDisposable(() => {
|
||||
if (visibleQuickAccess === this.visibleQuickAccess) {
|
||||
this.visibleQuickAccess = undefined;
|
||||
}
|
||||
}));
|
||||
// Whenever the value changes, check if the provider has
|
||||
// changed and if so - re-create the picker from the beginning
|
||||
disposables.add(picker.onDidChangeValue(value => {
|
||||
const [providerForValue] = this.getOrInstantiateProvider(value, options?.enabledProviderPrefixes);
|
||||
if (providerForValue !== provider) {
|
||||
this.show(value, {
|
||||
enabledProviderPrefixes: options?.enabledProviderPrefixes,
|
||||
// do not rewrite value from user typing!
|
||||
preserveValue: true,
|
||||
// persist the value of the providerOptions from the original showing
|
||||
providerOptions: options?.providerOptions
|
||||
});
|
||||
}
|
||||
else {
|
||||
visibleQuickAccess.value = value; // remember the value in our visible one
|
||||
}
|
||||
}));
|
||||
// Remember picker input for future use when accepting
|
||||
if (descriptor) {
|
||||
disposables.add(picker.onDidAccept(() => {
|
||||
this.lastAcceptedPickerValues.set(descriptor, picker.value);
|
||||
}));
|
||||
}
|
||||
return disposables;
|
||||
}
|
||||
getOrInstantiateProvider(value, enabledProviderPrefixes) {
|
||||
const providerDescriptor = this.registry.getQuickAccessProvider(value);
|
||||
if (!providerDescriptor || enabledProviderPrefixes && !enabledProviderPrefixes?.includes(providerDescriptor.prefix)) {
|
||||
return [undefined, undefined];
|
||||
}
|
||||
let provider = this.mapProviderToDescriptor.get(providerDescriptor);
|
||||
if (!provider) {
|
||||
provider = this.instantiationService.createInstance(providerDescriptor.ctor);
|
||||
this.mapProviderToDescriptor.set(providerDescriptor, provider);
|
||||
}
|
||||
return [provider, providerDescriptor];
|
||||
}
|
||||
};
|
||||
QuickAccessController = __decorate([
|
||||
__param(0, IQuickInputService),
|
||||
__param(1, IInstantiationService)
|
||||
], QuickAccessController);
|
||||
export { QuickAccessController };
|
||||
//# sourceMappingURL=quickAccess.js.map
|
||||
File diff suppressed because one or more lines are too long
1010
_internal/editor/esm/vs/platform/quickinput/browser/quickInput.js
Normal file
1010
_internal/editor/esm/vs/platform/quickinput/browser/quickInput.js
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@@ -0,0 +1,189 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import { isMacintosh } from '../../../base/common/platform.js';
|
||||
import { localize } from '../../../nls.js';
|
||||
import { ContextKeyExpr } from '../../contextkey/common/contextkey.js';
|
||||
import { InputFocusedContext } from '../../contextkey/common/contextkeys.js';
|
||||
import { KeybindingsRegistry } from '../../keybinding/common/keybindingsRegistry.js';
|
||||
import { endOfQuickInputBoxContext, inQuickInputContext, quickInputTypeContextKeyValue } from './quickInput.js';
|
||||
import { IQuickInputService, QuickPickFocus } from '../common/quickInput.js';
|
||||
function registerQuickInputCommandAndKeybindingRule(rule, options = {}) {
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
weight: 200 /* KeybindingWeight.WorkbenchContrib */,
|
||||
when: inQuickInputContext,
|
||||
metadata: { description: localize(1741, "Used while in the context of any kind of quick input. If you change one keybinding for this command, you should change all of the other keybindings (modifier variants) of this command as well.") },
|
||||
...rule,
|
||||
secondary: getSecondary(rule.primary, rule.secondary ?? [], options)
|
||||
});
|
||||
}
|
||||
function registerQuickPickCommandAndKeybindingRule(rule, options = {}) {
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
weight: 200 /* KeybindingWeight.WorkbenchContrib */,
|
||||
when: ContextKeyExpr.and(ContextKeyExpr.or(
|
||||
// Only things that use Tree widgets
|
||||
ContextKeyExpr.equals(quickInputTypeContextKeyValue, "quickPick" /* QuickInputType.QuickPick */), ContextKeyExpr.equals(quickInputTypeContextKeyValue, "quickTree" /* QuickInputType.QuickTree */)), inQuickInputContext),
|
||||
metadata: { description: localize(1742, "Used while in the context of the quick pick. If you change one keybinding for this command, you should change all of the other keybindings (modifier variants) of this command as well.") },
|
||||
...rule,
|
||||
secondary: getSecondary(rule.primary, rule.secondary ?? [], options)
|
||||
});
|
||||
}
|
||||
const ctrlKeyMod = isMacintosh ? 256 /* KeyMod.WinCtrl */ : 2048 /* KeyMod.CtrlCmd */;
|
||||
// This function will generate all the combinations of keybindings for the given primary keybinding
|
||||
function getSecondary(primary, secondary, options = {}) {
|
||||
if (options.withAltMod) {
|
||||
secondary.push(512 /* KeyMod.Alt */ + primary);
|
||||
}
|
||||
if (options.withCtrlMod) {
|
||||
secondary.push(ctrlKeyMod + primary);
|
||||
if (options.withAltMod) {
|
||||
secondary.push(512 /* KeyMod.Alt */ + ctrlKeyMod + primary);
|
||||
}
|
||||
}
|
||||
if (options.withCmdMod && isMacintosh) {
|
||||
secondary.push(2048 /* KeyMod.CtrlCmd */ + primary);
|
||||
if (options.withCtrlMod) {
|
||||
secondary.push(2048 /* KeyMod.CtrlCmd */ + 256 /* KeyMod.WinCtrl */ + primary);
|
||||
}
|
||||
if (options.withAltMod) {
|
||||
secondary.push(2048 /* KeyMod.CtrlCmd */ + 512 /* KeyMod.Alt */ + primary);
|
||||
if (options.withCtrlMod) {
|
||||
secondary.push(2048 /* KeyMod.CtrlCmd */ + 512 /* KeyMod.Alt */ + 256 /* KeyMod.WinCtrl */ + primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
return secondary;
|
||||
}
|
||||
//#region Navigation
|
||||
function focusHandler(focus, focusOnQuickNatigate) {
|
||||
return accessor => {
|
||||
// Assuming this is a quick pick due to above when clause
|
||||
const currentQuickPick = accessor.get(IQuickInputService).currentQuickInput;
|
||||
if (!currentQuickPick) {
|
||||
return;
|
||||
}
|
||||
if (focusOnQuickNatigate && currentQuickPick.quickNavigate) {
|
||||
return currentQuickPick.focus(focusOnQuickNatigate);
|
||||
}
|
||||
return currentQuickPick.focus(focus);
|
||||
};
|
||||
}
|
||||
registerQuickPickCommandAndKeybindingRule({ id: 'quickInput.pageNext', primary: 12 /* KeyCode.PageDown */, handler: focusHandler(QuickPickFocus.NextPage) }, { withAltMod: true, withCtrlMod: true, withCmdMod: true });
|
||||
registerQuickPickCommandAndKeybindingRule({ id: 'quickInput.pagePrevious', primary: 11 /* KeyCode.PageUp */, handler: focusHandler(QuickPickFocus.PreviousPage) }, { withAltMod: true, withCtrlMod: true, withCmdMod: true });
|
||||
registerQuickPickCommandAndKeybindingRule({ id: 'quickInput.first', primary: ctrlKeyMod + 14 /* KeyCode.Home */, handler: focusHandler(QuickPickFocus.First) }, { withAltMod: true, withCmdMod: true });
|
||||
registerQuickPickCommandAndKeybindingRule({ id: 'quickInput.last', primary: ctrlKeyMod + 13 /* KeyCode.End */, handler: focusHandler(QuickPickFocus.Last) }, { withAltMod: true, withCmdMod: true });
|
||||
registerQuickPickCommandAndKeybindingRule({ id: 'quickInput.next', primary: 18 /* KeyCode.DownArrow */, handler: focusHandler(QuickPickFocus.Next) }, { withCtrlMod: true });
|
||||
registerQuickPickCommandAndKeybindingRule({ id: 'quickInput.previous', primary: 16 /* KeyCode.UpArrow */, handler: focusHandler(QuickPickFocus.Previous) }, { withCtrlMod: true });
|
||||
// The next & previous separator commands are interesting because if we are in quick access mode, we are already holding a modifier key down.
|
||||
// In this case, we want that modifier key+up/down to navigate to the next/previous item, not the next/previous separator.
|
||||
// To handle this, we have a separate command for navigating to the next/previous separator when we are not in quick access mode.
|
||||
// If, however, we are in quick access mode, and you hold down an additional modifier key, we will navigate to the next/previous separator.
|
||||
const nextSeparatorFallbackDesc = localize(1743, "If we're in quick access mode, this will navigate to the next item. If we are not in quick access mode, this will navigate to the next separator.");
|
||||
const prevSeparatorFallbackDesc = localize(1744, "If we're in quick access mode, this will navigate to the previous item. If we are not in quick access mode, this will navigate to the previous separator.");
|
||||
if (isMacintosh) {
|
||||
registerQuickPickCommandAndKeybindingRule({
|
||||
id: 'quickInput.nextSeparatorWithQuickAccessFallback',
|
||||
primary: 2048 /* KeyMod.CtrlCmd */ + 18 /* KeyCode.DownArrow */,
|
||||
handler: focusHandler(QuickPickFocus.NextSeparator, QuickPickFocus.Next),
|
||||
metadata: { description: nextSeparatorFallbackDesc }
|
||||
});
|
||||
registerQuickPickCommandAndKeybindingRule({
|
||||
id: 'quickInput.nextSeparator',
|
||||
primary: 2048 /* KeyMod.CtrlCmd */ + 512 /* KeyMod.Alt */ + 18 /* KeyCode.DownArrow */,
|
||||
// Since macOS has the cmd key as the primary modifier, we need to add this additional
|
||||
// keybinding to capture cmd+ctrl+upArrow
|
||||
secondary: [2048 /* KeyMod.CtrlCmd */ + 256 /* KeyMod.WinCtrl */ + 18 /* KeyCode.DownArrow */],
|
||||
handler: focusHandler(QuickPickFocus.NextSeparator)
|
||||
}, { withCtrlMod: true });
|
||||
registerQuickPickCommandAndKeybindingRule({
|
||||
id: 'quickInput.previousSeparatorWithQuickAccessFallback',
|
||||
primary: 2048 /* KeyMod.CtrlCmd */ + 16 /* KeyCode.UpArrow */,
|
||||
handler: focusHandler(QuickPickFocus.PreviousSeparator, QuickPickFocus.Previous),
|
||||
metadata: { description: prevSeparatorFallbackDesc }
|
||||
});
|
||||
registerQuickPickCommandAndKeybindingRule({
|
||||
id: 'quickInput.previousSeparator',
|
||||
primary: 2048 /* KeyMod.CtrlCmd */ + 512 /* KeyMod.Alt */ + 16 /* KeyCode.UpArrow */,
|
||||
// Since macOS has the cmd key as the primary modifier, we need to add this additional
|
||||
// keybinding to capture cmd+ctrl+upArrow
|
||||
secondary: [2048 /* KeyMod.CtrlCmd */ + 256 /* KeyMod.WinCtrl */ + 16 /* KeyCode.UpArrow */],
|
||||
handler: focusHandler(QuickPickFocus.PreviousSeparator)
|
||||
}, { withCtrlMod: true });
|
||||
}
|
||||
else {
|
||||
registerQuickPickCommandAndKeybindingRule({
|
||||
id: 'quickInput.nextSeparatorWithQuickAccessFallback',
|
||||
primary: 512 /* KeyMod.Alt */ + 18 /* KeyCode.DownArrow */,
|
||||
handler: focusHandler(QuickPickFocus.NextSeparator, QuickPickFocus.Next),
|
||||
metadata: { description: nextSeparatorFallbackDesc }
|
||||
});
|
||||
registerQuickPickCommandAndKeybindingRule({
|
||||
id: 'quickInput.nextSeparator',
|
||||
primary: 2048 /* KeyMod.CtrlCmd */ + 512 /* KeyMod.Alt */ + 18 /* KeyCode.DownArrow */,
|
||||
handler: focusHandler(QuickPickFocus.NextSeparator)
|
||||
});
|
||||
registerQuickPickCommandAndKeybindingRule({
|
||||
id: 'quickInput.previousSeparatorWithQuickAccessFallback',
|
||||
primary: 512 /* KeyMod.Alt */ + 16 /* KeyCode.UpArrow */,
|
||||
handler: focusHandler(QuickPickFocus.PreviousSeparator, QuickPickFocus.Previous),
|
||||
metadata: { description: prevSeparatorFallbackDesc }
|
||||
});
|
||||
registerQuickPickCommandAndKeybindingRule({
|
||||
id: 'quickInput.previousSeparator',
|
||||
primary: 2048 /* KeyMod.CtrlCmd */ + 512 /* KeyMod.Alt */ + 16 /* KeyCode.UpArrow */,
|
||||
handler: focusHandler(QuickPickFocus.PreviousSeparator)
|
||||
});
|
||||
}
|
||||
//#endregion
|
||||
//#region Accept
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: 'quickInput.accept',
|
||||
primary: 3 /* KeyCode.Enter */,
|
||||
weight: 200 /* KeybindingWeight.WorkbenchContrib */,
|
||||
when: ContextKeyExpr.and(
|
||||
// All other kinds of Quick things handle Accept, except Widget. In other words, Accepting is a detail on the things
|
||||
// that extend IQuickInput
|
||||
ContextKeyExpr.notEquals(quickInputTypeContextKeyValue, "quickWidget" /* QuickInputType.QuickWidget */), inQuickInputContext),
|
||||
metadata: { description: localize(1745, "Used while in the context of some quick input. If you change one keybinding for this command, you should change all of the other keybindings (modifier variants) of this command as well.") },
|
||||
handler: (accessor) => {
|
||||
const currentQuickPick = accessor.get(IQuickInputService).currentQuickInput;
|
||||
currentQuickPick?.accept();
|
||||
},
|
||||
secondary: getSecondary(3 /* KeyCode.Enter */, [], { withAltMod: true, withCtrlMod: true, withCmdMod: true })
|
||||
});
|
||||
registerQuickPickCommandAndKeybindingRule({
|
||||
id: 'quickInput.acceptInBackground',
|
||||
// If we are in the quick pick but the input box is not focused or our cursor is at the end of the input box
|
||||
when: ContextKeyExpr.and(inQuickInputContext, ContextKeyExpr.equals(quickInputTypeContextKeyValue, "quickPick" /* QuickInputType.QuickPick */), ContextKeyExpr.or(InputFocusedContext.negate(), endOfQuickInputBoxContext)),
|
||||
primary: 17 /* KeyCode.RightArrow */,
|
||||
// Need a little extra weight to ensure this keybinding is preferred over the default cmd+alt+right arrow keybinding
|
||||
// https://github.com/microsoft/vscode/blob/1451e4fbbbf074a4355cc537c35b547b80ce1c52/src/vs/workbench/browser/parts/editor/editorActions.ts#L1178-L1195
|
||||
weight: 200 /* KeybindingWeight.WorkbenchContrib */ + 50,
|
||||
handler: (accessor) => {
|
||||
const currentQuickPick = accessor.get(IQuickInputService).currentQuickInput;
|
||||
currentQuickPick?.accept(true);
|
||||
},
|
||||
}, { withAltMod: true, withCtrlMod: true, withCmdMod: true });
|
||||
//#endregion
|
||||
//#region Hide
|
||||
registerQuickInputCommandAndKeybindingRule({
|
||||
id: 'quickInput.hide',
|
||||
primary: 9 /* KeyCode.Escape */,
|
||||
handler: (accessor) => {
|
||||
const currentQuickPick = accessor.get(IQuickInputService).currentQuickInput;
|
||||
currentQuickPick?.hide();
|
||||
}
|
||||
}, { withAltMod: true, withCtrlMod: true, withCmdMod: true });
|
||||
//#endregion
|
||||
//#region Toggle Hover
|
||||
registerQuickPickCommandAndKeybindingRule({
|
||||
id: 'quickInput.toggleHover',
|
||||
primary: ctrlKeyMod | 10 /* KeyCode.Space */,
|
||||
handler: accessor => {
|
||||
const quickInputService = accessor.get(IQuickInputService);
|
||||
quickInputService.toggleHover();
|
||||
}
|
||||
});
|
||||
//#endregion
|
||||
//# sourceMappingURL=quickInputActions.js.map
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,102 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import * as dom from '../../../base/browser/dom.js';
|
||||
import { FindInput } from '../../../base/browser/ui/findinput/findInput.js';
|
||||
import { Disposable } from '../../../base/common/lifecycle.js';
|
||||
import Severity from '../../../base/common/severity.js';
|
||||
import './media/quickInput.css';
|
||||
const $ = dom.$;
|
||||
export class QuickInputBox extends Disposable {
|
||||
constructor(parent, inputBoxStyles, toggleStyles) {
|
||||
super();
|
||||
this.parent = parent;
|
||||
this.onDidChange = (handler) => {
|
||||
return this.findInput.onDidChange(handler);
|
||||
};
|
||||
this.container = dom.append(this.parent, $('.quick-input-box'));
|
||||
this.findInput = this._register(new FindInput(this.container, undefined, { label: '', inputBoxStyles, toggleStyles }));
|
||||
const input = this.findInput.inputBox.inputElement;
|
||||
input.role = 'textbox';
|
||||
input.ariaHasPopup = 'menu';
|
||||
input.ariaAutoComplete = 'list';
|
||||
}
|
||||
get onKeyDown() {
|
||||
return this.findInput.onKeyDown;
|
||||
}
|
||||
get value() {
|
||||
return this.findInput.getValue();
|
||||
}
|
||||
set value(value) {
|
||||
this.findInput.setValue(value);
|
||||
}
|
||||
select(range = null) {
|
||||
this.findInput.inputBox.select(range);
|
||||
}
|
||||
getSelection() {
|
||||
return this.findInput.inputBox.getSelection();
|
||||
}
|
||||
isSelectionAtEnd() {
|
||||
return this.findInput.inputBox.isSelectionAtEnd();
|
||||
}
|
||||
get placeholder() {
|
||||
return this.findInput.inputBox.inputElement.getAttribute('placeholder') || '';
|
||||
}
|
||||
set placeholder(placeholder) {
|
||||
this.findInput.inputBox.setPlaceHolder(placeholder);
|
||||
}
|
||||
get password() {
|
||||
return this.findInput.inputBox.inputElement.type === 'password';
|
||||
}
|
||||
set password(password) {
|
||||
this.findInput.inputBox.inputElement.type = password ? 'password' : 'text';
|
||||
}
|
||||
set enabled(enabled) {
|
||||
// We can't disable the input box because it is still used for
|
||||
// navigating the list. Instead, we disable the list and the OK
|
||||
// so that nothing can be selected.
|
||||
// TODO: should this be what we do for all find inputs? Or maybe some _other_ API
|
||||
// on findInput to change it to readonly?
|
||||
this.findInput.inputBox.inputElement.toggleAttribute('readonly', !enabled);
|
||||
// TODO: styles of the quick pick need to be moved to the CSS instead of being in line
|
||||
// so things like this can be done in CSS
|
||||
// this.findInput.inputBox.inputElement.classList.toggle('disabled', !enabled);
|
||||
}
|
||||
set toggles(toggles) {
|
||||
this.findInput.setAdditionalToggles(toggles);
|
||||
}
|
||||
get ariaLabel() {
|
||||
return this.findInput.inputBox.inputElement.getAttribute('aria-label') || '';
|
||||
}
|
||||
set ariaLabel(ariaLabel) {
|
||||
this.findInput.inputBox.inputElement.setAttribute('aria-label', ariaLabel);
|
||||
}
|
||||
hasFocus() {
|
||||
return this.findInput.inputBox.hasFocus();
|
||||
}
|
||||
setAttribute(name, value) {
|
||||
this.findInput.inputBox.inputElement.setAttribute(name, value);
|
||||
}
|
||||
removeAttribute(name) {
|
||||
this.findInput.inputBox.inputElement.removeAttribute(name);
|
||||
}
|
||||
showDecoration(decoration) {
|
||||
if (decoration === Severity.Ignore) {
|
||||
this.findInput.clearMessage();
|
||||
}
|
||||
else {
|
||||
this.findInput.showMessage({ type: decoration === Severity.Info ? 1 /* MessageType.INFO */ : decoration === Severity.Warning ? 2 /* MessageType.WARNING */ : 3 /* MessageType.ERROR */, content: '' });
|
||||
}
|
||||
}
|
||||
stylesForType(decoration) {
|
||||
return this.findInput.inputBox.stylesForType(decoration === Severity.Info ? 1 /* MessageType.INFO */ : decoration === Severity.Warning ? 2 /* MessageType.WARNING */ : 3 /* MessageType.ERROR */);
|
||||
}
|
||||
setFocus() {
|
||||
this.findInput.focus();
|
||||
}
|
||||
layout() {
|
||||
this.findInput.inputBox.layout();
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=quickInputBox.js.map
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,888 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
||||
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
||||
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
||||
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||
};
|
||||
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
||||
return function (target, key) { decorator(target, key, paramIndex); }
|
||||
};
|
||||
var QuickInputController_1;
|
||||
import * as dom from '../../../base/browser/dom.js';
|
||||
import * as domStylesheetsJs from '../../../base/browser/domStylesheets.js';
|
||||
import { ActionBar } from '../../../base/browser/ui/actionbar/actionbar.js';
|
||||
import { Button } from '../../../base/browser/ui/button/button.js';
|
||||
import { CountBadge } from '../../../base/browser/ui/countBadge/countBadge.js';
|
||||
import { ProgressBar } from '../../../base/browser/ui/progressbar/progressbar.js';
|
||||
import { CancellationToken } from '../../../base/common/cancellation.js';
|
||||
import { Emitter, Event } from '../../../base/common/event.js';
|
||||
import { Disposable, dispose } from '../../../base/common/lifecycle.js';
|
||||
import Severity from '../../../base/common/severity.js';
|
||||
import { isString } from '../../../base/common/types.js';
|
||||
import { localize } from '../../../nls.js';
|
||||
import { QuickInputHideReason } from '../common/quickInput.js';
|
||||
import { QuickInputBox } from './quickInputBox.js';
|
||||
import { QuickPick, backButton, InputBox, InQuickInputContextKey, QuickInputTypeContextKey, EndOfQuickInputBoxContextKey, QuickInputAlignmentContextKey } from './quickInput.js';
|
||||
import { ILayoutService } from '../../layout/browser/layoutService.js';
|
||||
import { mainWindow } from '../../../base/browser/window.js';
|
||||
import { IInstantiationService } from '../../instantiation/common/instantiation.js';
|
||||
import { QuickInputList } from './quickInputList.js';
|
||||
import { IContextKeyService } from '../../contextkey/common/contextkey.js';
|
||||
import './quickInputActions.js';
|
||||
import { autorun, observableValue } from '../../../base/common/observable.js';
|
||||
import { StandardMouseEvent } from '../../../base/browser/mouseEvent.js';
|
||||
import { IStorageService } from '../../storage/common/storage.js';
|
||||
import { IConfigurationService } from '../../configuration/common/configuration.js';
|
||||
import { platform } from '../../../base/common/platform.js';
|
||||
import { getWindowControlsStyle } from '../../window/common/window.js';
|
||||
import { getZoomFactor } from '../../../base/browser/browser.js';
|
||||
import { TriStateCheckbox } from '../../../base/browser/ui/toggle/toggle.js';
|
||||
import { defaultCheckboxStyles } from '../../theme/browser/defaultStyles.js';
|
||||
import { QuickInputTreeController } from './tree/quickInputTreeController.js';
|
||||
const $ = dom.$;
|
||||
const VIEWSTATE_STORAGE_KEY = 'workbench.quickInput.viewState';
|
||||
let QuickInputController = class QuickInputController extends Disposable {
|
||||
static { QuickInputController_1 = this; }
|
||||
static { this.MAX_WIDTH = 600; } // Max total width of quick input widget
|
||||
get currentQuickInput() { return this.controller ?? undefined; }
|
||||
get container() { return this._container; }
|
||||
constructor(options, layoutService, instantiationService, contextKeyService, storageService) {
|
||||
super();
|
||||
this.options = options;
|
||||
this.layoutService = layoutService;
|
||||
this.instantiationService = instantiationService;
|
||||
this.storageService = storageService;
|
||||
this.enabled = true;
|
||||
this.onDidAcceptEmitter = this._register(new Emitter());
|
||||
this.onDidCustomEmitter = this._register(new Emitter());
|
||||
this.onDidTriggerButtonEmitter = this._register(new Emitter());
|
||||
this.keyMods = { ctrlCmd: false, alt: false };
|
||||
this.controller = null;
|
||||
this.onShowEmitter = this._register(new Emitter());
|
||||
this.onShow = this.onShowEmitter.event;
|
||||
this.onHideEmitter = this._register(new Emitter());
|
||||
this.onHide = this.onHideEmitter.event;
|
||||
this.inQuickInputContext = InQuickInputContextKey.bindTo(contextKeyService);
|
||||
this.quickInputTypeContext = QuickInputTypeContextKey.bindTo(contextKeyService);
|
||||
this.endOfQuickInputBoxContext = EndOfQuickInputBoxContextKey.bindTo(contextKeyService);
|
||||
this.idPrefix = options.idPrefix;
|
||||
this._container = options.container;
|
||||
this.styles = options.styles;
|
||||
this._register(Event.runAndSubscribe(dom.onDidRegisterWindow, ({ window, disposables }) => this.registerKeyModsListeners(window, disposables), { window: mainWindow, disposables: this._store }));
|
||||
this._register(dom.onWillUnregisterWindow(window => {
|
||||
if (this.ui && dom.getWindow(this.ui.container) === window) {
|
||||
// The window this quick input is contained in is about to
|
||||
// close, so we have to make sure to reparent it back to an
|
||||
// existing parent to not loose functionality.
|
||||
// (https://github.com/microsoft/vscode/issues/195870)
|
||||
this.reparentUI(this.layoutService.mainContainer);
|
||||
this.layout(this.layoutService.mainContainerDimension, this.layoutService.mainContainerOffset.quickPickTop);
|
||||
}
|
||||
}));
|
||||
this.viewState = this.loadViewState();
|
||||
}
|
||||
registerKeyModsListeners(window, disposables) {
|
||||
const listener = (e) => {
|
||||
this.keyMods.ctrlCmd = e.ctrlKey || e.metaKey;
|
||||
this.keyMods.alt = e.altKey;
|
||||
};
|
||||
for (const event of [dom.EventType.KEY_DOWN, dom.EventType.KEY_UP, dom.EventType.MOUSE_DOWN]) {
|
||||
disposables.add(dom.addDisposableListener(window, event, listener, true));
|
||||
}
|
||||
}
|
||||
getUI(showInActiveContainer) {
|
||||
if (this.ui) {
|
||||
// In order to support aux windows, re-parent the controller
|
||||
// if the original event is from a different document
|
||||
if (showInActiveContainer) {
|
||||
if (dom.getWindow(this._container) !== dom.getWindow(this.layoutService.activeContainer)) {
|
||||
this.reparentUI(this.layoutService.activeContainer);
|
||||
this.layout(this.layoutService.activeContainerDimension, this.layoutService.activeContainerOffset.quickPickTop);
|
||||
}
|
||||
}
|
||||
return this.ui;
|
||||
}
|
||||
const container = dom.append(this._container, $('.quick-input-widget.show-file-icons'));
|
||||
container.tabIndex = -1;
|
||||
container.style.display = 'none';
|
||||
const styleSheet = domStylesheetsJs.createStyleSheet(container);
|
||||
const titleBar = dom.append(container, $('.quick-input-titlebar'));
|
||||
const leftActionBar = this._register(new ActionBar(titleBar, { hoverDelegate: this.options.hoverDelegate }));
|
||||
leftActionBar.domNode.classList.add('quick-input-left-action-bar');
|
||||
const title = dom.append(titleBar, $('.quick-input-title'));
|
||||
const rightActionBar = this._register(new ActionBar(titleBar, { hoverDelegate: this.options.hoverDelegate }));
|
||||
rightActionBar.domNode.classList.add('quick-input-right-action-bar');
|
||||
const headerContainer = dom.append(container, $('.quick-input-header'));
|
||||
const checkAll = this._register(new TriStateCheckbox(localize(1746, "Toggle all checkboxes"), false, { ...defaultCheckboxStyles, size: 15 }));
|
||||
dom.append(headerContainer, checkAll.domNode);
|
||||
this._register(checkAll.onChange(() => {
|
||||
const checked = checkAll.checked;
|
||||
list.setAllVisibleChecked(checked === true);
|
||||
}));
|
||||
this._register(dom.addDisposableListener(checkAll.domNode, dom.EventType.CLICK, e => {
|
||||
if (e.x || e.y) { // Avoid 'click' triggered by 'space'...
|
||||
inputBox.setFocus();
|
||||
}
|
||||
}));
|
||||
const description2 = dom.append(headerContainer, $('.quick-input-description'));
|
||||
const inputContainer = dom.append(headerContainer, $('.quick-input-and-message'));
|
||||
const filterContainer = dom.append(inputContainer, $('.quick-input-filter'));
|
||||
const inputBox = this._register(new QuickInputBox(filterContainer, this.styles.inputBox, this.styles.toggle));
|
||||
inputBox.setAttribute('aria-describedby', `${this.idPrefix}message`);
|
||||
const visibleCountContainer = dom.append(filterContainer, $('.quick-input-visible-count'));
|
||||
visibleCountContainer.setAttribute('aria-live', 'polite');
|
||||
visibleCountContainer.setAttribute('aria-atomic', 'true');
|
||||
const visibleCount = this._register(new CountBadge(visibleCountContainer, { countFormat: localize(1747, "{0} Results") }, this.styles.countBadge));
|
||||
const countContainer = dom.append(filterContainer, $('.quick-input-count'));
|
||||
countContainer.setAttribute('aria-live', 'polite');
|
||||
const count = this._register(new CountBadge(countContainer, { countFormat: localize(1748, "{0} Selected") }, this.styles.countBadge));
|
||||
const inlineActionBar = this._register(new ActionBar(headerContainer, { hoverDelegate: this.options.hoverDelegate }));
|
||||
inlineActionBar.domNode.classList.add('quick-input-inline-action-bar');
|
||||
const okContainer = dom.append(headerContainer, $('.quick-input-action'));
|
||||
const ok = this._register(new Button(okContainer, this.styles.button));
|
||||
ok.label = localize(1749, "OK");
|
||||
this._register(ok.onDidClick(e => {
|
||||
this.onDidAcceptEmitter.fire();
|
||||
}));
|
||||
const customButtonContainer = dom.append(headerContainer, $('.quick-input-action'));
|
||||
const customButton = this._register(new Button(customButtonContainer, { ...this.styles.button, supportIcons: true }));
|
||||
customButton.label = localize(1750, "Custom");
|
||||
this._register(customButton.onDidClick(e => {
|
||||
this.onDidCustomEmitter.fire();
|
||||
}));
|
||||
const message = dom.append(inputContainer, $(`#${this.idPrefix}message.quick-input-message`));
|
||||
const progressBar = this._register(new ProgressBar(container, this.styles.progressBar));
|
||||
progressBar.getContainer().classList.add('quick-input-progress');
|
||||
const widget = dom.append(container, $('.quick-input-html-widget'));
|
||||
widget.tabIndex = -1;
|
||||
const description1 = dom.append(container, $('.quick-input-description'));
|
||||
// List
|
||||
const listId = this.idPrefix + 'list';
|
||||
const list = this._register(this.instantiationService.createInstance(QuickInputList, container, this.options.hoverDelegate, this.options.linkOpenerDelegate, listId));
|
||||
inputBox.setAttribute('aria-controls', listId);
|
||||
this._register(list.onDidChangeFocus(() => {
|
||||
if (inputBox.hasFocus()) {
|
||||
inputBox.setAttribute('aria-activedescendant', list.getActiveDescendant() ?? '');
|
||||
}
|
||||
}));
|
||||
this._register(list.onChangedAllVisibleChecked(checked => {
|
||||
// TODO: Support tri-state checkbox when we remove the .indent property that is faking tree structure.
|
||||
checkAll.checked = checked;
|
||||
}));
|
||||
this._register(list.onChangedVisibleCount(c => {
|
||||
visibleCount.setCount(c);
|
||||
}));
|
||||
this._register(list.onChangedCheckedCount(c => {
|
||||
count.setCount(c);
|
||||
}));
|
||||
this._register(list.onLeave(() => {
|
||||
// Defer to avoid the input field reacting to the triggering key.
|
||||
// TODO@TylerLeonhardt https://github.com/microsoft/vscode/issues/203675
|
||||
setTimeout(() => {
|
||||
if (!this.controller) {
|
||||
return;
|
||||
}
|
||||
inputBox.setFocus();
|
||||
if (this.controller instanceof QuickPick && this.controller.canSelectMany) {
|
||||
list.clearFocus();
|
||||
}
|
||||
}, 0);
|
||||
}));
|
||||
// Tree
|
||||
const tree = this._register(this.instantiationService.createInstance(QuickInputTreeController, container, this.options.hoverDelegate));
|
||||
this._register(tree.tree.onDidChangeFocus(() => {
|
||||
if (inputBox.hasFocus()) {
|
||||
inputBox.setAttribute('aria-activedescendant', tree.getActiveDescendant() ?? '');
|
||||
}
|
||||
}));
|
||||
this._register(tree.onLeave(() => {
|
||||
// Defer to avoid the input field reacting to the triggering key.
|
||||
// TODO@TylerLeonhardt https://github.com/microsoft/vscode/issues/203675
|
||||
setTimeout(() => {
|
||||
if (!this.controller) {
|
||||
return;
|
||||
}
|
||||
inputBox.setFocus();
|
||||
tree.tree.setFocus([]);
|
||||
}, 0);
|
||||
}));
|
||||
// Wire up tree's accept event to the UI's accept emitter for non-pickable items
|
||||
this._register(tree.onDidAccept(() => {
|
||||
this.onDidAcceptEmitter.fire();
|
||||
}));
|
||||
this._register(tree.tree.onDidChangeContentHeight(() => this.updateLayout()));
|
||||
const focusTracker = dom.trackFocus(container);
|
||||
this._register(focusTracker);
|
||||
this._register(dom.addDisposableListener(container, dom.EventType.FOCUS, e => {
|
||||
const ui = this.getUI();
|
||||
if (dom.isAncestor(e.relatedTarget, ui.inputContainer)) {
|
||||
const value = ui.inputBox.isSelectionAtEnd();
|
||||
if (this.endOfQuickInputBoxContext.get() !== value) {
|
||||
this.endOfQuickInputBoxContext.set(value);
|
||||
}
|
||||
}
|
||||
// Ignore focus events within container
|
||||
if (dom.isAncestor(e.relatedTarget, ui.container)) {
|
||||
return;
|
||||
}
|
||||
this.inQuickInputContext.set(true);
|
||||
this.previousFocusElement = dom.isHTMLElement(e.relatedTarget) ? e.relatedTarget : undefined;
|
||||
}, true));
|
||||
this._register(focusTracker.onDidBlur(() => {
|
||||
if (!this.getUI().ignoreFocusOut && !this.options.ignoreFocusOut()) {
|
||||
this.hide(QuickInputHideReason.Blur);
|
||||
}
|
||||
this.inQuickInputContext.set(false);
|
||||
this.endOfQuickInputBoxContext.set(false);
|
||||
this.previousFocusElement = undefined;
|
||||
}));
|
||||
this._register(inputBox.onKeyDown(_ => {
|
||||
const value = this.getUI().inputBox.isSelectionAtEnd();
|
||||
if (this.endOfQuickInputBoxContext.get() !== value) {
|
||||
this.endOfQuickInputBoxContext.set(value);
|
||||
}
|
||||
// Allow screenreaders to read what's in the input
|
||||
// Note: this works for arrow keys and selection changes,
|
||||
// but not for deletions since that often triggers a
|
||||
// change in the list.
|
||||
inputBox.removeAttribute('aria-activedescendant');
|
||||
}));
|
||||
this._register(dom.addDisposableListener(container, dom.EventType.FOCUS, (e) => {
|
||||
inputBox.setFocus();
|
||||
}));
|
||||
// Drag and Drop support
|
||||
this.dndController = this._register(this.instantiationService.createInstance(QuickInputDragAndDropController, this._container, container, [
|
||||
{
|
||||
node: titleBar,
|
||||
includeChildren: true
|
||||
},
|
||||
{
|
||||
node: headerContainer,
|
||||
includeChildren: false
|
||||
}
|
||||
], this.viewState));
|
||||
// DnD update layout
|
||||
this._register(autorun(reader => {
|
||||
const dndViewState = this.dndController?.dndViewState.read(reader);
|
||||
if (!dndViewState) {
|
||||
return;
|
||||
}
|
||||
if (dndViewState.top !== undefined && dndViewState.left !== undefined) {
|
||||
this.viewState = {
|
||||
...this.viewState,
|
||||
top: dndViewState.top,
|
||||
left: dndViewState.left
|
||||
};
|
||||
}
|
||||
else {
|
||||
// Reset position/size
|
||||
this.viewState = undefined;
|
||||
}
|
||||
this.updateLayout();
|
||||
// Save position
|
||||
if (dndViewState.done) {
|
||||
this.saveViewState(this.viewState);
|
||||
}
|
||||
}));
|
||||
this.ui = {
|
||||
container,
|
||||
styleSheet,
|
||||
leftActionBar,
|
||||
titleBar,
|
||||
title,
|
||||
description1,
|
||||
description2,
|
||||
widget,
|
||||
rightActionBar,
|
||||
inlineActionBar,
|
||||
checkAll,
|
||||
inputContainer,
|
||||
filterContainer,
|
||||
inputBox,
|
||||
visibleCountContainer,
|
||||
visibleCount,
|
||||
countContainer,
|
||||
count,
|
||||
okContainer,
|
||||
ok,
|
||||
message,
|
||||
customButtonContainer,
|
||||
customButton,
|
||||
list,
|
||||
tree,
|
||||
progressBar,
|
||||
onDidAccept: this.onDidAcceptEmitter.event,
|
||||
onDidCustom: this.onDidCustomEmitter.event,
|
||||
onDidTriggerButton: this.onDidTriggerButtonEmitter.event,
|
||||
ignoreFocusOut: false,
|
||||
keyMods: this.keyMods,
|
||||
show: controller => this.show(controller),
|
||||
hide: () => this.hide(),
|
||||
setVisibilities: visibilities => this.setVisibilities(visibilities),
|
||||
setEnabled: enabled => this.setEnabled(enabled),
|
||||
setContextKey: contextKey => this.options.setContextKey(contextKey),
|
||||
linkOpenerDelegate: content => this.options.linkOpenerDelegate(content)
|
||||
};
|
||||
this.updateStyles();
|
||||
return this.ui;
|
||||
}
|
||||
reparentUI(container) {
|
||||
if (this.ui) {
|
||||
this._container = container;
|
||||
dom.append(this._container, this.ui.container);
|
||||
this.dndController?.reparentUI(this._container);
|
||||
}
|
||||
}
|
||||
pick(picks, options = {}, token = CancellationToken.None) {
|
||||
return new Promise((doResolve, reject) => {
|
||||
let resolve = (result) => {
|
||||
resolve = doResolve;
|
||||
options.onKeyMods?.(input.keyMods);
|
||||
doResolve(result);
|
||||
};
|
||||
if (token.isCancellationRequested) {
|
||||
resolve(undefined);
|
||||
return;
|
||||
}
|
||||
const input = this.createQuickPick({ useSeparators: true });
|
||||
let activeItem;
|
||||
const disposables = [
|
||||
input,
|
||||
input.onDidAccept(() => {
|
||||
if (input.canSelectMany) {
|
||||
resolve(input.selectedItems.slice());
|
||||
input.hide();
|
||||
}
|
||||
else {
|
||||
const result = input.activeItems[0];
|
||||
if (result) {
|
||||
resolve(result);
|
||||
input.hide();
|
||||
}
|
||||
}
|
||||
}),
|
||||
input.onDidChangeActive(items => {
|
||||
const focused = items[0];
|
||||
if (focused && options.onDidFocus) {
|
||||
options.onDidFocus(focused);
|
||||
}
|
||||
}),
|
||||
input.onDidChangeSelection(items => {
|
||||
if (!input.canSelectMany) {
|
||||
const result = items[0];
|
||||
if (result) {
|
||||
resolve(result);
|
||||
input.hide();
|
||||
}
|
||||
}
|
||||
}),
|
||||
input.onDidTriggerItemButton(event => options.onDidTriggerItemButton && options.onDidTriggerItemButton({
|
||||
...event,
|
||||
removeItem: () => {
|
||||
const index = input.items.indexOf(event.item);
|
||||
if (index !== -1) {
|
||||
const items = input.items.slice();
|
||||
const removed = items.splice(index, 1);
|
||||
const activeItems = input.activeItems.filter(activeItem => activeItem !== removed[0]);
|
||||
const keepScrollPositionBefore = input.keepScrollPosition;
|
||||
input.keepScrollPosition = true;
|
||||
input.items = items;
|
||||
if (activeItems) {
|
||||
input.activeItems = activeItems;
|
||||
}
|
||||
input.keepScrollPosition = keepScrollPositionBefore;
|
||||
}
|
||||
}
|
||||
})),
|
||||
input.onDidTriggerSeparatorButton(event => options.onDidTriggerSeparatorButton?.(event)),
|
||||
input.onDidChangeValue(value => {
|
||||
if (activeItem && !value && (input.activeItems.length !== 1 || input.activeItems[0] !== activeItem)) {
|
||||
input.activeItems = [activeItem];
|
||||
}
|
||||
}),
|
||||
token.onCancellationRequested(() => {
|
||||
input.hide();
|
||||
}),
|
||||
input.onDidHide(() => {
|
||||
dispose(disposables);
|
||||
resolve(undefined);
|
||||
}),
|
||||
];
|
||||
input.title = options.title;
|
||||
if (options.value) {
|
||||
input.value = options.value;
|
||||
}
|
||||
input.canSelectMany = !!options.canPickMany;
|
||||
input.placeholder = options.placeHolder;
|
||||
input.ignoreFocusOut = !!options.ignoreFocusLost;
|
||||
input.matchOnDescription = !!options.matchOnDescription;
|
||||
input.matchOnDetail = !!options.matchOnDetail;
|
||||
if (options.sortByLabel !== undefined) {
|
||||
input.sortByLabel = options.sortByLabel;
|
||||
}
|
||||
input.matchOnLabel = (options.matchOnLabel === undefined) || options.matchOnLabel; // default to true
|
||||
input.quickNavigate = options.quickNavigate;
|
||||
input.hideInput = !!options.hideInput;
|
||||
input.contextKey = options.contextKey;
|
||||
input.busy = true;
|
||||
Promise.all([picks, options.activeItem])
|
||||
.then(([items, _activeItem]) => {
|
||||
activeItem = _activeItem;
|
||||
input.busy = false;
|
||||
input.items = items;
|
||||
if (input.canSelectMany) {
|
||||
input.selectedItems = items.filter(item => item.type !== 'separator' && item.picked);
|
||||
}
|
||||
if (activeItem) {
|
||||
input.activeItems = [activeItem];
|
||||
}
|
||||
});
|
||||
input.show();
|
||||
Promise.resolve(picks).then(undefined, err => {
|
||||
reject(err);
|
||||
input.hide();
|
||||
});
|
||||
});
|
||||
}
|
||||
setValidationOnInput(input, validationResult) {
|
||||
if (validationResult && isString(validationResult)) {
|
||||
input.severity = Severity.Error;
|
||||
input.validationMessage = validationResult;
|
||||
}
|
||||
else if (validationResult && !isString(validationResult)) {
|
||||
input.severity = validationResult.severity;
|
||||
input.validationMessage = validationResult.content;
|
||||
}
|
||||
else {
|
||||
input.severity = Severity.Ignore;
|
||||
input.validationMessage = undefined;
|
||||
}
|
||||
}
|
||||
input(options = {}, token = CancellationToken.None) {
|
||||
return new Promise((resolve) => {
|
||||
if (token.isCancellationRequested) {
|
||||
resolve(undefined);
|
||||
return;
|
||||
}
|
||||
const input = this.createInputBox();
|
||||
const validateInput = options.validateInput || (() => Promise.resolve(undefined));
|
||||
const onDidValueChange = Event.debounce(input.onDidChangeValue, (last, cur) => cur, 100);
|
||||
let validationValue = options.value || '';
|
||||
let validation = Promise.resolve(validateInput(validationValue));
|
||||
const disposables = [
|
||||
input,
|
||||
onDidValueChange(value => {
|
||||
if (value !== validationValue) {
|
||||
validation = Promise.resolve(validateInput(value));
|
||||
validationValue = value;
|
||||
}
|
||||
validation.then(result => {
|
||||
if (value === validationValue) {
|
||||
this.setValidationOnInput(input, result);
|
||||
}
|
||||
});
|
||||
}),
|
||||
input.onDidAccept(() => {
|
||||
const value = input.value;
|
||||
if (value !== validationValue) {
|
||||
validation = Promise.resolve(validateInput(value));
|
||||
validationValue = value;
|
||||
}
|
||||
validation.then(result => {
|
||||
if (!result || (!isString(result) && result.severity !== Severity.Error)) {
|
||||
resolve(value);
|
||||
input.hide();
|
||||
}
|
||||
else if (value === validationValue) {
|
||||
this.setValidationOnInput(input, result);
|
||||
}
|
||||
});
|
||||
}),
|
||||
token.onCancellationRequested(() => {
|
||||
input.hide();
|
||||
}),
|
||||
input.onDidHide(() => {
|
||||
dispose(disposables);
|
||||
resolve(undefined);
|
||||
}),
|
||||
];
|
||||
input.title = options.title;
|
||||
input.value = options.value || '';
|
||||
input.valueSelection = options.valueSelection;
|
||||
input.prompt = options.prompt;
|
||||
input.placeholder = options.placeHolder;
|
||||
input.password = !!options.password;
|
||||
input.ignoreFocusOut = !!options.ignoreFocusLost;
|
||||
input.show();
|
||||
});
|
||||
}
|
||||
createQuickPick(options = { useSeparators: false }) {
|
||||
const ui = this.getUI(true);
|
||||
return new QuickPick(ui);
|
||||
}
|
||||
createInputBox() {
|
||||
const ui = this.getUI(true);
|
||||
return new InputBox(ui);
|
||||
}
|
||||
show(controller) {
|
||||
const ui = this.getUI(true);
|
||||
this.onShowEmitter.fire();
|
||||
const oldController = this.controller;
|
||||
this.controller = controller;
|
||||
oldController?.didHide();
|
||||
this.setEnabled(true);
|
||||
ui.leftActionBar.clear();
|
||||
ui.title.textContent = '';
|
||||
ui.description1.textContent = '';
|
||||
ui.description2.textContent = '';
|
||||
dom.reset(ui.widget);
|
||||
ui.rightActionBar.clear();
|
||||
ui.inlineActionBar.clear();
|
||||
ui.checkAll.checked = false;
|
||||
// ui.inputBox.value = ''; Avoid triggering an event.
|
||||
ui.inputBox.placeholder = '';
|
||||
ui.inputBox.password = false;
|
||||
ui.inputBox.showDecoration(Severity.Ignore);
|
||||
ui.visibleCount.setCount(0);
|
||||
ui.count.setCount(0);
|
||||
dom.reset(ui.message);
|
||||
ui.progressBar.stop();
|
||||
ui.list.setElements([]);
|
||||
ui.list.matchOnDescription = false;
|
||||
ui.list.matchOnDetail = false;
|
||||
ui.list.matchOnLabel = true;
|
||||
ui.list.sortByLabel = true;
|
||||
ui.ignoreFocusOut = false;
|
||||
ui.inputBox.toggles = undefined;
|
||||
const backKeybindingLabel = this.options.backKeybindingLabel();
|
||||
backButton.tooltip = backKeybindingLabel ? localize(1751, "Back ({0})", backKeybindingLabel) : localize(1752, "Back");
|
||||
ui.container.style.display = '';
|
||||
this.updateLayout();
|
||||
this.dndController?.layoutContainer();
|
||||
ui.inputBox.setFocus();
|
||||
this.quickInputTypeContext.set(controller.type);
|
||||
}
|
||||
isVisible() {
|
||||
return !!this.ui && this.ui.container.style.display !== 'none';
|
||||
}
|
||||
setVisibilities(visibilities) {
|
||||
const ui = this.getUI();
|
||||
ui.title.style.display = visibilities.title ? '' : 'none';
|
||||
ui.description1.style.display = visibilities.description && (visibilities.inputBox || visibilities.checkAll) ? '' : 'none';
|
||||
ui.description2.style.display = visibilities.description && !(visibilities.inputBox || visibilities.checkAll) ? '' : 'none';
|
||||
ui.checkAll.domNode.style.display = visibilities.checkAll ? '' : 'none';
|
||||
ui.inputContainer.style.display = visibilities.inputBox ? '' : 'none';
|
||||
ui.filterContainer.style.display = visibilities.inputBox ? '' : 'none';
|
||||
ui.visibleCountContainer.style.display = visibilities.visibleCount ? '' : 'none';
|
||||
ui.countContainer.style.display = visibilities.count ? '' : 'none';
|
||||
ui.okContainer.style.display = visibilities.ok ? '' : 'none';
|
||||
ui.customButtonContainer.style.display = visibilities.customButton ? '' : 'none';
|
||||
ui.message.style.display = visibilities.message ? '' : 'none';
|
||||
ui.progressBar.getContainer().style.display = visibilities.progressBar ? '' : 'none';
|
||||
ui.list.displayed = !!visibilities.list;
|
||||
ui.tree.displayed = !!visibilities.tree;
|
||||
ui.container.classList.toggle('show-checkboxes', !!visibilities.checkBox);
|
||||
ui.container.classList.toggle('hidden-input', !visibilities.inputBox && !visibilities.description);
|
||||
this.updateLayout(); // TODO
|
||||
}
|
||||
setEnabled(enabled) {
|
||||
if (enabled !== this.enabled) {
|
||||
this.enabled = enabled;
|
||||
const ui = this.getUI();
|
||||
for (const item of ui.leftActionBar.viewItems) {
|
||||
item.action.enabled = enabled;
|
||||
}
|
||||
for (const item of ui.rightActionBar.viewItems) {
|
||||
item.action.enabled = enabled;
|
||||
}
|
||||
if (enabled) {
|
||||
ui.checkAll.enable();
|
||||
}
|
||||
else {
|
||||
ui.checkAll.disable();
|
||||
}
|
||||
ui.inputBox.enabled = enabled;
|
||||
ui.ok.enabled = enabled;
|
||||
ui.list.enabled = enabled;
|
||||
}
|
||||
}
|
||||
hide(reason) {
|
||||
const controller = this.controller;
|
||||
if (!controller) {
|
||||
return;
|
||||
}
|
||||
controller.willHide(reason);
|
||||
const container = this.ui?.container;
|
||||
const focusChanged = container && !dom.isAncestorOfActiveElement(container);
|
||||
this.controller = null;
|
||||
this.onHideEmitter.fire();
|
||||
if (container) {
|
||||
container.style.display = 'none';
|
||||
}
|
||||
if (!focusChanged) {
|
||||
let currentElement = this.previousFocusElement;
|
||||
while (currentElement && !currentElement.offsetParent) {
|
||||
currentElement = currentElement.parentElement ?? undefined;
|
||||
}
|
||||
if (currentElement?.offsetParent) {
|
||||
currentElement.focus();
|
||||
this.previousFocusElement = undefined;
|
||||
}
|
||||
else {
|
||||
this.options.returnFocus();
|
||||
}
|
||||
}
|
||||
controller.didHide(reason);
|
||||
}
|
||||
toggleHover() {
|
||||
if (this.isVisible() && this.controller instanceof QuickPick) {
|
||||
this.getUI().list.toggleHover();
|
||||
}
|
||||
}
|
||||
layout(dimension, titleBarOffset) {
|
||||
this.dimension = dimension;
|
||||
this.titleBarOffset = titleBarOffset;
|
||||
this.updateLayout();
|
||||
}
|
||||
updateLayout() {
|
||||
if (this.ui && this.isVisible()) {
|
||||
const style = this.ui.container.style;
|
||||
const width = Math.min(this.dimension.width * 0.62 /* golden cut */, QuickInputController_1.MAX_WIDTH);
|
||||
style.width = width + 'px';
|
||||
// Position
|
||||
style.top = `${this.viewState?.top ? Math.round(this.dimension.height * this.viewState.top) : this.titleBarOffset}px`;
|
||||
style.left = `${Math.round((this.dimension.width * (this.viewState?.left ?? 0.5 /* center */)) - (width / 2))}px`;
|
||||
this.ui.inputBox.layout();
|
||||
this.ui.list.layout(this.dimension && this.dimension.height * 0.4);
|
||||
this.ui.tree.layout(this.dimension && this.dimension.height * 0.4);
|
||||
}
|
||||
}
|
||||
applyStyles(styles) {
|
||||
this.styles = styles;
|
||||
this.updateStyles();
|
||||
}
|
||||
updateStyles() {
|
||||
if (this.ui) {
|
||||
const { quickInputTitleBackground, quickInputBackground, quickInputForeground, widgetBorder, widgetShadow, } = this.styles.widget;
|
||||
this.ui.titleBar.style.backgroundColor = quickInputTitleBackground ?? '';
|
||||
this.ui.container.style.backgroundColor = quickInputBackground ?? '';
|
||||
this.ui.container.style.color = quickInputForeground ?? '';
|
||||
this.ui.container.style.border = widgetBorder ? `1px solid ${widgetBorder}` : '';
|
||||
this.ui.container.style.boxShadow = widgetShadow ? `0 0 8px 2px ${widgetShadow}` : '';
|
||||
this.ui.list.style(this.styles.list);
|
||||
this.ui.tree.tree.style(this.styles.list);
|
||||
const content = [];
|
||||
if (this.styles.pickerGroup.pickerGroupBorder) {
|
||||
content.push(`.quick-input-list .quick-input-list-entry { border-top-color: ${this.styles.pickerGroup.pickerGroupBorder}; }`);
|
||||
}
|
||||
if (this.styles.pickerGroup.pickerGroupForeground) {
|
||||
content.push(`.quick-input-list .quick-input-list-separator { color: ${this.styles.pickerGroup.pickerGroupForeground}; }`);
|
||||
}
|
||||
if (this.styles.pickerGroup.pickerGroupForeground) {
|
||||
content.push(`.quick-input-list .quick-input-list-separator-as-item { color: var(--vscode-descriptionForeground); }`);
|
||||
}
|
||||
if (this.styles.keybindingLabel.keybindingLabelBackground ||
|
||||
this.styles.keybindingLabel.keybindingLabelBorder ||
|
||||
this.styles.keybindingLabel.keybindingLabelBottomBorder ||
|
||||
this.styles.keybindingLabel.keybindingLabelShadow ||
|
||||
this.styles.keybindingLabel.keybindingLabelForeground) {
|
||||
content.push('.quick-input-list .monaco-keybinding > .monaco-keybinding-key {');
|
||||
if (this.styles.keybindingLabel.keybindingLabelBackground) {
|
||||
content.push(`background-color: ${this.styles.keybindingLabel.keybindingLabelBackground};`);
|
||||
}
|
||||
if (this.styles.keybindingLabel.keybindingLabelBorder) {
|
||||
// Order matters here. `border-color` must come before `border-bottom-color`.
|
||||
content.push(`border-color: ${this.styles.keybindingLabel.keybindingLabelBorder};`);
|
||||
}
|
||||
if (this.styles.keybindingLabel.keybindingLabelBottomBorder) {
|
||||
content.push(`border-bottom-color: ${this.styles.keybindingLabel.keybindingLabelBottomBorder};`);
|
||||
}
|
||||
if (this.styles.keybindingLabel.keybindingLabelShadow) {
|
||||
content.push(`box-shadow: inset 0 -1px 0 ${this.styles.keybindingLabel.keybindingLabelShadow};`);
|
||||
}
|
||||
if (this.styles.keybindingLabel.keybindingLabelForeground) {
|
||||
content.push(`color: ${this.styles.keybindingLabel.keybindingLabelForeground};`);
|
||||
}
|
||||
content.push('}');
|
||||
}
|
||||
const newStyles = content.join('\n');
|
||||
if (newStyles !== this.ui.styleSheet.textContent) {
|
||||
this.ui.styleSheet.textContent = newStyles;
|
||||
}
|
||||
}
|
||||
}
|
||||
loadViewState() {
|
||||
try {
|
||||
const data = JSON.parse(this.storageService.get(VIEWSTATE_STORAGE_KEY, -1 /* StorageScope.APPLICATION */, '{}'));
|
||||
if (data.top !== undefined || data.left !== undefined) {
|
||||
return data;
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
return undefined;
|
||||
}
|
||||
saveViewState(viewState) {
|
||||
const isMainWindow = this.layoutService.activeContainer === this.layoutService.mainContainer;
|
||||
if (!isMainWindow) {
|
||||
return;
|
||||
}
|
||||
if (viewState !== undefined) {
|
||||
this.storageService.store(VIEWSTATE_STORAGE_KEY, JSON.stringify(viewState), -1 /* StorageScope.APPLICATION */, 1 /* StorageTarget.MACHINE */);
|
||||
}
|
||||
else {
|
||||
this.storageService.remove(VIEWSTATE_STORAGE_KEY, -1 /* StorageScope.APPLICATION */);
|
||||
}
|
||||
}
|
||||
};
|
||||
QuickInputController = QuickInputController_1 = __decorate([
|
||||
__param(1, ILayoutService),
|
||||
__param(2, IInstantiationService),
|
||||
__param(3, IContextKeyService),
|
||||
__param(4, IStorageService)
|
||||
], QuickInputController);
|
||||
export { QuickInputController };
|
||||
let QuickInputDragAndDropController = class QuickInputDragAndDropController extends Disposable {
|
||||
constructor(_container, _quickInputContainer, _quickInputDragAreas, initialViewState, _layoutService, contextKeyService, configurationService) {
|
||||
super();
|
||||
this._container = _container;
|
||||
this._quickInputContainer = _quickInputContainer;
|
||||
this._quickInputDragAreas = _quickInputDragAreas;
|
||||
this._layoutService = _layoutService;
|
||||
this.configurationService = configurationService;
|
||||
this.dndViewState = observableValue(this, undefined);
|
||||
this._snapThreshold = 20;
|
||||
this._snapLineHorizontalRatio = 0.25;
|
||||
this._quickInputAlignmentContext = QuickInputAlignmentContextKey.bindTo(contextKeyService);
|
||||
const customWindowControls = getWindowControlsStyle(this.configurationService) === "custom" /* WindowControlsStyle.CUSTOM */;
|
||||
// Do not allow the widget to overflow or underflow window controls.
|
||||
// Use CSS calculations to avoid having to force layout with `.clientWidth`
|
||||
this._controlsOnLeft = customWindowControls && platform === 1 /* Platform.Mac */;
|
||||
this._controlsOnRight = customWindowControls && (platform === 3 /* Platform.Windows */ || platform === 2 /* Platform.Linux */);
|
||||
this._registerLayoutListener();
|
||||
this.registerMouseListeners();
|
||||
this.dndViewState.set({ ...initialViewState, done: true }, undefined);
|
||||
}
|
||||
reparentUI(container) {
|
||||
this._container = container;
|
||||
}
|
||||
layoutContainer(dimension = this._layoutService.activeContainerDimension) {
|
||||
const state = this.dndViewState.get();
|
||||
const dragAreaRect = this._quickInputContainer.getBoundingClientRect();
|
||||
if (state?.top && state?.left) {
|
||||
const a = Math.round(state.left * 1e2) / 1e2;
|
||||
const b = dimension.width;
|
||||
const c = dragAreaRect.width;
|
||||
const d = a * b - c / 2;
|
||||
this._layout(state.top * dimension.height, d);
|
||||
}
|
||||
}
|
||||
_registerLayoutListener() {
|
||||
this._register(Event.filter(this._layoutService.onDidLayoutContainer, e => e.container === this._container)((e) => this.layoutContainer(e.dimension)));
|
||||
}
|
||||
registerMouseListeners() {
|
||||
const dragArea = this._quickInputContainer;
|
||||
// Double click
|
||||
this._register(dom.addDisposableGenericMouseUpListener(dragArea, (event) => {
|
||||
const originEvent = new StandardMouseEvent(dom.getWindow(dragArea), event);
|
||||
if (originEvent.detail !== 2) {
|
||||
return;
|
||||
}
|
||||
// Ignore event if the target is not the drag area
|
||||
if (!this._quickInputDragAreas.some(({ node, includeChildren }) => includeChildren ? dom.isAncestor(originEvent.target, node) : originEvent.target === node)) {
|
||||
return;
|
||||
}
|
||||
this.dndViewState.set({ top: undefined, left: undefined, done: true }, undefined);
|
||||
}));
|
||||
// Mouse down
|
||||
this._register(dom.addDisposableGenericMouseDownListener(dragArea, (e) => {
|
||||
const activeWindow = dom.getWindow(this._layoutService.activeContainer);
|
||||
const originEvent = new StandardMouseEvent(activeWindow, e);
|
||||
// Ignore event if the target is not the drag area
|
||||
if (!this._quickInputDragAreas.some(({ node, includeChildren }) => includeChildren ? dom.isAncestor(originEvent.target, node) : originEvent.target === node)) {
|
||||
return;
|
||||
}
|
||||
// Mouse position offset relative to dragArea
|
||||
const dragAreaRect = this._quickInputContainer.getBoundingClientRect();
|
||||
const dragOffsetX = originEvent.browserEvent.clientX - dragAreaRect.left;
|
||||
const dragOffsetY = originEvent.browserEvent.clientY - dragAreaRect.top;
|
||||
let isMovingQuickInput = false;
|
||||
const mouseMoveListener = dom.addDisposableGenericMouseMoveListener(activeWindow, (e) => {
|
||||
const mouseMoveEvent = new StandardMouseEvent(activeWindow, e);
|
||||
mouseMoveEvent.preventDefault();
|
||||
if (!isMovingQuickInput) {
|
||||
isMovingQuickInput = true;
|
||||
}
|
||||
this._layout(e.clientY - dragOffsetY, e.clientX - dragOffsetX);
|
||||
});
|
||||
const mouseUpListener = dom.addDisposableGenericMouseUpListener(activeWindow, (e) => {
|
||||
if (isMovingQuickInput) {
|
||||
// Save position
|
||||
const state = this.dndViewState.get();
|
||||
this.dndViewState.set({ top: state?.top, left: state?.left, done: true }, undefined);
|
||||
}
|
||||
// Dispose listeners
|
||||
mouseMoveListener.dispose();
|
||||
mouseUpListener.dispose();
|
||||
});
|
||||
}));
|
||||
}
|
||||
_layout(topCoordinate, leftCoordinate) {
|
||||
const snapCoordinateYTop = this._getTopSnapValue();
|
||||
const snapCoordinateY = this._getCenterYSnapValue();
|
||||
const snapCoordinateX = this._getCenterXSnapValue();
|
||||
// Make sure the quick input is not moved outside the container
|
||||
topCoordinate = Math.max(0, Math.min(topCoordinate, this._container.clientHeight - this._quickInputContainer.clientHeight));
|
||||
if (topCoordinate < this._layoutService.activeContainerOffset.top) {
|
||||
if (this._controlsOnLeft) {
|
||||
leftCoordinate = Math.max(leftCoordinate, 80 / getZoomFactor(dom.getActiveWindow()));
|
||||
}
|
||||
else if (this._controlsOnRight) {
|
||||
leftCoordinate = Math.min(leftCoordinate, this._container.clientWidth - this._quickInputContainer.clientWidth - (140 / getZoomFactor(dom.getActiveWindow())));
|
||||
}
|
||||
}
|
||||
const snappingToTop = Math.abs(topCoordinate - snapCoordinateYTop) < this._snapThreshold;
|
||||
topCoordinate = snappingToTop ? snapCoordinateYTop : topCoordinate;
|
||||
const snappingToCenter = Math.abs(topCoordinate - snapCoordinateY) < this._snapThreshold;
|
||||
topCoordinate = snappingToCenter ? snapCoordinateY : topCoordinate;
|
||||
const top = topCoordinate / this._container.clientHeight;
|
||||
// Make sure the quick input is not moved outside the container
|
||||
leftCoordinate = Math.max(0, Math.min(leftCoordinate, this._container.clientWidth - this._quickInputContainer.clientWidth));
|
||||
const snappingToCenterX = Math.abs(leftCoordinate - snapCoordinateX) < this._snapThreshold;
|
||||
leftCoordinate = snappingToCenterX ? snapCoordinateX : leftCoordinate;
|
||||
const b = this._container.clientWidth;
|
||||
const c = this._quickInputContainer.clientWidth;
|
||||
const d = leftCoordinate;
|
||||
const left = (d + c / 2) / b;
|
||||
this.dndViewState.set({ top, left, done: false }, undefined);
|
||||
if (snappingToCenterX) {
|
||||
if (snappingToTop) {
|
||||
this._quickInputAlignmentContext.set('top');
|
||||
return;
|
||||
}
|
||||
else if (snappingToCenter) {
|
||||
this._quickInputAlignmentContext.set('center');
|
||||
return;
|
||||
}
|
||||
}
|
||||
this._quickInputAlignmentContext.set(undefined);
|
||||
}
|
||||
_getTopSnapValue() {
|
||||
return this._layoutService.activeContainerOffset.quickPickTop;
|
||||
}
|
||||
_getCenterYSnapValue() {
|
||||
return Math.round(this._container.clientHeight * this._snapLineHorizontalRatio);
|
||||
}
|
||||
_getCenterXSnapValue() {
|
||||
return Math.round(this._container.clientWidth / 2) - Math.round(this._quickInputContainer.clientWidth / 2);
|
||||
}
|
||||
};
|
||||
QuickInputDragAndDropController = __decorate([
|
||||
__param(4, ILayoutService),
|
||||
__param(5, IContextKeyService),
|
||||
__param(6, IConfigurationService)
|
||||
], QuickInputDragAndDropController);
|
||||
//# sourceMappingURL=quickInputController.js.map
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@@ -0,0 +1,186 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
||||
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
||||
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
||||
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||
};
|
||||
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
||||
return function (target, key) { decorator(target, key, paramIndex); }
|
||||
};
|
||||
import { CancellationToken } from '../../../base/common/cancellation.js';
|
||||
import { Emitter } from '../../../base/common/event.js';
|
||||
import { IContextKeyService, RawContextKey } from '../../contextkey/common/contextkey.js';
|
||||
import { IInstantiationService } from '../../instantiation/common/instantiation.js';
|
||||
import { ILayoutService } from '../../layout/browser/layoutService.js';
|
||||
import { IOpenerService } from '../../opener/common/opener.js';
|
||||
import { QuickAccessController } from './quickAccess.js';
|
||||
import { defaultButtonStyles, defaultCountBadgeStyles, defaultInputBoxStyles, defaultKeybindingLabelStyles, defaultProgressBarStyles, defaultToggleStyles, getListStyles } from '../../theme/browser/defaultStyles.js';
|
||||
import { activeContrastBorder, asCssVariable, pickerGroupBorder, pickerGroupForeground, quickInputBackground, quickInputForeground, quickInputListFocusBackground, quickInputListFocusForeground, quickInputListFocusIconForeground, quickInputTitleBackground, widgetBorder, widgetShadow } from '../../theme/common/colorRegistry.js';
|
||||
import { IThemeService, Themable } from '../../theme/common/themeService.js';
|
||||
import { QuickInputHoverDelegate } from './quickInput.js';
|
||||
import { QuickInputController } from './quickInputController.js';
|
||||
import { IConfigurationService } from '../../configuration/common/configuration.js';
|
||||
import { getWindow } from '../../../base/browser/dom.js';
|
||||
let QuickInputService = class QuickInputService extends Themable {
|
||||
get controller() {
|
||||
if (!this._controller) {
|
||||
this._controller = this._register(this.createController());
|
||||
}
|
||||
return this._controller;
|
||||
}
|
||||
get hasController() { return !!this._controller; }
|
||||
get currentQuickInput() { return this.controller.currentQuickInput; }
|
||||
get quickAccess() {
|
||||
if (!this._quickAccess) {
|
||||
this._quickAccess = this._register(this.instantiationService.createInstance(QuickAccessController));
|
||||
}
|
||||
return this._quickAccess;
|
||||
}
|
||||
constructor(instantiationService, contextKeyService, themeService, layoutService, configurationService) {
|
||||
super(themeService);
|
||||
this.instantiationService = instantiationService;
|
||||
this.contextKeyService = contextKeyService;
|
||||
this.layoutService = layoutService;
|
||||
this.configurationService = configurationService;
|
||||
this._onShow = this._register(new Emitter());
|
||||
this._onHide = this._register(new Emitter());
|
||||
this.contexts = new Map();
|
||||
}
|
||||
createController(host = this.layoutService, options) {
|
||||
const defaultOptions = {
|
||||
idPrefix: 'quickInput_',
|
||||
container: host.activeContainer,
|
||||
ignoreFocusOut: () => false,
|
||||
backKeybindingLabel: () => undefined,
|
||||
setContextKey: (id) => this.setContextKey(id),
|
||||
linkOpenerDelegate: (content) => {
|
||||
// HACK: https://github.com/microsoft/vscode/issues/173691
|
||||
this.instantiationService.invokeFunction(accessor => {
|
||||
const openerService = accessor.get(IOpenerService);
|
||||
openerService.open(content, { allowCommands: true, fromUserGesture: true });
|
||||
});
|
||||
},
|
||||
returnFocus: () => host.focus(),
|
||||
styles: this.computeStyles(),
|
||||
hoverDelegate: this._register(this.instantiationService.createInstance(QuickInputHoverDelegate))
|
||||
};
|
||||
const controller = this._register(this.instantiationService.createInstance(QuickInputController, {
|
||||
...defaultOptions,
|
||||
...options
|
||||
}));
|
||||
controller.layout(host.activeContainerDimension, host.activeContainerOffset.quickPickTop);
|
||||
// Layout changes
|
||||
this._register(host.onDidLayoutActiveContainer(dimension => {
|
||||
if (getWindow(host.activeContainer) === getWindow(controller.container)) {
|
||||
controller.layout(dimension, host.activeContainerOffset.quickPickTop);
|
||||
}
|
||||
}));
|
||||
this._register(host.onDidChangeActiveContainer(() => {
|
||||
if (controller.isVisible()) {
|
||||
return;
|
||||
}
|
||||
controller.layout(host.activeContainerDimension, host.activeContainerOffset.quickPickTop);
|
||||
}));
|
||||
// Context keys
|
||||
this._register(controller.onShow(() => {
|
||||
this.resetContextKeys();
|
||||
this._onShow.fire();
|
||||
}));
|
||||
this._register(controller.onHide(() => {
|
||||
this.resetContextKeys();
|
||||
this._onHide.fire();
|
||||
}));
|
||||
return controller;
|
||||
}
|
||||
setContextKey(id) {
|
||||
let key;
|
||||
if (id) {
|
||||
key = this.contexts.get(id);
|
||||
if (!key) {
|
||||
key = new RawContextKey(id, false)
|
||||
.bindTo(this.contextKeyService);
|
||||
this.contexts.set(id, key);
|
||||
}
|
||||
}
|
||||
if (key && key.get()) {
|
||||
return; // already active context
|
||||
}
|
||||
this.resetContextKeys();
|
||||
key?.set(true);
|
||||
}
|
||||
resetContextKeys() {
|
||||
this.contexts.forEach(context => {
|
||||
if (context.get()) {
|
||||
context.reset();
|
||||
}
|
||||
});
|
||||
}
|
||||
pick(picks, options, token = CancellationToken.None) {
|
||||
return this.controller.pick(picks, options, token);
|
||||
}
|
||||
input(options = {}, token = CancellationToken.None) {
|
||||
return this.controller.input(options, token);
|
||||
}
|
||||
createQuickPick(options = { useSeparators: false }) {
|
||||
return this.controller.createQuickPick(options);
|
||||
}
|
||||
createInputBox() {
|
||||
return this.controller.createInputBox();
|
||||
}
|
||||
toggleHover() {
|
||||
if (this.hasController) {
|
||||
this.controller.toggleHover();
|
||||
}
|
||||
}
|
||||
updateStyles() {
|
||||
if (this.hasController) {
|
||||
this.controller.applyStyles(this.computeStyles());
|
||||
}
|
||||
}
|
||||
computeStyles() {
|
||||
return {
|
||||
widget: {
|
||||
quickInputBackground: asCssVariable(quickInputBackground),
|
||||
quickInputForeground: asCssVariable(quickInputForeground),
|
||||
quickInputTitleBackground: asCssVariable(quickInputTitleBackground),
|
||||
widgetBorder: asCssVariable(widgetBorder),
|
||||
widgetShadow: asCssVariable(widgetShadow),
|
||||
},
|
||||
inputBox: defaultInputBoxStyles,
|
||||
toggle: defaultToggleStyles,
|
||||
countBadge: defaultCountBadgeStyles,
|
||||
button: defaultButtonStyles,
|
||||
progressBar: defaultProgressBarStyles,
|
||||
keybindingLabel: defaultKeybindingLabelStyles,
|
||||
list: getListStyles({
|
||||
listBackground: quickInputBackground,
|
||||
listFocusBackground: quickInputListFocusBackground,
|
||||
listFocusForeground: quickInputListFocusForeground,
|
||||
// Look like focused when inactive.
|
||||
listInactiveFocusForeground: quickInputListFocusForeground,
|
||||
listInactiveSelectionIconForeground: quickInputListFocusIconForeground,
|
||||
listInactiveFocusBackground: quickInputListFocusBackground,
|
||||
listFocusOutline: activeContrastBorder,
|
||||
listInactiveFocusOutline: activeContrastBorder,
|
||||
treeStickyScrollBackground: quickInputBackground,
|
||||
}),
|
||||
pickerGroup: {
|
||||
pickerGroupBorder: asCssVariable(pickerGroupBorder),
|
||||
pickerGroupForeground: asCssVariable(pickerGroupForeground),
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
QuickInputService = __decorate([
|
||||
__param(0, IInstantiationService),
|
||||
__param(1, IContextKeyService),
|
||||
__param(2, IThemeService),
|
||||
__param(3, ILayoutService),
|
||||
__param(4, IConfigurationService)
|
||||
], QuickInputService);
|
||||
export { QuickInputService };
|
||||
//# sourceMappingURL=quickInputService.js.map
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,87 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import * as dom from '../../../base/browser/dom.js';
|
||||
import * as domStylesheetsJs from '../../../base/browser/domStylesheets.js';
|
||||
import * as cssJs from '../../../base/browser/cssValue.js';
|
||||
import { DomEmitter } from '../../../base/browser/event.js';
|
||||
import { Event } from '../../../base/common/event.js';
|
||||
import { StandardKeyboardEvent } from '../../../base/browser/keyboardEvent.js';
|
||||
import { Gesture, EventType as GestureEventType } from '../../../base/browser/touch.js';
|
||||
import { renderLabelWithIcons } from '../../../base/browser/ui/iconLabel/iconLabels.js';
|
||||
import { IdGenerator } from '../../../base/common/idGenerator.js';
|
||||
import { parseLinkedText } from '../../../base/common/linkedText.js';
|
||||
import './media/quickInput.css';
|
||||
import { localize } from '../../../nls.js';
|
||||
const iconPathToClass = {};
|
||||
const iconClassGenerator = new IdGenerator('quick-input-button-icon-');
|
||||
function getIconClass(iconPath) {
|
||||
if (!iconPath) {
|
||||
return undefined;
|
||||
}
|
||||
let iconClass;
|
||||
const key = iconPath.dark.toString();
|
||||
if (iconPathToClass[key]) {
|
||||
iconClass = iconPathToClass[key];
|
||||
}
|
||||
else {
|
||||
iconClass = iconClassGenerator.nextId();
|
||||
domStylesheetsJs.createCSSRule(`.${iconClass}, .hc-light .${iconClass}`, `background-image: ${cssJs.asCSSUrl(iconPath.light || iconPath.dark)}`);
|
||||
domStylesheetsJs.createCSSRule(`.vs-dark .${iconClass}, .hc-black .${iconClass}`, `background-image: ${cssJs.asCSSUrl(iconPath.dark)}`);
|
||||
iconPathToClass[key] = iconClass;
|
||||
}
|
||||
return iconClass;
|
||||
}
|
||||
export function quickInputButtonToAction(button, id, run) {
|
||||
let cssClasses = button.iconClass || getIconClass(button.iconPath);
|
||||
if (button.alwaysVisible) {
|
||||
cssClasses = cssClasses ? `${cssClasses} always-visible` : 'always-visible';
|
||||
}
|
||||
return {
|
||||
id,
|
||||
label: '',
|
||||
tooltip: button.tooltip || '',
|
||||
class: cssClasses,
|
||||
enabled: true,
|
||||
run
|
||||
};
|
||||
}
|
||||
export function renderQuickInputDescription(description, container, actionHandler) {
|
||||
dom.reset(container);
|
||||
const parsed = parseLinkedText(description);
|
||||
let tabIndex = 0;
|
||||
for (const node of parsed.nodes) {
|
||||
if (typeof node === 'string') {
|
||||
container.append(...renderLabelWithIcons(node));
|
||||
}
|
||||
else {
|
||||
let title = node.title;
|
||||
if (!title && node.href.startsWith('command:')) {
|
||||
title = localize(1754, "Click to execute command '{0}'", node.href.substring('command:'.length));
|
||||
}
|
||||
else if (!title) {
|
||||
title = node.href;
|
||||
}
|
||||
const anchor = dom.$('a', { href: node.href, title, tabIndex: tabIndex++ }, node.label);
|
||||
anchor.style.textDecoration = 'underline';
|
||||
const handleOpen = (e) => {
|
||||
if (dom.isEventLike(e)) {
|
||||
dom.EventHelper.stop(e, true);
|
||||
}
|
||||
actionHandler.callback(node.href);
|
||||
};
|
||||
const onClick = actionHandler.disposables.add(new DomEmitter(anchor, dom.EventType.CLICK)).event;
|
||||
const onKeydown = actionHandler.disposables.add(new DomEmitter(anchor, dom.EventType.KEY_DOWN)).event;
|
||||
const onSpaceOrEnter = Event.chain(onKeydown, $ => $.filter(e => {
|
||||
const event = new StandardKeyboardEvent(e);
|
||||
return event.equals(10 /* KeyCode.Space */) || event.equals(3 /* KeyCode.Enter */);
|
||||
}));
|
||||
actionHandler.disposables.add(Gesture.addTarget(anchor));
|
||||
const onTap = actionHandler.disposables.add(new DomEmitter(anchor, GestureEventType.Tap)).event;
|
||||
Event.any(onClick, onTap, onSpaceOrEnter)(handleOpen, null, actionHandler.disposables);
|
||||
container.appendChild(anchor);
|
||||
}
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=quickInputUtils.js.map
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,17 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import { QuickInputTreeRenderer } from './quickInputTreeRenderer.js';
|
||||
/**
|
||||
* Delegate for QuickInputTree that provides height and template information.
|
||||
*/
|
||||
export class QuickInputTreeDelegate {
|
||||
getHeight(_element) {
|
||||
return 22;
|
||||
}
|
||||
getTemplateId(_element) {
|
||||
return QuickInputTreeRenderer.ID;
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=quickInputDelegate.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"sources":["file:///mnt/vss/_work/1/s/dependencies/vscode/out-editor-src/vs/platform/quickinput/browser/tree/quickInputDelegate.ts","vs/platform/quickinput/browser/tree/quickInputDelegate.ts"],"names":[],"mappings":"AAAA;;;gGAGgG;AAIhG,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AAErE;;GAEG;AACH,MAAM,OAAO,sBAAsB;IAClC,SAAS,CAAC,QAAW;QACpB,OAAO,EAAE,CAAC;IACX,CAAC;IAED,aAAa,CAAC,QAAW;QACxB,OAAO,sBAAsB,CAAC,EAAE,CAAC;IAClC,CAAC;CACD","file":"quickInputDelegate.js","sourceRoot":"file:///mnt/vss/_work/1/s/dependencies/vscode/out-editor-src","sourcesContent":["/*---------------------------------------------------------------------------------------------\n * Copyright (c) Microsoft Corporation. All rights reserved.\n * Licensed under the MIT License. See License.txt in the project root for license information.\n *--------------------------------------------------------------------------------------------*/\n\nimport { IListVirtualDelegate } from '../../../../base/browser/ui/list/list.js';\nimport { IQuickTreeItem } from '../../common/quickInput.js';\nimport { QuickInputTreeRenderer } from './quickInputTreeRenderer.js';\n\n/**\n * Delegate for QuickInputTree that provides height and template information.\n */\nexport class QuickInputTreeDelegate<T extends IQuickTreeItem> implements IListVirtualDelegate<T> {\n\tgetHeight(_element: T): number {\n\t\treturn 22;\n\t}\n\n\tgetTemplateId(_element: T): string {\n\t\treturn QuickInputTreeRenderer.ID;\n\t}\n}\n","/*---------------------------------------------------------------------------------------------\n * Copyright (c) Microsoft Corporation. All rights reserved.\n * Licensed under the MIT License. See License.txt in the project root for license information.\n *--------------------------------------------------------------------------------------------*/\n\nimport { IListVirtualDelegate } from '../../../../base/browser/ui/list/list.js';\nimport { IQuickTreeItem } from '../../common/quickInput.js';\nimport { QuickInputTreeRenderer } from './quickInputTreeRenderer.js';\n\n/**\n * Delegate for QuickInputTree that provides height and template information.\n */\nexport class QuickInputTreeDelegate<T extends IQuickTreeItem> implements IListVirtualDelegate<T> {\n\tgetHeight(_element: T): number {\n\t\treturn 22;\n\t}\n\n\tgetTemplateId(_element: T): string {\n\t\treturn QuickInputTreeRenderer.ID;\n\t}\n}\n"]}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
export function getParentNodeState(parentChildren) {
|
||||
let containsChecks = false;
|
||||
let containsUnchecks = false;
|
||||
let containsPartial = false;
|
||||
for (const element of parentChildren) {
|
||||
switch (element.element?.checked) {
|
||||
case 'partial':
|
||||
containsPartial = true;
|
||||
break;
|
||||
case true:
|
||||
containsChecks = true;
|
||||
break;
|
||||
default:
|
||||
containsUnchecks = true;
|
||||
break;
|
||||
}
|
||||
if (containsChecks && containsUnchecks && containsPartial) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
const newState = containsUnchecks
|
||||
? containsPartial
|
||||
? 'partial'
|
||||
: containsChecks
|
||||
? 'partial'
|
||||
: false
|
||||
: containsPartial
|
||||
? 'partial'
|
||||
: containsChecks;
|
||||
return newState;
|
||||
}
|
||||
//# sourceMappingURL=quickInputTree.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"sources":["file:///mnt/vss/_work/1/s/dependencies/vscode/out-editor-src/vs/platform/quickinput/browser/tree/quickInputTree.ts","vs/platform/quickinput/browser/tree/quickInputTree.ts"],"names":[],"mappings":"AAAA;;;gGAGgG;AAWhG,MAAM,UAAU,kBAAkB,CAAC,cAA+G;IACjJ,IAAI,cAAc,GAAG,KAAK,CAAC;IAC3B,IAAI,gBAAgB,GAAG,KAAK,CAAC;IAC7B,IAAI,eAAe,GAAG,KAAK,CAAC;IAE5B,KAAK,MAAM,OAAO,IAAI,cAAc,EAAE,CAAC;QACtC,QAAQ,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,CAAC;YAClC,KAAK,SAAS;gBACb,eAAe,GAAG,IAAI,CAAC;gBACvB,MAAM;YACP,KAAK,IAAI;gBACR,cAAc,GAAG,IAAI,CAAC;gBACtB,MAAM;YACP;gBACC,gBAAgB,GAAG,IAAI,CAAC;gBACxB,MAAM;QACR,CAAC;QACD,IAAI,cAAc,IAAI,gBAAgB,IAAI,eAAe,EAAE,CAAC;YAC3D,MAAM;QACP,CAAC;IACF,CAAC;IACD,MAAM,QAAQ,GAAG,gBAAgB;QAChC,CAAC,CAAC,eAAe;YAChB,CAAC,CAAC,SAAS;YACX,CAAC,CAAC,cAAc;gBACf,CAAC,CAAC,SAAS;gBACX,CAAC,CAAC,KAAK;QACT,CAAC,CAAC,eAAe;YAChB,CAAC,CAAC,SAAS;YACX,CAAC,CAAC,cAAc,CAAC;IACnB,OAAO,QAAQ,CAAC;AACjB,CAAC","file":"quickInputTree.js","sourceRoot":"file:///mnt/vss/_work/1/s/dependencies/vscode/out-editor-src","sourcesContent":["/*---------------------------------------------------------------------------------------------\n * Copyright (c) Microsoft Corporation. All rights reserved.\n * Licensed under the MIT License. See License.txt in the project root for license information.\n *--------------------------------------------------------------------------------------------*/\n\nimport { IMatch } from '../../../../base/common/filters.js';\nimport { IQuickTreeItem } from '../../common/quickInput.js';\nimport { IObjectTreeElement, ITreeNode } from '../../../../base/browser/ui/tree/tree.js';\n\nexport interface IQuickTreeFilterData {\n\treadonly labelHighlights?: IMatch[];\n\treadonly descriptionHighlights?: IMatch[];\n}\n\nexport function getParentNodeState(parentChildren: ITreeNode<IQuickTreeItem | null, IQuickTreeFilterData>[] | IObjectTreeElement<IQuickTreeItem>[]): boolean | 'partial' {\n\tlet containsChecks = false;\n\tlet containsUnchecks = false;\n\tlet containsPartial = false;\n\n\tfor (const element of parentChildren) {\n\t\tswitch (element.element?.checked) {\n\t\t\tcase 'partial':\n\t\t\t\tcontainsPartial = true;\n\t\t\t\tbreak;\n\t\t\tcase true:\n\t\t\t\tcontainsChecks = true;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tcontainsUnchecks = true;\n\t\t\t\tbreak;\n\t\t}\n\t\tif (containsChecks && containsUnchecks && containsPartial) {\n\t\t\tbreak;\n\t\t}\n\t}\n\tconst newState = containsUnchecks\n\t\t? containsPartial\n\t\t\t? 'partial'\n\t\t\t: containsChecks\n\t\t\t\t? 'partial'\n\t\t\t\t: false\n\t\t: containsPartial\n\t\t\t? 'partial'\n\t\t\t: containsChecks;\n\treturn newState;\n}\n","/*---------------------------------------------------------------------------------------------\n * Copyright (c) Microsoft Corporation. All rights reserved.\n * Licensed under the MIT License. See License.txt in the project root for license information.\n *--------------------------------------------------------------------------------------------*/\n\nimport { IMatch } from '../../../../base/common/filters.js';\nimport { IQuickTreeItem } from '../../common/quickInput.js';\nimport { IObjectTreeElement, ITreeNode } from '../../../../base/browser/ui/tree/tree.js';\n\nexport interface IQuickTreeFilterData {\n\treadonly labelHighlights?: IMatch[];\n\treadonly descriptionHighlights?: IMatch[];\n}\n\nexport function getParentNodeState(parentChildren: ITreeNode<IQuickTreeItem | null, IQuickTreeFilterData>[] | IObjectTreeElement<IQuickTreeItem>[]): boolean | 'partial' {\n\tlet containsChecks = false;\n\tlet containsUnchecks = false;\n\tlet containsPartial = false;\n\n\tfor (const element of parentChildren) {\n\t\tswitch (element.element?.checked) {\n\t\t\tcase 'partial':\n\t\t\t\tcontainsPartial = true;\n\t\t\t\tbreak;\n\t\t\tcase true:\n\t\t\t\tcontainsChecks = true;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tcontainsUnchecks = true;\n\t\t\t\tbreak;\n\t\t}\n\t\tif (containsChecks && containsUnchecks && containsPartial) {\n\t\t\tbreak;\n\t\t}\n\t}\n\tconst newState = containsUnchecks\n\t\t? containsPartial\n\t\t\t? 'partial'\n\t\t\t: containsChecks\n\t\t\t\t? 'partial'\n\t\t\t\t: false\n\t\t: containsPartial\n\t\t\t? 'partial'\n\t\t\t: containsChecks;\n\treturn newState;\n}\n"]}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import { Event } from '../../../../base/common/event.js';
|
||||
import { getCodiconAriaLabel } from '../../../../base/common/iconLabels.js';
|
||||
import { localize } from '../../../../nls.js';
|
||||
/**
|
||||
* Accessibility provider for QuickTree.
|
||||
*/
|
||||
export class QuickTreeAccessibilityProvider {
|
||||
constructor(onCheckedEvent) {
|
||||
this.onCheckedEvent = onCheckedEvent;
|
||||
}
|
||||
getWidgetAriaLabel() {
|
||||
return localize(1755, "Quick Tree");
|
||||
}
|
||||
getAriaLabel(element) {
|
||||
return element.ariaLabel || [element.label, element.description]
|
||||
.map(s => getCodiconAriaLabel(s))
|
||||
.filter(s => !!s)
|
||||
.join(', ');
|
||||
}
|
||||
getWidgetRole() {
|
||||
return 'tree';
|
||||
}
|
||||
getRole(_element) {
|
||||
return 'checkbox';
|
||||
}
|
||||
isChecked(element) {
|
||||
return {
|
||||
get value() { return element.checked === true; },
|
||||
onDidChange: e => Event.filter(this.onCheckedEvent, e => e.item === element)(_ => e()),
|
||||
};
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=quickInputTreeAccessibilityProvider.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"sources":["vs/platform/quickinput/browser/tree/quickInputTreeAccessibilityProvider.ts"],"names":[],"mappings":"AAAA;;;gGAGgG;AAIhG,OAAO,EAAE,KAAK,EAAyB,MAAM,kCAAkC,CAAC;AAChF,OAAO,EAAE,mBAAmB,EAAE,MAAM,uCAAuC,CAAC;AAC5E,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAE9C;;GAEG;AACH,MAAM,OAAO,8BAA8B;IAC1C,YAA6B,cAAiD;QAAjD,mBAAc,GAAd,cAAc,CAAmC;IAAI,CAAC;IAEnF,kBAAkB;QACjB,OAAO,QAAQ,CAAC,IAAW,EAAE,YAAY,CAAC,CAAC;IAC5C,CAAC;IAED,YAAY,CAAC,OAAU;QACtB,OAAO,OAAO,CAAC,SAAS,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,WAAW,CAAC;aAC9D,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC;aAChC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;aAChB,IAAI,CAAC,IAAI,CAAC,CAAC;IACd,CAAC;IAED,aAAa;QACZ,OAAO,MAAM,CAAC;IACf,CAAC;IAED,OAAO,CAAC,QAAW;QAClB,OAAO,UAAU,CAAC;IACnB,CAAC;IAED,SAAS,CAAC,OAAU;QACnB,OAAO;YACN,IAAI,KAAK,KAAK,OAAO,OAAO,CAAC,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC;YAChD,WAAW,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;SACtF,CAAC;IACH,CAAC;CACD","file":"quickInputTreeAccessibilityProvider.js","sourceRoot":"file:///mnt/vss/_work/1/s/dependencies/vscode/out-editor-src","sourcesContent":["/*---------------------------------------------------------------------------------------------\n * Copyright (c) Microsoft Corporation. All rights reserved.\n * Licensed under the MIT License. See License.txt in the project root for license information.\n *--------------------------------------------------------------------------------------------*/\n\nimport { AriaRole } from '../../../../base/browser/ui/aria/aria.js';\nimport { IListAccessibilityProvider } from '../../../../base/browser/ui/list/listWidget.js';\nimport { Event, IValueWithChangeEvent } from '../../../../base/common/event.js';\nimport { getCodiconAriaLabel } from '../../../../base/common/iconLabels.js';\nimport { localize } from '../../../../nls.js';\nimport { IQuickTreeCheckboxEvent, IQuickTreeItem } from '../../common/quickInput.js';\n/**\n * Accessibility provider for QuickTree.\n */\nexport class QuickTreeAccessibilityProvider<T extends IQuickTreeItem> implements IListAccessibilityProvider<T> {\n\tconstructor(private readonly onCheckedEvent: Event<IQuickTreeCheckboxEvent<T>>) { }\n\n\tgetWidgetAriaLabel(): string {\n\t\treturn localize('quickTree', \"Quick Tree\");\n\t}\n\n\tgetAriaLabel(element: T): string {\n\t\treturn element.ariaLabel || [element.label, element.description]\n\t\t\t.map(s => getCodiconAriaLabel(s))\n\t\t\t.filter(s => !!s)\n\t\t\t.join(', ');\n\t}\n\n\tgetWidgetRole(): AriaRole {\n\t\treturn 'tree';\n\t}\n\n\tgetRole(_element: T): AriaRole {\n\t\treturn 'checkbox';\n\t}\n\n\tisChecked(element: T): IValueWithChangeEvent<boolean> | undefined {\n\t\treturn {\n\t\t\tget value() { return element.checked === true; },\n\t\t\tonDidChange: e => Event.filter(this.onCheckedEvent, e => e.item === element)(_ => e()),\n\t\t};\n\t}\n}\n"]}
|
||||
@@ -0,0 +1,187 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
||||
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
||||
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
||||
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||
};
|
||||
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
||||
return function (target, key) { decorator(target, key, paramIndex); }
|
||||
};
|
||||
import * as dom from '../../../../base/browser/dom.js';
|
||||
import { RenderIndentGuides } from '../../../../base/browser/ui/tree/abstractTree.js';
|
||||
import { Emitter } from '../../../../base/common/event.js';
|
||||
import { Disposable } from '../../../../base/common/lifecycle.js';
|
||||
import { IInstantiationService } from '../../../instantiation/common/instantiation.js';
|
||||
import { WorkbenchObjectTree } from '../../../list/browser/listService.js';
|
||||
import { QuickInputTreeDelegate } from './quickInputDelegate.js';
|
||||
import { getParentNodeState } from './quickInputTree.js';
|
||||
import { QuickTreeAccessibilityProvider } from './quickInputTreeAccessibilityProvider.js';
|
||||
import { QuickInputTreeFilter } from './quickInputTreeFilter.js';
|
||||
import { QuickInputTreeRenderer } from './quickInputTreeRenderer.js';
|
||||
const $ = dom.$;
|
||||
let QuickInputTreeController = class QuickInputTreeController extends Disposable {
|
||||
constructor(container, hoverDelegate, instantiationService) {
|
||||
super();
|
||||
this.instantiationService = instantiationService;
|
||||
this._onDidTriggerButton = this._register(new Emitter());
|
||||
this._onDidChangeCheckboxState = this._register(new Emitter());
|
||||
this.onDidChangeCheckboxState = this._onDidChangeCheckboxState.event;
|
||||
this._onDidCheckedLeafItemsChange = this._register(new Emitter);
|
||||
this._onLeave = new Emitter();
|
||||
/**
|
||||
* Event that is fired when the tree would no longer have focus.
|
||||
*/
|
||||
this.onLeave = this._onLeave.event;
|
||||
this._onDidAccept = this._register(new Emitter());
|
||||
/**
|
||||
* Event that is fired when a non-pickable item is clicked, indicating acceptance.
|
||||
*/
|
||||
this.onDidAccept = this._onDidAccept.event;
|
||||
this._container = dom.append(container, $('.quick-input-tree'));
|
||||
this._renderer = this._register(this.instantiationService.createInstance(QuickInputTreeRenderer, hoverDelegate, this._onDidTriggerButton, this.onDidChangeCheckboxState));
|
||||
this._filter = this.instantiationService.createInstance(QuickInputTreeFilter);
|
||||
this._tree = this._register(this.instantiationService.createInstance((WorkbenchObjectTree), 'QuickInputTree', this._container, new QuickInputTreeDelegate(), [this._renderer], {
|
||||
accessibilityProvider: new QuickTreeAccessibilityProvider(this.onDidChangeCheckboxState),
|
||||
horizontalScrolling: false,
|
||||
multipleSelectionSupport: false,
|
||||
findWidgetEnabled: false,
|
||||
alwaysConsumeMouseWheel: true,
|
||||
hideTwistiesOfChildlessElements: true,
|
||||
renderIndentGuides: RenderIndentGuides.None,
|
||||
expandOnDoubleClick: true,
|
||||
expandOnlyOnTwistieClick: true,
|
||||
disableExpandOnSpacebar: true,
|
||||
sorter: {
|
||||
compare: (a, b) => {
|
||||
if (a.label < b.label) {
|
||||
return -1;
|
||||
}
|
||||
else if (a.label > b.label) {
|
||||
return 1;
|
||||
}
|
||||
// use description to break ties
|
||||
if (a.description && b.description) {
|
||||
if (a.description < b.description) {
|
||||
return -1;
|
||||
}
|
||||
else if (a.description > b.description) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else if (a.description) {
|
||||
return -1;
|
||||
}
|
||||
else if (b.description) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
},
|
||||
filter: this._filter
|
||||
}));
|
||||
this.registerOnOpenListener();
|
||||
}
|
||||
get tree() {
|
||||
return this._tree;
|
||||
}
|
||||
get displayed() {
|
||||
return this._container.style.display !== 'none';
|
||||
}
|
||||
set displayed(value) {
|
||||
this._container.style.display = value ? '' : 'none';
|
||||
}
|
||||
getActiveDescendant() {
|
||||
return this._tree.getHTMLElement().getAttribute('aria-activedescendant');
|
||||
}
|
||||
layout(maxHeight) {
|
||||
this._tree.getHTMLElement().style.maxHeight = maxHeight ? `${
|
||||
// Make sure height aligns with list item heights
|
||||
Math.floor(maxHeight / 44) * 44
|
||||
// Add some extra height so that it's clear there's more to scroll
|
||||
+ 6}px` : '';
|
||||
this._tree.layout();
|
||||
}
|
||||
registerOnOpenListener() {
|
||||
this._register(this._tree.onDidOpen(e => {
|
||||
const item = e.element;
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
if (item.disabled) {
|
||||
return;
|
||||
}
|
||||
// Check if the item is pickable (defaults to true if not specified)
|
||||
if (item.pickable === false) {
|
||||
// For non-pickable items, set it as the active item and fire the accept event
|
||||
this._tree.setFocus([item]);
|
||||
this._onDidAccept.fire();
|
||||
return;
|
||||
}
|
||||
const newState = item.checked !== true;
|
||||
if ((item.checked ?? false) === newState) {
|
||||
return; // No change
|
||||
}
|
||||
// Handle checked item
|
||||
item.checked = newState;
|
||||
this._tree.rerender(item);
|
||||
// Handle children of the checked item
|
||||
const updateSet = new Set();
|
||||
const toUpdate = [...this._tree.getNode(item).children];
|
||||
while (toUpdate.length) {
|
||||
const pop = toUpdate.shift();
|
||||
if (pop?.element && !updateSet.has(pop.element)) {
|
||||
updateSet.add(pop.element);
|
||||
if ((pop.element.checked ?? false) !== item.checked) {
|
||||
pop.element.checked = item.checked;
|
||||
this._tree.rerender(pop.element);
|
||||
}
|
||||
toUpdate.push(...pop.children);
|
||||
}
|
||||
}
|
||||
// Handle parents of the checked item
|
||||
let parent = this._tree.getParentElement(item);
|
||||
while (parent) {
|
||||
const parentChildren = [...this._tree.getNode(parent).children];
|
||||
const newState = getParentNodeState(parentChildren);
|
||||
if ((parent.checked ?? false) !== newState) {
|
||||
parent.checked = newState;
|
||||
this._tree.rerender(parent);
|
||||
}
|
||||
parent = this._tree.getParentElement(parent);
|
||||
}
|
||||
this._onDidChangeCheckboxState.fire({
|
||||
item,
|
||||
checked: item.checked ?? false
|
||||
});
|
||||
this._onDidCheckedLeafItemsChange.fire(this.getCheckedLeafItems());
|
||||
}));
|
||||
}
|
||||
getCheckedLeafItems() {
|
||||
const lookedAt = new Set();
|
||||
const toLookAt = [...this._tree.getNode().children];
|
||||
const checkedItems = new Array();
|
||||
while (toLookAt.length) {
|
||||
const lookAt = toLookAt.shift();
|
||||
if (!lookAt?.element || lookedAt.has(lookAt.element)) {
|
||||
continue;
|
||||
}
|
||||
if (lookAt.element.checked) {
|
||||
lookedAt.add(lookAt.element);
|
||||
toLookAt.push(...lookAt.children);
|
||||
if (!lookAt.element.children) {
|
||||
checkedItems.push(lookAt.element);
|
||||
}
|
||||
}
|
||||
}
|
||||
return checkedItems;
|
||||
}
|
||||
};
|
||||
QuickInputTreeController = __decorate([
|
||||
__param(2, IInstantiationService)
|
||||
], QuickInputTreeController);
|
||||
export { QuickInputTreeController };
|
||||
//# sourceMappingURL=quickInputTreeController.js.map
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,40 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import { matchesFuzzyIconAware, parseLabelWithIcons } from '../../../../base/common/iconLabels.js';
|
||||
export class QuickInputTreeFilter {
|
||||
constructor() {
|
||||
this.filterValue = '';
|
||||
this.matchOnLabel = true;
|
||||
this.matchOnDescription = false;
|
||||
}
|
||||
filter(element, parentVisibility) {
|
||||
if (!this.filterValue || !(this.matchOnLabel || this.matchOnDescription)) {
|
||||
return element.children
|
||||
? { visibility: 2 /* TreeVisibility.Recurse */, data: {} }
|
||||
: { visibility: 1 /* TreeVisibility.Visible */, data: {} };
|
||||
}
|
||||
const labelHighlights = this.matchOnLabel ? matchesFuzzyIconAware(this.filterValue, parseLabelWithIcons(element.label)) ?? undefined : undefined;
|
||||
const descriptionHighlights = this.matchOnDescription ? matchesFuzzyIconAware(this.filterValue, parseLabelWithIcons(element.description || '')) ?? undefined : undefined;
|
||||
const visibility = parentVisibility === 1 /* TreeVisibility.Visible */
|
||||
// Parent is visible because it had matches, so we show all children
|
||||
? 1 /* TreeVisibility.Visible */
|
||||
// This would only happen on Parent is recurse so...
|
||||
: (labelHighlights || descriptionHighlights)
|
||||
// If we have any highlights, we are visible
|
||||
? 1 /* TreeVisibility.Visible */
|
||||
// Otherwise, we defer to the children or if no children, we are hidden
|
||||
: element.children
|
||||
? 2 /* TreeVisibility.Recurse */
|
||||
: 0 /* TreeVisibility.Hidden */;
|
||||
return {
|
||||
visibility,
|
||||
data: {
|
||||
labelHighlights,
|
||||
descriptionHighlights
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=quickInputTreeFilter.js.map
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,140 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
||||
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
||||
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
||||
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||
};
|
||||
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
||||
return function (target, key) { decorator(target, key, paramIndex); }
|
||||
};
|
||||
var QuickInputTreeRenderer_1;
|
||||
import * as cssJs from '../../../../base/browser/cssValue.js';
|
||||
import * as dom from '../../../../base/browser/dom.js';
|
||||
import { ActionBar } from '../../../../base/browser/ui/actionbar/actionbar.js';
|
||||
import { IconLabel } from '../../../../base/browser/ui/iconLabel/iconLabel.js';
|
||||
import { TriStateCheckbox } from '../../../../base/browser/ui/toggle/toggle.js';
|
||||
import { Event } from '../../../../base/common/event.js';
|
||||
import { Disposable, DisposableStore } from '../../../../base/common/lifecycle.js';
|
||||
import { URI } from '../../../../base/common/uri.js';
|
||||
import { defaultCheckboxStyles } from '../../../theme/browser/defaultStyles.js';
|
||||
import { isDark } from '../../../theme/common/theme.js';
|
||||
import { escape } from '../../../../base/common/strings.js';
|
||||
import { IThemeService } from '../../../theme/common/themeService.js';
|
||||
import { quickInputButtonToAction } from '../quickInputUtils.js';
|
||||
const $ = dom.$;
|
||||
let QuickInputTreeRenderer = class QuickInputTreeRenderer extends Disposable {
|
||||
static { QuickInputTreeRenderer_1 = this; }
|
||||
static { this.ID = 'quickInputTreeElement'; }
|
||||
constructor(_hoverDelegate, _buttonTriggeredEmitter, onCheckedEvent, _themeService) {
|
||||
super();
|
||||
this._hoverDelegate = _hoverDelegate;
|
||||
this._buttonTriggeredEmitter = _buttonTriggeredEmitter;
|
||||
this.onCheckedEvent = onCheckedEvent;
|
||||
this._themeService = _themeService;
|
||||
this.templateId = QuickInputTreeRenderer_1.ID;
|
||||
}
|
||||
renderTemplate(container) {
|
||||
const store = new DisposableStore();
|
||||
// Main entry container
|
||||
const entry = dom.append(container, $('.quick-input-tree-entry'));
|
||||
const checkbox = store.add(new TriStateCheckbox('', false, { ...defaultCheckboxStyles, size: 15 }));
|
||||
entry.appendChild(checkbox.domNode);
|
||||
const checkboxLabel = dom.append(entry, $('label.quick-input-tree-label'));
|
||||
const rows = dom.append(checkboxLabel, $('.quick-input-tree-rows'));
|
||||
const row1 = dom.append(rows, $('.quick-input-tree-row'));
|
||||
const icon = dom.prepend(row1, $('.quick-input-tree-icon'));
|
||||
const label = store.add(new IconLabel(row1, {
|
||||
supportHighlights: true,
|
||||
supportDescriptionHighlights: true,
|
||||
supportIcons: true,
|
||||
hoverDelegate: this._hoverDelegate
|
||||
}));
|
||||
const actionBar = store.add(new ActionBar(entry, this._hoverDelegate ? { hoverDelegate: this._hoverDelegate } : undefined));
|
||||
actionBar.domNode.classList.add('quick-input-tree-entry-action-bar');
|
||||
return {
|
||||
toDisposeTemplate: store,
|
||||
entry,
|
||||
checkbox,
|
||||
icon,
|
||||
label,
|
||||
actionBar,
|
||||
toDisposeElement: new DisposableStore(),
|
||||
};
|
||||
}
|
||||
renderElement(node, index, templateData, _details) {
|
||||
const store = templateData.toDisposeElement;
|
||||
const quickTreeItem = node.element;
|
||||
// Checkbox
|
||||
if (quickTreeItem.pickable === false) {
|
||||
// Hide checkbox for non-pickable items
|
||||
templateData.checkbox.domNode.style.display = 'none';
|
||||
}
|
||||
else {
|
||||
templateData.checkbox.domNode.style.display = '';
|
||||
templateData.checkbox.checked = quickTreeItem.checked ?? false;
|
||||
store.add(Event.filter(this.onCheckedEvent, e => e.item === quickTreeItem)(e => templateData.checkbox.checked = e.checked));
|
||||
if (quickTreeItem.disabled) {
|
||||
templateData.checkbox.disable();
|
||||
}
|
||||
}
|
||||
// Icon
|
||||
if (quickTreeItem.iconPath) {
|
||||
const icon = isDark(this._themeService.getColorTheme().type) ? quickTreeItem.iconPath.dark : (quickTreeItem.iconPath.light ?? quickTreeItem.iconPath.dark);
|
||||
const iconUrl = URI.revive(icon);
|
||||
templateData.icon.className = 'quick-input-tree-icon';
|
||||
templateData.icon.style.backgroundImage = cssJs.asCSSUrl(iconUrl);
|
||||
}
|
||||
else {
|
||||
templateData.icon.style.backgroundImage = '';
|
||||
templateData.icon.className = quickTreeItem.iconClass ? `quick-input-tree-icon ${quickTreeItem.iconClass}` : '';
|
||||
}
|
||||
const { labelHighlights: matches, descriptionHighlights: descriptionMatches } = node.filterData || {};
|
||||
// Label and Description
|
||||
let descriptionTitle;
|
||||
// NOTE: If we bring back quick tool tips, we need to check that here like we do in the QuickInputListRenderer
|
||||
if (quickTreeItem.description) {
|
||||
descriptionTitle = {
|
||||
markdown: {
|
||||
value: escape(quickTreeItem.description),
|
||||
supportThemeIcons: true
|
||||
},
|
||||
markdownNotSupportedFallback: quickTreeItem.description
|
||||
};
|
||||
}
|
||||
templateData.label.setLabel(quickTreeItem.label, quickTreeItem.description, {
|
||||
matches,
|
||||
descriptionMatches,
|
||||
extraClasses: quickTreeItem.iconClasses,
|
||||
italic: quickTreeItem.italic,
|
||||
strikethrough: quickTreeItem.strikethrough,
|
||||
labelEscapeNewLines: true,
|
||||
descriptionTitle
|
||||
});
|
||||
// Action Bar
|
||||
const buttons = quickTreeItem.buttons;
|
||||
if (buttons && buttons.length) {
|
||||
templateData.actionBar.push(buttons.map((button, index) => quickInputButtonToAction(button, `tree-${index}`, () => this._buttonTriggeredEmitter.fire({ item: quickTreeItem, button }))), { icon: true, label: false });
|
||||
templateData.entry.classList.add('has-actions');
|
||||
}
|
||||
else {
|
||||
templateData.entry.classList.remove('has-actions');
|
||||
}
|
||||
}
|
||||
disposeElement(_element, _index, templateData, _details) {
|
||||
templateData.toDisposeElement.clear();
|
||||
templateData.actionBar.clear();
|
||||
}
|
||||
disposeTemplate(templateData) {
|
||||
templateData.toDisposeElement.dispose();
|
||||
templateData.toDisposeTemplate.dispose();
|
||||
}
|
||||
};
|
||||
QuickInputTreeRenderer = QuickInputTreeRenderer_1 = __decorate([
|
||||
__param(3, IThemeService)
|
||||
], QuickInputTreeRenderer);
|
||||
export { QuickInputTreeRenderer };
|
||||
//# sourceMappingURL=quickInputTreeRenderer.js.map
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,54 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import { coalesce } from '../../../base/common/arrays.js';
|
||||
import { toDisposable } from '../../../base/common/lifecycle.js';
|
||||
import { Registry } from '../../registry/common/platform.js';
|
||||
export var DefaultQuickAccessFilterValue;
|
||||
(function (DefaultQuickAccessFilterValue) {
|
||||
/**
|
||||
* Keep the value as it is given to quick access.
|
||||
*/
|
||||
DefaultQuickAccessFilterValue[DefaultQuickAccessFilterValue["PRESERVE"] = 0] = "PRESERVE";
|
||||
/**
|
||||
* Use the value that was used last time something was accepted from the picker.
|
||||
*/
|
||||
DefaultQuickAccessFilterValue[DefaultQuickAccessFilterValue["LAST"] = 1] = "LAST";
|
||||
})(DefaultQuickAccessFilterValue || (DefaultQuickAccessFilterValue = {}));
|
||||
export const Extensions = {
|
||||
Quickaccess: 'workbench.contributions.quickaccess'
|
||||
};
|
||||
export class QuickAccessRegistry {
|
||||
constructor() {
|
||||
this.providers = [];
|
||||
this.defaultProvider = undefined;
|
||||
}
|
||||
registerQuickAccessProvider(provider) {
|
||||
// Extract the default provider when no prefix is present
|
||||
if (provider.prefix.length === 0) {
|
||||
this.defaultProvider = provider;
|
||||
}
|
||||
else {
|
||||
this.providers.push(provider);
|
||||
}
|
||||
// sort the providers by decreasing prefix length, such that longer
|
||||
// prefixes take priority: 'ext' vs 'ext install' - the latter should win
|
||||
this.providers.sort((providerA, providerB) => providerB.prefix.length - providerA.prefix.length);
|
||||
return toDisposable(() => {
|
||||
this.providers.splice(this.providers.indexOf(provider), 1);
|
||||
if (this.defaultProvider === provider) {
|
||||
this.defaultProvider = undefined;
|
||||
}
|
||||
});
|
||||
}
|
||||
getQuickAccessProviders() {
|
||||
return coalesce([this.defaultProvider, ...this.providers]);
|
||||
}
|
||||
getQuickAccessProvider(prefix) {
|
||||
const result = prefix ? (this.providers.find(provider => prefix.startsWith(provider.prefix)) || undefined) : undefined;
|
||||
return result || this.defaultProvider;
|
||||
}
|
||||
}
|
||||
Registry.add(Extensions.Quickaccess, new QuickAccessRegistry());
|
||||
//# sourceMappingURL=quickAccess.js.map
|
||||
File diff suppressed because one or more lines are too long
107
_internal/editor/esm/vs/platform/quickinput/common/quickInput.js
Normal file
107
_internal/editor/esm/vs/platform/quickinput/common/quickInput.js
Normal file
@@ -0,0 +1,107 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import { createDecorator } from '../../instantiation/common/instantiation.js';
|
||||
export const NO_KEY_MODS = { ctrlCmd: false, alt: false };
|
||||
export var QuickInputHideReason;
|
||||
(function (QuickInputHideReason) {
|
||||
/**
|
||||
* Focus moved away from the quick input.
|
||||
*/
|
||||
QuickInputHideReason[QuickInputHideReason["Blur"] = 1] = "Blur";
|
||||
/**
|
||||
* An explicit user gesture, e.g. pressing Escape key.
|
||||
*/
|
||||
QuickInputHideReason[QuickInputHideReason["Gesture"] = 2] = "Gesture";
|
||||
/**
|
||||
* Anything else.
|
||||
*/
|
||||
QuickInputHideReason[QuickInputHideReason["Other"] = 3] = "Other";
|
||||
})(QuickInputHideReason || (QuickInputHideReason = {}));
|
||||
/**
|
||||
* Represents the activation behavior for items in a quick input. This means which item will be
|
||||
* "active" (aka focused).
|
||||
*/
|
||||
export var ItemActivation;
|
||||
(function (ItemActivation) {
|
||||
/**
|
||||
* No item will be active.
|
||||
*/
|
||||
ItemActivation[ItemActivation["NONE"] = 0] = "NONE";
|
||||
/**
|
||||
* First item will be active.
|
||||
*/
|
||||
ItemActivation[ItemActivation["FIRST"] = 1] = "FIRST";
|
||||
/**
|
||||
* Second item will be active.
|
||||
*/
|
||||
ItemActivation[ItemActivation["SECOND"] = 2] = "SECOND";
|
||||
/**
|
||||
* Last item will be active.
|
||||
*/
|
||||
ItemActivation[ItemActivation["LAST"] = 3] = "LAST";
|
||||
})(ItemActivation || (ItemActivation = {}));
|
||||
/**
|
||||
* Represents the focus options for a quick pick.
|
||||
*/
|
||||
export var QuickPickFocus;
|
||||
(function (QuickPickFocus) {
|
||||
/**
|
||||
* Focus the first item in the list.
|
||||
*/
|
||||
QuickPickFocus[QuickPickFocus["First"] = 1] = "First";
|
||||
/**
|
||||
* Focus the second item in the list.
|
||||
*/
|
||||
QuickPickFocus[QuickPickFocus["Second"] = 2] = "Second";
|
||||
/**
|
||||
* Focus the last item in the list.
|
||||
*/
|
||||
QuickPickFocus[QuickPickFocus["Last"] = 3] = "Last";
|
||||
/**
|
||||
* Focus the next item in the list.
|
||||
*/
|
||||
QuickPickFocus[QuickPickFocus["Next"] = 4] = "Next";
|
||||
/**
|
||||
* Focus the previous item in the list.
|
||||
*/
|
||||
QuickPickFocus[QuickPickFocus["Previous"] = 5] = "Previous";
|
||||
/**
|
||||
* Focus the next page in the list.
|
||||
*/
|
||||
QuickPickFocus[QuickPickFocus["NextPage"] = 6] = "NextPage";
|
||||
/**
|
||||
* Focus the previous page in the list.
|
||||
*/
|
||||
QuickPickFocus[QuickPickFocus["PreviousPage"] = 7] = "PreviousPage";
|
||||
/**
|
||||
* Focus the first item under the next separator.
|
||||
*/
|
||||
QuickPickFocus[QuickPickFocus["NextSeparator"] = 8] = "NextSeparator";
|
||||
/**
|
||||
* Focus the first item under the current separator.
|
||||
*/
|
||||
QuickPickFocus[QuickPickFocus["PreviousSeparator"] = 9] = "PreviousSeparator";
|
||||
})(QuickPickFocus || (QuickPickFocus = {}));
|
||||
export var QuickInputButtonLocation;
|
||||
(function (QuickInputButtonLocation) {
|
||||
/**
|
||||
* In the title bar.
|
||||
*/
|
||||
QuickInputButtonLocation[QuickInputButtonLocation["Title"] = 1] = "Title";
|
||||
/**
|
||||
* To the right of the input box.
|
||||
*/
|
||||
QuickInputButtonLocation[QuickInputButtonLocation["Inline"] = 2] = "Inline";
|
||||
})(QuickInputButtonLocation || (QuickInputButtonLocation = {}));
|
||||
export class QuickPickItemScorerAccessor {
|
||||
constructor(options) {
|
||||
this.options = options;
|
||||
}
|
||||
}
|
||||
export const quickPickItemScorerAccessor = new QuickPickItemScorerAccessor();
|
||||
//#endregion
|
||||
export const IQuickInputService = createDecorator('quickInputService');
|
||||
//#endregion
|
||||
//# sourceMappingURL=quickInput.js.map
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user