@sussudio/base
Version:
Internal APIs for VS Code's utilities and user interface building blocks.
789 lines (788 loc) • 26.5 kB
JavaScript
/*---------------------------------------------------------------------------------------------
* 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;
};
import * as dom from '../../../browser/dom.mjs';
import { StandardKeyboardEvent } from '../../../browser/keyboardEvent.mjs';
import { ActionBar } from '../../../browser/ui/actionbar/actionbar.mjs';
import { IconLabel } from '../../../browser/ui/iconLabel/iconLabel.mjs';
import { KeybindingLabel } from '../../../browser/ui/keybindingLabel/keybindingLabel.mjs';
import { range } from '../../../common/arrays.mjs';
import { getCodiconAriaLabel } from '../../../common/codicons.mjs';
import { compareAnything } from '../../../common/comparers.mjs';
import { memoize } from '../../../common/decorators.mjs';
import { Emitter, Event } from '../../../common/event.mjs';
import { matchesFuzzyIconAware, parseLabelWithIcons } from '../../../common/iconLabels.mjs';
import { dispose } from '../../../common/lifecycle.mjs';
import * as platform from '../../../common/platform.mjs';
import { ltrim } from '../../../common/strings.mjs';
import { withNullAsUndefined } from '../../../common/types.mjs';
import { getIconClass } from './quickInputUtils.mjs';
import '../../../../css!./media/quickInput.mjs';
import { localize } from 'vscode-nls.mjs';
const $ = dom.$;
class ListElement {
hasCheckbox;
index;
item;
saneLabel;
saneSortLabel;
saneMeta;
saneAriaLabel;
saneDescription;
saneDetail;
hidden = false;
_onChecked = new Emitter();
onChecked = this._onChecked.event;
_checked;
get checked() {
return !!this._checked;
}
set checked(value) {
if (value !== this._checked) {
this._checked = value;
this._onChecked.fire(value);
}
}
separator;
labelHighlights;
descriptionHighlights;
detailHighlights;
fireButtonTriggered;
fireSeparatorButtonTriggered;
constructor(init) {
Object.assign(this, init);
}
dispose() {
this._onChecked.dispose();
}
}
class ListElementRenderer {
static ID = 'listelement';
get templateId() {
return ListElementRenderer.ID;
}
renderTemplate(container) {
const data = Object.create(null);
data.toDisposeElement = [];
data.toDisposeTemplate = [];
data.entry = dom.append(container, $('.quick-input-list-entry'));
// Checkbox
const label = dom.append(data.entry, $('label.quick-input-list-label'));
data.toDisposeTemplate.push(
dom.addStandardDisposableListener(label, dom.EventType.CLICK, (e) => {
if (!data.checkbox.offsetParent) {
// If checkbox not visible:
e.preventDefault(); // Prevent toggle of checkbox when it is immediately shown afterwards. #91740
}
}),
);
data.checkbox = dom.append(label, $('input.quick-input-list-checkbox'));
data.checkbox.type = 'checkbox';
data.toDisposeTemplate.push(
dom.addStandardDisposableListener(data.checkbox, dom.EventType.CHANGE, (e) => {
data.element.checked = data.checkbox.checked;
}),
);
// Rows
const rows = dom.append(label, $('.quick-input-list-rows'));
const row1 = dom.append(rows, $('.quick-input-list-row'));
const row2 = dom.append(rows, $('.quick-input-list-row'));
// Label
data.label = new IconLabel(row1, {
supportHighlights: true,
supportDescriptionHighlights: true,
supportIcons: true,
});
// Keybinding
const keybindingContainer = dom.append(row1, $('.quick-input-list-entry-keybinding'));
data.keybinding = new KeybindingLabel(keybindingContainer, platform.OS);
// Detail
const detailContainer = dom.append(row2, $('.quick-input-list-label-meta'));
data.detail = new IconLabel(detailContainer, { supportHighlights: true, supportIcons: true });
// Separator
data.separator = dom.append(data.entry, $('.quick-input-list-separator'));
// Actions
data.actionBar = new ActionBar(data.entry);
data.actionBar.domNode.classList.add('quick-input-list-entry-action-bar');
data.toDisposeTemplate.push(data.actionBar);
return data;
}
renderElement(element, index, data) {
data.element = element;
const mainItem = element.item ? element.item : element.separator;
data.checkbox.checked = element.checked;
data.toDisposeElement.push(element.onChecked((checked) => (data.checkbox.checked = checked)));
const { labelHighlights, descriptionHighlights, detailHighlights } = element;
// Label
const options = Object.create(null);
options.matches = labelHighlights || [];
options.descriptionTitle = element.saneDescription;
options.descriptionMatches = descriptionHighlights || [];
if (mainItem.type !== 'separator') {
options.extraClasses = mainItem.iconClasses;
options.italic = mainItem.italic;
options.strikethrough = mainItem.strikethrough;
data.entry.classList.remove('quick-input-list-separator-as-item');
} else {
data.entry.classList.add('quick-input-list-separator-as-item');
}
data.label.setLabel(element.saneLabel, element.saneDescription, options);
// Keybinding
data.keybinding.set(mainItem.type === 'separator' ? undefined : mainItem.keybinding);
// Meta
if (element.saneDetail) {
data.detail.setLabel(element.saneDetail, undefined, {
matches: detailHighlights,
title: element.saneDetail,
});
} /* else {
// TODO investigate potential detail bleeding into next quickpicks
data.detail.setLabel('');
} */
// Separator
if (element.item && element.separator && element.separator.label) {
data.separator.textContent = element.separator.label;
data.separator.style.display = '';
} else {
data.separator.style.display = 'none';
}
data.entry.classList.toggle('quick-input-list-separator-border', !!element.separator);
// Actions
const buttons = mainItem.buttons;
if (buttons && buttons.length) {
data.actionBar.push(
buttons.map((button, index) => {
let cssClasses = button.iconClass || (button.iconPath ? getIconClass(button.iconPath) : undefined);
if (button.alwaysVisible) {
cssClasses = cssClasses ? `${cssClasses} always-visible` : 'always-visible';
}
return {
id: `id-${index}`,
class: cssClasses,
enabled: true,
label: '',
tooltip: button.tooltip || '',
run: () => {
mainItem.type !== 'separator'
? element.fireButtonTriggered({
button,
item: mainItem,
})
: element.fireSeparatorButtonTriggered({
button,
separator: mainItem,
});
},
};
}),
{ icon: true, label: false },
);
data.entry.classList.add('has-actions');
} else {
data.entry.classList.remove('has-actions');
}
}
disposeElement(element, index, data) {
data.toDisposeElement = dispose(data.toDisposeElement);
data.actionBar.clear();
}
disposeTemplate(data) {
data.toDisposeElement = dispose(data.toDisposeElement);
data.toDisposeTemplate = dispose(data.toDisposeTemplate);
}
}
class ListElementDelegate {
getHeight(element) {
if (!element.item) {
// must be a separator
return 24;
}
return element.saneDetail ? 44 : 22;
}
getTemplateId(element) {
return ListElementRenderer.ID;
}
}
export var QuickInputListFocus;
(function (QuickInputListFocus) {
QuickInputListFocus[(QuickInputListFocus['First'] = 1)] = 'First';
QuickInputListFocus[(QuickInputListFocus['Second'] = 2)] = 'Second';
QuickInputListFocus[(QuickInputListFocus['Last'] = 3)] = 'Last';
QuickInputListFocus[(QuickInputListFocus['Next'] = 4)] = 'Next';
QuickInputListFocus[(QuickInputListFocus['Previous'] = 5)] = 'Previous';
QuickInputListFocus[(QuickInputListFocus['NextPage'] = 6)] = 'NextPage';
QuickInputListFocus[(QuickInputListFocus['PreviousPage'] = 7)] = 'PreviousPage';
})(QuickInputListFocus || (QuickInputListFocus = {}));
export class QuickInputList {
parent;
id;
container;
list;
inputElements = [];
elements = [];
elementsToIndexes = new Map();
matchOnDescription = false;
matchOnDetail = false;
matchOnLabel = true;
matchOnLabelMode = 'fuzzy';
matchOnMeta = true;
sortByLabel = true;
_onChangedAllVisibleChecked = new Emitter();
onChangedAllVisibleChecked = this._onChangedAllVisibleChecked.event;
_onChangedCheckedCount = new Emitter();
onChangedCheckedCount = this._onChangedCheckedCount.event;
_onChangedVisibleCount = new Emitter();
onChangedVisibleCount = this._onChangedVisibleCount.event;
_onChangedCheckedElements = new Emitter();
onChangedCheckedElements = this._onChangedCheckedElements.event;
_onButtonTriggered = new Emitter();
onButtonTriggered = this._onButtonTriggered.event;
_onSeparatorButtonTriggered = new Emitter();
onSeparatorButtonTriggered = this._onSeparatorButtonTriggered.event;
_onKeyDown = new Emitter();
onKeyDown = this._onKeyDown.event;
_onLeave = new Emitter();
onLeave = this._onLeave.event;
_fireCheckedEvents = true;
elementDisposables = [];
disposables = [];
constructor(parent, id, options) {
this.parent = parent;
this.id = id;
this.container = dom.append(this.parent, $('.quick-input-list'));
const delegate = new ListElementDelegate();
const accessibilityProvider = new QuickInputAccessibilityProvider();
this.list = options.createList('QuickInput', this.container, delegate, [new ListElementRenderer()], {
identityProvider: { getId: (element) => element.saneLabel },
setRowLineHeight: false,
multipleSelectionSupport: false,
horizontalScrolling: false,
accessibilityProvider,
});
this.list.getHTMLElement().id = id;
this.disposables.push(this.list);
this.disposables.push(
this.list.onKeyDown((e) => {
const event = new StandardKeyboardEvent(e);
switch (event.keyCode) {
case 10 /* KeyCode.Space */:
this.toggleCheckbox();
break;
case 31 /* KeyCode.KeyA */:
if (platform.isMacintosh ? e.metaKey : e.ctrlKey) {
this.list.setFocus(range(this.list.length));
}
break;
case 16 /* KeyCode.UpArrow */: {
const focus1 = this.list.getFocus();
if (focus1.length === 1 && focus1[0] === 0) {
this._onLeave.fire();
}
break;
}
case 18 /* KeyCode.DownArrow */: {
const focus2 = this.list.getFocus();
if (focus2.length === 1 && focus2[0] === this.list.length - 1) {
this._onLeave.fire();
}
break;
}
}
this._onKeyDown.fire(event);
}),
);
this.disposables.push(
this.list.onMouseDown((e) => {
if (e.browserEvent.button !== 2) {
// Works around / fixes #64350.
e.browserEvent.preventDefault();
}
}),
);
this.disposables.push(
dom.addDisposableListener(this.container, dom.EventType.CLICK, (e) => {
if (e.x || e.y) {
// Avoid 'click' triggered by 'space' on checkbox.
this._onLeave.fire();
}
}),
);
this.disposables.push(
this.list.onMouseMiddleClick((e) => {
this._onLeave.fire();
}),
);
this.disposables.push(
this.list.onContextMenu((e) => {
if (typeof e.index === 'number') {
e.browserEvent.preventDefault();
// we want to treat a context menu event as
// a gesture to open the item at the index
// since we do not have any context menu
// this enables for example macOS to Ctrl-
// click on an item to open it.
this.list.setSelection([e.index]);
}
}),
);
this.disposables.push(
this._onChangedAllVisibleChecked,
this._onChangedCheckedCount,
this._onChangedVisibleCount,
this._onChangedCheckedElements,
this._onButtonTriggered,
this._onSeparatorButtonTriggered,
this._onLeave,
this._onKeyDown,
);
}
get onDidChangeFocus() {
return Event.map(this.list.onDidChangeFocus, (e) => e.elements.map((e) => e.item));
}
get onDidChangeSelection() {
return Event.map(this.list.onDidChangeSelection, (e) => ({
items: e.elements.map((e) => e.item),
event: e.browserEvent,
}));
}
get scrollTop() {
return this.list.scrollTop;
}
set scrollTop(scrollTop) {
this.list.scrollTop = scrollTop;
}
getAllVisibleChecked() {
return this.allVisibleChecked(this.elements, false);
}
allVisibleChecked(elements, whenNoneVisible = true) {
for (let i = 0, n = elements.length; i < n; i++) {
const element = elements[i];
if (!element.hidden) {
if (!element.checked) {
return false;
} else {
whenNoneVisible = true;
}
}
}
return whenNoneVisible;
}
getCheckedCount() {
let count = 0;
const elements = this.elements;
for (let i = 0, n = elements.length; i < n; i++) {
if (elements[i].checked) {
count++;
}
}
return count;
}
getVisibleCount() {
let count = 0;
const elements = this.elements;
for (let i = 0, n = elements.length; i < n; i++) {
if (!elements[i].hidden) {
count++;
}
}
return count;
}
setAllVisibleChecked(checked) {
try {
this._fireCheckedEvents = false;
this.elements.forEach((element) => {
if (!element.hidden) {
element.checked = checked;
}
});
} finally {
this._fireCheckedEvents = true;
this.fireCheckedEvents();
}
}
setElements(inputElements) {
this.elementDisposables = dispose(this.elementDisposables);
const fireButtonTriggered = (event) => this.fireButtonTriggered(event);
const fireSeparatorButtonTriggered = (event) => this.fireSeparatorButtonTriggered(event);
this.inputElements = inputElements;
this.elements = inputElements.reduce((result, item, index) => {
const previous = index && inputElements[index - 1];
const saneLabel = item.label ? item.label.replace(/\r?\n/g, ' ') : '';
const saneSortLabel = parseLabelWithIcons(saneLabel).text.trim();
let saneMeta, saneDescription, saneDetail, labelHighlights, descriptionHighlights, detailHighlights;
if (item.type !== 'separator') {
saneMeta = item.meta && item.meta.replace(/\r?\n/g, ' ');
saneDescription = item.description && item.description.replace(/\r?\n/g, ' ');
saneDetail = item.detail && item.detail.replace(/\r?\n/g, ' ');
labelHighlights = item.highlights?.label;
descriptionHighlights = item.highlights?.description;
detailHighlights = item.highlights?.detail;
}
const saneAriaLabel =
item.ariaLabel ||
[saneLabel, saneDescription, saneDetail]
.map((s) => getCodiconAriaLabel(s))
.filter((s) => !!s)
.join(', ');
const hasCheckbox = this.parent.classList.contains('show-checkboxes');
let separator;
if (item.type === 'separator') {
if (!item.buttons) {
// This separator will be rendered as a part of the list item
return result;
}
separator = item;
} else if (previous && previous.type === 'separator' && !previous.buttons) {
separator = previous;
}
const element = new ListElement({
hasCheckbox,
index,
item: item.type !== 'separator' ? item : undefined,
saneLabel,
saneSortLabel,
saneMeta,
saneAriaLabel,
saneDescription,
saneDetail,
labelHighlights,
descriptionHighlights,
detailHighlights,
checked: false,
separator,
fireButtonTriggered,
fireSeparatorButtonTriggered,
});
this.elementDisposables.push(element);
this.elementDisposables.push(element.onChecked(() => this.fireCheckedEvents()));
result.push(element);
return result;
}, []);
this.elementsToIndexes = this.elements.reduce((map, element, index) => {
map.set(element.item ?? element.separator, index);
return map;
}, new Map());
this.list.splice(0, this.list.length); // Clear focus and selection first, sending the events when the list is empty.
this.list.splice(0, this.list.length, this.elements);
this._onChangedVisibleCount.fire(this.elements.length);
}
getElementsCount() {
return this.inputElements.length;
}
getFocusedElements() {
return this.list.getFocusedElements().map((e) => e.item);
}
setFocusedElements(items) {
this.list.setFocus(
items.filter((item) => this.elementsToIndexes.has(item)).map((item) => this.elementsToIndexes.get(item)),
);
if (items.length > 0) {
const focused = this.list.getFocus()[0];
if (typeof focused === 'number') {
this.list.reveal(focused);
}
}
}
getActiveDescendant() {
return this.list.getHTMLElement().getAttribute('aria-activedescendant');
}
getSelectedElements() {
return this.list.getSelectedElements().map((e) => e.item);
}
setSelectedElements(items) {
this.list.setSelection(
items.filter((item) => this.elementsToIndexes.has(item)).map((item) => this.elementsToIndexes.get(item)),
);
}
getCheckedElements() {
return this.elements
.filter((e) => e.checked)
.map((e) => e.item)
.filter((e) => !!e);
}
setCheckedElements(items) {
try {
this._fireCheckedEvents = false;
const checked = new Set();
for (const item of items) {
checked.add(item);
}
for (const element of this.elements) {
element.checked = checked.has(element.item);
}
} finally {
this._fireCheckedEvents = true;
this.fireCheckedEvents();
}
}
set enabled(value) {
this.list.getHTMLElement().style.pointerEvents = value ? '' : 'none';
}
focus(what) {
if (!this.list.length) {
return;
}
if (what === QuickInputListFocus.Second && this.list.length < 2) {
what = QuickInputListFocus.First;
}
switch (what) {
case QuickInputListFocus.First:
this.list.scrollTop = 0;
this.list.focusFirst(undefined, (e) => !!e.item);
break;
case QuickInputListFocus.Second:
this.list.scrollTop = 0;
this.list.focusNth(1, undefined, (e) => !!e.item);
break;
case QuickInputListFocus.Last:
this.list.scrollTop = this.list.scrollHeight;
this.list.focusLast(undefined, (e) => !!e.item);
break;
case QuickInputListFocus.Next: {
this.list.focusNext(undefined, true, undefined, (e) => !!e.item);
const index = this.list.getFocus()[0];
if (index !== 0 && !this.elements[index - 1].item && this.list.firstVisibleIndex > index - 1) {
this.list.reveal(index - 1);
}
break;
}
case QuickInputListFocus.Previous: {
this.list.focusPrevious(undefined, true, undefined, (e) => !!e.item);
const index = this.list.getFocus()[0];
if (index !== 0 && !this.elements[index - 1].item && this.list.firstVisibleIndex > index - 1) {
this.list.reveal(index - 1);
}
break;
}
case QuickInputListFocus.NextPage:
this.list.focusNextPage(undefined, (e) => !!e.item);
break;
case QuickInputListFocus.PreviousPage:
this.list.focusPreviousPage(undefined, (e) => !!e.item);
break;
}
const focused = this.list.getFocus()[0];
if (typeof focused === 'number') {
this.list.reveal(focused);
}
}
clearFocus() {
this.list.setFocus([]);
}
domFocus() {
this.list.domFocus();
}
layout(maxHeight) {
this.list.getHTMLElement().style.maxHeight = maxHeight ? `calc(${Math.floor(maxHeight / 44) * 44}px)` : '';
this.list.layout();
}
filter(query) {
if (!(this.sortByLabel || this.matchOnLabel || this.matchOnDescription || this.matchOnDetail)) {
this.list.layout();
return false;
}
const queryWithWhitespace = query;
query = query.trim();
// Reset filtering
if (!query || !(this.matchOnLabel || this.matchOnDescription || this.matchOnDetail)) {
this.elements.forEach((element) => {
element.labelHighlights = undefined;
element.descriptionHighlights = undefined;
element.detailHighlights = undefined;
element.hidden = false;
const previous = element.index && this.inputElements[element.index - 1];
if (element.item) {
element.separator = previous && previous.type === 'separator' && !previous.buttons ? previous : undefined;
}
});
}
// Filter by value (since we support icons in labels, use $(..) aware fuzzy matching)
else {
let currentSeparator;
this.elements.forEach((element) => {
let labelHighlights;
if (this.matchOnLabelMode === 'fuzzy') {
labelHighlights = this.matchOnLabel
? withNullAsUndefined(matchesFuzzyIconAware(query, parseLabelWithIcons(element.saneLabel)))
: undefined;
} else {
labelHighlights = this.matchOnLabel
? withNullAsUndefined(
matchesContiguousIconAware(queryWithWhitespace, parseLabelWithIcons(element.saneLabel)),
)
: undefined;
}
const descriptionHighlights = this.matchOnDescription
? withNullAsUndefined(matchesFuzzyIconAware(query, parseLabelWithIcons(element.saneDescription || '')))
: undefined;
const detailHighlights = this.matchOnDetail
? withNullAsUndefined(matchesFuzzyIconAware(query, parseLabelWithIcons(element.saneDetail || '')))
: undefined;
const metaHighlights = this.matchOnMeta
? withNullAsUndefined(matchesFuzzyIconAware(query, parseLabelWithIcons(element.saneMeta || '')))
: undefined;
if (labelHighlights || descriptionHighlights || detailHighlights || metaHighlights) {
element.labelHighlights = labelHighlights;
element.descriptionHighlights = descriptionHighlights;
element.detailHighlights = detailHighlights;
element.hidden = false;
} else {
element.labelHighlights = undefined;
element.descriptionHighlights = undefined;
element.detailHighlights = undefined;
element.hidden = element.item ? !element.item.alwaysShow : true;
}
// we can show the separator unless the list gets sorted by match
if (!this.sortByLabel) {
const previous = element.index && this.inputElements[element.index - 1];
currentSeparator = previous && previous.type === 'separator' ? previous : currentSeparator;
if (currentSeparator && !element.hidden) {
element.separator = currentSeparator;
currentSeparator = undefined;
}
}
});
}
const shownElements = this.elements.filter((element) => !element.hidden);
// Sort by value
if (this.sortByLabel && query) {
const normalizedSearchValue = query.toLowerCase();
shownElements.sort((a, b) => {
return compareEntries(a, b, normalizedSearchValue);
});
}
this.elementsToIndexes = shownElements.reduce((map, element, index) => {
map.set(element.item ?? element.separator, index);
return map;
}, new Map());
this.list.splice(0, this.list.length, shownElements);
this.list.setFocus([]);
this.list.layout();
this._onChangedAllVisibleChecked.fire(this.getAllVisibleChecked());
this._onChangedVisibleCount.fire(shownElements.length);
return true;
}
toggleCheckbox() {
try {
this._fireCheckedEvents = false;
const elements = this.list.getFocusedElements();
const allChecked = this.allVisibleChecked(elements);
for (const element of elements) {
element.checked = !allChecked;
}
} finally {
this._fireCheckedEvents = true;
this.fireCheckedEvents();
}
}
display(display) {
this.container.style.display = display ? '' : 'none';
}
isDisplayed() {
return this.container.style.display !== 'none';
}
dispose() {
this.elementDisposables = dispose(this.elementDisposables);
this.disposables = dispose(this.disposables);
}
fireCheckedEvents() {
if (this._fireCheckedEvents) {
this._onChangedAllVisibleChecked.fire(this.getAllVisibleChecked());
this._onChangedCheckedCount.fire(this.getCheckedCount());
this._onChangedCheckedElements.fire(this.getCheckedElements());
}
}
fireButtonTriggered(event) {
this._onButtonTriggered.fire(event);
}
fireSeparatorButtonTriggered(event) {
this._onSeparatorButtonTriggered.fire(event);
}
style(styles) {
this.list.style(styles);
}
}
__decorate([memoize], QuickInputList.prototype, 'onDidChangeFocus', null);
__decorate([memoize], QuickInputList.prototype, 'onDidChangeSelection', null);
function matchesContiguousIconAware(query, target) {
const { text, iconOffsets } = target;
// Return early if there are no icon markers in the word to match against
if (!iconOffsets || iconOffsets.length === 0) {
return matchesContiguous(query, text);
}
// Trim the word to match against because it could have leading
// whitespace now if the word started with an icon
const wordToMatchAgainstWithoutIconsTrimmed = ltrim(text, ' ');
const leadingWhitespaceOffset = text.length - wordToMatchAgainstWithoutIconsTrimmed.length;
// match on value without icon
const matches = matchesContiguous(query, wordToMatchAgainstWithoutIconsTrimmed);
// Map matches back to offsets with icon and trimming
if (matches) {
for (const match of matches) {
const iconOffset =
iconOffsets[match.start + leadingWhitespaceOffset] /* icon offsets at index */ +
leadingWhitespaceOffset; /* overall leading whitespace offset */
match.start += iconOffset;
match.end += iconOffset;
}
}
return matches;
}
function matchesContiguous(word, wordToMatchAgainst) {
const matchIndex = wordToMatchAgainst.toLowerCase().indexOf(word.toLowerCase());
if (matchIndex !== -1) {
return [{ start: matchIndex, end: matchIndex + word.length }];
}
return null;
}
function compareEntries(elementA, elementB, lookFor) {
const labelHighlightsA = elementA.labelHighlights || [];
const labelHighlightsB = elementB.labelHighlights || [];
if (labelHighlightsA.length && !labelHighlightsB.length) {
return -1;
}
if (!labelHighlightsA.length && labelHighlightsB.length) {
return 1;
}
if (labelHighlightsA.length === 0 && labelHighlightsB.length === 0) {
return 0;
}
return compareAnything(elementA.saneSortLabel, elementB.saneSortLabel, lookFor);
}
class QuickInputAccessibilityProvider {
getWidgetAriaLabel() {
return localize('quickInput', 'Quick Input');
}
getAriaLabel(element) {
return element.separator?.label ? `${element.saneAriaLabel}, ${element.separator.label}` : element.saneAriaLabel;
}
getWidgetRole() {
return 'listbox';
}
getRole(element) {
return element.hasCheckbox ? 'checkbox' : 'option';
}
isChecked(element) {
if (!element.hasCheckbox) {
return undefined;
}
return {
value: element.checked,
onDidChange: element.onChecked,
};
}
}