quickAccess.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. /*---------------------------------------------------------------------------------------------
  2. * Copyright (c) Microsoft Corporation. All rights reserved.
  3. * Licensed under the MIT License. See License.txt in the project root for license information.
  4. *--------------------------------------------------------------------------------------------*/
  5. var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
  6. var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
  7. if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
  8. 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;
  9. return c > 3 && r && Object.defineProperty(target, key, r), r;
  10. };
  11. var __param = (this && this.__param) || function (paramIndex, decorator) {
  12. return function (target, key) { decorator(target, key, paramIndex); }
  13. };
  14. import { DeferredPromise } from '../../../base/common/async.js';
  15. import { CancellationTokenSource } from '../../../base/common/cancellation.js';
  16. import { once } from '../../../base/common/functional.js';
  17. import { Disposable, DisposableStore, toDisposable } from '../../../base/common/lifecycle.js';
  18. import { IInstantiationService } from '../../instantiation/common/instantiation.js';
  19. import { DefaultQuickAccessFilterValue, Extensions } from '../common/quickAccess.js';
  20. import { IQuickInputService, ItemActivation } from '../common/quickInput.js';
  21. import { Registry } from '../../registry/common/platform.js';
  22. let QuickAccessController = class QuickAccessController extends Disposable {
  23. constructor(quickInputService, instantiationService) {
  24. super();
  25. this.quickInputService = quickInputService;
  26. this.instantiationService = instantiationService;
  27. this.registry = Registry.as(Extensions.Quickaccess);
  28. this.mapProviderToDescriptor = new Map();
  29. this.lastAcceptedPickerValues = new Map();
  30. this.visibleQuickAccess = undefined;
  31. }
  32. show(value = '', options) {
  33. this.doShowOrPick(value, false, options);
  34. }
  35. doShowOrPick(value, pick, options) {
  36. var _a;
  37. // Find provider for the value to show
  38. const [provider, descriptor] = this.getOrInstantiateProvider(value);
  39. // Return early if quick access is already showing on that same prefix
  40. const visibleQuickAccess = this.visibleQuickAccess;
  41. const visibleDescriptor = visibleQuickAccess === null || visibleQuickAccess === void 0 ? void 0 : visibleQuickAccess.descriptor;
  42. if (visibleQuickAccess && descriptor && visibleDescriptor === descriptor) {
  43. // Apply value only if it is more specific than the prefix
  44. // from the provider and we are not instructed to preserve
  45. if (value !== descriptor.prefix && !(options === null || options === void 0 ? void 0 : options.preserveValue)) {
  46. visibleQuickAccess.picker.value = value;
  47. }
  48. // Always adjust selection
  49. this.adjustValueSelection(visibleQuickAccess.picker, descriptor, options);
  50. return;
  51. }
  52. // Rewrite the filter value based on certain rules unless disabled
  53. if (descriptor && !(options === null || options === void 0 ? void 0 : options.preserveValue)) {
  54. let newValue = undefined;
  55. // If we have a visible provider with a value, take it's filter value but
  56. // rewrite to new provider prefix in case they differ
  57. if (visibleQuickAccess && visibleDescriptor && visibleDescriptor !== descriptor) {
  58. const newValueCandidateWithoutPrefix = visibleQuickAccess.value.substr(visibleDescriptor.prefix.length);
  59. if (newValueCandidateWithoutPrefix) {
  60. newValue = `${descriptor.prefix}${newValueCandidateWithoutPrefix}`;
  61. }
  62. }
  63. // Otherwise, take a default value as instructed
  64. if (!newValue) {
  65. const defaultFilterValue = provider === null || provider === void 0 ? void 0 : provider.defaultFilterValue;
  66. if (defaultFilterValue === DefaultQuickAccessFilterValue.LAST) {
  67. newValue = this.lastAcceptedPickerValues.get(descriptor);
  68. }
  69. else if (typeof defaultFilterValue === 'string') {
  70. newValue = `${descriptor.prefix}${defaultFilterValue}`;
  71. }
  72. }
  73. if (typeof newValue === 'string') {
  74. value = newValue;
  75. }
  76. }
  77. // Create a picker for the provider to use with the initial value
  78. // and adjust the filtering to exclude the prefix from filtering
  79. const disposables = new DisposableStore();
  80. const picker = disposables.add(this.quickInputService.createQuickPick());
  81. picker.value = value;
  82. this.adjustValueSelection(picker, descriptor, options);
  83. picker.placeholder = descriptor === null || descriptor === void 0 ? void 0 : descriptor.placeholder;
  84. picker.quickNavigate = options === null || options === void 0 ? void 0 : options.quickNavigateConfiguration;
  85. picker.hideInput = !!picker.quickNavigate && !visibleQuickAccess; // only hide input if there was no picker opened already
  86. if (typeof (options === null || options === void 0 ? void 0 : options.itemActivation) === 'number' || (options === null || options === void 0 ? void 0 : options.quickNavigateConfiguration)) {
  87. picker.itemActivation = (_a = options === null || options === void 0 ? void 0 : options.itemActivation) !== null && _a !== void 0 ? _a : ItemActivation.SECOND /* quick nav is always second */;
  88. }
  89. picker.contextKey = descriptor === null || descriptor === void 0 ? void 0 : descriptor.contextKey;
  90. picker.filterValue = (value) => value.substring(descriptor ? descriptor.prefix.length : 0);
  91. if (descriptor === null || descriptor === void 0 ? void 0 : descriptor.placeholder) {
  92. picker.ariaLabel = descriptor === null || descriptor === void 0 ? void 0 : descriptor.placeholder;
  93. }
  94. // Pick mode: setup a promise that can be resolved
  95. // with the selected items and prevent execution
  96. let pickPromise = undefined;
  97. if (pick) {
  98. pickPromise = new DeferredPromise();
  99. disposables.add(once(picker.onWillAccept)(e => {
  100. e.veto();
  101. picker.hide();
  102. }));
  103. }
  104. // Register listeners
  105. disposables.add(this.registerPickerListeners(picker, provider, descriptor, value));
  106. // Ask provider to fill the picker as needed if we have one
  107. // and pass over a cancellation token that will indicate when
  108. // the picker is hiding without a pick being made.
  109. const cts = disposables.add(new CancellationTokenSource());
  110. if (provider) {
  111. disposables.add(provider.provide(picker, cts.token));
  112. }
  113. // Finally, trigger disposal and cancellation when the picker
  114. // hides depending on items selected or not.
  115. once(picker.onDidHide)(() => {
  116. if (picker.selectedItems.length === 0) {
  117. cts.cancel();
  118. }
  119. // Start to dispose once picker hides
  120. disposables.dispose();
  121. // Resolve pick promise with selected items
  122. pickPromise === null || pickPromise === void 0 ? void 0 : pickPromise.complete(picker.selectedItems.slice(0));
  123. });
  124. // Finally, show the picker. This is important because a provider
  125. // may not call this and then our disposables would leak that rely
  126. // on the onDidHide event.
  127. picker.show();
  128. // Pick mode: return with promise
  129. if (pick) {
  130. return pickPromise === null || pickPromise === void 0 ? void 0 : pickPromise.p;
  131. }
  132. }
  133. adjustValueSelection(picker, descriptor, options) {
  134. var _a;
  135. let valueSelection;
  136. // Preserve: just always put the cursor at the end
  137. if (options === null || options === void 0 ? void 0 : options.preserveValue) {
  138. valueSelection = [picker.value.length, picker.value.length];
  139. }
  140. // Otherwise: select the value up until the prefix
  141. else {
  142. valueSelection = [(_a = descriptor === null || descriptor === void 0 ? void 0 : descriptor.prefix.length) !== null && _a !== void 0 ? _a : 0, picker.value.length];
  143. }
  144. picker.valueSelection = valueSelection;
  145. }
  146. registerPickerListeners(picker, provider, descriptor, value) {
  147. const disposables = new DisposableStore();
  148. // Remember as last visible picker and clean up once picker get's disposed
  149. const visibleQuickAccess = this.visibleQuickAccess = { picker, descriptor, value };
  150. disposables.add(toDisposable(() => {
  151. if (visibleQuickAccess === this.visibleQuickAccess) {
  152. this.visibleQuickAccess = undefined;
  153. }
  154. }));
  155. // Whenever the value changes, check if the provider has
  156. // changed and if so - re-create the picker from the beginning
  157. disposables.add(picker.onDidChangeValue(value => {
  158. const [providerForValue] = this.getOrInstantiateProvider(value);
  159. if (providerForValue !== provider) {
  160. this.show(value, { preserveValue: true } /* do not rewrite value from user typing! */);
  161. }
  162. else {
  163. visibleQuickAccess.value = value; // remember the value in our visible one
  164. }
  165. }));
  166. // Remember picker input for future use when accepting
  167. if (descriptor) {
  168. disposables.add(picker.onDidAccept(() => {
  169. this.lastAcceptedPickerValues.set(descriptor, picker.value);
  170. }));
  171. }
  172. return disposables;
  173. }
  174. getOrInstantiateProvider(value) {
  175. const providerDescriptor = this.registry.getQuickAccessProvider(value);
  176. if (!providerDescriptor) {
  177. return [undefined, undefined];
  178. }
  179. let provider = this.mapProviderToDescriptor.get(providerDescriptor);
  180. if (!provider) {
  181. provider = this.instantiationService.createInstance(providerDescriptor.ctor);
  182. this.mapProviderToDescriptor.set(providerDescriptor, provider);
  183. }
  184. return [provider, providerDescriptor];
  185. }
  186. };
  187. QuickAccessController = __decorate([
  188. __param(0, IQuickInputService),
  189. __param(1, IInstantiationService)
  190. ], QuickAccessController);
  191. export { QuickAccessController };