feat: 添加vscode编辑器
This commit is contained in:
@@ -0,0 +1,292 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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%;
|
||||
margin-left: -300px;
|
||||
-webkit-app-region: no-drag;
|
||||
}
|
||||
|
||||
.quick-input-titlebar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.quick-input-left-action-bar {
|
||||
display: flex;
|
||||
margin-left: 4px;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.quick-input-title {
|
||||
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-titlebar .monaco-action-bar .action-label.codicon {
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
.quick-input-description {
|
||||
margin: 6px;
|
||||
}
|
||||
|
||||
.quick-input-header .quick-input-description {
|
||||
margin: 4px 2px;
|
||||
}
|
||||
|
||||
.quick-input-header {
|
||||
display: flex;
|
||||
padding: 6px 6px 0px 6px;
|
||||
margin-bottom: -2px;
|
||||
}
|
||||
|
||||
.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-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: 27.5px;
|
||||
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;
|
||||
}
|
||||
|
||||
.quick-input-progress.monaco-progress-container {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.quick-input-progress.monaco-progress-container,
|
||||
.quick-input-progress.monaco-progress-container .progress-bit {
|
||||
height: 2px;
|
||||
}
|
||||
|
||||
.quick-input-list {
|
||||
line-height: 22px;
|
||||
margin-top: 6px;
|
||||
padding: 0px 1px 1px 1px;
|
||||
}
|
||||
|
||||
.quick-input-widget.hidden-input .quick-input-list {
|
||||
margin-top: 0; /* reduce margins when input box hidden */
|
||||
}
|
||||
|
||||
.quick-input-list .monaco-list {
|
||||
overflow: hidden;
|
||||
max-height: calc(20 * 22px);
|
||||
}
|
||||
|
||||
.quick-input-list .quick-input-list-entry {
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
height: 100%;
|
||||
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[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-list .quick-input-list-checkbox {
|
||||
align-self: center;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.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-widget.show-checkboxes .quick-input-list .quick-input-list-rows {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.quick-input-widget .quick-input-list .quick-input-list-checkbox {
|
||||
display: none;
|
||||
}
|
||||
.quick-input-widget.show-checkboxes .quick-input-list .quick-input-list-checkbox {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
.quick-input-list .monaco-highlighted-label .highlight {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.quick-input-list .quick-input-list-entry .quick-input-list-separator {
|
||||
margin-right: 8px; /* 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: 0px 2px 2px 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 .monaco-list-row.focused .quick-input-list-entry-action-bar .action-label {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,89 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 '../../../browser/dom.js';
|
||||
import { StandardKeyboardEvent } from '../../../browser/keyboardEvent.js';
|
||||
import { StandardMouseEvent } from '../../../browser/mouseEvent.js';
|
||||
import { InputBox } from '../../../browser/ui/inputbox/inputBox.js';
|
||||
import { Disposable } from '../../../common/lifecycle.js';
|
||||
import Severity from '../../../common/severity.js';
|
||||
import './media/quickInput.css';
|
||||
const $ = dom.$;
|
||||
export class QuickInputBox extends Disposable {
|
||||
constructor(parent) {
|
||||
super();
|
||||
this.parent = parent;
|
||||
this.onKeyDown = (handler) => {
|
||||
return dom.addDisposableListener(this.inputBox.inputElement, dom.EventType.KEY_DOWN, (e) => {
|
||||
handler(new StandardKeyboardEvent(e));
|
||||
});
|
||||
};
|
||||
this.onMouseDown = (handler) => {
|
||||
return dom.addDisposableListener(this.inputBox.inputElement, dom.EventType.MOUSE_DOWN, (e) => {
|
||||
handler(new StandardMouseEvent(e));
|
||||
});
|
||||
};
|
||||
this.onDidChange = (handler) => {
|
||||
return this.inputBox.onDidChange(handler);
|
||||
};
|
||||
this.container = dom.append(this.parent, $('.quick-input-box'));
|
||||
this.inputBox = this._register(new InputBox(this.container, undefined));
|
||||
}
|
||||
get value() {
|
||||
return this.inputBox.value;
|
||||
}
|
||||
set value(value) {
|
||||
this.inputBox.value = value;
|
||||
}
|
||||
select(range = null) {
|
||||
this.inputBox.select(range);
|
||||
}
|
||||
isSelectionAtEnd() {
|
||||
return this.inputBox.isSelectionAtEnd();
|
||||
}
|
||||
get placeholder() {
|
||||
return this.inputBox.inputElement.getAttribute('placeholder') || '';
|
||||
}
|
||||
set placeholder(placeholder) {
|
||||
this.inputBox.setPlaceHolder(placeholder);
|
||||
}
|
||||
get ariaLabel() {
|
||||
return this.inputBox.getAriaLabel();
|
||||
}
|
||||
set ariaLabel(ariaLabel) {
|
||||
this.inputBox.setAriaLabel(ariaLabel);
|
||||
}
|
||||
get password() {
|
||||
return this.inputBox.inputElement.type === 'password';
|
||||
}
|
||||
set password(password) {
|
||||
this.inputBox.inputElement.type = password ? 'password' : 'text';
|
||||
}
|
||||
setAttribute(name, value) {
|
||||
this.inputBox.inputElement.setAttribute(name, value);
|
||||
}
|
||||
removeAttribute(name) {
|
||||
this.inputBox.inputElement.removeAttribute(name);
|
||||
}
|
||||
showDecoration(decoration) {
|
||||
if (decoration === Severity.Ignore) {
|
||||
this.inputBox.hideMessage();
|
||||
}
|
||||
else {
|
||||
this.inputBox.showMessage({ type: decoration === Severity.Info ? 1 /* MessageType.INFO */ : decoration === Severity.Warning ? 2 /* MessageType.WARNING */ : 3 /* MessageType.ERROR */, content: '' });
|
||||
}
|
||||
}
|
||||
stylesForType(decoration) {
|
||||
return this.inputBox.stylesForType(decoration === Severity.Info ? 1 /* MessageType.INFO */ : decoration === Severity.Warning ? 2 /* MessageType.WARNING */ : 3 /* MessageType.ERROR */);
|
||||
}
|
||||
setFocus() {
|
||||
this.inputBox.focus();
|
||||
}
|
||||
layout() {
|
||||
this.inputBox.layout();
|
||||
}
|
||||
style(styles) {
|
||||
this.inputBox.style(styles);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,677 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
import * as dom from '../../../browser/dom.js';
|
||||
import { StandardKeyboardEvent } from '../../../browser/keyboardEvent.js';
|
||||
import { ActionBar } from '../../../browser/ui/actionbar/actionbar.js';
|
||||
import { IconLabel } from '../../../browser/ui/iconLabel/iconLabel.js';
|
||||
import { KeybindingLabel } from '../../../browser/ui/keybindingLabel/keybindingLabel.js';
|
||||
import { Action } from '../../../common/actions.js';
|
||||
import { range } from '../../../common/arrays.js';
|
||||
import { getCodiconAriaLabel } from '../../../common/codicons.js';
|
||||
import { compareAnything } from '../../../common/comparers.js';
|
||||
import { memoize } from '../../../common/decorators.js';
|
||||
import { Emitter, Event } from '../../../common/event.js';
|
||||
import { matchesFuzzyIconAware, parseLabelWithIcons } from '../../../common/iconLabels.js';
|
||||
import { dispose } from '../../../common/lifecycle.js';
|
||||
import * as platform from '../../../common/platform.js';
|
||||
import { ltrim } from '../../../common/strings.js';
|
||||
import { withNullAsUndefined } from '../../../common/types.js';
|
||||
import { getIconClass } from './quickInputUtils.js';
|
||||
import './media/quickInput.css';
|
||||
import { localize } from '../../../../nls.js';
|
||||
const $ = dom.$;
|
||||
class ListElement {
|
||||
constructor(init) {
|
||||
this.hidden = false;
|
||||
this._onChecked = new Emitter();
|
||||
this.onChecked = this._onChecked.event;
|
||||
Object.assign(this, init);
|
||||
}
|
||||
get checked() {
|
||||
return !!this._checked;
|
||||
}
|
||||
set checked(value) {
|
||||
if (value !== this._checked) {
|
||||
this._checked = value;
|
||||
this._onChecked.fire(value);
|
||||
}
|
||||
}
|
||||
dispose() {
|
||||
this._onChecked.dispose();
|
||||
}
|
||||
}
|
||||
class ListElementRenderer {
|
||||
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.toDisposeElement = dispose(data.toDisposeElement);
|
||||
data.element = element;
|
||||
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 || [];
|
||||
options.extraClasses = element.item.iconClasses;
|
||||
options.italic = element.item.italic;
|
||||
options.strikethrough = element.item.strikethrough;
|
||||
data.label.setLabel(element.saneLabel, element.saneDescription, options);
|
||||
// Keybinding
|
||||
data.keybinding.set(element.item.keybinding);
|
||||
// Meta
|
||||
if (element.saneDetail) {
|
||||
data.detail.setLabel(element.saneDetail, undefined, {
|
||||
matches: detailHighlights,
|
||||
title: element.saneDetail
|
||||
});
|
||||
}
|
||||
// Separator
|
||||
if (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
|
||||
data.actionBar.clear();
|
||||
const buttons = element.item.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';
|
||||
}
|
||||
const action = new Action(`id-${index}`, '', cssClasses, true, () => __awaiter(this, void 0, void 0, function* () {
|
||||
element.fireButtonTriggered({
|
||||
button,
|
||||
item: element.item
|
||||
});
|
||||
}));
|
||||
action.tooltip = button.tooltip || '';
|
||||
return action;
|
||||
}), { 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);
|
||||
}
|
||||
disposeTemplate(data) {
|
||||
data.toDisposeElement = dispose(data.toDisposeElement);
|
||||
data.toDisposeTemplate = dispose(data.toDisposeTemplate);
|
||||
}
|
||||
}
|
||||
ListElementRenderer.ID = 'listelement';
|
||||
class ListElementDelegate {
|
||||
getHeight(element) {
|
||||
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 {
|
||||
constructor(parent, id, options) {
|
||||
this.parent = parent;
|
||||
this.inputElements = [];
|
||||
this.elements = [];
|
||||
this.elementsToIndexes = new Map();
|
||||
this.matchOnDescription = false;
|
||||
this.matchOnDetail = false;
|
||||
this.matchOnLabel = true;
|
||||
this.matchOnLabelMode = 'fuzzy';
|
||||
this.matchOnMeta = true;
|
||||
this.sortByLabel = true;
|
||||
this._onChangedAllVisibleChecked = new Emitter();
|
||||
this.onChangedAllVisibleChecked = this._onChangedAllVisibleChecked.event;
|
||||
this._onChangedCheckedCount = new Emitter();
|
||||
this.onChangedCheckedCount = this._onChangedCheckedCount.event;
|
||||
this._onChangedVisibleCount = new Emitter();
|
||||
this.onChangedVisibleCount = this._onChangedVisibleCount.event;
|
||||
this._onChangedCheckedElements = new Emitter();
|
||||
this.onChangedCheckedElements = this._onChangedCheckedElements.event;
|
||||
this._onButtonTriggered = new Emitter();
|
||||
this.onButtonTriggered = this._onButtonTriggered.event;
|
||||
this._onKeyDown = new Emitter();
|
||||
this.onKeyDown = this._onKeyDown.event;
|
||||
this._onLeave = new Emitter();
|
||||
this.onLeave = this._onLeave.event;
|
||||
this._fireCheckedEvents = true;
|
||||
this.elementDisposables = [];
|
||||
this.disposables = [];
|
||||
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._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);
|
||||
this.inputElements = inputElements;
|
||||
this.elements = inputElements.reduce((result, item, index) => {
|
||||
var _a, _b, _c;
|
||||
if (item.type !== 'separator') {
|
||||
const previous = index && inputElements[index - 1];
|
||||
const saneLabel = item.label && item.label.replace(/\r?\n/g, ' ');
|
||||
const saneSortLabel = parseLabelWithIcons(saneLabel).text.trim();
|
||||
const saneMeta = item.meta && item.meta.replace(/\r?\n/g, ' ');
|
||||
const saneDescription = item.description && item.description.replace(/\r?\n/g, ' ');
|
||||
const saneDetail = item.detail && item.detail.replace(/\r?\n/g, ' ');
|
||||
const saneAriaLabel = item.ariaLabel || [saneLabel, saneDescription, saneDetail]
|
||||
.map(s => getCodiconAriaLabel(s))
|
||||
.filter(s => !!s)
|
||||
.join(', ');
|
||||
const hasCheckbox = this.parent.classList.contains('show-checkboxes');
|
||||
result.push(new ListElement({
|
||||
hasCheckbox,
|
||||
index,
|
||||
item,
|
||||
saneLabel,
|
||||
saneSortLabel,
|
||||
saneMeta,
|
||||
saneAriaLabel,
|
||||
saneDescription,
|
||||
saneDetail,
|
||||
labelHighlights: (_a = item.highlights) === null || _a === void 0 ? void 0 : _a.label,
|
||||
descriptionHighlights: (_b = item.highlights) === null || _b === void 0 ? void 0 : _b.description,
|
||||
detailHighlights: (_c = item.highlights) === null || _c === void 0 ? void 0 : _c.detail,
|
||||
checked: false,
|
||||
separator: previous && previous.type === 'separator' ? previous : undefined,
|
||||
fireButtonTriggered
|
||||
}));
|
||||
}
|
||||
return result;
|
||||
}, []);
|
||||
this.elementDisposables.push(...this.elements);
|
||||
this.elementDisposables.push(...this.elements.map(element => element.onChecked(() => this.fireCheckedEvents())));
|
||||
this.elementsToIndexes = this.elements.reduce((map, element, index) => {
|
||||
map.set(element.item, 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);
|
||||
}
|
||||
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');
|
||||
}
|
||||
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);
|
||||
}
|
||||
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.Next && this.list.getFocus()[0] === this.list.length - 1) {
|
||||
what = QuickInputListFocus.First;
|
||||
}
|
||||
if (what === QuickInputListFocus.Previous && this.list.getFocus()[0] === 0) {
|
||||
what = QuickInputListFocus.Last;
|
||||
}
|
||||
if (what === QuickInputListFocus.Second && this.list.length < 2) {
|
||||
what = QuickInputListFocus.First;
|
||||
}
|
||||
switch (what) {
|
||||
case QuickInputListFocus.First:
|
||||
this.list.focusFirst();
|
||||
break;
|
||||
case QuickInputListFocus.Second:
|
||||
this.list.focusNth(1);
|
||||
break;
|
||||
case QuickInputListFocus.Last:
|
||||
this.list.focusLast();
|
||||
break;
|
||||
case QuickInputListFocus.Next:
|
||||
this.list.focusNext();
|
||||
break;
|
||||
case QuickInputListFocus.Previous:
|
||||
this.list.focusPrevious();
|
||||
break;
|
||||
case QuickInputListFocus.NextPage:
|
||||
this.list.focusNextPage();
|
||||
break;
|
||||
case QuickInputListFocus.PreviousPage:
|
||||
this.list.focusPreviousPage();
|
||||
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];
|
||||
element.separator = previous && previous.type === 'separator' ? 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.alwaysShow;
|
||||
}
|
||||
element.separator = undefined;
|
||||
// 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, 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);
|
||||
}
|
||||
style(styles) {
|
||||
this.list.style(styles);
|
||||
}
|
||||
}
|
||||
__decorate([
|
||||
memoize
|
||||
], QuickInputList.prototype, "onDidChangeFocus", null);
|
||||
__decorate([
|
||||
memoize
|
||||
], QuickInputList.prototype, "onDidChangeSelection", null);
|
||||
export 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) {
|
||||
var _a;
|
||||
return ((_a = element.separator) === null || _a === void 0 ? void 0 : _a.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
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 '../../../browser/dom.js';
|
||||
import { IdGenerator } from '../../../common/idGenerator.js';
|
||||
import './media/quickInput.css';
|
||||
const iconPathToClass = {};
|
||||
const iconClassGenerator = new IdGenerator('quick-input-button-icon-');
|
||||
export function getIconClass(iconPath) {
|
||||
if (!iconPath) {
|
||||
return undefined;
|
||||
}
|
||||
let iconClass;
|
||||
const key = iconPath.dark.toString();
|
||||
if (iconPathToClass[key]) {
|
||||
iconClass = iconPathToClass[key];
|
||||
}
|
||||
else {
|
||||
iconClass = iconClassGenerator.nextId();
|
||||
dom.createCSSRule(`.${iconClass}, .hc-light .${iconClass}`, `background-image: ${dom.asCSSUrl(iconPath.light || iconPath.dark)}`);
|
||||
dom.createCSSRule(`.vs-dark .${iconClass}, .hc-black .${iconClass}`, `background-image: ${dom.asCSSUrl(iconPath.dark)}`);
|
||||
iconPathToClass[key] = iconClass;
|
||||
}
|
||||
return iconClass;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
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 = {}));
|
||||
export var ItemActivation;
|
||||
(function (ItemActivation) {
|
||||
ItemActivation[ItemActivation["NONE"] = 0] = "NONE";
|
||||
ItemActivation[ItemActivation["FIRST"] = 1] = "FIRST";
|
||||
ItemActivation[ItemActivation["SECOND"] = 2] = "SECOND";
|
||||
ItemActivation[ItemActivation["LAST"] = 3] = "LAST";
|
||||
})(ItemActivation || (ItemActivation = {}));
|
||||
export class QuickPickItemScorerAccessor {
|
||||
constructor(options) {
|
||||
this.options = options;
|
||||
}
|
||||
}
|
||||
export const quickPickItemScorerAccessor = new QuickPickItemScorerAccessor();
|
||||
//#endregion
|
||||
@@ -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 __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
import { ThrottledDelayer } from '../../../common/async.js';
|
||||
import { Emitter, Event } from '../../../common/event.js';
|
||||
import { Disposable } from '../../../common/lifecycle.js';
|
||||
import { isUndefinedOrNull } from '../../../common/types.js';
|
||||
export var StorageState;
|
||||
(function (StorageState) {
|
||||
StorageState[StorageState["None"] = 0] = "None";
|
||||
StorageState[StorageState["Initialized"] = 1] = "Initialized";
|
||||
StorageState[StorageState["Closed"] = 2] = "Closed";
|
||||
})(StorageState || (StorageState = {}));
|
||||
export class Storage extends Disposable {
|
||||
constructor(database, options = Object.create(null)) {
|
||||
super();
|
||||
this.database = database;
|
||||
this.options = options;
|
||||
this._onDidChangeStorage = this._register(new Emitter());
|
||||
this.onDidChangeStorage = this._onDidChangeStorage.event;
|
||||
this.state = StorageState.None;
|
||||
this.cache = new Map();
|
||||
this.flushDelayer = new ThrottledDelayer(Storage.DEFAULT_FLUSH_DELAY);
|
||||
this.pendingDeletes = new Set();
|
||||
this.pendingInserts = new Map();
|
||||
this.whenFlushedCallbacks = [];
|
||||
this.registerListeners();
|
||||
}
|
||||
registerListeners() {
|
||||
this._register(this.database.onDidChangeItemsExternal(e => this.onDidChangeItemsExternal(e)));
|
||||
}
|
||||
onDidChangeItemsExternal(e) {
|
||||
var _a, _b;
|
||||
// items that change external require us to update our
|
||||
// caches with the values. we just accept the value and
|
||||
// emit an event if there is a change.
|
||||
(_a = e.changed) === null || _a === void 0 ? void 0 : _a.forEach((value, key) => this.accept(key, value));
|
||||
(_b = e.deleted) === null || _b === void 0 ? void 0 : _b.forEach(key => this.accept(key, undefined));
|
||||
}
|
||||
accept(key, value) {
|
||||
if (this.state === StorageState.Closed) {
|
||||
return; // Return early if we are already closed
|
||||
}
|
||||
let changed = false;
|
||||
// Item got removed, check for deletion
|
||||
if (isUndefinedOrNull(value)) {
|
||||
changed = this.cache.delete(key);
|
||||
}
|
||||
// Item got updated, check for change
|
||||
else {
|
||||
const currentValue = this.cache.get(key);
|
||||
if (currentValue !== value) {
|
||||
this.cache.set(key, value);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
// Signal to outside listeners
|
||||
if (changed) {
|
||||
this._onDidChangeStorage.fire(key);
|
||||
}
|
||||
}
|
||||
get(key, fallbackValue) {
|
||||
const value = this.cache.get(key);
|
||||
if (isUndefinedOrNull(value)) {
|
||||
return fallbackValue;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
getBoolean(key, fallbackValue) {
|
||||
const value = this.get(key);
|
||||
if (isUndefinedOrNull(value)) {
|
||||
return fallbackValue;
|
||||
}
|
||||
return value === 'true';
|
||||
}
|
||||
getNumber(key, fallbackValue) {
|
||||
const value = this.get(key);
|
||||
if (isUndefinedOrNull(value)) {
|
||||
return fallbackValue;
|
||||
}
|
||||
return parseInt(value, 10);
|
||||
}
|
||||
set(key, value) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
if (this.state === StorageState.Closed) {
|
||||
return; // Return early if we are already closed
|
||||
}
|
||||
// We remove the key for undefined/null values
|
||||
if (isUndefinedOrNull(value)) {
|
||||
return this.delete(key);
|
||||
}
|
||||
// Otherwise, convert to String and store
|
||||
const valueStr = String(value);
|
||||
// Return early if value already set
|
||||
const currentValue = this.cache.get(key);
|
||||
if (currentValue === valueStr) {
|
||||
return;
|
||||
}
|
||||
// Update in cache and pending
|
||||
this.cache.set(key, valueStr);
|
||||
this.pendingInserts.set(key, valueStr);
|
||||
this.pendingDeletes.delete(key);
|
||||
// Event
|
||||
this._onDidChangeStorage.fire(key);
|
||||
// Accumulate work by scheduling after timeout
|
||||
return this.doFlush();
|
||||
});
|
||||
}
|
||||
delete(key) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
if (this.state === StorageState.Closed) {
|
||||
return; // Return early if we are already closed
|
||||
}
|
||||
// Remove from cache and add to pending
|
||||
const wasDeleted = this.cache.delete(key);
|
||||
if (!wasDeleted) {
|
||||
return; // Return early if value already deleted
|
||||
}
|
||||
if (!this.pendingDeletes.has(key)) {
|
||||
this.pendingDeletes.add(key);
|
||||
}
|
||||
this.pendingInserts.delete(key);
|
||||
// Event
|
||||
this._onDidChangeStorage.fire(key);
|
||||
// Accumulate work by scheduling after timeout
|
||||
return this.doFlush();
|
||||
});
|
||||
}
|
||||
get hasPending() {
|
||||
return this.pendingInserts.size > 0 || this.pendingDeletes.size > 0;
|
||||
}
|
||||
flushPending() {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
if (!this.hasPending) {
|
||||
return; // return early if nothing to do
|
||||
}
|
||||
// Get pending data
|
||||
const updateRequest = { insert: this.pendingInserts, delete: this.pendingDeletes };
|
||||
// Reset pending data for next run
|
||||
this.pendingDeletes = new Set();
|
||||
this.pendingInserts = new Map();
|
||||
// Update in storage and release any
|
||||
// waiters we have once done
|
||||
return this.database.updateItems(updateRequest).finally(() => {
|
||||
var _a;
|
||||
if (!this.hasPending) {
|
||||
while (this.whenFlushedCallbacks.length) {
|
||||
(_a = this.whenFlushedCallbacks.pop()) === null || _a === void 0 ? void 0 : _a();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
doFlush(delay) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
return this.flushDelayer.trigger(() => this.flushPending(), delay);
|
||||
});
|
||||
}
|
||||
dispose() {
|
||||
this.flushDelayer.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
Storage.DEFAULT_FLUSH_DELAY = 100;
|
||||
export class InMemoryStorageDatabase {
|
||||
constructor() {
|
||||
this.onDidChangeItemsExternal = Event.None;
|
||||
this.items = new Map();
|
||||
}
|
||||
updateItems(request) {
|
||||
var _a, _b;
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
(_a = request.insert) === null || _a === void 0 ? void 0 : _a.forEach((value, key) => this.items.set(key, value));
|
||||
(_b = request.delete) === null || _b === void 0 ? void 0 : _b.forEach(key => this.items.delete(key));
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user