Version 1.0
This commit is contained in:
+180
@@ -0,0 +1,180 @@
|
||||
import { Emitter } from './event.js';
|
||||
import { Disposable } from './lifecycle.js';
|
||||
import { localize } from '../../nls.js';
|
||||
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
/**
|
||||
* A concrete implementation of {@link IAction}.
|
||||
*
|
||||
* Note that in most cases you should use the lighter-weight {@linkcode toAction} function instead.
|
||||
*/
|
||||
class Action extends Disposable {
|
||||
get onDidChange() { return this._onDidChange.event; }
|
||||
constructor(id, label = '', cssClass = '', enabled = true, actionCallback) {
|
||||
super();
|
||||
this._onDidChange = this._register(new Emitter());
|
||||
this._enabled = true;
|
||||
this._id = id;
|
||||
this._label = label;
|
||||
this._cssClass = cssClass;
|
||||
this._enabled = enabled;
|
||||
this._actionCallback = actionCallback;
|
||||
}
|
||||
get id() {
|
||||
return this._id;
|
||||
}
|
||||
get label() {
|
||||
return this._label;
|
||||
}
|
||||
set label(value) {
|
||||
this._setLabel(value);
|
||||
}
|
||||
_setLabel(value) {
|
||||
if (this._label !== value) {
|
||||
this._label = value;
|
||||
this._onDidChange.fire({ label: value });
|
||||
}
|
||||
}
|
||||
get tooltip() {
|
||||
return this._tooltip || '';
|
||||
}
|
||||
set tooltip(value) {
|
||||
this._setTooltip(value);
|
||||
}
|
||||
_setTooltip(value) {
|
||||
if (this._tooltip !== value) {
|
||||
this._tooltip = value;
|
||||
this._onDidChange.fire({ tooltip: value });
|
||||
}
|
||||
}
|
||||
get class() {
|
||||
return this._cssClass;
|
||||
}
|
||||
set class(value) {
|
||||
this._setClass(value);
|
||||
}
|
||||
_setClass(value) {
|
||||
if (this._cssClass !== value) {
|
||||
this._cssClass = value;
|
||||
this._onDidChange.fire({ class: value });
|
||||
}
|
||||
}
|
||||
get enabled() {
|
||||
return this._enabled;
|
||||
}
|
||||
set enabled(value) {
|
||||
this._setEnabled(value);
|
||||
}
|
||||
_setEnabled(value) {
|
||||
if (this._enabled !== value) {
|
||||
this._enabled = value;
|
||||
this._onDidChange.fire({ enabled: value });
|
||||
}
|
||||
}
|
||||
get checked() {
|
||||
return this._checked;
|
||||
}
|
||||
set checked(value) {
|
||||
this._setChecked(value);
|
||||
}
|
||||
_setChecked(value) {
|
||||
if (this._checked !== value) {
|
||||
this._checked = value;
|
||||
this._onDidChange.fire({ checked: value });
|
||||
}
|
||||
}
|
||||
async run(event, data) {
|
||||
if (this._actionCallback) {
|
||||
await this._actionCallback(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
class ActionRunner extends Disposable {
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this._onWillRun = this._register(new Emitter());
|
||||
this._onDidRun = this._register(new Emitter());
|
||||
}
|
||||
get onWillRun() { return this._onWillRun.event; }
|
||||
get onDidRun() { return this._onDidRun.event; }
|
||||
async run(action, context) {
|
||||
if (!action.enabled) {
|
||||
return;
|
||||
}
|
||||
this._onWillRun.fire({ action });
|
||||
let error = undefined;
|
||||
try {
|
||||
await this.runAction(action, context);
|
||||
}
|
||||
catch (e) {
|
||||
error = e;
|
||||
}
|
||||
this._onDidRun.fire({ action, error });
|
||||
}
|
||||
async runAction(action, context) {
|
||||
await action.run(context);
|
||||
}
|
||||
}
|
||||
class Separator {
|
||||
constructor() {
|
||||
this.id = Separator.ID;
|
||||
this.label = '';
|
||||
this.tooltip = '';
|
||||
this.class = 'separator';
|
||||
this.enabled = false;
|
||||
this.checked = false;
|
||||
}
|
||||
/**
|
||||
* Joins all non-empty lists of actions with separators.
|
||||
*/
|
||||
static join(...actionLists) {
|
||||
let out = [];
|
||||
for (const list of actionLists) {
|
||||
if (!list.length) ;
|
||||
else if (out.length) {
|
||||
out = [...out, new Separator(), ...list];
|
||||
}
|
||||
else {
|
||||
out = list;
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
static { this.ID = 'vs.actions.separator'; }
|
||||
async run() { }
|
||||
}
|
||||
class SubmenuAction {
|
||||
get actions() { return this._actions; }
|
||||
constructor(id, label, actions, cssClass) {
|
||||
this.tooltip = '';
|
||||
this.enabled = true;
|
||||
this.checked = undefined;
|
||||
this.id = id;
|
||||
this.label = label;
|
||||
this.class = cssClass;
|
||||
this._actions = actions;
|
||||
}
|
||||
async run() { }
|
||||
}
|
||||
class EmptySubmenuAction extends Action {
|
||||
static { this.ID = 'vs.actions.empty'; }
|
||||
constructor() {
|
||||
super(EmptySubmenuAction.ID, localize(28, '(empty)'), undefined, false);
|
||||
}
|
||||
}
|
||||
function toAction(props) {
|
||||
return {
|
||||
id: props.id,
|
||||
label: props.label,
|
||||
tooltip: props.tooltip ?? props.label,
|
||||
class: props.class,
|
||||
enabled: props.enabled ?? true,
|
||||
checked: props.checked,
|
||||
run: async (...args) => props.run(...args),
|
||||
};
|
||||
}
|
||||
|
||||
export { Action, ActionRunner, EmptySubmenuAction, Separator, SubmenuAction, toAction };
|
||||
+529
@@ -0,0 +1,529 @@
|
||||
/**
|
||||
* Returns the last entry and the initial N-1 entries of the array, as a tuple of [rest, last].
|
||||
*
|
||||
* The array must have at least one element.
|
||||
*
|
||||
* @param arr The input array
|
||||
* @returns A tuple of [rest, last] where rest is all but the last element and last is the last element
|
||||
* @throws Error if the array is empty
|
||||
*/
|
||||
function tail(arr) {
|
||||
if (arr.length === 0) {
|
||||
throw new Error('Invalid tail call');
|
||||
}
|
||||
return [arr.slice(0, arr.length - 1), arr[arr.length - 1]];
|
||||
}
|
||||
function equals(one, other, itemEquals = (a, b) => a === b) {
|
||||
if (one === other) {
|
||||
return true;
|
||||
}
|
||||
if (!one || !other) {
|
||||
return false;
|
||||
}
|
||||
if (one.length !== other.length) {
|
||||
return false;
|
||||
}
|
||||
for (let i = 0, len = one.length; i < len; i++) {
|
||||
if (!itemEquals(one[i], other[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Remove the element at `index` by replacing it with the last element. This is faster than `splice`
|
||||
* but changes the order of the array
|
||||
*/
|
||||
function removeFastWithoutKeepingOrder(array, index) {
|
||||
const last = array.length - 1;
|
||||
if (index < last) {
|
||||
array[index] = array[last];
|
||||
}
|
||||
array.pop();
|
||||
}
|
||||
/**
|
||||
* Performs a binary search algorithm over a sorted array.
|
||||
*
|
||||
* @param array The array being searched.
|
||||
* @param key The value we search for.
|
||||
* @param comparator A function that takes two array elements and returns zero
|
||||
* if they are equal, a negative number if the first element precedes the
|
||||
* second one in the sorting order, or a positive number if the second element
|
||||
* precedes the first one.
|
||||
* @return See {@link binarySearch2}
|
||||
*/
|
||||
function binarySearch(array, key, comparator) {
|
||||
return binarySearch2(array.length, i => comparator(array[i], key));
|
||||
}
|
||||
/**
|
||||
* Performs a binary search algorithm over a sorted collection. Useful for cases
|
||||
* when we need to perform a binary search over something that isn't actually an
|
||||
* array, and converting data to an array would defeat the use of binary search
|
||||
* in the first place.
|
||||
*
|
||||
* @param length The collection length.
|
||||
* @param compareToKey A function that takes an index of an element in the
|
||||
* collection and returns zero if the value at this index is equal to the
|
||||
* search key, a negative number if the value precedes the search key in the
|
||||
* sorting order, or a positive number if the search key precedes the value.
|
||||
* @return A non-negative index of an element, if found. If not found, the
|
||||
* result is -(n+1) (or ~n, using bitwise notation), where n is the index
|
||||
* where the key should be inserted to maintain the sorting order.
|
||||
*/
|
||||
function binarySearch2(length, compareToKey) {
|
||||
let low = 0, high = length - 1;
|
||||
while (low <= high) {
|
||||
const mid = ((low + high) / 2) | 0;
|
||||
const comp = compareToKey(mid);
|
||||
if (comp < 0) {
|
||||
low = mid + 1;
|
||||
}
|
||||
else if (comp > 0) {
|
||||
high = mid - 1;
|
||||
}
|
||||
else {
|
||||
return mid;
|
||||
}
|
||||
}
|
||||
return -(low + 1);
|
||||
}
|
||||
function quickSelect(nth, data, compare) {
|
||||
nth = nth | 0;
|
||||
if (nth >= data.length) {
|
||||
throw new TypeError('invalid index');
|
||||
}
|
||||
const pivotValue = data[Math.floor(data.length * Math.random())];
|
||||
const lower = [];
|
||||
const higher = [];
|
||||
const pivots = [];
|
||||
for (const value of data) {
|
||||
const val = compare(value, pivotValue);
|
||||
if (val < 0) {
|
||||
lower.push(value);
|
||||
}
|
||||
else if (val > 0) {
|
||||
higher.push(value);
|
||||
}
|
||||
else {
|
||||
pivots.push(value);
|
||||
}
|
||||
}
|
||||
if (nth < lower.length) {
|
||||
return quickSelect(nth, lower, compare);
|
||||
}
|
||||
else if (nth < lower.length + pivots.length) {
|
||||
return pivots[0];
|
||||
}
|
||||
else {
|
||||
return quickSelect(nth - (lower.length + pivots.length), higher, compare);
|
||||
}
|
||||
}
|
||||
function groupBy(data, compare) {
|
||||
const result = [];
|
||||
let currentGroup = undefined;
|
||||
for (const element of data.slice(0).sort(compare)) {
|
||||
if (!currentGroup || compare(currentGroup[0], element) !== 0) {
|
||||
currentGroup = [element];
|
||||
result.push(currentGroup);
|
||||
}
|
||||
else {
|
||||
currentGroup.push(element);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
/**
|
||||
* Splits the given items into a list of (non-empty) groups.
|
||||
* `shouldBeGrouped` is used to decide if two consecutive items should be in the same group.
|
||||
* The order of the items is preserved.
|
||||
*/
|
||||
function* groupAdjacentBy(items, shouldBeGrouped) {
|
||||
let currentGroup;
|
||||
let last;
|
||||
for (const item of items) {
|
||||
if (last !== undefined && shouldBeGrouped(last, item)) {
|
||||
currentGroup.push(item);
|
||||
}
|
||||
else {
|
||||
if (currentGroup) {
|
||||
yield currentGroup;
|
||||
}
|
||||
currentGroup = [item];
|
||||
}
|
||||
last = item;
|
||||
}
|
||||
if (currentGroup) {
|
||||
yield currentGroup;
|
||||
}
|
||||
}
|
||||
function forEachAdjacent(arr, f) {
|
||||
for (let i = 0; i <= arr.length; i++) {
|
||||
f(i === 0 ? undefined : arr[i - 1], i === arr.length ? undefined : arr[i]);
|
||||
}
|
||||
}
|
||||
function forEachWithNeighbors(arr, f) {
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
f(i === 0 ? undefined : arr[i - 1], arr[i], i + 1 === arr.length ? undefined : arr[i + 1]);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @returns New array with all falsy values removed. The original array IS NOT modified.
|
||||
*/
|
||||
function coalesce(array) {
|
||||
return array.filter((e) => !!e);
|
||||
}
|
||||
/**
|
||||
* Remove all falsy values from `array`. The original array IS modified.
|
||||
*/
|
||||
function coalesceInPlace(array) {
|
||||
let to = 0;
|
||||
for (let i = 0; i < array.length; i++) {
|
||||
if (!!array[i]) {
|
||||
array[to] = array[i];
|
||||
to += 1;
|
||||
}
|
||||
}
|
||||
array.length = to;
|
||||
}
|
||||
/**
|
||||
* @returns false if the provided object is an array and not empty.
|
||||
*/
|
||||
function isFalsyOrEmpty(obj) {
|
||||
return !Array.isArray(obj) || obj.length === 0;
|
||||
}
|
||||
function isNonEmptyArray(obj) {
|
||||
return Array.isArray(obj) && obj.length > 0;
|
||||
}
|
||||
/**
|
||||
* Removes duplicates from the given array. The optional keyFn allows to specify
|
||||
* how elements are checked for equality by returning an alternate value for each.
|
||||
*/
|
||||
function distinct(array, keyFn = value => value) {
|
||||
const seen = new Set();
|
||||
return array.filter(element => {
|
||||
const key = keyFn(element);
|
||||
if (seen.has(key)) {
|
||||
return false;
|
||||
}
|
||||
seen.add(key);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
function range(arg, to) {
|
||||
let from = typeof to === 'number' ? arg : 0;
|
||||
if (typeof to === 'number') {
|
||||
from = arg;
|
||||
}
|
||||
else {
|
||||
from = 0;
|
||||
to = arg;
|
||||
}
|
||||
const result = [];
|
||||
if (from <= to) {
|
||||
for (let i = from; i < to; i++) {
|
||||
result.push(i);
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (let i = from; i > to; i--) {
|
||||
result.push(i);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
/**
|
||||
* Insert `insertArr` inside `target` at `insertIndex`.
|
||||
* Please don't touch unless you understand https://jsperf.com/inserting-an-array-within-an-array
|
||||
*/
|
||||
function arrayInsert(target, insertIndex, insertArr) {
|
||||
const before = target.slice(0, insertIndex);
|
||||
const after = target.slice(insertIndex);
|
||||
return before.concat(insertArr, after);
|
||||
}
|
||||
/**
|
||||
* Pushes an element to the start of the array, if found.
|
||||
*/
|
||||
function pushToStart(arr, value) {
|
||||
const index = arr.indexOf(value);
|
||||
if (index > -1) {
|
||||
arr.splice(index, 1);
|
||||
arr.unshift(value);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Pushes an element to the end of the array, if found.
|
||||
*/
|
||||
function pushToEnd(arr, value) {
|
||||
const index = arr.indexOf(value);
|
||||
if (index > -1) {
|
||||
arr.splice(index, 1);
|
||||
arr.push(value);
|
||||
}
|
||||
}
|
||||
function pushMany(arr, items) {
|
||||
for (const item of items) {
|
||||
arr.push(item);
|
||||
}
|
||||
}
|
||||
function mapFilter(array, fn) {
|
||||
const result = [];
|
||||
for (const item of array) {
|
||||
const mapped = fn(item);
|
||||
if (mapped !== undefined) {
|
||||
result.push(mapped);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
function asArray(x) {
|
||||
return Array.isArray(x) ? x : [x];
|
||||
}
|
||||
/**
|
||||
* Insert the new items in the array.
|
||||
* @param array The original array.
|
||||
* @param start The zero-based location in the array from which to start inserting elements.
|
||||
* @param newItems The items to be inserted
|
||||
*/
|
||||
function insertInto(array, start, newItems) {
|
||||
const startIdx = getActualStartIndex(array, start);
|
||||
const originalLength = array.length;
|
||||
const newItemsLength = newItems.length;
|
||||
array.length = originalLength + newItemsLength;
|
||||
// Move the items after the start index, start from the end so that we don't overwrite any value.
|
||||
for (let i = originalLength - 1; i >= startIdx; i--) {
|
||||
array[i + newItemsLength] = array[i];
|
||||
}
|
||||
for (let i = 0; i < newItemsLength; i++) {
|
||||
array[i + startIdx] = newItems[i];
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Removes elements from an array and inserts new elements in their place, returning the deleted elements. Alternative to the native Array.splice method, it
|
||||
* can only support limited number of items due to the maximum call stack size limit.
|
||||
* @param array The original array.
|
||||
* @param start The zero-based location in the array from which to start removing elements.
|
||||
* @param deleteCount The number of elements to remove.
|
||||
* @returns An array containing the elements that were deleted.
|
||||
*/
|
||||
function splice(array, start, deleteCount, newItems) {
|
||||
const index = getActualStartIndex(array, start);
|
||||
let result = array.splice(index, deleteCount);
|
||||
if (result === undefined) {
|
||||
// see https://bugs.webkit.org/show_bug.cgi?id=261140
|
||||
result = [];
|
||||
}
|
||||
insertInto(array, index, newItems);
|
||||
return result;
|
||||
}
|
||||
/**
|
||||
* Determine the actual start index (same logic as the native splice() or slice())
|
||||
* If greater than the length of the array, start will be set to the length of the array. In this case, no element will be deleted but the method will behave as an adding function, adding as many element as item[n*] provided.
|
||||
* If negative, it will begin that many elements from the end of the array. (In this case, the origin -1, meaning -n is the index of the nth last element, and is therefore equivalent to the index of array.length - n.) If array.length + start is less than 0, it will begin from index 0.
|
||||
* @param array The target array.
|
||||
* @param start The operation index.
|
||||
*/
|
||||
function getActualStartIndex(array, start) {
|
||||
return start < 0 ? Math.max(start + array.length, 0) : Math.min(start, array.length);
|
||||
}
|
||||
var CompareResult;
|
||||
(function (CompareResult) {
|
||||
function isLessThan(result) {
|
||||
return result < 0;
|
||||
}
|
||||
CompareResult.isLessThan = isLessThan;
|
||||
function isLessThanOrEqual(result) {
|
||||
return result <= 0;
|
||||
}
|
||||
CompareResult.isLessThanOrEqual = isLessThanOrEqual;
|
||||
function isGreaterThan(result) {
|
||||
return result > 0;
|
||||
}
|
||||
CompareResult.isGreaterThan = isGreaterThan;
|
||||
function isNeitherLessOrGreaterThan(result) {
|
||||
return result === 0;
|
||||
}
|
||||
CompareResult.isNeitherLessOrGreaterThan = isNeitherLessOrGreaterThan;
|
||||
CompareResult.greaterThan = 1;
|
||||
CompareResult.lessThan = -1;
|
||||
CompareResult.neitherLessOrGreaterThan = 0;
|
||||
})(CompareResult || (CompareResult = {}));
|
||||
function compareBy(selector, comparator) {
|
||||
return (a, b) => comparator(selector(a), selector(b));
|
||||
}
|
||||
function tieBreakComparators(...comparators) {
|
||||
return (item1, item2) => {
|
||||
for (const comparator of comparators) {
|
||||
const result = comparator(item1, item2);
|
||||
if (!CompareResult.isNeitherLessOrGreaterThan(result)) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return CompareResult.neitherLessOrGreaterThan;
|
||||
};
|
||||
}
|
||||
/**
|
||||
* The natural order on numbers.
|
||||
*/
|
||||
const numberComparator = (a, b) => a - b;
|
||||
const booleanComparator = (a, b) => numberComparator(a ? 1 : 0, b ? 1 : 0);
|
||||
function reverseOrder(comparator) {
|
||||
return (a, b) => -comparator(a, b);
|
||||
}
|
||||
/**
|
||||
* Returns a new comparator that treats `undefined` as the smallest value.
|
||||
* All other values are compared using the given comparator.
|
||||
*/
|
||||
function compareUndefinedSmallest(comparator) {
|
||||
return (a, b) => {
|
||||
if (a === undefined) {
|
||||
return b === undefined ? CompareResult.neitherLessOrGreaterThan : CompareResult.lessThan;
|
||||
}
|
||||
else if (b === undefined) {
|
||||
return CompareResult.greaterThan;
|
||||
}
|
||||
return comparator(a, b);
|
||||
};
|
||||
}
|
||||
class ArrayQueue {
|
||||
/**
|
||||
* Constructs a queue that is backed by the given array. Runtime is O(1).
|
||||
*/
|
||||
constructor(items) {
|
||||
this.firstIdx = 0;
|
||||
this.items = items;
|
||||
this.lastIdx = this.items.length - 1;
|
||||
}
|
||||
get length() {
|
||||
return this.lastIdx - this.firstIdx + 1;
|
||||
}
|
||||
/**
|
||||
* Consumes elements from the beginning of the queue as long as the predicate returns true.
|
||||
* If no elements were consumed, `null` is returned. Has a runtime of O(result.length).
|
||||
*/
|
||||
takeWhile(predicate) {
|
||||
// P(k) := k <= this.lastIdx && predicate(this.items[k])
|
||||
// Find s := min { k | k >= this.firstIdx && !P(k) } and return this.data[this.firstIdx...s)
|
||||
let startIdx = this.firstIdx;
|
||||
while (startIdx < this.items.length && predicate(this.items[startIdx])) {
|
||||
startIdx++;
|
||||
}
|
||||
const result = startIdx === this.firstIdx ? null : this.items.slice(this.firstIdx, startIdx);
|
||||
this.firstIdx = startIdx;
|
||||
return result;
|
||||
}
|
||||
/**
|
||||
* Consumes elements from the end of the queue as long as the predicate returns true.
|
||||
* If no elements were consumed, `null` is returned.
|
||||
* The result has the same order as the underlying array!
|
||||
*/
|
||||
takeFromEndWhile(predicate) {
|
||||
// P(k) := this.firstIdx >= k && predicate(this.items[k])
|
||||
// Find s := max { k | k <= this.lastIdx && !P(k) } and return this.data(s...this.lastIdx]
|
||||
let endIdx = this.lastIdx;
|
||||
while (endIdx >= 0 && predicate(this.items[endIdx])) {
|
||||
endIdx--;
|
||||
}
|
||||
const result = endIdx === this.lastIdx ? null : this.items.slice(endIdx + 1, this.lastIdx + 1);
|
||||
this.lastIdx = endIdx;
|
||||
return result;
|
||||
}
|
||||
peek() {
|
||||
if (this.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
return this.items[this.firstIdx];
|
||||
}
|
||||
dequeue() {
|
||||
const result = this.items[this.firstIdx];
|
||||
this.firstIdx++;
|
||||
return result;
|
||||
}
|
||||
takeCount(count) {
|
||||
const result = this.items.slice(this.firstIdx, this.firstIdx + count);
|
||||
this.firstIdx += count;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* This class is faster than an iterator and array for lazy computed data.
|
||||
*/
|
||||
class CallbackIterable {
|
||||
static { this.empty = new CallbackIterable(_callback => { }); }
|
||||
constructor(
|
||||
/**
|
||||
* Calls the callback for every item.
|
||||
* Stops when the callback returns false.
|
||||
*/
|
||||
iterate) {
|
||||
this.iterate = iterate;
|
||||
}
|
||||
toArray() {
|
||||
const result = [];
|
||||
this.iterate(item => { result.push(item); return true; });
|
||||
return result;
|
||||
}
|
||||
filter(predicate) {
|
||||
return new CallbackIterable(cb => this.iterate(item => predicate(item) ? cb(item) : true));
|
||||
}
|
||||
map(mapFn) {
|
||||
return new CallbackIterable(cb => this.iterate(item => cb(mapFn(item))));
|
||||
}
|
||||
findLast(predicate) {
|
||||
let result;
|
||||
this.iterate(item => {
|
||||
if (predicate(item)) {
|
||||
result = item;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
findLastMaxBy(comparator) {
|
||||
let result;
|
||||
let first = true;
|
||||
this.iterate(item => {
|
||||
if (first || CompareResult.isGreaterThan(comparator(item, result))) {
|
||||
first = false;
|
||||
result = item;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Represents a re-arrangement of items in an array.
|
||||
*/
|
||||
class Permutation {
|
||||
constructor(_indexMap) {
|
||||
this._indexMap = _indexMap;
|
||||
}
|
||||
/**
|
||||
* Returns a permutation that sorts the given array according to the given compare function.
|
||||
*/
|
||||
static createSortPermutation(arr, compareFn) {
|
||||
const sortIndices = Array.from(arr.keys()).sort((index1, index2) => compareFn(arr[index1], arr[index2]));
|
||||
return new Permutation(sortIndices);
|
||||
}
|
||||
/**
|
||||
* Returns a new array with the elements of the given array re-arranged according to this permutation.
|
||||
*/
|
||||
apply(arr) {
|
||||
return arr.map((_, index) => arr[this._indexMap[index]]);
|
||||
}
|
||||
/**
|
||||
* Returns a new permutation that undoes the re-arrangement of this permutation.
|
||||
*/
|
||||
inverse() {
|
||||
const inverseIndexMap = this._indexMap.slice();
|
||||
for (let i = 0; i < this._indexMap.length; i++) {
|
||||
inverseIndexMap[this._indexMap[i]] = i;
|
||||
}
|
||||
return new Permutation(inverseIndexMap);
|
||||
}
|
||||
}
|
||||
function sum(array) {
|
||||
return array.reduce((acc, value) => acc + value, 0);
|
||||
}
|
||||
|
||||
export { ArrayQueue, CallbackIterable, CompareResult, Permutation, arrayInsert, asArray, binarySearch, binarySearch2, booleanComparator, coalesce, coalesceInPlace, compareBy, compareUndefinedSmallest, distinct, equals, forEachAdjacent, forEachWithNeighbors, groupAdjacentBy, groupBy, insertInto, isFalsyOrEmpty, isNonEmptyArray, mapFilter, numberComparator, pushMany, pushToEnd, pushToStart, quickSelect, range, removeFastWithoutKeepingOrder, reverseOrder, splice, sum, tail, tieBreakComparators };
|
||||
+177
@@ -0,0 +1,177 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
function findLast(array, predicate, fromIndex = array.length - 1) {
|
||||
const idx = findLastIdx(array, predicate, fromIndex);
|
||||
if (idx === -1) {
|
||||
return undefined;
|
||||
}
|
||||
return array[idx];
|
||||
}
|
||||
function findLastIdx(array, predicate, fromIndex = array.length - 1) {
|
||||
for (let i = fromIndex; i >= 0; i--) {
|
||||
const element = array[i];
|
||||
if (predicate(element, i)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
/**
|
||||
* Finds the last item where predicate is true using binary search.
|
||||
* `predicate` must be monotonous, i.e. `arr.map(predicate)` must be like `[true, ..., true, false, ..., false]`!
|
||||
*
|
||||
* @returns `undefined` if no item matches, otherwise the last item that matches the predicate.
|
||||
*/
|
||||
function findLastMonotonous(array, predicate) {
|
||||
const idx = findLastIdxMonotonous(array, predicate);
|
||||
return idx === -1 ? undefined : array[idx];
|
||||
}
|
||||
/**
|
||||
* Finds the last item where predicate is true using binary search.
|
||||
* `predicate` must be monotonous, i.e. `arr.map(predicate)` must be like `[true, ..., true, false, ..., false]`!
|
||||
*
|
||||
* @returns `startIdx - 1` if predicate is false for all items, otherwise the index of the last item that matches the predicate.
|
||||
*/
|
||||
function findLastIdxMonotonous(array, predicate, startIdx = 0, endIdxEx = array.length) {
|
||||
let i = startIdx;
|
||||
let j = endIdxEx;
|
||||
while (i < j) {
|
||||
const k = Math.floor((i + j) / 2);
|
||||
if (predicate(array[k])) {
|
||||
i = k + 1;
|
||||
}
|
||||
else {
|
||||
j = k;
|
||||
}
|
||||
}
|
||||
return i - 1;
|
||||
}
|
||||
/**
|
||||
* Finds the first item where predicate is true using binary search.
|
||||
* `predicate` must be monotonous, i.e. `arr.map(predicate)` must be like `[false, ..., false, true, ..., true]`!
|
||||
*
|
||||
* @returns `undefined` if no item matches, otherwise the first item that matches the predicate.
|
||||
*/
|
||||
function findFirstMonotonous(array, predicate) {
|
||||
const idx = findFirstIdxMonotonousOrArrLen(array, predicate);
|
||||
return idx === array.length ? undefined : array[idx];
|
||||
}
|
||||
/**
|
||||
* Finds the first item where predicate is true using binary search.
|
||||
* `predicate` must be monotonous, i.e. `arr.map(predicate)` must be like `[false, ..., false, true, ..., true]`!
|
||||
*
|
||||
* @returns `endIdxEx` if predicate is false for all items, otherwise the index of the first item that matches the predicate.
|
||||
*/
|
||||
function findFirstIdxMonotonousOrArrLen(array, predicate, startIdx = 0, endIdxEx = array.length) {
|
||||
let i = startIdx;
|
||||
let j = endIdxEx;
|
||||
while (i < j) {
|
||||
const k = Math.floor((i + j) / 2);
|
||||
if (predicate(array[k])) {
|
||||
j = k;
|
||||
}
|
||||
else {
|
||||
i = k + 1;
|
||||
}
|
||||
}
|
||||
return i;
|
||||
}
|
||||
/**
|
||||
* Use this when
|
||||
* * You have a sorted array
|
||||
* * You query this array with a monotonous predicate to find the last item that has a certain property.
|
||||
* * You query this array multiple times with monotonous predicates that get weaker and weaker.
|
||||
*/
|
||||
class MonotonousArray {
|
||||
static { this.assertInvariants = false; }
|
||||
constructor(_array) {
|
||||
this._array = _array;
|
||||
this._findLastMonotonousLastIdx = 0;
|
||||
}
|
||||
/**
|
||||
* The predicate must be monotonous, i.e. `arr.map(predicate)` must be like `[true, ..., true, false, ..., false]`!
|
||||
* For subsequent calls, current predicate must be weaker than (or equal to) the previous predicate, i.e. more entries must be `true`.
|
||||
*/
|
||||
findLastMonotonous(predicate) {
|
||||
if (MonotonousArray.assertInvariants) {
|
||||
if (this._prevFindLastPredicate) {
|
||||
for (const item of this._array) {
|
||||
if (this._prevFindLastPredicate(item) && !predicate(item)) {
|
||||
throw new Error('MonotonousArray: current predicate must be weaker than (or equal to) the previous predicate.');
|
||||
}
|
||||
}
|
||||
}
|
||||
this._prevFindLastPredicate = predicate;
|
||||
}
|
||||
const idx = findLastIdxMonotonous(this._array, predicate, this._findLastMonotonousLastIdx);
|
||||
this._findLastMonotonousLastIdx = idx + 1;
|
||||
return idx === -1 ? undefined : this._array[idx];
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Returns the first item that is equal to or greater than every other item.
|
||||
*/
|
||||
function findFirstMax(array, comparator) {
|
||||
if (array.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
let max = array[0];
|
||||
for (let i = 1; i < array.length; i++) {
|
||||
const item = array[i];
|
||||
if (comparator(item, max) > 0) {
|
||||
max = item;
|
||||
}
|
||||
}
|
||||
return max;
|
||||
}
|
||||
/**
|
||||
* Returns the last item that is equal to or greater than every other item.
|
||||
*/
|
||||
function findLastMax(array, comparator) {
|
||||
if (array.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
let max = array[0];
|
||||
for (let i = 1; i < array.length; i++) {
|
||||
const item = array[i];
|
||||
if (comparator(item, max) >= 0) {
|
||||
max = item;
|
||||
}
|
||||
}
|
||||
return max;
|
||||
}
|
||||
/**
|
||||
* Returns the first item that is equal to or less than every other item.
|
||||
*/
|
||||
function findFirstMin(array, comparator) {
|
||||
return findFirstMax(array, (a, b) => -comparator(a, b));
|
||||
}
|
||||
function findMaxIdx(array, comparator) {
|
||||
if (array.length === 0) {
|
||||
return -1;
|
||||
}
|
||||
let maxIdx = 0;
|
||||
for (let i = 1; i < array.length; i++) {
|
||||
const item = array[i];
|
||||
if (comparator(item, array[maxIdx]) > 0) {
|
||||
maxIdx = i;
|
||||
}
|
||||
}
|
||||
return maxIdx;
|
||||
}
|
||||
/**
|
||||
* Returns the first mapped value of the array which is not undefined.
|
||||
*/
|
||||
function mapFindFirst(items, mapFn) {
|
||||
for (const value of items) {
|
||||
const mapped = mapFn(value);
|
||||
if (mapped !== undefined) {
|
||||
return mapped;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export { MonotonousArray, findFirstIdxMonotonousOrArrLen, findFirstMax, findFirstMin, findFirstMonotonous, findLast, findLastIdx, findLastIdxMonotonous, findLastMax, findLastMonotonous, findMaxIdx, mapFindFirst };
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
import { onUnexpectedError, BugIndicatingError } from './errors.js';
|
||||
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
/**
|
||||
* Throws an error with the provided message if the provided value does not evaluate to a true Javascript value.
|
||||
*
|
||||
* @deprecated Use `assert(...)` instead.
|
||||
* This method is usually used like this:
|
||||
* ```ts
|
||||
* import * as assert from 'vs/base/common/assert';
|
||||
* assert.ok(...);
|
||||
* ```
|
||||
*
|
||||
* However, `assert` in that example is a user chosen name.
|
||||
* There is no tooling for generating such an import statement.
|
||||
* Thus, the `assert(...)` function should be used instead.
|
||||
*/
|
||||
function ok(value, message) {
|
||||
if (!value) {
|
||||
throw new Error(message ? `Assertion failed (${message})` : 'Assertion Failed');
|
||||
}
|
||||
}
|
||||
function assertNever(value, message = 'Unreachable') {
|
||||
throw new Error(message);
|
||||
}
|
||||
/**
|
||||
* Asserts that a condition is `truthy`.
|
||||
*
|
||||
* @throws provided {@linkcode messageOrError} if the {@linkcode condition} is `falsy`.
|
||||
*
|
||||
* @param condition The condition to assert.
|
||||
* @param messageOrError An error message or error object to throw if condition is `falsy`.
|
||||
*/
|
||||
function assert(condition, messageOrError = 'unexpected state') {
|
||||
if (!condition) {
|
||||
// if error instance is provided, use it, otherwise create a new one
|
||||
const errorToThrow = typeof messageOrError === 'string'
|
||||
? new BugIndicatingError(`Assertion Failed: ${messageOrError}`)
|
||||
: messageOrError;
|
||||
throw errorToThrow;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Like assert, but doesn't throw.
|
||||
*/
|
||||
function softAssert(condition, message = 'Soft Assertion Failed') {
|
||||
if (!condition) {
|
||||
onUnexpectedError(new BugIndicatingError(message));
|
||||
}
|
||||
}
|
||||
/**
|
||||
* condition must be side-effect free!
|
||||
*/
|
||||
function assertFn(condition) {
|
||||
if (!condition()) {
|
||||
// eslint-disable-next-line no-debugger
|
||||
debugger;
|
||||
// Reevaluate `condition` again to make debugging easier
|
||||
condition();
|
||||
onUnexpectedError(new BugIndicatingError('Assertion Failed'));
|
||||
}
|
||||
}
|
||||
function checkAdjacentItems(items, predicate) {
|
||||
let i = 0;
|
||||
while (i < items.length - 1) {
|
||||
const a = items[i];
|
||||
const b = items[i + 1];
|
||||
if (!predicate(a, b)) {
|
||||
return false;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
export { assert, assertFn, assertNever, checkAdjacentItems, ok, softAssert };
|
||||
+938
@@ -0,0 +1,938 @@
|
||||
import { CancellationTokenSource } from './cancellation.js';
|
||||
import { BugIndicatingError, CancellationError } from './errors.js';
|
||||
import { toDisposable, isDisposable } from './lifecycle.js';
|
||||
import { setTimeout0 } from './platform.js';
|
||||
import { MicrotaskDelay } from './symbols.js';
|
||||
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
function isThenable(obj) {
|
||||
return !!obj && typeof obj.then === 'function';
|
||||
}
|
||||
/**
|
||||
* Returns a promise that can be cancelled using the provided cancellation token.
|
||||
*
|
||||
* @remarks When cancellation is requested, the promise will be rejected with a {@link CancellationError}.
|
||||
* If the promise resolves to a disposable object, it will be automatically disposed when cancellation
|
||||
* is requested.
|
||||
*
|
||||
* @param callback A function that accepts a cancellation token and returns a promise
|
||||
* @returns A promise that can be cancelled
|
||||
*/
|
||||
function createCancelablePromise(callback) {
|
||||
const source = new CancellationTokenSource();
|
||||
const thenable = callback(source.token);
|
||||
let isCancelled = false;
|
||||
const promise = new Promise((resolve, reject) => {
|
||||
const subscription = source.token.onCancellationRequested(() => {
|
||||
isCancelled = true;
|
||||
subscription.dispose();
|
||||
reject(new CancellationError());
|
||||
});
|
||||
Promise.resolve(thenable).then(value => {
|
||||
subscription.dispose();
|
||||
source.dispose();
|
||||
if (!isCancelled) {
|
||||
resolve(value);
|
||||
}
|
||||
else if (isDisposable(value)) {
|
||||
// promise has been cancelled, result is disposable and will
|
||||
// be cleaned up
|
||||
value.dispose();
|
||||
}
|
||||
}, err => {
|
||||
subscription.dispose();
|
||||
source.dispose();
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
return new class {
|
||||
cancel() {
|
||||
source.cancel();
|
||||
source.dispose();
|
||||
}
|
||||
then(resolve, reject) {
|
||||
return promise.then(resolve, reject);
|
||||
}
|
||||
catch(reject) {
|
||||
return this.then(undefined, reject);
|
||||
}
|
||||
finally(onfinally) {
|
||||
return promise.finally(onfinally);
|
||||
}
|
||||
};
|
||||
}
|
||||
function raceCancellation(promise, token, defaultValue) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const ref = token.onCancellationRequested(() => {
|
||||
ref.dispose();
|
||||
resolve(defaultValue);
|
||||
});
|
||||
promise.then(resolve, reject).finally(() => ref.dispose());
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Returns a promise that rejects with an {@CancellationError} as soon as the passed token is cancelled.
|
||||
* @see {@link raceCancellation}
|
||||
*/
|
||||
function raceCancellationError(promise, token) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const ref = token.onCancellationRequested(() => {
|
||||
ref.dispose();
|
||||
reject(new CancellationError());
|
||||
});
|
||||
promise.then(resolve, reject).finally(() => ref.dispose());
|
||||
});
|
||||
}
|
||||
/**
|
||||
* A helper to prevent accumulation of sequential async tasks.
|
||||
*
|
||||
* Imagine a mail man with the sole task of delivering letters. As soon as
|
||||
* a letter submitted for delivery, he drives to the destination, delivers it
|
||||
* and returns to his base. Imagine that during the trip, N more letters were submitted.
|
||||
* When the mail man returns, he picks those N letters and delivers them all in a
|
||||
* single trip. Even though N+1 submissions occurred, only 2 deliveries were made.
|
||||
*
|
||||
* The throttler implements this via the queue() method, by providing it a task
|
||||
* factory. Following the example:
|
||||
*
|
||||
* const throttler = new Throttler();
|
||||
* const letters = [];
|
||||
*
|
||||
* function deliver() {
|
||||
* const lettersToDeliver = letters;
|
||||
* letters = [];
|
||||
* return makeTheTrip(lettersToDeliver);
|
||||
* }
|
||||
*
|
||||
* function onLetterReceived(l) {
|
||||
* letters.push(l);
|
||||
* throttler.queue(deliver);
|
||||
* }
|
||||
*/
|
||||
class Throttler {
|
||||
constructor() {
|
||||
this.activePromise = null;
|
||||
this.queuedPromise = null;
|
||||
this.queuedPromiseFactory = null;
|
||||
this.cancellationTokenSource = new CancellationTokenSource();
|
||||
}
|
||||
queue(promiseFactory) {
|
||||
if (this.cancellationTokenSource.token.isCancellationRequested) {
|
||||
return Promise.reject(new Error('Throttler is disposed'));
|
||||
}
|
||||
if (this.activePromise) {
|
||||
this.queuedPromiseFactory = promiseFactory;
|
||||
if (!this.queuedPromise) {
|
||||
const onComplete = () => {
|
||||
this.queuedPromise = null;
|
||||
if (this.cancellationTokenSource.token.isCancellationRequested) {
|
||||
return;
|
||||
}
|
||||
const result = this.queue(this.queuedPromiseFactory);
|
||||
this.queuedPromiseFactory = null;
|
||||
return result;
|
||||
};
|
||||
this.queuedPromise = new Promise(resolve => {
|
||||
this.activePromise.then(onComplete, onComplete).then(resolve);
|
||||
});
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
this.queuedPromise.then(resolve, reject);
|
||||
});
|
||||
}
|
||||
this.activePromise = promiseFactory(this.cancellationTokenSource.token);
|
||||
return new Promise((resolve, reject) => {
|
||||
this.activePromise.then((result) => {
|
||||
this.activePromise = null;
|
||||
resolve(result);
|
||||
}, (err) => {
|
||||
this.activePromise = null;
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
dispose() {
|
||||
this.cancellationTokenSource.cancel();
|
||||
}
|
||||
}
|
||||
const timeoutDeferred = (timeout, fn) => {
|
||||
let scheduled = true;
|
||||
const handle = setTimeout(() => {
|
||||
scheduled = false;
|
||||
fn();
|
||||
}, timeout);
|
||||
return {
|
||||
isTriggered: () => scheduled,
|
||||
dispose: () => {
|
||||
clearTimeout(handle);
|
||||
scheduled = false;
|
||||
},
|
||||
};
|
||||
};
|
||||
const microtaskDeferred = (fn) => {
|
||||
let scheduled = true;
|
||||
queueMicrotask(() => {
|
||||
if (scheduled) {
|
||||
scheduled = false;
|
||||
fn();
|
||||
}
|
||||
});
|
||||
return {
|
||||
isTriggered: () => scheduled,
|
||||
dispose: () => { scheduled = false; },
|
||||
};
|
||||
};
|
||||
/**
|
||||
* A helper to delay (debounce) execution of a task that is being requested often.
|
||||
*
|
||||
* Following the throttler, now imagine the mail man wants to optimize the number of
|
||||
* trips proactively. The trip itself can be long, so he decides not to make the trip
|
||||
* as soon as a letter is submitted. Instead he waits a while, in case more
|
||||
* letters are submitted. After said waiting period, if no letters were submitted, he
|
||||
* decides to make the trip. Imagine that N more letters were submitted after the first
|
||||
* one, all within a short period of time between each other. Even though N+1
|
||||
* submissions occurred, only 1 delivery was made.
|
||||
*
|
||||
* The delayer offers this behavior via the trigger() method, into which both the task
|
||||
* to be executed and the waiting period (delay) must be passed in as arguments. Following
|
||||
* the example:
|
||||
*
|
||||
* const delayer = new Delayer(WAITING_PERIOD);
|
||||
* const letters = [];
|
||||
*
|
||||
* function letterReceived(l) {
|
||||
* letters.push(l);
|
||||
* delayer.trigger(() => { return makeTheTrip(); });
|
||||
* }
|
||||
*/
|
||||
class Delayer {
|
||||
constructor(defaultDelay) {
|
||||
this.defaultDelay = defaultDelay;
|
||||
this.deferred = null;
|
||||
this.completionPromise = null;
|
||||
this.doResolve = null;
|
||||
this.doReject = null;
|
||||
this.task = null;
|
||||
}
|
||||
trigger(task, delay = this.defaultDelay) {
|
||||
this.task = task;
|
||||
this.cancelTimeout();
|
||||
if (!this.completionPromise) {
|
||||
this.completionPromise = new Promise((resolve, reject) => {
|
||||
this.doResolve = resolve;
|
||||
this.doReject = reject;
|
||||
}).then(() => {
|
||||
this.completionPromise = null;
|
||||
this.doResolve = null;
|
||||
if (this.task) {
|
||||
const task = this.task;
|
||||
this.task = null;
|
||||
return task();
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
}
|
||||
const fn = () => {
|
||||
this.deferred = null;
|
||||
this.doResolve?.(null);
|
||||
};
|
||||
this.deferred = delay === MicrotaskDelay ? microtaskDeferred(fn) : timeoutDeferred(delay, fn);
|
||||
return this.completionPromise;
|
||||
}
|
||||
isTriggered() {
|
||||
return !!this.deferred?.isTriggered();
|
||||
}
|
||||
cancel() {
|
||||
this.cancelTimeout();
|
||||
if (this.completionPromise) {
|
||||
this.doReject?.(new CancellationError());
|
||||
this.completionPromise = null;
|
||||
}
|
||||
}
|
||||
cancelTimeout() {
|
||||
this.deferred?.dispose();
|
||||
this.deferred = null;
|
||||
}
|
||||
dispose() {
|
||||
this.cancel();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* A helper to delay execution of a task that is being requested often, while
|
||||
* preventing accumulation of consecutive executions, while the task runs.
|
||||
*
|
||||
* The mail man is clever and waits for a certain amount of time, before going
|
||||
* out to deliver letters. While the mail man is going out, more letters arrive
|
||||
* and can only be delivered once he is back. Once he is back the mail man will
|
||||
* do one more trip to deliver the letters that have accumulated while he was out.
|
||||
*/
|
||||
class ThrottledDelayer {
|
||||
constructor(defaultDelay) {
|
||||
this.delayer = new Delayer(defaultDelay);
|
||||
this.throttler = new Throttler();
|
||||
}
|
||||
trigger(promiseFactory, delay) {
|
||||
return this.delayer.trigger(() => this.throttler.queue(promiseFactory), delay);
|
||||
}
|
||||
cancel() {
|
||||
this.delayer.cancel();
|
||||
}
|
||||
dispose() {
|
||||
this.delayer.dispose();
|
||||
this.throttler.dispose();
|
||||
}
|
||||
}
|
||||
function timeout(millis, token) {
|
||||
if (!token) {
|
||||
return createCancelablePromise(token => timeout(millis, token));
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
const handle = setTimeout(() => {
|
||||
disposable.dispose();
|
||||
resolve();
|
||||
}, millis);
|
||||
const disposable = token.onCancellationRequested(() => {
|
||||
clearTimeout(handle);
|
||||
disposable.dispose();
|
||||
reject(new CancellationError());
|
||||
});
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Creates a timeout that can be disposed using its returned value.
|
||||
* @param handler The timeout handler.
|
||||
* @param timeout An optional timeout in milliseconds.
|
||||
* @param store An optional {@link DisposableStore} that will have the timeout disposable managed automatically.
|
||||
*
|
||||
* @example
|
||||
* const store = new DisposableStore;
|
||||
* // Call the timeout after 1000ms at which point it will be automatically
|
||||
* // evicted from the store.
|
||||
* const timeoutDisposable = disposableTimeout(() => {}, 1000, store);
|
||||
*
|
||||
* if (foo) {
|
||||
* // Cancel the timeout and evict it from store.
|
||||
* timeoutDisposable.dispose();
|
||||
* }
|
||||
*/
|
||||
function disposableTimeout(handler, timeout = 0, store) {
|
||||
const timer = setTimeout(() => {
|
||||
handler();
|
||||
if (store) {
|
||||
disposable.dispose();
|
||||
}
|
||||
}, timeout);
|
||||
const disposable = toDisposable(() => {
|
||||
clearTimeout(timer);
|
||||
store?.delete(disposable);
|
||||
});
|
||||
store?.add(disposable);
|
||||
return disposable;
|
||||
}
|
||||
function first(promiseFactories, shouldStop = t => !!t, defaultValue = null) {
|
||||
let index = 0;
|
||||
const len = promiseFactories.length;
|
||||
const loop = () => {
|
||||
if (index >= len) {
|
||||
return Promise.resolve(defaultValue);
|
||||
}
|
||||
const factory = promiseFactories[index++];
|
||||
const promise = Promise.resolve(factory());
|
||||
return promise.then(result => {
|
||||
if (shouldStop(result)) {
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
return loop();
|
||||
});
|
||||
};
|
||||
return loop();
|
||||
}
|
||||
/**
|
||||
* Processes tasks in the order they were scheduled.
|
||||
*/
|
||||
class TaskQueue {
|
||||
constructor() {
|
||||
this._runningTask = undefined;
|
||||
this._pendingTasks = [];
|
||||
}
|
||||
/**
|
||||
* Waits for the current and pending tasks to finish, then runs and awaits the given task.
|
||||
* If the task is skipped because of clearPending, the promise is rejected with a CancellationError.
|
||||
*/
|
||||
schedule(task) {
|
||||
const deferred = new DeferredPromise();
|
||||
this._pendingTasks.push({ task, deferred, setUndefinedWhenCleared: false });
|
||||
this._runIfNotRunning();
|
||||
return deferred.p;
|
||||
}
|
||||
_runIfNotRunning() {
|
||||
if (this._runningTask === undefined) {
|
||||
this._processQueue();
|
||||
}
|
||||
}
|
||||
async _processQueue() {
|
||||
if (this._pendingTasks.length === 0) {
|
||||
return;
|
||||
}
|
||||
const next = this._pendingTasks.shift();
|
||||
if (!next) {
|
||||
return;
|
||||
}
|
||||
if (this._runningTask) {
|
||||
throw new BugIndicatingError();
|
||||
}
|
||||
this._runningTask = next.task;
|
||||
try {
|
||||
const result = await next.task();
|
||||
next.deferred.complete(result);
|
||||
}
|
||||
catch (e) {
|
||||
next.deferred.error(e);
|
||||
}
|
||||
finally {
|
||||
this._runningTask = undefined;
|
||||
this._processQueue();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Clears all pending tasks. Does not cancel the currently running task.
|
||||
*/
|
||||
clearPending() {
|
||||
const tasks = this._pendingTasks;
|
||||
this._pendingTasks = [];
|
||||
for (const task of tasks) {
|
||||
if (task.setUndefinedWhenCleared) {
|
||||
task.deferred.complete(undefined);
|
||||
}
|
||||
else {
|
||||
task.deferred.error(new CancellationError());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
class TimeoutTimer {
|
||||
constructor(runner, timeout) {
|
||||
this._isDisposed = false;
|
||||
this._token = undefined;
|
||||
if (typeof runner === 'function' && typeof timeout === 'number') {
|
||||
this.setIfNotSet(runner, timeout);
|
||||
}
|
||||
}
|
||||
dispose() {
|
||||
this.cancel();
|
||||
this._isDisposed = true;
|
||||
}
|
||||
cancel() {
|
||||
if (this._token !== undefined) {
|
||||
clearTimeout(this._token);
|
||||
this._token = undefined;
|
||||
}
|
||||
}
|
||||
cancelAndSet(runner, timeout) {
|
||||
if (this._isDisposed) {
|
||||
throw new BugIndicatingError(`Calling 'cancelAndSet' on a disposed TimeoutTimer`);
|
||||
}
|
||||
this.cancel();
|
||||
this._token = setTimeout(() => {
|
||||
this._token = undefined;
|
||||
runner();
|
||||
}, timeout);
|
||||
}
|
||||
setIfNotSet(runner, timeout) {
|
||||
if (this._isDisposed) {
|
||||
throw new BugIndicatingError(`Calling 'setIfNotSet' on a disposed TimeoutTimer`);
|
||||
}
|
||||
if (this._token !== undefined) {
|
||||
// timer is already set
|
||||
return;
|
||||
}
|
||||
this._token = setTimeout(() => {
|
||||
this._token = undefined;
|
||||
runner();
|
||||
}, timeout);
|
||||
}
|
||||
}
|
||||
class IntervalTimer {
|
||||
constructor() {
|
||||
this.disposable = undefined;
|
||||
this.isDisposed = false;
|
||||
}
|
||||
cancel() {
|
||||
this.disposable?.dispose();
|
||||
this.disposable = undefined;
|
||||
}
|
||||
cancelAndSet(runner, interval, context = globalThis) {
|
||||
if (this.isDisposed) {
|
||||
throw new BugIndicatingError(`Calling 'cancelAndSet' on a disposed IntervalTimer`);
|
||||
}
|
||||
this.cancel();
|
||||
const handle = context.setInterval(() => {
|
||||
runner();
|
||||
}, interval);
|
||||
this.disposable = toDisposable(() => {
|
||||
context.clearInterval(handle);
|
||||
this.disposable = undefined;
|
||||
});
|
||||
}
|
||||
dispose() {
|
||||
this.cancel();
|
||||
this.isDisposed = true;
|
||||
}
|
||||
}
|
||||
class RunOnceScheduler {
|
||||
constructor(runner, delay) {
|
||||
this.timeoutToken = undefined;
|
||||
this.runner = runner;
|
||||
this.timeout = delay;
|
||||
this.timeoutHandler = this.onTimeout.bind(this);
|
||||
}
|
||||
/**
|
||||
* Dispose RunOnceScheduler
|
||||
*/
|
||||
dispose() {
|
||||
this.cancel();
|
||||
this.runner = null;
|
||||
}
|
||||
/**
|
||||
* Cancel current scheduled runner (if any).
|
||||
*/
|
||||
cancel() {
|
||||
if (this.isScheduled()) {
|
||||
clearTimeout(this.timeoutToken);
|
||||
this.timeoutToken = undefined;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Cancel previous runner (if any) & schedule a new runner.
|
||||
*/
|
||||
schedule(delay = this.timeout) {
|
||||
this.cancel();
|
||||
this.timeoutToken = setTimeout(this.timeoutHandler, delay);
|
||||
}
|
||||
get delay() {
|
||||
return this.timeout;
|
||||
}
|
||||
set delay(value) {
|
||||
this.timeout = value;
|
||||
}
|
||||
/**
|
||||
* Returns true if scheduled.
|
||||
*/
|
||||
isScheduled() {
|
||||
return this.timeoutToken !== undefined;
|
||||
}
|
||||
onTimeout() {
|
||||
this.timeoutToken = undefined;
|
||||
if (this.runner) {
|
||||
this.doRun();
|
||||
}
|
||||
}
|
||||
doRun() {
|
||||
this.runner?.();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Execute the callback the next time the browser is idle, returning an
|
||||
* {@link IDisposable} that will cancel the callback when disposed. This wraps
|
||||
* [requestIdleCallback] so it will fallback to [setTimeout] if the environment
|
||||
* doesn't support it.
|
||||
*
|
||||
* @param callback The callback to run when idle, this includes an
|
||||
* [IdleDeadline] that provides the time alloted for the idle callback by the
|
||||
* browser. Not respecting this deadline will result in a degraded user
|
||||
* experience.
|
||||
* @param timeout A timeout at which point to queue no longer wait for an idle
|
||||
* callback but queue it on the regular event loop (like setTimeout). Typically
|
||||
* this should not be used.
|
||||
*
|
||||
* [IdleDeadline]: https://developer.mozilla.org/en-US/docs/Web/API/IdleDeadline
|
||||
* [requestIdleCallback]: https://developer.mozilla.org/en-US/docs/Web/API/Window/requestIdleCallback
|
||||
* [setTimeout]: https://developer.mozilla.org/en-US/docs/Web/API/Window/setTimeout
|
||||
*
|
||||
* **Note** that there is `dom.ts#runWhenWindowIdle` which is better suited when running inside a browser
|
||||
* context
|
||||
*/
|
||||
let runWhenGlobalIdle;
|
||||
let _runWhenIdle;
|
||||
(function () {
|
||||
const safeGlobal = globalThis;
|
||||
if (typeof safeGlobal.requestIdleCallback !== 'function' || typeof safeGlobal.cancelIdleCallback !== 'function') {
|
||||
_runWhenIdle = (_targetWindow, runner, timeout) => {
|
||||
setTimeout0(() => {
|
||||
if (disposed) {
|
||||
return;
|
||||
}
|
||||
const end = Date.now() + 15; // one frame at 64fps
|
||||
const deadline = {
|
||||
didTimeout: true,
|
||||
timeRemaining() {
|
||||
return Math.max(0, end - Date.now());
|
||||
}
|
||||
};
|
||||
runner(Object.freeze(deadline));
|
||||
});
|
||||
let disposed = false;
|
||||
return {
|
||||
dispose() {
|
||||
if (disposed) {
|
||||
return;
|
||||
}
|
||||
disposed = true;
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
else {
|
||||
_runWhenIdle = (targetWindow, runner, timeout) => {
|
||||
const handle = targetWindow.requestIdleCallback(runner, typeof timeout === 'number' ? { timeout } : undefined);
|
||||
let disposed = false;
|
||||
return {
|
||||
dispose() {
|
||||
if (disposed) {
|
||||
return;
|
||||
}
|
||||
disposed = true;
|
||||
targetWindow.cancelIdleCallback(handle);
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
runWhenGlobalIdle = (runner, timeout) => _runWhenIdle(globalThis, runner, timeout);
|
||||
})();
|
||||
class AbstractIdleValue {
|
||||
constructor(targetWindow, executor) {
|
||||
this._didRun = false;
|
||||
this._executor = () => {
|
||||
try {
|
||||
this._value = executor();
|
||||
}
|
||||
catch (err) {
|
||||
this._error = err;
|
||||
}
|
||||
finally {
|
||||
this._didRun = true;
|
||||
}
|
||||
};
|
||||
this._handle = _runWhenIdle(targetWindow, () => this._executor());
|
||||
}
|
||||
dispose() {
|
||||
this._handle.dispose();
|
||||
}
|
||||
get value() {
|
||||
if (!this._didRun) {
|
||||
this._handle.dispose();
|
||||
this._executor();
|
||||
}
|
||||
if (this._error) {
|
||||
throw this._error;
|
||||
}
|
||||
return this._value;
|
||||
}
|
||||
get isInitialized() {
|
||||
return this._didRun;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* An `IdleValue` that always uses the current window (which might be throttled or inactive)
|
||||
*
|
||||
* **Note** that there is `dom.ts#WindowIdleValue` which is better suited when running inside a browser
|
||||
* context
|
||||
*/
|
||||
class GlobalIdleValue extends AbstractIdleValue {
|
||||
constructor(executor) {
|
||||
super(globalThis, executor);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Creates a promise whose resolution or rejection can be controlled imperatively.
|
||||
*/
|
||||
class DeferredPromise {
|
||||
get isRejected() {
|
||||
return this.outcome?.outcome === 1 /* DeferredOutcome.Rejected */;
|
||||
}
|
||||
get isSettled() {
|
||||
return !!this.outcome;
|
||||
}
|
||||
constructor() {
|
||||
this.p = new Promise((c, e) => {
|
||||
this.completeCallback = c;
|
||||
this.errorCallback = e;
|
||||
});
|
||||
}
|
||||
complete(value) {
|
||||
if (this.isSettled) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
return new Promise(resolve => {
|
||||
this.completeCallback(value);
|
||||
this.outcome = { outcome: 0 /* DeferredOutcome.Resolved */, value };
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
error(err) {
|
||||
if (this.isSettled) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
return new Promise(resolve => {
|
||||
this.errorCallback(err);
|
||||
this.outcome = { outcome: 1 /* DeferredOutcome.Rejected */, value: err };
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
cancel() {
|
||||
return this.error(new CancellationError());
|
||||
}
|
||||
}
|
||||
//#endregion
|
||||
//#region Promises
|
||||
var Promises;
|
||||
(function (Promises) {
|
||||
/**
|
||||
* A drop-in replacement for `Promise.all` with the only difference
|
||||
* that the method awaits every promise to either fulfill or reject.
|
||||
*
|
||||
* Similar to `Promise.all`, only the first error will be returned
|
||||
* if any.
|
||||
*/
|
||||
async function settled(promises) {
|
||||
let firstError = undefined;
|
||||
const result = await Promise.all(promises.map(promise => promise.then(value => value, error => {
|
||||
if (!firstError) {
|
||||
firstError = error;
|
||||
}
|
||||
return undefined; // do not rethrow so that other promises can settle
|
||||
})));
|
||||
if (typeof firstError !== 'undefined') {
|
||||
throw firstError;
|
||||
}
|
||||
return result; // cast is needed and protected by the `throw` above
|
||||
}
|
||||
Promises.settled = settled;
|
||||
/**
|
||||
* A helper to create a new `Promise<T>` with a body that is a promise
|
||||
* itself. By default, an error that raises from the async body will
|
||||
* end up as a unhandled rejection, so this utility properly awaits the
|
||||
* body and rejects the promise as a normal promise does without async
|
||||
* body.
|
||||
*
|
||||
* This method should only be used in rare cases where otherwise `async`
|
||||
* cannot be used (e.g. when callbacks are involved that require this).
|
||||
*/
|
||||
function withAsyncBody(bodyFn) {
|
||||
// eslint-disable-next-line no-async-promise-executor
|
||||
return new Promise(async (resolve, reject) => {
|
||||
try {
|
||||
await bodyFn(resolve, reject);
|
||||
}
|
||||
catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
Promises.withAsyncBody = withAsyncBody;
|
||||
})(Promises || (Promises = {}));
|
||||
function createCancelableAsyncIterableProducer(callback) {
|
||||
const source = new CancellationTokenSource();
|
||||
const innerIterable = callback(source.token);
|
||||
return new CancelableAsyncIterableProducer(source, async (emitter) => {
|
||||
const subscription = source.token.onCancellationRequested(() => {
|
||||
subscription.dispose();
|
||||
source.dispose();
|
||||
emitter.reject(new CancellationError());
|
||||
});
|
||||
try {
|
||||
for await (const item of innerIterable) {
|
||||
if (source.token.isCancellationRequested) {
|
||||
// canceled in the meantime
|
||||
return;
|
||||
}
|
||||
emitter.emitOne(item);
|
||||
}
|
||||
subscription.dispose();
|
||||
source.dispose();
|
||||
}
|
||||
catch (err) {
|
||||
subscription.dispose();
|
||||
source.dispose();
|
||||
emitter.reject(err);
|
||||
}
|
||||
});
|
||||
}
|
||||
class ProducerConsumer {
|
||||
constructor() {
|
||||
this._unsatisfiedConsumers = [];
|
||||
this._unconsumedValues = [];
|
||||
}
|
||||
get hasFinalValue() {
|
||||
return !!this._finalValue;
|
||||
}
|
||||
produce(value) {
|
||||
this._ensureNoFinalValue();
|
||||
if (this._unsatisfiedConsumers.length > 0) {
|
||||
const deferred = this._unsatisfiedConsumers.shift();
|
||||
this._resolveOrRejectDeferred(deferred, value);
|
||||
}
|
||||
else {
|
||||
this._unconsumedValues.push(value);
|
||||
}
|
||||
}
|
||||
produceFinal(value) {
|
||||
this._ensureNoFinalValue();
|
||||
this._finalValue = value;
|
||||
for (const deferred of this._unsatisfiedConsumers) {
|
||||
this._resolveOrRejectDeferred(deferred, value);
|
||||
}
|
||||
this._unsatisfiedConsumers.length = 0;
|
||||
}
|
||||
_ensureNoFinalValue() {
|
||||
if (this._finalValue) {
|
||||
throw new BugIndicatingError('ProducerConsumer: cannot produce after final value has been set');
|
||||
}
|
||||
}
|
||||
_resolveOrRejectDeferred(deferred, value) {
|
||||
if (value.ok) {
|
||||
deferred.complete(value.value);
|
||||
}
|
||||
else {
|
||||
deferred.error(value.error);
|
||||
}
|
||||
}
|
||||
consume() {
|
||||
if (this._unconsumedValues.length > 0 || this._finalValue) {
|
||||
const value = this._unconsumedValues.length > 0 ? this._unconsumedValues.shift() : this._finalValue;
|
||||
if (value.ok) {
|
||||
return Promise.resolve(value.value);
|
||||
}
|
||||
else {
|
||||
return Promise.reject(value.error);
|
||||
}
|
||||
}
|
||||
else {
|
||||
const deferred = new DeferredPromise();
|
||||
this._unsatisfiedConsumers.push(deferred);
|
||||
return deferred.p;
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Important difference to AsyncIterableObject:
|
||||
* If it is iterated two times, the second iterator will not see the values emitted by the first iterator.
|
||||
*/
|
||||
class AsyncIterableProducer {
|
||||
constructor(executor, _onReturn) {
|
||||
this._onReturn = _onReturn;
|
||||
this._producerConsumer = new ProducerConsumer();
|
||||
this._iterator = {
|
||||
next: () => this._producerConsumer.consume(),
|
||||
return: () => {
|
||||
this._onReturn?.();
|
||||
return Promise.resolve({ done: true, value: undefined });
|
||||
},
|
||||
throw: async (e) => {
|
||||
this._finishError(e);
|
||||
return { done: true, value: undefined };
|
||||
},
|
||||
};
|
||||
queueMicrotask(async () => {
|
||||
const p = executor({
|
||||
emitOne: value => this._producerConsumer.produce({ ok: true, value: { done: false, value: value } }),
|
||||
emitMany: values => {
|
||||
for (const value of values) {
|
||||
this._producerConsumer.produce({ ok: true, value: { done: false, value: value } });
|
||||
}
|
||||
},
|
||||
reject: error => this._finishError(error),
|
||||
});
|
||||
if (!this._producerConsumer.hasFinalValue) {
|
||||
try {
|
||||
await p;
|
||||
this._finishOk();
|
||||
}
|
||||
catch (error) {
|
||||
this._finishError(error);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
static fromArray(items) {
|
||||
return new AsyncIterableProducer((writer) => {
|
||||
writer.emitMany(items);
|
||||
});
|
||||
}
|
||||
static fromPromise(promise) {
|
||||
return new AsyncIterableProducer(async (emitter) => {
|
||||
emitter.emitMany(await promise);
|
||||
});
|
||||
}
|
||||
static fromPromisesResolveOrder(promises) {
|
||||
return new AsyncIterableProducer(async (emitter) => {
|
||||
await Promise.all(promises.map(async (p) => emitter.emitOne(await p)));
|
||||
});
|
||||
}
|
||||
static merge(iterables) {
|
||||
return new AsyncIterableProducer(async (emitter) => {
|
||||
await Promise.all(iterables.map(async (iterable) => {
|
||||
for await (const item of iterable) {
|
||||
emitter.emitOne(item);
|
||||
}
|
||||
}));
|
||||
});
|
||||
}
|
||||
static { this.EMPTY = AsyncIterableProducer.fromArray([]); }
|
||||
static map(iterable, mapFn) {
|
||||
return new AsyncIterableProducer(async (emitter) => {
|
||||
for await (const item of iterable) {
|
||||
emitter.emitOne(mapFn(item));
|
||||
}
|
||||
});
|
||||
}
|
||||
map(mapFn) {
|
||||
return AsyncIterableProducer.map(this, mapFn);
|
||||
}
|
||||
static coalesce(iterable) {
|
||||
return AsyncIterableProducer.filter(iterable, item => !!item);
|
||||
}
|
||||
coalesce() {
|
||||
return AsyncIterableProducer.coalesce(this);
|
||||
}
|
||||
static filter(iterable, filterFn) {
|
||||
return new AsyncIterableProducer(async (emitter) => {
|
||||
for await (const item of iterable) {
|
||||
if (filterFn(item)) {
|
||||
emitter.emitOne(item);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
filter(filterFn) {
|
||||
return AsyncIterableProducer.filter(this, filterFn);
|
||||
}
|
||||
_finishOk() {
|
||||
if (!this._producerConsumer.hasFinalValue) {
|
||||
this._producerConsumer.produceFinal({ ok: true, value: { done: true, value: undefined } });
|
||||
}
|
||||
}
|
||||
_finishError(error) {
|
||||
if (!this._producerConsumer.hasFinalValue) {
|
||||
this._producerConsumer.produceFinal({ ok: false, error: error });
|
||||
}
|
||||
// Warning: this can cause to dropped errors.
|
||||
}
|
||||
[Symbol.asyncIterator]() {
|
||||
return this._iterator;
|
||||
}
|
||||
}
|
||||
class CancelableAsyncIterableProducer extends AsyncIterableProducer {
|
||||
constructor(_source, executor) {
|
||||
super(executor);
|
||||
this._source = _source;
|
||||
}
|
||||
cancel() {
|
||||
this._source.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
export { AbstractIdleValue, AsyncIterableProducer, CancelableAsyncIterableProducer, DeferredPromise, Delayer, GlobalIdleValue, IntervalTimer, Promises, RunOnceScheduler, TaskQueue, ThrottledDelayer, Throttler, TimeoutTimer, _runWhenIdle, createCancelableAsyncIterableProducer, createCancelablePromise, disposableTimeout, first, isThenable, raceCancellation, raceCancellationError, runWhenGlobalIdle, timeout };
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
import { Lazy } from './lazy.js';
|
||||
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
const hasBuffer = (typeof Buffer !== 'undefined');
|
||||
new Lazy(() => new Uint8Array(256));
|
||||
let textDecoder;
|
||||
class VSBuffer {
|
||||
/**
|
||||
* When running in a nodejs context, if `actual` is not a nodejs Buffer, the backing store for
|
||||
* the returned `VSBuffer` instance might use a nodejs Buffer allocated from node's Buffer pool,
|
||||
* which is not transferrable.
|
||||
*/
|
||||
static wrap(actual) {
|
||||
if (hasBuffer && !(Buffer.isBuffer(actual))) {
|
||||
// https://nodejs.org/dist/latest-v10.x/docs/api/buffer.html#buffer_class_method_buffer_from_arraybuffer_byteoffset_length
|
||||
// Create a zero-copy Buffer wrapper around the ArrayBuffer pointed to by the Uint8Array
|
||||
actual = Buffer.from(actual.buffer, actual.byteOffset, actual.byteLength);
|
||||
}
|
||||
return new VSBuffer(actual);
|
||||
}
|
||||
constructor(buffer) {
|
||||
this.buffer = buffer;
|
||||
this.byteLength = this.buffer.byteLength;
|
||||
}
|
||||
toString() {
|
||||
if (hasBuffer) {
|
||||
return this.buffer.toString();
|
||||
}
|
||||
else {
|
||||
if (!textDecoder) {
|
||||
textDecoder = new TextDecoder();
|
||||
}
|
||||
return textDecoder.decode(this.buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
function readUInt16LE(source, offset) {
|
||||
return (((source[offset + 0] << 0) >>> 0) |
|
||||
((source[offset + 1] << 8) >>> 0));
|
||||
}
|
||||
function writeUInt16LE(destination, value, offset) {
|
||||
destination[offset + 0] = (value & 0b11111111);
|
||||
value = value >>> 8;
|
||||
destination[offset + 1] = (value & 0b11111111);
|
||||
}
|
||||
function readUInt32BE(source, offset) {
|
||||
return (source[offset] * 2 ** 24
|
||||
+ source[offset + 1] * 2 ** 16
|
||||
+ source[offset + 2] * 2 ** 8
|
||||
+ source[offset + 3]);
|
||||
}
|
||||
function writeUInt32BE(destination, value, offset) {
|
||||
destination[offset + 3] = value;
|
||||
value = value >>> 8;
|
||||
destination[offset + 2] = value;
|
||||
value = value >>> 8;
|
||||
destination[offset + 1] = value;
|
||||
value = value >>> 8;
|
||||
destination[offset] = value;
|
||||
}
|
||||
function readUInt8(source, offset) {
|
||||
return source[offset];
|
||||
}
|
||||
function writeUInt8(destination, value, offset) {
|
||||
destination[offset] = value;
|
||||
}
|
||||
const hexChars = '0123456789abcdef';
|
||||
function encodeHex({ buffer }) {
|
||||
let result = '';
|
||||
for (let i = 0; i < buffer.length; i++) {
|
||||
const byte = buffer[i];
|
||||
result += hexChars[byte >>> 4];
|
||||
result += hexChars[byte & 0x0f];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export { VSBuffer, encodeHex, readUInt16LE, readUInt32BE, readUInt8, writeUInt16LE, writeUInt32BE, writeUInt8 };
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
function identity(t) {
|
||||
return t;
|
||||
}
|
||||
/**
|
||||
* Uses a LRU cache to make a given parametrized function cached.
|
||||
* Caches just the last key/value.
|
||||
*/
|
||||
class LRUCachedFunction {
|
||||
constructor(arg1, arg2) {
|
||||
this.lastCache = undefined;
|
||||
this.lastArgKey = undefined;
|
||||
if (typeof arg1 === 'function') {
|
||||
this._fn = arg1;
|
||||
this._computeKey = identity;
|
||||
}
|
||||
else {
|
||||
this._fn = arg2;
|
||||
this._computeKey = arg1.getCacheKey;
|
||||
}
|
||||
}
|
||||
get(arg) {
|
||||
const key = this._computeKey(arg);
|
||||
if (this.lastArgKey !== key) {
|
||||
this.lastArgKey = key;
|
||||
this.lastCache = this._fn(arg);
|
||||
}
|
||||
return this.lastCache;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Uses an unbounded cache to memoize the results of the given function.
|
||||
*/
|
||||
class CachedFunction {
|
||||
get cachedValues() {
|
||||
return this._map;
|
||||
}
|
||||
constructor(arg1, arg2) {
|
||||
this._map = new Map();
|
||||
this._map2 = new Map();
|
||||
if (typeof arg1 === 'function') {
|
||||
this._fn = arg1;
|
||||
this._computeKey = identity;
|
||||
}
|
||||
else {
|
||||
this._fn = arg2;
|
||||
this._computeKey = arg1.getCacheKey;
|
||||
}
|
||||
}
|
||||
get(arg) {
|
||||
const key = this._computeKey(arg);
|
||||
if (this._map2.has(key)) {
|
||||
return this._map2.get(key);
|
||||
}
|
||||
const value = this._fn(arg);
|
||||
this._map.set(arg, value);
|
||||
this._map2.set(key, value);
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
export { CachedFunction, LRUCachedFunction, identity };
|
||||
+116
@@ -0,0 +1,116 @@
|
||||
import { Event, Emitter } from './event.js';
|
||||
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
const shortcutEvent = Object.freeze(function (callback, context) {
|
||||
const handle = setTimeout(callback.bind(context), 0);
|
||||
return { dispose() { clearTimeout(handle); } };
|
||||
});
|
||||
var CancellationToken;
|
||||
(function (CancellationToken) {
|
||||
function isCancellationToken(thing) {
|
||||
if (thing === CancellationToken.None || thing === CancellationToken.Cancelled) {
|
||||
return true;
|
||||
}
|
||||
if (thing instanceof MutableToken) {
|
||||
return true;
|
||||
}
|
||||
if (!thing || typeof thing !== 'object') {
|
||||
return false;
|
||||
}
|
||||
return typeof thing.isCancellationRequested === 'boolean'
|
||||
&& typeof thing.onCancellationRequested === 'function';
|
||||
}
|
||||
CancellationToken.isCancellationToken = isCancellationToken;
|
||||
CancellationToken.None = Object.freeze({
|
||||
isCancellationRequested: false,
|
||||
onCancellationRequested: Event.None
|
||||
});
|
||||
CancellationToken.Cancelled = Object.freeze({
|
||||
isCancellationRequested: true,
|
||||
onCancellationRequested: shortcutEvent
|
||||
});
|
||||
})(CancellationToken || (CancellationToken = {}));
|
||||
class MutableToken {
|
||||
constructor() {
|
||||
this._isCancelled = false;
|
||||
this._emitter = null;
|
||||
}
|
||||
cancel() {
|
||||
if (!this._isCancelled) {
|
||||
this._isCancelled = true;
|
||||
if (this._emitter) {
|
||||
this._emitter.fire(undefined);
|
||||
this.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
get isCancellationRequested() {
|
||||
return this._isCancelled;
|
||||
}
|
||||
get onCancellationRequested() {
|
||||
if (this._isCancelled) {
|
||||
return shortcutEvent;
|
||||
}
|
||||
if (!this._emitter) {
|
||||
this._emitter = new Emitter();
|
||||
}
|
||||
return this._emitter.event;
|
||||
}
|
||||
dispose() {
|
||||
if (this._emitter) {
|
||||
this._emitter.dispose();
|
||||
this._emitter = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
class CancellationTokenSource {
|
||||
constructor(parent) {
|
||||
this._token = undefined;
|
||||
this._parentListener = undefined;
|
||||
this._parentListener = parent && parent.onCancellationRequested(this.cancel, this);
|
||||
}
|
||||
get token() {
|
||||
if (!this._token) {
|
||||
// be lazy and create the token only when
|
||||
// actually needed
|
||||
this._token = new MutableToken();
|
||||
}
|
||||
return this._token;
|
||||
}
|
||||
cancel() {
|
||||
if (!this._token) {
|
||||
// save an object by returning the default
|
||||
// cancelled token when cancellation happens
|
||||
// before someone asks for the token
|
||||
this._token = CancellationToken.Cancelled;
|
||||
}
|
||||
else if (this._token instanceof MutableToken) {
|
||||
// actually cancel
|
||||
this._token.cancel();
|
||||
}
|
||||
}
|
||||
dispose(cancel = false) {
|
||||
if (cancel) {
|
||||
this.cancel();
|
||||
}
|
||||
this._parentListener?.dispose();
|
||||
if (!this._token) {
|
||||
// ensure to initialize with an empty token if we had none
|
||||
this._token = CancellationToken.None;
|
||||
}
|
||||
else if (this._token instanceof MutableToken) {
|
||||
// actually dispose
|
||||
this._token.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
function cancelOnDispose(store) {
|
||||
const source = new CancellationTokenSource();
|
||||
store.add({ dispose() { source.cancel(); } });
|
||||
return source.token;
|
||||
}
|
||||
|
||||
export { CancellationToken, CancellationTokenSource, cancelOnDispose };
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
import { register } from './codiconsUtil.js';
|
||||
import { codiconsLibrary } from './codiconsLibrary.js';
|
||||
|
||||
/**
|
||||
* Derived icons, that could become separate icons.
|
||||
* These mappings should be moved into the mapping file in the vscode-codicons repo at some point.
|
||||
*/
|
||||
const codiconsDerived = {
|
||||
dialogError: register('dialog-error', 'error'),
|
||||
dialogWarning: register('dialog-warning', 'warning'),
|
||||
dialogInfo: register('dialog-info', 'info'),
|
||||
dialogClose: register('dialog-close', 'close'),
|
||||
treeItemExpanded: register('tree-item-expanded', 'chevron-down'), // collapsed is done with rotation
|
||||
treeFilterOnTypeOn: register('tree-filter-on-type-on', 'list-filter'),
|
||||
treeFilterOnTypeOff: register('tree-filter-on-type-off', 'list-selection'),
|
||||
treeFilterClear: register('tree-filter-clear', 'close'),
|
||||
treeItemLoading: register('tree-item-loading', 'loading'),
|
||||
menuSelection: register('menu-selection', 'check'),
|
||||
menuSubmenu: register('menu-submenu', 'chevron-right'),
|
||||
menuBarMore: register('menubar-more', 'more'),
|
||||
scrollbarButtonLeft: register('scrollbar-button-left', 'triangle-left'),
|
||||
scrollbarButtonRight: register('scrollbar-button-right', 'triangle-right'),
|
||||
scrollbarButtonUp: register('scrollbar-button-up', 'triangle-up'),
|
||||
scrollbarButtonDown: register('scrollbar-button-down', 'triangle-down'),
|
||||
toolBarMore: register('toolbar-more', 'more'),
|
||||
quickInputBack: register('quick-input-back', 'arrow-left'),
|
||||
dropDownButton: register('drop-down-button', 0xeab4),
|
||||
symbolCustomColor: register('symbol-customcolor', 0xeb5c),
|
||||
exportIcon: register('export', 0xebac),
|
||||
workspaceUnspecified: register('workspace-unspecified', 0xebc3),
|
||||
newLine: register('newline', 0xebea),
|
||||
thumbsDownFilled: register('thumbsdown-filled', 0xec13),
|
||||
thumbsUpFilled: register('thumbsup-filled', 0xec14),
|
||||
gitFetch: register('git-fetch', 0xec1d),
|
||||
lightbulbSparkleAutofix: register('lightbulb-sparkle-autofix', 0xec1f),
|
||||
debugBreakpointPending: register('debug-breakpoint-pending', 0xebd9),
|
||||
};
|
||||
/**
|
||||
* The Codicon library is a set of default icons that are built-in in VS Code.
|
||||
*
|
||||
* In the product (outside of base) Codicons should only be used as defaults. In order to have all icons in VS Code
|
||||
* themeable, component should define new, UI component specific icons using `iconRegistry.registerIcon`.
|
||||
* In that call a Codicon can be named as default.
|
||||
*/
|
||||
const Codicon = {
|
||||
...codiconsLibrary,
|
||||
...codiconsDerived
|
||||
};
|
||||
|
||||
export { Codicon, codiconsDerived };
|
||||
+642
@@ -0,0 +1,642 @@
|
||||
import { register } from './codiconsUtil.js';
|
||||
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
// This file is automatically generated by (microsoft/vscode-codicons)/scripts/export-to-ts.js
|
||||
// Please don't edit it, as your changes will be overwritten.
|
||||
// Instead, add mappings to codiconsDerived in codicons.ts.
|
||||
const codiconsLibrary = {
|
||||
add: register('add', 0xea60),
|
||||
plus: register('plus', 0xea60),
|
||||
gistNew: register('gist-new', 0xea60),
|
||||
repoCreate: register('repo-create', 0xea60),
|
||||
lightbulb: register('lightbulb', 0xea61),
|
||||
lightBulb: register('light-bulb', 0xea61),
|
||||
repo: register('repo', 0xea62),
|
||||
repoDelete: register('repo-delete', 0xea62),
|
||||
gistFork: register('gist-fork', 0xea63),
|
||||
repoForked: register('repo-forked', 0xea63),
|
||||
gitPullRequest: register('git-pull-request', 0xea64),
|
||||
gitPullRequestAbandoned: register('git-pull-request-abandoned', 0xea64),
|
||||
recordKeys: register('record-keys', 0xea65),
|
||||
keyboard: register('keyboard', 0xea65),
|
||||
tag: register('tag', 0xea66),
|
||||
gitPullRequestLabel: register('git-pull-request-label', 0xea66),
|
||||
tagAdd: register('tag-add', 0xea66),
|
||||
tagRemove: register('tag-remove', 0xea66),
|
||||
person: register('person', 0xea67),
|
||||
personFollow: register('person-follow', 0xea67),
|
||||
personOutline: register('person-outline', 0xea67),
|
||||
personFilled: register('person-filled', 0xea67),
|
||||
sourceControl: register('source-control', 0xea68),
|
||||
mirror: register('mirror', 0xea69),
|
||||
mirrorPublic: register('mirror-public', 0xea69),
|
||||
star: register('star', 0xea6a),
|
||||
starAdd: register('star-add', 0xea6a),
|
||||
starDelete: register('star-delete', 0xea6a),
|
||||
starEmpty: register('star-empty', 0xea6a),
|
||||
comment: register('comment', 0xea6b),
|
||||
commentAdd: register('comment-add', 0xea6b),
|
||||
alert: register('alert', 0xea6c),
|
||||
warning: register('warning', 0xea6c),
|
||||
search: register('search', 0xea6d),
|
||||
searchSave: register('search-save', 0xea6d),
|
||||
logOut: register('log-out', 0xea6e),
|
||||
signOut: register('sign-out', 0xea6e),
|
||||
logIn: register('log-in', 0xea6f),
|
||||
signIn: register('sign-in', 0xea6f),
|
||||
eye: register('eye', 0xea70),
|
||||
eyeUnwatch: register('eye-unwatch', 0xea70),
|
||||
eyeWatch: register('eye-watch', 0xea70),
|
||||
circleFilled: register('circle-filled', 0xea71),
|
||||
primitiveDot: register('primitive-dot', 0xea71),
|
||||
closeDirty: register('close-dirty', 0xea71),
|
||||
debugBreakpoint: register('debug-breakpoint', 0xea71),
|
||||
debugBreakpointDisabled: register('debug-breakpoint-disabled', 0xea71),
|
||||
debugHint: register('debug-hint', 0xea71),
|
||||
terminalDecorationSuccess: register('terminal-decoration-success', 0xea71),
|
||||
primitiveSquare: register('primitive-square', 0xea72),
|
||||
edit: register('edit', 0xea73),
|
||||
pencil: register('pencil', 0xea73),
|
||||
info: register('info', 0xea74),
|
||||
issueOpened: register('issue-opened', 0xea74),
|
||||
gistPrivate: register('gist-private', 0xea75),
|
||||
gitForkPrivate: register('git-fork-private', 0xea75),
|
||||
lock: register('lock', 0xea75),
|
||||
mirrorPrivate: register('mirror-private', 0xea75),
|
||||
close: register('close', 0xea76),
|
||||
removeClose: register('remove-close', 0xea76),
|
||||
x: register('x', 0xea76),
|
||||
repoSync: register('repo-sync', 0xea77),
|
||||
sync: register('sync', 0xea77),
|
||||
clone: register('clone', 0xea78),
|
||||
desktopDownload: register('desktop-download', 0xea78),
|
||||
beaker: register('beaker', 0xea79),
|
||||
microscope: register('microscope', 0xea79),
|
||||
vm: register('vm', 0xea7a),
|
||||
deviceDesktop: register('device-desktop', 0xea7a),
|
||||
file: register('file', 0xea7b),
|
||||
more: register('more', 0xea7c),
|
||||
ellipsis: register('ellipsis', 0xea7c),
|
||||
kebabHorizontal: register('kebab-horizontal', 0xea7c),
|
||||
mailReply: register('mail-reply', 0xea7d),
|
||||
reply: register('reply', 0xea7d),
|
||||
organization: register('organization', 0xea7e),
|
||||
organizationFilled: register('organization-filled', 0xea7e),
|
||||
organizationOutline: register('organization-outline', 0xea7e),
|
||||
newFile: register('new-file', 0xea7f),
|
||||
fileAdd: register('file-add', 0xea7f),
|
||||
newFolder: register('new-folder', 0xea80),
|
||||
fileDirectoryCreate: register('file-directory-create', 0xea80),
|
||||
trash: register('trash', 0xea81),
|
||||
trashcan: register('trashcan', 0xea81),
|
||||
history: register('history', 0xea82),
|
||||
clock: register('clock', 0xea82),
|
||||
folder: register('folder', 0xea83),
|
||||
fileDirectory: register('file-directory', 0xea83),
|
||||
symbolFolder: register('symbol-folder', 0xea83),
|
||||
logoGithub: register('logo-github', 0xea84),
|
||||
markGithub: register('mark-github', 0xea84),
|
||||
github: register('github', 0xea84),
|
||||
terminal: register('terminal', 0xea85),
|
||||
console: register('console', 0xea85),
|
||||
repl: register('repl', 0xea85),
|
||||
zap: register('zap', 0xea86),
|
||||
symbolEvent: register('symbol-event', 0xea86),
|
||||
error: register('error', 0xea87),
|
||||
stop: register('stop', 0xea87),
|
||||
variable: register('variable', 0xea88),
|
||||
symbolVariable: register('symbol-variable', 0xea88),
|
||||
array: register('array', 0xea8a),
|
||||
symbolArray: register('symbol-array', 0xea8a),
|
||||
symbolModule: register('symbol-module', 0xea8b),
|
||||
symbolPackage: register('symbol-package', 0xea8b),
|
||||
symbolNamespace: register('symbol-namespace', 0xea8b),
|
||||
symbolObject: register('symbol-object', 0xea8b),
|
||||
symbolMethod: register('symbol-method', 0xea8c),
|
||||
symbolFunction: register('symbol-function', 0xea8c),
|
||||
symbolConstructor: register('symbol-constructor', 0xea8c),
|
||||
symbolBoolean: register('symbol-boolean', 0xea8f),
|
||||
symbolNull: register('symbol-null', 0xea8f),
|
||||
symbolNumeric: register('symbol-numeric', 0xea90),
|
||||
symbolNumber: register('symbol-number', 0xea90),
|
||||
symbolStructure: register('symbol-structure', 0xea91),
|
||||
symbolStruct: register('symbol-struct', 0xea91),
|
||||
symbolParameter: register('symbol-parameter', 0xea92),
|
||||
symbolTypeParameter: register('symbol-type-parameter', 0xea92),
|
||||
symbolKey: register('symbol-key', 0xea93),
|
||||
symbolText: register('symbol-text', 0xea93),
|
||||
symbolReference: register('symbol-reference', 0xea94),
|
||||
goToFile: register('go-to-file', 0xea94),
|
||||
symbolEnum: register('symbol-enum', 0xea95),
|
||||
symbolValue: register('symbol-value', 0xea95),
|
||||
symbolRuler: register('symbol-ruler', 0xea96),
|
||||
symbolUnit: register('symbol-unit', 0xea96),
|
||||
activateBreakpoints: register('activate-breakpoints', 0xea97),
|
||||
archive: register('archive', 0xea98),
|
||||
arrowBoth: register('arrow-both', 0xea99),
|
||||
arrowDown: register('arrow-down', 0xea9a),
|
||||
arrowLeft: register('arrow-left', 0xea9b),
|
||||
arrowRight: register('arrow-right', 0xea9c),
|
||||
arrowSmallDown: register('arrow-small-down', 0xea9d),
|
||||
arrowSmallLeft: register('arrow-small-left', 0xea9e),
|
||||
arrowSmallRight: register('arrow-small-right', 0xea9f),
|
||||
arrowSmallUp: register('arrow-small-up', 0xeaa0),
|
||||
arrowUp: register('arrow-up', 0xeaa1),
|
||||
bell: register('bell', 0xeaa2),
|
||||
bold: register('bold', 0xeaa3),
|
||||
book: register('book', 0xeaa4),
|
||||
bookmark: register('bookmark', 0xeaa5),
|
||||
debugBreakpointConditionalUnverified: register('debug-breakpoint-conditional-unverified', 0xeaa6),
|
||||
debugBreakpointConditional: register('debug-breakpoint-conditional', 0xeaa7),
|
||||
debugBreakpointConditionalDisabled: register('debug-breakpoint-conditional-disabled', 0xeaa7),
|
||||
debugBreakpointDataUnverified: register('debug-breakpoint-data-unverified', 0xeaa8),
|
||||
debugBreakpointData: register('debug-breakpoint-data', 0xeaa9),
|
||||
debugBreakpointDataDisabled: register('debug-breakpoint-data-disabled', 0xeaa9),
|
||||
debugBreakpointLogUnverified: register('debug-breakpoint-log-unverified', 0xeaaa),
|
||||
debugBreakpointLog: register('debug-breakpoint-log', 0xeaab),
|
||||
debugBreakpointLogDisabled: register('debug-breakpoint-log-disabled', 0xeaab),
|
||||
briefcase: register('briefcase', 0xeaac),
|
||||
broadcast: register('broadcast', 0xeaad),
|
||||
browser: register('browser', 0xeaae),
|
||||
bug: register('bug', 0xeaaf),
|
||||
calendar: register('calendar', 0xeab0),
|
||||
caseSensitive: register('case-sensitive', 0xeab1),
|
||||
check: register('check', 0xeab2),
|
||||
checklist: register('checklist', 0xeab3),
|
||||
chevronDown: register('chevron-down', 0xeab4),
|
||||
chevronLeft: register('chevron-left', 0xeab5),
|
||||
chevronRight: register('chevron-right', 0xeab6),
|
||||
chevronUp: register('chevron-up', 0xeab7),
|
||||
chromeClose: register('chrome-close', 0xeab8),
|
||||
chromeMaximize: register('chrome-maximize', 0xeab9),
|
||||
chromeMinimize: register('chrome-minimize', 0xeaba),
|
||||
chromeRestore: register('chrome-restore', 0xeabb),
|
||||
circleOutline: register('circle-outline', 0xeabc),
|
||||
circle: register('circle', 0xeabc),
|
||||
debugBreakpointUnverified: register('debug-breakpoint-unverified', 0xeabc),
|
||||
terminalDecorationIncomplete: register('terminal-decoration-incomplete', 0xeabc),
|
||||
circleSlash: register('circle-slash', 0xeabd),
|
||||
circuitBoard: register('circuit-board', 0xeabe),
|
||||
clearAll: register('clear-all', 0xeabf),
|
||||
clippy: register('clippy', 0xeac0),
|
||||
closeAll: register('close-all', 0xeac1),
|
||||
cloudDownload: register('cloud-download', 0xeac2),
|
||||
cloudUpload: register('cloud-upload', 0xeac3),
|
||||
code: register('code', 0xeac4),
|
||||
collapseAll: register('collapse-all', 0xeac5),
|
||||
colorMode: register('color-mode', 0xeac6),
|
||||
commentDiscussion: register('comment-discussion', 0xeac7),
|
||||
creditCard: register('credit-card', 0xeac9),
|
||||
dash: register('dash', 0xeacc),
|
||||
dashboard: register('dashboard', 0xeacd),
|
||||
database: register('database', 0xeace),
|
||||
debugContinue: register('debug-continue', 0xeacf),
|
||||
debugDisconnect: register('debug-disconnect', 0xead0),
|
||||
debugPause: register('debug-pause', 0xead1),
|
||||
debugRestart: register('debug-restart', 0xead2),
|
||||
debugStart: register('debug-start', 0xead3),
|
||||
debugStepInto: register('debug-step-into', 0xead4),
|
||||
debugStepOut: register('debug-step-out', 0xead5),
|
||||
debugStepOver: register('debug-step-over', 0xead6),
|
||||
debugStop: register('debug-stop', 0xead7),
|
||||
debug: register('debug', 0xead8),
|
||||
deviceCameraVideo: register('device-camera-video', 0xead9),
|
||||
deviceCamera: register('device-camera', 0xeada),
|
||||
deviceMobile: register('device-mobile', 0xeadb),
|
||||
diffAdded: register('diff-added', 0xeadc),
|
||||
diffIgnored: register('diff-ignored', 0xeadd),
|
||||
diffModified: register('diff-modified', 0xeade),
|
||||
diffRemoved: register('diff-removed', 0xeadf),
|
||||
diffRenamed: register('diff-renamed', 0xeae0),
|
||||
diff: register('diff', 0xeae1),
|
||||
diffSidebyside: register('diff-sidebyside', 0xeae1),
|
||||
discard: register('discard', 0xeae2),
|
||||
editorLayout: register('editor-layout', 0xeae3),
|
||||
emptyWindow: register('empty-window', 0xeae4),
|
||||
exclude: register('exclude', 0xeae5),
|
||||
extensions: register('extensions', 0xeae6),
|
||||
eyeClosed: register('eye-closed', 0xeae7),
|
||||
fileBinary: register('file-binary', 0xeae8),
|
||||
fileCode: register('file-code', 0xeae9),
|
||||
fileMedia: register('file-media', 0xeaea),
|
||||
filePdf: register('file-pdf', 0xeaeb),
|
||||
fileSubmodule: register('file-submodule', 0xeaec),
|
||||
fileSymlinkDirectory: register('file-symlink-directory', 0xeaed),
|
||||
fileSymlinkFile: register('file-symlink-file', 0xeaee),
|
||||
fileZip: register('file-zip', 0xeaef),
|
||||
files: register('files', 0xeaf0),
|
||||
filter: register('filter', 0xeaf1),
|
||||
flame: register('flame', 0xeaf2),
|
||||
foldDown: register('fold-down', 0xeaf3),
|
||||
foldUp: register('fold-up', 0xeaf4),
|
||||
fold: register('fold', 0xeaf5),
|
||||
folderActive: register('folder-active', 0xeaf6),
|
||||
folderOpened: register('folder-opened', 0xeaf7),
|
||||
gear: register('gear', 0xeaf8),
|
||||
gift: register('gift', 0xeaf9),
|
||||
gistSecret: register('gist-secret', 0xeafa),
|
||||
gist: register('gist', 0xeafb),
|
||||
gitCommit: register('git-commit', 0xeafc),
|
||||
gitCompare: register('git-compare', 0xeafd),
|
||||
compareChanges: register('compare-changes', 0xeafd),
|
||||
gitMerge: register('git-merge', 0xeafe),
|
||||
githubAction: register('github-action', 0xeaff),
|
||||
githubAlt: register('github-alt', 0xeb00),
|
||||
globe: register('globe', 0xeb01),
|
||||
grabber: register('grabber', 0xeb02),
|
||||
graph: register('graph', 0xeb03),
|
||||
gripper: register('gripper', 0xeb04),
|
||||
heart: register('heart', 0xeb05),
|
||||
home: register('home', 0xeb06),
|
||||
horizontalRule: register('horizontal-rule', 0xeb07),
|
||||
hubot: register('hubot', 0xeb08),
|
||||
inbox: register('inbox', 0xeb09),
|
||||
issueReopened: register('issue-reopened', 0xeb0b),
|
||||
issues: register('issues', 0xeb0c),
|
||||
italic: register('italic', 0xeb0d),
|
||||
jersey: register('jersey', 0xeb0e),
|
||||
json: register('json', 0xeb0f),
|
||||
kebabVertical: register('kebab-vertical', 0xeb10),
|
||||
key: register('key', 0xeb11),
|
||||
law: register('law', 0xeb12),
|
||||
lightbulbAutofix: register('lightbulb-autofix', 0xeb13),
|
||||
linkExternal: register('link-external', 0xeb14),
|
||||
link: register('link', 0xeb15),
|
||||
listOrdered: register('list-ordered', 0xeb16),
|
||||
listUnordered: register('list-unordered', 0xeb17),
|
||||
liveShare: register('live-share', 0xeb18),
|
||||
loading: register('loading', 0xeb19),
|
||||
location: register('location', 0xeb1a),
|
||||
mailRead: register('mail-read', 0xeb1b),
|
||||
mail: register('mail', 0xeb1c),
|
||||
markdown: register('markdown', 0xeb1d),
|
||||
megaphone: register('megaphone', 0xeb1e),
|
||||
mention: register('mention', 0xeb1f),
|
||||
milestone: register('milestone', 0xeb20),
|
||||
gitPullRequestMilestone: register('git-pull-request-milestone', 0xeb20),
|
||||
mortarBoard: register('mortar-board', 0xeb21),
|
||||
move: register('move', 0xeb22),
|
||||
multipleWindows: register('multiple-windows', 0xeb23),
|
||||
mute: register('mute', 0xeb24),
|
||||
noNewline: register('no-newline', 0xeb25),
|
||||
note: register('note', 0xeb26),
|
||||
octoface: register('octoface', 0xeb27),
|
||||
openPreview: register('open-preview', 0xeb28),
|
||||
package: register('package', 0xeb29),
|
||||
paintcan: register('paintcan', 0xeb2a),
|
||||
pin: register('pin', 0xeb2b),
|
||||
play: register('play', 0xeb2c),
|
||||
run: register('run', 0xeb2c),
|
||||
plug: register('plug', 0xeb2d),
|
||||
preserveCase: register('preserve-case', 0xeb2e),
|
||||
preview: register('preview', 0xeb2f),
|
||||
project: register('project', 0xeb30),
|
||||
pulse: register('pulse', 0xeb31),
|
||||
question: register('question', 0xeb32),
|
||||
quote: register('quote', 0xeb33),
|
||||
radioTower: register('radio-tower', 0xeb34),
|
||||
reactions: register('reactions', 0xeb35),
|
||||
references: register('references', 0xeb36),
|
||||
refresh: register('refresh', 0xeb37),
|
||||
regex: register('regex', 0xeb38),
|
||||
remoteExplorer: register('remote-explorer', 0xeb39),
|
||||
remote: register('remote', 0xeb3a),
|
||||
remove: register('remove', 0xeb3b),
|
||||
replaceAll: register('replace-all', 0xeb3c),
|
||||
replace: register('replace', 0xeb3d),
|
||||
repoClone: register('repo-clone', 0xeb3e),
|
||||
repoForcePush: register('repo-force-push', 0xeb3f),
|
||||
repoPull: register('repo-pull', 0xeb40),
|
||||
repoPush: register('repo-push', 0xeb41),
|
||||
report: register('report', 0xeb42),
|
||||
requestChanges: register('request-changes', 0xeb43),
|
||||
rocket: register('rocket', 0xeb44),
|
||||
rootFolderOpened: register('root-folder-opened', 0xeb45),
|
||||
rootFolder: register('root-folder', 0xeb46),
|
||||
rss: register('rss', 0xeb47),
|
||||
ruby: register('ruby', 0xeb48),
|
||||
saveAll: register('save-all', 0xeb49),
|
||||
saveAs: register('save-as', 0xeb4a),
|
||||
save: register('save', 0xeb4b),
|
||||
screenFull: register('screen-full', 0xeb4c),
|
||||
screenNormal: register('screen-normal', 0xeb4d),
|
||||
searchStop: register('search-stop', 0xeb4e),
|
||||
server: register('server', 0xeb50),
|
||||
settingsGear: register('settings-gear', 0xeb51),
|
||||
settings: register('settings', 0xeb52),
|
||||
shield: register('shield', 0xeb53),
|
||||
smiley: register('smiley', 0xeb54),
|
||||
sortPrecedence: register('sort-precedence', 0xeb55),
|
||||
splitHorizontal: register('split-horizontal', 0xeb56),
|
||||
splitVertical: register('split-vertical', 0xeb57),
|
||||
squirrel: register('squirrel', 0xeb58),
|
||||
starFull: register('star-full', 0xeb59),
|
||||
starHalf: register('star-half', 0xeb5a),
|
||||
symbolClass: register('symbol-class', 0xeb5b),
|
||||
symbolColor: register('symbol-color', 0xeb5c),
|
||||
symbolConstant: register('symbol-constant', 0xeb5d),
|
||||
symbolEnumMember: register('symbol-enum-member', 0xeb5e),
|
||||
symbolField: register('symbol-field', 0xeb5f),
|
||||
symbolFile: register('symbol-file', 0xeb60),
|
||||
symbolInterface: register('symbol-interface', 0xeb61),
|
||||
symbolKeyword: register('symbol-keyword', 0xeb62),
|
||||
symbolMisc: register('symbol-misc', 0xeb63),
|
||||
symbolOperator: register('symbol-operator', 0xeb64),
|
||||
symbolProperty: register('symbol-property', 0xeb65),
|
||||
wrench: register('wrench', 0xeb65),
|
||||
wrenchSubaction: register('wrench-subaction', 0xeb65),
|
||||
symbolSnippet: register('symbol-snippet', 0xeb66),
|
||||
tasklist: register('tasklist', 0xeb67),
|
||||
telescope: register('telescope', 0xeb68),
|
||||
textSize: register('text-size', 0xeb69),
|
||||
threeBars: register('three-bars', 0xeb6a),
|
||||
thumbsdown: register('thumbsdown', 0xeb6b),
|
||||
thumbsup: register('thumbsup', 0xeb6c),
|
||||
tools: register('tools', 0xeb6d),
|
||||
triangleDown: register('triangle-down', 0xeb6e),
|
||||
triangleLeft: register('triangle-left', 0xeb6f),
|
||||
triangleRight: register('triangle-right', 0xeb70),
|
||||
triangleUp: register('triangle-up', 0xeb71),
|
||||
twitter: register('twitter', 0xeb72),
|
||||
unfold: register('unfold', 0xeb73),
|
||||
unlock: register('unlock', 0xeb74),
|
||||
unmute: register('unmute', 0xeb75),
|
||||
unverified: register('unverified', 0xeb76),
|
||||
verified: register('verified', 0xeb77),
|
||||
versions: register('versions', 0xeb78),
|
||||
vmActive: register('vm-active', 0xeb79),
|
||||
vmOutline: register('vm-outline', 0xeb7a),
|
||||
vmRunning: register('vm-running', 0xeb7b),
|
||||
watch: register('watch', 0xeb7c),
|
||||
whitespace: register('whitespace', 0xeb7d),
|
||||
wholeWord: register('whole-word', 0xeb7e),
|
||||
window: register('window', 0xeb7f),
|
||||
wordWrap: register('word-wrap', 0xeb80),
|
||||
zoomIn: register('zoom-in', 0xeb81),
|
||||
zoomOut: register('zoom-out', 0xeb82),
|
||||
listFilter: register('list-filter', 0xeb83),
|
||||
listFlat: register('list-flat', 0xeb84),
|
||||
listSelection: register('list-selection', 0xeb85),
|
||||
selection: register('selection', 0xeb85),
|
||||
listTree: register('list-tree', 0xeb86),
|
||||
debugBreakpointFunctionUnverified: register('debug-breakpoint-function-unverified', 0xeb87),
|
||||
debugBreakpointFunction: register('debug-breakpoint-function', 0xeb88),
|
||||
debugBreakpointFunctionDisabled: register('debug-breakpoint-function-disabled', 0xeb88),
|
||||
debugStackframeActive: register('debug-stackframe-active', 0xeb89),
|
||||
circleSmallFilled: register('circle-small-filled', 0xeb8a),
|
||||
debugStackframeDot: register('debug-stackframe-dot', 0xeb8a),
|
||||
terminalDecorationMark: register('terminal-decoration-mark', 0xeb8a),
|
||||
debugStackframe: register('debug-stackframe', 0xeb8b),
|
||||
debugStackframeFocused: register('debug-stackframe-focused', 0xeb8b),
|
||||
debugBreakpointUnsupported: register('debug-breakpoint-unsupported', 0xeb8c),
|
||||
symbolString: register('symbol-string', 0xeb8d),
|
||||
debugReverseContinue: register('debug-reverse-continue', 0xeb8e),
|
||||
debugStepBack: register('debug-step-back', 0xeb8f),
|
||||
debugRestartFrame: register('debug-restart-frame', 0xeb90),
|
||||
debugAlt: register('debug-alt', 0xeb91),
|
||||
callIncoming: register('call-incoming', 0xeb92),
|
||||
callOutgoing: register('call-outgoing', 0xeb93),
|
||||
menu: register('menu', 0xeb94),
|
||||
expandAll: register('expand-all', 0xeb95),
|
||||
feedback: register('feedback', 0xeb96),
|
||||
gitPullRequestReviewer: register('git-pull-request-reviewer', 0xeb96),
|
||||
groupByRefType: register('group-by-ref-type', 0xeb97),
|
||||
ungroupByRefType: register('ungroup-by-ref-type', 0xeb98),
|
||||
account: register('account', 0xeb99),
|
||||
gitPullRequestAssignee: register('git-pull-request-assignee', 0xeb99),
|
||||
bellDot: register('bell-dot', 0xeb9a),
|
||||
debugConsole: register('debug-console', 0xeb9b),
|
||||
library: register('library', 0xeb9c),
|
||||
output: register('output', 0xeb9d),
|
||||
runAll: register('run-all', 0xeb9e),
|
||||
syncIgnored: register('sync-ignored', 0xeb9f),
|
||||
pinned: register('pinned', 0xeba0),
|
||||
githubInverted: register('github-inverted', 0xeba1),
|
||||
serverProcess: register('server-process', 0xeba2),
|
||||
serverEnvironment: register('server-environment', 0xeba3),
|
||||
pass: register('pass', 0xeba4),
|
||||
issueClosed: register('issue-closed', 0xeba4),
|
||||
stopCircle: register('stop-circle', 0xeba5),
|
||||
playCircle: register('play-circle', 0xeba6),
|
||||
record: register('record', 0xeba7),
|
||||
debugAltSmall: register('debug-alt-small', 0xeba8),
|
||||
vmConnect: register('vm-connect', 0xeba9),
|
||||
cloud: register('cloud', 0xebaa),
|
||||
merge: register('merge', 0xebab),
|
||||
export: register('export', 0xebac),
|
||||
graphLeft: register('graph-left', 0xebad),
|
||||
magnet: register('magnet', 0xebae),
|
||||
notebook: register('notebook', 0xebaf),
|
||||
redo: register('redo', 0xebb0),
|
||||
checkAll: register('check-all', 0xebb1),
|
||||
pinnedDirty: register('pinned-dirty', 0xebb2),
|
||||
passFilled: register('pass-filled', 0xebb3),
|
||||
circleLargeFilled: register('circle-large-filled', 0xebb4),
|
||||
circleLarge: register('circle-large', 0xebb5),
|
||||
circleLargeOutline: register('circle-large-outline', 0xebb5),
|
||||
combine: register('combine', 0xebb6),
|
||||
gather: register('gather', 0xebb6),
|
||||
table: register('table', 0xebb7),
|
||||
variableGroup: register('variable-group', 0xebb8),
|
||||
typeHierarchy: register('type-hierarchy', 0xebb9),
|
||||
typeHierarchySub: register('type-hierarchy-sub', 0xebba),
|
||||
typeHierarchySuper: register('type-hierarchy-super', 0xebbb),
|
||||
gitPullRequestCreate: register('git-pull-request-create', 0xebbc),
|
||||
runAbove: register('run-above', 0xebbd),
|
||||
runBelow: register('run-below', 0xebbe),
|
||||
notebookTemplate: register('notebook-template', 0xebbf),
|
||||
debugRerun: register('debug-rerun', 0xebc0),
|
||||
workspaceTrusted: register('workspace-trusted', 0xebc1),
|
||||
workspaceUntrusted: register('workspace-untrusted', 0xebc2),
|
||||
workspaceUnknown: register('workspace-unknown', 0xebc3),
|
||||
terminalCmd: register('terminal-cmd', 0xebc4),
|
||||
terminalDebian: register('terminal-debian', 0xebc5),
|
||||
terminalLinux: register('terminal-linux', 0xebc6),
|
||||
terminalPowershell: register('terminal-powershell', 0xebc7),
|
||||
terminalTmux: register('terminal-tmux', 0xebc8),
|
||||
terminalUbuntu: register('terminal-ubuntu', 0xebc9),
|
||||
terminalBash: register('terminal-bash', 0xebca),
|
||||
arrowSwap: register('arrow-swap', 0xebcb),
|
||||
copy: register('copy', 0xebcc),
|
||||
personAdd: register('person-add', 0xebcd),
|
||||
filterFilled: register('filter-filled', 0xebce),
|
||||
wand: register('wand', 0xebcf),
|
||||
debugLineByLine: register('debug-line-by-line', 0xebd0),
|
||||
inspect: register('inspect', 0xebd1),
|
||||
layers: register('layers', 0xebd2),
|
||||
layersDot: register('layers-dot', 0xebd3),
|
||||
layersActive: register('layers-active', 0xebd4),
|
||||
compass: register('compass', 0xebd5),
|
||||
compassDot: register('compass-dot', 0xebd6),
|
||||
compassActive: register('compass-active', 0xebd7),
|
||||
azure: register('azure', 0xebd8),
|
||||
issueDraft: register('issue-draft', 0xebd9),
|
||||
gitPullRequestClosed: register('git-pull-request-closed', 0xebda),
|
||||
gitPullRequestDraft: register('git-pull-request-draft', 0xebdb),
|
||||
debugAll: register('debug-all', 0xebdc),
|
||||
debugCoverage: register('debug-coverage', 0xebdd),
|
||||
runErrors: register('run-errors', 0xebde),
|
||||
folderLibrary: register('folder-library', 0xebdf),
|
||||
debugContinueSmall: register('debug-continue-small', 0xebe0),
|
||||
beakerStop: register('beaker-stop', 0xebe1),
|
||||
graphLine: register('graph-line', 0xebe2),
|
||||
graphScatter: register('graph-scatter', 0xebe3),
|
||||
pieChart: register('pie-chart', 0xebe4),
|
||||
bracket: register('bracket', 0xeb0f),
|
||||
bracketDot: register('bracket-dot', 0xebe5),
|
||||
bracketError: register('bracket-error', 0xebe6),
|
||||
lockSmall: register('lock-small', 0xebe7),
|
||||
azureDevops: register('azure-devops', 0xebe8),
|
||||
verifiedFilled: register('verified-filled', 0xebe9),
|
||||
newline: register('newline', 0xebea),
|
||||
layout: register('layout', 0xebeb),
|
||||
layoutActivitybarLeft: register('layout-activitybar-left', 0xebec),
|
||||
layoutActivitybarRight: register('layout-activitybar-right', 0xebed),
|
||||
layoutPanelLeft: register('layout-panel-left', 0xebee),
|
||||
layoutPanelCenter: register('layout-panel-center', 0xebef),
|
||||
layoutPanelJustify: register('layout-panel-justify', 0xebf0),
|
||||
layoutPanelRight: register('layout-panel-right', 0xebf1),
|
||||
layoutPanel: register('layout-panel', 0xebf2),
|
||||
layoutSidebarLeft: register('layout-sidebar-left', 0xebf3),
|
||||
layoutSidebarRight: register('layout-sidebar-right', 0xebf4),
|
||||
layoutStatusbar: register('layout-statusbar', 0xebf5),
|
||||
layoutMenubar: register('layout-menubar', 0xebf6),
|
||||
layoutCentered: register('layout-centered', 0xebf7),
|
||||
target: register('target', 0xebf8),
|
||||
indent: register('indent', 0xebf9),
|
||||
recordSmall: register('record-small', 0xebfa),
|
||||
errorSmall: register('error-small', 0xebfb),
|
||||
terminalDecorationError: register('terminal-decoration-error', 0xebfb),
|
||||
arrowCircleDown: register('arrow-circle-down', 0xebfc),
|
||||
arrowCircleLeft: register('arrow-circle-left', 0xebfd),
|
||||
arrowCircleRight: register('arrow-circle-right', 0xebfe),
|
||||
arrowCircleUp: register('arrow-circle-up', 0xebff),
|
||||
layoutSidebarRightOff: register('layout-sidebar-right-off', 0xec00),
|
||||
layoutPanelOff: register('layout-panel-off', 0xec01),
|
||||
layoutSidebarLeftOff: register('layout-sidebar-left-off', 0xec02),
|
||||
blank: register('blank', 0xec03),
|
||||
heartFilled: register('heart-filled', 0xec04),
|
||||
map: register('map', 0xec05),
|
||||
mapHorizontal: register('map-horizontal', 0xec05),
|
||||
foldHorizontal: register('fold-horizontal', 0xec05),
|
||||
mapFilled: register('map-filled', 0xec06),
|
||||
mapHorizontalFilled: register('map-horizontal-filled', 0xec06),
|
||||
foldHorizontalFilled: register('fold-horizontal-filled', 0xec06),
|
||||
circleSmall: register('circle-small', 0xec07),
|
||||
bellSlash: register('bell-slash', 0xec08),
|
||||
bellSlashDot: register('bell-slash-dot', 0xec09),
|
||||
commentUnresolved: register('comment-unresolved', 0xec0a),
|
||||
gitPullRequestGoToChanges: register('git-pull-request-go-to-changes', 0xec0b),
|
||||
gitPullRequestNewChanges: register('git-pull-request-new-changes', 0xec0c),
|
||||
searchFuzzy: register('search-fuzzy', 0xec0d),
|
||||
commentDraft: register('comment-draft', 0xec0e),
|
||||
send: register('send', 0xec0f),
|
||||
sparkle: register('sparkle', 0xec10),
|
||||
insert: register('insert', 0xec11),
|
||||
mic: register('mic', 0xec12),
|
||||
thumbsdownFilled: register('thumbsdown-filled', 0xec13),
|
||||
thumbsupFilled: register('thumbsup-filled', 0xec14),
|
||||
coffee: register('coffee', 0xec15),
|
||||
snake: register('snake', 0xec16),
|
||||
game: register('game', 0xec17),
|
||||
vr: register('vr', 0xec18),
|
||||
chip: register('chip', 0xec19),
|
||||
piano: register('piano', 0xec1a),
|
||||
music: register('music', 0xec1b),
|
||||
micFilled: register('mic-filled', 0xec1c),
|
||||
repoFetch: register('repo-fetch', 0xec1d),
|
||||
copilot: register('copilot', 0xec1e),
|
||||
lightbulbSparkle: register('lightbulb-sparkle', 0xec1f),
|
||||
robot: register('robot', 0xec20),
|
||||
sparkleFilled: register('sparkle-filled', 0xec21),
|
||||
diffSingle: register('diff-single', 0xec22),
|
||||
diffMultiple: register('diff-multiple', 0xec23),
|
||||
surroundWith: register('surround-with', 0xec24),
|
||||
share: register('share', 0xec25),
|
||||
gitStash: register('git-stash', 0xec26),
|
||||
gitStashApply: register('git-stash-apply', 0xec27),
|
||||
gitStashPop: register('git-stash-pop', 0xec28),
|
||||
vscode: register('vscode', 0xec29),
|
||||
vscodeInsiders: register('vscode-insiders', 0xec2a),
|
||||
codeOss: register('code-oss', 0xec2b),
|
||||
runCoverage: register('run-coverage', 0xec2c),
|
||||
runAllCoverage: register('run-all-coverage', 0xec2d),
|
||||
coverage: register('coverage', 0xec2e),
|
||||
githubProject: register('github-project', 0xec2f),
|
||||
mapVertical: register('map-vertical', 0xec30),
|
||||
foldVertical: register('fold-vertical', 0xec30),
|
||||
mapVerticalFilled: register('map-vertical-filled', 0xec31),
|
||||
foldVerticalFilled: register('fold-vertical-filled', 0xec31),
|
||||
goToSearch: register('go-to-search', 0xec32),
|
||||
percentage: register('percentage', 0xec33),
|
||||
sortPercentage: register('sort-percentage', 0xec33),
|
||||
attach: register('attach', 0xec34),
|
||||
goToEditingSession: register('go-to-editing-session', 0xec35),
|
||||
editSession: register('edit-session', 0xec36),
|
||||
codeReview: register('code-review', 0xec37),
|
||||
copilotWarning: register('copilot-warning', 0xec38),
|
||||
python: register('python', 0xec39),
|
||||
copilotLarge: register('copilot-large', 0xec3a),
|
||||
copilotWarningLarge: register('copilot-warning-large', 0xec3b),
|
||||
keyboardTab: register('keyboard-tab', 0xec3c),
|
||||
copilotBlocked: register('copilot-blocked', 0xec3d),
|
||||
copilotNotConnected: register('copilot-not-connected', 0xec3e),
|
||||
flag: register('flag', 0xec3f),
|
||||
lightbulbEmpty: register('lightbulb-empty', 0xec40),
|
||||
symbolMethodArrow: register('symbol-method-arrow', 0xec41),
|
||||
copilotUnavailable: register('copilot-unavailable', 0xec42),
|
||||
repoPinned: register('repo-pinned', 0xec43),
|
||||
keyboardTabAbove: register('keyboard-tab-above', 0xec44),
|
||||
keyboardTabBelow: register('keyboard-tab-below', 0xec45),
|
||||
gitPullRequestDone: register('git-pull-request-done', 0xec46),
|
||||
mcp: register('mcp', 0xec47),
|
||||
extensionsLarge: register('extensions-large', 0xec48),
|
||||
layoutPanelDock: register('layout-panel-dock', 0xec49),
|
||||
layoutSidebarLeftDock: register('layout-sidebar-left-dock', 0xec4a),
|
||||
layoutSidebarRightDock: register('layout-sidebar-right-dock', 0xec4b),
|
||||
copilotInProgress: register('copilot-in-progress', 0xec4c),
|
||||
copilotError: register('copilot-error', 0xec4d),
|
||||
copilotSuccess: register('copilot-success', 0xec4e),
|
||||
chatSparkle: register('chat-sparkle', 0xec4f),
|
||||
searchSparkle: register('search-sparkle', 0xec50),
|
||||
editSparkle: register('edit-sparkle', 0xec51),
|
||||
copilotSnooze: register('copilot-snooze', 0xec52),
|
||||
sendToRemoteAgent: register('send-to-remote-agent', 0xec53),
|
||||
commentDiscussionSparkle: register('comment-discussion-sparkle', 0xec54),
|
||||
chatSparkleWarning: register('chat-sparkle-warning', 0xec55),
|
||||
chatSparkleError: register('chat-sparkle-error', 0xec56),
|
||||
collection: register('collection', 0xec57),
|
||||
newCollection: register('new-collection', 0xec58),
|
||||
thinking: register('thinking', 0xec59),
|
||||
build: register('build', 0xec5a),
|
||||
commentDiscussionQuote: register('comment-discussion-quote', 0xec5b),
|
||||
cursor: register('cursor', 0xec5c),
|
||||
eraser: register('eraser', 0xec5d),
|
||||
fileText: register('file-text', 0xec5e),
|
||||
gitLens: register('git-lens', 0xec5f),
|
||||
quotes: register('quotes', 0xec60),
|
||||
rename: register('rename', 0xec61),
|
||||
runWithDeps: register('run-with-deps', 0xec62),
|
||||
debugConnected: register('debug-connected', 0xec63),
|
||||
strikethrough: register('strikethrough', 0xec64),
|
||||
openInProduct: register('open-in-product', 0xec65),
|
||||
indexZero: register('index-zero', 0xec66),
|
||||
agent: register('agent', 0xec67),
|
||||
editCode: register('edit-code', 0xec68),
|
||||
repoSelected: register('repo-selected', 0xec69),
|
||||
skip: register('skip', 0xec6a),
|
||||
mergeInto: register('merge-into', 0xec6b),
|
||||
gitBranchChanges: register('git-branch-changes', 0xec6c),
|
||||
gitBranchStagedChanges: register('git-branch-staged-changes', 0xec6d),
|
||||
gitBranchConflicts: register('git-branch-conflicts', 0xec6e),
|
||||
gitBranch: register('git-branch', 0xec6f),
|
||||
gitBranchCreate: register('git-branch-create', 0xec6f),
|
||||
gitBranchDelete: register('git-branch-delete', 0xec6f),
|
||||
searchLarge: register('search-large', 0xec70),
|
||||
terminalGitBash: register('terminal-git-bash', 0xec71),
|
||||
};
|
||||
|
||||
export { codiconsLibrary };
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
import { isString } from './types.js';
|
||||
|
||||
const _codiconFontCharacters = Object.create(null);
|
||||
function register(id, fontCharacter) {
|
||||
if (isString(fontCharacter)) {
|
||||
const val = _codiconFontCharacters[fontCharacter];
|
||||
if (val === undefined) {
|
||||
throw new Error(`${id} references an unknown codicon: ${fontCharacter}`);
|
||||
}
|
||||
fontCharacter = val;
|
||||
}
|
||||
_codiconFontCharacters[id] = fontCharacter;
|
||||
return { id };
|
||||
}
|
||||
/**
|
||||
* Only to be used by the iconRegistry.
|
||||
*/
|
||||
function getCodiconFontCharacters() {
|
||||
return _codiconFontCharacters;
|
||||
}
|
||||
|
||||
export { getCodiconFontCharacters, register };
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
var _a;
|
||||
function groupByMap(data, groupFn) {
|
||||
const result = new Map();
|
||||
for (const element of data) {
|
||||
const key = groupFn(element);
|
||||
let target = result.get(key);
|
||||
if (!target) {
|
||||
target = [];
|
||||
result.set(key, target);
|
||||
}
|
||||
target.push(element);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
function diffSets(before, after) {
|
||||
const removed = [];
|
||||
const added = [];
|
||||
for (const element of before) {
|
||||
if (!after.has(element)) {
|
||||
removed.push(element);
|
||||
}
|
||||
}
|
||||
for (const element of after) {
|
||||
if (!before.has(element)) {
|
||||
added.push(element);
|
||||
}
|
||||
}
|
||||
return { removed, added };
|
||||
}
|
||||
/**
|
||||
* Computes the intersection of two sets.
|
||||
*
|
||||
* @param setA - The first set.
|
||||
* @param setB - The second iterable.
|
||||
* @returns A new set containing the elements that are in both `setA` and `setB`.
|
||||
*/
|
||||
function intersection(setA, setB) {
|
||||
const result = new Set();
|
||||
for (const elem of setB) {
|
||||
if (setA.has(elem)) {
|
||||
result.add(elem);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
class SetWithKey {
|
||||
static { _a = Symbol.toStringTag; }
|
||||
constructor(values, toKey) {
|
||||
this.toKey = toKey;
|
||||
this._map = new Map();
|
||||
this[_a] = 'SetWithKey';
|
||||
for (const value of values) {
|
||||
this.add(value);
|
||||
}
|
||||
}
|
||||
get size() {
|
||||
return this._map.size;
|
||||
}
|
||||
add(value) {
|
||||
const key = this.toKey(value);
|
||||
this._map.set(key, value);
|
||||
return this;
|
||||
}
|
||||
delete(value) {
|
||||
return this._map.delete(this.toKey(value));
|
||||
}
|
||||
has(value) {
|
||||
return this._map.has(this.toKey(value));
|
||||
}
|
||||
*entries() {
|
||||
for (const entry of this._map.values()) {
|
||||
yield [entry, entry];
|
||||
}
|
||||
}
|
||||
keys() {
|
||||
return this.values();
|
||||
}
|
||||
*values() {
|
||||
for (const entry of this._map.values()) {
|
||||
yield entry;
|
||||
}
|
||||
}
|
||||
clear() {
|
||||
this._map.clear();
|
||||
}
|
||||
forEach(callbackfn, thisArg) {
|
||||
this._map.forEach(entry => callbackfn.call(thisArg, entry, entry, this));
|
||||
}
|
||||
[Symbol.iterator]() {
|
||||
return this.values();
|
||||
}
|
||||
}
|
||||
|
||||
export { SetWithKey, diffSets, groupByMap, intersection };
|
||||
+678
@@ -0,0 +1,678 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
function roundFloat(number, decimalPoints) {
|
||||
const decimal = Math.pow(10, decimalPoints);
|
||||
return Math.round(number * decimal) / decimal;
|
||||
}
|
||||
class RGBA {
|
||||
constructor(r, g, b, a = 1) {
|
||||
this._rgbaBrand = undefined;
|
||||
this.r = Math.min(255, Math.max(0, r)) | 0;
|
||||
this.g = Math.min(255, Math.max(0, g)) | 0;
|
||||
this.b = Math.min(255, Math.max(0, b)) | 0;
|
||||
this.a = roundFloat(Math.max(Math.min(1, a), 0), 3);
|
||||
}
|
||||
static equals(a, b) {
|
||||
return a.r === b.r && a.g === b.g && a.b === b.b && a.a === b.a;
|
||||
}
|
||||
}
|
||||
class HSLA {
|
||||
constructor(h, s, l, a) {
|
||||
this._hslaBrand = undefined;
|
||||
this.h = Math.max(Math.min(360, h), 0) | 0;
|
||||
this.s = roundFloat(Math.max(Math.min(1, s), 0), 3);
|
||||
this.l = roundFloat(Math.max(Math.min(1, l), 0), 3);
|
||||
this.a = roundFloat(Math.max(Math.min(1, a), 0), 3);
|
||||
}
|
||||
static equals(a, b) {
|
||||
return a.h === b.h && a.s === b.s && a.l === b.l && a.a === b.a;
|
||||
}
|
||||
/**
|
||||
* Converts an RGB color value to HSL. Conversion formula
|
||||
* adapted from http://en.wikipedia.org/wiki/HSL_color_space.
|
||||
* Assumes r, g, and b are contained in the set [0, 255] and
|
||||
* returns h in the set [0, 360], s, and l in the set [0, 1].
|
||||
*/
|
||||
static fromRGBA(rgba) {
|
||||
const r = rgba.r / 255;
|
||||
const g = rgba.g / 255;
|
||||
const b = rgba.b / 255;
|
||||
const a = rgba.a;
|
||||
const max = Math.max(r, g, b);
|
||||
const min = Math.min(r, g, b);
|
||||
let h = 0;
|
||||
let s = 0;
|
||||
const l = (min + max) / 2;
|
||||
const chroma = max - min;
|
||||
if (chroma > 0) {
|
||||
s = Math.min((l <= 0.5 ? chroma / (2 * l) : chroma / (2 - (2 * l))), 1);
|
||||
switch (max) {
|
||||
case r:
|
||||
h = (g - b) / chroma + (g < b ? 6 : 0);
|
||||
break;
|
||||
case g:
|
||||
h = (b - r) / chroma + 2;
|
||||
break;
|
||||
case b:
|
||||
h = (r - g) / chroma + 4;
|
||||
break;
|
||||
}
|
||||
h *= 60;
|
||||
h = Math.round(h);
|
||||
}
|
||||
return new HSLA(h, s, l, a);
|
||||
}
|
||||
static _hue2rgb(p, q, t) {
|
||||
if (t < 0) {
|
||||
t += 1;
|
||||
}
|
||||
if (t > 1) {
|
||||
t -= 1;
|
||||
}
|
||||
if (t < 1 / 6) {
|
||||
return p + (q - p) * 6 * t;
|
||||
}
|
||||
if (t < 1 / 2) {
|
||||
return q;
|
||||
}
|
||||
if (t < 2 / 3) {
|
||||
return p + (q - p) * (2 / 3 - t) * 6;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
/**
|
||||
* Converts an HSL color value to RGB. Conversion formula
|
||||
* adapted from http://en.wikipedia.org/wiki/HSL_color_space.
|
||||
* Assumes h in the set [0, 360] s, and l are contained in the set [0, 1] and
|
||||
* returns r, g, and b in the set [0, 255].
|
||||
*/
|
||||
static toRGBA(hsla) {
|
||||
const h = hsla.h / 360;
|
||||
const { s, l, a } = hsla;
|
||||
let r, g, b;
|
||||
if (s === 0) {
|
||||
r = g = b = l; // achromatic
|
||||
}
|
||||
else {
|
||||
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
||||
const p = 2 * l - q;
|
||||
r = HSLA._hue2rgb(p, q, h + 1 / 3);
|
||||
g = HSLA._hue2rgb(p, q, h);
|
||||
b = HSLA._hue2rgb(p, q, h - 1 / 3);
|
||||
}
|
||||
return new RGBA(Math.round(r * 255), Math.round(g * 255), Math.round(b * 255), a);
|
||||
}
|
||||
}
|
||||
class HSVA {
|
||||
constructor(h, s, v, a) {
|
||||
this._hsvaBrand = undefined;
|
||||
this.h = Math.max(Math.min(360, h), 0) | 0;
|
||||
this.s = roundFloat(Math.max(Math.min(1, s), 0), 3);
|
||||
this.v = roundFloat(Math.max(Math.min(1, v), 0), 3);
|
||||
this.a = roundFloat(Math.max(Math.min(1, a), 0), 3);
|
||||
}
|
||||
static equals(a, b) {
|
||||
return a.h === b.h && a.s === b.s && a.v === b.v && a.a === b.a;
|
||||
}
|
||||
// from http://www.rapidtables.com/convert/color/rgb-to-hsv.htm
|
||||
static fromRGBA(rgba) {
|
||||
const r = rgba.r / 255;
|
||||
const g = rgba.g / 255;
|
||||
const b = rgba.b / 255;
|
||||
const cmax = Math.max(r, g, b);
|
||||
const cmin = Math.min(r, g, b);
|
||||
const delta = cmax - cmin;
|
||||
const s = cmax === 0 ? 0 : (delta / cmax);
|
||||
let m;
|
||||
if (delta === 0) {
|
||||
m = 0;
|
||||
}
|
||||
else if (cmax === r) {
|
||||
m = ((((g - b) / delta) % 6) + 6) % 6;
|
||||
}
|
||||
else if (cmax === g) {
|
||||
m = ((b - r) / delta) + 2;
|
||||
}
|
||||
else {
|
||||
m = ((r - g) / delta) + 4;
|
||||
}
|
||||
return new HSVA(Math.round(m * 60), s, cmax, rgba.a);
|
||||
}
|
||||
// from http://www.rapidtables.com/convert/color/hsv-to-rgb.htm
|
||||
static toRGBA(hsva) {
|
||||
const { h, s, v, a } = hsva;
|
||||
const c = v * s;
|
||||
const x = c * (1 - Math.abs((h / 60) % 2 - 1));
|
||||
const m = v - c;
|
||||
let [r, g, b] = [0, 0, 0];
|
||||
if (h < 60) {
|
||||
r = c;
|
||||
g = x;
|
||||
}
|
||||
else if (h < 120) {
|
||||
r = x;
|
||||
g = c;
|
||||
}
|
||||
else if (h < 180) {
|
||||
g = c;
|
||||
b = x;
|
||||
}
|
||||
else if (h < 240) {
|
||||
g = x;
|
||||
b = c;
|
||||
}
|
||||
else if (h < 300) {
|
||||
r = x;
|
||||
b = c;
|
||||
}
|
||||
else if (h <= 360) {
|
||||
r = c;
|
||||
b = x;
|
||||
}
|
||||
r = Math.round((r + m) * 255);
|
||||
g = Math.round((g + m) * 255);
|
||||
b = Math.round((b + m) * 255);
|
||||
return new RGBA(r, g, b, a);
|
||||
}
|
||||
}
|
||||
class Color {
|
||||
static fromHex(hex) {
|
||||
return Color.Format.CSS.parseHex(hex) || Color.red;
|
||||
}
|
||||
static equals(a, b) {
|
||||
if (!a && !b) {
|
||||
return true;
|
||||
}
|
||||
if (!a || !b) {
|
||||
return false;
|
||||
}
|
||||
return a.equals(b);
|
||||
}
|
||||
get hsla() {
|
||||
if (this._hsla) {
|
||||
return this._hsla;
|
||||
}
|
||||
else {
|
||||
return HSLA.fromRGBA(this.rgba);
|
||||
}
|
||||
}
|
||||
get hsva() {
|
||||
if (this._hsva) {
|
||||
return this._hsva;
|
||||
}
|
||||
return HSVA.fromRGBA(this.rgba);
|
||||
}
|
||||
constructor(arg) {
|
||||
if (!arg) {
|
||||
throw new Error('Color needs a value');
|
||||
}
|
||||
else if (arg instanceof RGBA) {
|
||||
this.rgba = arg;
|
||||
}
|
||||
else if (arg instanceof HSLA) {
|
||||
this._hsla = arg;
|
||||
this.rgba = HSLA.toRGBA(arg);
|
||||
}
|
||||
else if (arg instanceof HSVA) {
|
||||
this._hsva = arg;
|
||||
this.rgba = HSVA.toRGBA(arg);
|
||||
}
|
||||
else {
|
||||
throw new Error('Invalid color ctor argument');
|
||||
}
|
||||
}
|
||||
equals(other) {
|
||||
return !!other && RGBA.equals(this.rgba, other.rgba) && HSLA.equals(this.hsla, other.hsla) && HSVA.equals(this.hsva, other.hsva);
|
||||
}
|
||||
/**
|
||||
* http://www.w3.org/TR/WCAG20/#relativeluminancedef
|
||||
* Returns the number in the set [0, 1]. O => Darkest Black. 1 => Lightest white.
|
||||
*/
|
||||
getRelativeLuminance() {
|
||||
const R = Color._relativeLuminanceForComponent(this.rgba.r);
|
||||
const G = Color._relativeLuminanceForComponent(this.rgba.g);
|
||||
const B = Color._relativeLuminanceForComponent(this.rgba.b);
|
||||
const luminance = 0.2126 * R + 0.7152 * G + 0.0722 * B;
|
||||
return roundFloat(luminance, 4);
|
||||
}
|
||||
static _relativeLuminanceForComponent(color) {
|
||||
const c = color / 255;
|
||||
return (c <= 0.03928) ? c / 12.92 : Math.pow(((c + 0.055) / 1.055), 2.4);
|
||||
}
|
||||
/**
|
||||
* http://24ways.org/2010/calculating-color-contrast
|
||||
* Return 'true' if lighter color otherwise 'false'
|
||||
*/
|
||||
isLighter() {
|
||||
const yiq = (this.rgba.r * 299 + this.rgba.g * 587 + this.rgba.b * 114) / 1000;
|
||||
return yiq >= 128;
|
||||
}
|
||||
isLighterThan(another) {
|
||||
const lum1 = this.getRelativeLuminance();
|
||||
const lum2 = another.getRelativeLuminance();
|
||||
return lum1 > lum2;
|
||||
}
|
||||
isDarkerThan(another) {
|
||||
const lum1 = this.getRelativeLuminance();
|
||||
const lum2 = another.getRelativeLuminance();
|
||||
return lum1 < lum2;
|
||||
}
|
||||
lighten(factor) {
|
||||
return new Color(new HSLA(this.hsla.h, this.hsla.s, this.hsla.l + this.hsla.l * factor, this.hsla.a));
|
||||
}
|
||||
darken(factor) {
|
||||
return new Color(new HSLA(this.hsla.h, this.hsla.s, this.hsla.l - this.hsla.l * factor, this.hsla.a));
|
||||
}
|
||||
transparent(factor) {
|
||||
const { r, g, b, a } = this.rgba;
|
||||
return new Color(new RGBA(r, g, b, a * factor));
|
||||
}
|
||||
isTransparent() {
|
||||
return this.rgba.a === 0;
|
||||
}
|
||||
isOpaque() {
|
||||
return this.rgba.a === 1;
|
||||
}
|
||||
opposite() {
|
||||
return new Color(new RGBA(255 - this.rgba.r, 255 - this.rgba.g, 255 - this.rgba.b, this.rgba.a));
|
||||
}
|
||||
/**
|
||||
* Mixes the current color with the provided color based on the given factor.
|
||||
* @param color The color to mix with
|
||||
* @param factor The factor of mixing (0 means this color, 1 means the input color, 0.5 means equal mix)
|
||||
* @returns A new color representing the mix
|
||||
*/
|
||||
mix(color, factor = 0.5) {
|
||||
const normalize = Math.min(Math.max(factor, 0), 1);
|
||||
const thisRGBA = this.rgba;
|
||||
const otherRGBA = color.rgba;
|
||||
const r = thisRGBA.r + (otherRGBA.r - thisRGBA.r) * normalize;
|
||||
const g = thisRGBA.g + (otherRGBA.g - thisRGBA.g) * normalize;
|
||||
const b = thisRGBA.b + (otherRGBA.b - thisRGBA.b) * normalize;
|
||||
const a = thisRGBA.a + (otherRGBA.a - thisRGBA.a) * normalize;
|
||||
return new Color(new RGBA(r, g, b, a));
|
||||
}
|
||||
makeOpaque(opaqueBackground) {
|
||||
if (this.isOpaque() || opaqueBackground.rgba.a !== 1) {
|
||||
// only allow to blend onto a non-opaque color onto a opaque color
|
||||
return this;
|
||||
}
|
||||
const { r, g, b, a } = this.rgba;
|
||||
// https://stackoverflow.com/questions/12228548/finding-equivalent-color-with-opacity
|
||||
return new Color(new RGBA(opaqueBackground.rgba.r - a * (opaqueBackground.rgba.r - r), opaqueBackground.rgba.g - a * (opaqueBackground.rgba.g - g), opaqueBackground.rgba.b - a * (opaqueBackground.rgba.b - b), 1));
|
||||
}
|
||||
toString() {
|
||||
if (!this._toString) {
|
||||
this._toString = Color.Format.CSS.format(this);
|
||||
}
|
||||
return this._toString;
|
||||
}
|
||||
toNumber32Bit() {
|
||||
if (!this._toNumber32Bit) {
|
||||
this._toNumber32Bit = (this.rgba.r /* */ << 24 |
|
||||
this.rgba.g /* */ << 16 |
|
||||
this.rgba.b /* */ << 8 |
|
||||
this.rgba.a * 0xFF << 0) >>> 0;
|
||||
}
|
||||
return this._toNumber32Bit;
|
||||
}
|
||||
static getLighterColor(of, relative, factor) {
|
||||
if (of.isLighterThan(relative)) {
|
||||
return of;
|
||||
}
|
||||
factor = factor ? factor : 0.5;
|
||||
const lum1 = of.getRelativeLuminance();
|
||||
const lum2 = relative.getRelativeLuminance();
|
||||
factor = factor * (lum2 - lum1) / lum2;
|
||||
return of.lighten(factor);
|
||||
}
|
||||
static getDarkerColor(of, relative, factor) {
|
||||
if (of.isDarkerThan(relative)) {
|
||||
return of;
|
||||
}
|
||||
factor = factor ? factor : 0.5;
|
||||
const lum1 = of.getRelativeLuminance();
|
||||
const lum2 = relative.getRelativeLuminance();
|
||||
factor = factor * (lum1 - lum2) / lum1;
|
||||
return of.darken(factor);
|
||||
}
|
||||
static { this.white = new Color(new RGBA(255, 255, 255, 1)); }
|
||||
static { this.black = new Color(new RGBA(0, 0, 0, 1)); }
|
||||
static { this.red = new Color(new RGBA(255, 0, 0, 1)); }
|
||||
static { this.blue = new Color(new RGBA(0, 0, 255, 1)); }
|
||||
static { this.green = new Color(new RGBA(0, 255, 0, 1)); }
|
||||
static { this.cyan = new Color(new RGBA(0, 255, 255, 1)); }
|
||||
static { this.lightgrey = new Color(new RGBA(211, 211, 211, 1)); }
|
||||
static { this.transparent = new Color(new RGBA(0, 0, 0, 0)); }
|
||||
}
|
||||
(function (Color) {
|
||||
(function (Format) {
|
||||
(function (CSS) {
|
||||
function formatRGB(color) {
|
||||
if (color.rgba.a === 1) {
|
||||
return `rgb(${color.rgba.r}, ${color.rgba.g}, ${color.rgba.b})`;
|
||||
}
|
||||
return Color.Format.CSS.formatRGBA(color);
|
||||
}
|
||||
CSS.formatRGB = formatRGB;
|
||||
function formatRGBA(color) {
|
||||
return `rgba(${color.rgba.r}, ${color.rgba.g}, ${color.rgba.b}, ${+(color.rgba.a).toFixed(2)})`;
|
||||
}
|
||||
CSS.formatRGBA = formatRGBA;
|
||||
function formatHSL(color) {
|
||||
if (color.hsla.a === 1) {
|
||||
return `hsl(${color.hsla.h}, ${Math.round(color.hsla.s * 100)}%, ${Math.round(color.hsla.l * 100)}%)`;
|
||||
}
|
||||
return Color.Format.CSS.formatHSLA(color);
|
||||
}
|
||||
CSS.formatHSL = formatHSL;
|
||||
function formatHSLA(color) {
|
||||
return `hsla(${color.hsla.h}, ${Math.round(color.hsla.s * 100)}%, ${Math.round(color.hsla.l * 100)}%, ${color.hsla.a.toFixed(2)})`;
|
||||
}
|
||||
CSS.formatHSLA = formatHSLA;
|
||||
function _toTwoDigitHex(n) {
|
||||
const r = n.toString(16);
|
||||
return r.length !== 2 ? '0' + r : r;
|
||||
}
|
||||
/**
|
||||
* Formats the color as #RRGGBB
|
||||
*/
|
||||
function formatHex(color) {
|
||||
return `#${_toTwoDigitHex(color.rgba.r)}${_toTwoDigitHex(color.rgba.g)}${_toTwoDigitHex(color.rgba.b)}`;
|
||||
}
|
||||
CSS.formatHex = formatHex;
|
||||
/**
|
||||
* Formats the color as #RRGGBBAA
|
||||
* If 'compact' is set, colors without transparancy will be printed as #RRGGBB
|
||||
*/
|
||||
function formatHexA(color, compact = false) {
|
||||
if (compact && color.rgba.a === 1) {
|
||||
return Color.Format.CSS.formatHex(color);
|
||||
}
|
||||
return `#${_toTwoDigitHex(color.rgba.r)}${_toTwoDigitHex(color.rgba.g)}${_toTwoDigitHex(color.rgba.b)}${_toTwoDigitHex(Math.round(color.rgba.a * 255))}`;
|
||||
}
|
||||
CSS.formatHexA = formatHexA;
|
||||
/**
|
||||
* The default format will use HEX if opaque and RGBA otherwise.
|
||||
*/
|
||||
function format(color) {
|
||||
if (color.isOpaque()) {
|
||||
return Color.Format.CSS.formatHex(color);
|
||||
}
|
||||
return Color.Format.CSS.formatRGBA(color);
|
||||
}
|
||||
CSS.format = format;
|
||||
/**
|
||||
* Parse a CSS color and return a {@link Color}.
|
||||
* @param css The CSS color to parse.
|
||||
* @see https://drafts.csswg.org/css-color/#typedef-color
|
||||
*/
|
||||
function parse(css) {
|
||||
if (css === 'transparent') {
|
||||
return Color.transparent;
|
||||
}
|
||||
if (css.startsWith('#')) {
|
||||
return parseHex(css);
|
||||
}
|
||||
if (css.startsWith('rgba(')) {
|
||||
const color = css.match(/rgba\((?<r>(?:\+|-)?\d+), *(?<g>(?:\+|-)?\d+), *(?<b>(?:\+|-)?\d+), *(?<a>(?:\+|-)?\d+(\.\d+)?)\)/);
|
||||
if (!color) {
|
||||
throw new Error('Invalid color format ' + css);
|
||||
}
|
||||
const r = parseInt(color.groups?.r ?? '0');
|
||||
const g = parseInt(color.groups?.g ?? '0');
|
||||
const b = parseInt(color.groups?.b ?? '0');
|
||||
const a = parseFloat(color.groups?.a ?? '0');
|
||||
return new Color(new RGBA(r, g, b, a));
|
||||
}
|
||||
if (css.startsWith('rgb(')) {
|
||||
const color = css.match(/rgb\((?<r>(?:\+|-)?\d+), *(?<g>(?:\+|-)?\d+), *(?<b>(?:\+|-)?\d+)\)/);
|
||||
if (!color) {
|
||||
throw new Error('Invalid color format ' + css);
|
||||
}
|
||||
const r = parseInt(color.groups?.r ?? '0');
|
||||
const g = parseInt(color.groups?.g ?? '0');
|
||||
const b = parseInt(color.groups?.b ?? '0');
|
||||
return new Color(new RGBA(r, g, b));
|
||||
}
|
||||
// TODO: Support more formats as needed
|
||||
return parseNamedKeyword(css);
|
||||
}
|
||||
CSS.parse = parse;
|
||||
function parseNamedKeyword(css) {
|
||||
// https://drafts.csswg.org/css-color/#named-colors
|
||||
switch (css) {
|
||||
case 'aliceblue': return new Color(new RGBA(240, 248, 255, 1));
|
||||
case 'antiquewhite': return new Color(new RGBA(250, 235, 215, 1));
|
||||
case 'aqua': return new Color(new RGBA(0, 255, 255, 1));
|
||||
case 'aquamarine': return new Color(new RGBA(127, 255, 212, 1));
|
||||
case 'azure': return new Color(new RGBA(240, 255, 255, 1));
|
||||
case 'beige': return new Color(new RGBA(245, 245, 220, 1));
|
||||
case 'bisque': return new Color(new RGBA(255, 228, 196, 1));
|
||||
case 'black': return new Color(new RGBA(0, 0, 0, 1));
|
||||
case 'blanchedalmond': return new Color(new RGBA(255, 235, 205, 1));
|
||||
case 'blue': return new Color(new RGBA(0, 0, 255, 1));
|
||||
case 'blueviolet': return new Color(new RGBA(138, 43, 226, 1));
|
||||
case 'brown': return new Color(new RGBA(165, 42, 42, 1));
|
||||
case 'burlywood': return new Color(new RGBA(222, 184, 135, 1));
|
||||
case 'cadetblue': return new Color(new RGBA(95, 158, 160, 1));
|
||||
case 'chartreuse': return new Color(new RGBA(127, 255, 0, 1));
|
||||
case 'chocolate': return new Color(new RGBA(210, 105, 30, 1));
|
||||
case 'coral': return new Color(new RGBA(255, 127, 80, 1));
|
||||
case 'cornflowerblue': return new Color(new RGBA(100, 149, 237, 1));
|
||||
case 'cornsilk': return new Color(new RGBA(255, 248, 220, 1));
|
||||
case 'crimson': return new Color(new RGBA(220, 20, 60, 1));
|
||||
case 'cyan': return new Color(new RGBA(0, 255, 255, 1));
|
||||
case 'darkblue': return new Color(new RGBA(0, 0, 139, 1));
|
||||
case 'darkcyan': return new Color(new RGBA(0, 139, 139, 1));
|
||||
case 'darkgoldenrod': return new Color(new RGBA(184, 134, 11, 1));
|
||||
case 'darkgray': return new Color(new RGBA(169, 169, 169, 1));
|
||||
case 'darkgreen': return new Color(new RGBA(0, 100, 0, 1));
|
||||
case 'darkgrey': return new Color(new RGBA(169, 169, 169, 1));
|
||||
case 'darkkhaki': return new Color(new RGBA(189, 183, 107, 1));
|
||||
case 'darkmagenta': return new Color(new RGBA(139, 0, 139, 1));
|
||||
case 'darkolivegreen': return new Color(new RGBA(85, 107, 47, 1));
|
||||
case 'darkorange': return new Color(new RGBA(255, 140, 0, 1));
|
||||
case 'darkorchid': return new Color(new RGBA(153, 50, 204, 1));
|
||||
case 'darkred': return new Color(new RGBA(139, 0, 0, 1));
|
||||
case 'darksalmon': return new Color(new RGBA(233, 150, 122, 1));
|
||||
case 'darkseagreen': return new Color(new RGBA(143, 188, 143, 1));
|
||||
case 'darkslateblue': return new Color(new RGBA(72, 61, 139, 1));
|
||||
case 'darkslategray': return new Color(new RGBA(47, 79, 79, 1));
|
||||
case 'darkslategrey': return new Color(new RGBA(47, 79, 79, 1));
|
||||
case 'darkturquoise': return new Color(new RGBA(0, 206, 209, 1));
|
||||
case 'darkviolet': return new Color(new RGBA(148, 0, 211, 1));
|
||||
case 'deeppink': return new Color(new RGBA(255, 20, 147, 1));
|
||||
case 'deepskyblue': return new Color(new RGBA(0, 191, 255, 1));
|
||||
case 'dimgray': return new Color(new RGBA(105, 105, 105, 1));
|
||||
case 'dimgrey': return new Color(new RGBA(105, 105, 105, 1));
|
||||
case 'dodgerblue': return new Color(new RGBA(30, 144, 255, 1));
|
||||
case 'firebrick': return new Color(new RGBA(178, 34, 34, 1));
|
||||
case 'floralwhite': return new Color(new RGBA(255, 250, 240, 1));
|
||||
case 'forestgreen': return new Color(new RGBA(34, 139, 34, 1));
|
||||
case 'fuchsia': return new Color(new RGBA(255, 0, 255, 1));
|
||||
case 'gainsboro': return new Color(new RGBA(220, 220, 220, 1));
|
||||
case 'ghostwhite': return new Color(new RGBA(248, 248, 255, 1));
|
||||
case 'gold': return new Color(new RGBA(255, 215, 0, 1));
|
||||
case 'goldenrod': return new Color(new RGBA(218, 165, 32, 1));
|
||||
case 'gray': return new Color(new RGBA(128, 128, 128, 1));
|
||||
case 'green': return new Color(new RGBA(0, 128, 0, 1));
|
||||
case 'greenyellow': return new Color(new RGBA(173, 255, 47, 1));
|
||||
case 'grey': return new Color(new RGBA(128, 128, 128, 1));
|
||||
case 'honeydew': return new Color(new RGBA(240, 255, 240, 1));
|
||||
case 'hotpink': return new Color(new RGBA(255, 105, 180, 1));
|
||||
case 'indianred': return new Color(new RGBA(205, 92, 92, 1));
|
||||
case 'indigo': return new Color(new RGBA(75, 0, 130, 1));
|
||||
case 'ivory': return new Color(new RGBA(255, 255, 240, 1));
|
||||
case 'khaki': return new Color(new RGBA(240, 230, 140, 1));
|
||||
case 'lavender': return new Color(new RGBA(230, 230, 250, 1));
|
||||
case 'lavenderblush': return new Color(new RGBA(255, 240, 245, 1));
|
||||
case 'lawngreen': return new Color(new RGBA(124, 252, 0, 1));
|
||||
case 'lemonchiffon': return new Color(new RGBA(255, 250, 205, 1));
|
||||
case 'lightblue': return new Color(new RGBA(173, 216, 230, 1));
|
||||
case 'lightcoral': return new Color(new RGBA(240, 128, 128, 1));
|
||||
case 'lightcyan': return new Color(new RGBA(224, 255, 255, 1));
|
||||
case 'lightgoldenrodyellow': return new Color(new RGBA(250, 250, 210, 1));
|
||||
case 'lightgray': return new Color(new RGBA(211, 211, 211, 1));
|
||||
case 'lightgreen': return new Color(new RGBA(144, 238, 144, 1));
|
||||
case 'lightgrey': return new Color(new RGBA(211, 211, 211, 1));
|
||||
case 'lightpink': return new Color(new RGBA(255, 182, 193, 1));
|
||||
case 'lightsalmon': return new Color(new RGBA(255, 160, 122, 1));
|
||||
case 'lightseagreen': return new Color(new RGBA(32, 178, 170, 1));
|
||||
case 'lightskyblue': return new Color(new RGBA(135, 206, 250, 1));
|
||||
case 'lightslategray': return new Color(new RGBA(119, 136, 153, 1));
|
||||
case 'lightslategrey': return new Color(new RGBA(119, 136, 153, 1));
|
||||
case 'lightsteelblue': return new Color(new RGBA(176, 196, 222, 1));
|
||||
case 'lightyellow': return new Color(new RGBA(255, 255, 224, 1));
|
||||
case 'lime': return new Color(new RGBA(0, 255, 0, 1));
|
||||
case 'limegreen': return new Color(new RGBA(50, 205, 50, 1));
|
||||
case 'linen': return new Color(new RGBA(250, 240, 230, 1));
|
||||
case 'magenta': return new Color(new RGBA(255, 0, 255, 1));
|
||||
case 'maroon': return new Color(new RGBA(128, 0, 0, 1));
|
||||
case 'mediumaquamarine': return new Color(new RGBA(102, 205, 170, 1));
|
||||
case 'mediumblue': return new Color(new RGBA(0, 0, 205, 1));
|
||||
case 'mediumorchid': return new Color(new RGBA(186, 85, 211, 1));
|
||||
case 'mediumpurple': return new Color(new RGBA(147, 112, 219, 1));
|
||||
case 'mediumseagreen': return new Color(new RGBA(60, 179, 113, 1));
|
||||
case 'mediumslateblue': return new Color(new RGBA(123, 104, 238, 1));
|
||||
case 'mediumspringgreen': return new Color(new RGBA(0, 250, 154, 1));
|
||||
case 'mediumturquoise': return new Color(new RGBA(72, 209, 204, 1));
|
||||
case 'mediumvioletred': return new Color(new RGBA(199, 21, 133, 1));
|
||||
case 'midnightblue': return new Color(new RGBA(25, 25, 112, 1));
|
||||
case 'mintcream': return new Color(new RGBA(245, 255, 250, 1));
|
||||
case 'mistyrose': return new Color(new RGBA(255, 228, 225, 1));
|
||||
case 'moccasin': return new Color(new RGBA(255, 228, 181, 1));
|
||||
case 'navajowhite': return new Color(new RGBA(255, 222, 173, 1));
|
||||
case 'navy': return new Color(new RGBA(0, 0, 128, 1));
|
||||
case 'oldlace': return new Color(new RGBA(253, 245, 230, 1));
|
||||
case 'olive': return new Color(new RGBA(128, 128, 0, 1));
|
||||
case 'olivedrab': return new Color(new RGBA(107, 142, 35, 1));
|
||||
case 'orange': return new Color(new RGBA(255, 165, 0, 1));
|
||||
case 'orangered': return new Color(new RGBA(255, 69, 0, 1));
|
||||
case 'orchid': return new Color(new RGBA(218, 112, 214, 1));
|
||||
case 'palegoldenrod': return new Color(new RGBA(238, 232, 170, 1));
|
||||
case 'palegreen': return new Color(new RGBA(152, 251, 152, 1));
|
||||
case 'paleturquoise': return new Color(new RGBA(175, 238, 238, 1));
|
||||
case 'palevioletred': return new Color(new RGBA(219, 112, 147, 1));
|
||||
case 'papayawhip': return new Color(new RGBA(255, 239, 213, 1));
|
||||
case 'peachpuff': return new Color(new RGBA(255, 218, 185, 1));
|
||||
case 'peru': return new Color(new RGBA(205, 133, 63, 1));
|
||||
case 'pink': return new Color(new RGBA(255, 192, 203, 1));
|
||||
case 'plum': return new Color(new RGBA(221, 160, 221, 1));
|
||||
case 'powderblue': return new Color(new RGBA(176, 224, 230, 1));
|
||||
case 'purple': return new Color(new RGBA(128, 0, 128, 1));
|
||||
case 'rebeccapurple': return new Color(new RGBA(102, 51, 153, 1));
|
||||
case 'red': return new Color(new RGBA(255, 0, 0, 1));
|
||||
case 'rosybrown': return new Color(new RGBA(188, 143, 143, 1));
|
||||
case 'royalblue': return new Color(new RGBA(65, 105, 225, 1));
|
||||
case 'saddlebrown': return new Color(new RGBA(139, 69, 19, 1));
|
||||
case 'salmon': return new Color(new RGBA(250, 128, 114, 1));
|
||||
case 'sandybrown': return new Color(new RGBA(244, 164, 96, 1));
|
||||
case 'seagreen': return new Color(new RGBA(46, 139, 87, 1));
|
||||
case 'seashell': return new Color(new RGBA(255, 245, 238, 1));
|
||||
case 'sienna': return new Color(new RGBA(160, 82, 45, 1));
|
||||
case 'silver': return new Color(new RGBA(192, 192, 192, 1));
|
||||
case 'skyblue': return new Color(new RGBA(135, 206, 235, 1));
|
||||
case 'slateblue': return new Color(new RGBA(106, 90, 205, 1));
|
||||
case 'slategray': return new Color(new RGBA(112, 128, 144, 1));
|
||||
case 'slategrey': return new Color(new RGBA(112, 128, 144, 1));
|
||||
case 'snow': return new Color(new RGBA(255, 250, 250, 1));
|
||||
case 'springgreen': return new Color(new RGBA(0, 255, 127, 1));
|
||||
case 'steelblue': return new Color(new RGBA(70, 130, 180, 1));
|
||||
case 'tan': return new Color(new RGBA(210, 180, 140, 1));
|
||||
case 'teal': return new Color(new RGBA(0, 128, 128, 1));
|
||||
case 'thistle': return new Color(new RGBA(216, 191, 216, 1));
|
||||
case 'tomato': return new Color(new RGBA(255, 99, 71, 1));
|
||||
case 'turquoise': return new Color(new RGBA(64, 224, 208, 1));
|
||||
case 'violet': return new Color(new RGBA(238, 130, 238, 1));
|
||||
case 'wheat': return new Color(new RGBA(245, 222, 179, 1));
|
||||
case 'white': return new Color(new RGBA(255, 255, 255, 1));
|
||||
case 'whitesmoke': return new Color(new RGBA(245, 245, 245, 1));
|
||||
case 'yellow': return new Color(new RGBA(255, 255, 0, 1));
|
||||
case 'yellowgreen': return new Color(new RGBA(154, 205, 50, 1));
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Converts an Hex color value to a Color.
|
||||
* returns r, g, and b are contained in the set [0, 255]
|
||||
* @param hex string (#RGB, #RGBA, #RRGGBB or #RRGGBBAA).
|
||||
*/
|
||||
function parseHex(hex) {
|
||||
const length = hex.length;
|
||||
if (length === 0) {
|
||||
// Invalid color
|
||||
return null;
|
||||
}
|
||||
if (hex.charCodeAt(0) !== 35 /* CharCode.Hash */) {
|
||||
// Does not begin with a #
|
||||
return null;
|
||||
}
|
||||
if (length === 7) {
|
||||
// #RRGGBB format
|
||||
const r = 16 * _parseHexDigit(hex.charCodeAt(1)) + _parseHexDigit(hex.charCodeAt(2));
|
||||
const g = 16 * _parseHexDigit(hex.charCodeAt(3)) + _parseHexDigit(hex.charCodeAt(4));
|
||||
const b = 16 * _parseHexDigit(hex.charCodeAt(5)) + _parseHexDigit(hex.charCodeAt(6));
|
||||
return new Color(new RGBA(r, g, b, 1));
|
||||
}
|
||||
if (length === 9) {
|
||||
// #RRGGBBAA format
|
||||
const r = 16 * _parseHexDigit(hex.charCodeAt(1)) + _parseHexDigit(hex.charCodeAt(2));
|
||||
const g = 16 * _parseHexDigit(hex.charCodeAt(3)) + _parseHexDigit(hex.charCodeAt(4));
|
||||
const b = 16 * _parseHexDigit(hex.charCodeAt(5)) + _parseHexDigit(hex.charCodeAt(6));
|
||||
const a = 16 * _parseHexDigit(hex.charCodeAt(7)) + _parseHexDigit(hex.charCodeAt(8));
|
||||
return new Color(new RGBA(r, g, b, a / 255));
|
||||
}
|
||||
if (length === 4) {
|
||||
// #RGB format
|
||||
const r = _parseHexDigit(hex.charCodeAt(1));
|
||||
const g = _parseHexDigit(hex.charCodeAt(2));
|
||||
const b = _parseHexDigit(hex.charCodeAt(3));
|
||||
return new Color(new RGBA(16 * r + r, 16 * g + g, 16 * b + b));
|
||||
}
|
||||
if (length === 5) {
|
||||
// #RGBA format
|
||||
const r = _parseHexDigit(hex.charCodeAt(1));
|
||||
const g = _parseHexDigit(hex.charCodeAt(2));
|
||||
const b = _parseHexDigit(hex.charCodeAt(3));
|
||||
const a = _parseHexDigit(hex.charCodeAt(4));
|
||||
return new Color(new RGBA(16 * r + r, 16 * g + g, 16 * b + b, (16 * a + a) / 255));
|
||||
}
|
||||
// Invalid color
|
||||
return null;
|
||||
}
|
||||
CSS.parseHex = parseHex;
|
||||
function _parseHexDigit(charCode) {
|
||||
switch (charCode) {
|
||||
case 48 /* CharCode.Digit0 */: return 0;
|
||||
case 49 /* CharCode.Digit1 */: return 1;
|
||||
case 50 /* CharCode.Digit2 */: return 2;
|
||||
case 51 /* CharCode.Digit3 */: return 3;
|
||||
case 52 /* CharCode.Digit4 */: return 4;
|
||||
case 53 /* CharCode.Digit5 */: return 5;
|
||||
case 54 /* CharCode.Digit6 */: return 6;
|
||||
case 55 /* CharCode.Digit7 */: return 7;
|
||||
case 56 /* CharCode.Digit8 */: return 8;
|
||||
case 57 /* CharCode.Digit9 */: return 9;
|
||||
case 97 /* CharCode.a */: return 10;
|
||||
case 65 /* CharCode.A */: return 10;
|
||||
case 98 /* CharCode.b */: return 11;
|
||||
case 66 /* CharCode.B */: return 11;
|
||||
case 99 /* CharCode.c */: return 12;
|
||||
case 67 /* CharCode.C */: return 12;
|
||||
case 100 /* CharCode.d */: return 13;
|
||||
case 68 /* CharCode.D */: return 13;
|
||||
case 101 /* CharCode.e */: return 14;
|
||||
case 69 /* CharCode.E */: return 14;
|
||||
case 102 /* CharCode.f */: return 15;
|
||||
case 70 /* CharCode.F */: return 15;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
})(Format.CSS || (Format.CSS = {}));
|
||||
})(Color.Format || (Color.Format = {}));
|
||||
})(Color || (Color = {}));
|
||||
|
||||
export { Color, HSLA, HSVA, RGBA };
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
import { safeIntl } from './date.js';
|
||||
import { Lazy } from './lazy.js';
|
||||
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
// When comparing large numbers of strings it's better for performance to create an
|
||||
// Intl.Collator object and use the function provided by its compare property
|
||||
// than it is to use String.prototype.localeCompare()
|
||||
// A collator with numeric sorting enabled, and no sensitivity to case, accents or diacritics.
|
||||
const intlFileNameCollatorBaseNumeric = new Lazy(() => {
|
||||
const collator = safeIntl.Collator(undefined, { numeric: true, sensitivity: 'base' }).value;
|
||||
return {
|
||||
collator,
|
||||
collatorIsNumeric: collator.resolvedOptions().numeric
|
||||
};
|
||||
});
|
||||
// A collator with numeric sorting enabled.
|
||||
new Lazy(() => {
|
||||
const collator = safeIntl.Collator(undefined, { numeric: true }).value;
|
||||
return {
|
||||
collator
|
||||
};
|
||||
});
|
||||
// A collator with numeric sorting enabled, and sensitivity to accents and diacritics but not case.
|
||||
new Lazy(() => {
|
||||
const collator = safeIntl.Collator(undefined, { numeric: true, sensitivity: 'accent' }).value;
|
||||
return {
|
||||
collator
|
||||
};
|
||||
});
|
||||
/** Compares filenames without distinguishing the name from the extension. Disambiguates by unicode comparison. */
|
||||
function compareFileNames(one, other, caseSensitive = false) {
|
||||
const a = one || '';
|
||||
const b = other || '';
|
||||
const result = intlFileNameCollatorBaseNumeric.value.collator.compare(a, b);
|
||||
// Using the numeric option will make compare(`foo1`, `foo01`) === 0. Disambiguate.
|
||||
if (intlFileNameCollatorBaseNumeric.value.collatorIsNumeric && result === 0 && a !== b) {
|
||||
return a < b ? -1 : 1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
function compareAnything(one, other, lookFor) {
|
||||
const elementAName = one.toLowerCase();
|
||||
const elementBName = other.toLowerCase();
|
||||
// Sort prefix matches over non prefix matches
|
||||
const prefixCompare = compareByPrefix(one, other, lookFor);
|
||||
if (prefixCompare) {
|
||||
return prefixCompare;
|
||||
}
|
||||
// Sort suffix matches over non suffix matches
|
||||
const elementASuffixMatch = elementAName.endsWith(lookFor);
|
||||
const elementBSuffixMatch = elementBName.endsWith(lookFor);
|
||||
if (elementASuffixMatch !== elementBSuffixMatch) {
|
||||
return elementASuffixMatch ? -1 : 1;
|
||||
}
|
||||
// Understand file names
|
||||
const r = compareFileNames(elementAName, elementBName);
|
||||
if (r !== 0) {
|
||||
return r;
|
||||
}
|
||||
// Compare by name
|
||||
return elementAName.localeCompare(elementBName);
|
||||
}
|
||||
function compareByPrefix(one, other, lookFor) {
|
||||
const elementAName = one.toLowerCase();
|
||||
const elementBName = other.toLowerCase();
|
||||
// Sort prefix matches over non prefix matches
|
||||
const elementAPrefixMatch = elementAName.startsWith(lookFor);
|
||||
const elementBPrefixMatch = elementBName.startsWith(lookFor);
|
||||
if (elementAPrefixMatch !== elementBPrefixMatch) {
|
||||
return elementAPrefixMatch ? -1 : 1;
|
||||
}
|
||||
// Same prefix: Sort shorter matches to the top to have those on top that match more precisely
|
||||
else if (elementAPrefixMatch && elementBPrefixMatch) {
|
||||
if (elementAName.length < elementBName.length) {
|
||||
return -1;
|
||||
}
|
||||
if (elementAName.length > elementBName.length) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
export { compareAnything, compareByPrefix, compareFileNames };
|
||||
+133
@@ -0,0 +1,133 @@
|
||||
import { distinct } from './arrays.js';
|
||||
import { Iterable } from './iterator.js';
|
||||
import { generateUuid } from './uuid.js';
|
||||
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
function createStringDataTransferItem(stringOrPromise, id) {
|
||||
return {
|
||||
id,
|
||||
asString: async () => stringOrPromise,
|
||||
asFile: () => undefined,
|
||||
value: typeof stringOrPromise === 'string' ? stringOrPromise : undefined,
|
||||
};
|
||||
}
|
||||
function createFileDataTransferItem(fileName, uri, data, id) {
|
||||
const file = { id: generateUuid(), name: fileName, uri, data };
|
||||
return {
|
||||
id,
|
||||
asString: async () => '',
|
||||
asFile: () => file,
|
||||
value: undefined,
|
||||
};
|
||||
}
|
||||
class VSDataTransfer {
|
||||
constructor() {
|
||||
this._entries = new Map();
|
||||
}
|
||||
get size() {
|
||||
let size = 0;
|
||||
for (const _ of this._entries) {
|
||||
size++;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
has(mimeType) {
|
||||
return this._entries.has(this.toKey(mimeType));
|
||||
}
|
||||
matches(pattern) {
|
||||
const mimes = [...this._entries.keys()];
|
||||
if (Iterable.some(this, ([_, item]) => item.asFile())) {
|
||||
mimes.push('files');
|
||||
}
|
||||
return matchesMimeType_normalized(normalizeMimeType(pattern), mimes);
|
||||
}
|
||||
get(mimeType) {
|
||||
return this._entries.get(this.toKey(mimeType))?.[0];
|
||||
}
|
||||
/**
|
||||
* Add a new entry to this data transfer.
|
||||
*
|
||||
* This does not replace existing entries for `mimeType`.
|
||||
*/
|
||||
append(mimeType, value) {
|
||||
const existing = this._entries.get(mimeType);
|
||||
if (existing) {
|
||||
existing.push(value);
|
||||
}
|
||||
else {
|
||||
this._entries.set(this.toKey(mimeType), [value]);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Set the entry for a given mime type.
|
||||
*
|
||||
* This replaces all existing entries for `mimeType`.
|
||||
*/
|
||||
replace(mimeType, value) {
|
||||
this._entries.set(this.toKey(mimeType), [value]);
|
||||
}
|
||||
/**
|
||||
* Remove all entries for `mimeType`.
|
||||
*/
|
||||
delete(mimeType) {
|
||||
this._entries.delete(this.toKey(mimeType));
|
||||
}
|
||||
/**
|
||||
* Iterate over all `[mime, item]` pairs in this data transfer.
|
||||
*
|
||||
* There may be multiple entries for each mime type.
|
||||
*/
|
||||
*[Symbol.iterator]() {
|
||||
for (const [mine, items] of this._entries) {
|
||||
for (const item of items) {
|
||||
yield [mine, item];
|
||||
}
|
||||
}
|
||||
}
|
||||
toKey(mimeType) {
|
||||
return normalizeMimeType(mimeType);
|
||||
}
|
||||
}
|
||||
function normalizeMimeType(mimeType) {
|
||||
return mimeType.toLowerCase();
|
||||
}
|
||||
function matchesMimeType(pattern, mimeTypes) {
|
||||
return matchesMimeType_normalized(normalizeMimeType(pattern), mimeTypes.map(normalizeMimeType));
|
||||
}
|
||||
function matchesMimeType_normalized(normalizedPattern, normalizedMimeTypes) {
|
||||
// Anything wildcard
|
||||
if (normalizedPattern === '*/*') {
|
||||
return normalizedMimeTypes.length > 0;
|
||||
}
|
||||
// Exact match
|
||||
if (normalizedMimeTypes.includes(normalizedPattern)) {
|
||||
return true;
|
||||
}
|
||||
// Wildcard, such as `image/*`
|
||||
const wildcard = normalizedPattern.match(/^([a-z]+)\/([a-z]+|\*)$/i);
|
||||
if (!wildcard) {
|
||||
return false;
|
||||
}
|
||||
const [_, type, subtype] = wildcard;
|
||||
if (subtype === '*') {
|
||||
return normalizedMimeTypes.some(mime => mime.startsWith(type + '/'));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
const UriList = Object.freeze({
|
||||
// http://amundsen.com/hypermedia/urilist/
|
||||
create: (entries) => {
|
||||
return distinct(entries.map(x => x.toString())).join('\r\n');
|
||||
},
|
||||
split: (str) => {
|
||||
return str.split('\r\n');
|
||||
},
|
||||
parse: (str) => {
|
||||
return UriList.split(str).filter(value => !value.startsWith('#'));
|
||||
}
|
||||
});
|
||||
|
||||
export { UriList, VSDataTransfer, createFileDataTransferItem, createStringDataTransferItem, matchesMimeType };
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
import { Lazy } from './lazy.js';
|
||||
import { LANGUAGE_DEFAULT } from './platform.js';
|
||||
|
||||
const safeIntl = {
|
||||
DateTimeFormat(locales, options) {
|
||||
return new Lazy(() => {
|
||||
try {
|
||||
return new Intl.DateTimeFormat(locales, options);
|
||||
}
|
||||
catch {
|
||||
return new Intl.DateTimeFormat(undefined, options);
|
||||
}
|
||||
});
|
||||
},
|
||||
Collator(locales, options) {
|
||||
return new Lazy(() => {
|
||||
try {
|
||||
return new Intl.Collator(locales, options);
|
||||
}
|
||||
catch {
|
||||
return new Intl.Collator(undefined, options);
|
||||
}
|
||||
});
|
||||
},
|
||||
Segmenter(locales, options) {
|
||||
return new Lazy(() => {
|
||||
try {
|
||||
return new Intl.Segmenter(locales, options);
|
||||
}
|
||||
catch {
|
||||
return new Intl.Segmenter(undefined, options);
|
||||
}
|
||||
});
|
||||
},
|
||||
Locale(tag, options) {
|
||||
return new Lazy(() => {
|
||||
try {
|
||||
return new Intl.Locale(tag, options);
|
||||
}
|
||||
catch {
|
||||
return new Intl.Locale(LANGUAGE_DEFAULT, options);
|
||||
}
|
||||
});
|
||||
},
|
||||
NumberFormat(locales, options) {
|
||||
return new Lazy(() => {
|
||||
try {
|
||||
return new Intl.NumberFormat(locales, options);
|
||||
}
|
||||
catch {
|
||||
return new Intl.NumberFormat(undefined, options);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export { safeIntl };
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
function memoize(_target, key, descriptor) {
|
||||
let fnKey = null;
|
||||
let fn = null;
|
||||
if (typeof descriptor.value === 'function') {
|
||||
fnKey = 'value';
|
||||
fn = descriptor.value;
|
||||
if (fn.length !== 0) {
|
||||
console.warn('Memoize should only be used in functions with zero parameters');
|
||||
}
|
||||
}
|
||||
else if (typeof descriptor.get === 'function') {
|
||||
fnKey = 'get';
|
||||
fn = descriptor.get;
|
||||
}
|
||||
if (!fn) {
|
||||
throw new Error('not supported');
|
||||
}
|
||||
const memoizeKey = `$memoize$${key}`;
|
||||
descriptor[fnKey] = function (...args) {
|
||||
if (!this.hasOwnProperty(memoizeKey)) {
|
||||
Object.defineProperty(this, memoizeKey, {
|
||||
configurable: false,
|
||||
enumerable: false,
|
||||
writable: false,
|
||||
value: fn.apply(this, args)
|
||||
});
|
||||
}
|
||||
// eslint-disable-next-line local/code-no-any-casts
|
||||
return this[memoizeKey];
|
||||
};
|
||||
}
|
||||
|
||||
export { memoize };
|
||||
+902
@@ -0,0 +1,902 @@
|
||||
import { DiffChange } from './diffChange.js';
|
||||
import { stringHash } from '../hash.js';
|
||||
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
class StringDiffSequence {
|
||||
constructor(source) {
|
||||
this.source = source;
|
||||
}
|
||||
getElements() {
|
||||
const source = this.source;
|
||||
const characters = new Int32Array(source.length);
|
||||
for (let i = 0, len = source.length; i < len; i++) {
|
||||
characters[i] = source.charCodeAt(i);
|
||||
}
|
||||
return characters;
|
||||
}
|
||||
}
|
||||
function stringDiff(original, modified, pretty) {
|
||||
return new LcsDiff(new StringDiffSequence(original), new StringDiffSequence(modified)).ComputeDiff(pretty).changes;
|
||||
}
|
||||
//
|
||||
// The code below has been ported from a C# implementation in VS
|
||||
//
|
||||
class Debug {
|
||||
static Assert(condition, message) {
|
||||
if (!condition) {
|
||||
throw new Error(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
class MyArray {
|
||||
/**
|
||||
* Copies a range of elements from an Array starting at the specified source index and pastes
|
||||
* them to another Array starting at the specified destination index. The length and the indexes
|
||||
* are specified as 64-bit integers.
|
||||
* sourceArray:
|
||||
* The Array that contains the data to copy.
|
||||
* sourceIndex:
|
||||
* A 64-bit integer that represents the index in the sourceArray at which copying begins.
|
||||
* destinationArray:
|
||||
* The Array that receives the data.
|
||||
* destinationIndex:
|
||||
* A 64-bit integer that represents the index in the destinationArray at which storing begins.
|
||||
* length:
|
||||
* A 64-bit integer that represents the number of elements to copy.
|
||||
*/
|
||||
static Copy(sourceArray, sourceIndex, destinationArray, destinationIndex, length) {
|
||||
for (let i = 0; i < length; i++) {
|
||||
destinationArray[destinationIndex + i] = sourceArray[sourceIndex + i];
|
||||
}
|
||||
}
|
||||
static Copy2(sourceArray, sourceIndex, destinationArray, destinationIndex, length) {
|
||||
for (let i = 0; i < length; i++) {
|
||||
destinationArray[destinationIndex + i] = sourceArray[sourceIndex + i];
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* A utility class which helps to create the set of DiffChanges from
|
||||
* a difference operation. This class accepts original DiffElements and
|
||||
* modified DiffElements that are involved in a particular change. The
|
||||
* MarkNextChange() method can be called to mark the separation between
|
||||
* distinct changes. At the end, the Changes property can be called to retrieve
|
||||
* the constructed changes.
|
||||
*/
|
||||
class DiffChangeHelper {
|
||||
/**
|
||||
* Constructs a new DiffChangeHelper for the given DiffSequences.
|
||||
*/
|
||||
constructor() {
|
||||
this.m_changes = [];
|
||||
this.m_originalStart = 1073741824 /* Constants.MAX_SAFE_SMALL_INTEGER */;
|
||||
this.m_modifiedStart = 1073741824 /* Constants.MAX_SAFE_SMALL_INTEGER */;
|
||||
this.m_originalCount = 0;
|
||||
this.m_modifiedCount = 0;
|
||||
}
|
||||
/**
|
||||
* Marks the beginning of the next change in the set of differences.
|
||||
*/
|
||||
MarkNextChange() {
|
||||
// Only add to the list if there is something to add
|
||||
if (this.m_originalCount > 0 || this.m_modifiedCount > 0) {
|
||||
// Add the new change to our list
|
||||
this.m_changes.push(new DiffChange(this.m_originalStart, this.m_originalCount, this.m_modifiedStart, this.m_modifiedCount));
|
||||
}
|
||||
// Reset for the next change
|
||||
this.m_originalCount = 0;
|
||||
this.m_modifiedCount = 0;
|
||||
this.m_originalStart = 1073741824 /* Constants.MAX_SAFE_SMALL_INTEGER */;
|
||||
this.m_modifiedStart = 1073741824 /* Constants.MAX_SAFE_SMALL_INTEGER */;
|
||||
}
|
||||
/**
|
||||
* Adds the original element at the given position to the elements
|
||||
* affected by the current change. The modified index gives context
|
||||
* to the change position with respect to the original sequence.
|
||||
* @param originalIndex The index of the original element to add.
|
||||
* @param modifiedIndex The index of the modified element that provides corresponding position in the modified sequence.
|
||||
*/
|
||||
AddOriginalElement(originalIndex, modifiedIndex) {
|
||||
// The 'true' start index is the smallest of the ones we've seen
|
||||
this.m_originalStart = Math.min(this.m_originalStart, originalIndex);
|
||||
this.m_modifiedStart = Math.min(this.m_modifiedStart, modifiedIndex);
|
||||
this.m_originalCount++;
|
||||
}
|
||||
/**
|
||||
* Adds the modified element at the given position to the elements
|
||||
* affected by the current change. The original index gives context
|
||||
* to the change position with respect to the modified sequence.
|
||||
* @param originalIndex The index of the original element that provides corresponding position in the original sequence.
|
||||
* @param modifiedIndex The index of the modified element to add.
|
||||
*/
|
||||
AddModifiedElement(originalIndex, modifiedIndex) {
|
||||
// The 'true' start index is the smallest of the ones we've seen
|
||||
this.m_originalStart = Math.min(this.m_originalStart, originalIndex);
|
||||
this.m_modifiedStart = Math.min(this.m_modifiedStart, modifiedIndex);
|
||||
this.m_modifiedCount++;
|
||||
}
|
||||
/**
|
||||
* Retrieves all of the changes marked by the class.
|
||||
*/
|
||||
getChanges() {
|
||||
if (this.m_originalCount > 0 || this.m_modifiedCount > 0) {
|
||||
// Finish up on whatever is left
|
||||
this.MarkNextChange();
|
||||
}
|
||||
return this.m_changes;
|
||||
}
|
||||
/**
|
||||
* Retrieves all of the changes marked by the class in the reverse order
|
||||
*/
|
||||
getReverseChanges() {
|
||||
if (this.m_originalCount > 0 || this.m_modifiedCount > 0) {
|
||||
// Finish up on whatever is left
|
||||
this.MarkNextChange();
|
||||
}
|
||||
this.m_changes.reverse();
|
||||
return this.m_changes;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* An implementation of the difference algorithm described in
|
||||
* "An O(ND) Difference Algorithm and its variations" by Eugene W. Myers
|
||||
*/
|
||||
class LcsDiff {
|
||||
/**
|
||||
* Constructs the DiffFinder
|
||||
*/
|
||||
constructor(originalSequence, modifiedSequence, continueProcessingPredicate = null) {
|
||||
this.ContinueProcessingPredicate = continueProcessingPredicate;
|
||||
this._originalSequence = originalSequence;
|
||||
this._modifiedSequence = modifiedSequence;
|
||||
const [originalStringElements, originalElementsOrHash, originalHasStrings] = LcsDiff._getElements(originalSequence);
|
||||
const [modifiedStringElements, modifiedElementsOrHash, modifiedHasStrings] = LcsDiff._getElements(modifiedSequence);
|
||||
this._hasStrings = (originalHasStrings && modifiedHasStrings);
|
||||
this._originalStringElements = originalStringElements;
|
||||
this._originalElementsOrHash = originalElementsOrHash;
|
||||
this._modifiedStringElements = modifiedStringElements;
|
||||
this._modifiedElementsOrHash = modifiedElementsOrHash;
|
||||
this.m_forwardHistory = [];
|
||||
this.m_reverseHistory = [];
|
||||
}
|
||||
static _isStringArray(arr) {
|
||||
return (arr.length > 0 && typeof arr[0] === 'string');
|
||||
}
|
||||
static _getElements(sequence) {
|
||||
const elements = sequence.getElements();
|
||||
if (LcsDiff._isStringArray(elements)) {
|
||||
const hashes = new Int32Array(elements.length);
|
||||
for (let i = 0, len = elements.length; i < len; i++) {
|
||||
hashes[i] = stringHash(elements[i], 0);
|
||||
}
|
||||
return [elements, hashes, true];
|
||||
}
|
||||
if (elements instanceof Int32Array) {
|
||||
return [[], elements, false];
|
||||
}
|
||||
return [[], new Int32Array(elements), false];
|
||||
}
|
||||
ElementsAreEqual(originalIndex, newIndex) {
|
||||
if (this._originalElementsOrHash[originalIndex] !== this._modifiedElementsOrHash[newIndex]) {
|
||||
return false;
|
||||
}
|
||||
return (this._hasStrings ? this._originalStringElements[originalIndex] === this._modifiedStringElements[newIndex] : true);
|
||||
}
|
||||
ElementsAreStrictEqual(originalIndex, newIndex) {
|
||||
if (!this.ElementsAreEqual(originalIndex, newIndex)) {
|
||||
return false;
|
||||
}
|
||||
const originalElement = LcsDiff._getStrictElement(this._originalSequence, originalIndex);
|
||||
const modifiedElement = LcsDiff._getStrictElement(this._modifiedSequence, newIndex);
|
||||
return (originalElement === modifiedElement);
|
||||
}
|
||||
static _getStrictElement(sequence, index) {
|
||||
if (typeof sequence.getStrictElement === 'function') {
|
||||
return sequence.getStrictElement(index);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
OriginalElementsAreEqual(index1, index2) {
|
||||
if (this._originalElementsOrHash[index1] !== this._originalElementsOrHash[index2]) {
|
||||
return false;
|
||||
}
|
||||
return (this._hasStrings ? this._originalStringElements[index1] === this._originalStringElements[index2] : true);
|
||||
}
|
||||
ModifiedElementsAreEqual(index1, index2) {
|
||||
if (this._modifiedElementsOrHash[index1] !== this._modifiedElementsOrHash[index2]) {
|
||||
return false;
|
||||
}
|
||||
return (this._hasStrings ? this._modifiedStringElements[index1] === this._modifiedStringElements[index2] : true);
|
||||
}
|
||||
ComputeDiff(pretty) {
|
||||
return this._ComputeDiff(0, this._originalElementsOrHash.length - 1, 0, this._modifiedElementsOrHash.length - 1, pretty);
|
||||
}
|
||||
/**
|
||||
* Computes the differences between the original and modified input
|
||||
* sequences on the bounded range.
|
||||
* @returns An array of the differences between the two input sequences.
|
||||
*/
|
||||
_ComputeDiff(originalStart, originalEnd, modifiedStart, modifiedEnd, pretty) {
|
||||
const quitEarlyArr = [false];
|
||||
let changes = this.ComputeDiffRecursive(originalStart, originalEnd, modifiedStart, modifiedEnd, quitEarlyArr);
|
||||
if (pretty) {
|
||||
// We have to clean up the computed diff to be more intuitive
|
||||
// but it turns out this cannot be done correctly until the entire set
|
||||
// of diffs have been computed
|
||||
changes = this.PrettifyChanges(changes);
|
||||
}
|
||||
return {
|
||||
quitEarly: quitEarlyArr[0],
|
||||
changes: changes
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Private helper method which computes the differences on the bounded range
|
||||
* recursively.
|
||||
* @returns An array of the differences between the two input sequences.
|
||||
*/
|
||||
ComputeDiffRecursive(originalStart, originalEnd, modifiedStart, modifiedEnd, quitEarlyArr) {
|
||||
quitEarlyArr[0] = false;
|
||||
// Find the start of the differences
|
||||
while (originalStart <= originalEnd && modifiedStart <= modifiedEnd && this.ElementsAreEqual(originalStart, modifiedStart)) {
|
||||
originalStart++;
|
||||
modifiedStart++;
|
||||
}
|
||||
// Find the end of the differences
|
||||
while (originalEnd >= originalStart && modifiedEnd >= modifiedStart && this.ElementsAreEqual(originalEnd, modifiedEnd)) {
|
||||
originalEnd--;
|
||||
modifiedEnd--;
|
||||
}
|
||||
// In the special case where we either have all insertions or all deletions or the sequences are identical
|
||||
if (originalStart > originalEnd || modifiedStart > modifiedEnd) {
|
||||
let changes;
|
||||
if (modifiedStart <= modifiedEnd) {
|
||||
Debug.Assert(originalStart === originalEnd + 1, 'originalStart should only be one more than originalEnd');
|
||||
// All insertions
|
||||
changes = [
|
||||
new DiffChange(originalStart, 0, modifiedStart, modifiedEnd - modifiedStart + 1)
|
||||
];
|
||||
}
|
||||
else if (originalStart <= originalEnd) {
|
||||
Debug.Assert(modifiedStart === modifiedEnd + 1, 'modifiedStart should only be one more than modifiedEnd');
|
||||
// All deletions
|
||||
changes = [
|
||||
new DiffChange(originalStart, originalEnd - originalStart + 1, modifiedStart, 0)
|
||||
];
|
||||
}
|
||||
else {
|
||||
Debug.Assert(originalStart === originalEnd + 1, 'originalStart should only be one more than originalEnd');
|
||||
Debug.Assert(modifiedStart === modifiedEnd + 1, 'modifiedStart should only be one more than modifiedEnd');
|
||||
// Identical sequences - No differences
|
||||
changes = [];
|
||||
}
|
||||
return changes;
|
||||
}
|
||||
// This problem can be solved using the Divide-And-Conquer technique.
|
||||
const midOriginalArr = [0];
|
||||
const midModifiedArr = [0];
|
||||
const result = this.ComputeRecursionPoint(originalStart, originalEnd, modifiedStart, modifiedEnd, midOriginalArr, midModifiedArr, quitEarlyArr);
|
||||
const midOriginal = midOriginalArr[0];
|
||||
const midModified = midModifiedArr[0];
|
||||
if (result !== null) {
|
||||
// Result is not-null when there was enough memory to compute the changes while
|
||||
// searching for the recursion point
|
||||
return result;
|
||||
}
|
||||
else if (!quitEarlyArr[0]) {
|
||||
// We can break the problem down recursively by finding the changes in the
|
||||
// First Half: (originalStart, modifiedStart) to (midOriginal, midModified)
|
||||
// Second Half: (midOriginal + 1, minModified + 1) to (originalEnd, modifiedEnd)
|
||||
// NOTE: ComputeDiff() is inclusive, therefore the second range starts on the next point
|
||||
const leftChanges = this.ComputeDiffRecursive(originalStart, midOriginal, modifiedStart, midModified, quitEarlyArr);
|
||||
let rightChanges = [];
|
||||
if (!quitEarlyArr[0]) {
|
||||
rightChanges = this.ComputeDiffRecursive(midOriginal + 1, originalEnd, midModified + 1, modifiedEnd, quitEarlyArr);
|
||||
}
|
||||
else {
|
||||
// We didn't have time to finish the first half, so we don't have time to compute this half.
|
||||
// Consider the entire rest of the sequence different.
|
||||
rightChanges = [
|
||||
new DiffChange(midOriginal + 1, originalEnd - (midOriginal + 1) + 1, midModified + 1, modifiedEnd - (midModified + 1) + 1)
|
||||
];
|
||||
}
|
||||
return this.ConcatenateChanges(leftChanges, rightChanges);
|
||||
}
|
||||
// If we hit here, we quit early, and so can't return anything meaningful
|
||||
return [
|
||||
new DiffChange(originalStart, originalEnd - originalStart + 1, modifiedStart, modifiedEnd - modifiedStart + 1)
|
||||
];
|
||||
}
|
||||
WALKTRACE(diagonalForwardBase, diagonalForwardStart, diagonalForwardEnd, diagonalForwardOffset, diagonalReverseBase, diagonalReverseStart, diagonalReverseEnd, diagonalReverseOffset, forwardPoints, reversePoints, originalIndex, originalEnd, midOriginalArr, modifiedIndex, modifiedEnd, midModifiedArr, deltaIsEven, quitEarlyArr) {
|
||||
let forwardChanges = null;
|
||||
let reverseChanges = null;
|
||||
// First, walk backward through the forward diagonals history
|
||||
let changeHelper = new DiffChangeHelper();
|
||||
let diagonalMin = diagonalForwardStart;
|
||||
let diagonalMax = diagonalForwardEnd;
|
||||
let diagonalRelative = (midOriginalArr[0] - midModifiedArr[0]) - diagonalForwardOffset;
|
||||
let lastOriginalIndex = -1073741824 /* Constants.MIN_SAFE_SMALL_INTEGER */;
|
||||
let historyIndex = this.m_forwardHistory.length - 1;
|
||||
do {
|
||||
// Get the diagonal index from the relative diagonal number
|
||||
const diagonal = diagonalRelative + diagonalForwardBase;
|
||||
// Figure out where we came from
|
||||
if (diagonal === diagonalMin || (diagonal < diagonalMax && forwardPoints[diagonal - 1] < forwardPoints[diagonal + 1])) {
|
||||
// Vertical line (the element is an insert)
|
||||
originalIndex = forwardPoints[diagonal + 1];
|
||||
modifiedIndex = originalIndex - diagonalRelative - diagonalForwardOffset;
|
||||
if (originalIndex < lastOriginalIndex) {
|
||||
changeHelper.MarkNextChange();
|
||||
}
|
||||
lastOriginalIndex = originalIndex;
|
||||
changeHelper.AddModifiedElement(originalIndex + 1, modifiedIndex);
|
||||
diagonalRelative = (diagonal + 1) - diagonalForwardBase; //Setup for the next iteration
|
||||
}
|
||||
else {
|
||||
// Horizontal line (the element is a deletion)
|
||||
originalIndex = forwardPoints[diagonal - 1] + 1;
|
||||
modifiedIndex = originalIndex - diagonalRelative - diagonalForwardOffset;
|
||||
if (originalIndex < lastOriginalIndex) {
|
||||
changeHelper.MarkNextChange();
|
||||
}
|
||||
lastOriginalIndex = originalIndex - 1;
|
||||
changeHelper.AddOriginalElement(originalIndex, modifiedIndex + 1);
|
||||
diagonalRelative = (diagonal - 1) - diagonalForwardBase; //Setup for the next iteration
|
||||
}
|
||||
if (historyIndex >= 0) {
|
||||
forwardPoints = this.m_forwardHistory[historyIndex];
|
||||
diagonalForwardBase = forwardPoints[0]; //We stored this in the first spot
|
||||
diagonalMin = 1;
|
||||
diagonalMax = forwardPoints.length - 1;
|
||||
}
|
||||
} while (--historyIndex >= -1);
|
||||
// Ironically, we get the forward changes as the reverse of the
|
||||
// order we added them since we technically added them backwards
|
||||
forwardChanges = changeHelper.getReverseChanges();
|
||||
if (quitEarlyArr[0]) {
|
||||
// TODO: Calculate a partial from the reverse diagonals.
|
||||
// For now, just assume everything after the midOriginal/midModified point is a diff
|
||||
let originalStartPoint = midOriginalArr[0] + 1;
|
||||
let modifiedStartPoint = midModifiedArr[0] + 1;
|
||||
if (forwardChanges !== null && forwardChanges.length > 0) {
|
||||
const lastForwardChange = forwardChanges[forwardChanges.length - 1];
|
||||
originalStartPoint = Math.max(originalStartPoint, lastForwardChange.getOriginalEnd());
|
||||
modifiedStartPoint = Math.max(modifiedStartPoint, lastForwardChange.getModifiedEnd());
|
||||
}
|
||||
reverseChanges = [
|
||||
new DiffChange(originalStartPoint, originalEnd - originalStartPoint + 1, modifiedStartPoint, modifiedEnd - modifiedStartPoint + 1)
|
||||
];
|
||||
}
|
||||
else {
|
||||
// Now walk backward through the reverse diagonals history
|
||||
changeHelper = new DiffChangeHelper();
|
||||
diagonalMin = diagonalReverseStart;
|
||||
diagonalMax = diagonalReverseEnd;
|
||||
diagonalRelative = (midOriginalArr[0] - midModifiedArr[0]) - diagonalReverseOffset;
|
||||
lastOriginalIndex = 1073741824 /* Constants.MAX_SAFE_SMALL_INTEGER */;
|
||||
historyIndex = (deltaIsEven) ? this.m_reverseHistory.length - 1 : this.m_reverseHistory.length - 2;
|
||||
do {
|
||||
// Get the diagonal index from the relative diagonal number
|
||||
const diagonal = diagonalRelative + diagonalReverseBase;
|
||||
// Figure out where we came from
|
||||
if (diagonal === diagonalMin || (diagonal < diagonalMax && reversePoints[diagonal - 1] >= reversePoints[diagonal + 1])) {
|
||||
// Horizontal line (the element is a deletion))
|
||||
originalIndex = reversePoints[diagonal + 1] - 1;
|
||||
modifiedIndex = originalIndex - diagonalRelative - diagonalReverseOffset;
|
||||
if (originalIndex > lastOriginalIndex) {
|
||||
changeHelper.MarkNextChange();
|
||||
}
|
||||
lastOriginalIndex = originalIndex + 1;
|
||||
changeHelper.AddOriginalElement(originalIndex + 1, modifiedIndex + 1);
|
||||
diagonalRelative = (diagonal + 1) - diagonalReverseBase; //Setup for the next iteration
|
||||
}
|
||||
else {
|
||||
// Vertical line (the element is an insertion)
|
||||
originalIndex = reversePoints[diagonal - 1];
|
||||
modifiedIndex = originalIndex - diagonalRelative - diagonalReverseOffset;
|
||||
if (originalIndex > lastOriginalIndex) {
|
||||
changeHelper.MarkNextChange();
|
||||
}
|
||||
lastOriginalIndex = originalIndex;
|
||||
changeHelper.AddModifiedElement(originalIndex + 1, modifiedIndex + 1);
|
||||
diagonalRelative = (diagonal - 1) - diagonalReverseBase; //Setup for the next iteration
|
||||
}
|
||||
if (historyIndex >= 0) {
|
||||
reversePoints = this.m_reverseHistory[historyIndex];
|
||||
diagonalReverseBase = reversePoints[0]; //We stored this in the first spot
|
||||
diagonalMin = 1;
|
||||
diagonalMax = reversePoints.length - 1;
|
||||
}
|
||||
} while (--historyIndex >= -1);
|
||||
// There are cases where the reverse history will find diffs that
|
||||
// are correct, but not intuitive, so we need shift them.
|
||||
reverseChanges = changeHelper.getChanges();
|
||||
}
|
||||
return this.ConcatenateChanges(forwardChanges, reverseChanges);
|
||||
}
|
||||
/**
|
||||
* Given the range to compute the diff on, this method finds the point:
|
||||
* (midOriginal, midModified)
|
||||
* that exists in the middle of the LCS of the two sequences and
|
||||
* is the point at which the LCS problem may be broken down recursively.
|
||||
* This method will try to keep the LCS trace in memory. If the LCS recursion
|
||||
* point is calculated and the full trace is available in memory, then this method
|
||||
* will return the change list.
|
||||
* @param originalStart The start bound of the original sequence range
|
||||
* @param originalEnd The end bound of the original sequence range
|
||||
* @param modifiedStart The start bound of the modified sequence range
|
||||
* @param modifiedEnd The end bound of the modified sequence range
|
||||
* @param midOriginal The middle point of the original sequence range
|
||||
* @param midModified The middle point of the modified sequence range
|
||||
* @returns The diff changes, if available, otherwise null
|
||||
*/
|
||||
ComputeRecursionPoint(originalStart, originalEnd, modifiedStart, modifiedEnd, midOriginalArr, midModifiedArr, quitEarlyArr) {
|
||||
let originalIndex = 0, modifiedIndex = 0;
|
||||
let diagonalForwardStart = 0, diagonalForwardEnd = 0;
|
||||
let diagonalReverseStart = 0, diagonalReverseEnd = 0;
|
||||
// To traverse the edit graph and produce the proper LCS, our actual
|
||||
// start position is just outside the given boundary
|
||||
originalStart--;
|
||||
modifiedStart--;
|
||||
// We set these up to make the compiler happy, but they will
|
||||
// be replaced before we return with the actual recursion point
|
||||
midOriginalArr[0] = 0;
|
||||
midModifiedArr[0] = 0;
|
||||
// Clear out the history
|
||||
this.m_forwardHistory = [];
|
||||
this.m_reverseHistory = [];
|
||||
// Each cell in the two arrays corresponds to a diagonal in the edit graph.
|
||||
// The integer value in the cell represents the originalIndex of the furthest
|
||||
// reaching point found so far that ends in that diagonal.
|
||||
// The modifiedIndex can be computed mathematically from the originalIndex and the diagonal number.
|
||||
const maxDifferences = (originalEnd - originalStart) + (modifiedEnd - modifiedStart);
|
||||
const numDiagonals = maxDifferences + 1;
|
||||
const forwardPoints = new Int32Array(numDiagonals);
|
||||
const reversePoints = new Int32Array(numDiagonals);
|
||||
// diagonalForwardBase: Index into forwardPoints of the diagonal which passes through (originalStart, modifiedStart)
|
||||
// diagonalReverseBase: Index into reversePoints of the diagonal which passes through (originalEnd, modifiedEnd)
|
||||
const diagonalForwardBase = (modifiedEnd - modifiedStart);
|
||||
const diagonalReverseBase = (originalEnd - originalStart);
|
||||
// diagonalForwardOffset: Geometric offset which allows modifiedIndex to be computed from originalIndex and the
|
||||
// diagonal number (relative to diagonalForwardBase)
|
||||
// diagonalReverseOffset: Geometric offset which allows modifiedIndex to be computed from originalIndex and the
|
||||
// diagonal number (relative to diagonalReverseBase)
|
||||
const diagonalForwardOffset = (originalStart - modifiedStart);
|
||||
const diagonalReverseOffset = (originalEnd - modifiedEnd);
|
||||
// delta: The difference between the end diagonal and the start diagonal. This is used to relate diagonal numbers
|
||||
// relative to the start diagonal with diagonal numbers relative to the end diagonal.
|
||||
// The Even/Oddn-ness of this delta is important for determining when we should check for overlap
|
||||
const delta = diagonalReverseBase - diagonalForwardBase;
|
||||
const deltaIsEven = (delta % 2 === 0);
|
||||
// Here we set up the start and end points as the furthest points found so far
|
||||
// in both the forward and reverse directions, respectively
|
||||
forwardPoints[diagonalForwardBase] = originalStart;
|
||||
reversePoints[diagonalReverseBase] = originalEnd;
|
||||
// Remember if we quit early, and thus need to do a best-effort result instead of a real result.
|
||||
quitEarlyArr[0] = false;
|
||||
// A couple of points:
|
||||
// --With this method, we iterate on the number of differences between the two sequences.
|
||||
// The more differences there actually are, the longer this will take.
|
||||
// --Also, as the number of differences increases, we have to search on diagonals further
|
||||
// away from the reference diagonal (which is diagonalForwardBase for forward, diagonalReverseBase for reverse).
|
||||
// --We extend on even diagonals (relative to the reference diagonal) only when numDifferences
|
||||
// is even and odd diagonals only when numDifferences is odd.
|
||||
for (let numDifferences = 1; numDifferences <= (maxDifferences / 2) + 1; numDifferences++) {
|
||||
let furthestOriginalIndex = 0;
|
||||
let furthestModifiedIndex = 0;
|
||||
// Run the algorithm in the forward direction
|
||||
diagonalForwardStart = this.ClipDiagonalBound(diagonalForwardBase - numDifferences, numDifferences, diagonalForwardBase, numDiagonals);
|
||||
diagonalForwardEnd = this.ClipDiagonalBound(diagonalForwardBase + numDifferences, numDifferences, diagonalForwardBase, numDiagonals);
|
||||
for (let diagonal = diagonalForwardStart; diagonal <= diagonalForwardEnd; diagonal += 2) {
|
||||
// STEP 1: We extend the furthest reaching point in the present diagonal
|
||||
// by looking at the diagonals above and below and picking the one whose point
|
||||
// is further away from the start point (originalStart, modifiedStart)
|
||||
if (diagonal === diagonalForwardStart || (diagonal < diagonalForwardEnd && forwardPoints[diagonal - 1] < forwardPoints[diagonal + 1])) {
|
||||
originalIndex = forwardPoints[diagonal + 1];
|
||||
}
|
||||
else {
|
||||
originalIndex = forwardPoints[diagonal - 1] + 1;
|
||||
}
|
||||
modifiedIndex = originalIndex - (diagonal - diagonalForwardBase) - diagonalForwardOffset;
|
||||
// Save the current originalIndex so we can test for false overlap in step 3
|
||||
const tempOriginalIndex = originalIndex;
|
||||
// STEP 2: We can continue to extend the furthest reaching point in the present diagonal
|
||||
// so long as the elements are equal.
|
||||
while (originalIndex < originalEnd && modifiedIndex < modifiedEnd && this.ElementsAreEqual(originalIndex + 1, modifiedIndex + 1)) {
|
||||
originalIndex++;
|
||||
modifiedIndex++;
|
||||
}
|
||||
forwardPoints[diagonal] = originalIndex;
|
||||
if (originalIndex + modifiedIndex > furthestOriginalIndex + furthestModifiedIndex) {
|
||||
furthestOriginalIndex = originalIndex;
|
||||
furthestModifiedIndex = modifiedIndex;
|
||||
}
|
||||
// STEP 3: If delta is odd (overlap first happens on forward when delta is odd)
|
||||
// and diagonal is in the range of reverse diagonals computed for numDifferences-1
|
||||
// (the previous iteration; we haven't computed reverse diagonals for numDifferences yet)
|
||||
// then check for overlap.
|
||||
if (!deltaIsEven && Math.abs(diagonal - diagonalReverseBase) <= (numDifferences - 1)) {
|
||||
if (originalIndex >= reversePoints[diagonal]) {
|
||||
midOriginalArr[0] = originalIndex;
|
||||
midModifiedArr[0] = modifiedIndex;
|
||||
if (tempOriginalIndex <= reversePoints[diagonal] && 1447 /* LocalConstants.MaxDifferencesHistory */ > 0 && numDifferences <= (1447 /* LocalConstants.MaxDifferencesHistory */ + 1)) {
|
||||
// BINGO! We overlapped, and we have the full trace in memory!
|
||||
return this.WALKTRACE(diagonalForwardBase, diagonalForwardStart, diagonalForwardEnd, diagonalForwardOffset, diagonalReverseBase, diagonalReverseStart, diagonalReverseEnd, diagonalReverseOffset, forwardPoints, reversePoints, originalIndex, originalEnd, midOriginalArr, modifiedIndex, modifiedEnd, midModifiedArr, deltaIsEven, quitEarlyArr);
|
||||
}
|
||||
else {
|
||||
// Either false overlap, or we didn't have enough memory for the full trace
|
||||
// Just return the recursion point
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Check to see if we should be quitting early, before moving on to the next iteration.
|
||||
const matchLengthOfLongest = ((furthestOriginalIndex - originalStart) + (furthestModifiedIndex - modifiedStart) - numDifferences) / 2;
|
||||
if (this.ContinueProcessingPredicate !== null && !this.ContinueProcessingPredicate(furthestOriginalIndex, matchLengthOfLongest)) {
|
||||
// We can't finish, so skip ahead to generating a result from what we have.
|
||||
quitEarlyArr[0] = true;
|
||||
// Use the furthest distance we got in the forward direction.
|
||||
midOriginalArr[0] = furthestOriginalIndex;
|
||||
midModifiedArr[0] = furthestModifiedIndex;
|
||||
if (matchLengthOfLongest > 0 && 1447 /* LocalConstants.MaxDifferencesHistory */ > 0 && numDifferences <= (1447 /* LocalConstants.MaxDifferencesHistory */ + 1)) {
|
||||
// Enough of the history is in memory to walk it backwards
|
||||
return this.WALKTRACE(diagonalForwardBase, diagonalForwardStart, diagonalForwardEnd, diagonalForwardOffset, diagonalReverseBase, diagonalReverseStart, diagonalReverseEnd, diagonalReverseOffset, forwardPoints, reversePoints, originalIndex, originalEnd, midOriginalArr, modifiedIndex, modifiedEnd, midModifiedArr, deltaIsEven, quitEarlyArr);
|
||||
}
|
||||
else {
|
||||
// We didn't actually remember enough of the history.
|
||||
//Since we are quitting the diff early, we need to shift back the originalStart and modified start
|
||||
//back into the boundary limits since we decremented their value above beyond the boundary limit.
|
||||
originalStart++;
|
||||
modifiedStart++;
|
||||
return [
|
||||
new DiffChange(originalStart, originalEnd - originalStart + 1, modifiedStart, modifiedEnd - modifiedStart + 1)
|
||||
];
|
||||
}
|
||||
}
|
||||
// Run the algorithm in the reverse direction
|
||||
diagonalReverseStart = this.ClipDiagonalBound(diagonalReverseBase - numDifferences, numDifferences, diagonalReverseBase, numDiagonals);
|
||||
diagonalReverseEnd = this.ClipDiagonalBound(diagonalReverseBase + numDifferences, numDifferences, diagonalReverseBase, numDiagonals);
|
||||
for (let diagonal = diagonalReverseStart; diagonal <= diagonalReverseEnd; diagonal += 2) {
|
||||
// STEP 1: We extend the furthest reaching point in the present diagonal
|
||||
// by looking at the diagonals above and below and picking the one whose point
|
||||
// is further away from the start point (originalEnd, modifiedEnd)
|
||||
if (diagonal === diagonalReverseStart || (diagonal < diagonalReverseEnd && reversePoints[diagonal - 1] >= reversePoints[diagonal + 1])) {
|
||||
originalIndex = reversePoints[diagonal + 1] - 1;
|
||||
}
|
||||
else {
|
||||
originalIndex = reversePoints[diagonal - 1];
|
||||
}
|
||||
modifiedIndex = originalIndex - (diagonal - diagonalReverseBase) - diagonalReverseOffset;
|
||||
// Save the current originalIndex so we can test for false overlap
|
||||
const tempOriginalIndex = originalIndex;
|
||||
// STEP 2: We can continue to extend the furthest reaching point in the present diagonal
|
||||
// as long as the elements are equal.
|
||||
while (originalIndex > originalStart && modifiedIndex > modifiedStart && this.ElementsAreEqual(originalIndex, modifiedIndex)) {
|
||||
originalIndex--;
|
||||
modifiedIndex--;
|
||||
}
|
||||
reversePoints[diagonal] = originalIndex;
|
||||
// STEP 4: If delta is even (overlap first happens on reverse when delta is even)
|
||||
// and diagonal is in the range of forward diagonals computed for numDifferences
|
||||
// then check for overlap.
|
||||
if (deltaIsEven && Math.abs(diagonal - diagonalForwardBase) <= numDifferences) {
|
||||
if (originalIndex <= forwardPoints[diagonal]) {
|
||||
midOriginalArr[0] = originalIndex;
|
||||
midModifiedArr[0] = modifiedIndex;
|
||||
if (tempOriginalIndex >= forwardPoints[diagonal] && 1447 /* LocalConstants.MaxDifferencesHistory */ > 0 && numDifferences <= (1447 /* LocalConstants.MaxDifferencesHistory */ + 1)) {
|
||||
// BINGO! We overlapped, and we have the full trace in memory!
|
||||
return this.WALKTRACE(diagonalForwardBase, diagonalForwardStart, diagonalForwardEnd, diagonalForwardOffset, diagonalReverseBase, diagonalReverseStart, diagonalReverseEnd, diagonalReverseOffset, forwardPoints, reversePoints, originalIndex, originalEnd, midOriginalArr, modifiedIndex, modifiedEnd, midModifiedArr, deltaIsEven, quitEarlyArr);
|
||||
}
|
||||
else {
|
||||
// Either false overlap, or we didn't have enough memory for the full trace
|
||||
// Just return the recursion point
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Save current vectors to history before the next iteration
|
||||
if (numDifferences <= 1447 /* LocalConstants.MaxDifferencesHistory */) {
|
||||
// We are allocating space for one extra int, which we fill with
|
||||
// the index of the diagonal base index
|
||||
let temp = new Int32Array(diagonalForwardEnd - diagonalForwardStart + 2);
|
||||
temp[0] = diagonalForwardBase - diagonalForwardStart + 1;
|
||||
MyArray.Copy2(forwardPoints, diagonalForwardStart, temp, 1, diagonalForwardEnd - diagonalForwardStart + 1);
|
||||
this.m_forwardHistory.push(temp);
|
||||
temp = new Int32Array(diagonalReverseEnd - diagonalReverseStart + 2);
|
||||
temp[0] = diagonalReverseBase - diagonalReverseStart + 1;
|
||||
MyArray.Copy2(reversePoints, diagonalReverseStart, temp, 1, diagonalReverseEnd - diagonalReverseStart + 1);
|
||||
this.m_reverseHistory.push(temp);
|
||||
}
|
||||
}
|
||||
// If we got here, then we have the full trace in history. We just have to convert it to a change list
|
||||
// NOTE: This part is a bit messy
|
||||
return this.WALKTRACE(diagonalForwardBase, diagonalForwardStart, diagonalForwardEnd, diagonalForwardOffset, diagonalReverseBase, diagonalReverseStart, diagonalReverseEnd, diagonalReverseOffset, forwardPoints, reversePoints, originalIndex, originalEnd, midOriginalArr, modifiedIndex, modifiedEnd, midModifiedArr, deltaIsEven, quitEarlyArr);
|
||||
}
|
||||
/**
|
||||
* Shifts the given changes to provide a more intuitive diff.
|
||||
* While the first element in a diff matches the first element after the diff,
|
||||
* we shift the diff down.
|
||||
*
|
||||
* @param changes The list of changes to shift
|
||||
* @returns The shifted changes
|
||||
*/
|
||||
PrettifyChanges(changes) {
|
||||
// Shift all the changes down first
|
||||
for (let i = 0; i < changes.length; i++) {
|
||||
const change = changes[i];
|
||||
const originalStop = (i < changes.length - 1) ? changes[i + 1].originalStart : this._originalElementsOrHash.length;
|
||||
const modifiedStop = (i < changes.length - 1) ? changes[i + 1].modifiedStart : this._modifiedElementsOrHash.length;
|
||||
const checkOriginal = change.originalLength > 0;
|
||||
const checkModified = change.modifiedLength > 0;
|
||||
while (change.originalStart + change.originalLength < originalStop
|
||||
&& change.modifiedStart + change.modifiedLength < modifiedStop
|
||||
&& (!checkOriginal || this.OriginalElementsAreEqual(change.originalStart, change.originalStart + change.originalLength))
|
||||
&& (!checkModified || this.ModifiedElementsAreEqual(change.modifiedStart, change.modifiedStart + change.modifiedLength))) {
|
||||
const startStrictEqual = this.ElementsAreStrictEqual(change.originalStart, change.modifiedStart);
|
||||
const endStrictEqual = this.ElementsAreStrictEqual(change.originalStart + change.originalLength, change.modifiedStart + change.modifiedLength);
|
||||
if (endStrictEqual && !startStrictEqual) {
|
||||
// moving the change down would create an equal change, but the elements are not strict equal
|
||||
break;
|
||||
}
|
||||
change.originalStart++;
|
||||
change.modifiedStart++;
|
||||
}
|
||||
const mergedChangeArr = [null];
|
||||
if (i < changes.length - 1 && this.ChangesOverlap(changes[i], changes[i + 1], mergedChangeArr)) {
|
||||
changes[i] = mergedChangeArr[0];
|
||||
changes.splice(i + 1, 1);
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// Shift changes back up until we hit empty or whitespace-only lines
|
||||
for (let i = changes.length - 1; i >= 0; i--) {
|
||||
const change = changes[i];
|
||||
let originalStop = 0;
|
||||
let modifiedStop = 0;
|
||||
if (i > 0) {
|
||||
const prevChange = changes[i - 1];
|
||||
originalStop = prevChange.originalStart + prevChange.originalLength;
|
||||
modifiedStop = prevChange.modifiedStart + prevChange.modifiedLength;
|
||||
}
|
||||
const checkOriginal = change.originalLength > 0;
|
||||
const checkModified = change.modifiedLength > 0;
|
||||
let bestDelta = 0;
|
||||
let bestScore = this._boundaryScore(change.originalStart, change.originalLength, change.modifiedStart, change.modifiedLength);
|
||||
for (let delta = 1;; delta++) {
|
||||
const originalStart = change.originalStart - delta;
|
||||
const modifiedStart = change.modifiedStart - delta;
|
||||
if (originalStart < originalStop || modifiedStart < modifiedStop) {
|
||||
break;
|
||||
}
|
||||
if (checkOriginal && !this.OriginalElementsAreEqual(originalStart, originalStart + change.originalLength)) {
|
||||
break;
|
||||
}
|
||||
if (checkModified && !this.ModifiedElementsAreEqual(modifiedStart, modifiedStart + change.modifiedLength)) {
|
||||
break;
|
||||
}
|
||||
const touchingPreviousChange = (originalStart === originalStop && modifiedStart === modifiedStop);
|
||||
const score = ((touchingPreviousChange ? 5 : 0)
|
||||
+ this._boundaryScore(originalStart, change.originalLength, modifiedStart, change.modifiedLength));
|
||||
if (score > bestScore) {
|
||||
bestScore = score;
|
||||
bestDelta = delta;
|
||||
}
|
||||
}
|
||||
change.originalStart -= bestDelta;
|
||||
change.modifiedStart -= bestDelta;
|
||||
const mergedChangeArr = [null];
|
||||
if (i > 0 && this.ChangesOverlap(changes[i - 1], changes[i], mergedChangeArr)) {
|
||||
changes[i - 1] = mergedChangeArr[0];
|
||||
changes.splice(i, 1);
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// There could be multiple longest common substrings.
|
||||
// Give preference to the ones containing longer lines
|
||||
if (this._hasStrings) {
|
||||
for (let i = 1, len = changes.length; i < len; i++) {
|
||||
const aChange = changes[i - 1];
|
||||
const bChange = changes[i];
|
||||
const matchedLength = bChange.originalStart - aChange.originalStart - aChange.originalLength;
|
||||
const aOriginalStart = aChange.originalStart;
|
||||
const bOriginalEnd = bChange.originalStart + bChange.originalLength;
|
||||
const abOriginalLength = bOriginalEnd - aOriginalStart;
|
||||
const aModifiedStart = aChange.modifiedStart;
|
||||
const bModifiedEnd = bChange.modifiedStart + bChange.modifiedLength;
|
||||
const abModifiedLength = bModifiedEnd - aModifiedStart;
|
||||
// Avoid wasting a lot of time with these searches
|
||||
if (matchedLength < 5 && abOriginalLength < 20 && abModifiedLength < 20) {
|
||||
const t = this._findBetterContiguousSequence(aOriginalStart, abOriginalLength, aModifiedStart, abModifiedLength, matchedLength);
|
||||
if (t) {
|
||||
const [originalMatchStart, modifiedMatchStart] = t;
|
||||
if (originalMatchStart !== aChange.originalStart + aChange.originalLength || modifiedMatchStart !== aChange.modifiedStart + aChange.modifiedLength) {
|
||||
// switch to another sequence that has a better score
|
||||
aChange.originalLength = originalMatchStart - aChange.originalStart;
|
||||
aChange.modifiedLength = modifiedMatchStart - aChange.modifiedStart;
|
||||
bChange.originalStart = originalMatchStart + matchedLength;
|
||||
bChange.modifiedStart = modifiedMatchStart + matchedLength;
|
||||
bChange.originalLength = bOriginalEnd - bChange.originalStart;
|
||||
bChange.modifiedLength = bModifiedEnd - bChange.modifiedStart;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return changes;
|
||||
}
|
||||
_findBetterContiguousSequence(originalStart, originalLength, modifiedStart, modifiedLength, desiredLength) {
|
||||
if (originalLength < desiredLength || modifiedLength < desiredLength) {
|
||||
return null;
|
||||
}
|
||||
const originalMax = originalStart + originalLength - desiredLength + 1;
|
||||
const modifiedMax = modifiedStart + modifiedLength - desiredLength + 1;
|
||||
let bestScore = 0;
|
||||
let bestOriginalStart = 0;
|
||||
let bestModifiedStart = 0;
|
||||
for (let i = originalStart; i < originalMax; i++) {
|
||||
for (let j = modifiedStart; j < modifiedMax; j++) {
|
||||
const score = this._contiguousSequenceScore(i, j, desiredLength);
|
||||
if (score > 0 && score > bestScore) {
|
||||
bestScore = score;
|
||||
bestOriginalStart = i;
|
||||
bestModifiedStart = j;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bestScore > 0) {
|
||||
return [bestOriginalStart, bestModifiedStart];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
_contiguousSequenceScore(originalStart, modifiedStart, length) {
|
||||
let score = 0;
|
||||
for (let l = 0; l < length; l++) {
|
||||
if (!this.ElementsAreEqual(originalStart + l, modifiedStart + l)) {
|
||||
return 0;
|
||||
}
|
||||
score += this._originalStringElements[originalStart + l].length;
|
||||
}
|
||||
return score;
|
||||
}
|
||||
_OriginalIsBoundary(index) {
|
||||
if (index <= 0 || index >= this._originalElementsOrHash.length - 1) {
|
||||
return true;
|
||||
}
|
||||
return (this._hasStrings && /^\s*$/.test(this._originalStringElements[index]));
|
||||
}
|
||||
_OriginalRegionIsBoundary(originalStart, originalLength) {
|
||||
if (this._OriginalIsBoundary(originalStart) || this._OriginalIsBoundary(originalStart - 1)) {
|
||||
return true;
|
||||
}
|
||||
if (originalLength > 0) {
|
||||
const originalEnd = originalStart + originalLength;
|
||||
if (this._OriginalIsBoundary(originalEnd - 1) || this._OriginalIsBoundary(originalEnd)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
_ModifiedIsBoundary(index) {
|
||||
if (index <= 0 || index >= this._modifiedElementsOrHash.length - 1) {
|
||||
return true;
|
||||
}
|
||||
return (this._hasStrings && /^\s*$/.test(this._modifiedStringElements[index]));
|
||||
}
|
||||
_ModifiedRegionIsBoundary(modifiedStart, modifiedLength) {
|
||||
if (this._ModifiedIsBoundary(modifiedStart) || this._ModifiedIsBoundary(modifiedStart - 1)) {
|
||||
return true;
|
||||
}
|
||||
if (modifiedLength > 0) {
|
||||
const modifiedEnd = modifiedStart + modifiedLength;
|
||||
if (this._ModifiedIsBoundary(modifiedEnd - 1) || this._ModifiedIsBoundary(modifiedEnd)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
_boundaryScore(originalStart, originalLength, modifiedStart, modifiedLength) {
|
||||
const originalScore = (this._OriginalRegionIsBoundary(originalStart, originalLength) ? 1 : 0);
|
||||
const modifiedScore = (this._ModifiedRegionIsBoundary(modifiedStart, modifiedLength) ? 1 : 0);
|
||||
return (originalScore + modifiedScore);
|
||||
}
|
||||
/**
|
||||
* Concatenates the two input DiffChange lists and returns the resulting
|
||||
* list.
|
||||
* @param The left changes
|
||||
* @param The right changes
|
||||
* @returns The concatenated list
|
||||
*/
|
||||
ConcatenateChanges(left, right) {
|
||||
const mergedChangeArr = [];
|
||||
if (left.length === 0 || right.length === 0) {
|
||||
return (right.length > 0) ? right : left;
|
||||
}
|
||||
else if (this.ChangesOverlap(left[left.length - 1], right[0], mergedChangeArr)) {
|
||||
// Since we break the problem down recursively, it is possible that we
|
||||
// might recurse in the middle of a change thereby splitting it into
|
||||
// two changes. Here in the combining stage, we detect and fuse those
|
||||
// changes back together
|
||||
const result = new Array(left.length + right.length - 1);
|
||||
MyArray.Copy(left, 0, result, 0, left.length - 1);
|
||||
result[left.length - 1] = mergedChangeArr[0];
|
||||
MyArray.Copy(right, 1, result, left.length, right.length - 1);
|
||||
return result;
|
||||
}
|
||||
else {
|
||||
const result = new Array(left.length + right.length);
|
||||
MyArray.Copy(left, 0, result, 0, left.length);
|
||||
MyArray.Copy(right, 0, result, left.length, right.length);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Returns true if the two changes overlap and can be merged into a single
|
||||
* change
|
||||
* @param left The left change
|
||||
* @param right The right change
|
||||
* @param mergedChange The merged change if the two overlap, null otherwise
|
||||
* @returns True if the two changes overlap
|
||||
*/
|
||||
ChangesOverlap(left, right, mergedChangeArr) {
|
||||
Debug.Assert(left.originalStart <= right.originalStart, 'Left change is not less than or equal to right change');
|
||||
Debug.Assert(left.modifiedStart <= right.modifiedStart, 'Left change is not less than or equal to right change');
|
||||
if (left.originalStart + left.originalLength >= right.originalStart || left.modifiedStart + left.modifiedLength >= right.modifiedStart) {
|
||||
const originalStart = left.originalStart;
|
||||
let originalLength = left.originalLength;
|
||||
const modifiedStart = left.modifiedStart;
|
||||
let modifiedLength = left.modifiedLength;
|
||||
if (left.originalStart + left.originalLength >= right.originalStart) {
|
||||
originalLength = right.originalStart + right.originalLength - left.originalStart;
|
||||
}
|
||||
if (left.modifiedStart + left.modifiedLength >= right.modifiedStart) {
|
||||
modifiedLength = right.modifiedStart + right.modifiedLength - left.modifiedStart;
|
||||
}
|
||||
mergedChangeArr[0] = new DiffChange(originalStart, originalLength, modifiedStart, modifiedLength);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
mergedChangeArr[0] = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Helper method used to clip a diagonal index to the range of valid
|
||||
* diagonals. This also decides whether or not the diagonal index,
|
||||
* if it exceeds the boundary, should be clipped to the boundary or clipped
|
||||
* one inside the boundary depending on the Even/Odd status of the boundary
|
||||
* and numDifferences.
|
||||
* @param diagonal The index of the diagonal to clip.
|
||||
* @param numDifferences The current number of differences being iterated upon.
|
||||
* @param diagonalBaseIndex The base reference diagonal.
|
||||
* @param numDiagonals The total number of diagonals.
|
||||
* @returns The clipped diagonal index.
|
||||
*/
|
||||
ClipDiagonalBound(diagonal, numDifferences, diagonalBaseIndex, numDiagonals) {
|
||||
if (diagonal >= 0 && diagonal < numDiagonals) {
|
||||
// Nothing to clip, its in range
|
||||
return diagonal;
|
||||
}
|
||||
// diagonalsBelow: The number of diagonals below the reference diagonal
|
||||
// diagonalsAbove: The number of diagonals above the reference diagonal
|
||||
const diagonalsBelow = diagonalBaseIndex;
|
||||
const diagonalsAbove = numDiagonals - diagonalBaseIndex - 1;
|
||||
const diffEven = (numDifferences % 2 === 0);
|
||||
if (diagonal < 0) {
|
||||
const lowerBoundEven = (diagonalsBelow % 2 === 0);
|
||||
return (diffEven === lowerBoundEven) ? 0 : 1;
|
||||
}
|
||||
else {
|
||||
const upperBoundEven = (diagonalsAbove % 2 === 0);
|
||||
return (diffEven === upperBoundEven) ? numDiagonals - 1 : numDiagonals - 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export { LcsDiff, StringDiffSequence, stringDiff };
|
||||
+34
@@ -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.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
/**
|
||||
* Represents information about a specific difference between two sequences.
|
||||
*/
|
||||
class DiffChange {
|
||||
/**
|
||||
* Constructs a new DiffChange with the given sequence information
|
||||
* and content.
|
||||
*/
|
||||
constructor(originalStart, originalLength, modifiedStart, modifiedLength) {
|
||||
//Debug.Assert(originalLength > 0 || modifiedLength > 0, "originalLength and modifiedLength cannot both be <= 0");
|
||||
this.originalStart = originalStart;
|
||||
this.originalLength = originalLength;
|
||||
this.modifiedStart = modifiedStart;
|
||||
this.modifiedLength = modifiedLength;
|
||||
}
|
||||
/**
|
||||
* The end point (exclusive) of the change in the original sequence.
|
||||
*/
|
||||
getOriginalEnd() {
|
||||
return this.originalStart + this.originalLength;
|
||||
}
|
||||
/**
|
||||
* The end point (exclusive) of the change in the modified sequence.
|
||||
*/
|
||||
getModifiedEnd() {
|
||||
return this.modifiedStart + this.modifiedLength;
|
||||
}
|
||||
}
|
||||
|
||||
export { DiffChange };
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
import { equals } from './arrays.js';
|
||||
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
/**
|
||||
* Compares two items for equality using strict equality.
|
||||
*/
|
||||
const strictEquals = (a, b) => a === b;
|
||||
/**
|
||||
* Checks if the items of two arrays are equal.
|
||||
* By default, strict equality is used to compare elements, but a custom equality comparer can be provided.
|
||||
*/
|
||||
function itemsEquals(itemEquals = strictEquals) {
|
||||
return (a, b) => equals(a, b, itemEquals);
|
||||
}
|
||||
/**
|
||||
* Uses `item.equals(other)` to determine equality.
|
||||
*/
|
||||
function itemEquals() {
|
||||
return (a, b) => a.equals(b);
|
||||
}
|
||||
function equalsIfDefined(equalsOrV1, v2, equals) {
|
||||
if (equals !== undefined) {
|
||||
const v1 = equalsOrV1;
|
||||
if (v1 === undefined || v1 === null || v2 === undefined || v2 === null) {
|
||||
return v2 === v1;
|
||||
}
|
||||
return equals(v1, v2);
|
||||
}
|
||||
else {
|
||||
const equals = equalsOrV1;
|
||||
return (v1, v2) => {
|
||||
if (v1 === undefined || v1 === null || v2 === undefined || v2 === null) {
|
||||
return v2 === v1;
|
||||
}
|
||||
return equals(v1, v2);
|
||||
};
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Drills into arrays (items ordered) and objects (keys unordered) and uses strict equality on everything else.
|
||||
*/
|
||||
function structuralEquals(a, b) {
|
||||
if (a === b) {
|
||||
return true;
|
||||
}
|
||||
if (Array.isArray(a) && Array.isArray(b)) {
|
||||
if (a.length !== b.length) {
|
||||
return false;
|
||||
}
|
||||
for (let i = 0; i < a.length; i++) {
|
||||
if (!structuralEquals(a[i], b[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (a && typeof a === 'object' && b && typeof b === 'object') {
|
||||
if (Object.getPrototypeOf(a) === Object.prototype && Object.getPrototypeOf(b) === Object.prototype) {
|
||||
const aObj = a;
|
||||
const bObj = b;
|
||||
const keysA = Object.keys(aObj);
|
||||
const keysB = Object.keys(bObj);
|
||||
const keysBSet = new Set(keysB);
|
||||
if (keysA.length !== keysB.length) {
|
||||
return false;
|
||||
}
|
||||
for (const key of keysA) {
|
||||
if (!keysBSet.has(key)) {
|
||||
return false;
|
||||
}
|
||||
if (!structuralEquals(aObj[key], bObj[key])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export { equalsIfDefined, itemEquals, itemsEquals, strictEquals, structuralEquals };
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
import { coalesce } from './arrays.js';
|
||||
import { isString } from './types.js';
|
||||
import { localize } from '../../nls.js';
|
||||
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
function exceptionToErrorMessage(exception, verbose) {
|
||||
if (verbose && (exception.stack || exception.stacktrace)) {
|
||||
return localize(29, "{0}: {1}", detectSystemErrorMessage(exception), stackToString(exception.stack) || stackToString(exception.stacktrace));
|
||||
}
|
||||
return detectSystemErrorMessage(exception);
|
||||
}
|
||||
function stackToString(stack) {
|
||||
if (Array.isArray(stack)) {
|
||||
return stack.join('\n');
|
||||
}
|
||||
return stack;
|
||||
}
|
||||
function detectSystemErrorMessage(exception) {
|
||||
// Custom node.js error from us
|
||||
if (exception.code === 'ERR_UNC_HOST_NOT_ALLOWED') {
|
||||
return `${exception.message}. Please update the 'security.allowedUNCHosts' setting if you want to allow this host.`;
|
||||
}
|
||||
// See https://nodejs.org/api/errors.html#errors_class_system_error
|
||||
if (typeof exception.code === 'string' && typeof exception.errno === 'number' && typeof exception.syscall === 'string') {
|
||||
return localize(30, "A system error occurred ({0})", exception.message);
|
||||
}
|
||||
return exception.message || localize(31, "An unknown error occurred. Please consult the log for more details.");
|
||||
}
|
||||
/**
|
||||
* Tries to generate a human readable error message out of the error. If the verbose parameter
|
||||
* is set to true, the error message will include stacktrace details if provided.
|
||||
*
|
||||
* @returns A string containing the error message.
|
||||
*/
|
||||
function toErrorMessage(error = null, verbose = false) {
|
||||
if (!error) {
|
||||
return localize(32, "An unknown error occurred. Please consult the log for more details.");
|
||||
}
|
||||
if (Array.isArray(error)) {
|
||||
const errors = coalesce(error);
|
||||
const msg = toErrorMessage(errors[0], verbose);
|
||||
if (errors.length > 1) {
|
||||
return localize(33, "{0} ({1} errors in total)", msg, errors.length);
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
if (isString(error)) {
|
||||
return error;
|
||||
}
|
||||
if (error.detail) {
|
||||
const detail = error.detail;
|
||||
if (detail.error) {
|
||||
return exceptionToErrorMessage(detail.error, verbose);
|
||||
}
|
||||
if (detail.exception) {
|
||||
return exceptionToErrorMessage(detail.exception, verbose);
|
||||
}
|
||||
}
|
||||
if (error.stack) {
|
||||
return exceptionToErrorMessage(error, verbose);
|
||||
}
|
||||
if (error.message) {
|
||||
return error.message;
|
||||
}
|
||||
return localize(34, "An unknown error occurred. Please consult the log for more details.");
|
||||
}
|
||||
|
||||
export { toErrorMessage };
|
||||
+163
@@ -0,0 +1,163 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
// Avoid circular dependency on EventEmitter by implementing a subset of the interface.
|
||||
class ErrorHandler {
|
||||
constructor() {
|
||||
this.listeners = [];
|
||||
this.unexpectedErrorHandler = function (e) {
|
||||
setTimeout(() => {
|
||||
if (e.stack) {
|
||||
if (ErrorNoTelemetry.isErrorNoTelemetry(e)) {
|
||||
throw new ErrorNoTelemetry(e.message + '\n\n' + e.stack);
|
||||
}
|
||||
throw new Error(e.message + '\n\n' + e.stack);
|
||||
}
|
||||
throw e;
|
||||
}, 0);
|
||||
};
|
||||
}
|
||||
emit(e) {
|
||||
this.listeners.forEach((listener) => {
|
||||
listener(e);
|
||||
});
|
||||
}
|
||||
onUnexpectedError(e) {
|
||||
this.unexpectedErrorHandler(e);
|
||||
this.emit(e);
|
||||
}
|
||||
// For external errors, we don't want the listeners to be called
|
||||
onUnexpectedExternalError(e) {
|
||||
this.unexpectedErrorHandler(e);
|
||||
}
|
||||
}
|
||||
const errorHandler = new ErrorHandler();
|
||||
/**
|
||||
* This function should only be called with errors that indicate a bug in the product.
|
||||
* E.g. buggy extensions/invalid user-input/network issues should not be able to trigger this code path.
|
||||
* If they are, this indicates there is also a bug in the product.
|
||||
*/
|
||||
function onBugIndicatingError(e) {
|
||||
errorHandler.onUnexpectedError(e);
|
||||
return undefined;
|
||||
}
|
||||
function onUnexpectedError(e) {
|
||||
// ignore errors from cancelled promises
|
||||
if (!isCancellationError(e)) {
|
||||
errorHandler.onUnexpectedError(e);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
function onUnexpectedExternalError(e) {
|
||||
// ignore errors from cancelled promises
|
||||
if (!isCancellationError(e)) {
|
||||
errorHandler.onUnexpectedExternalError(e);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
function transformErrorForSerialization(error) {
|
||||
if (error instanceof Error) {
|
||||
const { name, message, cause } = error;
|
||||
// eslint-disable-next-line local/code-no-any-casts
|
||||
const stack = error.stacktrace || error.stack;
|
||||
return {
|
||||
$isError: true,
|
||||
name,
|
||||
message,
|
||||
stack,
|
||||
noTelemetry: ErrorNoTelemetry.isErrorNoTelemetry(error),
|
||||
cause: cause ? transformErrorForSerialization(cause) : undefined,
|
||||
code: error.code
|
||||
};
|
||||
}
|
||||
// return as is
|
||||
return error;
|
||||
}
|
||||
const canceledName = 'Canceled';
|
||||
/**
|
||||
* Checks if the given error is a promise in canceled state
|
||||
*/
|
||||
function isCancellationError(error) {
|
||||
if (error instanceof CancellationError) {
|
||||
return true;
|
||||
}
|
||||
return error instanceof Error && error.name === canceledName && error.message === canceledName;
|
||||
}
|
||||
// !!!IMPORTANT!!!
|
||||
// Do NOT change this class because it is also used as an API-type.
|
||||
class CancellationError extends Error {
|
||||
constructor() {
|
||||
super(canceledName);
|
||||
this.name = this.message;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @deprecated use {@link CancellationError `new CancellationError()`} instead
|
||||
*/
|
||||
function canceled() {
|
||||
const error = new Error(canceledName);
|
||||
error.name = error.message;
|
||||
return error;
|
||||
}
|
||||
function illegalArgument(name) {
|
||||
if (name) {
|
||||
return new Error(`Illegal argument: ${name}`);
|
||||
}
|
||||
else {
|
||||
return new Error('Illegal argument');
|
||||
}
|
||||
}
|
||||
function illegalState(name) {
|
||||
if (name) {
|
||||
return new Error(`Illegal state: ${name}`);
|
||||
}
|
||||
else {
|
||||
return new Error('Illegal state');
|
||||
}
|
||||
}
|
||||
class NotSupportedError extends Error {
|
||||
constructor(message) {
|
||||
super('NotSupported');
|
||||
if (message) {
|
||||
this.message = message;
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Error that when thrown won't be logged in telemetry as an unhandled error.
|
||||
*/
|
||||
class ErrorNoTelemetry extends Error {
|
||||
constructor(msg) {
|
||||
super(msg);
|
||||
this.name = 'CodeExpectedError';
|
||||
}
|
||||
static fromError(err) {
|
||||
if (err instanceof ErrorNoTelemetry) {
|
||||
return err;
|
||||
}
|
||||
const result = new ErrorNoTelemetry();
|
||||
result.message = err.message;
|
||||
result.stack = err.stack;
|
||||
return result;
|
||||
}
|
||||
static isErrorNoTelemetry(err) {
|
||||
return err.name === 'CodeExpectedError';
|
||||
}
|
||||
}
|
||||
/**
|
||||
* This error indicates a bug.
|
||||
* Do not throw this for invalid user input.
|
||||
* Only catch this error to recover gracefully from bugs.
|
||||
*/
|
||||
class BugIndicatingError extends Error {
|
||||
constructor(message) {
|
||||
super(message || 'An unexpected bug occurred.');
|
||||
Object.setPrototypeOf(this, BugIndicatingError.prototype);
|
||||
// Because we know for sure only buggy code throws this,
|
||||
// we definitely want to break here and fix the bug.
|
||||
// debugger;
|
||||
}
|
||||
}
|
||||
|
||||
export { BugIndicatingError, CancellationError, ErrorHandler, ErrorNoTelemetry, NotSupportedError, canceled, canceledName, errorHandler, illegalArgument, illegalState, isCancellationError, onBugIndicatingError, onUnexpectedError, onUnexpectedExternalError, transformErrorForSerialization };
|
||||
+1197
File diff suppressed because it is too large
Load Diff
+147
@@ -0,0 +1,147 @@
|
||||
import { sep, posix } from './path.js';
|
||||
import { isWindows } from './platform.js';
|
||||
import { startsWithIgnoreCase } from './strings.js';
|
||||
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
function isPathSeparator(code) {
|
||||
return code === 47 /* CharCode.Slash */ || code === 92 /* CharCode.Backslash */;
|
||||
}
|
||||
/**
|
||||
* Takes a Windows OS path and changes backward slashes to forward slashes.
|
||||
* This should only be done for OS paths from Windows (or user provided paths potentially from Windows).
|
||||
* Using it on a Linux or MaxOS path might change it.
|
||||
*/
|
||||
function toSlashes(osPath) {
|
||||
return osPath.replace(/[\\/]/g, posix.sep);
|
||||
}
|
||||
/**
|
||||
* Takes a Windows OS path (using backward or forward slashes) and turns it into a posix path:
|
||||
* - turns backward slashes into forward slashes
|
||||
* - makes it absolute if it starts with a drive letter
|
||||
* This should only be done for OS paths from Windows (or user provided paths potentially from Windows).
|
||||
* Using it on a Linux or MaxOS path might change it.
|
||||
*/
|
||||
function toPosixPath(osPath) {
|
||||
if (osPath.indexOf('/') === -1) {
|
||||
osPath = toSlashes(osPath);
|
||||
}
|
||||
if (/^[a-zA-Z]:(\/|$)/.test(osPath)) { // starts with a drive letter
|
||||
osPath = '/' + osPath;
|
||||
}
|
||||
return osPath;
|
||||
}
|
||||
/**
|
||||
* Computes the _root_ this path, like `getRoot('c:\files') === c:\`,
|
||||
* `getRoot('files:///files/path') === files:///`,
|
||||
* or `getRoot('\\server\shares\path') === \\server\shares\`
|
||||
*/
|
||||
function getRoot(path, sep = posix.sep) {
|
||||
if (!path) {
|
||||
return '';
|
||||
}
|
||||
const len = path.length;
|
||||
const firstLetter = path.charCodeAt(0);
|
||||
if (isPathSeparator(firstLetter)) {
|
||||
if (isPathSeparator(path.charCodeAt(1))) {
|
||||
// UNC candidate \\localhost\shares\ddd
|
||||
// ^^^^^^^^^^^^^^^^^^^
|
||||
if (!isPathSeparator(path.charCodeAt(2))) {
|
||||
let pos = 3;
|
||||
const start = pos;
|
||||
for (; pos < len; pos++) {
|
||||
if (isPathSeparator(path.charCodeAt(pos))) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (start !== pos && !isPathSeparator(path.charCodeAt(pos + 1))) {
|
||||
pos += 1;
|
||||
for (; pos < len; pos++) {
|
||||
if (isPathSeparator(path.charCodeAt(pos))) {
|
||||
return path.slice(0, pos + 1) // consume this separator
|
||||
.replace(/[\\/]/g, sep);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// /user/far
|
||||
// ^
|
||||
return sep;
|
||||
}
|
||||
else if (isWindowsDriveLetter(firstLetter)) {
|
||||
// check for windows drive letter c:\ or c:
|
||||
if (path.charCodeAt(1) === 58 /* CharCode.Colon */) {
|
||||
if (isPathSeparator(path.charCodeAt(2))) {
|
||||
// C:\fff
|
||||
// ^^^
|
||||
return path.slice(0, 2) + sep;
|
||||
}
|
||||
else {
|
||||
// C:
|
||||
// ^^
|
||||
return path.slice(0, 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
// check for URI
|
||||
// scheme://authority/path
|
||||
// ^^^^^^^^^^^^^^^^^^^
|
||||
let pos = path.indexOf('://');
|
||||
if (pos !== -1) {
|
||||
pos += 3; // 3 -> "://".length
|
||||
for (; pos < len; pos++) {
|
||||
if (isPathSeparator(path.charCodeAt(pos))) {
|
||||
return path.slice(0, pos + 1); // consume this separator
|
||||
}
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
/**
|
||||
* @deprecated please use `IUriIdentityService.extUri.isEqualOrParent` instead. If
|
||||
* you are in a context without services, consider to pass down the `extUri` from the
|
||||
* outside, or use `extUriBiasedIgnorePathCase` if you know what you are doing.
|
||||
*/
|
||||
function isEqualOrParent(base, parentCandidate, ignoreCase, separator = sep) {
|
||||
if (base === parentCandidate) {
|
||||
return true;
|
||||
}
|
||||
if (!base || !parentCandidate) {
|
||||
return false;
|
||||
}
|
||||
if (parentCandidate.length > base.length) {
|
||||
return false;
|
||||
}
|
||||
if (ignoreCase) {
|
||||
const beginsWith = startsWithIgnoreCase(base, parentCandidate);
|
||||
if (!beginsWith) {
|
||||
return false;
|
||||
}
|
||||
if (parentCandidate.length === base.length) {
|
||||
return true; // same path, different casing
|
||||
}
|
||||
let sepOffset = parentCandidate.length;
|
||||
if (parentCandidate.charAt(parentCandidate.length - 1) === separator) {
|
||||
sepOffset--; // adjust the expected sep offset in case our candidate already ends in separator character
|
||||
}
|
||||
return base.charAt(sepOffset) === separator;
|
||||
}
|
||||
if (parentCandidate.charAt(parentCandidate.length - 1) !== separator) {
|
||||
parentCandidate += separator;
|
||||
}
|
||||
return base.indexOf(parentCandidate) === 0;
|
||||
}
|
||||
function isWindowsDriveLetter(char0) {
|
||||
return char0 >= 65 /* CharCode.A */ && char0 <= 90 /* CharCode.Z */ || char0 >= 97 /* CharCode.a */ && char0 <= 122 /* CharCode.z */;
|
||||
}
|
||||
function hasDriveLetter(path, isWindowsOS = isWindows) {
|
||||
if (isWindowsOS) {
|
||||
return isWindowsDriveLetter(path.charCodeAt(0)) && path.charCodeAt(1) === 58 /* CharCode.Colon */;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export { getRoot, hasDriveLetter, isEqualOrParent, isPathSeparator, isWindowsDriveLetter, toPosixPath, toSlashes };
|
||||
+742
@@ -0,0 +1,742 @@
|
||||
import { LRUCache } from './map.js';
|
||||
import { getKoreanAltChars } from './naturalLanguage/korean.js';
|
||||
import { startsWithIgnoreCase, isEmojiImprecise, convertSimple2RegExpPattern } from './strings.js';
|
||||
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
// Combined filters
|
||||
/**
|
||||
* @returns A filter which combines the provided set
|
||||
* of filters with an or. The *first* filters that
|
||||
* matches defined the return value of the returned
|
||||
* filter.
|
||||
*/
|
||||
function or(...filter) {
|
||||
return function (word, wordToMatchAgainst) {
|
||||
for (let i = 0, len = filter.length; i < len; i++) {
|
||||
const match = filter[i](word, wordToMatchAgainst);
|
||||
if (match) {
|
||||
return match;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
}
|
||||
// Prefix
|
||||
_matchesPrefix.bind(undefined, false);
|
||||
const matchesPrefix = _matchesPrefix.bind(undefined, true);
|
||||
function _matchesPrefix(ignoreCase, word, wordToMatchAgainst) {
|
||||
if (!wordToMatchAgainst || wordToMatchAgainst.length < word.length) {
|
||||
return null;
|
||||
}
|
||||
let matches;
|
||||
if (ignoreCase) {
|
||||
matches = startsWithIgnoreCase(wordToMatchAgainst, word);
|
||||
}
|
||||
else {
|
||||
matches = wordToMatchAgainst.indexOf(word) === 0;
|
||||
}
|
||||
if (!matches) {
|
||||
return null;
|
||||
}
|
||||
return word.length > 0 ? [{ start: 0, end: word.length }] : [];
|
||||
}
|
||||
// Contiguous Substring
|
||||
function matchesContiguousSubString(word, wordToMatchAgainst) {
|
||||
const index = wordToMatchAgainst.toLowerCase().indexOf(word.toLowerCase());
|
||||
if (index === -1) {
|
||||
return null;
|
||||
}
|
||||
return [{ start: index, end: index + word.length }];
|
||||
}
|
||||
// Substring
|
||||
function matchesSubString(word, wordToMatchAgainst) {
|
||||
return _matchesSubString(word.toLowerCase(), wordToMatchAgainst.toLowerCase(), 0, 0);
|
||||
}
|
||||
function _matchesSubString(word, wordToMatchAgainst, i, j) {
|
||||
if (i === word.length) {
|
||||
return [];
|
||||
}
|
||||
else if (j === wordToMatchAgainst.length) {
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
if (word[i] === wordToMatchAgainst[j]) {
|
||||
let result = null;
|
||||
if (result = _matchesSubString(word, wordToMatchAgainst, i + 1, j + 1)) {
|
||||
return join({ start: j, end: j + 1 }, result);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return _matchesSubString(word, wordToMatchAgainst, i, j + 1);
|
||||
}
|
||||
}
|
||||
// CamelCase
|
||||
function isLower(code) {
|
||||
return 97 /* CharCode.a */ <= code && code <= 122 /* CharCode.z */;
|
||||
}
|
||||
function isUpper(code) {
|
||||
return 65 /* CharCode.A */ <= code && code <= 90 /* CharCode.Z */;
|
||||
}
|
||||
function isNumber(code) {
|
||||
return 48 /* CharCode.Digit0 */ <= code && code <= 57 /* CharCode.Digit9 */;
|
||||
}
|
||||
function isWhitespace(code) {
|
||||
return (code === 32 /* CharCode.Space */
|
||||
|| code === 9 /* CharCode.Tab */
|
||||
|| code === 10 /* CharCode.LineFeed */
|
||||
|| code === 13 /* CharCode.CarriageReturn */);
|
||||
}
|
||||
const wordSeparators = new Set();
|
||||
// These are chosen as natural word separators based on writen text.
|
||||
// It is a subset of the word separators used by the monaco editor.
|
||||
'()[]{}<>`\'"-/;:,.?!'
|
||||
.split('')
|
||||
.forEach(s => wordSeparators.add(s.charCodeAt(0)));
|
||||
function isWordSeparator(code) {
|
||||
return isWhitespace(code) || wordSeparators.has(code);
|
||||
}
|
||||
function charactersMatch(codeA, codeB) {
|
||||
return (codeA === codeB) || (isWordSeparator(codeA) && isWordSeparator(codeB));
|
||||
}
|
||||
const alternateCharsCache = new Map();
|
||||
/**
|
||||
* Gets alternative codes to the character code passed in. This comes in the
|
||||
* form of an array of character codes, all of which must match _in order_ to
|
||||
* successfully match.
|
||||
*
|
||||
* @param code The character code to check.
|
||||
*/
|
||||
function getAlternateCodes(code) {
|
||||
if (alternateCharsCache.has(code)) {
|
||||
return alternateCharsCache.get(code);
|
||||
}
|
||||
// NOTE: This function is written in such a way that it can be extended in
|
||||
// the future, but right now the return type takes into account it's only
|
||||
// supported by a single "alt codes provider".
|
||||
// `ArrayLike<ArrayLike<number>>` is a more appropriate type if changed.
|
||||
let result;
|
||||
const codes = getKoreanAltChars(code);
|
||||
if (codes) {
|
||||
result = codes;
|
||||
}
|
||||
alternateCharsCache.set(code, result);
|
||||
return result;
|
||||
}
|
||||
function isAlphanumeric(code) {
|
||||
return isLower(code) || isUpper(code) || isNumber(code);
|
||||
}
|
||||
function join(head, tail) {
|
||||
if (tail.length === 0) {
|
||||
tail = [head];
|
||||
}
|
||||
else if (head.end === tail[0].start) {
|
||||
tail[0].start = head.start;
|
||||
}
|
||||
else {
|
||||
tail.unshift(head);
|
||||
}
|
||||
return tail;
|
||||
}
|
||||
function nextAnchor(camelCaseWord, start) {
|
||||
for (let i = start; i < camelCaseWord.length; i++) {
|
||||
const c = camelCaseWord.charCodeAt(i);
|
||||
if (isUpper(c) || isNumber(c) || (i > 0 && !isAlphanumeric(camelCaseWord.charCodeAt(i - 1)))) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return camelCaseWord.length;
|
||||
}
|
||||
function _matchesCamelCase(word, camelCaseWord, i, j) {
|
||||
if (i === word.length) {
|
||||
return [];
|
||||
}
|
||||
else if (j === camelCaseWord.length) {
|
||||
return null;
|
||||
}
|
||||
else if (word[i] !== camelCaseWord[j].toLowerCase()) {
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
let result = null;
|
||||
let nextUpperIndex = j + 1;
|
||||
result = _matchesCamelCase(word, camelCaseWord, i + 1, j + 1);
|
||||
while (!result && (nextUpperIndex = nextAnchor(camelCaseWord, nextUpperIndex)) < camelCaseWord.length) {
|
||||
result = _matchesCamelCase(word, camelCaseWord, i + 1, nextUpperIndex);
|
||||
nextUpperIndex++;
|
||||
}
|
||||
return result === null ? null : join({ start: j, end: j + 1 }, result);
|
||||
}
|
||||
}
|
||||
// Heuristic to avoid computing camel case matcher for words that don't
|
||||
// look like camelCaseWords.
|
||||
function analyzeCamelCaseWord(word) {
|
||||
let upper = 0, lower = 0, alpha = 0, numeric = 0, code = 0;
|
||||
for (let i = 0; i < word.length; i++) {
|
||||
code = word.charCodeAt(i);
|
||||
if (isUpper(code)) {
|
||||
upper++;
|
||||
}
|
||||
if (isLower(code)) {
|
||||
lower++;
|
||||
}
|
||||
if (isAlphanumeric(code)) {
|
||||
alpha++;
|
||||
}
|
||||
if (isNumber(code)) {
|
||||
numeric++;
|
||||
}
|
||||
}
|
||||
const upperPercent = upper / word.length;
|
||||
const lowerPercent = lower / word.length;
|
||||
const alphaPercent = alpha / word.length;
|
||||
const numericPercent = numeric / word.length;
|
||||
return { upperPercent, lowerPercent, alphaPercent, numericPercent };
|
||||
}
|
||||
function isUpperCaseWord(analysis) {
|
||||
const { upperPercent, lowerPercent } = analysis;
|
||||
return lowerPercent === 0 && upperPercent > 0.6;
|
||||
}
|
||||
function isCamelCaseWord(analysis) {
|
||||
const { upperPercent, lowerPercent, alphaPercent, numericPercent } = analysis;
|
||||
return lowerPercent > 0.2 && upperPercent < 0.8 && alphaPercent > 0.6 && numericPercent < 0.2;
|
||||
}
|
||||
// Heuristic to avoid computing camel case matcher for words that don't
|
||||
// look like camel case patterns.
|
||||
function isCamelCasePattern(word) {
|
||||
let upper = 0, lower = 0, code = 0, whitespace = 0;
|
||||
for (let i = 0; i < word.length; i++) {
|
||||
code = word.charCodeAt(i);
|
||||
if (isUpper(code)) {
|
||||
upper++;
|
||||
}
|
||||
if (isLower(code)) {
|
||||
lower++;
|
||||
}
|
||||
if (isWhitespace(code)) {
|
||||
whitespace++;
|
||||
}
|
||||
}
|
||||
if ((upper === 0 || lower === 0) && whitespace === 0) {
|
||||
return word.length <= 30;
|
||||
}
|
||||
else {
|
||||
return upper <= 5;
|
||||
}
|
||||
}
|
||||
function matchesCamelCase(word, camelCaseWord) {
|
||||
if (!camelCaseWord) {
|
||||
return null;
|
||||
}
|
||||
camelCaseWord = camelCaseWord.trim();
|
||||
if (camelCaseWord.length === 0) {
|
||||
return null;
|
||||
}
|
||||
if (!isCamelCasePattern(word)) {
|
||||
return null;
|
||||
}
|
||||
// TODO: Consider removing this check
|
||||
if (camelCaseWord.length > 60) {
|
||||
camelCaseWord = camelCaseWord.substring(0, 60);
|
||||
}
|
||||
const analysis = analyzeCamelCaseWord(camelCaseWord);
|
||||
if (!isCamelCaseWord(analysis)) {
|
||||
if (!isUpperCaseWord(analysis)) {
|
||||
return null;
|
||||
}
|
||||
camelCaseWord = camelCaseWord.toLowerCase();
|
||||
}
|
||||
let result = null;
|
||||
let i = 0;
|
||||
word = word.toLowerCase();
|
||||
while (i < camelCaseWord.length && (result = _matchesCamelCase(word, camelCaseWord, 0, i)) === null) {
|
||||
i = nextAnchor(camelCaseWord, i + 1);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
// Matches beginning of words supporting non-ASCII languages
|
||||
// If `contiguous` is true then matches word with beginnings of the words in the target. E.g. "pul" will match "Git: Pull"
|
||||
// Otherwise also matches sub string of the word with beginnings of the words in the target. E.g. "gp" or "g p" will match "Git: Pull"
|
||||
// Useful in cases where the target is words (e.g. command labels)
|
||||
function matchesWords(word, target, contiguous = false) {
|
||||
if (!target || target.length === 0) {
|
||||
return null;
|
||||
}
|
||||
let result = null;
|
||||
let targetIndex = 0;
|
||||
word = word.toLowerCase();
|
||||
target = target.toLowerCase();
|
||||
while (targetIndex < target.length) {
|
||||
result = _matchesWords(word, target, 0, targetIndex, contiguous);
|
||||
if (result !== null) {
|
||||
break;
|
||||
}
|
||||
targetIndex = nextWord(target, targetIndex + 1);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
function _matchesWords(word, target, wordIndex, targetIndex, contiguous) {
|
||||
let targetIndexOffset = 0;
|
||||
if (wordIndex === word.length) {
|
||||
return [];
|
||||
}
|
||||
else if (targetIndex === target.length) {
|
||||
return null;
|
||||
}
|
||||
else if (!charactersMatch(word.charCodeAt(wordIndex), target.charCodeAt(targetIndex))) {
|
||||
// Verify alternate characters before exiting
|
||||
const altChars = getAlternateCodes(word.charCodeAt(wordIndex));
|
||||
if (!altChars) {
|
||||
return null;
|
||||
}
|
||||
for (let k = 0; k < altChars.length; k++) {
|
||||
if (!charactersMatch(altChars[k], target.charCodeAt(targetIndex + k))) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
targetIndexOffset += altChars.length - 1;
|
||||
}
|
||||
let result = null;
|
||||
let nextWordIndex = targetIndex + targetIndexOffset + 1;
|
||||
result = _matchesWords(word, target, wordIndex + 1, nextWordIndex, contiguous);
|
||||
if (!contiguous) {
|
||||
while (!result && (nextWordIndex = nextWord(target, nextWordIndex)) < target.length) {
|
||||
result = _matchesWords(word, target, wordIndex + 1, nextWordIndex, contiguous);
|
||||
nextWordIndex++;
|
||||
}
|
||||
}
|
||||
if (!result) {
|
||||
return null;
|
||||
}
|
||||
// If the characters don't exactly match, then they must be word separators (see charactersMatch(...)).
|
||||
// We don't want to include this in the matches but we don't want to throw the target out all together so we return `result`.
|
||||
if (word.charCodeAt(wordIndex) !== target.charCodeAt(targetIndex)) {
|
||||
// Verify alternate characters before exiting
|
||||
const altChars = getAlternateCodes(word.charCodeAt(wordIndex));
|
||||
if (!altChars) {
|
||||
return result;
|
||||
}
|
||||
for (let k = 0; k < altChars.length; k++) {
|
||||
if (altChars[k] !== target.charCodeAt(targetIndex + k)) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
return join({ start: targetIndex, end: targetIndex + targetIndexOffset + 1 }, result);
|
||||
}
|
||||
function nextWord(word, start) {
|
||||
for (let i = start; i < word.length; i++) {
|
||||
if (isWordSeparator(word.charCodeAt(i)) ||
|
||||
(i > 0 && isWordSeparator(word.charCodeAt(i - 1)))) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return word.length;
|
||||
}
|
||||
// Fuzzy
|
||||
const fuzzyContiguousFilter = or(matchesPrefix, matchesCamelCase, matchesContiguousSubString);
|
||||
const fuzzySeparateFilter = or(matchesPrefix, matchesCamelCase, matchesSubString);
|
||||
const fuzzyRegExpCache = new LRUCache(10000); // bounded to 10000 elements
|
||||
function matchesFuzzy(word, wordToMatchAgainst, enableSeparateSubstringMatching = false) {
|
||||
if (typeof word !== 'string' || typeof wordToMatchAgainst !== 'string') {
|
||||
return null; // return early for invalid input
|
||||
}
|
||||
// Form RegExp for wildcard matches
|
||||
let regexp = fuzzyRegExpCache.get(word);
|
||||
if (!regexp) {
|
||||
regexp = new RegExp(convertSimple2RegExpPattern(word), 'i');
|
||||
fuzzyRegExpCache.set(word, regexp);
|
||||
}
|
||||
// RegExp Filter
|
||||
const match = regexp.exec(wordToMatchAgainst);
|
||||
if (match) {
|
||||
return [{ start: match.index, end: match.index + match[0].length }];
|
||||
}
|
||||
// Default Filter
|
||||
return enableSeparateSubstringMatching ? fuzzySeparateFilter(word, wordToMatchAgainst) : fuzzyContiguousFilter(word, wordToMatchAgainst);
|
||||
}
|
||||
/**
|
||||
* Match pattern against word in a fuzzy way. As in IntelliSense and faster and more
|
||||
* powerful than `matchesFuzzy`
|
||||
*/
|
||||
function matchesFuzzy2(pattern, word) {
|
||||
const score = fuzzyScore(pattern, pattern.toLowerCase(), 0, word, word.toLowerCase(), 0, { firstMatchCanBeWeak: true, boostFullMatch: true });
|
||||
return score ? createMatches(score) : null;
|
||||
}
|
||||
function anyScore(pattern, lowPattern, patternPos, word, lowWord, wordPos) {
|
||||
const max = Math.min(13, pattern.length);
|
||||
for (; patternPos < max; patternPos++) {
|
||||
const result = fuzzyScore(pattern, lowPattern, patternPos, word, lowWord, wordPos, { firstMatchCanBeWeak: true, boostFullMatch: true });
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return [0, wordPos];
|
||||
}
|
||||
//#region --- fuzzyScore ---
|
||||
function createMatches(score) {
|
||||
if (typeof score === 'undefined') {
|
||||
return [];
|
||||
}
|
||||
const res = [];
|
||||
const wordPos = score[1];
|
||||
for (let i = score.length - 1; i > 1; i--) {
|
||||
const pos = score[i] + wordPos;
|
||||
const last = res[res.length - 1];
|
||||
if (last && last.end === pos) {
|
||||
last.end = pos + 1;
|
||||
}
|
||||
else {
|
||||
res.push({ start: pos, end: pos + 1 });
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
const _maxLen = 128;
|
||||
function initTable() {
|
||||
const table = [];
|
||||
const row = [];
|
||||
for (let i = 0; i <= _maxLen; i++) {
|
||||
row[i] = 0;
|
||||
}
|
||||
for (let i = 0; i <= _maxLen; i++) {
|
||||
table.push(row.slice(0));
|
||||
}
|
||||
return table;
|
||||
}
|
||||
function initArr(maxLen) {
|
||||
const row = [];
|
||||
for (let i = 0; i <= maxLen; i++) {
|
||||
row[i] = 0;
|
||||
}
|
||||
return row;
|
||||
}
|
||||
const _minWordMatchPos = initArr(2 * _maxLen); // min word position for a certain pattern position
|
||||
const _maxWordMatchPos = initArr(2 * _maxLen); // max word position for a certain pattern position
|
||||
const _diag = initTable(); // the length of a contiguous diagonal match
|
||||
const _table = initTable();
|
||||
const _arrows = initTable();
|
||||
function isSeparatorAtPos(value, index) {
|
||||
if (index < 0 || index >= value.length) {
|
||||
return false;
|
||||
}
|
||||
const code = value.codePointAt(index);
|
||||
switch (code) {
|
||||
case 95 /* CharCode.Underline */:
|
||||
case 45 /* CharCode.Dash */:
|
||||
case 46 /* CharCode.Period */:
|
||||
case 32 /* CharCode.Space */:
|
||||
case 47 /* CharCode.Slash */:
|
||||
case 92 /* CharCode.Backslash */:
|
||||
case 39 /* CharCode.SingleQuote */:
|
||||
case 34 /* CharCode.DoubleQuote */:
|
||||
case 58 /* CharCode.Colon */:
|
||||
case 36 /* CharCode.DollarSign */:
|
||||
case 60 /* CharCode.LessThan */:
|
||||
case 62 /* CharCode.GreaterThan */:
|
||||
case 40 /* CharCode.OpenParen */:
|
||||
case 41 /* CharCode.CloseParen */:
|
||||
case 91 /* CharCode.OpenSquareBracket */:
|
||||
case 93 /* CharCode.CloseSquareBracket */:
|
||||
case 123 /* CharCode.OpenCurlyBrace */:
|
||||
case 125 /* CharCode.CloseCurlyBrace */:
|
||||
return true;
|
||||
case undefined:
|
||||
return false;
|
||||
default:
|
||||
if (isEmojiImprecise(code)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
function isWhitespaceAtPos(value, index) {
|
||||
if (index < 0 || index >= value.length) {
|
||||
return false;
|
||||
}
|
||||
const code = value.charCodeAt(index);
|
||||
switch (code) {
|
||||
case 32 /* CharCode.Space */:
|
||||
case 9 /* CharCode.Tab */:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
function isUpperCaseAtPos(pos, word, wordLow) {
|
||||
return word[pos] !== wordLow[pos];
|
||||
}
|
||||
function isPatternInWord(patternLow, patternPos, patternLen, wordLow, wordPos, wordLen, fillMinWordPosArr = false) {
|
||||
while (patternPos < patternLen && wordPos < wordLen) {
|
||||
if (patternLow[patternPos] === wordLow[wordPos]) {
|
||||
if (fillMinWordPosArr) {
|
||||
// Remember the min word position for each pattern position
|
||||
_minWordMatchPos[patternPos] = wordPos;
|
||||
}
|
||||
patternPos += 1;
|
||||
}
|
||||
wordPos += 1;
|
||||
}
|
||||
return patternPos === patternLen; // pattern must be exhausted
|
||||
}
|
||||
var FuzzyScore;
|
||||
(function (FuzzyScore) {
|
||||
/**
|
||||
* No matches and value `-100`
|
||||
*/
|
||||
FuzzyScore.Default = ([-100, 0]);
|
||||
function isDefault(score) {
|
||||
return !score || (score.length === 2 && score[0] === -100 && score[1] === 0);
|
||||
}
|
||||
FuzzyScore.isDefault = isDefault;
|
||||
})(FuzzyScore || (FuzzyScore = {}));
|
||||
class FuzzyScoreOptions {
|
||||
static { this.default = { boostFullMatch: true, firstMatchCanBeWeak: false }; }
|
||||
constructor(firstMatchCanBeWeak, boostFullMatch) {
|
||||
this.firstMatchCanBeWeak = firstMatchCanBeWeak;
|
||||
this.boostFullMatch = boostFullMatch;
|
||||
}
|
||||
}
|
||||
function fuzzyScore(pattern, patternLow, patternStart, word, wordLow, wordStart, options = FuzzyScoreOptions.default) {
|
||||
const patternLen = pattern.length > _maxLen ? _maxLen : pattern.length;
|
||||
const wordLen = word.length > _maxLen ? _maxLen : word.length;
|
||||
if (patternStart >= patternLen || wordStart >= wordLen || (patternLen - patternStart) > (wordLen - wordStart)) {
|
||||
return undefined;
|
||||
}
|
||||
// Run a simple check if the characters of pattern occur
|
||||
// (in order) at all in word. If that isn't the case we
|
||||
// stop because no match will be possible
|
||||
if (!isPatternInWord(patternLow, patternStart, patternLen, wordLow, wordStart, wordLen, true)) {
|
||||
return undefined;
|
||||
}
|
||||
// Find the max matching word position for each pattern position
|
||||
// NOTE: the min matching word position was filled in above, in the `isPatternInWord` call
|
||||
_fillInMaxWordMatchPos(patternLen, wordLen, patternStart, wordStart, patternLow, wordLow);
|
||||
let row = 1;
|
||||
let column = 1;
|
||||
let patternPos = patternStart;
|
||||
let wordPos = wordStart;
|
||||
const hasStrongFirstMatch = [false];
|
||||
// There will be a match, fill in tables
|
||||
for (row = 1, patternPos = patternStart; patternPos < patternLen; row++, patternPos++) {
|
||||
// Reduce search space to possible matching word positions and to possible access from next row
|
||||
const minWordMatchPos = _minWordMatchPos[patternPos];
|
||||
const maxWordMatchPos = _maxWordMatchPos[patternPos];
|
||||
const nextMaxWordMatchPos = (patternPos + 1 < patternLen ? _maxWordMatchPos[patternPos + 1] : wordLen);
|
||||
for (column = minWordMatchPos - wordStart + 1, wordPos = minWordMatchPos; wordPos < nextMaxWordMatchPos; column++, wordPos++) {
|
||||
let score = Number.MIN_SAFE_INTEGER;
|
||||
let canComeDiag = false;
|
||||
if (wordPos <= maxWordMatchPos) {
|
||||
score = _doScore(pattern, patternLow, patternPos, patternStart, word, wordLow, wordPos, wordLen, wordStart, _diag[row - 1][column - 1] === 0, hasStrongFirstMatch);
|
||||
}
|
||||
let diagScore = 0;
|
||||
if (score !== Number.MIN_SAFE_INTEGER) {
|
||||
canComeDiag = true;
|
||||
diagScore = score + _table[row - 1][column - 1];
|
||||
}
|
||||
const canComeLeft = wordPos > minWordMatchPos;
|
||||
const leftScore = canComeLeft ? _table[row][column - 1] + (_diag[row][column - 1] > 0 ? -5 : 0) : 0; // penalty for a gap start
|
||||
const canComeLeftLeft = wordPos > minWordMatchPos + 1 && _diag[row][column - 1] > 0;
|
||||
const leftLeftScore = canComeLeftLeft ? _table[row][column - 2] + (_diag[row][column - 2] > 0 ? -5 : 0) : 0; // penalty for a gap start
|
||||
if (canComeLeftLeft && (!canComeLeft || leftLeftScore >= leftScore) && (!canComeDiag || leftLeftScore >= diagScore)) {
|
||||
// always prefer choosing left left to jump over a diagonal because that means a match is earlier in the word
|
||||
_table[row][column] = leftLeftScore;
|
||||
_arrows[row][column] = 3 /* Arrow.LeftLeft */;
|
||||
_diag[row][column] = 0;
|
||||
}
|
||||
else if (canComeLeft && (!canComeDiag || leftScore >= diagScore)) {
|
||||
// always prefer choosing left since that means a match is earlier in the word
|
||||
_table[row][column] = leftScore;
|
||||
_arrows[row][column] = 2 /* Arrow.Left */;
|
||||
_diag[row][column] = 0;
|
||||
}
|
||||
else if (canComeDiag) {
|
||||
_table[row][column] = diagScore;
|
||||
_arrows[row][column] = 1 /* Arrow.Diag */;
|
||||
_diag[row][column] = _diag[row - 1][column - 1] + 1;
|
||||
}
|
||||
else {
|
||||
throw new Error(`not possible`);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!hasStrongFirstMatch[0] && !options.firstMatchCanBeWeak) {
|
||||
return undefined;
|
||||
}
|
||||
row--;
|
||||
column--;
|
||||
const result = [_table[row][column], wordStart];
|
||||
let backwardsDiagLength = 0;
|
||||
let maxMatchColumn = 0;
|
||||
while (row >= 1) {
|
||||
// Find the column where we go diagonally up
|
||||
let diagColumn = column;
|
||||
do {
|
||||
const arrow = _arrows[row][diagColumn];
|
||||
if (arrow === 3 /* Arrow.LeftLeft */) {
|
||||
diagColumn = diagColumn - 2;
|
||||
}
|
||||
else if (arrow === 2 /* Arrow.Left */) {
|
||||
diagColumn = diagColumn - 1;
|
||||
}
|
||||
else {
|
||||
// found the diagonal
|
||||
break;
|
||||
}
|
||||
} while (diagColumn >= 1);
|
||||
// Overturn the "forwards" decision if keeping the "backwards" diagonal would give a better match
|
||||
if (backwardsDiagLength > 1 // only if we would have a contiguous match of 3 characters
|
||||
&& patternLow[patternStart + row - 1] === wordLow[wordStart + column - 1] // only if we can do a contiguous match diagonally
|
||||
&& !isUpperCaseAtPos(diagColumn + wordStart - 1, word, wordLow) // only if the forwards chose diagonal is not an uppercase
|
||||
&& backwardsDiagLength + 1 > _diag[row][diagColumn] // only if our contiguous match would be longer than the "forwards" contiguous match
|
||||
) {
|
||||
diagColumn = column;
|
||||
}
|
||||
if (diagColumn === column) {
|
||||
// this is a contiguous match
|
||||
backwardsDiagLength++;
|
||||
}
|
||||
else {
|
||||
backwardsDiagLength = 1;
|
||||
}
|
||||
if (!maxMatchColumn) {
|
||||
// remember the last matched column
|
||||
maxMatchColumn = diagColumn;
|
||||
}
|
||||
row--;
|
||||
column = diagColumn - 1;
|
||||
result.push(column);
|
||||
}
|
||||
if (wordLen - wordStart === patternLen && options.boostFullMatch) {
|
||||
// the word matches the pattern with all characters!
|
||||
// giving the score a total match boost (to come up ahead other words)
|
||||
result[0] += 2;
|
||||
}
|
||||
// Add 1 penalty for each skipped character in the word
|
||||
const skippedCharsCount = maxMatchColumn - patternLen;
|
||||
result[0] -= skippedCharsCount;
|
||||
return result;
|
||||
}
|
||||
function _fillInMaxWordMatchPos(patternLen, wordLen, patternStart, wordStart, patternLow, wordLow) {
|
||||
let patternPos = patternLen - 1;
|
||||
let wordPos = wordLen - 1;
|
||||
while (patternPos >= patternStart && wordPos >= wordStart) {
|
||||
if (patternLow[patternPos] === wordLow[wordPos]) {
|
||||
_maxWordMatchPos[patternPos] = wordPos;
|
||||
patternPos--;
|
||||
}
|
||||
wordPos--;
|
||||
}
|
||||
}
|
||||
function _doScore(pattern, patternLow, patternPos, patternStart, word, wordLow, wordPos, wordLen, wordStart, newMatchStart, outFirstMatchStrong) {
|
||||
if (patternLow[patternPos] !== wordLow[wordPos]) {
|
||||
return Number.MIN_SAFE_INTEGER;
|
||||
}
|
||||
let score = 1;
|
||||
let isGapLocation = false;
|
||||
if (wordPos === (patternPos - patternStart)) {
|
||||
// common prefix: `foobar <-> foobaz`
|
||||
// ^^^^^
|
||||
score = pattern[patternPos] === word[wordPos] ? 7 : 5;
|
||||
}
|
||||
else if (isUpperCaseAtPos(wordPos, word, wordLow) && (wordPos === 0 || !isUpperCaseAtPos(wordPos - 1, word, wordLow))) {
|
||||
// hitting upper-case: `foo <-> forOthers`
|
||||
// ^^ ^
|
||||
score = pattern[patternPos] === word[wordPos] ? 7 : 5;
|
||||
isGapLocation = true;
|
||||
}
|
||||
else if (isSeparatorAtPos(wordLow, wordPos) && (wordPos === 0 || !isSeparatorAtPos(wordLow, wordPos - 1))) {
|
||||
// hitting a separator: `. <-> foo.bar`
|
||||
// ^
|
||||
score = 5;
|
||||
}
|
||||
else if (isSeparatorAtPos(wordLow, wordPos - 1) || isWhitespaceAtPos(wordLow, wordPos - 1)) {
|
||||
// post separator: `foo <-> bar_foo`
|
||||
// ^^^
|
||||
score = 5;
|
||||
isGapLocation = true;
|
||||
}
|
||||
if (score > 1 && patternPos === patternStart) {
|
||||
outFirstMatchStrong[0] = true;
|
||||
}
|
||||
if (!isGapLocation) {
|
||||
isGapLocation = isUpperCaseAtPos(wordPos, word, wordLow) || isSeparatorAtPos(wordLow, wordPos - 1) || isWhitespaceAtPos(wordLow, wordPos - 1);
|
||||
}
|
||||
//
|
||||
if (patternPos === patternStart) { // first character in pattern
|
||||
if (wordPos > wordStart) {
|
||||
// the first pattern character would match a word character that is not at the word start
|
||||
// so introduce a penalty to account for the gap preceding this match
|
||||
score -= isGapLocation ? 3 : 5;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (newMatchStart) {
|
||||
// this would be the beginning of a new match (i.e. there would be a gap before this location)
|
||||
score += isGapLocation ? 2 : 0;
|
||||
}
|
||||
else {
|
||||
// this is part of a contiguous match, so give it a slight bonus, but do so only if it would not be a preferred gap location
|
||||
score += isGapLocation ? 0 : 1;
|
||||
}
|
||||
}
|
||||
if (wordPos + 1 === wordLen) {
|
||||
// we always penalize gaps, but this gives unfair advantages to a match that would match the last character in the word
|
||||
// so pretend there is a gap after the last character in the word to normalize things
|
||||
score -= isGapLocation ? 3 : 5;
|
||||
}
|
||||
return score;
|
||||
}
|
||||
//#endregion
|
||||
//#region --- graceful ---
|
||||
function fuzzyScoreGracefulAggressive(pattern, lowPattern, patternPos, word, lowWord, wordPos, options) {
|
||||
return fuzzyScoreWithPermutations(pattern, lowPattern, patternPos, word, lowWord, wordPos, true, options);
|
||||
}
|
||||
function fuzzyScoreWithPermutations(pattern, lowPattern, patternPos, word, lowWord, wordPos, aggressive, options) {
|
||||
let top = fuzzyScore(pattern, lowPattern, patternPos, word, lowWord, wordPos, options);
|
||||
if (top && false) {
|
||||
// when using the original pattern yield a result we`
|
||||
// return it unless we are aggressive and try to find
|
||||
// a better alignment, e.g. `cno` -> `^co^ns^ole` or `^c^o^nsole`.
|
||||
return top;
|
||||
}
|
||||
if (pattern.length >= 3) {
|
||||
// When the pattern is long enough then try a few (max 7)
|
||||
// permutations of the pattern to find a better match. The
|
||||
// permutations only swap neighbouring characters, e.g
|
||||
// `cnoso` becomes `conso`, `cnsoo`, `cnoos`.
|
||||
const tries = Math.min(7, pattern.length - 1);
|
||||
for (let movingPatternPos = patternPos + 1; movingPatternPos < tries; movingPatternPos++) {
|
||||
const newPattern = nextTypoPermutation(pattern, movingPatternPos);
|
||||
if (newPattern) {
|
||||
const candidate = fuzzyScore(newPattern, newPattern.toLowerCase(), patternPos, word, lowWord, wordPos, options);
|
||||
if (candidate) {
|
||||
candidate[0] -= 3; // permutation penalty
|
||||
if (!top || candidate[0] > top[0]) {
|
||||
top = candidate;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return top;
|
||||
}
|
||||
function nextTypoPermutation(pattern, patternPos) {
|
||||
if (patternPos + 1 >= pattern.length) {
|
||||
return undefined;
|
||||
}
|
||||
const swap1 = pattern[patternPos];
|
||||
const swap2 = pattern[patternPos + 1];
|
||||
if (swap1 === swap2) {
|
||||
return undefined;
|
||||
}
|
||||
return pattern.slice(0, patternPos)
|
||||
+ swap2
|
||||
+ swap1
|
||||
+ pattern.slice(patternPos + 2);
|
||||
}
|
||||
//#endregion
|
||||
|
||||
export { FuzzyScore, FuzzyScoreOptions, anyScore, createMatches, fuzzyScore, fuzzyScoreGracefulAggressive, isPatternInWord, isUpper, matchesCamelCase, matchesContiguousSubString, matchesFuzzy, matchesFuzzy2, matchesPrefix, matchesSubString, matchesWords, or };
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
/**
|
||||
* Given a function, returns a function that is only calling that function once.
|
||||
*/
|
||||
function createSingleCallFunction(fn, fnDidRunCallback) {
|
||||
const _this = this;
|
||||
let didCall = false;
|
||||
let result;
|
||||
return function () {
|
||||
if (didCall) {
|
||||
return result;
|
||||
}
|
||||
didCall = true;
|
||||
{
|
||||
result = fn.apply(_this, arguments);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
export { createSingleCallFunction };
|
||||
+143
@@ -0,0 +1,143 @@
|
||||
import { fuzzyScore, createMatches } from './filters.js';
|
||||
import { sep } from './path.js';
|
||||
import { isWindows } from './platform.js';
|
||||
|
||||
const NO_SCORE2 = [undefined, []];
|
||||
function scoreFuzzy2(target, query, patternStart = 0, wordStart = 0) {
|
||||
// Score: multiple inputs
|
||||
const preparedQuery = query;
|
||||
if (preparedQuery.values && preparedQuery.values.length > 1) {
|
||||
return doScoreFuzzy2Multiple(target, preparedQuery.values, patternStart, wordStart);
|
||||
}
|
||||
// Score: single input
|
||||
return doScoreFuzzy2Single(target, query, patternStart, wordStart);
|
||||
}
|
||||
function doScoreFuzzy2Multiple(target, query, patternStart, wordStart) {
|
||||
let totalScore = 0;
|
||||
const totalMatches = [];
|
||||
for (const queryPiece of query) {
|
||||
const [score, matches] = doScoreFuzzy2Single(target, queryPiece, patternStart, wordStart);
|
||||
if (typeof score !== 'number') {
|
||||
// if a single query value does not match, return with
|
||||
// no score entirely, we require all queries to match
|
||||
return NO_SCORE2;
|
||||
}
|
||||
totalScore += score;
|
||||
totalMatches.push(...matches);
|
||||
}
|
||||
// if we have a score, ensure that the positions are
|
||||
// sorted in ascending order and distinct
|
||||
return [totalScore, normalizeMatches(totalMatches)];
|
||||
}
|
||||
function doScoreFuzzy2Single(target, query, patternStart, wordStart) {
|
||||
const score = fuzzyScore(query.original, query.originalLowercase, patternStart, target, target.toLowerCase(), wordStart, { firstMatchCanBeWeak: true, boostFullMatch: true });
|
||||
if (!score) {
|
||||
return NO_SCORE2;
|
||||
}
|
||||
return [score[0], createMatches(score)];
|
||||
}
|
||||
function normalizeMatches(matches) {
|
||||
// sort matches by start to be able to normalize
|
||||
const sortedMatches = matches.sort((matchA, matchB) => {
|
||||
return matchA.start - matchB.start;
|
||||
});
|
||||
// merge matches that overlap
|
||||
const normalizedMatches = [];
|
||||
let currentMatch = undefined;
|
||||
for (const match of sortedMatches) {
|
||||
// if we have no current match or the matches
|
||||
// do not overlap, we take it as is and remember
|
||||
// it for future merging
|
||||
if (!currentMatch || !matchOverlaps(currentMatch, match)) {
|
||||
currentMatch = match;
|
||||
normalizedMatches.push(match);
|
||||
}
|
||||
// otherwise we merge the matches
|
||||
else {
|
||||
currentMatch.start = Math.min(currentMatch.start, match.start);
|
||||
currentMatch.end = Math.max(currentMatch.end, match.end);
|
||||
}
|
||||
}
|
||||
return normalizedMatches;
|
||||
}
|
||||
function matchOverlaps(matchA, matchB) {
|
||||
if (matchA.end < matchB.start) {
|
||||
return false; // A ends before B starts
|
||||
}
|
||||
if (matchB.end < matchA.start) {
|
||||
return false; // B ends before A starts
|
||||
}
|
||||
return true;
|
||||
}
|
||||
/*
|
||||
* If a query is wrapped in quotes, the user does not want to
|
||||
* use fuzzy search for this query.
|
||||
*/
|
||||
function queryExpectsExactMatch(query) {
|
||||
return query.startsWith('"') && query.endsWith('"');
|
||||
}
|
||||
/**
|
||||
* Helper function to prepare a search value for scoring by removing unwanted characters
|
||||
* and allowing to score on multiple pieces separated by whitespace character.
|
||||
*/
|
||||
const MULTIPLE_QUERY_VALUES_SEPARATOR = ' ';
|
||||
function prepareQuery(original) {
|
||||
if (typeof original !== 'string') {
|
||||
original = '';
|
||||
}
|
||||
const originalLowercase = original.toLowerCase();
|
||||
const { pathNormalized, normalized, normalizedLowercase } = normalizeQuery(original);
|
||||
const containsPathSeparator = pathNormalized.indexOf(sep) >= 0;
|
||||
const expectExactMatch = queryExpectsExactMatch(original);
|
||||
let values = undefined;
|
||||
const originalSplit = original.split(MULTIPLE_QUERY_VALUES_SEPARATOR);
|
||||
if (originalSplit.length > 1) {
|
||||
for (const originalPiece of originalSplit) {
|
||||
const expectExactMatchPiece = queryExpectsExactMatch(originalPiece);
|
||||
const { pathNormalized: pathNormalizedPiece, normalized: normalizedPiece, normalizedLowercase: normalizedLowercasePiece } = normalizeQuery(originalPiece);
|
||||
if (normalizedPiece) {
|
||||
if (!values) {
|
||||
values = [];
|
||||
}
|
||||
values.push({
|
||||
original: originalPiece,
|
||||
originalLowercase: originalPiece.toLowerCase(),
|
||||
pathNormalized: pathNormalizedPiece,
|
||||
normalized: normalizedPiece,
|
||||
normalizedLowercase: normalizedLowercasePiece,
|
||||
expectContiguousMatch: expectExactMatchPiece
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
return { original, originalLowercase, pathNormalized, normalized, normalizedLowercase, values, containsPathSeparator, expectContiguousMatch: expectExactMatch };
|
||||
}
|
||||
function normalizeQuery(original) {
|
||||
let pathNormalized;
|
||||
if (isWindows) {
|
||||
pathNormalized = original.replace(/\//g, sep); // Help Windows users to search for paths when using slash
|
||||
}
|
||||
else {
|
||||
pathNormalized = original.replace(/\\/g, sep); // Help macOS/Linux users to search for paths when using backslash
|
||||
}
|
||||
// remove certain characters that help find better results:
|
||||
// - quotes: are used for exact match search
|
||||
// - wildcards: are used for fuzzy matching
|
||||
// - whitespace: are used to separate queries
|
||||
// - ellipsis: sometimes used to indicate any path segments
|
||||
const normalized = pathNormalized.replace(/[\*\u2026\s"]/g, '');
|
||||
return {
|
||||
pathNormalized,
|
||||
normalized,
|
||||
normalizedLowercase: normalized.toLowerCase()
|
||||
};
|
||||
}
|
||||
function pieceToQuery(arg1) {
|
||||
if (Array.isArray(arg1)) {
|
||||
return prepareQuery(arg1.map(piece => piece.original).join(MULTIPLE_QUERY_VALUES_SEPARATOR));
|
||||
}
|
||||
return prepareQuery(arg1.original);
|
||||
}
|
||||
//#endregion
|
||||
|
||||
export { pieceToQuery, prepareQuery, scoreFuzzy2 };
|
||||
+581
@@ -0,0 +1,581 @@
|
||||
import { isThenable } from './async.js';
|
||||
import { isEqualOrParent } from './extpath.js';
|
||||
import { LRUCache } from './map.js';
|
||||
import { sep, posix, basename, extname } from './path.js';
|
||||
import { isLinux } from './platform.js';
|
||||
import { ltrim, escapeRegExpCharacters, endsWithIgnoreCase, equalsIgnoreCase } from './strings.js';
|
||||
|
||||
const GLOBSTAR = '**';
|
||||
const GLOB_SPLIT = '/';
|
||||
const PATH_REGEX = '[/\\\\]'; // any slash or backslash
|
||||
const NO_PATH_REGEX = '[^/\\\\]'; // any non-slash and non-backslash
|
||||
const ALL_FORWARD_SLASHES = /\//g;
|
||||
function starsToRegExp(starCount, isLastPattern) {
|
||||
switch (starCount) {
|
||||
case 0:
|
||||
return '';
|
||||
case 1:
|
||||
return `${NO_PATH_REGEX}*?`; // 1 star matches any number of characters except path separator (/ and \) - non greedy (?)
|
||||
default:
|
||||
// Matches: (Path Sep OR Path Val followed by Path Sep) 0-many times except when it's the last pattern
|
||||
// in which case also matches (Path Sep followed by Path Val)
|
||||
// Group is non capturing because we don't need to capture at all (?:...)
|
||||
// Overall we use non-greedy matching because it could be that we match too much
|
||||
return `(?:${PATH_REGEX}|${NO_PATH_REGEX}+${PATH_REGEX}${isLastPattern ? `|${PATH_REGEX}${NO_PATH_REGEX}+` : ''})*?`;
|
||||
}
|
||||
}
|
||||
function splitGlobAware(pattern, splitChar) {
|
||||
if (!pattern) {
|
||||
return [];
|
||||
}
|
||||
const segments = [];
|
||||
let inBraces = false;
|
||||
let inBrackets = false;
|
||||
let curVal = '';
|
||||
for (const char of pattern) {
|
||||
switch (char) {
|
||||
case splitChar:
|
||||
if (!inBraces && !inBrackets) {
|
||||
segments.push(curVal);
|
||||
curVal = '';
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case '{':
|
||||
inBraces = true;
|
||||
break;
|
||||
case '}':
|
||||
inBraces = false;
|
||||
break;
|
||||
case '[':
|
||||
inBrackets = true;
|
||||
break;
|
||||
case ']':
|
||||
inBrackets = false;
|
||||
break;
|
||||
}
|
||||
curVal += char;
|
||||
}
|
||||
// Tail
|
||||
if (curVal) {
|
||||
segments.push(curVal);
|
||||
}
|
||||
return segments;
|
||||
}
|
||||
function parseRegExp(pattern) {
|
||||
if (!pattern) {
|
||||
return '';
|
||||
}
|
||||
let regEx = '';
|
||||
// Split up into segments for each slash found
|
||||
const segments = splitGlobAware(pattern, GLOB_SPLIT);
|
||||
// Special case where we only have globstars
|
||||
if (segments.every(segment => segment === GLOBSTAR)) {
|
||||
regEx = '.*';
|
||||
}
|
||||
// Build regex over segments
|
||||
else {
|
||||
let previousSegmentWasGlobStar = false;
|
||||
segments.forEach((segment, index) => {
|
||||
// Treat globstar specially
|
||||
if (segment === GLOBSTAR) {
|
||||
// if we have more than one globstar after another, just ignore it
|
||||
if (previousSegmentWasGlobStar) {
|
||||
return;
|
||||
}
|
||||
regEx += starsToRegExp(2, index === segments.length - 1);
|
||||
}
|
||||
// Anything else, not globstar
|
||||
else {
|
||||
// States
|
||||
let inBraces = false;
|
||||
let braceVal = '';
|
||||
let inBrackets = false;
|
||||
let bracketVal = '';
|
||||
for (const char of segment) {
|
||||
// Support brace expansion
|
||||
if (char !== '}' && inBraces) {
|
||||
braceVal += char;
|
||||
continue;
|
||||
}
|
||||
// Support brackets
|
||||
if (inBrackets && (char !== ']' || !bracketVal) /* ] is literally only allowed as first character in brackets to match it */) {
|
||||
let res;
|
||||
// range operator
|
||||
if (char === '-') {
|
||||
res = char;
|
||||
}
|
||||
// negation operator (only valid on first index in bracket)
|
||||
else if ((char === '^' || char === '!') && !bracketVal) {
|
||||
res = '^';
|
||||
}
|
||||
// glob split matching is not allowed within character ranges
|
||||
// see http://man7.org/linux/man-pages/man7/glob.7.html
|
||||
else if (char === GLOB_SPLIT) {
|
||||
res = '';
|
||||
}
|
||||
// anything else gets escaped
|
||||
else {
|
||||
res = escapeRegExpCharacters(char);
|
||||
}
|
||||
bracketVal += res;
|
||||
continue;
|
||||
}
|
||||
switch (char) {
|
||||
case '{':
|
||||
inBraces = true;
|
||||
continue;
|
||||
case '[':
|
||||
inBrackets = true;
|
||||
continue;
|
||||
case '}': {
|
||||
const choices = splitGlobAware(braceVal, ',');
|
||||
// Converts {foo,bar} => [foo|bar]
|
||||
const braceRegExp = `(?:${choices.map(choice => parseRegExp(choice)).join('|')})`;
|
||||
regEx += braceRegExp;
|
||||
inBraces = false;
|
||||
braceVal = '';
|
||||
break;
|
||||
}
|
||||
case ']': {
|
||||
regEx += ('[' + bracketVal + ']');
|
||||
inBrackets = false;
|
||||
bracketVal = '';
|
||||
break;
|
||||
}
|
||||
case '?':
|
||||
regEx += NO_PATH_REGEX; // 1 ? matches any single character except path separator (/ and \)
|
||||
continue;
|
||||
case '*':
|
||||
regEx += starsToRegExp(1);
|
||||
continue;
|
||||
default:
|
||||
regEx += escapeRegExpCharacters(char);
|
||||
}
|
||||
}
|
||||
// Tail: Add the slash we had split on if there is more to
|
||||
// come and the remaining pattern is not a globstar
|
||||
// For example if pattern: some/**/*.js we want the "/" after
|
||||
// some to be included in the RegEx to prevent a folder called
|
||||
// "something" to match as well.
|
||||
if (index < segments.length - 1 && // more segments to come after this
|
||||
(segments[index + 1] !== GLOBSTAR || // next segment is not **, or...
|
||||
index + 2 < segments.length // ...next segment is ** but there is more segments after that
|
||||
)) {
|
||||
regEx += PATH_REGEX;
|
||||
}
|
||||
}
|
||||
// update globstar state
|
||||
previousSegmentWasGlobStar = (segment === GLOBSTAR);
|
||||
});
|
||||
}
|
||||
return regEx;
|
||||
}
|
||||
// regexes to check for trivial glob patterns that just check for String#endsWith
|
||||
const T1 = /^\*\*\/\*\.[\w\.-]+$/; // **/*.something
|
||||
const T2 = /^\*\*\/([\w\.-]+)\/?$/; // **/something
|
||||
const T3 = /^{\*\*\/\*?[\w\.-]+\/?(,\*\*\/\*?[\w\.-]+\/?)*}$/; // {**/*.something,**/*.else} or {**/package.json,**/project.json}
|
||||
const T3_2 = /^{\*\*\/\*?[\w\.-]+(\/(\*\*)?)?(,\*\*\/\*?[\w\.-]+(\/(\*\*)?)?)*}$/; // Like T3, with optional trailing /**
|
||||
const T4 = /^\*\*((\/[\w\.-]+)+)\/?$/; // **/something/else
|
||||
const T5 = /^([\w\.-]+(\/[\w\.-]+)*)\/?$/; // something/else
|
||||
const CACHE = new LRUCache(10000); // bounded to 10000 elements
|
||||
const FALSE = function () {
|
||||
return false;
|
||||
};
|
||||
const NULL = function () {
|
||||
return null;
|
||||
};
|
||||
function parsePattern(arg1, options) {
|
||||
if (!arg1) {
|
||||
return NULL;
|
||||
}
|
||||
// Handle relative patterns
|
||||
let pattern;
|
||||
if (typeof arg1 !== 'string') {
|
||||
pattern = arg1.pattern;
|
||||
}
|
||||
else {
|
||||
pattern = arg1;
|
||||
}
|
||||
// Whitespace trimming
|
||||
pattern = pattern.trim();
|
||||
const ignoreCase = options.ignoreCase ?? false;
|
||||
const internalOptions = {
|
||||
...options,
|
||||
equals: ignoreCase ? equalsIgnoreCase : (a, b) => a === b,
|
||||
endsWith: ignoreCase ? endsWithIgnoreCase : (str, candidate) => str.endsWith(candidate),
|
||||
// TODO: the '!isLinux' part below is to keep current behavior unchanged, but it should probably be removed
|
||||
// in favor of passing correct options from the caller.
|
||||
isEqualOrParent: (base, candidate) => isEqualOrParent(base, candidate, !isLinux || ignoreCase)
|
||||
};
|
||||
// Check cache
|
||||
const patternKey = `${ignoreCase ? pattern.toLowerCase() : pattern}_${!!options.trimForExclusions}_${ignoreCase}`;
|
||||
let parsedPattern = CACHE.get(patternKey);
|
||||
if (parsedPattern) {
|
||||
return wrapRelativePattern(parsedPattern, arg1, internalOptions);
|
||||
}
|
||||
// Check for Trivials
|
||||
let match;
|
||||
if (T1.test(pattern)) {
|
||||
parsedPattern = trivia1(pattern.substring(4), pattern, internalOptions); // common pattern: **/*.txt just need endsWith check
|
||||
}
|
||||
else if (match = T2.exec(trimForExclusions(pattern, internalOptions))) { // common pattern: **/some.txt just need basename check
|
||||
parsedPattern = trivia2(match[1], pattern, internalOptions);
|
||||
}
|
||||
else if ((options.trimForExclusions ? T3_2 : T3).test(pattern)) { // repetition of common patterns (see above) {**/*.txt,**/*.png}
|
||||
parsedPattern = trivia3(pattern, internalOptions);
|
||||
}
|
||||
else if (match = T4.exec(trimForExclusions(pattern, internalOptions))) { // common pattern: **/something/else just need endsWith check
|
||||
parsedPattern = trivia4and5(match[1].substring(1), pattern, true, internalOptions);
|
||||
}
|
||||
else if (match = T5.exec(trimForExclusions(pattern, internalOptions))) { // common pattern: something/else just need equals check
|
||||
parsedPattern = trivia4and5(match[1], pattern, false, internalOptions);
|
||||
}
|
||||
// Otherwise convert to pattern
|
||||
else {
|
||||
parsedPattern = toRegExp(pattern, internalOptions);
|
||||
}
|
||||
// Cache
|
||||
CACHE.set(patternKey, parsedPattern);
|
||||
return wrapRelativePattern(parsedPattern, arg1, internalOptions);
|
||||
}
|
||||
function wrapRelativePattern(parsedPattern, arg2, options) {
|
||||
if (typeof arg2 === 'string') {
|
||||
return parsedPattern;
|
||||
}
|
||||
const wrappedPattern = function (path, basename) {
|
||||
if (!options.isEqualOrParent(path, arg2.base)) {
|
||||
// skip glob matching if `base` is not a parent of `path`
|
||||
return null;
|
||||
}
|
||||
// Given we have checked `base` being a parent of `path`,
|
||||
// we can now remove the `base` portion of the `path`
|
||||
// and only match on the remaining path components
|
||||
// For that we try to extract the portion of the `path`
|
||||
// that comes after the `base` portion. We have to account
|
||||
// for the fact that `base` might end in a path separator
|
||||
// (https://github.com/microsoft/vscode/issues/162498)
|
||||
return parsedPattern(ltrim(path.substring(arg2.base.length), sep), basename);
|
||||
};
|
||||
// Make sure to preserve associated metadata
|
||||
wrappedPattern.allBasenames = parsedPattern.allBasenames;
|
||||
wrappedPattern.allPaths = parsedPattern.allPaths;
|
||||
wrappedPattern.basenames = parsedPattern.basenames;
|
||||
wrappedPattern.patterns = parsedPattern.patterns;
|
||||
return wrappedPattern;
|
||||
}
|
||||
function trimForExclusions(pattern, options) {
|
||||
return options.trimForExclusions && pattern.endsWith('/**') ? pattern.substring(0, pattern.length - 2) : pattern; // dropping **, tailing / is dropped later
|
||||
}
|
||||
// common pattern: **/*.txt just need endsWith check
|
||||
function trivia1(base, pattern, options) {
|
||||
return function (path, basename) {
|
||||
return typeof path === 'string' && options.endsWith(path, base) ? pattern : null;
|
||||
};
|
||||
}
|
||||
// common pattern: **/some.txt just need basename check
|
||||
function trivia2(base, pattern, options) {
|
||||
const slashBase = `/${base}`;
|
||||
const backslashBase = `\\${base}`;
|
||||
const parsedPattern = function (path, basename) {
|
||||
if (typeof path !== 'string') {
|
||||
return null;
|
||||
}
|
||||
if (basename) {
|
||||
return options.equals(basename, base) ? pattern : null;
|
||||
}
|
||||
return options.equals(path, base) || options.endsWith(path, slashBase) || options.endsWith(path, backslashBase) ? pattern : null;
|
||||
};
|
||||
const basenames = [base];
|
||||
parsedPattern.basenames = basenames;
|
||||
parsedPattern.patterns = [pattern];
|
||||
parsedPattern.allBasenames = basenames;
|
||||
return parsedPattern;
|
||||
}
|
||||
// repetition of common patterns (see above) {**/*.txt,**/*.png}
|
||||
function trivia3(pattern, options) {
|
||||
const parsedPatterns = aggregateBasenameMatches(pattern.slice(1, -1)
|
||||
.split(',')
|
||||
.map(pattern => parsePattern(pattern, options))
|
||||
.filter(pattern => pattern !== NULL), pattern);
|
||||
const patternsLength = parsedPatterns.length;
|
||||
if (!patternsLength) {
|
||||
return NULL;
|
||||
}
|
||||
if (patternsLength === 1) {
|
||||
return parsedPatterns[0];
|
||||
}
|
||||
const parsedPattern = function (path, basename) {
|
||||
for (let i = 0, n = parsedPatterns.length; i < n; i++) {
|
||||
if (parsedPatterns[i](path, basename)) {
|
||||
return pattern;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
const withBasenames = parsedPatterns.find(pattern => !!pattern.allBasenames);
|
||||
if (withBasenames) {
|
||||
parsedPattern.allBasenames = withBasenames.allBasenames;
|
||||
}
|
||||
const allPaths = parsedPatterns.reduce((all, current) => current.allPaths ? all.concat(current.allPaths) : all, []);
|
||||
if (allPaths.length) {
|
||||
parsedPattern.allPaths = allPaths;
|
||||
}
|
||||
return parsedPattern;
|
||||
}
|
||||
// common patterns: **/something/else just need endsWith check, something/else just needs and equals check
|
||||
function trivia4and5(targetPath, pattern, matchPathEnds, options) {
|
||||
const usingPosixSep = sep === posix.sep;
|
||||
const nativePath = usingPosixSep ? targetPath : targetPath.replace(ALL_FORWARD_SLASHES, sep);
|
||||
const nativePathEnd = sep + nativePath;
|
||||
const targetPathEnd = posix.sep + targetPath;
|
||||
let parsedPattern;
|
||||
if (matchPathEnds) {
|
||||
parsedPattern = function (path, basename) {
|
||||
return typeof path === 'string' && ((options.equals(path, nativePath) || options.endsWith(path, nativePathEnd)) ||
|
||||
!usingPosixSep && (options.equals(path, targetPath) || options.endsWith(path, targetPathEnd))) ? pattern : null;
|
||||
};
|
||||
}
|
||||
else {
|
||||
parsedPattern = function (path, basename) {
|
||||
return typeof path === 'string' && (options.equals(path, nativePath) || (!usingPosixSep && options.equals(path, targetPath))) ? pattern : null;
|
||||
};
|
||||
}
|
||||
parsedPattern.allPaths = [(matchPathEnds ? '*/' : './') + targetPath];
|
||||
return parsedPattern;
|
||||
}
|
||||
function toRegExp(pattern, options) {
|
||||
try {
|
||||
const regExp = new RegExp(`^${parseRegExp(pattern)}$`, options.ignoreCase ? 'i' : undefined);
|
||||
return function (path) {
|
||||
regExp.lastIndex = 0; // reset RegExp to its initial state to reuse it!
|
||||
return typeof path === 'string' && regExp.test(path) ? pattern : null;
|
||||
};
|
||||
}
|
||||
catch {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
function match(arg1, path, options) {
|
||||
if (!arg1 || typeof path !== 'string') {
|
||||
return false;
|
||||
}
|
||||
return parse(arg1, options)(path);
|
||||
}
|
||||
function parse(arg1, options = {}) {
|
||||
if (!arg1) {
|
||||
return FALSE;
|
||||
}
|
||||
// Glob with String
|
||||
if (typeof arg1 === 'string' || isRelativePattern(arg1)) {
|
||||
const parsedPattern = parsePattern(arg1, options);
|
||||
if (parsedPattern === NULL) {
|
||||
return FALSE;
|
||||
}
|
||||
const resultPattern = function (path, basename) {
|
||||
return !!parsedPattern(path, basename);
|
||||
};
|
||||
if (parsedPattern.allBasenames) {
|
||||
resultPattern.allBasenames = parsedPattern.allBasenames;
|
||||
}
|
||||
if (parsedPattern.allPaths) {
|
||||
resultPattern.allPaths = parsedPattern.allPaths;
|
||||
}
|
||||
return resultPattern;
|
||||
}
|
||||
// Glob with Expression
|
||||
return parsedExpression(arg1, options);
|
||||
}
|
||||
function isRelativePattern(obj) {
|
||||
const rp = obj;
|
||||
if (!rp) {
|
||||
return false;
|
||||
}
|
||||
return typeof rp.base === 'string' && typeof rp.pattern === 'string';
|
||||
}
|
||||
function parsedExpression(expression, options) {
|
||||
const parsedPatterns = aggregateBasenameMatches(Object.getOwnPropertyNames(expression)
|
||||
.map(pattern => parseExpressionPattern(pattern, expression[pattern], options))
|
||||
.filter(pattern => pattern !== NULL));
|
||||
const patternsLength = parsedPatterns.length;
|
||||
if (!patternsLength) {
|
||||
return NULL;
|
||||
}
|
||||
if (!parsedPatterns.some(parsedPattern => !!parsedPattern.requiresSiblings)) {
|
||||
if (patternsLength === 1) {
|
||||
return parsedPatterns[0];
|
||||
}
|
||||
const resultExpression = function (path, basename) {
|
||||
let resultPromises = undefined;
|
||||
for (let i = 0, n = parsedPatterns.length; i < n; i++) {
|
||||
const result = parsedPatterns[i](path, basename);
|
||||
if (typeof result === 'string') {
|
||||
return result; // immediately return as soon as the first expression matches
|
||||
}
|
||||
// If the result is a promise, we have to keep it for
|
||||
// later processing and await the result properly.
|
||||
if (isThenable(result)) {
|
||||
if (!resultPromises) {
|
||||
resultPromises = [];
|
||||
}
|
||||
resultPromises.push(result);
|
||||
}
|
||||
}
|
||||
// With result promises, we have to loop over each and
|
||||
// await the result before we can return any result.
|
||||
if (resultPromises) {
|
||||
return (async () => {
|
||||
for (const resultPromise of resultPromises) {
|
||||
const result = await resultPromise;
|
||||
if (typeof result === 'string') {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
})();
|
||||
}
|
||||
return null;
|
||||
};
|
||||
const withBasenames = parsedPatterns.find(pattern => !!pattern.allBasenames);
|
||||
if (withBasenames) {
|
||||
resultExpression.allBasenames = withBasenames.allBasenames;
|
||||
}
|
||||
const allPaths = parsedPatterns.reduce((all, current) => current.allPaths ? all.concat(current.allPaths) : all, []);
|
||||
if (allPaths.length) {
|
||||
resultExpression.allPaths = allPaths;
|
||||
}
|
||||
return resultExpression;
|
||||
}
|
||||
const resultExpression = function (path, base, hasSibling) {
|
||||
let name = undefined;
|
||||
let resultPromises = undefined;
|
||||
for (let i = 0, n = parsedPatterns.length; i < n; i++) {
|
||||
// Pattern matches path
|
||||
const parsedPattern = parsedPatterns[i];
|
||||
if (parsedPattern.requiresSiblings && hasSibling) {
|
||||
if (!base) {
|
||||
base = basename(path);
|
||||
}
|
||||
if (!name) {
|
||||
name = base.substring(0, base.length - extname(path).length);
|
||||
}
|
||||
}
|
||||
const result = parsedPattern(path, base, name, hasSibling);
|
||||
if (typeof result === 'string') {
|
||||
return result; // immediately return as soon as the first expression matches
|
||||
}
|
||||
// If the result is a promise, we have to keep it for
|
||||
// later processing and await the result properly.
|
||||
if (isThenable(result)) {
|
||||
if (!resultPromises) {
|
||||
resultPromises = [];
|
||||
}
|
||||
resultPromises.push(result);
|
||||
}
|
||||
}
|
||||
// With result promises, we have to loop over each and
|
||||
// await the result before we can return any result.
|
||||
if (resultPromises) {
|
||||
return (async () => {
|
||||
for (const resultPromise of resultPromises) {
|
||||
const result = await resultPromise;
|
||||
if (typeof result === 'string') {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
})();
|
||||
}
|
||||
return null;
|
||||
};
|
||||
const withBasenames = parsedPatterns.find(pattern => !!pattern.allBasenames);
|
||||
if (withBasenames) {
|
||||
resultExpression.allBasenames = withBasenames.allBasenames;
|
||||
}
|
||||
const allPaths = parsedPatterns.reduce((all, current) => current.allPaths ? all.concat(current.allPaths) : all, []);
|
||||
if (allPaths.length) {
|
||||
resultExpression.allPaths = allPaths;
|
||||
}
|
||||
return resultExpression;
|
||||
}
|
||||
function parseExpressionPattern(pattern, value, options) {
|
||||
if (value === false) {
|
||||
return NULL; // pattern is disabled
|
||||
}
|
||||
const parsedPattern = parsePattern(pattern, options);
|
||||
if (parsedPattern === NULL) {
|
||||
return NULL;
|
||||
}
|
||||
// Expression Pattern is <boolean>
|
||||
if (typeof value === 'boolean') {
|
||||
return parsedPattern;
|
||||
}
|
||||
// Expression Pattern is <SiblingClause>
|
||||
if (value) {
|
||||
const when = value.when;
|
||||
if (typeof when === 'string') {
|
||||
const result = (path, basename, name, hasSibling) => {
|
||||
if (!hasSibling || !parsedPattern(path, basename)) {
|
||||
return null;
|
||||
}
|
||||
const clausePattern = when.replace('$(basename)', () => name);
|
||||
const matched = hasSibling(clausePattern);
|
||||
return isThenable(matched) ?
|
||||
matched.then(match => match ? pattern : null) :
|
||||
matched ? pattern : null;
|
||||
};
|
||||
result.requiresSiblings = true;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
// Expression is anything
|
||||
return parsedPattern;
|
||||
}
|
||||
function aggregateBasenameMatches(parsedPatterns, result) {
|
||||
const basenamePatterns = parsedPatterns.filter(parsedPattern => !!parsedPattern.basenames);
|
||||
if (basenamePatterns.length < 2) {
|
||||
return parsedPatterns;
|
||||
}
|
||||
const basenames = basenamePatterns.reduce((all, current) => {
|
||||
const basenames = current.basenames;
|
||||
return basenames ? all.concat(basenames) : all;
|
||||
}, []);
|
||||
let patterns;
|
||||
if (result) {
|
||||
patterns = [];
|
||||
for (let i = 0, n = basenames.length; i < n; i++) {
|
||||
patterns.push(result);
|
||||
}
|
||||
}
|
||||
else {
|
||||
patterns = basenamePatterns.reduce((all, current) => {
|
||||
const patterns = current.patterns;
|
||||
return patterns ? all.concat(patterns) : all;
|
||||
}, []);
|
||||
}
|
||||
const aggregate = function (path, basename) {
|
||||
if (typeof path !== 'string') {
|
||||
return null;
|
||||
}
|
||||
if (!basename) {
|
||||
let i;
|
||||
for (i = path.length; i > 0; i--) {
|
||||
const ch = path.charCodeAt(i - 1);
|
||||
if (ch === 47 /* CharCode.Slash */ || ch === 92 /* CharCode.Backslash */) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
basename = path.substring(i);
|
||||
}
|
||||
const index = basenames.indexOf(basename);
|
||||
return index !== -1 ? patterns[index] : null;
|
||||
};
|
||||
aggregate.basenames = basenames;
|
||||
aggregate.patterns = patterns;
|
||||
aggregate.allBasenames = basenames;
|
||||
const aggregatedPatterns = parsedPatterns.filter(parsedPattern => !parsedPattern.basenames);
|
||||
aggregatedPatterns.push(aggregate);
|
||||
return aggregatedPatterns;
|
||||
}
|
||||
|
||||
export { GLOBSTAR, GLOB_SPLIT, isRelativePattern, match, parse, splitGlobAware };
|
||||
+256
@@ -0,0 +1,256 @@
|
||||
import { encodeHex, VSBuffer } from './buffer.js';
|
||||
import { isHighSurrogate, isLowSurrogate, computeCodePoint } from './strings.js';
|
||||
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
/**
|
||||
* Return a hash value for an object.
|
||||
*
|
||||
* Note that this should not be used for binary data types. Instead,
|
||||
* prefer {@link hashAsync}.
|
||||
*/
|
||||
function hash(obj) {
|
||||
return doHash(obj, 0);
|
||||
}
|
||||
function doHash(obj, hashVal) {
|
||||
switch (typeof obj) {
|
||||
case 'object':
|
||||
if (obj === null) {
|
||||
return numberHash(349, hashVal);
|
||||
}
|
||||
else if (Array.isArray(obj)) {
|
||||
return arrayHash(obj, hashVal);
|
||||
}
|
||||
return objectHash(obj, hashVal);
|
||||
case 'string':
|
||||
return stringHash(obj, hashVal);
|
||||
case 'boolean':
|
||||
return booleanHash(obj, hashVal);
|
||||
case 'number':
|
||||
return numberHash(obj, hashVal);
|
||||
case 'undefined':
|
||||
return numberHash(937, hashVal);
|
||||
default:
|
||||
return numberHash(617, hashVal);
|
||||
}
|
||||
}
|
||||
function numberHash(val, initialHashVal) {
|
||||
return (((initialHashVal << 5) - initialHashVal) + val) | 0; // hashVal * 31 + ch, keep as int32
|
||||
}
|
||||
function booleanHash(b, initialHashVal) {
|
||||
return numberHash(b ? 433 : 863, initialHashVal);
|
||||
}
|
||||
function stringHash(s, hashVal) {
|
||||
hashVal = numberHash(149417, hashVal);
|
||||
for (let i = 0, length = s.length; i < length; i++) {
|
||||
hashVal = numberHash(s.charCodeAt(i), hashVal);
|
||||
}
|
||||
return hashVal;
|
||||
}
|
||||
function arrayHash(arr, initialHashVal) {
|
||||
initialHashVal = numberHash(104579, initialHashVal);
|
||||
return arr.reduce((hashVal, item) => doHash(item, hashVal), initialHashVal);
|
||||
}
|
||||
function objectHash(obj, initialHashVal) {
|
||||
initialHashVal = numberHash(181387, initialHashVal);
|
||||
return Object.keys(obj).sort().reduce((hashVal, key) => {
|
||||
hashVal = stringHash(key, hashVal);
|
||||
return doHash(obj[key], hashVal);
|
||||
}, initialHashVal);
|
||||
}
|
||||
function leftRotate(value, bits, totalBits = 32) {
|
||||
// delta + bits = totalBits
|
||||
const delta = totalBits - bits;
|
||||
// All ones, expect `delta` zeros aligned to the right
|
||||
const mask = ~((1 << delta) - 1);
|
||||
// Join (value left-shifted `bits` bits) with (masked value right-shifted `delta` bits)
|
||||
return ((value << bits) | ((mask & value) >>> delta)) >>> 0;
|
||||
}
|
||||
function toHexString(bufferOrValue, bitsize = 32) {
|
||||
if (bufferOrValue instanceof ArrayBuffer) {
|
||||
return encodeHex(VSBuffer.wrap(new Uint8Array(bufferOrValue)));
|
||||
}
|
||||
return (bufferOrValue >>> 0).toString(16).padStart(bitsize / 4, '0');
|
||||
}
|
||||
/**
|
||||
* A SHA1 implementation that works with strings and does not allocate.
|
||||
*
|
||||
* Prefer to use {@link hashAsync} in async contexts
|
||||
*/
|
||||
class StringSHA1 {
|
||||
static { this._bigBlock32 = new DataView(new ArrayBuffer(320)); } // 80 * 4 = 320
|
||||
constructor() {
|
||||
this._h0 = 0x67452301;
|
||||
this._h1 = 0xEFCDAB89;
|
||||
this._h2 = 0x98BADCFE;
|
||||
this._h3 = 0x10325476;
|
||||
this._h4 = 0xC3D2E1F0;
|
||||
this._buff = new Uint8Array(64 /* SHA1Constant.BLOCK_SIZE */ + 3 /* to fit any utf-8 */);
|
||||
this._buffDV = new DataView(this._buff.buffer);
|
||||
this._buffLen = 0;
|
||||
this._totalLen = 0;
|
||||
this._leftoverHighSurrogate = 0;
|
||||
this._finished = false;
|
||||
}
|
||||
update(str) {
|
||||
const strLen = str.length;
|
||||
if (strLen === 0) {
|
||||
return;
|
||||
}
|
||||
const buff = this._buff;
|
||||
let buffLen = this._buffLen;
|
||||
let leftoverHighSurrogate = this._leftoverHighSurrogate;
|
||||
let charCode;
|
||||
let offset;
|
||||
if (leftoverHighSurrogate !== 0) {
|
||||
charCode = leftoverHighSurrogate;
|
||||
offset = -1;
|
||||
leftoverHighSurrogate = 0;
|
||||
}
|
||||
else {
|
||||
charCode = str.charCodeAt(0);
|
||||
offset = 0;
|
||||
}
|
||||
while (true) {
|
||||
let codePoint = charCode;
|
||||
if (isHighSurrogate(charCode)) {
|
||||
if (offset + 1 < strLen) {
|
||||
const nextCharCode = str.charCodeAt(offset + 1);
|
||||
if (isLowSurrogate(nextCharCode)) {
|
||||
offset++;
|
||||
codePoint = computeCodePoint(charCode, nextCharCode);
|
||||
}
|
||||
else {
|
||||
// illegal => unicode replacement character
|
||||
codePoint = 65533 /* SHA1Constant.UNICODE_REPLACEMENT */;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// last character is a surrogate pair
|
||||
leftoverHighSurrogate = charCode;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (isLowSurrogate(charCode)) {
|
||||
// illegal => unicode replacement character
|
||||
codePoint = 65533 /* SHA1Constant.UNICODE_REPLACEMENT */;
|
||||
}
|
||||
buffLen = this._push(buff, buffLen, codePoint);
|
||||
offset++;
|
||||
if (offset < strLen) {
|
||||
charCode = str.charCodeAt(offset);
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
this._buffLen = buffLen;
|
||||
this._leftoverHighSurrogate = leftoverHighSurrogate;
|
||||
}
|
||||
_push(buff, buffLen, codePoint) {
|
||||
if (codePoint < 0x0080) {
|
||||
buff[buffLen++] = codePoint;
|
||||
}
|
||||
else if (codePoint < 0x0800) {
|
||||
buff[buffLen++] = 0b11000000 | ((codePoint & 0b00000000000000000000011111000000) >>> 6);
|
||||
buff[buffLen++] = 0b10000000 | ((codePoint & 0b00000000000000000000000000111111) >>> 0);
|
||||
}
|
||||
else if (codePoint < 0x10000) {
|
||||
buff[buffLen++] = 0b11100000 | ((codePoint & 0b00000000000000001111000000000000) >>> 12);
|
||||
buff[buffLen++] = 0b10000000 | ((codePoint & 0b00000000000000000000111111000000) >>> 6);
|
||||
buff[buffLen++] = 0b10000000 | ((codePoint & 0b00000000000000000000000000111111) >>> 0);
|
||||
}
|
||||
else {
|
||||
buff[buffLen++] = 0b11110000 | ((codePoint & 0b00000000000111000000000000000000) >>> 18);
|
||||
buff[buffLen++] = 0b10000000 | ((codePoint & 0b00000000000000111111000000000000) >>> 12);
|
||||
buff[buffLen++] = 0b10000000 | ((codePoint & 0b00000000000000000000111111000000) >>> 6);
|
||||
buff[buffLen++] = 0b10000000 | ((codePoint & 0b00000000000000000000000000111111) >>> 0);
|
||||
}
|
||||
if (buffLen >= 64 /* SHA1Constant.BLOCK_SIZE */) {
|
||||
this._step();
|
||||
buffLen -= 64 /* SHA1Constant.BLOCK_SIZE */;
|
||||
this._totalLen += 64 /* SHA1Constant.BLOCK_SIZE */;
|
||||
// take last 3 in case of UTF8 overflow
|
||||
buff[0] = buff[64 /* SHA1Constant.BLOCK_SIZE */ + 0];
|
||||
buff[1] = buff[64 /* SHA1Constant.BLOCK_SIZE */ + 1];
|
||||
buff[2] = buff[64 /* SHA1Constant.BLOCK_SIZE */ + 2];
|
||||
}
|
||||
return buffLen;
|
||||
}
|
||||
digest() {
|
||||
if (!this._finished) {
|
||||
this._finished = true;
|
||||
if (this._leftoverHighSurrogate) {
|
||||
// illegal => unicode replacement character
|
||||
this._leftoverHighSurrogate = 0;
|
||||
this._buffLen = this._push(this._buff, this._buffLen, 65533 /* SHA1Constant.UNICODE_REPLACEMENT */);
|
||||
}
|
||||
this._totalLen += this._buffLen;
|
||||
this._wrapUp();
|
||||
}
|
||||
return toHexString(this._h0) + toHexString(this._h1) + toHexString(this._h2) + toHexString(this._h3) + toHexString(this._h4);
|
||||
}
|
||||
_wrapUp() {
|
||||
this._buff[this._buffLen++] = 0x80;
|
||||
this._buff.subarray(this._buffLen).fill(0);
|
||||
if (this._buffLen > 56) {
|
||||
this._step();
|
||||
this._buff.fill(0);
|
||||
}
|
||||
// this will fit because the mantissa can cover up to 52 bits
|
||||
const ml = 8 * this._totalLen;
|
||||
this._buffDV.setUint32(56, Math.floor(ml / 4294967296), false);
|
||||
this._buffDV.setUint32(60, ml % 4294967296, false);
|
||||
this._step();
|
||||
}
|
||||
_step() {
|
||||
const bigBlock32 = StringSHA1._bigBlock32;
|
||||
const data = this._buffDV;
|
||||
for (let j = 0; j < 64 /* 16*4 */; j += 4) {
|
||||
bigBlock32.setUint32(j, data.getUint32(j, false), false);
|
||||
}
|
||||
for (let j = 64; j < 320 /* 80*4 */; j += 4) {
|
||||
bigBlock32.setUint32(j, leftRotate((bigBlock32.getUint32(j - 12, false) ^ bigBlock32.getUint32(j - 32, false) ^ bigBlock32.getUint32(j - 56, false) ^ bigBlock32.getUint32(j - 64, false)), 1), false);
|
||||
}
|
||||
let a = this._h0;
|
||||
let b = this._h1;
|
||||
let c = this._h2;
|
||||
let d = this._h3;
|
||||
let e = this._h4;
|
||||
let f, k;
|
||||
let temp;
|
||||
for (let j = 0; j < 80; j++) {
|
||||
if (j < 20) {
|
||||
f = (b & c) | ((~b) & d);
|
||||
k = 0x5A827999;
|
||||
}
|
||||
else if (j < 40) {
|
||||
f = b ^ c ^ d;
|
||||
k = 0x6ED9EBA1;
|
||||
}
|
||||
else if (j < 60) {
|
||||
f = (b & c) | (b & d) | (c & d);
|
||||
k = 0x8F1BBCDC;
|
||||
}
|
||||
else {
|
||||
f = b ^ c ^ d;
|
||||
k = 0xCA62C1D6;
|
||||
}
|
||||
temp = (leftRotate(a, 5) + f + e + k + bigBlock32.getUint32(j * 4, false)) & 0xffffffff;
|
||||
e = d;
|
||||
d = c;
|
||||
c = leftRotate(b, 30);
|
||||
b = a;
|
||||
a = temp;
|
||||
}
|
||||
this._h0 = (this._h0 + a) & 0xffffffff;
|
||||
this._h1 = (this._h1 + b) & 0xffffffff;
|
||||
this._h2 = (this._h2 + c) & 0xffffffff;
|
||||
this._h3 = (this._h3 + d) & 0xffffffff;
|
||||
this._h4 = (this._h4 + e) & 0xffffffff;
|
||||
}
|
||||
}
|
||||
|
||||
export { StringSHA1, doHash, hash, numberHash, stringHash };
|
||||
+26
@@ -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.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
class HierarchicalKind {
|
||||
static { this.sep = '.'; }
|
||||
static { this.None = new HierarchicalKind('@@none@@'); } // Special kind that matches nothing
|
||||
static { this.Empty = new HierarchicalKind(''); }
|
||||
constructor(value) {
|
||||
this.value = value;
|
||||
}
|
||||
equals(other) {
|
||||
return this.value === other.value;
|
||||
}
|
||||
contains(other) {
|
||||
return this.equals(other) || this.value === '' || other.value.startsWith(this.value + HierarchicalKind.sep);
|
||||
}
|
||||
intersects(other) {
|
||||
return this.contains(other) || other.contains(this);
|
||||
}
|
||||
append(...parts) {
|
||||
return new HierarchicalKind((this.value ? [this.value, ...parts] : parts).join(HierarchicalKind.sep));
|
||||
}
|
||||
}
|
||||
|
||||
export { HierarchicalKind };
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
import { ArrayNavigator } from './navigator.js';
|
||||
|
||||
class HistoryNavigator {
|
||||
constructor(_history = new Set(), limit = 10) {
|
||||
this._history = _history;
|
||||
this._limit = limit;
|
||||
this._onChange();
|
||||
if (this._history.onDidChange) {
|
||||
this._disposable = this._history.onDidChange(() => this._onChange());
|
||||
}
|
||||
}
|
||||
getHistory() {
|
||||
return this._elements;
|
||||
}
|
||||
add(t) {
|
||||
this._history.delete(t);
|
||||
this._history.add(t);
|
||||
this._onChange();
|
||||
}
|
||||
next() {
|
||||
// This will navigate past the end of the last element, and in that case the input should be cleared
|
||||
return this._navigator.next();
|
||||
}
|
||||
previous() {
|
||||
if (this._currentPosition() !== 0) {
|
||||
return this._navigator.previous();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
current() {
|
||||
return this._navigator.current();
|
||||
}
|
||||
first() {
|
||||
return this._navigator.first();
|
||||
}
|
||||
last() {
|
||||
return this._navigator.last();
|
||||
}
|
||||
isLast() {
|
||||
return this._currentPosition() >= this._elements.length - 1;
|
||||
}
|
||||
isNowhere() {
|
||||
return this._navigator.current() === null;
|
||||
}
|
||||
has(t) {
|
||||
return this._history.has(t);
|
||||
}
|
||||
_onChange() {
|
||||
this._reduceToLimit();
|
||||
const elements = this._elements;
|
||||
this._navigator = new ArrayNavigator(elements, 0, elements.length, elements.length);
|
||||
}
|
||||
_reduceToLimit() {
|
||||
const data = this._elements;
|
||||
if (data.length > this._limit) {
|
||||
const replaceValue = data.slice(data.length - this._limit);
|
||||
if (this._history.replace) {
|
||||
this._history.replace(replaceValue);
|
||||
}
|
||||
else {
|
||||
this._history = new Set(replaceValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
_currentPosition() {
|
||||
const currentElement = this._navigator.current();
|
||||
if (!currentElement) {
|
||||
return -1;
|
||||
}
|
||||
return this._elements.indexOf(currentElement);
|
||||
}
|
||||
get _elements() {
|
||||
const elements = [];
|
||||
this._history.forEach(e => elements.push(e));
|
||||
return elements;
|
||||
}
|
||||
dispose() {
|
||||
if (this._disposable) {
|
||||
this._disposable.dispose();
|
||||
this._disposable = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export { HistoryNavigator };
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
import './observableInternal/index.js';
|
||||
import { constObservable } from './observableInternal/observables/constObservable.js';
|
||||
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
function readHotReloadableExport(value, reader) {
|
||||
return value;
|
||||
}
|
||||
function createHotClass(clazz) {
|
||||
{
|
||||
return constObservable(clazz);
|
||||
}
|
||||
}
|
||||
|
||||
export { createHotClass, readHotReloadableExport };
|
||||
+164
@@ -0,0 +1,164 @@
|
||||
import { illegalArgument } from './errors.js';
|
||||
import { escapeIcons } from './iconLabels.js';
|
||||
import { Schemas } from './network.js';
|
||||
import { isEqual } from './resources.js';
|
||||
import { escapeRegExpCharacters } from './strings.js';
|
||||
import { URI } from './uri.js';
|
||||
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
class MarkdownString {
|
||||
constructor(value = '', isTrustedOrOptions = false) {
|
||||
this.value = value;
|
||||
if (typeof this.value !== 'string') {
|
||||
throw illegalArgument('value');
|
||||
}
|
||||
if (typeof isTrustedOrOptions === 'boolean') {
|
||||
this.isTrusted = isTrustedOrOptions;
|
||||
this.supportThemeIcons = false;
|
||||
this.supportHtml = false;
|
||||
this.supportAlertSyntax = false;
|
||||
}
|
||||
else {
|
||||
this.isTrusted = isTrustedOrOptions.isTrusted ?? undefined;
|
||||
this.supportThemeIcons = isTrustedOrOptions.supportThemeIcons ?? false;
|
||||
this.supportHtml = isTrustedOrOptions.supportHtml ?? false;
|
||||
this.supportAlertSyntax = isTrustedOrOptions.supportAlertSyntax ?? false;
|
||||
}
|
||||
}
|
||||
appendText(value, newlineStyle = 0 /* MarkdownStringTextNewlineStyle.Paragraph */) {
|
||||
this.value += escapeMarkdownSyntaxTokens(this.supportThemeIcons ? escapeIcons(value) : value) // CodeQL [SM02383] The Markdown is fully sanitized after being rendered.
|
||||
.replace(/([ \t]+)/g, (_match, g1) => ' '.repeat(g1.length)) // CodeQL [SM02383] The Markdown is fully sanitized after being rendered.
|
||||
.replace(/\>/gm, '\\>') // CodeQL [SM02383] The Markdown is fully sanitized after being rendered.
|
||||
.replace(/\n/g, newlineStyle === 1 /* MarkdownStringTextNewlineStyle.Break */ ? '\\\n' : '\n\n'); // CodeQL [SM02383] The Markdown is fully sanitized after being rendered.
|
||||
return this;
|
||||
}
|
||||
appendMarkdown(value) {
|
||||
this.value += value;
|
||||
return this;
|
||||
}
|
||||
appendCodeblock(langId, code) {
|
||||
this.value += `\n${appendEscapedMarkdownCodeBlockFence(code, langId)}\n`;
|
||||
return this;
|
||||
}
|
||||
appendLink(target, label, title) {
|
||||
this.value += '[';
|
||||
this.value += this._escape(label, ']');
|
||||
this.value += '](';
|
||||
this.value += this._escape(String(target), ')');
|
||||
if (title) {
|
||||
this.value += ` "${this._escape(this._escape(title, '"'), ')')}"`;
|
||||
}
|
||||
this.value += ')';
|
||||
return this;
|
||||
}
|
||||
_escape(value, ch) {
|
||||
const r = new RegExp(escapeRegExpCharacters(ch), 'g');
|
||||
return value.replace(r, (match, offset) => {
|
||||
if (value.charAt(offset - 1) !== '\\') {
|
||||
return `\\${match}`;
|
||||
}
|
||||
else {
|
||||
return match;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
function isEmptyMarkdownString(oneOrMany) {
|
||||
if (isMarkdownString(oneOrMany)) {
|
||||
return !oneOrMany.value;
|
||||
}
|
||||
else if (Array.isArray(oneOrMany)) {
|
||||
return oneOrMany.every(isEmptyMarkdownString);
|
||||
}
|
||||
else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
function isMarkdownString(thing) {
|
||||
if (thing instanceof MarkdownString) {
|
||||
return true;
|
||||
}
|
||||
else if (thing && typeof thing === 'object') {
|
||||
return typeof thing.value === 'string'
|
||||
&& (typeof thing.isTrusted === 'boolean' || typeof thing.isTrusted === 'object' || thing.isTrusted === undefined)
|
||||
&& (typeof thing.supportThemeIcons === 'boolean' || thing.supportThemeIcons === undefined)
|
||||
&& (typeof thing.supportAlertSyntax === 'boolean' || thing.supportAlertSyntax === undefined);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function markdownStringEqual(a, b) {
|
||||
if (a === b) {
|
||||
return true;
|
||||
}
|
||||
else if (!a || !b) {
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
return a.value === b.value
|
||||
&& a.isTrusted === b.isTrusted
|
||||
&& a.supportThemeIcons === b.supportThemeIcons
|
||||
&& a.supportHtml === b.supportHtml
|
||||
&& a.supportAlertSyntax === b.supportAlertSyntax
|
||||
&& (a.baseUri === b.baseUri || !!a.baseUri && !!b.baseUri && isEqual(URI.from(a.baseUri), URI.from(b.baseUri)));
|
||||
}
|
||||
}
|
||||
function escapeMarkdownSyntaxTokens(text) {
|
||||
// escape markdown syntax tokens: http://daringfireball.net/projects/markdown/syntax#backslash
|
||||
return text.replace(/[\\`*_{}[\]()#+\-!~]/g, '\\$&'); // CodeQL [SM02383] Backslash is escaped in the character class
|
||||
}
|
||||
/**
|
||||
* @see https://github.com/microsoft/vscode/issues/193746
|
||||
*/
|
||||
function appendEscapedMarkdownCodeBlockFence(code, langId) {
|
||||
const longestFenceLength = code.match(/^`+/gm)?.reduce((a, b) => (a.length > b.length ? a : b)).length ??
|
||||
0;
|
||||
const desiredFenceLength = longestFenceLength >= 3 ? longestFenceLength + 1 : 3;
|
||||
// the markdown result
|
||||
return [
|
||||
`${'`'.repeat(desiredFenceLength)}${langId}`,
|
||||
code,
|
||||
`${'`'.repeat(desiredFenceLength)}`,
|
||||
].join('\n');
|
||||
}
|
||||
function escapeDoubleQuotes(input) {
|
||||
return input.replace(/"/g, '"');
|
||||
}
|
||||
function removeMarkdownEscapes(text) {
|
||||
if (!text) {
|
||||
return text;
|
||||
}
|
||||
return text.replace(/\\([\\`*_{}[\]()#+\-.!~])/g, '$1');
|
||||
}
|
||||
function parseHrefAndDimensions(href) {
|
||||
const dimensions = [];
|
||||
const splitted = href.split('|').map(s => s.trim());
|
||||
href = splitted[0];
|
||||
const parameters = splitted[1];
|
||||
if (parameters) {
|
||||
const heightFromParams = /height=(\d+)/.exec(parameters);
|
||||
const widthFromParams = /width=(\d+)/.exec(parameters);
|
||||
const height = heightFromParams ? heightFromParams[1] : '';
|
||||
const width = widthFromParams ? widthFromParams[1] : '';
|
||||
const widthIsFinite = isFinite(parseInt(width));
|
||||
const heightIsFinite = isFinite(parseInt(height));
|
||||
if (widthIsFinite) {
|
||||
dimensions.push(`width="${width}"`);
|
||||
}
|
||||
if (heightIsFinite) {
|
||||
dimensions.push(`height="${height}"`);
|
||||
}
|
||||
}
|
||||
return { href, dimensions };
|
||||
}
|
||||
function createCommandUri(commandId, ...commandArgs) {
|
||||
return URI.from({
|
||||
scheme: Schemas.command,
|
||||
path: commandId,
|
||||
query: commandArgs.length ? encodeURIComponent(JSON.stringify(commandArgs)) : undefined,
|
||||
});
|
||||
}
|
||||
|
||||
export { MarkdownString, appendEscapedMarkdownCodeBlockFence, createCommandUri, escapeDoubleQuotes, escapeMarkdownSyntaxTokens, isEmptyMarkdownString, isMarkdownString, markdownStringEqual, parseHrefAndDimensions, removeMarkdownEscapes };
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
import { matchesFuzzy } from './filters.js';
|
||||
import { ltrim } from './strings.js';
|
||||
import { ThemeIcon } from './themables.js';
|
||||
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
const iconStartMarker = '$(';
|
||||
const iconsRegex = new RegExp(`\\$\\(${ThemeIcon.iconNameExpression}(?:${ThemeIcon.iconModifierExpression})?\\)`, 'g'); // no capturing groups
|
||||
const escapeIconsRegex = new RegExp(`(\\\\)?${iconsRegex.source}`, 'g');
|
||||
function escapeIcons(text) {
|
||||
return text.replace(escapeIconsRegex, (match, escaped) => escaped ? match : `\\${match}`);
|
||||
}
|
||||
const markdownEscapedIconsRegex = new RegExp(`\\\\${iconsRegex.source}`, 'g');
|
||||
function markdownEscapeEscapedIcons(text) {
|
||||
// Need to add an extra \ for escaping in markdown
|
||||
return text.replace(markdownEscapedIconsRegex, match => `\\${match}`);
|
||||
}
|
||||
const stripIconsRegex = new RegExp(`(\\s)?(\\\\)?${iconsRegex.source}(\\s)?`, 'g');
|
||||
/**
|
||||
* Takes a label with icons (`$(iconId)xyz`) and strips the icons out (`xyz`)
|
||||
*/
|
||||
function stripIcons(text) {
|
||||
if (text.indexOf(iconStartMarker) === -1) {
|
||||
return text;
|
||||
}
|
||||
return text.replace(stripIconsRegex, (match, preWhitespace, escaped, postWhitespace) => escaped ? match : preWhitespace || postWhitespace || '');
|
||||
}
|
||||
/**
|
||||
* Takes a label with icons (`$(iconId)xyz`), removes the icon syntax adds whitespace so that screen readers can read the text better.
|
||||
*/
|
||||
function getCodiconAriaLabel(text) {
|
||||
if (!text) {
|
||||
return '';
|
||||
}
|
||||
return text.replace(/\$\((.*?)\)/g, (_match, codiconName) => ` ${codiconName} `).trim();
|
||||
}
|
||||
const _parseIconsRegex = new RegExp(`\\$\\(${ThemeIcon.iconNameCharacter}+\\)`, 'g');
|
||||
/**
|
||||
* Takes a label with icons (`abc $(iconId)xyz`) and returns the text (`abc xyz`) and the offsets of the icons (`[3]`)
|
||||
*/
|
||||
function parseLabelWithIcons(input) {
|
||||
_parseIconsRegex.lastIndex = 0;
|
||||
let text = '';
|
||||
const iconOffsets = [];
|
||||
let iconsOffset = 0;
|
||||
while (true) {
|
||||
const pos = _parseIconsRegex.lastIndex;
|
||||
const match = _parseIconsRegex.exec(input);
|
||||
const chars = input.substring(pos, match?.index);
|
||||
if (chars.length > 0) {
|
||||
text += chars;
|
||||
for (let i = 0; i < chars.length; i++) {
|
||||
iconOffsets.push(iconsOffset);
|
||||
}
|
||||
}
|
||||
if (!match) {
|
||||
break;
|
||||
}
|
||||
iconsOffset += match[0].length;
|
||||
}
|
||||
return { text, iconOffsets };
|
||||
}
|
||||
function matchesFuzzyIconAware(query, target, enableSeparateSubstringMatching = false) {
|
||||
const { text, iconOffsets } = target;
|
||||
// Return early if there are no icon markers in the word to match against
|
||||
if (!iconOffsets || iconOffsets.length === 0) {
|
||||
return matchesFuzzy(query, text, enableSeparateSubstringMatching);
|
||||
}
|
||||
// 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 = matchesFuzzy(query, wordToMatchAgainstWithoutIconsTrimmed, enableSeparateSubstringMatching);
|
||||
// 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;
|
||||
}
|
||||
|
||||
export { escapeIcons, getCodiconAriaLabel, markdownEscapeEscapedIcons, matchesFuzzyIconAware, parseLabelWithIcons, stripIcons };
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
class IdGenerator {
|
||||
constructor(prefix) {
|
||||
this._prefix = prefix;
|
||||
this._lastId = 0;
|
||||
}
|
||||
nextId() {
|
||||
return this._prefix + (++this._lastId);
|
||||
}
|
||||
}
|
||||
const defaultGenerator = new IdGenerator('id#');
|
||||
|
||||
export { IdGenerator, defaultGenerator };
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
import { Emitter } from './event.js';
|
||||
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
class IMEImpl {
|
||||
constructor() {
|
||||
this._onDidChange = new Emitter();
|
||||
this.onDidChange = this._onDidChange.event;
|
||||
this._enabled = true;
|
||||
}
|
||||
get enabled() {
|
||||
return this._enabled;
|
||||
}
|
||||
/**
|
||||
* Enable IME
|
||||
*/
|
||||
enable() {
|
||||
this._enabled = true;
|
||||
this._onDidChange.fire();
|
||||
}
|
||||
/**
|
||||
* Disable IME
|
||||
*/
|
||||
disable() {
|
||||
this._enabled = false;
|
||||
this._onDidChange.fire();
|
||||
}
|
||||
}
|
||||
const IME = new IMEImpl();
|
||||
|
||||
export { IME, IMEImpl };
|
||||
+186
@@ -0,0 +1,186 @@
|
||||
import { isIterable } from './types.js';
|
||||
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
var Iterable;
|
||||
(function (Iterable) {
|
||||
function is(thing) {
|
||||
return !!thing && typeof thing === 'object' && typeof thing[Symbol.iterator] === 'function';
|
||||
}
|
||||
Iterable.is = is;
|
||||
const _empty = Object.freeze([]);
|
||||
function empty() {
|
||||
return _empty;
|
||||
}
|
||||
Iterable.empty = empty;
|
||||
function* single(element) {
|
||||
yield element;
|
||||
}
|
||||
Iterable.single = single;
|
||||
function wrap(iterableOrElement) {
|
||||
if (is(iterableOrElement)) {
|
||||
return iterableOrElement;
|
||||
}
|
||||
else {
|
||||
return single(iterableOrElement);
|
||||
}
|
||||
}
|
||||
Iterable.wrap = wrap;
|
||||
function from(iterable) {
|
||||
return iterable || _empty;
|
||||
}
|
||||
Iterable.from = from;
|
||||
function* reverse(array) {
|
||||
for (let i = array.length - 1; i >= 0; i--) {
|
||||
yield array[i];
|
||||
}
|
||||
}
|
||||
Iterable.reverse = reverse;
|
||||
function isEmpty(iterable) {
|
||||
return !iterable || iterable[Symbol.iterator]().next().done === true;
|
||||
}
|
||||
Iterable.isEmpty = isEmpty;
|
||||
function first(iterable) {
|
||||
return iterable[Symbol.iterator]().next().value;
|
||||
}
|
||||
Iterable.first = first;
|
||||
function some(iterable, predicate) {
|
||||
let i = 0;
|
||||
for (const element of iterable) {
|
||||
if (predicate(element, i++)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
Iterable.some = some;
|
||||
function every(iterable, predicate) {
|
||||
let i = 0;
|
||||
for (const element of iterable) {
|
||||
if (!predicate(element, i++)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
Iterable.every = every;
|
||||
function find(iterable, predicate) {
|
||||
for (const element of iterable) {
|
||||
if (predicate(element)) {
|
||||
return element;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
Iterable.find = find;
|
||||
function* filter(iterable, predicate) {
|
||||
for (const element of iterable) {
|
||||
if (predicate(element)) {
|
||||
yield element;
|
||||
}
|
||||
}
|
||||
}
|
||||
Iterable.filter = filter;
|
||||
function* map(iterable, fn) {
|
||||
let index = 0;
|
||||
for (const element of iterable) {
|
||||
yield fn(element, index++);
|
||||
}
|
||||
}
|
||||
Iterable.map = map;
|
||||
function* flatMap(iterable, fn) {
|
||||
let index = 0;
|
||||
for (const element of iterable) {
|
||||
yield* fn(element, index++);
|
||||
}
|
||||
}
|
||||
Iterable.flatMap = flatMap;
|
||||
function* concat(...iterables) {
|
||||
for (const item of iterables) {
|
||||
if (isIterable(item)) {
|
||||
yield* item;
|
||||
}
|
||||
else {
|
||||
yield item;
|
||||
}
|
||||
}
|
||||
}
|
||||
Iterable.concat = concat;
|
||||
function reduce(iterable, reducer, initialValue) {
|
||||
let value = initialValue;
|
||||
for (const element of iterable) {
|
||||
value = reducer(value, element);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
Iterable.reduce = reduce;
|
||||
function length(iterable) {
|
||||
let count = 0;
|
||||
for (const _ of iterable) {
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
Iterable.length = length;
|
||||
/**
|
||||
* Returns an iterable slice of the array, with the same semantics as `array.slice()`.
|
||||
*/
|
||||
function* slice(arr, from, to = arr.length) {
|
||||
if (from < -arr.length) {
|
||||
from = 0;
|
||||
}
|
||||
if (from < 0) {
|
||||
from += arr.length;
|
||||
}
|
||||
if (to < 0) {
|
||||
to += arr.length;
|
||||
}
|
||||
else if (to > arr.length) {
|
||||
to = arr.length;
|
||||
}
|
||||
for (; from < to; from++) {
|
||||
yield arr[from];
|
||||
}
|
||||
}
|
||||
Iterable.slice = slice;
|
||||
/**
|
||||
* Consumes `atMost` elements from iterable and returns the consumed elements,
|
||||
* and an iterable for the rest of the elements.
|
||||
*/
|
||||
function consume(iterable, atMost = Number.POSITIVE_INFINITY) {
|
||||
const consumed = [];
|
||||
if (atMost === 0) {
|
||||
return [consumed, iterable];
|
||||
}
|
||||
const iterator = iterable[Symbol.iterator]();
|
||||
for (let i = 0; i < atMost; i++) {
|
||||
const next = iterator.next();
|
||||
if (next.done) {
|
||||
return [consumed, Iterable.empty()];
|
||||
}
|
||||
consumed.push(next.value);
|
||||
}
|
||||
return [consumed, { [Symbol.iterator]() { return iterator; } }];
|
||||
}
|
||||
Iterable.consume = consume;
|
||||
async function asyncToArray(iterable) {
|
||||
const result = [];
|
||||
for await (const item of iterable) {
|
||||
result.push(item);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
Iterable.asyncToArray = asyncToArray;
|
||||
async function asyncToArrayFlat(iterable) {
|
||||
let result = [];
|
||||
for await (const item of iterable) {
|
||||
result = result.concat(item);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
Iterable.asyncToArrayFlat = asyncToArrayFlat;
|
||||
})(Iterable || (Iterable = {}));
|
||||
|
||||
export { Iterable };
|
||||
+351
@@ -0,0 +1,351 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
class KeyCodeStrMap {
|
||||
constructor() {
|
||||
this._keyCodeToStr = [];
|
||||
this._strToKeyCode = Object.create(null);
|
||||
}
|
||||
define(keyCode, str) {
|
||||
this._keyCodeToStr[keyCode] = str;
|
||||
this._strToKeyCode[str.toLowerCase()] = keyCode;
|
||||
}
|
||||
keyCodeToStr(keyCode) {
|
||||
return this._keyCodeToStr[keyCode];
|
||||
}
|
||||
strToKeyCode(str) {
|
||||
return this._strToKeyCode[str.toLowerCase()] || 0 /* KeyCode.Unknown */;
|
||||
}
|
||||
}
|
||||
const uiMap = new KeyCodeStrMap();
|
||||
const userSettingsUSMap = new KeyCodeStrMap();
|
||||
const userSettingsGeneralMap = new KeyCodeStrMap();
|
||||
const EVENT_KEY_CODE_MAP = new Array(230);
|
||||
const scanCodeStrToInt = Object.create(null);
|
||||
const scanCodeLowerCaseStrToInt = Object.create(null);
|
||||
/**
|
||||
* -1 if a ScanCode => KeyCode mapping depends on kb layout.
|
||||
*/
|
||||
const IMMUTABLE_CODE_TO_KEY_CODE = [];
|
||||
for (let i = 0; i <= 193 /* ScanCode.MAX_VALUE */; i++) {
|
||||
IMMUTABLE_CODE_TO_KEY_CODE[i] = -1 /* KeyCode.DependsOnKbLayout */;
|
||||
}
|
||||
(function () {
|
||||
// See https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx
|
||||
// See https://github.com/microsoft/node-native-keymap/blob/88c0b0e5/deps/chromium/keyboard_codes_win.h
|
||||
const empty = '';
|
||||
const mappings = [
|
||||
// immutable, scanCode, scanCodeStr, keyCode, keyCodeStr, eventKeyCode, vkey, usUserSettingsLabel, generalUserSettingsLabel
|
||||
[1, 0 /* ScanCode.None */, 'None', 0 /* KeyCode.Unknown */, 'unknown', 0, 'VK_UNKNOWN', empty, empty],
|
||||
[1, 1 /* ScanCode.Hyper */, 'Hyper', 0 /* KeyCode.Unknown */, empty, 0, empty, empty, empty],
|
||||
[1, 2 /* ScanCode.Super */, 'Super', 0 /* KeyCode.Unknown */, empty, 0, empty, empty, empty],
|
||||
[1, 3 /* ScanCode.Fn */, 'Fn', 0 /* KeyCode.Unknown */, empty, 0, empty, empty, empty],
|
||||
[1, 4 /* ScanCode.FnLock */, 'FnLock', 0 /* KeyCode.Unknown */, empty, 0, empty, empty, empty],
|
||||
[1, 5 /* ScanCode.Suspend */, 'Suspend', 0 /* KeyCode.Unknown */, empty, 0, empty, empty, empty],
|
||||
[1, 6 /* ScanCode.Resume */, 'Resume', 0 /* KeyCode.Unknown */, empty, 0, empty, empty, empty],
|
||||
[1, 7 /* ScanCode.Turbo */, 'Turbo', 0 /* KeyCode.Unknown */, empty, 0, empty, empty, empty],
|
||||
[1, 8 /* ScanCode.Sleep */, 'Sleep', 0 /* KeyCode.Unknown */, empty, 0, 'VK_SLEEP', empty, empty],
|
||||
[1, 9 /* ScanCode.WakeUp */, 'WakeUp', 0 /* KeyCode.Unknown */, empty, 0, empty, empty, empty],
|
||||
[0, 10 /* ScanCode.KeyA */, 'KeyA', 31 /* KeyCode.KeyA */, 'A', 65, 'VK_A', empty, empty],
|
||||
[0, 11 /* ScanCode.KeyB */, 'KeyB', 32 /* KeyCode.KeyB */, 'B', 66, 'VK_B', empty, empty],
|
||||
[0, 12 /* ScanCode.KeyC */, 'KeyC', 33 /* KeyCode.KeyC */, 'C', 67, 'VK_C', empty, empty],
|
||||
[0, 13 /* ScanCode.KeyD */, 'KeyD', 34 /* KeyCode.KeyD */, 'D', 68, 'VK_D', empty, empty],
|
||||
[0, 14 /* ScanCode.KeyE */, 'KeyE', 35 /* KeyCode.KeyE */, 'E', 69, 'VK_E', empty, empty],
|
||||
[0, 15 /* ScanCode.KeyF */, 'KeyF', 36 /* KeyCode.KeyF */, 'F', 70, 'VK_F', empty, empty],
|
||||
[0, 16 /* ScanCode.KeyG */, 'KeyG', 37 /* KeyCode.KeyG */, 'G', 71, 'VK_G', empty, empty],
|
||||
[0, 17 /* ScanCode.KeyH */, 'KeyH', 38 /* KeyCode.KeyH */, 'H', 72, 'VK_H', empty, empty],
|
||||
[0, 18 /* ScanCode.KeyI */, 'KeyI', 39 /* KeyCode.KeyI */, 'I', 73, 'VK_I', empty, empty],
|
||||
[0, 19 /* ScanCode.KeyJ */, 'KeyJ', 40 /* KeyCode.KeyJ */, 'J', 74, 'VK_J', empty, empty],
|
||||
[0, 20 /* ScanCode.KeyK */, 'KeyK', 41 /* KeyCode.KeyK */, 'K', 75, 'VK_K', empty, empty],
|
||||
[0, 21 /* ScanCode.KeyL */, 'KeyL', 42 /* KeyCode.KeyL */, 'L', 76, 'VK_L', empty, empty],
|
||||
[0, 22 /* ScanCode.KeyM */, 'KeyM', 43 /* KeyCode.KeyM */, 'M', 77, 'VK_M', empty, empty],
|
||||
[0, 23 /* ScanCode.KeyN */, 'KeyN', 44 /* KeyCode.KeyN */, 'N', 78, 'VK_N', empty, empty],
|
||||
[0, 24 /* ScanCode.KeyO */, 'KeyO', 45 /* KeyCode.KeyO */, 'O', 79, 'VK_O', empty, empty],
|
||||
[0, 25 /* ScanCode.KeyP */, 'KeyP', 46 /* KeyCode.KeyP */, 'P', 80, 'VK_P', empty, empty],
|
||||
[0, 26 /* ScanCode.KeyQ */, 'KeyQ', 47 /* KeyCode.KeyQ */, 'Q', 81, 'VK_Q', empty, empty],
|
||||
[0, 27 /* ScanCode.KeyR */, 'KeyR', 48 /* KeyCode.KeyR */, 'R', 82, 'VK_R', empty, empty],
|
||||
[0, 28 /* ScanCode.KeyS */, 'KeyS', 49 /* KeyCode.KeyS */, 'S', 83, 'VK_S', empty, empty],
|
||||
[0, 29 /* ScanCode.KeyT */, 'KeyT', 50 /* KeyCode.KeyT */, 'T', 84, 'VK_T', empty, empty],
|
||||
[0, 30 /* ScanCode.KeyU */, 'KeyU', 51 /* KeyCode.KeyU */, 'U', 85, 'VK_U', empty, empty],
|
||||
[0, 31 /* ScanCode.KeyV */, 'KeyV', 52 /* KeyCode.KeyV */, 'V', 86, 'VK_V', empty, empty],
|
||||
[0, 32 /* ScanCode.KeyW */, 'KeyW', 53 /* KeyCode.KeyW */, 'W', 87, 'VK_W', empty, empty],
|
||||
[0, 33 /* ScanCode.KeyX */, 'KeyX', 54 /* KeyCode.KeyX */, 'X', 88, 'VK_X', empty, empty],
|
||||
[0, 34 /* ScanCode.KeyY */, 'KeyY', 55 /* KeyCode.KeyY */, 'Y', 89, 'VK_Y', empty, empty],
|
||||
[0, 35 /* ScanCode.KeyZ */, 'KeyZ', 56 /* KeyCode.KeyZ */, 'Z', 90, 'VK_Z', empty, empty],
|
||||
[0, 36 /* ScanCode.Digit1 */, 'Digit1', 22 /* KeyCode.Digit1 */, '1', 49, 'VK_1', empty, empty],
|
||||
[0, 37 /* ScanCode.Digit2 */, 'Digit2', 23 /* KeyCode.Digit2 */, '2', 50, 'VK_2', empty, empty],
|
||||
[0, 38 /* ScanCode.Digit3 */, 'Digit3', 24 /* KeyCode.Digit3 */, '3', 51, 'VK_3', empty, empty],
|
||||
[0, 39 /* ScanCode.Digit4 */, 'Digit4', 25 /* KeyCode.Digit4 */, '4', 52, 'VK_4', empty, empty],
|
||||
[0, 40 /* ScanCode.Digit5 */, 'Digit5', 26 /* KeyCode.Digit5 */, '5', 53, 'VK_5', empty, empty],
|
||||
[0, 41 /* ScanCode.Digit6 */, 'Digit6', 27 /* KeyCode.Digit6 */, '6', 54, 'VK_6', empty, empty],
|
||||
[0, 42 /* ScanCode.Digit7 */, 'Digit7', 28 /* KeyCode.Digit7 */, '7', 55, 'VK_7', empty, empty],
|
||||
[0, 43 /* ScanCode.Digit8 */, 'Digit8', 29 /* KeyCode.Digit8 */, '8', 56, 'VK_8', empty, empty],
|
||||
[0, 44 /* ScanCode.Digit9 */, 'Digit9', 30 /* KeyCode.Digit9 */, '9', 57, 'VK_9', empty, empty],
|
||||
[0, 45 /* ScanCode.Digit0 */, 'Digit0', 21 /* KeyCode.Digit0 */, '0', 48, 'VK_0', empty, empty],
|
||||
[1, 46 /* ScanCode.Enter */, 'Enter', 3 /* KeyCode.Enter */, 'Enter', 13, 'VK_RETURN', empty, empty],
|
||||
[1, 47 /* ScanCode.Escape */, 'Escape', 9 /* KeyCode.Escape */, 'Escape', 27, 'VK_ESCAPE', empty, empty],
|
||||
[1, 48 /* ScanCode.Backspace */, 'Backspace', 1 /* KeyCode.Backspace */, 'Backspace', 8, 'VK_BACK', empty, empty],
|
||||
[1, 49 /* ScanCode.Tab */, 'Tab', 2 /* KeyCode.Tab */, 'Tab', 9, 'VK_TAB', empty, empty],
|
||||
[1, 50 /* ScanCode.Space */, 'Space', 10 /* KeyCode.Space */, 'Space', 32, 'VK_SPACE', empty, empty],
|
||||
[0, 51 /* ScanCode.Minus */, 'Minus', 88 /* KeyCode.Minus */, '-', 189, 'VK_OEM_MINUS', '-', 'OEM_MINUS'],
|
||||
[0, 52 /* ScanCode.Equal */, 'Equal', 86 /* KeyCode.Equal */, '=', 187, 'VK_OEM_PLUS', '=', 'OEM_PLUS'],
|
||||
[0, 53 /* ScanCode.BracketLeft */, 'BracketLeft', 92 /* KeyCode.BracketLeft */, '[', 219, 'VK_OEM_4', '[', 'OEM_4'],
|
||||
[0, 54 /* ScanCode.BracketRight */, 'BracketRight', 94 /* KeyCode.BracketRight */, ']', 221, 'VK_OEM_6', ']', 'OEM_6'],
|
||||
[0, 55 /* ScanCode.Backslash */, 'Backslash', 93 /* KeyCode.Backslash */, '\\', 220, 'VK_OEM_5', '\\', 'OEM_5'],
|
||||
[0, 56 /* ScanCode.IntlHash */, 'IntlHash', 0 /* KeyCode.Unknown */, empty, 0, empty, empty, empty], // has been dropped from the w3c spec
|
||||
[0, 57 /* ScanCode.Semicolon */, 'Semicolon', 85 /* KeyCode.Semicolon */, ';', 186, 'VK_OEM_1', ';', 'OEM_1'],
|
||||
[0, 58 /* ScanCode.Quote */, 'Quote', 95 /* KeyCode.Quote */, '\'', 222, 'VK_OEM_7', '\'', 'OEM_7'],
|
||||
[0, 59 /* ScanCode.Backquote */, 'Backquote', 91 /* KeyCode.Backquote */, '`', 192, 'VK_OEM_3', '`', 'OEM_3'],
|
||||
[0, 60 /* ScanCode.Comma */, 'Comma', 87 /* KeyCode.Comma */, ',', 188, 'VK_OEM_COMMA', ',', 'OEM_COMMA'],
|
||||
[0, 61 /* ScanCode.Period */, 'Period', 89 /* KeyCode.Period */, '.', 190, 'VK_OEM_PERIOD', '.', 'OEM_PERIOD'],
|
||||
[0, 62 /* ScanCode.Slash */, 'Slash', 90 /* KeyCode.Slash */, '/', 191, 'VK_OEM_2', '/', 'OEM_2'],
|
||||
[1, 63 /* ScanCode.CapsLock */, 'CapsLock', 8 /* KeyCode.CapsLock */, 'CapsLock', 20, 'VK_CAPITAL', empty, empty],
|
||||
[1, 64 /* ScanCode.F1 */, 'F1', 59 /* KeyCode.F1 */, 'F1', 112, 'VK_F1', empty, empty],
|
||||
[1, 65 /* ScanCode.F2 */, 'F2', 60 /* KeyCode.F2 */, 'F2', 113, 'VK_F2', empty, empty],
|
||||
[1, 66 /* ScanCode.F3 */, 'F3', 61 /* KeyCode.F3 */, 'F3', 114, 'VK_F3', empty, empty],
|
||||
[1, 67 /* ScanCode.F4 */, 'F4', 62 /* KeyCode.F4 */, 'F4', 115, 'VK_F4', empty, empty],
|
||||
[1, 68 /* ScanCode.F5 */, 'F5', 63 /* KeyCode.F5 */, 'F5', 116, 'VK_F5', empty, empty],
|
||||
[1, 69 /* ScanCode.F6 */, 'F6', 64 /* KeyCode.F6 */, 'F6', 117, 'VK_F6', empty, empty],
|
||||
[1, 70 /* ScanCode.F7 */, 'F7', 65 /* KeyCode.F7 */, 'F7', 118, 'VK_F7', empty, empty],
|
||||
[1, 71 /* ScanCode.F8 */, 'F8', 66 /* KeyCode.F8 */, 'F8', 119, 'VK_F8', empty, empty],
|
||||
[1, 72 /* ScanCode.F9 */, 'F9', 67 /* KeyCode.F9 */, 'F9', 120, 'VK_F9', empty, empty],
|
||||
[1, 73 /* ScanCode.F10 */, 'F10', 68 /* KeyCode.F10 */, 'F10', 121, 'VK_F10', empty, empty],
|
||||
[1, 74 /* ScanCode.F11 */, 'F11', 69 /* KeyCode.F11 */, 'F11', 122, 'VK_F11', empty, empty],
|
||||
[1, 75 /* ScanCode.F12 */, 'F12', 70 /* KeyCode.F12 */, 'F12', 123, 'VK_F12', empty, empty],
|
||||
[1, 76 /* ScanCode.PrintScreen */, 'PrintScreen', 0 /* KeyCode.Unknown */, empty, 0, empty, empty, empty],
|
||||
[1, 77 /* ScanCode.ScrollLock */, 'ScrollLock', 84 /* KeyCode.ScrollLock */, 'ScrollLock', 145, 'VK_SCROLL', empty, empty],
|
||||
[1, 78 /* ScanCode.Pause */, 'Pause', 7 /* KeyCode.PauseBreak */, 'PauseBreak', 19, 'VK_PAUSE', empty, empty],
|
||||
[1, 79 /* ScanCode.Insert */, 'Insert', 19 /* KeyCode.Insert */, 'Insert', 45, 'VK_INSERT', empty, empty],
|
||||
[1, 80 /* ScanCode.Home */, 'Home', 14 /* KeyCode.Home */, 'Home', 36, 'VK_HOME', empty, empty],
|
||||
[1, 81 /* ScanCode.PageUp */, 'PageUp', 11 /* KeyCode.PageUp */, 'PageUp', 33, 'VK_PRIOR', empty, empty],
|
||||
[1, 82 /* ScanCode.Delete */, 'Delete', 20 /* KeyCode.Delete */, 'Delete', 46, 'VK_DELETE', empty, empty],
|
||||
[1, 83 /* ScanCode.End */, 'End', 13 /* KeyCode.End */, 'End', 35, 'VK_END', empty, empty],
|
||||
[1, 84 /* ScanCode.PageDown */, 'PageDown', 12 /* KeyCode.PageDown */, 'PageDown', 34, 'VK_NEXT', empty, empty],
|
||||
[1, 85 /* ScanCode.ArrowRight */, 'ArrowRight', 17 /* KeyCode.RightArrow */, 'RightArrow', 39, 'VK_RIGHT', 'Right', empty],
|
||||
[1, 86 /* ScanCode.ArrowLeft */, 'ArrowLeft', 15 /* KeyCode.LeftArrow */, 'LeftArrow', 37, 'VK_LEFT', 'Left', empty],
|
||||
[1, 87 /* ScanCode.ArrowDown */, 'ArrowDown', 18 /* KeyCode.DownArrow */, 'DownArrow', 40, 'VK_DOWN', 'Down', empty],
|
||||
[1, 88 /* ScanCode.ArrowUp */, 'ArrowUp', 16 /* KeyCode.UpArrow */, 'UpArrow', 38, 'VK_UP', 'Up', empty],
|
||||
[1, 89 /* ScanCode.NumLock */, 'NumLock', 83 /* KeyCode.NumLock */, 'NumLock', 144, 'VK_NUMLOCK', empty, empty],
|
||||
[1, 90 /* ScanCode.NumpadDivide */, 'NumpadDivide', 113 /* KeyCode.NumpadDivide */, 'NumPad_Divide', 111, 'VK_DIVIDE', empty, empty],
|
||||
[1, 91 /* ScanCode.NumpadMultiply */, 'NumpadMultiply', 108 /* KeyCode.NumpadMultiply */, 'NumPad_Multiply', 106, 'VK_MULTIPLY', empty, empty],
|
||||
[1, 92 /* ScanCode.NumpadSubtract */, 'NumpadSubtract', 111 /* KeyCode.NumpadSubtract */, 'NumPad_Subtract', 109, 'VK_SUBTRACT', empty, empty],
|
||||
[1, 93 /* ScanCode.NumpadAdd */, 'NumpadAdd', 109 /* KeyCode.NumpadAdd */, 'NumPad_Add', 107, 'VK_ADD', empty, empty],
|
||||
[1, 94 /* ScanCode.NumpadEnter */, 'NumpadEnter', 3 /* KeyCode.Enter */, empty, 0, empty, empty, empty],
|
||||
[1, 95 /* ScanCode.Numpad1 */, 'Numpad1', 99 /* KeyCode.Numpad1 */, 'NumPad1', 97, 'VK_NUMPAD1', empty, empty],
|
||||
[1, 96 /* ScanCode.Numpad2 */, 'Numpad2', 100 /* KeyCode.Numpad2 */, 'NumPad2', 98, 'VK_NUMPAD2', empty, empty],
|
||||
[1, 97 /* ScanCode.Numpad3 */, 'Numpad3', 101 /* KeyCode.Numpad3 */, 'NumPad3', 99, 'VK_NUMPAD3', empty, empty],
|
||||
[1, 98 /* ScanCode.Numpad4 */, 'Numpad4', 102 /* KeyCode.Numpad4 */, 'NumPad4', 100, 'VK_NUMPAD4', empty, empty],
|
||||
[1, 99 /* ScanCode.Numpad5 */, 'Numpad5', 103 /* KeyCode.Numpad5 */, 'NumPad5', 101, 'VK_NUMPAD5', empty, empty],
|
||||
[1, 100 /* ScanCode.Numpad6 */, 'Numpad6', 104 /* KeyCode.Numpad6 */, 'NumPad6', 102, 'VK_NUMPAD6', empty, empty],
|
||||
[1, 101 /* ScanCode.Numpad7 */, 'Numpad7', 105 /* KeyCode.Numpad7 */, 'NumPad7', 103, 'VK_NUMPAD7', empty, empty],
|
||||
[1, 102 /* ScanCode.Numpad8 */, 'Numpad8', 106 /* KeyCode.Numpad8 */, 'NumPad8', 104, 'VK_NUMPAD8', empty, empty],
|
||||
[1, 103 /* ScanCode.Numpad9 */, 'Numpad9', 107 /* KeyCode.Numpad9 */, 'NumPad9', 105, 'VK_NUMPAD9', empty, empty],
|
||||
[1, 104 /* ScanCode.Numpad0 */, 'Numpad0', 98 /* KeyCode.Numpad0 */, 'NumPad0', 96, 'VK_NUMPAD0', empty, empty],
|
||||
[1, 105 /* ScanCode.NumpadDecimal */, 'NumpadDecimal', 112 /* KeyCode.NumpadDecimal */, 'NumPad_Decimal', 110, 'VK_DECIMAL', empty, empty],
|
||||
[0, 106 /* ScanCode.IntlBackslash */, 'IntlBackslash', 97 /* KeyCode.IntlBackslash */, 'OEM_102', 226, 'VK_OEM_102', empty, empty],
|
||||
[1, 107 /* ScanCode.ContextMenu */, 'ContextMenu', 58 /* KeyCode.ContextMenu */, 'ContextMenu', 93, empty, empty, empty],
|
||||
[1, 108 /* ScanCode.Power */, 'Power', 0 /* KeyCode.Unknown */, empty, 0, empty, empty, empty],
|
||||
[1, 109 /* ScanCode.NumpadEqual */, 'NumpadEqual', 0 /* KeyCode.Unknown */, empty, 0, empty, empty, empty],
|
||||
[1, 110 /* ScanCode.F13 */, 'F13', 71 /* KeyCode.F13 */, 'F13', 124, 'VK_F13', empty, empty],
|
||||
[1, 111 /* ScanCode.F14 */, 'F14', 72 /* KeyCode.F14 */, 'F14', 125, 'VK_F14', empty, empty],
|
||||
[1, 112 /* ScanCode.F15 */, 'F15', 73 /* KeyCode.F15 */, 'F15', 126, 'VK_F15', empty, empty],
|
||||
[1, 113 /* ScanCode.F16 */, 'F16', 74 /* KeyCode.F16 */, 'F16', 127, 'VK_F16', empty, empty],
|
||||
[1, 114 /* ScanCode.F17 */, 'F17', 75 /* KeyCode.F17 */, 'F17', 128, 'VK_F17', empty, empty],
|
||||
[1, 115 /* ScanCode.F18 */, 'F18', 76 /* KeyCode.F18 */, 'F18', 129, 'VK_F18', empty, empty],
|
||||
[1, 116 /* ScanCode.F19 */, 'F19', 77 /* KeyCode.F19 */, 'F19', 130, 'VK_F19', empty, empty],
|
||||
[1, 117 /* ScanCode.F20 */, 'F20', 78 /* KeyCode.F20 */, 'F20', 131, 'VK_F20', empty, empty],
|
||||
[1, 118 /* ScanCode.F21 */, 'F21', 79 /* KeyCode.F21 */, 'F21', 132, 'VK_F21', empty, empty],
|
||||
[1, 119 /* ScanCode.F22 */, 'F22', 80 /* KeyCode.F22 */, 'F22', 133, 'VK_F22', empty, empty],
|
||||
[1, 120 /* ScanCode.F23 */, 'F23', 81 /* KeyCode.F23 */, 'F23', 134, 'VK_F23', empty, empty],
|
||||
[1, 121 /* ScanCode.F24 */, 'F24', 82 /* KeyCode.F24 */, 'F24', 135, 'VK_F24', empty, empty],
|
||||
[1, 122 /* ScanCode.Open */, 'Open', 0 /* KeyCode.Unknown */, empty, 0, empty, empty, empty],
|
||||
[1, 123 /* ScanCode.Help */, 'Help', 0 /* KeyCode.Unknown */, empty, 0, empty, empty, empty],
|
||||
[1, 124 /* ScanCode.Select */, 'Select', 0 /* KeyCode.Unknown */, empty, 0, empty, empty, empty],
|
||||
[1, 125 /* ScanCode.Again */, 'Again', 0 /* KeyCode.Unknown */, empty, 0, empty, empty, empty],
|
||||
[1, 126 /* ScanCode.Undo */, 'Undo', 0 /* KeyCode.Unknown */, empty, 0, empty, empty, empty],
|
||||
[1, 127 /* ScanCode.Cut */, 'Cut', 0 /* KeyCode.Unknown */, empty, 0, empty, empty, empty],
|
||||
[1, 128 /* ScanCode.Copy */, 'Copy', 0 /* KeyCode.Unknown */, empty, 0, empty, empty, empty],
|
||||
[1, 129 /* ScanCode.Paste */, 'Paste', 0 /* KeyCode.Unknown */, empty, 0, empty, empty, empty],
|
||||
[1, 130 /* ScanCode.Find */, 'Find', 0 /* KeyCode.Unknown */, empty, 0, empty, empty, empty],
|
||||
[1, 131 /* ScanCode.AudioVolumeMute */, 'AudioVolumeMute', 117 /* KeyCode.AudioVolumeMute */, 'AudioVolumeMute', 173, 'VK_VOLUME_MUTE', empty, empty],
|
||||
[1, 132 /* ScanCode.AudioVolumeUp */, 'AudioVolumeUp', 118 /* KeyCode.AudioVolumeUp */, 'AudioVolumeUp', 175, 'VK_VOLUME_UP', empty, empty],
|
||||
[1, 133 /* ScanCode.AudioVolumeDown */, 'AudioVolumeDown', 119 /* KeyCode.AudioVolumeDown */, 'AudioVolumeDown', 174, 'VK_VOLUME_DOWN', empty, empty],
|
||||
[1, 134 /* ScanCode.NumpadComma */, 'NumpadComma', 110 /* KeyCode.NUMPAD_SEPARATOR */, 'NumPad_Separator', 108, 'VK_SEPARATOR', empty, empty],
|
||||
[0, 135 /* ScanCode.IntlRo */, 'IntlRo', 115 /* KeyCode.ABNT_C1 */, 'ABNT_C1', 193, 'VK_ABNT_C1', empty, empty],
|
||||
[1, 136 /* ScanCode.KanaMode */, 'KanaMode', 0 /* KeyCode.Unknown */, empty, 0, empty, empty, empty],
|
||||
[0, 137 /* ScanCode.IntlYen */, 'IntlYen', 0 /* KeyCode.Unknown */, empty, 0, empty, empty, empty],
|
||||
[1, 138 /* ScanCode.Convert */, 'Convert', 0 /* KeyCode.Unknown */, empty, 0, empty, empty, empty],
|
||||
[1, 139 /* ScanCode.NonConvert */, 'NonConvert', 0 /* KeyCode.Unknown */, empty, 0, empty, empty, empty],
|
||||
[1, 140 /* ScanCode.Lang1 */, 'Lang1', 0 /* KeyCode.Unknown */, empty, 0, empty, empty, empty],
|
||||
[1, 141 /* ScanCode.Lang2 */, 'Lang2', 0 /* KeyCode.Unknown */, empty, 0, empty, empty, empty],
|
||||
[1, 142 /* ScanCode.Lang3 */, 'Lang3', 0 /* KeyCode.Unknown */, empty, 0, empty, empty, empty],
|
||||
[1, 143 /* ScanCode.Lang4 */, 'Lang4', 0 /* KeyCode.Unknown */, empty, 0, empty, empty, empty],
|
||||
[1, 144 /* ScanCode.Lang5 */, 'Lang5', 0 /* KeyCode.Unknown */, empty, 0, empty, empty, empty],
|
||||
[1, 145 /* ScanCode.Abort */, 'Abort', 0 /* KeyCode.Unknown */, empty, 0, empty, empty, empty],
|
||||
[1, 146 /* ScanCode.Props */, 'Props', 0 /* KeyCode.Unknown */, empty, 0, empty, empty, empty],
|
||||
[1, 147 /* ScanCode.NumpadParenLeft */, 'NumpadParenLeft', 0 /* KeyCode.Unknown */, empty, 0, empty, empty, empty],
|
||||
[1, 148 /* ScanCode.NumpadParenRight */, 'NumpadParenRight', 0 /* KeyCode.Unknown */, empty, 0, empty, empty, empty],
|
||||
[1, 149 /* ScanCode.NumpadBackspace */, 'NumpadBackspace', 0 /* KeyCode.Unknown */, empty, 0, empty, empty, empty],
|
||||
[1, 150 /* ScanCode.NumpadMemoryStore */, 'NumpadMemoryStore', 0 /* KeyCode.Unknown */, empty, 0, empty, empty, empty],
|
||||
[1, 151 /* ScanCode.NumpadMemoryRecall */, 'NumpadMemoryRecall', 0 /* KeyCode.Unknown */, empty, 0, empty, empty, empty],
|
||||
[1, 152 /* ScanCode.NumpadMemoryClear */, 'NumpadMemoryClear', 0 /* KeyCode.Unknown */, empty, 0, empty, empty, empty],
|
||||
[1, 153 /* ScanCode.NumpadMemoryAdd */, 'NumpadMemoryAdd', 0 /* KeyCode.Unknown */, empty, 0, empty, empty, empty],
|
||||
[1, 154 /* ScanCode.NumpadMemorySubtract */, 'NumpadMemorySubtract', 0 /* KeyCode.Unknown */, empty, 0, empty, empty, empty],
|
||||
[1, 155 /* ScanCode.NumpadClear */, 'NumpadClear', 131 /* KeyCode.Clear */, 'Clear', 12, 'VK_CLEAR', empty, empty],
|
||||
[1, 156 /* ScanCode.NumpadClearEntry */, 'NumpadClearEntry', 0 /* KeyCode.Unknown */, empty, 0, empty, empty, empty],
|
||||
[1, 0 /* ScanCode.None */, empty, 5 /* KeyCode.Ctrl */, 'Ctrl', 17, 'VK_CONTROL', empty, empty],
|
||||
[1, 0 /* ScanCode.None */, empty, 4 /* KeyCode.Shift */, 'Shift', 16, 'VK_SHIFT', empty, empty],
|
||||
[1, 0 /* ScanCode.None */, empty, 6 /* KeyCode.Alt */, 'Alt', 18, 'VK_MENU', empty, empty],
|
||||
[1, 0 /* ScanCode.None */, empty, 57 /* KeyCode.Meta */, 'Meta', 91, 'VK_COMMAND', empty, empty],
|
||||
[1, 157 /* ScanCode.ControlLeft */, 'ControlLeft', 5 /* KeyCode.Ctrl */, empty, 0, 'VK_LCONTROL', empty, empty],
|
||||
[1, 158 /* ScanCode.ShiftLeft */, 'ShiftLeft', 4 /* KeyCode.Shift */, empty, 0, 'VK_LSHIFT', empty, empty],
|
||||
[1, 159 /* ScanCode.AltLeft */, 'AltLeft', 6 /* KeyCode.Alt */, empty, 0, 'VK_LMENU', empty, empty],
|
||||
[1, 160 /* ScanCode.MetaLeft */, 'MetaLeft', 57 /* KeyCode.Meta */, empty, 0, 'VK_LWIN', empty, empty],
|
||||
[1, 161 /* ScanCode.ControlRight */, 'ControlRight', 5 /* KeyCode.Ctrl */, empty, 0, 'VK_RCONTROL', empty, empty],
|
||||
[1, 162 /* ScanCode.ShiftRight */, 'ShiftRight', 4 /* KeyCode.Shift */, empty, 0, 'VK_RSHIFT', empty, empty],
|
||||
[1, 163 /* ScanCode.AltRight */, 'AltRight', 6 /* KeyCode.Alt */, empty, 0, 'VK_RMENU', empty, empty],
|
||||
[1, 164 /* ScanCode.MetaRight */, 'MetaRight', 57 /* KeyCode.Meta */, empty, 0, 'VK_RWIN', empty, empty],
|
||||
[1, 165 /* ScanCode.BrightnessUp */, 'BrightnessUp', 0 /* KeyCode.Unknown */, empty, 0, empty, empty, empty],
|
||||
[1, 166 /* ScanCode.BrightnessDown */, 'BrightnessDown', 0 /* KeyCode.Unknown */, empty, 0, empty, empty, empty],
|
||||
[1, 167 /* ScanCode.MediaPlay */, 'MediaPlay', 0 /* KeyCode.Unknown */, empty, 0, empty, empty, empty],
|
||||
[1, 168 /* ScanCode.MediaRecord */, 'MediaRecord', 0 /* KeyCode.Unknown */, empty, 0, empty, empty, empty],
|
||||
[1, 169 /* ScanCode.MediaFastForward */, 'MediaFastForward', 0 /* KeyCode.Unknown */, empty, 0, empty, empty, empty],
|
||||
[1, 170 /* ScanCode.MediaRewind */, 'MediaRewind', 0 /* KeyCode.Unknown */, empty, 0, empty, empty, empty],
|
||||
[1, 171 /* ScanCode.MediaTrackNext */, 'MediaTrackNext', 124 /* KeyCode.MediaTrackNext */, 'MediaTrackNext', 176, 'VK_MEDIA_NEXT_TRACK', empty, empty],
|
||||
[1, 172 /* ScanCode.MediaTrackPrevious */, 'MediaTrackPrevious', 125 /* KeyCode.MediaTrackPrevious */, 'MediaTrackPrevious', 177, 'VK_MEDIA_PREV_TRACK', empty, empty],
|
||||
[1, 173 /* ScanCode.MediaStop */, 'MediaStop', 126 /* KeyCode.MediaStop */, 'MediaStop', 178, 'VK_MEDIA_STOP', empty, empty],
|
||||
[1, 174 /* ScanCode.Eject */, 'Eject', 0 /* KeyCode.Unknown */, empty, 0, empty, empty, empty],
|
||||
[1, 175 /* ScanCode.MediaPlayPause */, 'MediaPlayPause', 127 /* KeyCode.MediaPlayPause */, 'MediaPlayPause', 179, 'VK_MEDIA_PLAY_PAUSE', empty, empty],
|
||||
[1, 176 /* ScanCode.MediaSelect */, 'MediaSelect', 128 /* KeyCode.LaunchMediaPlayer */, 'LaunchMediaPlayer', 181, 'VK_MEDIA_LAUNCH_MEDIA_SELECT', empty, empty],
|
||||
[1, 177 /* ScanCode.LaunchMail */, 'LaunchMail', 129 /* KeyCode.LaunchMail */, 'LaunchMail', 180, 'VK_MEDIA_LAUNCH_MAIL', empty, empty],
|
||||
[1, 178 /* ScanCode.LaunchApp2 */, 'LaunchApp2', 130 /* KeyCode.LaunchApp2 */, 'LaunchApp2', 183, 'VK_MEDIA_LAUNCH_APP2', empty, empty],
|
||||
[1, 179 /* ScanCode.LaunchApp1 */, 'LaunchApp1', 0 /* KeyCode.Unknown */, empty, 0, 'VK_MEDIA_LAUNCH_APP1', empty, empty],
|
||||
[1, 180 /* ScanCode.SelectTask */, 'SelectTask', 0 /* KeyCode.Unknown */, empty, 0, empty, empty, empty],
|
||||
[1, 181 /* ScanCode.LaunchScreenSaver */, 'LaunchScreenSaver', 0 /* KeyCode.Unknown */, empty, 0, empty, empty, empty],
|
||||
[1, 182 /* ScanCode.BrowserSearch */, 'BrowserSearch', 120 /* KeyCode.BrowserSearch */, 'BrowserSearch', 170, 'VK_BROWSER_SEARCH', empty, empty],
|
||||
[1, 183 /* ScanCode.BrowserHome */, 'BrowserHome', 121 /* KeyCode.BrowserHome */, 'BrowserHome', 172, 'VK_BROWSER_HOME', empty, empty],
|
||||
[1, 184 /* ScanCode.BrowserBack */, 'BrowserBack', 122 /* KeyCode.BrowserBack */, 'BrowserBack', 166, 'VK_BROWSER_BACK', empty, empty],
|
||||
[1, 185 /* ScanCode.BrowserForward */, 'BrowserForward', 123 /* KeyCode.BrowserForward */, 'BrowserForward', 167, 'VK_BROWSER_FORWARD', empty, empty],
|
||||
[1, 186 /* ScanCode.BrowserStop */, 'BrowserStop', 0 /* KeyCode.Unknown */, empty, 0, 'VK_BROWSER_STOP', empty, empty],
|
||||
[1, 187 /* ScanCode.BrowserRefresh */, 'BrowserRefresh', 0 /* KeyCode.Unknown */, empty, 0, 'VK_BROWSER_REFRESH', empty, empty],
|
||||
[1, 188 /* ScanCode.BrowserFavorites */, 'BrowserFavorites', 0 /* KeyCode.Unknown */, empty, 0, 'VK_BROWSER_FAVORITES', empty, empty],
|
||||
[1, 189 /* ScanCode.ZoomToggle */, 'ZoomToggle', 0 /* KeyCode.Unknown */, empty, 0, empty, empty, empty],
|
||||
[1, 190 /* ScanCode.MailReply */, 'MailReply', 0 /* KeyCode.Unknown */, empty, 0, empty, empty, empty],
|
||||
[1, 191 /* ScanCode.MailForward */, 'MailForward', 0 /* KeyCode.Unknown */, empty, 0, empty, empty, empty],
|
||||
[1, 192 /* ScanCode.MailSend */, 'MailSend', 0 /* KeyCode.Unknown */, empty, 0, empty, empty, empty],
|
||||
// See https://lists.w3.org/Archives/Public/www-dom/2010JulSep/att-0182/keyCode-spec.html
|
||||
// If an Input Method Editor is processing key input and the event is keydown, return 229.
|
||||
[1, 0 /* ScanCode.None */, empty, 114 /* KeyCode.KEY_IN_COMPOSITION */, 'KeyInComposition', 229, empty, empty, empty],
|
||||
[1, 0 /* ScanCode.None */, empty, 116 /* KeyCode.ABNT_C2 */, 'ABNT_C2', 194, 'VK_ABNT_C2', empty, empty],
|
||||
[1, 0 /* ScanCode.None */, empty, 96 /* KeyCode.OEM_8 */, 'OEM_8', 223, 'VK_OEM_8', empty, empty],
|
||||
[1, 0 /* ScanCode.None */, empty, 0 /* KeyCode.Unknown */, empty, 0, 'VK_KANA', empty, empty],
|
||||
[1, 0 /* ScanCode.None */, empty, 0 /* KeyCode.Unknown */, empty, 0, 'VK_HANGUL', empty, empty],
|
||||
[1, 0 /* ScanCode.None */, empty, 0 /* KeyCode.Unknown */, empty, 0, 'VK_JUNJA', empty, empty],
|
||||
[1, 0 /* ScanCode.None */, empty, 0 /* KeyCode.Unknown */, empty, 0, 'VK_FINAL', empty, empty],
|
||||
[1, 0 /* ScanCode.None */, empty, 0 /* KeyCode.Unknown */, empty, 0, 'VK_HANJA', empty, empty],
|
||||
[1, 0 /* ScanCode.None */, empty, 0 /* KeyCode.Unknown */, empty, 0, 'VK_KANJI', empty, empty],
|
||||
[1, 0 /* ScanCode.None */, empty, 0 /* KeyCode.Unknown */, empty, 0, 'VK_CONVERT', empty, empty],
|
||||
[1, 0 /* ScanCode.None */, empty, 0 /* KeyCode.Unknown */, empty, 0, 'VK_NONCONVERT', empty, empty],
|
||||
[1, 0 /* ScanCode.None */, empty, 0 /* KeyCode.Unknown */, empty, 0, 'VK_ACCEPT', empty, empty],
|
||||
[1, 0 /* ScanCode.None */, empty, 0 /* KeyCode.Unknown */, empty, 0, 'VK_MODECHANGE', empty, empty],
|
||||
[1, 0 /* ScanCode.None */, empty, 0 /* KeyCode.Unknown */, empty, 0, 'VK_SELECT', empty, empty],
|
||||
[1, 0 /* ScanCode.None */, empty, 0 /* KeyCode.Unknown */, empty, 0, 'VK_PRINT', empty, empty],
|
||||
[1, 0 /* ScanCode.None */, empty, 0 /* KeyCode.Unknown */, empty, 0, 'VK_EXECUTE', empty, empty],
|
||||
[1, 0 /* ScanCode.None */, empty, 0 /* KeyCode.Unknown */, empty, 0, 'VK_SNAPSHOT', empty, empty],
|
||||
[1, 0 /* ScanCode.None */, empty, 0 /* KeyCode.Unknown */, empty, 0, 'VK_HELP', empty, empty],
|
||||
[1, 0 /* ScanCode.None */, empty, 0 /* KeyCode.Unknown */, empty, 0, 'VK_APPS', empty, empty],
|
||||
[1, 0 /* ScanCode.None */, empty, 0 /* KeyCode.Unknown */, empty, 0, 'VK_PROCESSKEY', empty, empty],
|
||||
[1, 0 /* ScanCode.None */, empty, 0 /* KeyCode.Unknown */, empty, 0, 'VK_PACKET', empty, empty],
|
||||
[1, 0 /* ScanCode.None */, empty, 0 /* KeyCode.Unknown */, empty, 0, 'VK_DBE_SBCSCHAR', empty, empty],
|
||||
[1, 0 /* ScanCode.None */, empty, 0 /* KeyCode.Unknown */, empty, 0, 'VK_DBE_DBCSCHAR', empty, empty],
|
||||
[1, 0 /* ScanCode.None */, empty, 0 /* KeyCode.Unknown */, empty, 0, 'VK_ATTN', empty, empty],
|
||||
[1, 0 /* ScanCode.None */, empty, 0 /* KeyCode.Unknown */, empty, 0, 'VK_CRSEL', empty, empty],
|
||||
[1, 0 /* ScanCode.None */, empty, 0 /* KeyCode.Unknown */, empty, 0, 'VK_EXSEL', empty, empty],
|
||||
[1, 0 /* ScanCode.None */, empty, 0 /* KeyCode.Unknown */, empty, 0, 'VK_EREOF', empty, empty],
|
||||
[1, 0 /* ScanCode.None */, empty, 0 /* KeyCode.Unknown */, empty, 0, 'VK_PLAY', empty, empty],
|
||||
[1, 0 /* ScanCode.None */, empty, 0 /* KeyCode.Unknown */, empty, 0, 'VK_ZOOM', empty, empty],
|
||||
[1, 0 /* ScanCode.None */, empty, 0 /* KeyCode.Unknown */, empty, 0, 'VK_NONAME', empty, empty],
|
||||
[1, 0 /* ScanCode.None */, empty, 0 /* KeyCode.Unknown */, empty, 0, 'VK_PA1', empty, empty],
|
||||
[1, 0 /* ScanCode.None */, empty, 0 /* KeyCode.Unknown */, empty, 0, 'VK_OEM_CLEAR', empty, empty],
|
||||
];
|
||||
const seenKeyCode = [];
|
||||
const seenScanCode = [];
|
||||
for (const mapping of mappings) {
|
||||
const [immutable, scanCode, scanCodeStr, keyCode, keyCodeStr, eventKeyCode, vkey, usUserSettingsLabel, generalUserSettingsLabel] = mapping;
|
||||
if (!seenScanCode[scanCode]) {
|
||||
seenScanCode[scanCode] = true;
|
||||
scanCodeStrToInt[scanCodeStr] = scanCode;
|
||||
scanCodeLowerCaseStrToInt[scanCodeStr.toLowerCase()] = scanCode;
|
||||
if (immutable) {
|
||||
IMMUTABLE_CODE_TO_KEY_CODE[scanCode] = keyCode;
|
||||
}
|
||||
}
|
||||
if (!seenKeyCode[keyCode]) {
|
||||
seenKeyCode[keyCode] = true;
|
||||
if (!keyCodeStr) {
|
||||
throw new Error(`String representation missing for key code ${keyCode} around scan code ${scanCodeStr}`);
|
||||
}
|
||||
uiMap.define(keyCode, keyCodeStr);
|
||||
userSettingsUSMap.define(keyCode, usUserSettingsLabel || keyCodeStr);
|
||||
userSettingsGeneralMap.define(keyCode, generalUserSettingsLabel || usUserSettingsLabel || keyCodeStr);
|
||||
}
|
||||
if (eventKeyCode) {
|
||||
EVENT_KEY_CODE_MAP[eventKeyCode] = keyCode;
|
||||
}
|
||||
}
|
||||
})();
|
||||
var KeyCodeUtils;
|
||||
(function (KeyCodeUtils) {
|
||||
function toString(keyCode) {
|
||||
return uiMap.keyCodeToStr(keyCode);
|
||||
}
|
||||
KeyCodeUtils.toString = toString;
|
||||
function fromString(key) {
|
||||
return uiMap.strToKeyCode(key);
|
||||
}
|
||||
KeyCodeUtils.fromString = fromString;
|
||||
function toUserSettingsUS(keyCode) {
|
||||
return userSettingsUSMap.keyCodeToStr(keyCode);
|
||||
}
|
||||
KeyCodeUtils.toUserSettingsUS = toUserSettingsUS;
|
||||
function toUserSettingsGeneral(keyCode) {
|
||||
return userSettingsGeneralMap.keyCodeToStr(keyCode);
|
||||
}
|
||||
KeyCodeUtils.toUserSettingsGeneral = toUserSettingsGeneral;
|
||||
function fromUserSettings(key) {
|
||||
return userSettingsUSMap.strToKeyCode(key) || userSettingsGeneralMap.strToKeyCode(key);
|
||||
}
|
||||
KeyCodeUtils.fromUserSettings = fromUserSettings;
|
||||
function toElectronAccelerator(keyCode) {
|
||||
if (keyCode >= 98 /* KeyCode.Numpad0 */ && keyCode <= 113 /* KeyCode.NumpadDivide */) {
|
||||
// [Electron Accelerators] Electron is able to parse numpad keys, but unfortunately it
|
||||
// renders them just as regular keys in menus. For example, num0 is rendered as "0",
|
||||
// numdiv is rendered as "/", numsub is rendered as "-".
|
||||
//
|
||||
// This can lead to incredible confusion, as it makes numpad based keybindings indistinguishable
|
||||
// from keybindings based on regular keys.
|
||||
//
|
||||
// We therefore need to fall back to custom rendering for numpad keys.
|
||||
return null;
|
||||
}
|
||||
switch (keyCode) {
|
||||
case 16 /* KeyCode.UpArrow */:
|
||||
return 'Up';
|
||||
case 18 /* KeyCode.DownArrow */:
|
||||
return 'Down';
|
||||
case 15 /* KeyCode.LeftArrow */:
|
||||
return 'Left';
|
||||
case 17 /* KeyCode.RightArrow */:
|
||||
return 'Right';
|
||||
}
|
||||
return uiMap.keyCodeToStr(keyCode);
|
||||
}
|
||||
KeyCodeUtils.toElectronAccelerator = toElectronAccelerator;
|
||||
})(KeyCodeUtils || (KeyCodeUtils = {}));
|
||||
function KeyChord(firstPart, secondPart) {
|
||||
const chordPart = ((secondPart & 0x0000FFFF) << 16) >>> 0;
|
||||
return (firstPart | chordPart) >>> 0;
|
||||
}
|
||||
|
||||
export { EVENT_KEY_CODE_MAP, IMMUTABLE_CODE_TO_KEY_CODE, KeyChord, KeyCodeUtils };
|
||||
+139
@@ -0,0 +1,139 @@
|
||||
import { localize } from '../../nls.js';
|
||||
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
class ModifierLabelProvider {
|
||||
constructor(mac, windows, linux = windows) {
|
||||
this.modifierLabels = [null]; // index 0 will never me accessed.
|
||||
this.modifierLabels[2 /* OperatingSystem.Macintosh */] = mac;
|
||||
this.modifierLabels[1 /* OperatingSystem.Windows */] = windows;
|
||||
this.modifierLabels[3 /* OperatingSystem.Linux */] = linux;
|
||||
}
|
||||
toLabel(OS, chords, keyLabelProvider) {
|
||||
if (chords.length === 0) {
|
||||
return null;
|
||||
}
|
||||
const result = [];
|
||||
for (let i = 0, len = chords.length; i < len; i++) {
|
||||
const chord = chords[i];
|
||||
const keyLabel = keyLabelProvider(chord);
|
||||
if (keyLabel === null) {
|
||||
// this keybinding cannot be expressed...
|
||||
return null;
|
||||
}
|
||||
result[i] = _simpleAsString(chord, keyLabel, this.modifierLabels[OS]);
|
||||
}
|
||||
return result.join(' ');
|
||||
}
|
||||
}
|
||||
/**
|
||||
* A label provider that prints modifiers in a suitable format for displaying in the UI.
|
||||
*/
|
||||
const UILabelProvider = new ModifierLabelProvider({
|
||||
ctrlKey: '\u2303',
|
||||
shiftKey: '⇧',
|
||||
altKey: '⌥',
|
||||
metaKey: '⌘',
|
||||
separator: '',
|
||||
}, {
|
||||
ctrlKey: localize(35, "Ctrl"),
|
||||
shiftKey: localize(36, "Shift"),
|
||||
altKey: localize(37, "Alt"),
|
||||
metaKey: localize(38, "Windows"),
|
||||
separator: '+',
|
||||
}, {
|
||||
ctrlKey: localize(39, "Ctrl"),
|
||||
shiftKey: localize(40, "Shift"),
|
||||
altKey: localize(41, "Alt"),
|
||||
metaKey: localize(42, "Super"),
|
||||
separator: '+',
|
||||
});
|
||||
/**
|
||||
* A label provider that prints modifiers in a suitable format for ARIA.
|
||||
*/
|
||||
const AriaLabelProvider = new ModifierLabelProvider({
|
||||
ctrlKey: localize(43, "Control"),
|
||||
shiftKey: localize(44, "Shift"),
|
||||
altKey: localize(45, "Option"),
|
||||
metaKey: localize(46, "Command"),
|
||||
separator: '+',
|
||||
}, {
|
||||
ctrlKey: localize(47, "Control"),
|
||||
shiftKey: localize(48, "Shift"),
|
||||
altKey: localize(49, "Alt"),
|
||||
metaKey: localize(50, "Windows"),
|
||||
separator: '+',
|
||||
}, {
|
||||
ctrlKey: localize(51, "Control"),
|
||||
shiftKey: localize(52, "Shift"),
|
||||
altKey: localize(53, "Alt"),
|
||||
metaKey: localize(54, "Super"),
|
||||
separator: '+',
|
||||
});
|
||||
/**
|
||||
* A label provider that prints modifiers in a suitable format for Electron Accelerators.
|
||||
* See https://github.com/electron/electron/blob/master/docs/api/accelerator.md
|
||||
*/
|
||||
const ElectronAcceleratorLabelProvider = new ModifierLabelProvider({
|
||||
ctrlKey: 'Ctrl',
|
||||
shiftKey: 'Shift',
|
||||
altKey: 'Alt',
|
||||
metaKey: 'Cmd',
|
||||
separator: '+',
|
||||
}, {
|
||||
ctrlKey: 'Ctrl',
|
||||
shiftKey: 'Shift',
|
||||
altKey: 'Alt',
|
||||
metaKey: 'Super',
|
||||
separator: '+',
|
||||
});
|
||||
/**
|
||||
* A label provider that prints modifiers in a suitable format for user settings.
|
||||
*/
|
||||
const UserSettingsLabelProvider = new ModifierLabelProvider({
|
||||
ctrlKey: 'ctrl',
|
||||
shiftKey: 'shift',
|
||||
altKey: 'alt',
|
||||
metaKey: 'cmd',
|
||||
separator: '+',
|
||||
}, {
|
||||
ctrlKey: 'ctrl',
|
||||
shiftKey: 'shift',
|
||||
altKey: 'alt',
|
||||
metaKey: 'win',
|
||||
separator: '+',
|
||||
}, {
|
||||
ctrlKey: 'ctrl',
|
||||
shiftKey: 'shift',
|
||||
altKey: 'alt',
|
||||
metaKey: 'meta',
|
||||
separator: '+',
|
||||
});
|
||||
function _simpleAsString(modifiers, key, labels) {
|
||||
if (key === null) {
|
||||
return '';
|
||||
}
|
||||
const result = [];
|
||||
// translate modifier keys: Ctrl-Shift-Alt-Meta
|
||||
if (modifiers.ctrlKey) {
|
||||
result.push(labels.ctrlKey);
|
||||
}
|
||||
if (modifiers.shiftKey) {
|
||||
result.push(labels.shiftKey);
|
||||
}
|
||||
if (modifiers.altKey) {
|
||||
result.push(labels.altKey);
|
||||
}
|
||||
if (modifiers.metaKey) {
|
||||
result.push(labels.metaKey);
|
||||
}
|
||||
// the actual key
|
||||
if (key !== '') {
|
||||
result.push(key);
|
||||
}
|
||||
return result.join(labels.separator);
|
||||
}
|
||||
|
||||
export { AriaLabelProvider, ElectronAcceleratorLabelProvider, ModifierLabelProvider, UILabelProvider, UserSettingsLabelProvider };
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
import { illegalArgument } from './errors.js';
|
||||
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
function decodeKeybinding(keybinding, OS) {
|
||||
if (typeof keybinding === 'number') {
|
||||
if (keybinding === 0) {
|
||||
return null;
|
||||
}
|
||||
const firstChord = (keybinding & 0x0000FFFF) >>> 0;
|
||||
const secondChord = (keybinding & 0xFFFF0000) >>> 16;
|
||||
if (secondChord !== 0) {
|
||||
return new Keybinding([
|
||||
createSimpleKeybinding(firstChord, OS),
|
||||
createSimpleKeybinding(secondChord, OS)
|
||||
]);
|
||||
}
|
||||
return new Keybinding([createSimpleKeybinding(firstChord, OS)]);
|
||||
}
|
||||
else {
|
||||
const chords = [];
|
||||
for (let i = 0; i < keybinding.length; i++) {
|
||||
chords.push(createSimpleKeybinding(keybinding[i], OS));
|
||||
}
|
||||
return new Keybinding(chords);
|
||||
}
|
||||
}
|
||||
function createSimpleKeybinding(keybinding, OS) {
|
||||
const ctrlCmd = (keybinding & 2048 /* BinaryKeybindingsMask.CtrlCmd */ ? true : false);
|
||||
const winCtrl = (keybinding & 256 /* BinaryKeybindingsMask.WinCtrl */ ? true : false);
|
||||
const ctrlKey = (OS === 2 /* OperatingSystem.Macintosh */ ? winCtrl : ctrlCmd);
|
||||
const shiftKey = (keybinding & 1024 /* BinaryKeybindingsMask.Shift */ ? true : false);
|
||||
const altKey = (keybinding & 512 /* BinaryKeybindingsMask.Alt */ ? true : false);
|
||||
const metaKey = (OS === 2 /* OperatingSystem.Macintosh */ ? ctrlCmd : winCtrl);
|
||||
const keyCode = (keybinding & 255 /* BinaryKeybindingsMask.KeyCode */);
|
||||
return new KeyCodeChord(ctrlKey, shiftKey, altKey, metaKey, keyCode);
|
||||
}
|
||||
/**
|
||||
* Represents a chord which uses the `keyCode` field of keyboard events.
|
||||
* A chord is a combination of keys pressed simultaneously.
|
||||
*/
|
||||
class KeyCodeChord {
|
||||
constructor(ctrlKey, shiftKey, altKey, metaKey, keyCode) {
|
||||
this.ctrlKey = ctrlKey;
|
||||
this.shiftKey = shiftKey;
|
||||
this.altKey = altKey;
|
||||
this.metaKey = metaKey;
|
||||
this.keyCode = keyCode;
|
||||
}
|
||||
equals(other) {
|
||||
return (other instanceof KeyCodeChord
|
||||
&& this.ctrlKey === other.ctrlKey
|
||||
&& this.shiftKey === other.shiftKey
|
||||
&& this.altKey === other.altKey
|
||||
&& this.metaKey === other.metaKey
|
||||
&& this.keyCode === other.keyCode);
|
||||
}
|
||||
isModifierKey() {
|
||||
return (this.keyCode === 0 /* KeyCode.Unknown */
|
||||
|| this.keyCode === 5 /* KeyCode.Ctrl */
|
||||
|| this.keyCode === 57 /* KeyCode.Meta */
|
||||
|| this.keyCode === 6 /* KeyCode.Alt */
|
||||
|| this.keyCode === 4 /* KeyCode.Shift */);
|
||||
}
|
||||
/**
|
||||
* Does this keybinding refer to the key code of a modifier and it also has the modifier flag?
|
||||
*/
|
||||
isDuplicateModifierCase() {
|
||||
return ((this.ctrlKey && this.keyCode === 5 /* KeyCode.Ctrl */)
|
||||
|| (this.shiftKey && this.keyCode === 4 /* KeyCode.Shift */)
|
||||
|| (this.altKey && this.keyCode === 6 /* KeyCode.Alt */)
|
||||
|| (this.metaKey && this.keyCode === 57 /* KeyCode.Meta */));
|
||||
}
|
||||
}
|
||||
/**
|
||||
* A keybinding is a sequence of chords.
|
||||
*/
|
||||
class Keybinding {
|
||||
constructor(chords) {
|
||||
if (chords.length === 0) {
|
||||
throw illegalArgument(`chords`);
|
||||
}
|
||||
this.chords = chords;
|
||||
}
|
||||
}
|
||||
class ResolvedChord {
|
||||
constructor(ctrlKey, shiftKey, altKey, metaKey, keyLabel, keyAriaLabel) {
|
||||
this.ctrlKey = ctrlKey;
|
||||
this.shiftKey = shiftKey;
|
||||
this.altKey = altKey;
|
||||
this.metaKey = metaKey;
|
||||
this.keyLabel = keyLabel;
|
||||
this.keyAriaLabel = keyAriaLabel;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* A resolved keybinding. Consists of one or multiple chords.
|
||||
*/
|
||||
class ResolvedKeybinding {
|
||||
}
|
||||
|
||||
export { KeyCodeChord, Keybinding, ResolvedChord, ResolvedKeybinding, createSimpleKeybinding, decodeKeybinding };
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
import { hasDriveLetter } from './extpath.js';
|
||||
import { isWindows } from './platform.js';
|
||||
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
function normalizeDriveLetter(path, isWindowsOS = isWindows) {
|
||||
if (hasDriveLetter(path, isWindowsOS)) {
|
||||
return path.charAt(0).toUpperCase() + path.slice(1);
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
export { normalizeDriveLetter };
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
var LazyValueState;
|
||||
(function (LazyValueState) {
|
||||
LazyValueState[LazyValueState["Uninitialized"] = 0] = "Uninitialized";
|
||||
LazyValueState[LazyValueState["Running"] = 1] = "Running";
|
||||
LazyValueState[LazyValueState["Completed"] = 2] = "Completed";
|
||||
})(LazyValueState || (LazyValueState = {}));
|
||||
class Lazy {
|
||||
constructor(executor) {
|
||||
this.executor = executor;
|
||||
this._state = LazyValueState.Uninitialized;
|
||||
}
|
||||
/**
|
||||
* Get the wrapped value.
|
||||
*
|
||||
* This will force evaluation of the lazy value if it has not been resolved yet. Lazy values are only
|
||||
* resolved once. `getValue` will re-throw exceptions that are hit while resolving the value
|
||||
*/
|
||||
get value() {
|
||||
if (this._state === LazyValueState.Uninitialized) {
|
||||
this._state = LazyValueState.Running;
|
||||
try {
|
||||
this._value = this.executor();
|
||||
}
|
||||
catch (err) {
|
||||
this._error = err;
|
||||
}
|
||||
finally {
|
||||
this._state = LazyValueState.Completed;
|
||||
}
|
||||
}
|
||||
else if (this._state === LazyValueState.Running) {
|
||||
throw new Error('Cannot read the value of a lazy that is being initialized');
|
||||
}
|
||||
if (this._error) {
|
||||
throw this._error;
|
||||
}
|
||||
return this._value;
|
||||
}
|
||||
/**
|
||||
* Get the wrapped value without forcing evaluation.
|
||||
*/
|
||||
get rawValue() { return this._value; }
|
||||
}
|
||||
|
||||
export { Lazy };
|
||||
+311
@@ -0,0 +1,311 @@
|
||||
import { Iterable } from './iterator.js';
|
||||
|
||||
function setParentOfDisposable(child, parent) {
|
||||
}
|
||||
/**
|
||||
* Indicates that the given object is a singleton which does not need to be disposed.
|
||||
*/
|
||||
function markAsSingleton(singleton) {
|
||||
return singleton;
|
||||
}
|
||||
/**
|
||||
* Check if `thing` is {@link IDisposable disposable}.
|
||||
*/
|
||||
function isDisposable(thing) {
|
||||
// eslint-disable-next-line local/code-no-any-casts
|
||||
return typeof thing === 'object' && thing !== null && typeof thing.dispose === 'function' && thing.dispose.length === 0;
|
||||
}
|
||||
function dispose(arg) {
|
||||
if (Iterable.is(arg)) {
|
||||
const errors = [];
|
||||
for (const d of arg) {
|
||||
if (d) {
|
||||
try {
|
||||
d.dispose();
|
||||
}
|
||||
catch (e) {
|
||||
errors.push(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (errors.length === 1) {
|
||||
throw errors[0];
|
||||
}
|
||||
else if (errors.length > 1) {
|
||||
throw new AggregateError(errors, 'Encountered errors while disposing of store');
|
||||
}
|
||||
return Array.isArray(arg) ? [] : arg;
|
||||
}
|
||||
else if (arg) {
|
||||
arg.dispose();
|
||||
return arg;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Combine multiple disposable values into a single {@link IDisposable}.
|
||||
*/
|
||||
function combinedDisposable(...disposables) {
|
||||
const parent = toDisposable(() => dispose(disposables));
|
||||
return parent;
|
||||
}
|
||||
class FunctionDisposable {
|
||||
constructor(fn) {
|
||||
this._isDisposed = false;
|
||||
this._fn = fn;
|
||||
}
|
||||
dispose() {
|
||||
if (this._isDisposed) {
|
||||
return;
|
||||
}
|
||||
if (!this._fn) {
|
||||
throw new Error(`Unbound disposable context: Need to use an arrow function to preserve the value of this`);
|
||||
}
|
||||
this._isDisposed = true;
|
||||
this._fn();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Turn a function that implements dispose into an {@link IDisposable}.
|
||||
*
|
||||
* @param fn Clean up function, guaranteed to be called only **once**.
|
||||
*/
|
||||
function toDisposable(fn) {
|
||||
return new FunctionDisposable(fn);
|
||||
}
|
||||
/**
|
||||
* Manages a collection of disposable values.
|
||||
*
|
||||
* This is the preferred way to manage multiple disposables. A `DisposableStore` is safer to work with than an
|
||||
* `IDisposable[]` as it considers edge cases, such as registering the same value multiple times or adding an item to a
|
||||
* store that has already been disposed of.
|
||||
*/
|
||||
class DisposableStore {
|
||||
static { this.DISABLE_DISPOSED_WARNING = false; }
|
||||
constructor() {
|
||||
this._toDispose = new Set();
|
||||
this._isDisposed = false;
|
||||
}
|
||||
/**
|
||||
* Dispose of all registered disposables and mark this object as disposed.
|
||||
*
|
||||
* Any future disposables added to this object will be disposed of on `add`.
|
||||
*/
|
||||
dispose() {
|
||||
if (this._isDisposed) {
|
||||
return;
|
||||
}
|
||||
this._isDisposed = true;
|
||||
this.clear();
|
||||
}
|
||||
/**
|
||||
* @return `true` if this object has been disposed of.
|
||||
*/
|
||||
get isDisposed() {
|
||||
return this._isDisposed;
|
||||
}
|
||||
/**
|
||||
* Dispose of all registered disposables but do not mark this object as disposed.
|
||||
*/
|
||||
clear() {
|
||||
if (this._toDispose.size === 0) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
dispose(this._toDispose);
|
||||
}
|
||||
finally {
|
||||
this._toDispose.clear();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Add a new {@link IDisposable disposable} to the collection.
|
||||
*/
|
||||
add(o) {
|
||||
if (!o || o === Disposable.None) {
|
||||
return o;
|
||||
}
|
||||
if (o === this) {
|
||||
throw new Error('Cannot register a disposable on itself!');
|
||||
}
|
||||
if (this._isDisposed) {
|
||||
if (!DisposableStore.DISABLE_DISPOSED_WARNING) {
|
||||
console.warn(new Error('Trying to add a disposable to a DisposableStore that has already been disposed of. The added object will be leaked!').stack);
|
||||
}
|
||||
}
|
||||
else {
|
||||
this._toDispose.add(o);
|
||||
}
|
||||
return o;
|
||||
}
|
||||
/**
|
||||
* Deletes a disposable from store and disposes of it. This will not throw or warn and proceed to dispose the
|
||||
* disposable even when the disposable is not part in the store.
|
||||
*/
|
||||
delete(o) {
|
||||
if (!o) {
|
||||
return;
|
||||
}
|
||||
if (o === this) {
|
||||
throw new Error('Cannot dispose a disposable on itself!');
|
||||
}
|
||||
this._toDispose.delete(o);
|
||||
o.dispose();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Abstract base class for a {@link IDisposable disposable} object.
|
||||
*
|
||||
* Subclasses can {@linkcode _register} disposables that will be automatically cleaned up when this object is disposed of.
|
||||
*/
|
||||
class Disposable {
|
||||
/**
|
||||
* A disposable that does nothing when it is disposed of.
|
||||
*
|
||||
* TODO: This should not be a static property.
|
||||
*/
|
||||
static { this.None = Object.freeze({ dispose() { } }); }
|
||||
constructor() {
|
||||
this._store = new DisposableStore();
|
||||
setParentOfDisposable(this._store);
|
||||
}
|
||||
dispose() {
|
||||
this._store.dispose();
|
||||
}
|
||||
/**
|
||||
* Adds `o` to the collection of disposables managed by this object.
|
||||
*/
|
||||
_register(o) {
|
||||
if (o === this) {
|
||||
throw new Error('Cannot register a disposable on itself!');
|
||||
}
|
||||
return this._store.add(o);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Manages the lifecycle of a disposable value that may be changed.
|
||||
*
|
||||
* This ensures that when the disposable value is changed, the previously held disposable is disposed of. You can
|
||||
* also register a `MutableDisposable` on a `Disposable` to ensure it is automatically cleaned up.
|
||||
*/
|
||||
class MutableDisposable {
|
||||
constructor() {
|
||||
this._isDisposed = false;
|
||||
}
|
||||
/**
|
||||
* Get the currently held disposable value, or `undefined` if this MutableDisposable has been disposed
|
||||
*/
|
||||
get value() {
|
||||
return this._isDisposed ? undefined : this._value;
|
||||
}
|
||||
/**
|
||||
* Set a new disposable value.
|
||||
*
|
||||
* Behaviour:
|
||||
* - If the MutableDisposable has been disposed, the setter is a no-op.
|
||||
* - If the new value is strictly equal to the current value, the setter is a no-op.
|
||||
* - Otherwise the previous value (if any) is disposed and the new value is stored.
|
||||
*
|
||||
* Related helpers:
|
||||
* - clear() resets the value to `undefined` (and disposes the previous value).
|
||||
* - clearAndLeak() returns the old value without disposing it and removes its parent.
|
||||
*/
|
||||
set value(value) {
|
||||
if (this._isDisposed || value === this._value) {
|
||||
return;
|
||||
}
|
||||
this._value?.dispose();
|
||||
this._value = value;
|
||||
}
|
||||
/**
|
||||
* Resets the stored value and disposed of the previously stored value.
|
||||
*/
|
||||
clear() {
|
||||
this.value = undefined;
|
||||
}
|
||||
dispose() {
|
||||
this._isDisposed = true;
|
||||
this._value?.dispose();
|
||||
this._value = undefined;
|
||||
}
|
||||
}
|
||||
class RefCountedDisposable {
|
||||
constructor(_disposable) {
|
||||
this._disposable = _disposable;
|
||||
this._counter = 1;
|
||||
}
|
||||
acquire() {
|
||||
this._counter++;
|
||||
return this;
|
||||
}
|
||||
release() {
|
||||
if (--this._counter === 0) {
|
||||
this._disposable.dispose();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
}
|
||||
class ImmortalReference {
|
||||
constructor(object) {
|
||||
this.object = object;
|
||||
}
|
||||
dispose() { }
|
||||
}
|
||||
/**
|
||||
* A map the manages the lifecycle of the values that it stores.
|
||||
*/
|
||||
class DisposableMap {
|
||||
constructor() {
|
||||
this._store = new Map();
|
||||
this._isDisposed = false;
|
||||
}
|
||||
/**
|
||||
* Disposes of all stored values and mark this object as disposed.
|
||||
*
|
||||
* Trying to use this object after it has been disposed of is an error.
|
||||
*/
|
||||
dispose() {
|
||||
this._isDisposed = true;
|
||||
this.clearAndDisposeAll();
|
||||
}
|
||||
/**
|
||||
* Disposes of all stored values and clear the map, but DO NOT mark this object as disposed.
|
||||
*/
|
||||
clearAndDisposeAll() {
|
||||
if (!this._store.size) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
dispose(this._store.values());
|
||||
}
|
||||
finally {
|
||||
this._store.clear();
|
||||
}
|
||||
}
|
||||
get(key) {
|
||||
return this._store.get(key);
|
||||
}
|
||||
set(key, value, skipDisposeOnOverwrite = false) {
|
||||
if (this._isDisposed) {
|
||||
console.warn(new Error('Trying to add a disposable to a DisposableMap that has already been disposed of. The added object will be leaked!').stack);
|
||||
}
|
||||
if (!skipDisposeOnOverwrite) {
|
||||
this._store.get(key)?.dispose();
|
||||
}
|
||||
this._store.set(key, value);
|
||||
}
|
||||
/**
|
||||
* Delete the value stored for `key` from this map and also dispose of it.
|
||||
*/
|
||||
deleteAndDispose(key) {
|
||||
this._store.get(key)?.dispose();
|
||||
this._store.delete(key);
|
||||
}
|
||||
values() {
|
||||
return this._store.values();
|
||||
}
|
||||
[Symbol.iterator]() {
|
||||
return this._store[Symbol.iterator]();
|
||||
}
|
||||
}
|
||||
|
||||
export { Disposable, DisposableMap, DisposableStore, ImmortalReference, MutableDisposable, RefCountedDisposable, combinedDisposable, dispose, isDisposable, markAsSingleton, toDisposable };
|
||||
+126
@@ -0,0 +1,126 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
class Node {
|
||||
static { this.Undefined = new Node(undefined); }
|
||||
constructor(element) {
|
||||
this.element = element;
|
||||
this.next = Node.Undefined;
|
||||
this.prev = Node.Undefined;
|
||||
}
|
||||
}
|
||||
class LinkedList {
|
||||
constructor() {
|
||||
this._first = Node.Undefined;
|
||||
this._last = Node.Undefined;
|
||||
this._size = 0;
|
||||
}
|
||||
get size() {
|
||||
return this._size;
|
||||
}
|
||||
isEmpty() {
|
||||
return this._first === Node.Undefined;
|
||||
}
|
||||
clear() {
|
||||
let node = this._first;
|
||||
while (node !== Node.Undefined) {
|
||||
const next = node.next;
|
||||
node.prev = Node.Undefined;
|
||||
node.next = Node.Undefined;
|
||||
node = next;
|
||||
}
|
||||
this._first = Node.Undefined;
|
||||
this._last = Node.Undefined;
|
||||
this._size = 0;
|
||||
}
|
||||
unshift(element) {
|
||||
return this._insert(element, false);
|
||||
}
|
||||
push(element) {
|
||||
return this._insert(element, true);
|
||||
}
|
||||
_insert(element, atTheEnd) {
|
||||
const newNode = new Node(element);
|
||||
if (this._first === Node.Undefined) {
|
||||
this._first = newNode;
|
||||
this._last = newNode;
|
||||
}
|
||||
else if (atTheEnd) {
|
||||
// push
|
||||
const oldLast = this._last;
|
||||
this._last = newNode;
|
||||
newNode.prev = oldLast;
|
||||
oldLast.next = newNode;
|
||||
}
|
||||
else {
|
||||
// unshift
|
||||
const oldFirst = this._first;
|
||||
this._first = newNode;
|
||||
newNode.next = oldFirst;
|
||||
oldFirst.prev = newNode;
|
||||
}
|
||||
this._size += 1;
|
||||
let didRemove = false;
|
||||
return () => {
|
||||
if (!didRemove) {
|
||||
didRemove = true;
|
||||
this._remove(newNode);
|
||||
}
|
||||
};
|
||||
}
|
||||
shift() {
|
||||
if (this._first === Node.Undefined) {
|
||||
return undefined;
|
||||
}
|
||||
else {
|
||||
const res = this._first.element;
|
||||
this._remove(this._first);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
pop() {
|
||||
if (this._last === Node.Undefined) {
|
||||
return undefined;
|
||||
}
|
||||
else {
|
||||
const res = this._last.element;
|
||||
this._remove(this._last);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
_remove(node) {
|
||||
if (node.prev !== Node.Undefined && node.next !== Node.Undefined) {
|
||||
// middle
|
||||
const anchor = node.prev;
|
||||
anchor.next = node.next;
|
||||
node.next.prev = anchor;
|
||||
}
|
||||
else if (node.prev === Node.Undefined && node.next === Node.Undefined) {
|
||||
// only node
|
||||
this._first = Node.Undefined;
|
||||
this._last = Node.Undefined;
|
||||
}
|
||||
else if (node.next === Node.Undefined) {
|
||||
// last
|
||||
this._last = this._last.prev;
|
||||
this._last.next = Node.Undefined;
|
||||
}
|
||||
else if (node.prev === Node.Undefined) {
|
||||
// first
|
||||
this._first = this._first.next;
|
||||
this._first.prev = Node.Undefined;
|
||||
}
|
||||
// done
|
||||
this._size -= 1;
|
||||
}
|
||||
*[Symbol.iterator]() {
|
||||
let node = this._first;
|
||||
while (node !== Node.Undefined) {
|
||||
yield node.element;
|
||||
node = node.next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export { LinkedList };
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
import { memoize } from './decorators.js';
|
||||
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
var __decorate = (undefined && undefined.__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;
|
||||
};
|
||||
class LinkedText {
|
||||
constructor(nodes) {
|
||||
this.nodes = nodes;
|
||||
}
|
||||
toString() {
|
||||
return this.nodes.map(node => typeof node === 'string' ? node : node.label).join('');
|
||||
}
|
||||
}
|
||||
__decorate([
|
||||
memoize
|
||||
], LinkedText.prototype, "toString", null);
|
||||
const LINK_REGEX = /\[([^\]]+)\]\(((?:https?:\/\/|command:|file:)[^\)\s]+)(?: (["'])(.+?)(\3))?\)/gi;
|
||||
function parseLinkedText(text) {
|
||||
const result = [];
|
||||
let index = 0;
|
||||
let match;
|
||||
while (match = LINK_REGEX.exec(text)) {
|
||||
if (match.index - index > 0) {
|
||||
result.push(text.substring(index, match.index));
|
||||
}
|
||||
const [, label, href, , title] = match;
|
||||
if (title) {
|
||||
result.push({ label, href, title });
|
||||
}
|
||||
else {
|
||||
result.push({ label, href });
|
||||
}
|
||||
index = match.index + match[0].length;
|
||||
}
|
||||
if (index < text.length) {
|
||||
result.push(text.substring(index));
|
||||
}
|
||||
return new LinkedText(result);
|
||||
}
|
||||
|
||||
export { LinkedText, parseLinkedText };
|
||||
+670
@@ -0,0 +1,670 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
var _a, _b, _c;
|
||||
class ResourceMapEntry {
|
||||
constructor(uri, value) {
|
||||
this.uri = uri;
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
function isEntries(arg) {
|
||||
return Array.isArray(arg);
|
||||
}
|
||||
class ResourceMap {
|
||||
static { this.defaultToKey = (resource) => resource.toString(); }
|
||||
constructor(arg, toKey) {
|
||||
this[_a] = 'ResourceMap';
|
||||
if (arg instanceof ResourceMap) {
|
||||
this.map = new Map(arg.map);
|
||||
this.toKey = toKey ?? ResourceMap.defaultToKey;
|
||||
}
|
||||
else if (isEntries(arg)) {
|
||||
this.map = new Map();
|
||||
this.toKey = toKey ?? ResourceMap.defaultToKey;
|
||||
for (const [resource, value] of arg) {
|
||||
this.set(resource, value);
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.map = new Map();
|
||||
this.toKey = arg ?? ResourceMap.defaultToKey;
|
||||
}
|
||||
}
|
||||
set(resource, value) {
|
||||
this.map.set(this.toKey(resource), new ResourceMapEntry(resource, value));
|
||||
return this;
|
||||
}
|
||||
get(resource) {
|
||||
return this.map.get(this.toKey(resource))?.value;
|
||||
}
|
||||
has(resource) {
|
||||
return this.map.has(this.toKey(resource));
|
||||
}
|
||||
get size() {
|
||||
return this.map.size;
|
||||
}
|
||||
clear() {
|
||||
this.map.clear();
|
||||
}
|
||||
delete(resource) {
|
||||
return this.map.delete(this.toKey(resource));
|
||||
}
|
||||
forEach(clb, thisArg) {
|
||||
if (typeof thisArg !== 'undefined') {
|
||||
clb = clb.bind(thisArg);
|
||||
}
|
||||
for (const [_, entry] of this.map) {
|
||||
clb(entry.value, entry.uri, this);
|
||||
}
|
||||
}
|
||||
*values() {
|
||||
for (const entry of this.map.values()) {
|
||||
yield entry.value;
|
||||
}
|
||||
}
|
||||
*keys() {
|
||||
for (const entry of this.map.values()) {
|
||||
yield entry.uri;
|
||||
}
|
||||
}
|
||||
*entries() {
|
||||
for (const entry of this.map.values()) {
|
||||
yield [entry.uri, entry.value];
|
||||
}
|
||||
}
|
||||
*[(_a = Symbol.toStringTag, Symbol.iterator)]() {
|
||||
for (const [, entry] of this.map) {
|
||||
yield [entry.uri, entry.value];
|
||||
}
|
||||
}
|
||||
}
|
||||
class ResourceSet {
|
||||
constructor(entriesOrKey, toKey) {
|
||||
this[_b] = 'ResourceSet';
|
||||
if (!entriesOrKey || typeof entriesOrKey === 'function') {
|
||||
this._map = new ResourceMap(entriesOrKey);
|
||||
}
|
||||
else {
|
||||
this._map = new ResourceMap(toKey);
|
||||
entriesOrKey.forEach(this.add, this);
|
||||
}
|
||||
}
|
||||
get size() {
|
||||
return this._map.size;
|
||||
}
|
||||
add(value) {
|
||||
this._map.set(value, value);
|
||||
return this;
|
||||
}
|
||||
clear() {
|
||||
this._map.clear();
|
||||
}
|
||||
delete(value) {
|
||||
return this._map.delete(value);
|
||||
}
|
||||
forEach(callbackfn, thisArg) {
|
||||
this._map.forEach((_value, key) => callbackfn.call(thisArg, key, key, this));
|
||||
}
|
||||
has(value) {
|
||||
return this._map.has(value);
|
||||
}
|
||||
entries() {
|
||||
return this._map.entries();
|
||||
}
|
||||
keys() {
|
||||
return this._map.keys();
|
||||
}
|
||||
values() {
|
||||
return this._map.keys();
|
||||
}
|
||||
[(_b = Symbol.toStringTag, Symbol.iterator)]() {
|
||||
return this.keys();
|
||||
}
|
||||
}
|
||||
class LinkedMap {
|
||||
constructor() {
|
||||
this[_c] = 'LinkedMap';
|
||||
this._map = new Map();
|
||||
this._head = undefined;
|
||||
this._tail = undefined;
|
||||
this._size = 0;
|
||||
this._state = 0;
|
||||
}
|
||||
clear() {
|
||||
this._map.clear();
|
||||
this._head = undefined;
|
||||
this._tail = undefined;
|
||||
this._size = 0;
|
||||
this._state++;
|
||||
}
|
||||
isEmpty() {
|
||||
return !this._head && !this._tail;
|
||||
}
|
||||
get size() {
|
||||
return this._size;
|
||||
}
|
||||
get first() {
|
||||
return this._head?.value;
|
||||
}
|
||||
get last() {
|
||||
return this._tail?.value;
|
||||
}
|
||||
has(key) {
|
||||
return this._map.has(key);
|
||||
}
|
||||
get(key, touch = 0 /* Touch.None */) {
|
||||
const item = this._map.get(key);
|
||||
if (!item) {
|
||||
return undefined;
|
||||
}
|
||||
if (touch !== 0 /* Touch.None */) {
|
||||
this.touch(item, touch);
|
||||
}
|
||||
return item.value;
|
||||
}
|
||||
set(key, value, touch = 0 /* Touch.None */) {
|
||||
let item = this._map.get(key);
|
||||
if (item) {
|
||||
item.value = value;
|
||||
if (touch !== 0 /* Touch.None */) {
|
||||
this.touch(item, touch);
|
||||
}
|
||||
}
|
||||
else {
|
||||
item = { key, value, next: undefined, previous: undefined };
|
||||
switch (touch) {
|
||||
case 0 /* Touch.None */:
|
||||
this.addItemLast(item);
|
||||
break;
|
||||
case 1 /* Touch.AsOld */:
|
||||
this.addItemFirst(item);
|
||||
break;
|
||||
case 2 /* Touch.AsNew */:
|
||||
this.addItemLast(item);
|
||||
break;
|
||||
default:
|
||||
this.addItemLast(item);
|
||||
break;
|
||||
}
|
||||
this._map.set(key, item);
|
||||
this._size++;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
delete(key) {
|
||||
return !!this.remove(key);
|
||||
}
|
||||
remove(key) {
|
||||
const item = this._map.get(key);
|
||||
if (!item) {
|
||||
return undefined;
|
||||
}
|
||||
this._map.delete(key);
|
||||
this.removeItem(item);
|
||||
this._size--;
|
||||
return item.value;
|
||||
}
|
||||
shift() {
|
||||
if (!this._head && !this._tail) {
|
||||
return undefined;
|
||||
}
|
||||
if (!this._head || !this._tail) {
|
||||
throw new Error('Invalid list');
|
||||
}
|
||||
const item = this._head;
|
||||
this._map.delete(item.key);
|
||||
this.removeItem(item);
|
||||
this._size--;
|
||||
return item.value;
|
||||
}
|
||||
forEach(callbackfn, thisArg) {
|
||||
const state = this._state;
|
||||
let current = this._head;
|
||||
while (current) {
|
||||
if (thisArg) {
|
||||
callbackfn.bind(thisArg)(current.value, current.key, this);
|
||||
}
|
||||
else {
|
||||
callbackfn(current.value, current.key, this);
|
||||
}
|
||||
if (this._state !== state) {
|
||||
throw new Error(`LinkedMap got modified during iteration.`);
|
||||
}
|
||||
current = current.next;
|
||||
}
|
||||
}
|
||||
keys() {
|
||||
const map = this;
|
||||
const state = this._state;
|
||||
let current = this._head;
|
||||
const iterator = {
|
||||
[Symbol.iterator]() {
|
||||
return iterator;
|
||||
},
|
||||
next() {
|
||||
if (map._state !== state) {
|
||||
throw new Error(`LinkedMap got modified during iteration.`);
|
||||
}
|
||||
if (current) {
|
||||
const result = { value: current.key, done: false };
|
||||
current = current.next;
|
||||
return result;
|
||||
}
|
||||
else {
|
||||
return { value: undefined, done: true };
|
||||
}
|
||||
}
|
||||
};
|
||||
return iterator;
|
||||
}
|
||||
values() {
|
||||
const map = this;
|
||||
const state = this._state;
|
||||
let current = this._head;
|
||||
const iterator = {
|
||||
[Symbol.iterator]() {
|
||||
return iterator;
|
||||
},
|
||||
next() {
|
||||
if (map._state !== state) {
|
||||
throw new Error(`LinkedMap got modified during iteration.`);
|
||||
}
|
||||
if (current) {
|
||||
const result = { value: current.value, done: false };
|
||||
current = current.next;
|
||||
return result;
|
||||
}
|
||||
else {
|
||||
return { value: undefined, done: true };
|
||||
}
|
||||
}
|
||||
};
|
||||
return iterator;
|
||||
}
|
||||
entries() {
|
||||
const map = this;
|
||||
const state = this._state;
|
||||
let current = this._head;
|
||||
const iterator = {
|
||||
[Symbol.iterator]() {
|
||||
return iterator;
|
||||
},
|
||||
next() {
|
||||
if (map._state !== state) {
|
||||
throw new Error(`LinkedMap got modified during iteration.`);
|
||||
}
|
||||
if (current) {
|
||||
const result = { value: [current.key, current.value], done: false };
|
||||
current = current.next;
|
||||
return result;
|
||||
}
|
||||
else {
|
||||
return { value: undefined, done: true };
|
||||
}
|
||||
}
|
||||
};
|
||||
return iterator;
|
||||
}
|
||||
[(_c = Symbol.toStringTag, Symbol.iterator)]() {
|
||||
return this.entries();
|
||||
}
|
||||
trimOld(newSize) {
|
||||
if (newSize >= this.size) {
|
||||
return;
|
||||
}
|
||||
if (newSize === 0) {
|
||||
this.clear();
|
||||
return;
|
||||
}
|
||||
let current = this._head;
|
||||
let currentSize = this.size;
|
||||
while (current && currentSize > newSize) {
|
||||
this._map.delete(current.key);
|
||||
current = current.next;
|
||||
currentSize--;
|
||||
}
|
||||
this._head = current;
|
||||
this._size = currentSize;
|
||||
if (current) {
|
||||
current.previous = undefined;
|
||||
}
|
||||
this._state++;
|
||||
}
|
||||
trimNew(newSize) {
|
||||
if (newSize >= this.size) {
|
||||
return;
|
||||
}
|
||||
if (newSize === 0) {
|
||||
this.clear();
|
||||
return;
|
||||
}
|
||||
let current = this._tail;
|
||||
let currentSize = this.size;
|
||||
while (current && currentSize > newSize) {
|
||||
this._map.delete(current.key);
|
||||
current = current.previous;
|
||||
currentSize--;
|
||||
}
|
||||
this._tail = current;
|
||||
this._size = currentSize;
|
||||
if (current) {
|
||||
current.next = undefined;
|
||||
}
|
||||
this._state++;
|
||||
}
|
||||
addItemFirst(item) {
|
||||
// First time Insert
|
||||
if (!this._head && !this._tail) {
|
||||
this._tail = item;
|
||||
}
|
||||
else if (!this._head) {
|
||||
throw new Error('Invalid list');
|
||||
}
|
||||
else {
|
||||
item.next = this._head;
|
||||
this._head.previous = item;
|
||||
}
|
||||
this._head = item;
|
||||
this._state++;
|
||||
}
|
||||
addItemLast(item) {
|
||||
// First time Insert
|
||||
if (!this._head && !this._tail) {
|
||||
this._head = item;
|
||||
}
|
||||
else if (!this._tail) {
|
||||
throw new Error('Invalid list');
|
||||
}
|
||||
else {
|
||||
item.previous = this._tail;
|
||||
this._tail.next = item;
|
||||
}
|
||||
this._tail = item;
|
||||
this._state++;
|
||||
}
|
||||
removeItem(item) {
|
||||
if (item === this._head && item === this._tail) {
|
||||
this._head = undefined;
|
||||
this._tail = undefined;
|
||||
}
|
||||
else if (item === this._head) {
|
||||
// This can only happen if size === 1 which is handled
|
||||
// by the case above.
|
||||
if (!item.next) {
|
||||
throw new Error('Invalid list');
|
||||
}
|
||||
item.next.previous = undefined;
|
||||
this._head = item.next;
|
||||
}
|
||||
else if (item === this._tail) {
|
||||
// This can only happen if size === 1 which is handled
|
||||
// by the case above.
|
||||
if (!item.previous) {
|
||||
throw new Error('Invalid list');
|
||||
}
|
||||
item.previous.next = undefined;
|
||||
this._tail = item.previous;
|
||||
}
|
||||
else {
|
||||
const next = item.next;
|
||||
const previous = item.previous;
|
||||
if (!next || !previous) {
|
||||
throw new Error('Invalid list');
|
||||
}
|
||||
next.previous = previous;
|
||||
previous.next = next;
|
||||
}
|
||||
item.next = undefined;
|
||||
item.previous = undefined;
|
||||
this._state++;
|
||||
}
|
||||
touch(item, touch) {
|
||||
if (!this._head || !this._tail) {
|
||||
throw new Error('Invalid list');
|
||||
}
|
||||
if ((touch !== 1 /* Touch.AsOld */ && touch !== 2 /* Touch.AsNew */)) {
|
||||
return;
|
||||
}
|
||||
if (touch === 1 /* Touch.AsOld */) {
|
||||
if (item === this._head) {
|
||||
return;
|
||||
}
|
||||
const next = item.next;
|
||||
const previous = item.previous;
|
||||
// Unlink the item
|
||||
if (item === this._tail) {
|
||||
// previous must be defined since item was not head but is tail
|
||||
// So there are more than on item in the map
|
||||
previous.next = undefined;
|
||||
this._tail = previous;
|
||||
}
|
||||
else {
|
||||
// Both next and previous are not undefined since item was neither head nor tail.
|
||||
next.previous = previous;
|
||||
previous.next = next;
|
||||
}
|
||||
// Insert the node at head
|
||||
item.previous = undefined;
|
||||
item.next = this._head;
|
||||
this._head.previous = item;
|
||||
this._head = item;
|
||||
this._state++;
|
||||
}
|
||||
else if (touch === 2 /* Touch.AsNew */) {
|
||||
if (item === this._tail) {
|
||||
return;
|
||||
}
|
||||
const next = item.next;
|
||||
const previous = item.previous;
|
||||
// Unlink the item.
|
||||
if (item === this._head) {
|
||||
// next must be defined since item was not tail but is head
|
||||
// So there are more than on item in the map
|
||||
next.previous = undefined;
|
||||
this._head = next;
|
||||
}
|
||||
else {
|
||||
// Both next and previous are not undefined since item was neither head nor tail.
|
||||
next.previous = previous;
|
||||
previous.next = next;
|
||||
}
|
||||
item.next = undefined;
|
||||
item.previous = this._tail;
|
||||
this._tail.next = item;
|
||||
this._tail = item;
|
||||
this._state++;
|
||||
}
|
||||
}
|
||||
toJSON() {
|
||||
const data = [];
|
||||
this.forEach((value, key) => {
|
||||
data.push([key, value]);
|
||||
});
|
||||
return data;
|
||||
}
|
||||
fromJSON(data) {
|
||||
this.clear();
|
||||
for (const [key, value] of data) {
|
||||
this.set(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
class Cache extends LinkedMap {
|
||||
constructor(limit, ratio = 1) {
|
||||
super();
|
||||
this._limit = limit;
|
||||
this._ratio = Math.min(Math.max(0, ratio), 1);
|
||||
}
|
||||
get limit() {
|
||||
return this._limit;
|
||||
}
|
||||
set limit(limit) {
|
||||
this._limit = limit;
|
||||
this.checkTrim();
|
||||
}
|
||||
get(key, touch = 2 /* Touch.AsNew */) {
|
||||
return super.get(key, touch);
|
||||
}
|
||||
peek(key) {
|
||||
return super.get(key, 0 /* Touch.None */);
|
||||
}
|
||||
set(key, value) {
|
||||
super.set(key, value, 2 /* Touch.AsNew */);
|
||||
return this;
|
||||
}
|
||||
checkTrim() {
|
||||
if (this.size > this._limit) {
|
||||
this.trim(Math.round(this._limit * this._ratio));
|
||||
}
|
||||
}
|
||||
}
|
||||
class LRUCache extends Cache {
|
||||
constructor(limit, ratio = 1) {
|
||||
super(limit, ratio);
|
||||
}
|
||||
trim(newSize) {
|
||||
this.trimOld(newSize);
|
||||
}
|
||||
set(key, value) {
|
||||
super.set(key, value);
|
||||
this.checkTrim();
|
||||
return this;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* A map that allows access both by keys and values.
|
||||
* **NOTE**: values need to be unique.
|
||||
*/
|
||||
class BidirectionalMap {
|
||||
constructor(entries) {
|
||||
this._m1 = new Map();
|
||||
this._m2 = new Map();
|
||||
if (entries) {
|
||||
for (const [key, value] of entries) {
|
||||
this.set(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
clear() {
|
||||
this._m1.clear();
|
||||
this._m2.clear();
|
||||
}
|
||||
set(key, value) {
|
||||
this._m1.set(key, value);
|
||||
this._m2.set(value, key);
|
||||
}
|
||||
get(key) {
|
||||
return this._m1.get(key);
|
||||
}
|
||||
getKey(value) {
|
||||
return this._m2.get(value);
|
||||
}
|
||||
delete(key) {
|
||||
const value = this._m1.get(key);
|
||||
if (value === undefined) {
|
||||
return false;
|
||||
}
|
||||
this._m1.delete(key);
|
||||
this._m2.delete(value);
|
||||
return true;
|
||||
}
|
||||
keys() {
|
||||
return this._m1.keys();
|
||||
}
|
||||
values() {
|
||||
return this._m1.values();
|
||||
}
|
||||
}
|
||||
class SetMap {
|
||||
constructor() {
|
||||
this.map = new Map();
|
||||
}
|
||||
add(key, value) {
|
||||
let values = this.map.get(key);
|
||||
if (!values) {
|
||||
values = new Set();
|
||||
this.map.set(key, values);
|
||||
}
|
||||
values.add(value);
|
||||
}
|
||||
delete(key, value) {
|
||||
const values = this.map.get(key);
|
||||
if (!values) {
|
||||
return;
|
||||
}
|
||||
values.delete(value);
|
||||
if (values.size === 0) {
|
||||
this.map.delete(key);
|
||||
}
|
||||
}
|
||||
forEach(key, fn) {
|
||||
const values = this.map.get(key);
|
||||
if (!values) {
|
||||
return;
|
||||
}
|
||||
values.forEach(fn);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* A map that is addressable with an arbitrary number of keys. This is useful in high performance
|
||||
* scenarios where creating a composite key whenever the data is accessed is too expensive. For
|
||||
* example for a very hot function, constructing a string like `first-second-third` for every call
|
||||
* will cause a significant hit to performance.
|
||||
*/
|
||||
class NKeyMap {
|
||||
constructor() {
|
||||
this._data = new Map();
|
||||
}
|
||||
/**
|
||||
* Sets a value on the map. Note that unlike a standard `Map`, the first argument is the value.
|
||||
* This is because the spread operator is used for the keys and must be last..
|
||||
* @param value The value to set.
|
||||
* @param keys The keys for the value.
|
||||
*/
|
||||
set(value, ...keys) {
|
||||
let currentMap = this._data;
|
||||
for (let i = 0; i < keys.length - 1; i++) {
|
||||
if (!currentMap.has(keys[i])) {
|
||||
currentMap.set(keys[i], new Map());
|
||||
}
|
||||
currentMap = currentMap.get(keys[i]);
|
||||
}
|
||||
currentMap.set(keys[keys.length - 1], value);
|
||||
}
|
||||
get(...keys) {
|
||||
let currentMap = this._data;
|
||||
for (let i = 0; i < keys.length - 1; i++) {
|
||||
if (!currentMap.has(keys[i])) {
|
||||
return undefined;
|
||||
}
|
||||
currentMap = currentMap.get(keys[i]);
|
||||
}
|
||||
return currentMap.get(keys[keys.length - 1]);
|
||||
}
|
||||
clear() {
|
||||
this._data.clear();
|
||||
}
|
||||
/**
|
||||
* Get a textual representation of the map for debugging purposes.
|
||||
*/
|
||||
toString() {
|
||||
const printMap = (map, depth) => {
|
||||
let result = '';
|
||||
for (const [key, value] of map) {
|
||||
result += `${' '.repeat(depth)}${key}: `;
|
||||
if (value instanceof Map) {
|
||||
result += '\n' + printMap(value, depth + 1);
|
||||
}
|
||||
else {
|
||||
result += `${value}\n`;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
return printMap(this._data, 0);
|
||||
}
|
||||
}
|
||||
|
||||
export { BidirectionalMap, LRUCache, LinkedMap, NKeyMap, ResourceMap, ResourceSet, SetMap };
|
||||
+2478
File diff suppressed because it is too large
Load Diff
+62
@@ -0,0 +1,62 @@
|
||||
import { VSBuffer } from './buffer.js';
|
||||
import { URI } from './uri.js';
|
||||
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
function stringify(obj) {
|
||||
return JSON.stringify(obj, replacer);
|
||||
}
|
||||
function parse(text) {
|
||||
let data = JSON.parse(text);
|
||||
data = revive(data);
|
||||
return data;
|
||||
}
|
||||
function replacer(key, value) {
|
||||
// URI is done via toJSON-member
|
||||
if (value instanceof RegExp) {
|
||||
return {
|
||||
$mid: 2 /* MarshalledId.Regexp */,
|
||||
source: value.source,
|
||||
flags: value.flags,
|
||||
};
|
||||
}
|
||||
return value;
|
||||
}
|
||||
function revive(obj, depth = 0) {
|
||||
if (!obj || depth > 200) {
|
||||
return obj;
|
||||
}
|
||||
if (typeof obj === 'object') {
|
||||
switch (obj.$mid) {
|
||||
// eslint-disable-next-line local/code-no-any-casts
|
||||
case 1 /* MarshalledId.Uri */: return URI.revive(obj);
|
||||
// eslint-disable-next-line local/code-no-any-casts
|
||||
case 2 /* MarshalledId.Regexp */: return new RegExp(obj.source, obj.flags);
|
||||
// eslint-disable-next-line local/code-no-any-casts
|
||||
case 17 /* MarshalledId.Date */: return new Date(obj.source);
|
||||
}
|
||||
if (obj instanceof VSBuffer
|
||||
|| obj instanceof Uint8Array) {
|
||||
// eslint-disable-next-line local/code-no-any-casts
|
||||
return obj;
|
||||
}
|
||||
if (Array.isArray(obj)) {
|
||||
for (let i = 0; i < obj.length; ++i) {
|
||||
obj[i] = revive(obj[i], depth + 1);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// walk object
|
||||
for (const key in obj) {
|
||||
if (Object.hasOwnProperty.call(obj, key)) {
|
||||
obj[key] = revive(obj[key], depth + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
export { parse, revive, stringify };
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
const Mimes = Object.freeze({
|
||||
text: 'text/plain',
|
||||
binary: 'application/octet-stream',
|
||||
unknown: 'application/unknown',
|
||||
markdown: 'text/markdown',
|
||||
latex: 'text/latex',
|
||||
uriList: 'text/uri-list',
|
||||
html: 'text/html',
|
||||
});
|
||||
|
||||
export { Mimes };
|
||||
+321
@@ -0,0 +1,321 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
// allow-any-unicode-comment-file
|
||||
/**
|
||||
* Gets alternative Korean characters for the character code. This will return the ascii
|
||||
* character code(s) that a Hangul character may have been input with using a qwerty layout.
|
||||
*
|
||||
* This only aims to cover modern (not archaic) Hangul syllables.
|
||||
*
|
||||
* @param code The character code to get alternate characters for
|
||||
*/
|
||||
function getKoreanAltChars(code) {
|
||||
const result = disassembleKorean(code);
|
||||
if (result && result.length > 0) {
|
||||
return new Uint32Array(result);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
let codeBufferLength = 0;
|
||||
const codeBuffer = new Uint32Array(10);
|
||||
function disassembleKorean(code) {
|
||||
codeBufferLength = 0;
|
||||
// Initial consonants (초성)
|
||||
getCodesFromArray(code, modernConsonants, 4352 /* HangulRangeStartCode.InitialConsonant */);
|
||||
if (codeBufferLength > 0) {
|
||||
return codeBuffer.subarray(0, codeBufferLength);
|
||||
}
|
||||
// Vowels (중성)
|
||||
getCodesFromArray(code, modernVowels, 4449 /* HangulRangeStartCode.Vowel */);
|
||||
if (codeBufferLength > 0) {
|
||||
return codeBuffer.subarray(0, codeBufferLength);
|
||||
}
|
||||
// Final consonants (종성)
|
||||
getCodesFromArray(code, modernFinalConsonants, 4520 /* HangulRangeStartCode.FinalConsonant */);
|
||||
if (codeBufferLength > 0) {
|
||||
return codeBuffer.subarray(0, codeBufferLength);
|
||||
}
|
||||
// Hangul Compatibility Jamo
|
||||
getCodesFromArray(code, compatibilityJamo, 12593 /* HangulRangeStartCode.CompatibilityJamo */);
|
||||
if (codeBufferLength) {
|
||||
return codeBuffer.subarray(0, codeBufferLength);
|
||||
}
|
||||
// Hangul Syllables
|
||||
if (code >= 0xAC00 && code <= 0xD7A3) {
|
||||
const hangulIndex = code - 0xAC00;
|
||||
const vowelAndFinalConsonantProduct = hangulIndex % 588;
|
||||
// 0-based starting at 0x1100
|
||||
const initialConsonantIndex = Math.floor(hangulIndex / 588);
|
||||
// 0-based starting at 0x1161
|
||||
const vowelIndex = Math.floor(vowelAndFinalConsonantProduct / 28);
|
||||
// 0-based starting at 0x11A8
|
||||
// Subtract 1 as the standard algorithm uses the 0 index to represent no
|
||||
// final consonant
|
||||
const finalConsonantIndex = vowelAndFinalConsonantProduct % 28 - 1;
|
||||
if (initialConsonantIndex < modernConsonants.length) {
|
||||
getCodesFromArray(initialConsonantIndex, modernConsonants, 0);
|
||||
}
|
||||
else if (4352 /* HangulRangeStartCode.InitialConsonant */ + initialConsonantIndex - 12593 /* HangulRangeStartCode.CompatibilityJamo */ < compatibilityJamo.length) {
|
||||
getCodesFromArray(4352 /* HangulRangeStartCode.InitialConsonant */ + initialConsonantIndex, compatibilityJamo, 12593 /* HangulRangeStartCode.CompatibilityJamo */);
|
||||
}
|
||||
if (vowelIndex < modernVowels.length) {
|
||||
getCodesFromArray(vowelIndex, modernVowels, 0);
|
||||
}
|
||||
else if (4449 /* HangulRangeStartCode.Vowel */ + vowelIndex - 12593 /* HangulRangeStartCode.CompatibilityJamo */ < compatibilityJamo.length) {
|
||||
getCodesFromArray(4449 /* HangulRangeStartCode.Vowel */ + vowelIndex - 12593 /* HangulRangeStartCode.CompatibilityJamo */, compatibilityJamo, 12593 /* HangulRangeStartCode.CompatibilityJamo */);
|
||||
}
|
||||
if (finalConsonantIndex >= 0) {
|
||||
if (finalConsonantIndex < modernFinalConsonants.length) {
|
||||
getCodesFromArray(finalConsonantIndex, modernFinalConsonants, 0);
|
||||
}
|
||||
else if (4520 /* HangulRangeStartCode.FinalConsonant */ + finalConsonantIndex - 12593 /* HangulRangeStartCode.CompatibilityJamo */ < compatibilityJamo.length) {
|
||||
getCodesFromArray(4520 /* HangulRangeStartCode.FinalConsonant */ + finalConsonantIndex - 12593 /* HangulRangeStartCode.CompatibilityJamo */, compatibilityJamo, 12593 /* HangulRangeStartCode.CompatibilityJamo */);
|
||||
}
|
||||
}
|
||||
if (codeBufferLength > 0) {
|
||||
return codeBuffer.subarray(0, codeBufferLength);
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
function getCodesFromArray(code, array, arrayStartIndex) {
|
||||
// Verify the code is within the array's range
|
||||
if (code >= arrayStartIndex && code < arrayStartIndex + array.length) {
|
||||
addCodesToBuffer(array[code - arrayStartIndex]);
|
||||
}
|
||||
}
|
||||
function addCodesToBuffer(codes) {
|
||||
// NUL is ignored, this is used for archaic characters to avoid using a Map
|
||||
// for the data
|
||||
if (codes === 0 /* AsciiCode.NUL */) {
|
||||
return;
|
||||
}
|
||||
// Number stored in format: OptionalThirdCode << 16 | OptionalSecondCode << 8 | Code
|
||||
codeBuffer[codeBufferLength++] = codes & 0xFF;
|
||||
if (codes >> 8) {
|
||||
codeBuffer[codeBufferLength++] = (codes >> 8) & 0xFF;
|
||||
}
|
||||
if (codes >> 16) {
|
||||
codeBuffer[codeBufferLength++] = (codes >> 16) & 0xFF;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Hangul Jamo - Modern consonants #1
|
||||
*
|
||||
* Range U+1100..U+1112
|
||||
*
|
||||
* | | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F |
|
||||
* |--------|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
||||
* | U+110x | ᄀ | ᄁ | ᄂ | ᄃ | ᄄ | ᄅ | ᄆ | ᄇ | ᄈ | ᄉ | ᄊ | ᄋ | ᄌ | ᄍ | ᄎ | ᄏ |
|
||||
* | U+111x | ᄐ | ᄑ | ᄒ |
|
||||
*/
|
||||
const modernConsonants = new Uint8Array([
|
||||
114 /* AsciiCode.r */, // ㄱ
|
||||
82 /* AsciiCode.R */, // ㄲ
|
||||
115 /* AsciiCode.s */, // ㄴ
|
||||
101 /* AsciiCode.e */, // ㄷ
|
||||
69 /* AsciiCode.E */, // ㄸ
|
||||
102 /* AsciiCode.f */, // ㄹ
|
||||
97 /* AsciiCode.a */, // ㅁ
|
||||
113 /* AsciiCode.q */, // ㅂ
|
||||
81 /* AsciiCode.Q */, // ㅃ
|
||||
116 /* AsciiCode.t */, // ㅅ
|
||||
84 /* AsciiCode.T */, // ㅆ
|
||||
100 /* AsciiCode.d */, // ㅇ
|
||||
119 /* AsciiCode.w */, // ㅈ
|
||||
87 /* AsciiCode.W */, // ㅉ
|
||||
99 /* AsciiCode.c */, // ㅊ
|
||||
122 /* AsciiCode.z */, // ㅋ
|
||||
120 /* AsciiCode.x */, // ㅌ
|
||||
118 /* AsciiCode.v */, // ㅍ
|
||||
103 /* AsciiCode.g */, // ㅎ
|
||||
]);
|
||||
/**
|
||||
* Hangul Jamo - Modern Vowels
|
||||
*
|
||||
* Range U+1161..U+1175
|
||||
*
|
||||
* | | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F |
|
||||
* |--------|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
||||
* | U+116x | | ᅡ | ᅢ | ᅣ | ᅤ | ᅥ | ᅦ | ᅧ | ᅨ | ᅩ | ᅪ | ᅫ | ᅬ | ᅭ | ᅮ | ᅯ |
|
||||
* | U+117x | ᅰ | ᅱ | ᅲ | ᅳ | ᅴ | ᅵ |
|
||||
*/
|
||||
const modernVowels = new Uint16Array([
|
||||
107 /* AsciiCode.k */, // -> ㅏ
|
||||
111 /* AsciiCode.o */, // -> ㅐ
|
||||
105 /* AsciiCode.i */, // -> ㅑ
|
||||
79 /* AsciiCode.O */, // -> ㅒ
|
||||
106 /* AsciiCode.j */, // -> ㅓ
|
||||
112 /* AsciiCode.p */, // -> ㅔ
|
||||
117 /* AsciiCode.u */, // -> ㅕ
|
||||
80 /* AsciiCode.P */, // -> ㅖ
|
||||
104 /* AsciiCode.h */, // -> ㅗ
|
||||
27496 /* AsciiCodeCombo.hk */, // -> ㅘ
|
||||
28520 /* AsciiCodeCombo.ho */, // -> ㅙ
|
||||
27752 /* AsciiCodeCombo.hl */, // -> ㅚ
|
||||
121 /* AsciiCode.y */, // -> ㅛ
|
||||
110 /* AsciiCode.n */, // -> ㅜ
|
||||
27246 /* AsciiCodeCombo.nj */, // -> ㅝ
|
||||
28782 /* AsciiCodeCombo.np */, // -> ㅞ
|
||||
27758 /* AsciiCodeCombo.nl */, // -> ㅟ
|
||||
98 /* AsciiCode.b */, // -> ㅠ
|
||||
109 /* AsciiCode.m */, // -> ㅡ
|
||||
27757 /* AsciiCodeCombo.ml */, // -> ㅢ
|
||||
108 /* AsciiCode.l */, // -> ㅣ
|
||||
]);
|
||||
/**
|
||||
* Hangul Jamo - Modern Consonants #2
|
||||
*
|
||||
* Range U+11A8..U+11C2
|
||||
*
|
||||
* | | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F |
|
||||
* |--------|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
||||
* | U+11Ax | | | | | | | | | ᆨ | ᆩ | ᆪ | ᆫ | ᆬ | ᆭ | ᆮ | ᆯ |
|
||||
* | U+11Bx | ᆰ | ᆱ | ᆲ | ᆳ | ᆴ | ᆵ | ᆶ | ᆷ | ᆸ | ᆹ | ᆺ | ᆻ | ᆼ | ᆽ | ᆾ | ᆿ |
|
||||
* | U+11Cx | ᇀ | ᇁ | ᇂ |
|
||||
*/
|
||||
const modernFinalConsonants = new Uint16Array([
|
||||
114 /* AsciiCode.r */, // ㄱ
|
||||
82 /* AsciiCode.R */, // ㄲ
|
||||
29810 /* AsciiCodeCombo.rt */, // ㄳ
|
||||
115 /* AsciiCode.s */, // ㄴ
|
||||
30579 /* AsciiCodeCombo.sw */, // ㄵ
|
||||
26483 /* AsciiCodeCombo.sg */, // ㄶ
|
||||
101 /* AsciiCode.e */, // ㄷ
|
||||
102 /* AsciiCode.f */, // ㄹ
|
||||
29286 /* AsciiCodeCombo.fr */, // ㄺ
|
||||
24934 /* AsciiCodeCombo.fa */, // ㄻ
|
||||
29030 /* AsciiCodeCombo.fq */, // ㄼ
|
||||
29798 /* AsciiCodeCombo.ft */, // ㄽ
|
||||
30822 /* AsciiCodeCombo.fx */, // ㄾ
|
||||
30310 /* AsciiCodeCombo.fv */, // ㄿ
|
||||
26470 /* AsciiCodeCombo.fg */, // ㅀ
|
||||
97 /* AsciiCode.a */, // ㅁ
|
||||
113 /* AsciiCode.q */, // ㅂ
|
||||
29809 /* AsciiCodeCombo.qt */, // ㅄ
|
||||
116 /* AsciiCode.t */, // ㅅ
|
||||
84 /* AsciiCode.T */, // ㅆ
|
||||
100 /* AsciiCode.d */, // ㅇ
|
||||
119 /* AsciiCode.w */, // ㅈ
|
||||
99 /* AsciiCode.c */, // ㅊ
|
||||
122 /* AsciiCode.z */, // ㅋ
|
||||
120 /* AsciiCode.x */, // ㅌ
|
||||
118 /* AsciiCode.v */, // ㅍ
|
||||
103 /* AsciiCode.g */, // ㅎ
|
||||
]);
|
||||
/**
|
||||
* Hangul Compatibility Jamo
|
||||
*
|
||||
* Range U+3131..U+318F
|
||||
*
|
||||
* This includes range includes archaic jamo which we don't consider, these are
|
||||
* given the NUL character code in order to be ignored.
|
||||
*
|
||||
* | | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F |
|
||||
* |--------|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
||||
* | U+313x | | ㄱ | ㄲ | ㄳ | ㄴ | ㄵ | ㄶ | ㄷ | ㄸ | ㄹ | ㄺ | ㄻ | ㄼ | ㄽ | ㄾ | ㄿ |
|
||||
* | U+314x | ㅀ | ㅁ | ㅂ | ㅃ | ㅄ | ㅅ | ㅆ | ㅇ | ㅈ | ㅉ | ㅊ | ㅋ | ㅌ | ㅍ | ㅎ | ㅏ |
|
||||
* | U+315x | ㅐ | ㅑ | ㅒ | ㅓ | ㅔ | ㅕ | ㅖ | ㅗ | ㅘ | ㅙ | ㅚ | ㅛ | ㅜ | ㅝ | ㅞ | ㅟ |
|
||||
* | U+316x | ㅠ | ㅡ | ㅢ | ㅣ | HF | ㅥ | ㅦ | ㅧ | ㅨ | ㅩ | ㅪ | ㅫ | ㅬ | ㅭ | ㅮ | ㅯ |
|
||||
* | U+317x | ㅰ | ㅱ | ㅲ | ㅳ | ㅴ | ㅵ | ㅶ | ㅷ | ㅸ | ㅹ | ㅺ | ㅻ | ㅼ | ㅽ | ㅾ | ㅿ |
|
||||
* | U+318x | ㆀ | ㆁ | ㆂ | ㆃ | ㆄ | ㆅ | ㆆ | ㆇ | ㆈ | ㆉ | ㆊ | ㆋ | ㆌ | ㆍ | ㆎ |
|
||||
*/
|
||||
const compatibilityJamo = new Uint16Array([
|
||||
114 /* AsciiCode.r */, // ㄱ
|
||||
82 /* AsciiCode.R */, // ㄲ
|
||||
29810 /* AsciiCodeCombo.rt */, // ㄳ
|
||||
115 /* AsciiCode.s */, // ㄴ
|
||||
30579 /* AsciiCodeCombo.sw */, // ㄵ
|
||||
26483 /* AsciiCodeCombo.sg */, // ㄶ
|
||||
101 /* AsciiCode.e */, // ㄷ
|
||||
69 /* AsciiCode.E */, // ㄸ
|
||||
102 /* AsciiCode.f */, // ㄹ
|
||||
29286 /* AsciiCodeCombo.fr */, // ㄺ
|
||||
24934 /* AsciiCodeCombo.fa */, // ㄻ
|
||||
29030 /* AsciiCodeCombo.fq */, // ㄼ
|
||||
29798 /* AsciiCodeCombo.ft */, // ㄽ
|
||||
30822 /* AsciiCodeCombo.fx */, // ㄾ
|
||||
30310 /* AsciiCodeCombo.fv */, // ㄿ
|
||||
26470 /* AsciiCodeCombo.fg */, // ㅀ
|
||||
97 /* AsciiCode.a */, // ㅁ
|
||||
113 /* AsciiCode.q */, // ㅂ
|
||||
81 /* AsciiCode.Q */, // ㅃ
|
||||
29809 /* AsciiCodeCombo.qt */, // ㅄ
|
||||
116 /* AsciiCode.t */, // ㅅ
|
||||
84 /* AsciiCode.T */, // ㅆ
|
||||
100 /* AsciiCode.d */, // ㅇ
|
||||
119 /* AsciiCode.w */, // ㅈ
|
||||
87 /* AsciiCode.W */, // ㅉ
|
||||
99 /* AsciiCode.c */, // ㅊ
|
||||
122 /* AsciiCode.z */, // ㅋ
|
||||
120 /* AsciiCode.x */, // ㅌ
|
||||
118 /* AsciiCode.v */, // ㅍ
|
||||
103 /* AsciiCode.g */, // ㅎ
|
||||
107 /* AsciiCode.k */, // ㅏ
|
||||
111 /* AsciiCode.o */, // ㅐ
|
||||
105 /* AsciiCode.i */, // ㅑ
|
||||
79 /* AsciiCode.O */, // ㅒ
|
||||
106 /* AsciiCode.j */, // ㅓ
|
||||
112 /* AsciiCode.p */, // ㅔ
|
||||
117 /* AsciiCode.u */, // ㅕ
|
||||
80 /* AsciiCode.P */, // ㅖ
|
||||
104 /* AsciiCode.h */, // ㅗ
|
||||
27496 /* AsciiCodeCombo.hk */, // ㅘ
|
||||
28520 /* AsciiCodeCombo.ho */, // ㅙ
|
||||
27752 /* AsciiCodeCombo.hl */, // ㅚ
|
||||
121 /* AsciiCode.y */, // ㅛ
|
||||
110 /* AsciiCode.n */, // ㅜ
|
||||
27246 /* AsciiCodeCombo.nj */, // ㅝ
|
||||
28782 /* AsciiCodeCombo.np */, // ㅞ
|
||||
27758 /* AsciiCodeCombo.nl */, // ㅟ
|
||||
98 /* AsciiCode.b */, // ㅠ
|
||||
109 /* AsciiCode.m */, // ㅡ
|
||||
27757 /* AsciiCodeCombo.ml */, // ㅢ
|
||||
108 /* AsciiCode.l */, // ㅣ
|
||||
// HF: Hangul Filler (everything after this is archaic)
|
||||
// ㅥ
|
||||
// ㅦ
|
||||
// ㅧ
|
||||
// ㅨ
|
||||
// ㅩ
|
||||
// ㅪ
|
||||
// ㅫ
|
||||
// ㅬ
|
||||
// ㅮ
|
||||
// ㅯ
|
||||
// ㅰ
|
||||
// ㅱ
|
||||
// ㅲ
|
||||
// ㅳ
|
||||
// ㅴ
|
||||
// ㅵ
|
||||
// ㅶ
|
||||
// ㅷ
|
||||
// ㅸ
|
||||
// ㅹ
|
||||
// ㅺ
|
||||
// ㅻ
|
||||
// ㅼ
|
||||
// ㅽ
|
||||
// ㅾ
|
||||
// ㅿ
|
||||
// ㆀ
|
||||
// ㆁ
|
||||
// ㆂ
|
||||
// ㆃ
|
||||
// ㆄ
|
||||
// ㆅ
|
||||
// ㆆ
|
||||
// ㆇ
|
||||
// ㆈ
|
||||
// ㆉ
|
||||
// ㆊ
|
||||
// ㆋ
|
||||
// ㆌ
|
||||
// ㆍ
|
||||
// ㆎ
|
||||
]);
|
||||
|
||||
export { getKoreanAltChars };
|
||||
+36
@@ -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.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
class ArrayNavigator {
|
||||
constructor(items, start = 0, end = items.length, index = start - 1) {
|
||||
this.items = items;
|
||||
this.start = start;
|
||||
this.end = end;
|
||||
this.index = index;
|
||||
}
|
||||
current() {
|
||||
if (this.index === this.start - 1 || this.index === this.end) {
|
||||
return null;
|
||||
}
|
||||
return this.items[this.index];
|
||||
}
|
||||
next() {
|
||||
this.index = Math.min(this.index + 1, this.end);
|
||||
return this.current();
|
||||
}
|
||||
previous() {
|
||||
this.index = Math.max(this.index - 1, this.start - 1);
|
||||
return this.current();
|
||||
}
|
||||
first() {
|
||||
this.index = this.start;
|
||||
return this.current();
|
||||
}
|
||||
last() {
|
||||
this.index = this.end - 1;
|
||||
return this.current();
|
||||
}
|
||||
}
|
||||
|
||||
export { ArrayNavigator };
|
||||
+267
@@ -0,0 +1,267 @@
|
||||
import { onUnexpectedError } from './errors.js';
|
||||
import { isWeb, isNative, webWorkerOrigin } from './platform.js';
|
||||
import { equalsIgnoreCase, startsWithIgnoreCase } from './strings.js';
|
||||
import { URI } from './uri.js';
|
||||
import { posix } from './path.js';
|
||||
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
var Schemas;
|
||||
(function (Schemas) {
|
||||
/**
|
||||
* A schema that is used for models that exist in memory
|
||||
* only and that have no correspondence on a server or such.
|
||||
*/
|
||||
Schemas.inMemory = 'inmemory';
|
||||
/**
|
||||
* A schema that is used for setting files
|
||||
*/
|
||||
Schemas.vscode = 'vscode';
|
||||
/**
|
||||
* A schema that is used for internal private files
|
||||
*/
|
||||
Schemas.internal = 'private';
|
||||
/**
|
||||
* A walk-through document.
|
||||
*/
|
||||
Schemas.walkThrough = 'walkThrough';
|
||||
/**
|
||||
* An embedded code snippet.
|
||||
*/
|
||||
Schemas.walkThroughSnippet = 'walkThroughSnippet';
|
||||
Schemas.http = 'http';
|
||||
Schemas.https = 'https';
|
||||
Schemas.file = 'file';
|
||||
Schemas.mailto = 'mailto';
|
||||
Schemas.untitled = 'untitled';
|
||||
Schemas.data = 'data';
|
||||
Schemas.command = 'command';
|
||||
Schemas.vscodeRemote = 'vscode-remote';
|
||||
Schemas.vscodeRemoteResource = 'vscode-remote-resource';
|
||||
Schemas.vscodeManagedRemoteResource = 'vscode-managed-remote-resource';
|
||||
Schemas.vscodeUserData = 'vscode-userdata';
|
||||
Schemas.vscodeCustomEditor = 'vscode-custom-editor';
|
||||
Schemas.vscodeNotebookCell = 'vscode-notebook-cell';
|
||||
Schemas.vscodeNotebookCellMetadata = 'vscode-notebook-cell-metadata';
|
||||
Schemas.vscodeNotebookCellMetadataDiff = 'vscode-notebook-cell-metadata-diff';
|
||||
Schemas.vscodeNotebookCellOutput = 'vscode-notebook-cell-output';
|
||||
Schemas.vscodeNotebookCellOutputDiff = 'vscode-notebook-cell-output-diff';
|
||||
Schemas.vscodeNotebookMetadata = 'vscode-notebook-metadata';
|
||||
Schemas.vscodeInteractiveInput = 'vscode-interactive-input';
|
||||
Schemas.vscodeSettings = 'vscode-settings';
|
||||
Schemas.vscodeWorkspaceTrust = 'vscode-workspace-trust';
|
||||
Schemas.vscodeTerminal = 'vscode-terminal';
|
||||
/** Scheme used for code blocks in chat. */
|
||||
Schemas.vscodeChatCodeBlock = 'vscode-chat-code-block';
|
||||
/** Scheme used for LHS of code compare (aka diff) blocks in chat. */
|
||||
Schemas.vscodeChatCodeCompareBlock = 'vscode-chat-code-compare-block';
|
||||
/** Scheme used for the chat input editor. */
|
||||
Schemas.vscodeChatEditor = 'vscode-chat-editor';
|
||||
/** Scheme used for the chat input part */
|
||||
Schemas.vscodeChatInput = 'chatSessionInput';
|
||||
/** Scheme used for local chat session content */
|
||||
Schemas.vscodeLocalChatSession = 'vscode-chat-session';
|
||||
/**
|
||||
* Scheme used internally for webviews that aren't linked to a resource (i.e. not custom editors)
|
||||
*/
|
||||
Schemas.webviewPanel = 'webview-panel';
|
||||
/**
|
||||
* Scheme used for loading the wrapper html and script in webviews.
|
||||
*/
|
||||
Schemas.vscodeWebview = 'vscode-webview';
|
||||
/**
|
||||
* Scheme used for extension pages
|
||||
*/
|
||||
Schemas.extension = 'extension';
|
||||
/**
|
||||
* Scheme used as a replacement of `file` scheme to load
|
||||
* files with our custom protocol handler (desktop only).
|
||||
*/
|
||||
Schemas.vscodeFileResource = 'vscode-file';
|
||||
/**
|
||||
* Scheme used for temporary resources
|
||||
*/
|
||||
Schemas.tmp = 'tmp';
|
||||
/**
|
||||
* Scheme used vs live share
|
||||
*/
|
||||
Schemas.vsls = 'vsls';
|
||||
/**
|
||||
* Scheme used for the Source Control commit input's text document
|
||||
*/
|
||||
Schemas.vscodeSourceControl = 'vscode-scm';
|
||||
/**
|
||||
* Scheme used for input box for creating comments.
|
||||
*/
|
||||
Schemas.commentsInput = 'comment';
|
||||
/**
|
||||
* Scheme used for special rendering of settings in the release notes
|
||||
*/
|
||||
Schemas.codeSetting = 'code-setting';
|
||||
/**
|
||||
* Scheme used for output panel resources
|
||||
*/
|
||||
Schemas.outputChannel = 'output';
|
||||
/**
|
||||
* Scheme used for the accessible view
|
||||
*/
|
||||
Schemas.accessibleView = 'accessible-view';
|
||||
/**
|
||||
* Used for snapshots of chat edits
|
||||
*/
|
||||
Schemas.chatEditingSnapshotScheme = 'chat-editing-snapshot-text-model';
|
||||
Schemas.chatEditingModel = 'chat-editing-text-model';
|
||||
/**
|
||||
* Used for rendering multidiffs in copilot agent sessions
|
||||
*/
|
||||
Schemas.copilotPr = 'copilot-pr';
|
||||
})(Schemas || (Schemas = {}));
|
||||
function matchesScheme(target, scheme) {
|
||||
if (URI.isUri(target)) {
|
||||
return equalsIgnoreCase(target.scheme, scheme);
|
||||
}
|
||||
else {
|
||||
return startsWithIgnoreCase(target, scheme + ':');
|
||||
}
|
||||
}
|
||||
function matchesSomeScheme(target, ...schemes) {
|
||||
return schemes.some(scheme => matchesScheme(target, scheme));
|
||||
}
|
||||
const connectionTokenQueryName = 'tkn';
|
||||
class RemoteAuthoritiesImpl {
|
||||
constructor() {
|
||||
this._hosts = Object.create(null);
|
||||
this._ports = Object.create(null);
|
||||
this._connectionTokens = Object.create(null);
|
||||
this._preferredWebSchema = 'http';
|
||||
this._delegate = null;
|
||||
this._serverRootPath = '/';
|
||||
}
|
||||
setPreferredWebSchema(schema) {
|
||||
this._preferredWebSchema = schema;
|
||||
}
|
||||
get _remoteResourcesPath() {
|
||||
return posix.join(this._serverRootPath, Schemas.vscodeRemoteResource);
|
||||
}
|
||||
rewrite(uri) {
|
||||
if (this._delegate) {
|
||||
try {
|
||||
return this._delegate(uri);
|
||||
}
|
||||
catch (err) {
|
||||
onUnexpectedError(err);
|
||||
return uri;
|
||||
}
|
||||
}
|
||||
const authority = uri.authority;
|
||||
let host = this._hosts[authority];
|
||||
if (host && host.indexOf(':') !== -1 && host.indexOf('[') === -1) {
|
||||
host = `[${host}]`;
|
||||
}
|
||||
const port = this._ports[authority];
|
||||
const connectionToken = this._connectionTokens[authority];
|
||||
let query = `path=${encodeURIComponent(uri.path)}`;
|
||||
if (typeof connectionToken === 'string') {
|
||||
query += `&${connectionTokenQueryName}=${encodeURIComponent(connectionToken)}`;
|
||||
}
|
||||
return URI.from({
|
||||
scheme: isWeb ? this._preferredWebSchema : Schemas.vscodeRemoteResource,
|
||||
authority: `${host}:${port}`,
|
||||
path: this._remoteResourcesPath,
|
||||
query
|
||||
});
|
||||
}
|
||||
}
|
||||
const RemoteAuthorities = new RemoteAuthoritiesImpl();
|
||||
const VSCODE_AUTHORITY = 'vscode-app';
|
||||
class FileAccessImpl {
|
||||
static { this.FALLBACK_AUTHORITY = VSCODE_AUTHORITY; }
|
||||
/**
|
||||
* Returns a URI to use in contexts where the browser is responsible
|
||||
* for loading (e.g. fetch()) or when used within the DOM.
|
||||
*
|
||||
* **Note:** use `dom.ts#asCSSUrl` whenever the URL is to be used in CSS context.
|
||||
*/
|
||||
uriToBrowserUri(uri) {
|
||||
// Handle remote URIs via `RemoteAuthorities`
|
||||
if (uri.scheme === Schemas.vscodeRemote) {
|
||||
return RemoteAuthorities.rewrite(uri);
|
||||
}
|
||||
// Convert to `vscode-file` resource..
|
||||
if (
|
||||
// ...only ever for `file` resources
|
||||
uri.scheme === Schemas.file &&
|
||||
(
|
||||
// ...and we run in native environments
|
||||
isNative ||
|
||||
// ...or web worker extensions on desktop
|
||||
(webWorkerOrigin === `${Schemas.vscodeFileResource}://${FileAccessImpl.FALLBACK_AUTHORITY}`))) {
|
||||
return uri.with({
|
||||
scheme: Schemas.vscodeFileResource,
|
||||
// We need to provide an authority here so that it can serve
|
||||
// as origin for network and loading matters in chromium.
|
||||
// If the URI is not coming with an authority already, we
|
||||
// add our own
|
||||
authority: uri.authority || FileAccessImpl.FALLBACK_AUTHORITY,
|
||||
query: null,
|
||||
fragment: null
|
||||
});
|
||||
}
|
||||
return uri;
|
||||
}
|
||||
}
|
||||
const FileAccess = new FileAccessImpl();
|
||||
var COI;
|
||||
(function (COI) {
|
||||
const coiHeaders = new Map([
|
||||
['1', { 'Cross-Origin-Opener-Policy': 'same-origin' }],
|
||||
['2', { 'Cross-Origin-Embedder-Policy': 'require-corp' }],
|
||||
['3', { 'Cross-Origin-Opener-Policy': 'same-origin', 'Cross-Origin-Embedder-Policy': 'require-corp' }],
|
||||
]);
|
||||
COI.CoopAndCoep = Object.freeze(coiHeaders.get('3'));
|
||||
const coiSearchParamName = 'vscode-coi';
|
||||
/**
|
||||
* Extract desired headers from `vscode-coi` invocation
|
||||
*/
|
||||
function getHeadersFromQuery(url) {
|
||||
let params;
|
||||
if (typeof url === 'string') {
|
||||
params = new URL(url).searchParams;
|
||||
}
|
||||
else if (url instanceof URL) {
|
||||
params = url.searchParams;
|
||||
}
|
||||
else if (URI.isUri(url)) {
|
||||
params = new URL(url.toString(true)).searchParams;
|
||||
}
|
||||
const value = params?.get(coiSearchParamName);
|
||||
if (!value) {
|
||||
return undefined;
|
||||
}
|
||||
return coiHeaders.get(value);
|
||||
}
|
||||
COI.getHeadersFromQuery = getHeadersFromQuery;
|
||||
/**
|
||||
* Add the `vscode-coi` query attribute based on wanting `COOP` and `COEP`. Will be a noop when `crossOriginIsolated`
|
||||
* isn't enabled the current context
|
||||
*/
|
||||
function addSearchParam(urlOrSearch, coop, coep) {
|
||||
// eslint-disable-next-line local/code-no-any-casts
|
||||
if (!globalThis.crossOriginIsolated) {
|
||||
// depends on the current context being COI
|
||||
return;
|
||||
}
|
||||
const value = coop && coep ? '3' : coep ? '2' : '1';
|
||||
if (urlOrSearch instanceof URLSearchParams) {
|
||||
urlOrSearch.set(coiSearchParamName, value);
|
||||
}
|
||||
else {
|
||||
urlOrSearch[coiSearchParamName] = value;
|
||||
}
|
||||
}
|
||||
COI.addSearchParam = addSearchParam;
|
||||
})(COI || (COI = {}));
|
||||
|
||||
export { COI, FileAccess, RemoteAuthorities, Schemas, VSCODE_AUTHORITY, connectionTokenQueryName, matchesScheme, matchesSomeScheme };
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
import { LRUCache } from './map.js';
|
||||
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
new LRUCache(10000);
|
||||
const nfdCache = new LRUCache(10000); // bounded to 10000 elements
|
||||
function normalizeNFD(str) {
|
||||
return normalize(str, 'NFD', nfdCache);
|
||||
}
|
||||
const nonAsciiCharactersPattern = /[^\u0000-\u0080]/;
|
||||
function normalize(str, form, normalizedCache) {
|
||||
if (!str) {
|
||||
return str;
|
||||
}
|
||||
const cached = normalizedCache.get(str);
|
||||
if (cached) {
|
||||
return cached;
|
||||
}
|
||||
let res;
|
||||
if (nonAsciiCharactersPattern.test(str)) {
|
||||
res = str.normalize(form);
|
||||
}
|
||||
else {
|
||||
res = str;
|
||||
}
|
||||
// Use the cache for fast lookup
|
||||
normalizedCache.set(str, res);
|
||||
return res;
|
||||
}
|
||||
const removeAccents = (function () {
|
||||
// transform into NFD form and remove accents
|
||||
// see: https://stackoverflow.com/questions/990904/remove-accents-diacritics-in-a-string-in-javascript/37511463#37511463
|
||||
const regex = /[\u0300-\u036f]/g;
|
||||
return function (str) {
|
||||
return normalizeNFD(str).replace(regex, '');
|
||||
};
|
||||
})();
|
||||
|
||||
export { normalizeNFD, removeAccents };
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
function clamp(value, min, max) {
|
||||
return Math.min(Math.max(value, min), max);
|
||||
}
|
||||
class MovingAverage {
|
||||
constructor() {
|
||||
this._n = 1;
|
||||
this._val = 0;
|
||||
}
|
||||
update(value) {
|
||||
this._val = this._val + (value - this._val) / this._n;
|
||||
this._n += 1;
|
||||
return this._val;
|
||||
}
|
||||
get value() {
|
||||
return this._val;
|
||||
}
|
||||
}
|
||||
class SlidingWindowAverage {
|
||||
constructor(size) {
|
||||
this._n = 0;
|
||||
this._val = 0;
|
||||
this._values = [];
|
||||
this._index = 0;
|
||||
this._sum = 0;
|
||||
this._values = new Array(size);
|
||||
this._values.fill(0, 0, size);
|
||||
}
|
||||
update(value) {
|
||||
const oldValue = this._values[this._index];
|
||||
this._values[this._index] = value;
|
||||
this._index = (this._index + 1) % this._values.length;
|
||||
this._sum -= oldValue;
|
||||
this._sum += value;
|
||||
if (this._n < this._values.length) {
|
||||
this._n += 1;
|
||||
}
|
||||
this._val = this._sum / this._n;
|
||||
return this._val;
|
||||
}
|
||||
get value() {
|
||||
return this._val;
|
||||
}
|
||||
}
|
||||
|
||||
export { MovingAverage, SlidingWindowAverage, clamp };
|
||||
+152
@@ -0,0 +1,152 @@
|
||||
import { isUndefinedOrNull, isObject, isTypedArray } from './types.js';
|
||||
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
function deepClone(obj) {
|
||||
if (!obj || typeof obj !== 'object') {
|
||||
return obj;
|
||||
}
|
||||
if (obj instanceof RegExp) {
|
||||
return obj;
|
||||
}
|
||||
const result = Array.isArray(obj) ? [] : {};
|
||||
Object.entries(obj).forEach(([key, value]) => {
|
||||
result[key] = value && typeof value === 'object' ? deepClone(value) : value;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
function deepFreeze(obj) {
|
||||
if (!obj || typeof obj !== 'object') {
|
||||
return obj;
|
||||
}
|
||||
const stack = [obj];
|
||||
while (stack.length > 0) {
|
||||
const obj = stack.shift();
|
||||
Object.freeze(obj);
|
||||
for (const key in obj) {
|
||||
if (_hasOwnProperty.call(obj, key)) {
|
||||
const prop = obj[key];
|
||||
if (typeof prop === 'object' && !Object.isFrozen(prop) && !isTypedArray(prop)) {
|
||||
stack.push(prop);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
const _hasOwnProperty = Object.prototype.hasOwnProperty;
|
||||
function cloneAndChange(obj, changer) {
|
||||
return _cloneAndChange(obj, changer, new Set());
|
||||
}
|
||||
function _cloneAndChange(obj, changer, seen) {
|
||||
if (isUndefinedOrNull(obj)) {
|
||||
return obj;
|
||||
}
|
||||
const changed = changer(obj);
|
||||
if (typeof changed !== 'undefined') {
|
||||
return changed;
|
||||
}
|
||||
if (Array.isArray(obj)) {
|
||||
const r1 = [];
|
||||
for (const e of obj) {
|
||||
r1.push(_cloneAndChange(e, changer, seen));
|
||||
}
|
||||
return r1;
|
||||
}
|
||||
if (isObject(obj)) {
|
||||
if (seen.has(obj)) {
|
||||
throw new Error('Cannot clone recursive data-structure');
|
||||
}
|
||||
seen.add(obj);
|
||||
const r2 = {};
|
||||
for (const i2 in obj) {
|
||||
if (_hasOwnProperty.call(obj, i2)) {
|
||||
r2[i2] = _cloneAndChange(obj[i2], changer, seen);
|
||||
}
|
||||
}
|
||||
seen.delete(obj);
|
||||
return r2;
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
/**
|
||||
* Copies all properties of source into destination. The optional parameter "overwrite" allows to control
|
||||
* if existing properties on the destination should be overwritten or not. Defaults to true (overwrite).
|
||||
*/
|
||||
function mixin(destination, source, overwrite = true) {
|
||||
if (!isObject(destination)) {
|
||||
return source;
|
||||
}
|
||||
if (isObject(source)) {
|
||||
Object.keys(source).forEach(key => {
|
||||
if (key in destination) {
|
||||
if (overwrite) {
|
||||
if (isObject(destination[key]) && isObject(source[key])) {
|
||||
mixin(destination[key], source[key], overwrite);
|
||||
}
|
||||
else {
|
||||
destination[key] = source[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
destination[key] = source[key];
|
||||
}
|
||||
});
|
||||
}
|
||||
return destination;
|
||||
}
|
||||
function equals(one, other) {
|
||||
if (one === other) {
|
||||
return true;
|
||||
}
|
||||
if (one === null || one === undefined || other === null || other === undefined) {
|
||||
return false;
|
||||
}
|
||||
if (typeof one !== typeof other) {
|
||||
return false;
|
||||
}
|
||||
if (typeof one !== 'object') {
|
||||
return false;
|
||||
}
|
||||
if ((Array.isArray(one)) !== (Array.isArray(other))) {
|
||||
return false;
|
||||
}
|
||||
let i;
|
||||
let key;
|
||||
if (Array.isArray(one)) {
|
||||
if (one.length !== other.length) {
|
||||
return false;
|
||||
}
|
||||
for (i = 0; i < one.length; i++) {
|
||||
if (!equals(one[i], other[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
const oneKeys = [];
|
||||
for (key in one) {
|
||||
oneKeys.push(key);
|
||||
}
|
||||
oneKeys.sort();
|
||||
const otherKeys = [];
|
||||
for (key in other) {
|
||||
otherKeys.push(key);
|
||||
}
|
||||
otherKeys.sort();
|
||||
if (!equals(oneKeys, otherKeys)) {
|
||||
return false;
|
||||
}
|
||||
for (i = 0; i < oneKeys.length; i++) {
|
||||
if (!equals(one[oneKeys[i]], other[oneKeys[i]])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
export { cloneAndChange, deepClone, deepFreeze, equals, mixin };
|
||||
Generated
Vendored
+19
@@ -0,0 +1,19 @@
|
||||
import { onUnexpectedError } from '../errors.js';
|
||||
import '../arrays.js';
|
||||
import '../event.js';
|
||||
import '../lifecycle.js';
|
||||
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
/**
|
||||
* This function is used to indicate that the caller recovered from an error that indicates a bug.
|
||||
*/
|
||||
function handleBugIndicatingErrorRecovery(message) {
|
||||
const err = new Error('BugIndicatingErrorRecovery: ' + message);
|
||||
onUnexpectedError(err);
|
||||
console.error('recovered from an error that indicates a bug', err);
|
||||
}
|
||||
|
||||
export { handleBugIndicatingErrorRecovery };
|
||||
Generated
Vendored
+80
@@ -0,0 +1,80 @@
|
||||
import { BugIndicatingError } from '../errors.js';
|
||||
import '../arrays.js';
|
||||
import '../event.js';
|
||||
import '../lifecycle.js';
|
||||
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
/**
|
||||
* Subscribes to and records changes and the last value of the given observables.
|
||||
* Don't use the key "changes", as it is reserved for the changes array!
|
||||
*/
|
||||
function recordChanges(obs) {
|
||||
return {
|
||||
createChangeSummary: (_previousChangeSummary) => {
|
||||
// eslint-disable-next-line local/code-no-any-casts
|
||||
return {
|
||||
changes: [],
|
||||
};
|
||||
},
|
||||
handleChange(ctx, changeSummary) {
|
||||
for (const key in obs) {
|
||||
if (ctx.didChange(obs[key])) {
|
||||
// eslint-disable-next-line local/code-no-any-casts
|
||||
changeSummary.changes.push({ key, change: ctx.change });
|
||||
}
|
||||
}
|
||||
return true;
|
||||
},
|
||||
beforeUpdate(reader, changeSummary) {
|
||||
for (const key in obs) {
|
||||
if (key === 'changes') {
|
||||
throw new BugIndicatingError('property name "changes" is reserved for change tracking');
|
||||
}
|
||||
changeSummary[key] = obs[key].read(reader);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Subscribes to and records changes and the last value of the given observables.
|
||||
* Don't use the key "changes", as it is reserved for the changes array!
|
||||
*/
|
||||
function recordChangesLazy(getObs) {
|
||||
let obs = undefined;
|
||||
return {
|
||||
createChangeSummary: (_previousChangeSummary) => {
|
||||
// eslint-disable-next-line local/code-no-any-casts
|
||||
return {
|
||||
changes: [],
|
||||
};
|
||||
},
|
||||
handleChange(ctx, changeSummary) {
|
||||
if (!obs) {
|
||||
obs = getObs();
|
||||
}
|
||||
for (const key in obs) {
|
||||
if (ctx.didChange(obs[key])) {
|
||||
// eslint-disable-next-line local/code-no-any-casts
|
||||
changeSummary.changes.push({ key, change: ctx.change });
|
||||
}
|
||||
}
|
||||
return true;
|
||||
},
|
||||
beforeUpdate(reader, changeSummary) {
|
||||
if (!obs) {
|
||||
obs = getObs();
|
||||
}
|
||||
for (const key in obs) {
|
||||
if (key === 'changes') {
|
||||
throw new BugIndicatingError('property name "changes" is reserved for change tracking');
|
||||
}
|
||||
changeSummary[key] = obs[key].read(reader);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export { recordChanges, recordChangesLazy };
|
||||
Generated
Vendored
+66
@@ -0,0 +1,66 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
var DebugLocation;
|
||||
(function (DebugLocation) {
|
||||
let enabled = false;
|
||||
function enable() {
|
||||
enabled = true;
|
||||
}
|
||||
DebugLocation.enable = enable;
|
||||
function ofCaller() {
|
||||
if (!enabled) {
|
||||
return undefined;
|
||||
}
|
||||
// eslint-disable-next-line local/code-no-any-casts
|
||||
const Err = Error; // For the monaco editor checks, which don't have the nodejs types.
|
||||
const l = Err.stackTraceLimit;
|
||||
Err.stackTraceLimit = 3;
|
||||
const stack = new Error().stack;
|
||||
Err.stackTraceLimit = l;
|
||||
return DebugLocationImpl.fromStack(stack, 2);
|
||||
}
|
||||
DebugLocation.ofCaller = ofCaller;
|
||||
})(DebugLocation || (DebugLocation = {}));
|
||||
class DebugLocationImpl {
|
||||
static fromStack(stack, parentIdx) {
|
||||
const lines = stack.split('\n');
|
||||
const location = parseLine(lines[parentIdx + 1]);
|
||||
if (location) {
|
||||
return new DebugLocationImpl(location.fileName, location.line, location.column, location.id);
|
||||
}
|
||||
else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
constructor(fileName, line, column, id) {
|
||||
this.fileName = fileName;
|
||||
this.line = line;
|
||||
this.column = column;
|
||||
this.id = id;
|
||||
}
|
||||
}
|
||||
function parseLine(stackLine) {
|
||||
const match = stackLine.match(/\((.*):(\d+):(\d+)\)/);
|
||||
if (match) {
|
||||
return {
|
||||
fileName: match[1],
|
||||
line: parseInt(match[2]),
|
||||
column: parseInt(match[3]),
|
||||
id: stackLine,
|
||||
};
|
||||
}
|
||||
const match2 = stackLine.match(/at ([^\(\)]*):(\d+):(\d+)/);
|
||||
if (match2) {
|
||||
return {
|
||||
fileName: match2[1],
|
||||
line: parseInt(match2[2]),
|
||||
column: parseInt(match2[3]),
|
||||
id: stackLine,
|
||||
};
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export { DebugLocation };
|
||||
Generated
Vendored
+110
@@ -0,0 +1,110 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
class DebugNameData {
|
||||
constructor(owner, debugNameSource, referenceFn) {
|
||||
this.owner = owner;
|
||||
this.debugNameSource = debugNameSource;
|
||||
this.referenceFn = referenceFn;
|
||||
}
|
||||
getDebugName(target) {
|
||||
return getDebugName(target, this);
|
||||
}
|
||||
}
|
||||
const countPerName = new Map();
|
||||
const cachedDebugName = new WeakMap();
|
||||
function getDebugName(target, data) {
|
||||
const cached = cachedDebugName.get(target);
|
||||
if (cached) {
|
||||
return cached;
|
||||
}
|
||||
const dbgName = computeDebugName(target, data);
|
||||
if (dbgName) {
|
||||
let count = countPerName.get(dbgName) ?? 0;
|
||||
count++;
|
||||
countPerName.set(dbgName, count);
|
||||
const result = count === 1 ? dbgName : `${dbgName}#${count}`;
|
||||
cachedDebugName.set(target, result);
|
||||
return result;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
function computeDebugName(self, data) {
|
||||
const cached = cachedDebugName.get(self);
|
||||
if (cached) {
|
||||
return cached;
|
||||
}
|
||||
const ownerStr = data.owner ? formatOwner(data.owner) + `.` : '';
|
||||
let result;
|
||||
const debugNameSource = data.debugNameSource;
|
||||
if (debugNameSource !== undefined) {
|
||||
if (typeof debugNameSource === 'function') {
|
||||
result = debugNameSource();
|
||||
if (result !== undefined) {
|
||||
return ownerStr + result;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return ownerStr + debugNameSource;
|
||||
}
|
||||
}
|
||||
const referenceFn = data.referenceFn;
|
||||
if (referenceFn !== undefined) {
|
||||
result = getFunctionName(referenceFn);
|
||||
if (result !== undefined) {
|
||||
return ownerStr + result;
|
||||
}
|
||||
}
|
||||
if (data.owner !== undefined) {
|
||||
const key = findKey(data.owner, self);
|
||||
if (key !== undefined) {
|
||||
return ownerStr + key;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
function findKey(obj, value) {
|
||||
for (const key in obj) {
|
||||
// eslint-disable-next-line local/code-no-any-casts
|
||||
if (obj[key] === value) {
|
||||
return key;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
const countPerClassName = new Map();
|
||||
const ownerId = new WeakMap();
|
||||
function formatOwner(owner) {
|
||||
const id = ownerId.get(owner);
|
||||
if (id) {
|
||||
return id;
|
||||
}
|
||||
const className = getClassName(owner) ?? 'Object';
|
||||
let count = countPerClassName.get(className) ?? 0;
|
||||
count++;
|
||||
countPerClassName.set(className, count);
|
||||
const result = count === 1 ? className : `${className}#${count}`;
|
||||
ownerId.set(owner, result);
|
||||
return result;
|
||||
}
|
||||
function getClassName(obj) {
|
||||
const ctor = obj.constructor;
|
||||
if (ctor) {
|
||||
if (ctor.name === 'Object') {
|
||||
return undefined;
|
||||
}
|
||||
return ctor.name;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
function getFunctionName(fn) {
|
||||
const fnSrc = fn.toString();
|
||||
// Pattern: /** @description ... */
|
||||
const regexp = /\/\*\*\s*@description\s*([^*]*)\*\//;
|
||||
const match = regexp.exec(fnSrc);
|
||||
const result = match ? match[1] : undefined;
|
||||
return result?.trim();
|
||||
}
|
||||
|
||||
export { DebugNameData, getClassName, getDebugName, getFunctionName };
|
||||
Generated
Vendored
+46
@@ -0,0 +1,46 @@
|
||||
import { BugIndicatingError } from '../../errors.js';
|
||||
import { strictEquals } from '../../equals.js';
|
||||
import '../../event.js';
|
||||
import '../../lifecycle.js';
|
||||
import { subtransaction } from '../transaction.js';
|
||||
import { DebugNameData } from '../debugName.js';
|
||||
import { DerivedWithSetter } from '../observables/derivedImpl.js';
|
||||
import { DebugLocation } from '../debugLocation.js';
|
||||
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
/**
|
||||
* Creates an observable value that is based on values and changes from other observables.
|
||||
* Additionally, a reducer can report how that state changed.
|
||||
*/
|
||||
function observableReducerSettable(owner, options) {
|
||||
let prevValue = undefined;
|
||||
let hasValue = false;
|
||||
const d = new DerivedWithSetter(new DebugNameData(owner, undefined, options.update), (reader, changeSummary) => {
|
||||
if (!hasValue) {
|
||||
prevValue = options.initial instanceof Function ? options.initial() : options.initial;
|
||||
hasValue = true;
|
||||
}
|
||||
const newValue = options.update(reader, prevValue, changeSummary);
|
||||
prevValue = newValue;
|
||||
return newValue;
|
||||
}, options.changeTracker, () => {
|
||||
if (hasValue) {
|
||||
options.disposeFinal?.(prevValue);
|
||||
hasValue = false;
|
||||
}
|
||||
}, options.equalityComparer ?? strictEquals, (value, tx, change) => {
|
||||
if (!hasValue) {
|
||||
throw new BugIndicatingError('Can only set when there is a listener! This is to prevent leaks.');
|
||||
}
|
||||
subtransaction(tx, tx => {
|
||||
prevValue = value;
|
||||
d.setValue(value, tx, change);
|
||||
});
|
||||
}, DebugLocation.ofCaller());
|
||||
return d;
|
||||
}
|
||||
|
||||
export { observableReducerSettable };
|
||||
Generated
Vendored
+16
@@ -0,0 +1,16 @@
|
||||
import { derivedObservableWithCache } from '../utils/utils.js';
|
||||
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
/**
|
||||
* Works like a derived.
|
||||
* However, if the value is not undefined, it is cached and will not be recomputed anymore.
|
||||
* In that case, the derived will unsubscribe from its dependencies.
|
||||
*/
|
||||
function derivedConstOnceDefined(owner, fn) {
|
||||
return derivedObservableWithCache(owner, (reader, lastValue) => lastValue ?? fn(reader));
|
||||
}
|
||||
|
||||
export { derivedConstOnceDefined };
|
||||
Generated
Vendored
+21
@@ -0,0 +1,21 @@
|
||||
import '../arrays.js';
|
||||
import '../event.js';
|
||||
import '../lifecycle.js';
|
||||
import { addLogger } from './logging/logging.js';
|
||||
export { DebugLocation } from './debugLocation.js';
|
||||
export { derived, derivedDisposable, derivedHandleChanges, derivedOpts, derivedWithSetter } from './observables/derived.js';
|
||||
import '../cancellation.js';
|
||||
export { debouncedObservable, derivedObservableWithCache, mapObservableArrayCached, recomputeInitiallyAndOnChange } from './utils/utils.js';
|
||||
export { observableFromEvent, observableFromEventOpts } from './observables/observableFromEvent.js';
|
||||
import { DevToolsLogger } from './logging/debugger/devToolsLogger.js';
|
||||
import { env } from '../process.js';
|
||||
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
// This is a facade for the observable implementation. Only import from here!
|
||||
if (env && env['VSCODE_DEV_DEBUG_OBSERVABLES']) {
|
||||
// To debug observables you also need the extension "ms-vscode.debug-value-editor"
|
||||
addLogger(DevToolsLogger.getInstance());
|
||||
}
|
||||
Generated
Vendored
+83
@@ -0,0 +1,83 @@
|
||||
import { getClassName } from '../debugName.js';
|
||||
import '../debugLocation.js';
|
||||
import '../../arrays.js';
|
||||
import '../../event.js';
|
||||
import '../../lifecycle.js';
|
||||
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
function formatValue(value, availableLen) {
|
||||
switch (typeof value) {
|
||||
case 'number':
|
||||
return '' + value;
|
||||
case 'string':
|
||||
if (value.length + 2 <= availableLen) {
|
||||
return `"${value}"`;
|
||||
}
|
||||
return `"${value.substr(0, availableLen - 7)}"+...`;
|
||||
case 'boolean':
|
||||
return value ? 'true' : 'false';
|
||||
case 'undefined':
|
||||
return 'undefined';
|
||||
case 'object':
|
||||
if (value === null) {
|
||||
return 'null';
|
||||
}
|
||||
if (Array.isArray(value)) {
|
||||
return formatArray(value, availableLen);
|
||||
}
|
||||
return formatObject(value, availableLen);
|
||||
case 'symbol':
|
||||
return value.toString();
|
||||
case 'function':
|
||||
return `[[Function${value.name ? ' ' + value.name : ''}]]`;
|
||||
default:
|
||||
return '' + value;
|
||||
}
|
||||
}
|
||||
function formatArray(value, availableLen) {
|
||||
let result = '[ ';
|
||||
let first = true;
|
||||
for (const val of value) {
|
||||
if (!first) {
|
||||
result += ', ';
|
||||
}
|
||||
if (result.length - 5 > availableLen) {
|
||||
result += '...';
|
||||
break;
|
||||
}
|
||||
first = false;
|
||||
result += `${formatValue(val, availableLen - result.length)}`;
|
||||
}
|
||||
result += ' ]';
|
||||
return result;
|
||||
}
|
||||
function formatObject(value, availableLen) {
|
||||
if (typeof value.toString === 'function' && value.toString !== Object.prototype.toString) {
|
||||
const val = value.toString();
|
||||
if (val.length <= availableLen) {
|
||||
return val;
|
||||
}
|
||||
return val.substring(0, availableLen - 3) + '...';
|
||||
}
|
||||
const className = getClassName(value);
|
||||
let result = className ? className + '(' : '{ ';
|
||||
let first = true;
|
||||
for (const [key, val] of Object.entries(value)) {
|
||||
if (!first) {
|
||||
result += ', ';
|
||||
}
|
||||
if (result.length - 5 > availableLen) {
|
||||
result += '...';
|
||||
break;
|
||||
}
|
||||
first = false;
|
||||
result += `${key}: ${formatValue(val, availableLen - result.length)}`;
|
||||
}
|
||||
result += className ? ')' : ' }';
|
||||
return result;
|
||||
}
|
||||
|
||||
export { formatValue };
|
||||
Generated
Vendored
+67
@@ -0,0 +1,67 @@
|
||||
import { SimpleTypedRpcConnection } from './rpc.js';
|
||||
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
function registerDebugChannel(channelId, createClient) {
|
||||
// eslint-disable-next-line local/code-no-any-casts
|
||||
const g = globalThis;
|
||||
let queuedNotifications = [];
|
||||
let curHost = undefined;
|
||||
const { channel, handler } = createChannelFactoryFromDebugChannel({
|
||||
sendNotification: (data) => {
|
||||
if (curHost) {
|
||||
curHost.sendNotification(data);
|
||||
}
|
||||
else {
|
||||
queuedNotifications.push(data);
|
||||
}
|
||||
},
|
||||
});
|
||||
let curClient = undefined;
|
||||
(g.$$debugValueEditor_debugChannels ?? (g.$$debugValueEditor_debugChannels = {}))[channelId] = (host) => {
|
||||
curClient = createClient();
|
||||
curHost = host;
|
||||
for (const n of queuedNotifications) {
|
||||
host.sendNotification(n);
|
||||
}
|
||||
queuedNotifications = [];
|
||||
return handler;
|
||||
};
|
||||
return SimpleTypedRpcConnection.createClient(channel, () => {
|
||||
if (!curClient) {
|
||||
throw new Error('Not supported');
|
||||
}
|
||||
return curClient;
|
||||
});
|
||||
}
|
||||
function createChannelFactoryFromDebugChannel(host) {
|
||||
let h;
|
||||
const channel = (handler) => {
|
||||
h = handler;
|
||||
return {
|
||||
sendNotification: data => {
|
||||
host.sendNotification(data);
|
||||
},
|
||||
sendRequest: data => {
|
||||
throw new Error('not supported');
|
||||
},
|
||||
};
|
||||
};
|
||||
return {
|
||||
channel: channel,
|
||||
handler: {
|
||||
handleRequest: (data) => {
|
||||
if (data.type === 'notification') {
|
||||
return h?.handleNotification(data.data);
|
||||
}
|
||||
else {
|
||||
return h?.handleRequest(data.data);
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export { registerDebugChannel };
|
||||
Generated
Vendored
+442
@@ -0,0 +1,442 @@
|
||||
import { AutorunObserver } from '../../reactions/autorunImpl.js';
|
||||
import { formatValue } from '../consoleObservableLogger.js';
|
||||
import { registerDebugChannel } from './debuggerRpc.js';
|
||||
import { Throttler, deepAssignDeleteNulls, deepAssign } from './utils.js';
|
||||
import { isDefined } from '../../../types.js';
|
||||
import { FromEventObservable } from '../../observables/observableFromEvent.js';
|
||||
import { onUnexpectedError, BugIndicatingError } from '../../../errors.js';
|
||||
import { Derived } from '../../observables/derivedImpl.js';
|
||||
import { ObservableValue } from '../../observables/observableValue.js';
|
||||
import { DebugLocation } from '../../debugLocation.js';
|
||||
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
class DevToolsLogger {
|
||||
static { this._instance = undefined; }
|
||||
static getInstance() {
|
||||
if (DevToolsLogger._instance === undefined) {
|
||||
DevToolsLogger._instance = new DevToolsLogger();
|
||||
}
|
||||
return DevToolsLogger._instance;
|
||||
}
|
||||
getTransactionState() {
|
||||
const affected = [];
|
||||
const txs = [...this._activeTransactions];
|
||||
if (txs.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
const observerQueue = txs.flatMap(t => t.debugGetUpdatingObservers() ?? []).map(o => o.observer);
|
||||
const processedObservers = new Set();
|
||||
while (observerQueue.length > 0) {
|
||||
const observer = observerQueue.shift();
|
||||
if (processedObservers.has(observer)) {
|
||||
continue;
|
||||
}
|
||||
processedObservers.add(observer);
|
||||
const state = this._getInfo(observer, d => {
|
||||
if (!processedObservers.has(d)) {
|
||||
observerQueue.push(d);
|
||||
}
|
||||
});
|
||||
if (state) {
|
||||
affected.push(state);
|
||||
}
|
||||
}
|
||||
return { names: txs.map(t => t.getDebugName() ?? 'tx'), affected };
|
||||
}
|
||||
_getObservableInfo(observable) {
|
||||
const info = this._instanceInfos.get(observable);
|
||||
if (!info) {
|
||||
onUnexpectedError(new BugIndicatingError('No info found'));
|
||||
return undefined;
|
||||
}
|
||||
return info;
|
||||
}
|
||||
_getAutorunInfo(autorun) {
|
||||
const info = this._instanceInfos.get(autorun);
|
||||
if (!info) {
|
||||
onUnexpectedError(new BugIndicatingError('No info found'));
|
||||
return undefined;
|
||||
}
|
||||
return info;
|
||||
}
|
||||
_getInfo(observer, queue) {
|
||||
if (observer instanceof Derived) {
|
||||
const observersToUpdate = [...observer.debugGetObservers()];
|
||||
for (const o of observersToUpdate) {
|
||||
queue(o);
|
||||
}
|
||||
const info = this._getObservableInfo(observer);
|
||||
if (!info) {
|
||||
return;
|
||||
}
|
||||
const observerState = observer.debugGetState();
|
||||
const base = { name: observer.debugName, instanceId: info.instanceId, updateCount: observerState.updateCount };
|
||||
const changedDependencies = [...info.changedObservables].map(o => this._instanceInfos.get(o)?.instanceId).filter(isDefined);
|
||||
if (observerState.isComputing) {
|
||||
return { ...base, type: 'observable/derived', state: 'updating', changedDependencies, initialComputation: false };
|
||||
}
|
||||
switch (observerState.state) {
|
||||
case 0 /* DerivedState.initial */:
|
||||
return { ...base, type: 'observable/derived', state: 'noValue' };
|
||||
case 3 /* DerivedState.upToDate */:
|
||||
return { ...base, type: 'observable/derived', state: 'upToDate' };
|
||||
case 2 /* DerivedState.stale */:
|
||||
return { ...base, type: 'observable/derived', state: 'stale', changedDependencies };
|
||||
case 1 /* DerivedState.dependenciesMightHaveChanged */:
|
||||
return { ...base, type: 'observable/derived', state: 'possiblyStale' };
|
||||
}
|
||||
}
|
||||
else if (observer instanceof AutorunObserver) {
|
||||
const info = this._getAutorunInfo(observer);
|
||||
if (!info) {
|
||||
return undefined;
|
||||
}
|
||||
const base = { name: observer.debugName, instanceId: info.instanceId, updateCount: info.updateCount };
|
||||
const changedDependencies = [...info.changedObservables].map(o => this._instanceInfos.get(o).instanceId);
|
||||
if (observer.debugGetState().isRunning) {
|
||||
return { ...base, type: 'autorun', state: 'updating', changedDependencies };
|
||||
}
|
||||
switch (observer.debugGetState().state) {
|
||||
case 3 /* AutorunState.upToDate */:
|
||||
return { ...base, type: 'autorun', state: 'upToDate' };
|
||||
case 2 /* AutorunState.stale */:
|
||||
return { ...base, type: 'autorun', state: 'stale', changedDependencies };
|
||||
case 1 /* AutorunState.dependenciesMightHaveChanged */:
|
||||
return { ...base, type: 'autorun', state: 'possiblyStale' };
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
_formatObservable(obs) {
|
||||
const info = this._getObservableInfo(obs);
|
||||
if (!info) {
|
||||
return undefined;
|
||||
}
|
||||
return { name: obs.debugName, instanceId: info.instanceId };
|
||||
}
|
||||
_formatObserver(obs) {
|
||||
if (obs instanceof Derived) {
|
||||
return { name: obs.toString(), instanceId: this._getObservableInfo(obs)?.instanceId };
|
||||
}
|
||||
const autorunInfo = this._getAutorunInfo(obs);
|
||||
if (autorunInfo) {
|
||||
return { name: obs.toString(), instanceId: autorunInfo.instanceId };
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
constructor() {
|
||||
this._declarationId = 0;
|
||||
this._instanceId = 0;
|
||||
this._declarations = new Map();
|
||||
this._instanceInfos = new WeakMap();
|
||||
this._aliveInstances = new Map();
|
||||
this._activeTransactions = new Set();
|
||||
this._channel = registerDebugChannel('observableDevTools', () => {
|
||||
return {
|
||||
notifications: {
|
||||
setDeclarationIdFilter: declarationIds => {
|
||||
},
|
||||
logObservableValue: (observableId) => {
|
||||
console.log('logObservableValue', observableId);
|
||||
},
|
||||
flushUpdates: () => {
|
||||
this._flushUpdates();
|
||||
},
|
||||
resetUpdates: () => {
|
||||
this._pendingChanges = null;
|
||||
this._channel.api.notifications.handleChange(this._fullState, true);
|
||||
},
|
||||
},
|
||||
requests: {
|
||||
getDeclarations: () => {
|
||||
const result = {};
|
||||
for (const decl of this._declarations.values()) {
|
||||
result[decl.id] = decl;
|
||||
}
|
||||
return { decls: result };
|
||||
},
|
||||
getSummarizedInstances: () => {
|
||||
return null;
|
||||
},
|
||||
getObservableValueInfo: instanceId => {
|
||||
const obs = this._aliveInstances.get(instanceId);
|
||||
return {
|
||||
observers: [...obs.debugGetObservers()].map(d => this._formatObserver(d)).filter(isDefined),
|
||||
};
|
||||
},
|
||||
getDerivedInfo: instanceId => {
|
||||
const d = this._aliveInstances.get(instanceId);
|
||||
return {
|
||||
dependencies: [...d.debugGetState().dependencies].map(d => this._formatObservable(d)).filter(isDefined),
|
||||
observers: [...d.debugGetObservers()].map(d => this._formatObserver(d)).filter(isDefined),
|
||||
};
|
||||
},
|
||||
getAutorunInfo: instanceId => {
|
||||
const obs = this._aliveInstances.get(instanceId);
|
||||
return {
|
||||
dependencies: [...obs.debugGetState().dependencies].map(d => this._formatObservable(d)).filter(isDefined),
|
||||
};
|
||||
},
|
||||
getTransactionState: () => {
|
||||
return this.getTransactionState();
|
||||
},
|
||||
setValue: (instanceId, jsonValue) => {
|
||||
const obs = this._aliveInstances.get(instanceId);
|
||||
if (obs instanceof Derived) {
|
||||
obs.debugSetValue(jsonValue);
|
||||
}
|
||||
else if (obs instanceof ObservableValue) {
|
||||
obs.debugSetValue(jsonValue);
|
||||
}
|
||||
else if (obs instanceof FromEventObservable) {
|
||||
obs.debugSetValue(jsonValue);
|
||||
}
|
||||
else {
|
||||
throw new BugIndicatingError('Observable is not supported');
|
||||
}
|
||||
const observers = [...obs.debugGetObservers()];
|
||||
for (const d of observers) {
|
||||
d.beginUpdate(obs);
|
||||
}
|
||||
for (const d of observers) {
|
||||
d.handleChange(obs, undefined);
|
||||
}
|
||||
for (const d of observers) {
|
||||
d.endUpdate(obs);
|
||||
}
|
||||
},
|
||||
getValue: instanceId => {
|
||||
const obs = this._aliveInstances.get(instanceId);
|
||||
if (obs instanceof Derived) {
|
||||
return formatValue(obs.debugGetState().value, 200);
|
||||
}
|
||||
else if (obs instanceof ObservableValue) {
|
||||
return formatValue(obs.debugGetState().value, 200);
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
logValue: (instanceId) => {
|
||||
const obs = this._aliveInstances.get(instanceId);
|
||||
if (obs && 'get' in obs) {
|
||||
console.log('Logged Value:', obs.get());
|
||||
}
|
||||
else {
|
||||
throw new BugIndicatingError('Observable is not supported');
|
||||
}
|
||||
},
|
||||
rerun: (instanceId) => {
|
||||
const obs = this._aliveInstances.get(instanceId);
|
||||
if (obs instanceof Derived) {
|
||||
obs.debugRecompute();
|
||||
}
|
||||
else if (obs instanceof AutorunObserver) {
|
||||
obs.debugRerun();
|
||||
}
|
||||
else {
|
||||
throw new BugIndicatingError('Observable is not supported');
|
||||
}
|
||||
},
|
||||
}
|
||||
};
|
||||
});
|
||||
this._pendingChanges = null;
|
||||
this._changeThrottler = new Throttler();
|
||||
this._fullState = {};
|
||||
this._flushUpdates = () => {
|
||||
if (this._pendingChanges !== null) {
|
||||
this._channel.api.notifications.handleChange(this._pendingChanges, false);
|
||||
this._pendingChanges = null;
|
||||
}
|
||||
};
|
||||
DebugLocation.enable();
|
||||
}
|
||||
_handleChange(update) {
|
||||
deepAssignDeleteNulls(this._fullState, update);
|
||||
if (this._pendingChanges === null) {
|
||||
this._pendingChanges = update;
|
||||
}
|
||||
else {
|
||||
deepAssign(this._pendingChanges, update);
|
||||
}
|
||||
this._changeThrottler.throttle(this._flushUpdates, 10);
|
||||
}
|
||||
_getDeclarationId(type, location) {
|
||||
if (!location) {
|
||||
return -1;
|
||||
}
|
||||
let decInfo = this._declarations.get(location.id);
|
||||
if (decInfo === undefined) {
|
||||
decInfo = {
|
||||
id: this._declarationId++,
|
||||
type,
|
||||
url: location.fileName,
|
||||
line: location.line,
|
||||
column: location.column,
|
||||
};
|
||||
this._declarations.set(location.id, decInfo);
|
||||
this._handleChange({ decls: { [decInfo.id]: decInfo } });
|
||||
}
|
||||
return decInfo.id;
|
||||
}
|
||||
handleObservableCreated(observable, location) {
|
||||
const declarationId = this._getDeclarationId('observable/value', location);
|
||||
const info = {
|
||||
declarationId,
|
||||
instanceId: this._instanceId++,
|
||||
listenerCount: 0,
|
||||
lastValue: undefined,
|
||||
updateCount: 0,
|
||||
changedObservables: new Set(),
|
||||
};
|
||||
this._instanceInfos.set(observable, info);
|
||||
}
|
||||
handleOnListenerCountChanged(observable, newCount) {
|
||||
const info = this._getObservableInfo(observable);
|
||||
if (!info) {
|
||||
return;
|
||||
}
|
||||
if (info.listenerCount === 0 && newCount > 0) {
|
||||
const type = observable instanceof Derived ? 'observable/derived' : 'observable/value';
|
||||
this._aliveInstances.set(info.instanceId, observable);
|
||||
this._handleChange({
|
||||
instances: {
|
||||
[info.instanceId]: {
|
||||
instanceId: info.instanceId,
|
||||
declarationId: info.declarationId,
|
||||
formattedValue: info.lastValue,
|
||||
type,
|
||||
name: observable.debugName,
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
else if (info.listenerCount > 0 && newCount === 0) {
|
||||
this._handleChange({
|
||||
instances: { [info.instanceId]: null }
|
||||
});
|
||||
this._aliveInstances.delete(info.instanceId);
|
||||
}
|
||||
info.listenerCount = newCount;
|
||||
}
|
||||
handleObservableUpdated(observable, changeInfo) {
|
||||
if (observable instanceof Derived) {
|
||||
this._handleDerivedRecomputed(observable, changeInfo);
|
||||
return;
|
||||
}
|
||||
const info = this._getObservableInfo(observable);
|
||||
if (info) {
|
||||
if (changeInfo.didChange) {
|
||||
info.lastValue = formatValue(changeInfo.newValue, 30);
|
||||
if (info.listenerCount > 0) {
|
||||
this._handleChange({
|
||||
instances: { [info.instanceId]: { formattedValue: info.lastValue } }
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
handleAutorunCreated(autorun, location) {
|
||||
const declarationId = this._getDeclarationId('autorun', location);
|
||||
const info = {
|
||||
declarationId,
|
||||
instanceId: this._instanceId++,
|
||||
updateCount: 0,
|
||||
changedObservables: new Set(),
|
||||
};
|
||||
this._instanceInfos.set(autorun, info);
|
||||
this._aliveInstances.set(info.instanceId, autorun);
|
||||
if (info) {
|
||||
this._handleChange({
|
||||
instances: {
|
||||
[info.instanceId]: {
|
||||
instanceId: info.instanceId,
|
||||
declarationId: info.declarationId,
|
||||
runCount: 0,
|
||||
type: 'autorun',
|
||||
name: autorun.debugName,
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
handleAutorunDisposed(autorun) {
|
||||
const info = this._getAutorunInfo(autorun);
|
||||
if (!info) {
|
||||
return;
|
||||
}
|
||||
this._handleChange({
|
||||
instances: { [info.instanceId]: null }
|
||||
});
|
||||
this._instanceInfos.delete(autorun);
|
||||
this._aliveInstances.delete(info.instanceId);
|
||||
}
|
||||
handleAutorunDependencyChanged(autorun, observable, change) {
|
||||
const info = this._getAutorunInfo(autorun);
|
||||
if (!info) {
|
||||
return;
|
||||
}
|
||||
info.changedObservables.add(observable);
|
||||
}
|
||||
handleAutorunStarted(autorun) {
|
||||
}
|
||||
handleAutorunFinished(autorun) {
|
||||
const info = this._getAutorunInfo(autorun);
|
||||
if (!info) {
|
||||
return;
|
||||
}
|
||||
info.changedObservables.clear();
|
||||
info.updateCount++;
|
||||
this._handleChange({
|
||||
instances: { [info.instanceId]: { runCount: info.updateCount } }
|
||||
});
|
||||
}
|
||||
handleDerivedDependencyChanged(derived, observable, change) {
|
||||
const info = this._getObservableInfo(derived);
|
||||
if (info) {
|
||||
info.changedObservables.add(observable);
|
||||
}
|
||||
}
|
||||
_handleDerivedRecomputed(observable, changeInfo) {
|
||||
const info = this._getObservableInfo(observable);
|
||||
if (!info) {
|
||||
return;
|
||||
}
|
||||
const formattedValue = formatValue(changeInfo.newValue, 30);
|
||||
info.updateCount++;
|
||||
info.changedObservables.clear();
|
||||
info.lastValue = formattedValue;
|
||||
if (info.listenerCount > 0) {
|
||||
this._handleChange({
|
||||
instances: { [info.instanceId]: { formattedValue: formattedValue, recomputationCount: info.updateCount } }
|
||||
});
|
||||
}
|
||||
}
|
||||
handleDerivedCleared(observable) {
|
||||
const info = this._getObservableInfo(observable);
|
||||
if (!info) {
|
||||
return;
|
||||
}
|
||||
info.lastValue = undefined;
|
||||
info.changedObservables.clear();
|
||||
if (info.listenerCount > 0) {
|
||||
this._handleChange({
|
||||
instances: {
|
||||
[info.instanceId]: {
|
||||
formattedValue: undefined,
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
handleBeginTransaction(transaction) {
|
||||
this._activeTransactions.add(transaction);
|
||||
}
|
||||
handleEndTransaction(transaction) {
|
||||
this._activeTransactions.delete(transaction);
|
||||
}
|
||||
}
|
||||
|
||||
export { DevToolsLogger };
|
||||
Generated
Vendored
+57
@@ -0,0 +1,57 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
class SimpleTypedRpcConnection {
|
||||
static createClient(channelFactory, getHandler) {
|
||||
return new SimpleTypedRpcConnection(channelFactory, getHandler);
|
||||
}
|
||||
constructor(_channelFactory, _getHandler) {
|
||||
this._channelFactory = _channelFactory;
|
||||
this._getHandler = _getHandler;
|
||||
this._channel = this._channelFactory({
|
||||
handleNotification: (notificationData) => {
|
||||
const m = notificationData;
|
||||
const fn = this._getHandler().notifications[m[0]];
|
||||
if (!fn) {
|
||||
throw new Error(`Unknown notification "${m[0]}"!`);
|
||||
}
|
||||
fn(...m[1]);
|
||||
},
|
||||
handleRequest: (requestData) => {
|
||||
const m = requestData;
|
||||
try {
|
||||
const result = this._getHandler().requests[m[0]](...m[1]);
|
||||
return { type: 'result', value: result };
|
||||
}
|
||||
catch (e) {
|
||||
return { type: 'error', value: e };
|
||||
}
|
||||
},
|
||||
});
|
||||
const requests = new Proxy({}, {
|
||||
get: (target, key) => {
|
||||
return async (...args) => {
|
||||
const result = await this._channel.sendRequest([key, args]);
|
||||
if (result.type === 'error') {
|
||||
throw result.value;
|
||||
}
|
||||
else {
|
||||
return result.value;
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
const notifications = new Proxy({}, {
|
||||
get: (target, key) => {
|
||||
return (...args) => {
|
||||
this._channel.sendNotification([key, args]);
|
||||
};
|
||||
}
|
||||
});
|
||||
// eslint-disable-next-line local/code-no-any-casts
|
||||
this.api = { notifications: notifications, requests: requests };
|
||||
}
|
||||
}
|
||||
|
||||
export { SimpleTypedRpcConnection };
|
||||
Generated
Vendored
+47
@@ -0,0 +1,47 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
class Throttler {
|
||||
constructor() {
|
||||
this._timeout = undefined;
|
||||
}
|
||||
throttle(fn, timeoutMs) {
|
||||
if (this._timeout === undefined) {
|
||||
this._timeout = setTimeout(() => {
|
||||
this._timeout = undefined;
|
||||
fn();
|
||||
}, timeoutMs);
|
||||
}
|
||||
}
|
||||
dispose() {
|
||||
if (this._timeout !== undefined) {
|
||||
clearTimeout(this._timeout);
|
||||
}
|
||||
}
|
||||
}
|
||||
function deepAssign(target, source) {
|
||||
for (const key in source) {
|
||||
if (!!target[key] && typeof target[key] === 'object' && !!source[key] && typeof source[key] === 'object') {
|
||||
deepAssign(target[key], source[key]);
|
||||
}
|
||||
else {
|
||||
target[key] = source[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
function deepAssignDeleteNulls(target, source) {
|
||||
for (const key in source) {
|
||||
if (source[key] === null) {
|
||||
delete target[key];
|
||||
}
|
||||
else if (!!target[key] && typeof target[key] === 'object' && !!source[key] && typeof source[key] === 'object') {
|
||||
deepAssignDeleteNulls(target[key], source[key]);
|
||||
}
|
||||
else {
|
||||
target[key] = source[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export { Throttler, deepAssign, deepAssignDeleteNulls };
|
||||
Generated
Vendored
+86
@@ -0,0 +1,86 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
let globalObservableLogger;
|
||||
function addLogger(logger) {
|
||||
if (!globalObservableLogger) {
|
||||
globalObservableLogger = logger;
|
||||
}
|
||||
else if (globalObservableLogger instanceof ComposedLogger) {
|
||||
globalObservableLogger.loggers.push(logger);
|
||||
}
|
||||
else {
|
||||
globalObservableLogger = new ComposedLogger([globalObservableLogger, logger]);
|
||||
}
|
||||
}
|
||||
function getLogger() {
|
||||
return globalObservableLogger;
|
||||
}
|
||||
class ComposedLogger {
|
||||
constructor(loggers) {
|
||||
this.loggers = loggers;
|
||||
}
|
||||
handleObservableCreated(observable, location) {
|
||||
for (const logger of this.loggers) {
|
||||
logger.handleObservableCreated(observable, location);
|
||||
}
|
||||
}
|
||||
handleOnListenerCountChanged(observable, newCount) {
|
||||
for (const logger of this.loggers) {
|
||||
logger.handleOnListenerCountChanged(observable, newCount);
|
||||
}
|
||||
}
|
||||
handleObservableUpdated(observable, info) {
|
||||
for (const logger of this.loggers) {
|
||||
logger.handleObservableUpdated(observable, info);
|
||||
}
|
||||
}
|
||||
handleAutorunCreated(autorun, location) {
|
||||
for (const logger of this.loggers) {
|
||||
logger.handleAutorunCreated(autorun, location);
|
||||
}
|
||||
}
|
||||
handleAutorunDisposed(autorun) {
|
||||
for (const logger of this.loggers) {
|
||||
logger.handleAutorunDisposed(autorun);
|
||||
}
|
||||
}
|
||||
handleAutorunDependencyChanged(autorun, observable, change) {
|
||||
for (const logger of this.loggers) {
|
||||
logger.handleAutorunDependencyChanged(autorun, observable, change);
|
||||
}
|
||||
}
|
||||
handleAutorunStarted(autorun) {
|
||||
for (const logger of this.loggers) {
|
||||
logger.handleAutorunStarted(autorun);
|
||||
}
|
||||
}
|
||||
handleAutorunFinished(autorun) {
|
||||
for (const logger of this.loggers) {
|
||||
logger.handleAutorunFinished(autorun);
|
||||
}
|
||||
}
|
||||
handleDerivedDependencyChanged(derived, observable, change) {
|
||||
for (const logger of this.loggers) {
|
||||
logger.handleDerivedDependencyChanged(derived, observable, change);
|
||||
}
|
||||
}
|
||||
handleDerivedCleared(observable) {
|
||||
for (const logger of this.loggers) {
|
||||
logger.handleDerivedCleared(observable);
|
||||
}
|
||||
}
|
||||
handleBeginTransaction(transaction) {
|
||||
for (const logger of this.loggers) {
|
||||
logger.handleBeginTransaction(transaction);
|
||||
}
|
||||
}
|
||||
handleEndTransaction(transaction) {
|
||||
for (const logger of this.loggers) {
|
||||
logger.handleEndTransaction(transaction);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export { addLogger, getLogger };
|
||||
Generated
Vendored
+106
@@ -0,0 +1,106 @@
|
||||
import { DebugLocation } from '../debugLocation.js';
|
||||
import { getFunctionName } from '../debugName.js';
|
||||
import { getLogger } from '../logging/logging.js';
|
||||
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
let _derived;
|
||||
/**
|
||||
* @internal
|
||||
* This is to allow splitting files.
|
||||
*/
|
||||
function _setDerivedOpts(derived) {
|
||||
_derived = derived;
|
||||
}
|
||||
let _recomputeInitiallyAndOnChange;
|
||||
function _setRecomputeInitiallyAndOnChange(recomputeInitiallyAndOnChange) {
|
||||
_recomputeInitiallyAndOnChange = recomputeInitiallyAndOnChange;
|
||||
}
|
||||
class ConvenientObservable {
|
||||
get TChange() { return null; }
|
||||
reportChanges() {
|
||||
this.get();
|
||||
}
|
||||
/** @sealed */
|
||||
read(reader) {
|
||||
if (reader) {
|
||||
return reader.readObservable(this);
|
||||
}
|
||||
else {
|
||||
return this.get();
|
||||
}
|
||||
}
|
||||
map(fnOrOwner, fnOrUndefined, debugLocation = DebugLocation.ofCaller()) {
|
||||
const owner = fnOrUndefined === undefined ? undefined : fnOrOwner;
|
||||
const fn = fnOrUndefined === undefined ? fnOrOwner : fnOrUndefined;
|
||||
return _derived({
|
||||
owner,
|
||||
debugName: () => {
|
||||
const name = getFunctionName(fn);
|
||||
if (name !== undefined) {
|
||||
return name;
|
||||
}
|
||||
// regexp to match `x => x.y` or `x => x?.y` where x and y can be arbitrary identifiers (uses backref):
|
||||
const regexp = /^\s*\(?\s*([a-zA-Z_$][a-zA-Z_$0-9]*)\s*\)?\s*=>\s*\1(?:\??)\.([a-zA-Z_$][a-zA-Z_$0-9]*)\s*$/;
|
||||
const match = regexp.exec(fn.toString());
|
||||
if (match) {
|
||||
return `${this.debugName}.${match[2]}`;
|
||||
}
|
||||
if (!owner) {
|
||||
return `${this.debugName} (mapped)`;
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
debugReferenceFn: fn,
|
||||
}, (reader) => fn(this.read(reader), reader), debugLocation);
|
||||
}
|
||||
/**
|
||||
* @sealed
|
||||
* Converts an observable of an observable value into a direct observable of the value.
|
||||
*/
|
||||
flatten() {
|
||||
return _derived({
|
||||
owner: undefined,
|
||||
debugName: () => `${this.debugName} (flattened)`,
|
||||
}, (reader) => this.read(reader).read(reader));
|
||||
}
|
||||
recomputeInitiallyAndOnChange(store, handleValue) {
|
||||
store.add(_recomputeInitiallyAndOnChange(this, handleValue));
|
||||
return this;
|
||||
}
|
||||
}
|
||||
class BaseObservable extends ConvenientObservable {
|
||||
constructor(debugLocation) {
|
||||
super();
|
||||
this._observers = new Set();
|
||||
getLogger()?.handleObservableCreated(this, debugLocation);
|
||||
}
|
||||
addObserver(observer) {
|
||||
const len = this._observers.size;
|
||||
this._observers.add(observer);
|
||||
if (len === 0) {
|
||||
this.onFirstObserverAdded();
|
||||
}
|
||||
if (len !== this._observers.size) {
|
||||
getLogger()?.handleOnListenerCountChanged(this, this._observers.size);
|
||||
}
|
||||
}
|
||||
removeObserver(observer) {
|
||||
const deleted = this._observers.delete(observer);
|
||||
if (deleted && this._observers.size === 0) {
|
||||
this.onLastObserverRemoved();
|
||||
}
|
||||
if (deleted) {
|
||||
getLogger()?.handleOnListenerCountChanged(this, this._observers.size);
|
||||
}
|
||||
}
|
||||
onFirstObserverAdded() { }
|
||||
onLastObserverRemoved() { }
|
||||
debugGetObservers() {
|
||||
return this._observers;
|
||||
}
|
||||
}
|
||||
|
||||
export { BaseObservable, ConvenientObservable, _setDerivedOpts, _setRecomputeInitiallyAndOnChange };
|
||||
Generated
Vendored
+35
@@ -0,0 +1,35 @@
|
||||
import { ConvenientObservable } from './baseObservable.js';
|
||||
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
/**
|
||||
* Represents an efficient observable whose value never changes.
|
||||
*/
|
||||
function constObservable(value) {
|
||||
return new ConstObservable(value);
|
||||
}
|
||||
class ConstObservable extends ConvenientObservable {
|
||||
constructor(value) {
|
||||
super();
|
||||
this.value = value;
|
||||
}
|
||||
get debugName() {
|
||||
return this.toString();
|
||||
}
|
||||
get() {
|
||||
return this.value;
|
||||
}
|
||||
addObserver(observer) {
|
||||
// NO OP
|
||||
}
|
||||
removeObserver(observer) {
|
||||
// NO OP
|
||||
}
|
||||
toString() {
|
||||
return `Const: ${this.value}`;
|
||||
}
|
||||
}
|
||||
|
||||
export { constObservable };
|
||||
Generated
Vendored
+80
@@ -0,0 +1,80 @@
|
||||
import { strictEquals } from '../../equals.js';
|
||||
import '../../event.js';
|
||||
import { DisposableStore } from '../../lifecycle.js';
|
||||
import { DebugLocation } from '../debugLocation.js';
|
||||
import { DebugNameData } from '../debugName.js';
|
||||
import { _setDerivedOpts } from './baseObservable.js';
|
||||
import { Derived, DerivedWithSetter } from './derivedImpl.js';
|
||||
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
function derived(computeFnOrOwner, computeFn, debugLocation = DebugLocation.ofCaller()) {
|
||||
if (computeFn !== undefined) {
|
||||
return new Derived(new DebugNameData(computeFnOrOwner, undefined, computeFn), computeFn, undefined, undefined, strictEquals, debugLocation);
|
||||
}
|
||||
return new Derived(
|
||||
// eslint-disable-next-line local/code-no-any-casts
|
||||
new DebugNameData(undefined, undefined, computeFnOrOwner),
|
||||
// eslint-disable-next-line local/code-no-any-casts
|
||||
computeFnOrOwner, undefined, undefined, strictEquals, debugLocation);
|
||||
}
|
||||
function derivedWithSetter(owner, computeFn, setter, debugLocation = DebugLocation.ofCaller()) {
|
||||
return new DerivedWithSetter(new DebugNameData(owner, undefined, computeFn), computeFn, undefined, undefined, strictEquals, setter, debugLocation);
|
||||
}
|
||||
function derivedOpts(options, computeFn, debugLocation = DebugLocation.ofCaller()) {
|
||||
return new Derived(new DebugNameData(options.owner, options.debugName, options.debugReferenceFn), computeFn, undefined, options.onLastObserverRemoved, options.equalsFn ?? strictEquals, debugLocation);
|
||||
}
|
||||
_setDerivedOpts(derivedOpts);
|
||||
/**
|
||||
* Represents an observable that is derived from other observables.
|
||||
* The value is only recomputed when absolutely needed.
|
||||
*
|
||||
* {@link computeFn} should start with a JS Doc using `@description` to name the derived.
|
||||
*
|
||||
* Use `createEmptyChangeSummary` to create a "change summary" that can collect the changes.
|
||||
* Use `handleChange` to add a reported change to the change summary.
|
||||
* The compute function is given the last change summary.
|
||||
* The change summary is discarded after the compute function was called.
|
||||
*
|
||||
* @see derived
|
||||
*/
|
||||
function derivedHandleChanges(options, computeFn, debugLocation = DebugLocation.ofCaller()) {
|
||||
return new Derived(new DebugNameData(options.owner, options.debugName, undefined), computeFn, options.changeTracker, undefined, options.equalityComparer ?? strictEquals, debugLocation);
|
||||
}
|
||||
function derivedDisposable(computeFnOrOwner, computeFnOrUndefined, debugLocation = DebugLocation.ofCaller()) {
|
||||
let computeFn;
|
||||
let owner;
|
||||
if (computeFnOrUndefined === undefined) {
|
||||
// eslint-disable-next-line local/code-no-any-casts
|
||||
computeFn = computeFnOrOwner;
|
||||
owner = undefined;
|
||||
}
|
||||
else {
|
||||
owner = computeFnOrOwner;
|
||||
// eslint-disable-next-line local/code-no-any-casts
|
||||
computeFn = computeFnOrUndefined;
|
||||
}
|
||||
let store = undefined;
|
||||
return new Derived(new DebugNameData(owner, undefined, computeFn), r => {
|
||||
if (!store) {
|
||||
store = new DisposableStore();
|
||||
}
|
||||
else {
|
||||
store.clear();
|
||||
}
|
||||
const result = computeFn(r);
|
||||
if (result) {
|
||||
store.add(result);
|
||||
}
|
||||
return result;
|
||||
}, undefined, () => {
|
||||
if (store) {
|
||||
store.dispose();
|
||||
store = undefined;
|
||||
}
|
||||
}, strictEquals, debugLocation);
|
||||
}
|
||||
|
||||
export { derived, derivedDisposable, derivedHandleChanges, derivedOpts, derivedWithSetter };
|
||||
frontend/node_modules/monaco-editor/esm/vs/base/common/observableInternal/observables/derivedImpl.js
Generated
Vendored
+353
@@ -0,0 +1,353 @@
|
||||
import { BaseObservable } from './baseObservable.js';
|
||||
import { assertFn } from '../../assert.js';
|
||||
import '../../arrays.js';
|
||||
import { onBugIndicatingError, BugIndicatingError } from '../../errors.js';
|
||||
import '../../event.js';
|
||||
import { DisposableStore } from '../../lifecycle.js';
|
||||
import { getLogger } from '../logging/logging.js';
|
||||
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
function derivedStateToString(state) {
|
||||
switch (state) {
|
||||
case 0 /* DerivedState.initial */: return 'initial';
|
||||
case 1 /* DerivedState.dependenciesMightHaveChanged */: return 'dependenciesMightHaveChanged';
|
||||
case 2 /* DerivedState.stale */: return 'stale';
|
||||
case 3 /* DerivedState.upToDate */: return 'upToDate';
|
||||
default: return '<unknown>';
|
||||
}
|
||||
}
|
||||
class Derived extends BaseObservable {
|
||||
get debugName() {
|
||||
return this._debugNameData.getDebugName(this) ?? '(anonymous)';
|
||||
}
|
||||
constructor(_debugNameData, _computeFn, _changeTracker, _handleLastObserverRemoved = undefined, _equalityComparator, debugLocation) {
|
||||
super(debugLocation);
|
||||
this._debugNameData = _debugNameData;
|
||||
this._computeFn = _computeFn;
|
||||
this._changeTracker = _changeTracker;
|
||||
this._handleLastObserverRemoved = _handleLastObserverRemoved;
|
||||
this._equalityComparator = _equalityComparator;
|
||||
this._state = 0 /* DerivedState.initial */;
|
||||
this._value = undefined;
|
||||
this._updateCount = 0;
|
||||
this._dependencies = new Set();
|
||||
this._dependenciesToBeRemoved = new Set();
|
||||
this._changeSummary = undefined;
|
||||
this._isUpdating = false;
|
||||
this._isComputing = false;
|
||||
this._didReportChange = false;
|
||||
this._isInBeforeUpdate = false;
|
||||
this._isReaderValid = false;
|
||||
this._store = undefined;
|
||||
this._delayedStore = undefined;
|
||||
this._removedObserverToCallEndUpdateOn = null;
|
||||
this._changeSummary = this._changeTracker?.createChangeSummary(undefined);
|
||||
}
|
||||
onLastObserverRemoved() {
|
||||
/**
|
||||
* We are not tracking changes anymore, thus we have to assume
|
||||
* that our cache is invalid.
|
||||
*/
|
||||
this._state = 0 /* DerivedState.initial */;
|
||||
this._value = undefined;
|
||||
getLogger()?.handleDerivedCleared(this);
|
||||
for (const d of this._dependencies) {
|
||||
d.removeObserver(this);
|
||||
}
|
||||
this._dependencies.clear();
|
||||
if (this._store !== undefined) {
|
||||
this._store.dispose();
|
||||
this._store = undefined;
|
||||
}
|
||||
if (this._delayedStore !== undefined) {
|
||||
this._delayedStore.dispose();
|
||||
this._delayedStore = undefined;
|
||||
}
|
||||
this._handleLastObserverRemoved?.();
|
||||
}
|
||||
get() {
|
||||
const checkEnabled = false; // TODO set to true
|
||||
if (this._isComputing && checkEnabled) ;
|
||||
if (this._observers.size === 0) {
|
||||
let result;
|
||||
// Without observers, we don't know when to clean up stuff.
|
||||
// Thus, we don't cache anything to prevent memory leaks.
|
||||
try {
|
||||
this._isReaderValid = true;
|
||||
let changeSummary = undefined;
|
||||
if (this._changeTracker) {
|
||||
changeSummary = this._changeTracker.createChangeSummary(undefined);
|
||||
this._changeTracker.beforeUpdate?.(this, changeSummary);
|
||||
}
|
||||
result = this._computeFn(this, changeSummary);
|
||||
}
|
||||
finally {
|
||||
this._isReaderValid = false;
|
||||
}
|
||||
// Clear new dependencies
|
||||
this.onLastObserverRemoved();
|
||||
return result;
|
||||
}
|
||||
else {
|
||||
do {
|
||||
// We might not get a notification for a dependency that changed while it is updating,
|
||||
// thus we also have to ask all our depedencies if they changed in this case.
|
||||
if (this._state === 1 /* DerivedState.dependenciesMightHaveChanged */) {
|
||||
for (const d of this._dependencies) {
|
||||
/** might call {@link handleChange} indirectly, which could make us stale */
|
||||
d.reportChanges();
|
||||
if (this._state === 2 /* DerivedState.stale */) {
|
||||
// The other dependencies will refresh on demand, so early break
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// We called report changes of all dependencies.
|
||||
// If we are still not stale, we can assume to be up to date again.
|
||||
if (this._state === 1 /* DerivedState.dependenciesMightHaveChanged */) {
|
||||
this._state = 3 /* DerivedState.upToDate */;
|
||||
}
|
||||
if (this._state !== 3 /* DerivedState.upToDate */) {
|
||||
this._recompute();
|
||||
}
|
||||
// In case recomputation changed one of our dependencies, we need to recompute again.
|
||||
} while (this._state !== 3 /* DerivedState.upToDate */);
|
||||
return this._value;
|
||||
}
|
||||
}
|
||||
_recompute() {
|
||||
let didChange = false;
|
||||
this._isComputing = true;
|
||||
this._didReportChange = false;
|
||||
const emptySet = this._dependenciesToBeRemoved;
|
||||
this._dependenciesToBeRemoved = this._dependencies;
|
||||
this._dependencies = emptySet;
|
||||
try {
|
||||
const changeSummary = this._changeSummary;
|
||||
this._isReaderValid = true;
|
||||
if (this._changeTracker) {
|
||||
this._isInBeforeUpdate = true;
|
||||
this._changeTracker.beforeUpdate?.(this, changeSummary);
|
||||
this._isInBeforeUpdate = false;
|
||||
this._changeSummary = this._changeTracker?.createChangeSummary(changeSummary);
|
||||
}
|
||||
const hadValue = this._state !== 0 /* DerivedState.initial */;
|
||||
const oldValue = this._value;
|
||||
this._state = 3 /* DerivedState.upToDate */;
|
||||
const delayedStore = this._delayedStore;
|
||||
if (delayedStore !== undefined) {
|
||||
this._delayedStore = undefined;
|
||||
}
|
||||
try {
|
||||
if (this._store !== undefined) {
|
||||
this._store.dispose();
|
||||
this._store = undefined;
|
||||
}
|
||||
/** might call {@link handleChange} indirectly, which could invalidate us */
|
||||
this._value = this._computeFn(this, changeSummary);
|
||||
}
|
||||
finally {
|
||||
this._isReaderValid = false;
|
||||
// We don't want our observed observables to think that they are (not even temporarily) not being observed.
|
||||
// Thus, we only unsubscribe from observables that are definitely not read anymore.
|
||||
for (const o of this._dependenciesToBeRemoved) {
|
||||
o.removeObserver(this);
|
||||
}
|
||||
this._dependenciesToBeRemoved.clear();
|
||||
if (delayedStore !== undefined) {
|
||||
delayedStore.dispose();
|
||||
}
|
||||
}
|
||||
didChange = this._didReportChange || (hadValue && !(this._equalityComparator(oldValue, this._value)));
|
||||
getLogger()?.handleObservableUpdated(this, {
|
||||
oldValue,
|
||||
newValue: this._value,
|
||||
change: undefined,
|
||||
didChange,
|
||||
hadValue,
|
||||
});
|
||||
}
|
||||
catch (e) {
|
||||
onBugIndicatingError(e);
|
||||
}
|
||||
this._isComputing = false;
|
||||
if (!this._didReportChange && didChange) {
|
||||
for (const r of this._observers) {
|
||||
r.handleChange(this, undefined);
|
||||
}
|
||||
}
|
||||
else {
|
||||
this._didReportChange = false;
|
||||
}
|
||||
}
|
||||
toString() {
|
||||
return `LazyDerived<${this.debugName}>`;
|
||||
}
|
||||
// IObserver Implementation
|
||||
beginUpdate(_observable) {
|
||||
if (this._isUpdating) {
|
||||
throw new BugIndicatingError('Cyclic deriveds are not supported yet!');
|
||||
}
|
||||
this._updateCount++;
|
||||
this._isUpdating = true;
|
||||
try {
|
||||
const propagateBeginUpdate = this._updateCount === 1;
|
||||
if (this._state === 3 /* DerivedState.upToDate */) {
|
||||
this._state = 1 /* DerivedState.dependenciesMightHaveChanged */;
|
||||
// If we propagate begin update, that will already signal a possible change.
|
||||
if (!propagateBeginUpdate) {
|
||||
for (const r of this._observers) {
|
||||
r.handlePossibleChange(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (propagateBeginUpdate) {
|
||||
for (const r of this._observers) {
|
||||
r.beginUpdate(this); // This signals a possible change
|
||||
}
|
||||
}
|
||||
}
|
||||
finally {
|
||||
this._isUpdating = false;
|
||||
}
|
||||
}
|
||||
endUpdate(_observable) {
|
||||
this._updateCount--;
|
||||
if (this._updateCount === 0) {
|
||||
// End update could change the observer list.
|
||||
const observers = [...this._observers];
|
||||
for (const r of observers) {
|
||||
r.endUpdate(this);
|
||||
}
|
||||
if (this._removedObserverToCallEndUpdateOn) {
|
||||
const observers = [...this._removedObserverToCallEndUpdateOn];
|
||||
this._removedObserverToCallEndUpdateOn = null;
|
||||
for (const r of observers) {
|
||||
r.endUpdate(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
assertFn(() => this._updateCount >= 0);
|
||||
}
|
||||
handlePossibleChange(observable) {
|
||||
// In all other states, observers already know that we might have changed.
|
||||
if (this._state === 3 /* DerivedState.upToDate */ && this._dependencies.has(observable) && !this._dependenciesToBeRemoved.has(observable)) {
|
||||
this._state = 1 /* DerivedState.dependenciesMightHaveChanged */;
|
||||
for (const r of this._observers) {
|
||||
r.handlePossibleChange(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
handleChange(observable, change) {
|
||||
if (this._dependencies.has(observable) && !this._dependenciesToBeRemoved.has(observable) || this._isInBeforeUpdate) {
|
||||
getLogger()?.handleDerivedDependencyChanged(this, observable, change);
|
||||
let shouldReact = false;
|
||||
try {
|
||||
shouldReact = this._changeTracker ? this._changeTracker.handleChange({
|
||||
changedObservable: observable,
|
||||
change,
|
||||
// eslint-disable-next-line local/code-no-any-casts
|
||||
didChange: (o) => o === observable,
|
||||
}, this._changeSummary) : true;
|
||||
}
|
||||
catch (e) {
|
||||
onBugIndicatingError(e);
|
||||
}
|
||||
const wasUpToDate = this._state === 3 /* DerivedState.upToDate */;
|
||||
if (shouldReact && (this._state === 1 /* DerivedState.dependenciesMightHaveChanged */ || wasUpToDate)) {
|
||||
this._state = 2 /* DerivedState.stale */;
|
||||
if (wasUpToDate) {
|
||||
for (const r of this._observers) {
|
||||
r.handlePossibleChange(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// IReader Implementation
|
||||
_ensureReaderValid() {
|
||||
if (!this._isReaderValid) {
|
||||
throw new BugIndicatingError('The reader object cannot be used outside its compute function!');
|
||||
}
|
||||
}
|
||||
readObservable(observable) {
|
||||
this._ensureReaderValid();
|
||||
// Subscribe before getting the value to enable caching
|
||||
observable.addObserver(this);
|
||||
/** This might call {@link handleChange} indirectly, which could invalidate us */
|
||||
const value = observable.get();
|
||||
// Which is why we only add the observable to the dependencies now.
|
||||
this._dependencies.add(observable);
|
||||
this._dependenciesToBeRemoved.delete(observable);
|
||||
return value;
|
||||
}
|
||||
get store() {
|
||||
this._ensureReaderValid();
|
||||
if (this._store === undefined) {
|
||||
this._store = new DisposableStore();
|
||||
}
|
||||
return this._store;
|
||||
}
|
||||
addObserver(observer) {
|
||||
const shouldCallBeginUpdate = !this._observers.has(observer) && this._updateCount > 0;
|
||||
super.addObserver(observer);
|
||||
if (shouldCallBeginUpdate) {
|
||||
if (this._removedObserverToCallEndUpdateOn && this._removedObserverToCallEndUpdateOn.has(observer)) {
|
||||
this._removedObserverToCallEndUpdateOn.delete(observer);
|
||||
}
|
||||
else {
|
||||
observer.beginUpdate(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
removeObserver(observer) {
|
||||
if (this._observers.has(observer) && this._updateCount > 0) {
|
||||
if (!this._removedObserverToCallEndUpdateOn) {
|
||||
this._removedObserverToCallEndUpdateOn = new Set();
|
||||
}
|
||||
this._removedObserverToCallEndUpdateOn.add(observer);
|
||||
}
|
||||
super.removeObserver(observer);
|
||||
}
|
||||
debugGetState() {
|
||||
return {
|
||||
state: this._state,
|
||||
stateStr: derivedStateToString(this._state),
|
||||
updateCount: this._updateCount,
|
||||
isComputing: this._isComputing,
|
||||
dependencies: this._dependencies,
|
||||
value: this._value,
|
||||
};
|
||||
}
|
||||
debugSetValue(newValue) {
|
||||
// eslint-disable-next-line local/code-no-any-casts
|
||||
this._value = newValue;
|
||||
}
|
||||
debugRecompute() {
|
||||
if (!this._isComputing) {
|
||||
this._recompute();
|
||||
}
|
||||
else {
|
||||
this._state = 2 /* DerivedState.stale */;
|
||||
}
|
||||
}
|
||||
setValue(newValue, tx, change) {
|
||||
this._value = newValue;
|
||||
const observers = this._observers;
|
||||
tx.updateObserver(this, this);
|
||||
for (const d of observers) {
|
||||
d.handleChange(this, change);
|
||||
}
|
||||
}
|
||||
}
|
||||
class DerivedWithSetter extends Derived {
|
||||
constructor(debugNameData, computeFn, changeTracker, handleLastObserverRemoved = undefined, equalityComparator, set, debugLocation) {
|
||||
super(debugNameData, computeFn, changeTracker, handleLastObserverRemoved, equalityComparator, debugLocation);
|
||||
this.set = set;
|
||||
}
|
||||
}
|
||||
|
||||
export { Derived, DerivedWithSetter };
|
||||
Generated
Vendored
+125
@@ -0,0 +1,125 @@
|
||||
import { TransactionImpl } from '../transaction.js';
|
||||
import { getLogger } from '../logging/logging.js';
|
||||
import { BaseObservable } from './baseObservable.js';
|
||||
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
/**
|
||||
* Holds off updating observers until the value is actually read.
|
||||
*/
|
||||
class LazyObservableValue extends BaseObservable {
|
||||
get debugName() {
|
||||
return this._debugNameData.getDebugName(this) ?? 'LazyObservableValue';
|
||||
}
|
||||
constructor(_debugNameData, initialValue, _equalityComparator, debugLocation) {
|
||||
super(debugLocation);
|
||||
this._debugNameData = _debugNameData;
|
||||
this._equalityComparator = _equalityComparator;
|
||||
this._isUpToDate = true;
|
||||
this._deltas = [];
|
||||
this._updateCounter = 0;
|
||||
this._value = initialValue;
|
||||
}
|
||||
get() {
|
||||
this._update();
|
||||
return this._value;
|
||||
}
|
||||
_update() {
|
||||
if (this._isUpToDate) {
|
||||
return;
|
||||
}
|
||||
this._isUpToDate = true;
|
||||
if (this._deltas.length > 0) {
|
||||
for (const change of this._deltas) {
|
||||
getLogger()?.handleObservableUpdated(this, { change, didChange: true, oldValue: '(unknown)', newValue: this._value, hadValue: true });
|
||||
for (const observer of this._observers) {
|
||||
observer.handleChange(this, change);
|
||||
}
|
||||
}
|
||||
this._deltas.length = 0;
|
||||
}
|
||||
else {
|
||||
getLogger()?.handleObservableUpdated(this, { change: undefined, didChange: true, oldValue: '(unknown)', newValue: this._value, hadValue: true });
|
||||
for (const observer of this._observers) {
|
||||
observer.handleChange(this, undefined);
|
||||
}
|
||||
}
|
||||
}
|
||||
_beginUpdate() {
|
||||
this._updateCounter++;
|
||||
if (this._updateCounter === 1) {
|
||||
for (const observer of this._observers) {
|
||||
observer.beginUpdate(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
_endUpdate() {
|
||||
this._updateCounter--;
|
||||
if (this._updateCounter === 0) {
|
||||
this._update();
|
||||
// End update could change the observer list.
|
||||
const observers = [...this._observers];
|
||||
for (const r of observers) {
|
||||
r.endUpdate(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
addObserver(observer) {
|
||||
const shouldCallBeginUpdate = !this._observers.has(observer) && this._updateCounter > 0;
|
||||
super.addObserver(observer);
|
||||
if (shouldCallBeginUpdate) {
|
||||
observer.beginUpdate(this);
|
||||
}
|
||||
}
|
||||
removeObserver(observer) {
|
||||
const shouldCallEndUpdate = this._observers.has(observer) && this._updateCounter > 0;
|
||||
super.removeObserver(observer);
|
||||
if (shouldCallEndUpdate) {
|
||||
// Calling end update after removing the observer makes sure endUpdate cannot be called twice here.
|
||||
observer.endUpdate(this);
|
||||
}
|
||||
}
|
||||
set(value, tx, change) {
|
||||
if (change === undefined && this._equalityComparator(this._value, value)) {
|
||||
return;
|
||||
}
|
||||
let _tx;
|
||||
if (!tx) {
|
||||
tx = _tx = new TransactionImpl(() => { }, () => `Setting ${this.debugName}`);
|
||||
}
|
||||
try {
|
||||
this._isUpToDate = false;
|
||||
this._setValue(value);
|
||||
if (change !== undefined) {
|
||||
this._deltas.push(change);
|
||||
}
|
||||
tx.updateObserver({
|
||||
beginUpdate: () => this._beginUpdate(),
|
||||
endUpdate: () => this._endUpdate(),
|
||||
handleChange: (observable, change) => { },
|
||||
handlePossibleChange: (observable) => { },
|
||||
}, this);
|
||||
if (this._updateCounter > 1) {
|
||||
// We already started begin/end update, so we need to manually call handlePossibleChange
|
||||
for (const observer of this._observers) {
|
||||
observer.handlePossibleChange(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
finally {
|
||||
if (_tx) {
|
||||
_tx.finish();
|
||||
}
|
||||
}
|
||||
}
|
||||
toString() {
|
||||
return `${this.debugName}: ${this._value}`;
|
||||
}
|
||||
_setValue(newValue) {
|
||||
this._value = newValue;
|
||||
}
|
||||
}
|
||||
|
||||
export { LazyObservableValue };
|
||||
Generated
Vendored
+123
@@ -0,0 +1,123 @@
|
||||
import { subtransaction } from '../transaction.js';
|
||||
import { strictEquals } from '../../equals.js';
|
||||
import '../../event.js';
|
||||
import '../../lifecycle.js';
|
||||
import { DebugNameData } from '../debugName.js';
|
||||
import { getLogger } from '../logging/logging.js';
|
||||
import { BaseObservable } from './baseObservable.js';
|
||||
import { DebugLocation } from '../debugLocation.js';
|
||||
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
function observableFromEvent(...args) {
|
||||
let owner;
|
||||
let event;
|
||||
let getValue;
|
||||
let debugLocation;
|
||||
if (args.length === 2) {
|
||||
[event, getValue] = args;
|
||||
}
|
||||
else {
|
||||
[owner, event, getValue, debugLocation] = args;
|
||||
}
|
||||
return new FromEventObservable(new DebugNameData(owner, undefined, getValue), event, getValue, () => FromEventObservable.globalTransaction, strictEquals, debugLocation ?? DebugLocation.ofCaller());
|
||||
}
|
||||
function observableFromEventOpts(options, event, getValue, debugLocation = DebugLocation.ofCaller()) {
|
||||
return new FromEventObservable(new DebugNameData(options.owner, options.debugName, options.debugReferenceFn ?? getValue), event, getValue, () => FromEventObservable.globalTransaction, options.equalsFn ?? strictEquals, debugLocation);
|
||||
}
|
||||
class FromEventObservable extends BaseObservable {
|
||||
constructor(_debugNameData, event, _getValue, _getTransaction, _equalityComparator, debugLocation) {
|
||||
super(debugLocation);
|
||||
this._debugNameData = _debugNameData;
|
||||
this.event = event;
|
||||
this._getValue = _getValue;
|
||||
this._getTransaction = _getTransaction;
|
||||
this._equalityComparator = _equalityComparator;
|
||||
this._hasValue = false;
|
||||
this.handleEvent = (args) => {
|
||||
const newValue = this._getValue(args);
|
||||
const oldValue = this._value;
|
||||
const didChange = !this._hasValue || !(this._equalityComparator(oldValue, newValue));
|
||||
let didRunTransaction = false;
|
||||
if (didChange) {
|
||||
this._value = newValue;
|
||||
if (this._hasValue) {
|
||||
didRunTransaction = true;
|
||||
subtransaction(this._getTransaction(), (tx) => {
|
||||
getLogger()?.handleObservableUpdated(this, { oldValue, newValue, change: undefined, didChange, hadValue: this._hasValue });
|
||||
for (const o of this._observers) {
|
||||
tx.updateObserver(o, this);
|
||||
o.handleChange(this, undefined);
|
||||
}
|
||||
}, () => {
|
||||
const name = this.getDebugName();
|
||||
return 'Event fired' + (name ? `: ${name}` : '');
|
||||
});
|
||||
}
|
||||
this._hasValue = true;
|
||||
}
|
||||
if (!didRunTransaction) {
|
||||
getLogger()?.handleObservableUpdated(this, { oldValue, newValue, change: undefined, didChange, hadValue: this._hasValue });
|
||||
}
|
||||
};
|
||||
}
|
||||
getDebugName() {
|
||||
return this._debugNameData.getDebugName(this);
|
||||
}
|
||||
get debugName() {
|
||||
const name = this.getDebugName();
|
||||
return 'From Event' + (name ? `: ${name}` : '');
|
||||
}
|
||||
onFirstObserverAdded() {
|
||||
this._subscription = this.event(this.handleEvent);
|
||||
}
|
||||
onLastObserverRemoved() {
|
||||
this._subscription.dispose();
|
||||
this._subscription = undefined;
|
||||
this._hasValue = false;
|
||||
this._value = undefined;
|
||||
}
|
||||
get() {
|
||||
if (this._subscription) {
|
||||
if (!this._hasValue) {
|
||||
this.handleEvent(undefined);
|
||||
}
|
||||
return this._value;
|
||||
}
|
||||
else {
|
||||
// no cache, as there are no subscribers to keep it updated
|
||||
const value = this._getValue(undefined);
|
||||
return value;
|
||||
}
|
||||
}
|
||||
debugSetValue(value) {
|
||||
// eslint-disable-next-line local/code-no-any-casts
|
||||
this._value = value;
|
||||
}
|
||||
debugGetState() {
|
||||
return { value: this._value, hasValue: this._hasValue };
|
||||
}
|
||||
}
|
||||
(function (observableFromEvent) {
|
||||
observableFromEvent.Observer = FromEventObservable;
|
||||
function batchEventsGlobally(tx, fn) {
|
||||
let didSet = false;
|
||||
if (FromEventObservable.globalTransaction === undefined) {
|
||||
FromEventObservable.globalTransaction = tx;
|
||||
didSet = true;
|
||||
}
|
||||
try {
|
||||
fn();
|
||||
}
|
||||
finally {
|
||||
if (didSet) {
|
||||
FromEventObservable.globalTransaction = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
observableFromEvent.batchEventsGlobally = batchEventsGlobally;
|
||||
})(observableFromEvent || (observableFromEvent = {}));
|
||||
|
||||
export { FromEventObservable, observableFromEvent, observableFromEventOpts };
|
||||
Generated
Vendored
+47
@@ -0,0 +1,47 @@
|
||||
import { transaction } from '../transaction.js';
|
||||
import { DebugNameData } from '../debugName.js';
|
||||
import { BaseObservable } from './baseObservable.js';
|
||||
import { DebugLocation } from '../debugLocation.js';
|
||||
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
function observableSignal(debugNameOrOwner, debugLocation = DebugLocation.ofCaller()) {
|
||||
if (typeof debugNameOrOwner === 'string') {
|
||||
return new ObservableSignal(debugNameOrOwner, undefined, debugLocation);
|
||||
}
|
||||
else {
|
||||
return new ObservableSignal(undefined, debugNameOrOwner, debugLocation);
|
||||
}
|
||||
}
|
||||
class ObservableSignal extends BaseObservable {
|
||||
get debugName() {
|
||||
return new DebugNameData(this._owner, this._debugName, undefined).getDebugName(this) ?? 'Observable Signal';
|
||||
}
|
||||
toString() {
|
||||
return this.debugName;
|
||||
}
|
||||
constructor(_debugName, _owner, debugLocation) {
|
||||
super(debugLocation);
|
||||
this._debugName = _debugName;
|
||||
this._owner = _owner;
|
||||
}
|
||||
trigger(tx, change) {
|
||||
if (!tx) {
|
||||
transaction(tx => {
|
||||
this.trigger(tx, change);
|
||||
}, () => `Trigger signal ${this.debugName}`);
|
||||
return;
|
||||
}
|
||||
for (const o of this._observers) {
|
||||
tx.updateObserver(o, this);
|
||||
o.handleChange(this, change);
|
||||
}
|
||||
}
|
||||
get() {
|
||||
// NO OP
|
||||
}
|
||||
}
|
||||
|
||||
export { observableSignal };
|
||||
Generated
Vendored
+41
@@ -0,0 +1,41 @@
|
||||
import { transaction } from '../transaction.js';
|
||||
import { DebugNameData } from '../debugName.js';
|
||||
import { BaseObservable } from './baseObservable.js';
|
||||
import { DebugLocation } from '../debugLocation.js';
|
||||
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
function observableSignalFromEvent(owner, event, debugLocation = DebugLocation.ofCaller()) {
|
||||
return new FromEventObservableSignal(typeof owner === 'string' ? owner : new DebugNameData(owner, undefined, undefined), event, debugLocation);
|
||||
}
|
||||
class FromEventObservableSignal extends BaseObservable {
|
||||
constructor(debugNameDataOrName, event, debugLocation) {
|
||||
super(debugLocation);
|
||||
this.event = event;
|
||||
this.handleEvent = () => {
|
||||
transaction((tx) => {
|
||||
for (const o of this._observers) {
|
||||
tx.updateObserver(o, this);
|
||||
o.handleChange(this, undefined);
|
||||
}
|
||||
}, () => this.debugName);
|
||||
};
|
||||
this.debugName = typeof debugNameDataOrName === 'string'
|
||||
? debugNameDataOrName
|
||||
: debugNameDataOrName.getDebugName(this) ?? 'Observable Signal From Event';
|
||||
}
|
||||
onFirstObserverAdded() {
|
||||
this.subscription = this.event(this.handleEvent);
|
||||
}
|
||||
onLastObserverRemoved() {
|
||||
this.subscription.dispose();
|
||||
this.subscription = undefined;
|
||||
}
|
||||
get() {
|
||||
// NO OP
|
||||
}
|
||||
}
|
||||
|
||||
export { observableSignalFromEvent };
|
||||
Generated
Vendored
+105
@@ -0,0 +1,105 @@
|
||||
import { TransactionImpl } from '../transaction.js';
|
||||
import { BaseObservable } from './baseObservable.js';
|
||||
import { strictEquals } from '../../equals.js';
|
||||
import '../../event.js';
|
||||
import '../../lifecycle.js';
|
||||
import { DebugNameData } from '../debugName.js';
|
||||
import { getLogger } from '../logging/logging.js';
|
||||
import { DebugLocation } from '../debugLocation.js';
|
||||
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
function observableValue(nameOrOwner, initialValue, debugLocation = DebugLocation.ofCaller()) {
|
||||
let debugNameData;
|
||||
if (typeof nameOrOwner === 'string') {
|
||||
debugNameData = new DebugNameData(undefined, nameOrOwner, undefined);
|
||||
}
|
||||
else {
|
||||
debugNameData = new DebugNameData(nameOrOwner, undefined, undefined);
|
||||
}
|
||||
return new ObservableValue(debugNameData, initialValue, strictEquals, debugLocation);
|
||||
}
|
||||
class ObservableValue extends BaseObservable {
|
||||
get debugName() {
|
||||
return this._debugNameData.getDebugName(this) ?? 'ObservableValue';
|
||||
}
|
||||
constructor(_debugNameData, initialValue, _equalityComparator, debugLocation) {
|
||||
super(debugLocation);
|
||||
this._debugNameData = _debugNameData;
|
||||
this._equalityComparator = _equalityComparator;
|
||||
this._value = initialValue;
|
||||
getLogger()?.handleObservableUpdated(this, { hadValue: false, newValue: initialValue, change: undefined, didChange: true, oldValue: undefined });
|
||||
}
|
||||
get() {
|
||||
return this._value;
|
||||
}
|
||||
set(value, tx, change) {
|
||||
if (change === undefined && this._equalityComparator(this._value, value)) {
|
||||
return;
|
||||
}
|
||||
let _tx;
|
||||
if (!tx) {
|
||||
tx = _tx = new TransactionImpl(() => { }, () => `Setting ${this.debugName}`);
|
||||
}
|
||||
try {
|
||||
const oldValue = this._value;
|
||||
this._setValue(value);
|
||||
getLogger()?.handleObservableUpdated(this, { oldValue, newValue: value, change, didChange: true, hadValue: true });
|
||||
for (const observer of this._observers) {
|
||||
tx.updateObserver(observer, this);
|
||||
observer.handleChange(this, change);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
if (_tx) {
|
||||
_tx.finish();
|
||||
}
|
||||
}
|
||||
}
|
||||
toString() {
|
||||
return `${this.debugName}: ${this._value}`;
|
||||
}
|
||||
_setValue(newValue) {
|
||||
this._value = newValue;
|
||||
}
|
||||
debugGetState() {
|
||||
return {
|
||||
value: this._value,
|
||||
};
|
||||
}
|
||||
debugSetValue(value) {
|
||||
this._value = value;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* A disposable observable. When disposed, its value is also disposed.
|
||||
* When a new value is set, the previous value is disposed.
|
||||
*/
|
||||
function disposableObservableValue(nameOrOwner, initialValue, debugLocation = DebugLocation.ofCaller()) {
|
||||
let debugNameData;
|
||||
if (typeof nameOrOwner === 'string') {
|
||||
debugNameData = new DebugNameData(undefined, nameOrOwner, undefined);
|
||||
}
|
||||
else {
|
||||
debugNameData = new DebugNameData(nameOrOwner, undefined, undefined);
|
||||
}
|
||||
return new DisposableObservableValue(debugNameData, initialValue, strictEquals, debugLocation);
|
||||
}
|
||||
class DisposableObservableValue extends ObservableValue {
|
||||
_setValue(newValue) {
|
||||
if (this._value === newValue) {
|
||||
return;
|
||||
}
|
||||
if (this._value) {
|
||||
this._value.dispose();
|
||||
}
|
||||
this._value = newValue;
|
||||
}
|
||||
dispose() {
|
||||
this._value?.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
export { DisposableObservableValue, ObservableValue, disposableObservableValue, observableValue };
|
||||
Generated
Vendored
+20
@@ -0,0 +1,20 @@
|
||||
import { DebugNameData } from '../debugName.js';
|
||||
import { strictEquals } from '../../equals.js';
|
||||
import '../../event.js';
|
||||
import '../../lifecycle.js';
|
||||
import { ObservableValue } from './observableValue.js';
|
||||
import { LazyObservableValue } from './lazyObservableValue.js';
|
||||
import { DebugLocation } from '../debugLocation.js';
|
||||
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
function observableValueOpts(options, initialValue, debugLocation = DebugLocation.ofCaller()) {
|
||||
if (options.lazy) {
|
||||
return new LazyObservableValue(new DebugNameData(options.owner, options.debugName, undefined), initialValue, options.equalsFn ?? strictEquals, debugLocation);
|
||||
}
|
||||
return new ObservableValue(new DebugNameData(options.owner, options.debugName, undefined), initialValue, options.equalsFn ?? strictEquals, debugLocation);
|
||||
}
|
||||
|
||||
export { observableValueOpts };
|
||||
Generated
Vendored
+89
@@ -0,0 +1,89 @@
|
||||
import '../../arrays.js';
|
||||
import '../../event.js';
|
||||
import { toDisposable, DisposableStore } from '../../lifecycle.js';
|
||||
import { DebugNameData } from '../debugName.js';
|
||||
import { AutorunObserver } from './autorunImpl.js';
|
||||
import { DebugLocation } from '../debugLocation.js';
|
||||
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
/**
|
||||
* Runs immediately and whenever a transaction ends and an observed observable changed.
|
||||
* {@link fn} should start with a JS Doc using `@description` to name the autorun.
|
||||
*/
|
||||
function autorun(fn, debugLocation = DebugLocation.ofCaller()) {
|
||||
return new AutorunObserver(new DebugNameData(undefined, undefined, fn), fn, undefined, debugLocation);
|
||||
}
|
||||
/**
|
||||
* Runs immediately and whenever a transaction ends and an observed observable changed.
|
||||
* {@link fn} should start with a JS Doc using `@description` to name the autorun.
|
||||
*/
|
||||
function autorunOpts(options, fn, debugLocation = DebugLocation.ofCaller()) {
|
||||
return new AutorunObserver(new DebugNameData(options.owner, options.debugName, options.debugReferenceFn ?? fn), fn, undefined, debugLocation);
|
||||
}
|
||||
/**
|
||||
* Runs immediately and whenever a transaction ends and an observed observable changed.
|
||||
* {@link fn} should start with a JS Doc using `@description` to name the autorun.
|
||||
*
|
||||
* Use `changeTracker.createChangeSummary` to create a "change summary" that can collect the changes.
|
||||
* Use `changeTracker.handleChange` to add a reported change to the change summary.
|
||||
* The run function is given the last change summary.
|
||||
* The change summary is discarded after the run function was called.
|
||||
*
|
||||
* @see autorun
|
||||
*/
|
||||
function autorunHandleChanges(options, fn, debugLocation = DebugLocation.ofCaller()) {
|
||||
return new AutorunObserver(new DebugNameData(options.owner, options.debugName, options.debugReferenceFn ?? fn), fn, options.changeTracker, debugLocation);
|
||||
}
|
||||
/**
|
||||
* @see autorunHandleChanges (but with a disposable store that is cleared before the next run or on dispose)
|
||||
*/
|
||||
function autorunWithStoreHandleChanges(options, fn) {
|
||||
const store = new DisposableStore();
|
||||
const disposable = autorunHandleChanges({
|
||||
owner: options.owner,
|
||||
debugName: options.debugName,
|
||||
debugReferenceFn: options.debugReferenceFn ?? fn,
|
||||
changeTracker: options.changeTracker,
|
||||
}, (reader, changeSummary) => {
|
||||
store.clear();
|
||||
fn(reader, changeSummary, store);
|
||||
});
|
||||
return toDisposable(() => {
|
||||
disposable.dispose();
|
||||
store.dispose();
|
||||
});
|
||||
}
|
||||
/**
|
||||
* @see autorun (but with a disposable store that is cleared before the next run or on dispose)
|
||||
*
|
||||
* @deprecated Use `autorun(reader => { reader.store.add(...) })` instead!
|
||||
*/
|
||||
function autorunWithStore(fn) {
|
||||
const store = new DisposableStore();
|
||||
const disposable = autorunOpts({
|
||||
owner: undefined,
|
||||
debugName: undefined,
|
||||
debugReferenceFn: fn,
|
||||
}, reader => {
|
||||
store.clear();
|
||||
fn(reader, store);
|
||||
});
|
||||
return toDisposable(() => {
|
||||
disposable.dispose();
|
||||
store.dispose();
|
||||
});
|
||||
}
|
||||
function autorunDelta(observable, handler) {
|
||||
let _lastValue;
|
||||
return autorunOpts({ debugReferenceFn: handler }, (reader) => {
|
||||
const newValue = observable.read(reader);
|
||||
const lastValue = _lastValue;
|
||||
_lastValue = newValue;
|
||||
handler({ lastValue, newValue });
|
||||
});
|
||||
}
|
||||
|
||||
export { autorun, autorunDelta, autorunHandleChanges, autorunOpts, autorunWithStore, autorunWithStoreHandleChanges };
|
||||
Generated
Vendored
+215
@@ -0,0 +1,215 @@
|
||||
import { assertFn } from '../../assert.js';
|
||||
import '../../arrays.js';
|
||||
import { onBugIndicatingError, BugIndicatingError } from '../../errors.js';
|
||||
import '../../event.js';
|
||||
import { DisposableStore } from '../../lifecycle.js';
|
||||
import { getLogger } from '../logging/logging.js';
|
||||
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
function autorunStateToString(state) {
|
||||
switch (state) {
|
||||
case 1 /* AutorunState.dependenciesMightHaveChanged */: return 'dependenciesMightHaveChanged';
|
||||
case 2 /* AutorunState.stale */: return 'stale';
|
||||
case 3 /* AutorunState.upToDate */: return 'upToDate';
|
||||
default: return '<unknown>';
|
||||
}
|
||||
}
|
||||
class AutorunObserver {
|
||||
get debugName() {
|
||||
return this._debugNameData.getDebugName(this) ?? '(anonymous)';
|
||||
}
|
||||
constructor(_debugNameData, _runFn, _changeTracker, debugLocation) {
|
||||
this._debugNameData = _debugNameData;
|
||||
this._runFn = _runFn;
|
||||
this._changeTracker = _changeTracker;
|
||||
this._state = 2 /* AutorunState.stale */;
|
||||
this._updateCount = 0;
|
||||
this._disposed = false;
|
||||
this._dependencies = new Set();
|
||||
this._dependenciesToBeRemoved = new Set();
|
||||
this._isRunning = false;
|
||||
this._store = undefined;
|
||||
this._delayedStore = undefined;
|
||||
this._changeSummary = this._changeTracker?.createChangeSummary(undefined);
|
||||
getLogger()?.handleAutorunCreated(this, debugLocation);
|
||||
this._run();
|
||||
}
|
||||
dispose() {
|
||||
if (this._disposed) {
|
||||
return;
|
||||
}
|
||||
this._disposed = true;
|
||||
for (const o of this._dependencies) {
|
||||
o.removeObserver(this); // Warning: external call!
|
||||
}
|
||||
this._dependencies.clear();
|
||||
if (this._store !== undefined) {
|
||||
this._store.dispose();
|
||||
}
|
||||
if (this._delayedStore !== undefined) {
|
||||
this._delayedStore.dispose();
|
||||
}
|
||||
getLogger()?.handleAutorunDisposed(this);
|
||||
}
|
||||
_run() {
|
||||
const emptySet = this._dependenciesToBeRemoved;
|
||||
this._dependenciesToBeRemoved = this._dependencies;
|
||||
this._dependencies = emptySet;
|
||||
this._state = 3 /* AutorunState.upToDate */;
|
||||
try {
|
||||
if (!this._disposed) {
|
||||
getLogger()?.handleAutorunStarted(this);
|
||||
const changeSummary = this._changeSummary;
|
||||
const delayedStore = this._delayedStore;
|
||||
if (delayedStore !== undefined) {
|
||||
this._delayedStore = undefined;
|
||||
}
|
||||
try {
|
||||
this._isRunning = true;
|
||||
if (this._changeTracker) {
|
||||
this._changeTracker.beforeUpdate?.(this, changeSummary);
|
||||
this._changeSummary = this._changeTracker.createChangeSummary(changeSummary); // Warning: external call!
|
||||
}
|
||||
if (this._store !== undefined) {
|
||||
this._store.dispose();
|
||||
this._store = undefined;
|
||||
}
|
||||
this._runFn(this, changeSummary); // Warning: external call!
|
||||
}
|
||||
catch (e) {
|
||||
onBugIndicatingError(e);
|
||||
}
|
||||
finally {
|
||||
this._isRunning = false;
|
||||
if (delayedStore !== undefined) {
|
||||
delayedStore.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
finally {
|
||||
if (!this._disposed) {
|
||||
getLogger()?.handleAutorunFinished(this);
|
||||
}
|
||||
// We don't want our observed observables to think that they are (not even temporarily) not being observed.
|
||||
// Thus, we only unsubscribe from observables that are definitely not read anymore.
|
||||
for (const o of this._dependenciesToBeRemoved) {
|
||||
o.removeObserver(this); // Warning: external call!
|
||||
}
|
||||
this._dependenciesToBeRemoved.clear();
|
||||
}
|
||||
}
|
||||
toString() {
|
||||
return `Autorun<${this.debugName}>`;
|
||||
}
|
||||
// IObserver implementation
|
||||
beginUpdate(_observable) {
|
||||
if (this._state === 3 /* AutorunState.upToDate */) {
|
||||
this._state = 1 /* AutorunState.dependenciesMightHaveChanged */;
|
||||
}
|
||||
this._updateCount++;
|
||||
}
|
||||
endUpdate(_observable) {
|
||||
try {
|
||||
if (this._updateCount === 1) {
|
||||
do {
|
||||
if (this._state === 1 /* AutorunState.dependenciesMightHaveChanged */) {
|
||||
this._state = 3 /* AutorunState.upToDate */;
|
||||
for (const d of this._dependencies) {
|
||||
d.reportChanges(); // Warning: external call!
|
||||
if (this._state === 2 /* AutorunState.stale */) {
|
||||
// The other dependencies will refresh on demand
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this._state !== 3 /* AutorunState.upToDate */) {
|
||||
this._run(); // Warning: indirect external call!
|
||||
}
|
||||
} while (this._state !== 3 /* AutorunState.upToDate */);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
this._updateCount--;
|
||||
}
|
||||
assertFn(() => this._updateCount >= 0);
|
||||
}
|
||||
handlePossibleChange(observable) {
|
||||
if (this._state === 3 /* AutorunState.upToDate */ && this._isDependency(observable)) {
|
||||
this._state = 1 /* AutorunState.dependenciesMightHaveChanged */;
|
||||
}
|
||||
}
|
||||
handleChange(observable, change) {
|
||||
if (this._isDependency(observable)) {
|
||||
getLogger()?.handleAutorunDependencyChanged(this, observable, change);
|
||||
try {
|
||||
// Warning: external call!
|
||||
const shouldReact = this._changeTracker ? this._changeTracker.handleChange({
|
||||
changedObservable: observable,
|
||||
change,
|
||||
// eslint-disable-next-line local/code-no-any-casts
|
||||
didChange: (o) => o === observable,
|
||||
}, this._changeSummary) : true;
|
||||
if (shouldReact) {
|
||||
this._state = 2 /* AutorunState.stale */;
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
onBugIndicatingError(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
_isDependency(observable) {
|
||||
return this._dependencies.has(observable) && !this._dependenciesToBeRemoved.has(observable);
|
||||
}
|
||||
// IReader implementation
|
||||
_ensureNoRunning() {
|
||||
if (!this._isRunning) {
|
||||
throw new BugIndicatingError('The reader object cannot be used outside its compute function!');
|
||||
}
|
||||
}
|
||||
readObservable(observable) {
|
||||
this._ensureNoRunning();
|
||||
// In case the run action disposes the autorun
|
||||
if (this._disposed) {
|
||||
return observable.get(); // warning: external call!
|
||||
}
|
||||
observable.addObserver(this); // warning: external call!
|
||||
const value = observable.get(); // warning: external call!
|
||||
this._dependencies.add(observable);
|
||||
this._dependenciesToBeRemoved.delete(observable);
|
||||
return value;
|
||||
}
|
||||
get store() {
|
||||
this._ensureNoRunning();
|
||||
if (this._disposed) {
|
||||
throw new BugIndicatingError('Cannot access store after dispose');
|
||||
}
|
||||
if (this._store === undefined) {
|
||||
this._store = new DisposableStore();
|
||||
}
|
||||
return this._store;
|
||||
}
|
||||
debugGetState() {
|
||||
return {
|
||||
isRunning: this._isRunning,
|
||||
updateCount: this._updateCount,
|
||||
dependencies: this._dependencies,
|
||||
state: this._state,
|
||||
stateStr: autorunStateToString(this._state),
|
||||
};
|
||||
}
|
||||
debugRerun() {
|
||||
if (!this._isRunning) {
|
||||
this._run();
|
||||
}
|
||||
else {
|
||||
this._state = 2 /* AutorunState.stale */;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export { AutorunObserver };
|
||||
Generated
Vendored
+109
@@ -0,0 +1,109 @@
|
||||
import { handleBugIndicatingErrorRecovery } from './base.js';
|
||||
import { getFunctionName } from './debugName.js';
|
||||
import { getLogger } from './logging/logging.js';
|
||||
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
/**
|
||||
* Starts a transaction in which many observables can be changed at once.
|
||||
* {@link fn} should start with a JS Doc using `@description` to give the transaction a debug name.
|
||||
* Reaction run on demand or when the transaction ends.
|
||||
*/
|
||||
function transaction(fn, getDebugName) {
|
||||
const tx = new TransactionImpl(fn, getDebugName);
|
||||
try {
|
||||
fn(tx);
|
||||
}
|
||||
finally {
|
||||
tx.finish();
|
||||
}
|
||||
}
|
||||
let _globalTransaction = undefined;
|
||||
function globalTransaction(fn) {
|
||||
if (_globalTransaction) {
|
||||
fn(_globalTransaction);
|
||||
}
|
||||
else {
|
||||
const tx = new TransactionImpl(fn, undefined);
|
||||
_globalTransaction = tx;
|
||||
try {
|
||||
fn(tx);
|
||||
}
|
||||
finally {
|
||||
tx.finish(); // During finish, more actions might be added to the transaction.
|
||||
// Which is why we only clear the global transaction after finish.
|
||||
_globalTransaction = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
/** @deprecated */
|
||||
async function asyncTransaction(fn, getDebugName) {
|
||||
const tx = new TransactionImpl(fn, getDebugName);
|
||||
try {
|
||||
await fn(tx);
|
||||
}
|
||||
finally {
|
||||
tx.finish();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Allows to chain transactions.
|
||||
*/
|
||||
function subtransaction(tx, fn, getDebugName) {
|
||||
if (!tx) {
|
||||
transaction(fn, getDebugName);
|
||||
}
|
||||
else {
|
||||
fn(tx);
|
||||
}
|
||||
}
|
||||
class TransactionImpl {
|
||||
constructor(_fn, _getDebugName) {
|
||||
this._fn = _fn;
|
||||
this._getDebugName = _getDebugName;
|
||||
this._updatingObservers = [];
|
||||
getLogger()?.handleBeginTransaction(this);
|
||||
}
|
||||
getDebugName() {
|
||||
if (this._getDebugName) {
|
||||
return this._getDebugName();
|
||||
}
|
||||
return getFunctionName(this._fn);
|
||||
}
|
||||
updateObserver(observer, observable) {
|
||||
if (!this._updatingObservers) {
|
||||
// This happens when a transaction is used in a callback or async function.
|
||||
// If an async transaction is used, make sure the promise awaits all users of the transaction (e.g. no race).
|
||||
handleBugIndicatingErrorRecovery('Transaction already finished!');
|
||||
// Error recovery
|
||||
transaction(tx => {
|
||||
tx.updateObserver(observer, observable);
|
||||
});
|
||||
return;
|
||||
}
|
||||
// When this gets called while finish is active, they will still get considered
|
||||
this._updatingObservers.push({ observer, observable });
|
||||
observer.beginUpdate(observable);
|
||||
}
|
||||
finish() {
|
||||
const updatingObservers = this._updatingObservers;
|
||||
if (!updatingObservers) {
|
||||
handleBugIndicatingErrorRecovery('transaction.finish() has already been called!');
|
||||
return;
|
||||
}
|
||||
for (let i = 0; i < updatingObservers.length; i++) {
|
||||
const { observer, observable } = updatingObservers[i];
|
||||
observer.endUpdate(observable);
|
||||
}
|
||||
// Prevent anyone from updating observers from now on.
|
||||
this._updatingObservers = null;
|
||||
getLogger()?.handleEndTransaction(this);
|
||||
}
|
||||
debugGetUpdatingObservers() {
|
||||
return this._updatingObservers;
|
||||
}
|
||||
}
|
||||
|
||||
export { TransactionImpl, asyncTransaction, globalTransaction, subtransaction, transaction };
|
||||
Generated
Vendored
+56
@@ -0,0 +1,56 @@
|
||||
import { transaction } from '../transaction.js';
|
||||
import { observableValue } from '../observables/observableValue.js';
|
||||
|
||||
/**
|
||||
* A promise whose state is observable.
|
||||
*/
|
||||
class ObservablePromise {
|
||||
constructor(promise) {
|
||||
this._value = observableValue(this, undefined);
|
||||
/**
|
||||
* The current state of the promise.
|
||||
* Is `undefined` if the promise didn't resolve yet.
|
||||
*/
|
||||
this.promiseResult = this._value;
|
||||
this.promise = promise.then(value => {
|
||||
transaction(tx => {
|
||||
/** @description onPromiseResolved */
|
||||
this._value.set(new PromiseResult(value, undefined), tx);
|
||||
});
|
||||
return value;
|
||||
}, error => {
|
||||
transaction(tx => {
|
||||
/** @description onPromiseRejected */
|
||||
this._value.set(new PromiseResult(undefined, error), tx);
|
||||
});
|
||||
throw error;
|
||||
});
|
||||
}
|
||||
}
|
||||
class PromiseResult {
|
||||
constructor(
|
||||
/**
|
||||
* The value of the resolved promise.
|
||||
* Undefined if the promise rejected.
|
||||
*/
|
||||
data,
|
||||
/**
|
||||
* The error in case of a rejected promise.
|
||||
* Undefined if the promise resolved.
|
||||
*/
|
||||
error) {
|
||||
this.data = data;
|
||||
this.error = error;
|
||||
}
|
||||
/**
|
||||
* Returns the value if the promise resolved, otherwise throws the error.
|
||||
*/
|
||||
getDataOrThrow() {
|
||||
if (this.error) {
|
||||
throw this.error;
|
||||
}
|
||||
return this.data;
|
||||
}
|
||||
}
|
||||
|
||||
export { ObservablePromise, PromiseResult };
|
||||
Generated
Vendored
+61
@@ -0,0 +1,61 @@
|
||||
import { cancelOnDispose } from '../../cancellation.js';
|
||||
import '../../arrays.js';
|
||||
import '../../event.js';
|
||||
import { DisposableStore } from '../../lifecycle.js';
|
||||
import { autorunWithStoreHandleChanges } from '../reactions/autorun.js';
|
||||
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
function runOnChange(observable, cb) {
|
||||
let _previousValue;
|
||||
let _firstRun = true;
|
||||
return autorunWithStoreHandleChanges({
|
||||
changeTracker: {
|
||||
createChangeSummary: () => ({ deltas: [], didChange: false }),
|
||||
handleChange: (context, changeSummary) => {
|
||||
if (context.didChange(observable)) {
|
||||
const e = context.change;
|
||||
if (e !== undefined) {
|
||||
changeSummary.deltas.push(e);
|
||||
}
|
||||
changeSummary.didChange = true;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
}
|
||||
}, (reader, changeSummary) => {
|
||||
const value = observable.read(reader);
|
||||
const previousValue = _previousValue;
|
||||
if (changeSummary.didChange) {
|
||||
_previousValue = value;
|
||||
// didChange can never be true on the first autorun, so we know previousValue is defined
|
||||
cb(value, previousValue, changeSummary.deltas);
|
||||
}
|
||||
if (_firstRun) {
|
||||
_firstRun = false;
|
||||
_previousValue = value;
|
||||
}
|
||||
});
|
||||
}
|
||||
function runOnChangeWithStore(observable, cb) {
|
||||
const store = new DisposableStore();
|
||||
const disposable = runOnChange(observable, (value, previousValue, deltas) => {
|
||||
store.clear();
|
||||
cb(value, previousValue, deltas, store);
|
||||
});
|
||||
return {
|
||||
dispose() {
|
||||
disposable.dispose();
|
||||
store.dispose();
|
||||
}
|
||||
};
|
||||
}
|
||||
function runOnChangeWithCancellationToken(observable, cb) {
|
||||
return runOnChangeWithStore(observable, (value, previousValue, deltas, store) => {
|
||||
cb(value, previousValue, deltas, cancelOnDispose(store));
|
||||
});
|
||||
}
|
||||
|
||||
export { runOnChange, runOnChangeWithCancellationToken, runOnChangeWithStore };
|
||||
Generated
Vendored
+164
@@ -0,0 +1,164 @@
|
||||
import { autorun } from '../reactions/autorun.js';
|
||||
import '../../arrays.js';
|
||||
import '../../event.js';
|
||||
import { toDisposable, DisposableStore } from '../../lifecycle.js';
|
||||
import { derivedOpts } from '../observables/derived.js';
|
||||
import { observableFromEvent } from '../observables/observableFromEvent.js';
|
||||
import { _setRecomputeInitiallyAndOnChange } from '../observables/baseObservable.js';
|
||||
import '../debugLocation.js';
|
||||
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
/**
|
||||
* Creates an observable that debounces the input observable.
|
||||
*/
|
||||
function debouncedObservable(observable, debounceMs) {
|
||||
let hasValue = false;
|
||||
let lastValue;
|
||||
let timeout = undefined;
|
||||
return observableFromEvent(cb => {
|
||||
const d = autorun(reader => {
|
||||
const value = observable.read(reader);
|
||||
if (!hasValue) {
|
||||
hasValue = true;
|
||||
lastValue = value;
|
||||
}
|
||||
else {
|
||||
if (timeout) {
|
||||
clearTimeout(timeout);
|
||||
}
|
||||
timeout = setTimeout(() => {
|
||||
lastValue = value;
|
||||
cb();
|
||||
}, debounceMs);
|
||||
}
|
||||
});
|
||||
return {
|
||||
dispose() {
|
||||
d.dispose();
|
||||
hasValue = false;
|
||||
lastValue = undefined;
|
||||
},
|
||||
};
|
||||
}, () => {
|
||||
if (hasValue) {
|
||||
return lastValue;
|
||||
}
|
||||
else {
|
||||
return observable.get();
|
||||
}
|
||||
});
|
||||
}
|
||||
/**
|
||||
* This converts the given observable into an autorun.
|
||||
*/
|
||||
function recomputeInitiallyAndOnChange(observable, handleValue) {
|
||||
const o = new KeepAliveObserver(true, handleValue);
|
||||
observable.addObserver(o);
|
||||
try {
|
||||
o.beginUpdate(observable);
|
||||
}
|
||||
finally {
|
||||
o.endUpdate(observable);
|
||||
}
|
||||
return toDisposable(() => {
|
||||
observable.removeObserver(o);
|
||||
});
|
||||
}
|
||||
_setRecomputeInitiallyAndOnChange(recomputeInitiallyAndOnChange);
|
||||
class KeepAliveObserver {
|
||||
constructor(_forceRecompute, _handleValue) {
|
||||
this._forceRecompute = _forceRecompute;
|
||||
this._handleValue = _handleValue;
|
||||
this._counter = 0;
|
||||
}
|
||||
beginUpdate(observable) {
|
||||
this._counter++;
|
||||
}
|
||||
endUpdate(observable) {
|
||||
if (this._counter === 1 && this._forceRecompute) {
|
||||
if (this._handleValue) {
|
||||
this._handleValue(observable.get());
|
||||
}
|
||||
else {
|
||||
observable.reportChanges();
|
||||
}
|
||||
}
|
||||
this._counter--;
|
||||
}
|
||||
handlePossibleChange(observable) {
|
||||
// NO OP
|
||||
}
|
||||
handleChange(observable, change) {
|
||||
// NO OP
|
||||
}
|
||||
}
|
||||
function derivedObservableWithCache(owner, computeFn) {
|
||||
let lastValue = undefined;
|
||||
const observable = derivedOpts({ owner, debugReferenceFn: computeFn }, reader => {
|
||||
lastValue = computeFn(reader, lastValue);
|
||||
return lastValue;
|
||||
});
|
||||
return observable;
|
||||
}
|
||||
/**
|
||||
* When the items array changes, referential equal items are not mapped again.
|
||||
*/
|
||||
function mapObservableArrayCached(owner, items, map, keySelector) {
|
||||
let m = new ArrayMap(map, keySelector);
|
||||
const self = derivedOpts({
|
||||
debugReferenceFn: map,
|
||||
owner,
|
||||
onLastObserverRemoved: () => {
|
||||
m.dispose();
|
||||
m = new ArrayMap(map);
|
||||
}
|
||||
}, (reader) => {
|
||||
m.setItems(items.read(reader));
|
||||
return m.getItems();
|
||||
});
|
||||
return self;
|
||||
}
|
||||
class ArrayMap {
|
||||
constructor(_map, _keySelector) {
|
||||
this._map = _map;
|
||||
this._keySelector = _keySelector;
|
||||
this._cache = new Map();
|
||||
this._items = [];
|
||||
}
|
||||
dispose() {
|
||||
this._cache.forEach(entry => entry.store.dispose());
|
||||
this._cache.clear();
|
||||
}
|
||||
setItems(items) {
|
||||
const newItems = [];
|
||||
const itemsToRemove = new Set(this._cache.keys());
|
||||
for (const item of items) {
|
||||
const key = this._keySelector ? this._keySelector(item) : item;
|
||||
let entry = this._cache.get(key);
|
||||
if (!entry) {
|
||||
const store = new DisposableStore();
|
||||
const out = this._map(item, store);
|
||||
entry = { out, store };
|
||||
this._cache.set(key, entry);
|
||||
}
|
||||
else {
|
||||
itemsToRemove.delete(key);
|
||||
}
|
||||
newItems.push(entry.out);
|
||||
}
|
||||
for (const item of itemsToRemove) {
|
||||
const entry = this._cache.get(item);
|
||||
entry.store.dispose();
|
||||
this._cache.delete(item);
|
||||
}
|
||||
this._items = newItems;
|
||||
}
|
||||
getItems() {
|
||||
return this._items;
|
||||
}
|
||||
}
|
||||
|
||||
export { KeepAliveObserver, debouncedObservable, derivedObservableWithCache, mapObservableArrayCached, recomputeInitiallyAndOnChange };
|
||||
frontend/node_modules/monaco-editor/esm/vs/base/common/observableInternal/utils/utilsCancellation.js
Generated
Vendored
+63
@@ -0,0 +1,63 @@
|
||||
import { CancellationError } from '../../errors.js';
|
||||
import '../../cancellation.js';
|
||||
import { autorun } from '../reactions/autorun.js';
|
||||
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
function waitForState(observable, predicate, isError, cancellationToken) {
|
||||
if (!predicate) {
|
||||
predicate = state => state !== null && state !== undefined;
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
let isImmediateRun = true;
|
||||
let shouldDispose = false;
|
||||
const stateObs = observable.map(state => {
|
||||
/** @description waitForState.state */
|
||||
return {
|
||||
isFinished: predicate(state),
|
||||
error: isError ? isError(state) : false,
|
||||
state
|
||||
};
|
||||
});
|
||||
const d = autorun(reader => {
|
||||
/** @description waitForState */
|
||||
const { isFinished, error, state } = stateObs.read(reader);
|
||||
if (isFinished || error) {
|
||||
if (isImmediateRun) {
|
||||
// The variable `d` is not initialized yet
|
||||
shouldDispose = true;
|
||||
}
|
||||
else {
|
||||
d.dispose();
|
||||
}
|
||||
if (error) {
|
||||
reject(error === true ? state : error);
|
||||
}
|
||||
else {
|
||||
resolve(state);
|
||||
}
|
||||
}
|
||||
});
|
||||
if (cancellationToken) {
|
||||
const dc = cancellationToken.onCancellationRequested(() => {
|
||||
d.dispose();
|
||||
dc.dispose();
|
||||
reject(new CancellationError());
|
||||
});
|
||||
if (cancellationToken.isCancellationRequested) {
|
||||
d.dispose();
|
||||
dc.dispose();
|
||||
reject(new CancellationError());
|
||||
return;
|
||||
}
|
||||
}
|
||||
isImmediateRun = false;
|
||||
if (shouldDispose) {
|
||||
d.dispose();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export { waitForState };
|
||||
+1451
File diff suppressed because it is too large
Load Diff
+154
@@ -0,0 +1,154 @@
|
||||
import '../../nls.js';
|
||||
import { getNLSLanguage } from '../../nls.messages.js';
|
||||
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
const LANGUAGE_DEFAULT = 'en';
|
||||
let _isWindows = false;
|
||||
let _isMacintosh = false;
|
||||
let _isLinux = false;
|
||||
let _isNative = false;
|
||||
let _isWeb = false;
|
||||
let _isIOS = false;
|
||||
let _isMobile = false;
|
||||
let _locale = undefined;
|
||||
let _language = LANGUAGE_DEFAULT;
|
||||
let _platformLocale = LANGUAGE_DEFAULT;
|
||||
let _translationsConfigFile = undefined;
|
||||
let _userAgent = undefined;
|
||||
const $globalThis = globalThis;
|
||||
let nodeProcess = undefined;
|
||||
if (typeof $globalThis.vscode !== 'undefined' && typeof $globalThis.vscode.process !== 'undefined') {
|
||||
// Native environment (sandboxed)
|
||||
nodeProcess = $globalThis.vscode.process;
|
||||
}
|
||||
else if (typeof process !== 'undefined' && typeof process?.versions?.node === 'string') {
|
||||
// Native environment (non-sandboxed)
|
||||
nodeProcess = process;
|
||||
}
|
||||
const isElectronProcess = typeof nodeProcess?.versions?.electron === 'string';
|
||||
const isElectronRenderer = isElectronProcess && nodeProcess?.type === 'renderer';
|
||||
// Native environment
|
||||
if (typeof nodeProcess === 'object') {
|
||||
_isWindows = (nodeProcess.platform === 'win32');
|
||||
_isMacintosh = (nodeProcess.platform === 'darwin');
|
||||
_isLinux = (nodeProcess.platform === 'linux');
|
||||
_isLinux && !!nodeProcess.env['SNAP'] && !!nodeProcess.env['SNAP_REVISION'];
|
||||
!!nodeProcess.env['CI'] || !!nodeProcess.env['BUILD_ARTIFACTSTAGINGDIRECTORY'] || !!nodeProcess.env['GITHUB_WORKSPACE'];
|
||||
_locale = LANGUAGE_DEFAULT;
|
||||
_language = LANGUAGE_DEFAULT;
|
||||
const rawNlsConfig = nodeProcess.env['VSCODE_NLS_CONFIG'];
|
||||
if (rawNlsConfig) {
|
||||
try {
|
||||
const nlsConfig = JSON.parse(rawNlsConfig);
|
||||
_locale = nlsConfig.userLocale;
|
||||
_platformLocale = nlsConfig.osLocale;
|
||||
_language = nlsConfig.resolvedLanguage || LANGUAGE_DEFAULT;
|
||||
_translationsConfigFile = nlsConfig.languagePack?.translationsConfigFile;
|
||||
}
|
||||
catch (e) {
|
||||
}
|
||||
}
|
||||
_isNative = true;
|
||||
}
|
||||
// Web environment
|
||||
else if (typeof navigator === 'object' && !isElectronRenderer) {
|
||||
_userAgent = navigator.userAgent;
|
||||
_isWindows = _userAgent.indexOf('Windows') >= 0;
|
||||
_isMacintosh = _userAgent.indexOf('Macintosh') >= 0;
|
||||
_isIOS = (_userAgent.indexOf('Macintosh') >= 0 || _userAgent.indexOf('iPad') >= 0 || _userAgent.indexOf('iPhone') >= 0) && !!navigator.maxTouchPoints && navigator.maxTouchPoints > 0;
|
||||
_isLinux = _userAgent.indexOf('Linux') >= 0;
|
||||
_isMobile = _userAgent?.indexOf('Mobi') >= 0;
|
||||
_isWeb = true;
|
||||
_language = getNLSLanguage() || LANGUAGE_DEFAULT;
|
||||
_locale = navigator.language.toLowerCase();
|
||||
_platformLocale = _locale;
|
||||
}
|
||||
// Unknown environment
|
||||
else {
|
||||
console.error('Unable to resolve platform.');
|
||||
}
|
||||
let _platform = 0 /* Platform.Web */;
|
||||
if (_isMacintosh) {
|
||||
_platform = 1 /* Platform.Mac */;
|
||||
}
|
||||
else if (_isWindows) {
|
||||
_platform = 3 /* Platform.Windows */;
|
||||
}
|
||||
else if (_isLinux) {
|
||||
_platform = 2 /* Platform.Linux */;
|
||||
}
|
||||
const isWindows = _isWindows;
|
||||
const isMacintosh = _isMacintosh;
|
||||
const isLinux = _isLinux;
|
||||
const isNative = _isNative;
|
||||
const isWeb = _isWeb;
|
||||
const isWebWorker = (_isWeb && typeof $globalThis.importScripts === 'function');
|
||||
const webWorkerOrigin = isWebWorker ? $globalThis.origin : undefined;
|
||||
const isIOS = _isIOS;
|
||||
const isMobile = _isMobile;
|
||||
const platform = _platform;
|
||||
const userAgent = _userAgent;
|
||||
/**
|
||||
* The language used for the user interface. The format of
|
||||
* the string is all lower case (e.g. zh-tw for Traditional
|
||||
* Chinese or de for German)
|
||||
*/
|
||||
const language = _language;
|
||||
const setTimeout0IsFaster = (typeof $globalThis.postMessage === 'function' && !$globalThis.importScripts);
|
||||
/**
|
||||
* See https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#:~:text=than%204%2C%20then-,set%20timeout%20to%204,-.
|
||||
*
|
||||
* Works similarly to `setTimeout(0)` but doesn't suffer from the 4ms artificial delay
|
||||
* that browsers set when the nesting level is > 5.
|
||||
*/
|
||||
const setTimeout0 = (() => {
|
||||
if (setTimeout0IsFaster) {
|
||||
const pending = [];
|
||||
$globalThis.addEventListener('message', (e) => {
|
||||
if (e.data && e.data.vscodeScheduleAsyncWork) {
|
||||
for (let i = 0, len = pending.length; i < len; i++) {
|
||||
const candidate = pending[i];
|
||||
if (candidate.id === e.data.vscodeScheduleAsyncWork) {
|
||||
pending.splice(i, 1);
|
||||
candidate.callback();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
let lastId = 0;
|
||||
return (callback) => {
|
||||
const myId = ++lastId;
|
||||
pending.push({
|
||||
id: myId,
|
||||
callback: callback
|
||||
});
|
||||
$globalThis.postMessage({ vscodeScheduleAsyncWork: myId }, '*');
|
||||
};
|
||||
}
|
||||
return (callback) => setTimeout(callback);
|
||||
})();
|
||||
const OS = (_isMacintosh || _isIOS ? 2 /* OperatingSystem.Macintosh */ : (_isWindows ? 1 /* OperatingSystem.Windows */ : 3 /* OperatingSystem.Linux */));
|
||||
let _isLittleEndian = true;
|
||||
let _isLittleEndianComputed = false;
|
||||
function isLittleEndian() {
|
||||
if (!_isLittleEndianComputed) {
|
||||
_isLittleEndianComputed = true;
|
||||
const test = new Uint8Array(2);
|
||||
test[0] = 1;
|
||||
test[1] = 2;
|
||||
const view = new Uint16Array(test.buffer);
|
||||
_isLittleEndian = (view[0] === (2 << 8) + 1);
|
||||
}
|
||||
return _isLittleEndian;
|
||||
}
|
||||
const isChrome = !!(userAgent && userAgent.indexOf('Chrome') >= 0);
|
||||
const isFirefox = !!(userAgent && userAgent.indexOf('Firefox') >= 0);
|
||||
const isSafari = !!(!isChrome && (userAgent && userAgent.indexOf('Safari') >= 0));
|
||||
const isEdge = !!(userAgent && userAgent.indexOf('Edg/') >= 0);
|
||||
const isAndroid = !!(userAgent && userAgent.indexOf('Android') >= 0);
|
||||
|
||||
export { LANGUAGE_DEFAULT, OS, isAndroid, isChrome, isEdge, isFirefox, isIOS, isLinux, isLittleEndian, isMacintosh, isMobile, isNative, isSafari, isWeb, isWebWorker, isWindows, language, platform, setTimeout0, setTimeout0IsFaster, userAgent, webWorkerOrigin };
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
import { isWindows, isMacintosh } from './platform.js';
|
||||
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
let safeProcess;
|
||||
// Native sandbox environment
|
||||
const vscodeGlobal = globalThis.vscode;
|
||||
if (typeof vscodeGlobal !== 'undefined' && typeof vscodeGlobal.process !== 'undefined') {
|
||||
const sandboxProcess = vscodeGlobal.process;
|
||||
safeProcess = {
|
||||
get platform() { return sandboxProcess.platform; },
|
||||
get arch() { return sandboxProcess.arch; },
|
||||
get env() { return sandboxProcess.env; },
|
||||
cwd() { return sandboxProcess.cwd(); }
|
||||
};
|
||||
}
|
||||
// Native node.js environment
|
||||
else if (typeof process !== 'undefined' && typeof process?.versions?.node === 'string') {
|
||||
safeProcess = {
|
||||
get platform() { return process.platform; },
|
||||
get arch() { return process.arch; },
|
||||
get env() { return process.env; },
|
||||
cwd() { return process.env['VSCODE_CWD'] || process.cwd(); }
|
||||
};
|
||||
}
|
||||
// Web environment
|
||||
else {
|
||||
safeProcess = {
|
||||
// Supported
|
||||
get platform() { return isWindows ? 'win32' : isMacintosh ? 'darwin' : 'linux'; },
|
||||
get arch() { return undefined; /* arch is undefined in web */ },
|
||||
// Unsupported
|
||||
get env() { return {}; },
|
||||
cwd() { return '/'; }
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Provides safe access to the `cwd` property in node.js, sandboxed or web
|
||||
* environments.
|
||||
*
|
||||
* Note: in web, this property is hardcoded to be `/`.
|
||||
*
|
||||
* @skipMangle
|
||||
*/
|
||||
const cwd = safeProcess.cwd;
|
||||
/**
|
||||
* Provides safe access to the `env` property in node.js, sandboxed or web
|
||||
* environments.
|
||||
*
|
||||
* Note: in web, this property is hardcoded to be `{}`.
|
||||
*/
|
||||
const env = safeProcess.env;
|
||||
/**
|
||||
* Provides safe access to the `platform` property in node.js, sandboxed or web
|
||||
* environments.
|
||||
*/
|
||||
const platform = safeProcess.platform;
|
||||
|
||||
export { cwd, env, platform };
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
var Range;
|
||||
(function (Range) {
|
||||
/**
|
||||
* Returns the intersection between two ranges as a range itself.
|
||||
* Returns `{ start: 0, end: 0 }` if the intersection is empty.
|
||||
*/
|
||||
function intersect(one, other) {
|
||||
if (one.start >= other.end || other.start >= one.end) {
|
||||
return { start: 0, end: 0 };
|
||||
}
|
||||
const start = Math.max(one.start, other.start);
|
||||
const end = Math.min(one.end, other.end);
|
||||
if (end - start <= 0) {
|
||||
return { start: 0, end: 0 };
|
||||
}
|
||||
return { start, end };
|
||||
}
|
||||
Range.intersect = intersect;
|
||||
function isEmpty(range) {
|
||||
return range.end - range.start <= 0;
|
||||
}
|
||||
Range.isEmpty = isEmpty;
|
||||
function intersects(one, other) {
|
||||
return !isEmpty(intersect(one, other));
|
||||
}
|
||||
Range.intersects = intersects;
|
||||
function relativeComplement(one, other) {
|
||||
const result = [];
|
||||
const first = { start: one.start, end: Math.min(other.start, one.end) };
|
||||
const second = { start: Math.max(other.end, one.start), end: one.end };
|
||||
if (!isEmpty(first)) {
|
||||
result.push(first);
|
||||
}
|
||||
if (!isEmpty(second)) {
|
||||
result.push(second);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
Range.relativeComplement = relativeComplement;
|
||||
})(Range || (Range = {}));
|
||||
|
||||
export { Range };
|
||||
+231
@@ -0,0 +1,231 @@
|
||||
import { isEqualOrParent, toSlashes, toPosixPath, getRoot } from './extpath.js';
|
||||
import { Schemas } from './network.js';
|
||||
import { posix, dirname as dirname$1, normalize, relative, resolve, sep } from './path.js';
|
||||
import { isWindows } from './platform.js';
|
||||
import { compare, equalsIgnoreCase } from './strings.js';
|
||||
import { URI, uriToFsPath } from './uri.js';
|
||||
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
function originalFSPath(uri) {
|
||||
return uriToFsPath(uri, true);
|
||||
}
|
||||
class ExtUri {
|
||||
constructor(_ignorePathCasing) {
|
||||
this._ignorePathCasing = _ignorePathCasing;
|
||||
}
|
||||
compare(uri1, uri2, ignoreFragment = false) {
|
||||
if (uri1 === uri2) {
|
||||
return 0;
|
||||
}
|
||||
return compare(this.getComparisonKey(uri1, ignoreFragment), this.getComparisonKey(uri2, ignoreFragment));
|
||||
}
|
||||
isEqual(uri1, uri2, ignoreFragment = false) {
|
||||
if (uri1 === uri2) {
|
||||
return true;
|
||||
}
|
||||
if (!uri1 || !uri2) {
|
||||
return false;
|
||||
}
|
||||
return this.getComparisonKey(uri1, ignoreFragment) === this.getComparisonKey(uri2, ignoreFragment);
|
||||
}
|
||||
getComparisonKey(uri, ignoreFragment = false) {
|
||||
return uri.with({
|
||||
path: this._ignorePathCasing(uri) ? uri.path.toLowerCase() : undefined,
|
||||
fragment: ignoreFragment ? null : undefined
|
||||
}).toString();
|
||||
}
|
||||
isEqualOrParent(base, parentCandidate, ignoreFragment = false) {
|
||||
if (base.scheme === parentCandidate.scheme) {
|
||||
if (base.scheme === Schemas.file) {
|
||||
return isEqualOrParent(originalFSPath(base), originalFSPath(parentCandidate), this._ignorePathCasing(base)) && base.query === parentCandidate.query && (ignoreFragment || base.fragment === parentCandidate.fragment);
|
||||
}
|
||||
if (isEqualAuthority(base.authority, parentCandidate.authority)) {
|
||||
return isEqualOrParent(base.path, parentCandidate.path, this._ignorePathCasing(base), '/') && base.query === parentCandidate.query && (ignoreFragment || base.fragment === parentCandidate.fragment);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
// --- path math
|
||||
joinPath(resource, ...pathFragment) {
|
||||
return URI.joinPath(resource, ...pathFragment);
|
||||
}
|
||||
basenameOrAuthority(resource) {
|
||||
return basename(resource) || resource.authority;
|
||||
}
|
||||
basename(resource) {
|
||||
return posix.basename(resource.path);
|
||||
}
|
||||
extname(resource) {
|
||||
return posix.extname(resource.path);
|
||||
}
|
||||
dirname(resource) {
|
||||
if (resource.path.length === 0) {
|
||||
return resource;
|
||||
}
|
||||
let dirname;
|
||||
if (resource.scheme === Schemas.file) {
|
||||
dirname = URI.file(dirname$1(originalFSPath(resource))).path;
|
||||
}
|
||||
else {
|
||||
dirname = posix.dirname(resource.path);
|
||||
if (resource.authority && dirname.length && dirname.charCodeAt(0) !== 47 /* CharCode.Slash */) {
|
||||
console.error(`dirname("${resource.toString})) resulted in a relative path`);
|
||||
dirname = '/'; // If a URI contains an authority component, then the path component must either be empty or begin with a CharCode.Slash ("/") character
|
||||
}
|
||||
}
|
||||
return resource.with({
|
||||
path: dirname
|
||||
});
|
||||
}
|
||||
normalizePath(resource) {
|
||||
if (!resource.path.length) {
|
||||
return resource;
|
||||
}
|
||||
let normalizedPath;
|
||||
if (resource.scheme === Schemas.file) {
|
||||
normalizedPath = URI.file(normalize(originalFSPath(resource))).path;
|
||||
}
|
||||
else {
|
||||
normalizedPath = posix.normalize(resource.path);
|
||||
}
|
||||
return resource.with({
|
||||
path: normalizedPath
|
||||
});
|
||||
}
|
||||
relativePath(from, to) {
|
||||
if (from.scheme !== to.scheme || !isEqualAuthority(from.authority, to.authority)) {
|
||||
return undefined;
|
||||
}
|
||||
if (from.scheme === Schemas.file) {
|
||||
const relativePath = relative(originalFSPath(from), originalFSPath(to));
|
||||
return isWindows ? toSlashes(relativePath) : relativePath;
|
||||
}
|
||||
let fromPath = from.path || '/';
|
||||
const toPath = to.path || '/';
|
||||
if (this._ignorePathCasing(from)) {
|
||||
// make casing of fromPath match toPath
|
||||
let i = 0;
|
||||
for (const len = Math.min(fromPath.length, toPath.length); i < len; i++) {
|
||||
if (fromPath.charCodeAt(i) !== toPath.charCodeAt(i)) {
|
||||
if (fromPath.charAt(i).toLowerCase() !== toPath.charAt(i).toLowerCase()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
fromPath = toPath.substr(0, i) + fromPath.substr(i);
|
||||
}
|
||||
return posix.relative(fromPath, toPath);
|
||||
}
|
||||
resolvePath(base, path) {
|
||||
if (base.scheme === Schemas.file) {
|
||||
const newURI = URI.file(resolve(originalFSPath(base), path));
|
||||
return base.with({
|
||||
authority: newURI.authority,
|
||||
path: newURI.path
|
||||
});
|
||||
}
|
||||
path = toPosixPath(path); // we allow path to be a windows path
|
||||
return base.with({
|
||||
path: posix.resolve(base.path, path)
|
||||
});
|
||||
}
|
||||
// --- misc
|
||||
isAbsolutePath(resource) {
|
||||
return !!resource.path && resource.path[0] === '/';
|
||||
}
|
||||
isEqualAuthority(a1, a2) {
|
||||
return a1 === a2 || (a1 !== undefined && a2 !== undefined && equalsIgnoreCase(a1, a2));
|
||||
}
|
||||
hasTrailingPathSeparator(resource, sep$1 = sep) {
|
||||
if (resource.scheme === Schemas.file) {
|
||||
const fsp = originalFSPath(resource);
|
||||
return fsp.length > getRoot(fsp).length && fsp[fsp.length - 1] === sep$1;
|
||||
}
|
||||
else {
|
||||
const p = resource.path;
|
||||
return (p.length > 1 && p.charCodeAt(p.length - 1) === 47 /* CharCode.Slash */) && !(/^[a-zA-Z]:(\/$|\\$)/.test(resource.fsPath)); // ignore the slash at offset 0
|
||||
}
|
||||
}
|
||||
removeTrailingPathSeparator(resource, sep$1 = sep) {
|
||||
// Make sure that the path isn't a drive letter. A trailing separator there is not removable.
|
||||
if (hasTrailingPathSeparator(resource, sep$1)) {
|
||||
return resource.with({ path: resource.path.substr(0, resource.path.length - 1) });
|
||||
}
|
||||
return resource;
|
||||
}
|
||||
addTrailingPathSeparator(resource, sep$1 = sep) {
|
||||
let isRootSep = false;
|
||||
if (resource.scheme === Schemas.file) {
|
||||
const fsp = originalFSPath(resource);
|
||||
isRootSep = ((fsp !== undefined) && (fsp.length === getRoot(fsp).length) && (fsp[fsp.length - 1] === sep$1));
|
||||
}
|
||||
else {
|
||||
sep$1 = '/';
|
||||
const p = resource.path;
|
||||
isRootSep = p.length === 1 && p.charCodeAt(p.length - 1) === 47 /* CharCode.Slash */;
|
||||
}
|
||||
if (!isRootSep && !hasTrailingPathSeparator(resource, sep$1)) {
|
||||
return resource.with({ path: resource.path + '/' });
|
||||
}
|
||||
return resource;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Unbiased utility that takes uris "as they are". This means it can be interchanged with
|
||||
* uri#toString() usages. The following is true
|
||||
* ```
|
||||
* assertEqual(aUri.toString() === bUri.toString(), exturi.isEqual(aUri, bUri))
|
||||
* ```
|
||||
*/
|
||||
const extUri = new ExtUri(() => false);
|
||||
const isEqual = extUri.isEqual.bind(extUri);
|
||||
extUri.isEqualOrParent.bind(extUri);
|
||||
extUri.getComparisonKey.bind(extUri);
|
||||
const basenameOrAuthority = extUri.basenameOrAuthority.bind(extUri);
|
||||
const basename = extUri.basename.bind(extUri);
|
||||
const extname = extUri.extname.bind(extUri);
|
||||
const dirname = extUri.dirname.bind(extUri);
|
||||
const joinPath = extUri.joinPath.bind(extUri);
|
||||
const normalizePath = extUri.normalizePath.bind(extUri);
|
||||
const relativePath = extUri.relativePath.bind(extUri);
|
||||
const resolvePath = extUri.resolvePath.bind(extUri);
|
||||
extUri.isAbsolutePath.bind(extUri);
|
||||
const isEqualAuthority = extUri.isEqualAuthority.bind(extUri);
|
||||
const hasTrailingPathSeparator = extUri.hasTrailingPathSeparator.bind(extUri);
|
||||
extUri.removeTrailingPathSeparator.bind(extUri);
|
||||
extUri.addTrailingPathSeparator.bind(extUri);
|
||||
/**
|
||||
* Data URI related helpers.
|
||||
*/
|
||||
var DataUri;
|
||||
(function (DataUri) {
|
||||
DataUri.META_DATA_LABEL = 'label';
|
||||
DataUri.META_DATA_DESCRIPTION = 'description';
|
||||
DataUri.META_DATA_SIZE = 'size';
|
||||
DataUri.META_DATA_MIME = 'mime';
|
||||
function parseMetaData(dataUri) {
|
||||
const metadata = new Map();
|
||||
// Given a URI of: data:image/png;size:2313;label:SomeLabel;description:SomeDescription;base64,77+9UE5...
|
||||
// the metadata is: size:2313;label:SomeLabel;description:SomeDescription
|
||||
const meta = dataUri.path.substring(dataUri.path.indexOf(';') + 1, dataUri.path.lastIndexOf(';'));
|
||||
meta.split(';').forEach(property => {
|
||||
const [key, value] = property.split(':');
|
||||
if (key && value) {
|
||||
metadata.set(key, value);
|
||||
}
|
||||
});
|
||||
// Given a URI of: data:image/png;size:2313;label:SomeLabel;description:SomeDescription;base64,77+9UE5...
|
||||
// the mime is: image/png
|
||||
const mime = dataUri.path.substring(0, dataUri.path.indexOf(';'));
|
||||
if (mime) {
|
||||
metadata.set(DataUri.META_DATA_MIME, mime);
|
||||
}
|
||||
return metadata;
|
||||
}
|
||||
DataUri.parseMetaData = parseMetaData;
|
||||
})(DataUri || (DataUri = {}));
|
||||
|
||||
export { DataUri, ExtUri, basename, basenameOrAuthority, dirname, extUri, extname, hasTrailingPathSeparator, isEqual, isEqualAuthority, joinPath, normalizePath, originalFSPath, relativePath, resolvePath };
|
||||
+323
@@ -0,0 +1,323 @@
|
||||
import { Emitter } from './event.js';
|
||||
import { Disposable } from './lifecycle.js';
|
||||
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
class ScrollState {
|
||||
constructor(_forceIntegerValues, width, scrollWidth, scrollLeft, height, scrollHeight, scrollTop) {
|
||||
this._forceIntegerValues = _forceIntegerValues;
|
||||
this._scrollStateBrand = undefined;
|
||||
if (this._forceIntegerValues) {
|
||||
width = width | 0;
|
||||
scrollWidth = scrollWidth | 0;
|
||||
scrollLeft = scrollLeft | 0;
|
||||
height = height | 0;
|
||||
scrollHeight = scrollHeight | 0;
|
||||
scrollTop = scrollTop | 0;
|
||||
}
|
||||
this.rawScrollLeft = scrollLeft; // before validation
|
||||
this.rawScrollTop = scrollTop; // before validation
|
||||
if (width < 0) {
|
||||
width = 0;
|
||||
}
|
||||
if (scrollLeft + width > scrollWidth) {
|
||||
scrollLeft = scrollWidth - width;
|
||||
}
|
||||
if (scrollLeft < 0) {
|
||||
scrollLeft = 0;
|
||||
}
|
||||
if (height < 0) {
|
||||
height = 0;
|
||||
}
|
||||
if (scrollTop + height > scrollHeight) {
|
||||
scrollTop = scrollHeight - height;
|
||||
}
|
||||
if (scrollTop < 0) {
|
||||
scrollTop = 0;
|
||||
}
|
||||
this.width = width;
|
||||
this.scrollWidth = scrollWidth;
|
||||
this.scrollLeft = scrollLeft;
|
||||
this.height = height;
|
||||
this.scrollHeight = scrollHeight;
|
||||
this.scrollTop = scrollTop;
|
||||
}
|
||||
equals(other) {
|
||||
return (this.rawScrollLeft === other.rawScrollLeft
|
||||
&& this.rawScrollTop === other.rawScrollTop
|
||||
&& this.width === other.width
|
||||
&& this.scrollWidth === other.scrollWidth
|
||||
&& this.scrollLeft === other.scrollLeft
|
||||
&& this.height === other.height
|
||||
&& this.scrollHeight === other.scrollHeight
|
||||
&& this.scrollTop === other.scrollTop);
|
||||
}
|
||||
withScrollDimensions(update, useRawScrollPositions) {
|
||||
return new ScrollState(this._forceIntegerValues, (typeof update.width !== 'undefined' ? update.width : this.width), (typeof update.scrollWidth !== 'undefined' ? update.scrollWidth : this.scrollWidth), useRawScrollPositions ? this.rawScrollLeft : this.scrollLeft, (typeof update.height !== 'undefined' ? update.height : this.height), (typeof update.scrollHeight !== 'undefined' ? update.scrollHeight : this.scrollHeight), useRawScrollPositions ? this.rawScrollTop : this.scrollTop);
|
||||
}
|
||||
withScrollPosition(update) {
|
||||
return new ScrollState(this._forceIntegerValues, this.width, this.scrollWidth, (typeof update.scrollLeft !== 'undefined' ? update.scrollLeft : this.rawScrollLeft), this.height, this.scrollHeight, (typeof update.scrollTop !== 'undefined' ? update.scrollTop : this.rawScrollTop));
|
||||
}
|
||||
createScrollEvent(previous, inSmoothScrolling) {
|
||||
const widthChanged = (this.width !== previous.width);
|
||||
const scrollWidthChanged = (this.scrollWidth !== previous.scrollWidth);
|
||||
const scrollLeftChanged = (this.scrollLeft !== previous.scrollLeft);
|
||||
const heightChanged = (this.height !== previous.height);
|
||||
const scrollHeightChanged = (this.scrollHeight !== previous.scrollHeight);
|
||||
const scrollTopChanged = (this.scrollTop !== previous.scrollTop);
|
||||
return {
|
||||
inSmoothScrolling: inSmoothScrolling,
|
||||
oldWidth: previous.width,
|
||||
oldScrollWidth: previous.scrollWidth,
|
||||
oldScrollLeft: previous.scrollLeft,
|
||||
width: this.width,
|
||||
scrollWidth: this.scrollWidth,
|
||||
scrollLeft: this.scrollLeft,
|
||||
oldHeight: previous.height,
|
||||
oldScrollHeight: previous.scrollHeight,
|
||||
oldScrollTop: previous.scrollTop,
|
||||
height: this.height,
|
||||
scrollHeight: this.scrollHeight,
|
||||
scrollTop: this.scrollTop,
|
||||
widthChanged: widthChanged,
|
||||
scrollWidthChanged: scrollWidthChanged,
|
||||
scrollLeftChanged: scrollLeftChanged,
|
||||
heightChanged: heightChanged,
|
||||
scrollHeightChanged: scrollHeightChanged,
|
||||
scrollTopChanged: scrollTopChanged,
|
||||
};
|
||||
}
|
||||
}
|
||||
class Scrollable extends Disposable {
|
||||
constructor(options) {
|
||||
super();
|
||||
this._scrollableBrand = undefined;
|
||||
this._onScroll = this._register(new Emitter());
|
||||
this.onScroll = this._onScroll.event;
|
||||
this._smoothScrollDuration = options.smoothScrollDuration;
|
||||
this._scheduleAtNextAnimationFrame = options.scheduleAtNextAnimationFrame;
|
||||
this._state = new ScrollState(options.forceIntegerValues, 0, 0, 0, 0, 0, 0);
|
||||
this._smoothScrolling = null;
|
||||
}
|
||||
dispose() {
|
||||
if (this._smoothScrolling) {
|
||||
this._smoothScrolling.dispose();
|
||||
this._smoothScrolling = null;
|
||||
}
|
||||
super.dispose();
|
||||
}
|
||||
setSmoothScrollDuration(smoothScrollDuration) {
|
||||
this._smoothScrollDuration = smoothScrollDuration;
|
||||
}
|
||||
validateScrollPosition(scrollPosition) {
|
||||
return this._state.withScrollPosition(scrollPosition);
|
||||
}
|
||||
getScrollDimensions() {
|
||||
return this._state;
|
||||
}
|
||||
setScrollDimensions(dimensions, useRawScrollPositions) {
|
||||
const newState = this._state.withScrollDimensions(dimensions, useRawScrollPositions);
|
||||
this._setState(newState, Boolean(this._smoothScrolling));
|
||||
// Validate outstanding animated scroll position target
|
||||
this._smoothScrolling?.acceptScrollDimensions(this._state);
|
||||
}
|
||||
/**
|
||||
* Returns the final scroll position that the instance will have once the smooth scroll animation concludes.
|
||||
* If no scroll animation is occurring, it will return the current scroll position instead.
|
||||
*/
|
||||
getFutureScrollPosition() {
|
||||
if (this._smoothScrolling) {
|
||||
return this._smoothScrolling.to;
|
||||
}
|
||||
return this._state;
|
||||
}
|
||||
/**
|
||||
* Returns the current scroll position.
|
||||
* Note: This result might be an intermediate scroll position, as there might be an ongoing smooth scroll animation.
|
||||
*/
|
||||
getCurrentScrollPosition() {
|
||||
return this._state;
|
||||
}
|
||||
setScrollPositionNow(update) {
|
||||
// no smooth scrolling requested
|
||||
const newState = this._state.withScrollPosition(update);
|
||||
// Terminate any outstanding smooth scrolling
|
||||
if (this._smoothScrolling) {
|
||||
this._smoothScrolling.dispose();
|
||||
this._smoothScrolling = null;
|
||||
}
|
||||
this._setState(newState, false);
|
||||
}
|
||||
setScrollPositionSmooth(update, reuseAnimation) {
|
||||
if (this._smoothScrollDuration === 0) {
|
||||
// Smooth scrolling not supported.
|
||||
return this.setScrollPositionNow(update);
|
||||
}
|
||||
if (this._smoothScrolling) {
|
||||
// Combine our pending scrollLeft/scrollTop with incoming scrollLeft/scrollTop
|
||||
update = {
|
||||
scrollLeft: (typeof update.scrollLeft === 'undefined' ? this._smoothScrolling.to.scrollLeft : update.scrollLeft),
|
||||
scrollTop: (typeof update.scrollTop === 'undefined' ? this._smoothScrolling.to.scrollTop : update.scrollTop)
|
||||
};
|
||||
// Validate `update`
|
||||
const validTarget = this._state.withScrollPosition(update);
|
||||
if (this._smoothScrolling.to.scrollLeft === validTarget.scrollLeft && this._smoothScrolling.to.scrollTop === validTarget.scrollTop) {
|
||||
// No need to interrupt or extend the current animation since we're going to the same place
|
||||
return;
|
||||
}
|
||||
let newSmoothScrolling;
|
||||
if (reuseAnimation) {
|
||||
newSmoothScrolling = new SmoothScrollingOperation(this._smoothScrolling.from, validTarget, this._smoothScrolling.startTime, this._smoothScrolling.duration);
|
||||
}
|
||||
else {
|
||||
newSmoothScrolling = this._smoothScrolling.combine(this._state, validTarget, this._smoothScrollDuration);
|
||||
}
|
||||
this._smoothScrolling.dispose();
|
||||
this._smoothScrolling = newSmoothScrolling;
|
||||
}
|
||||
else {
|
||||
// Validate `update`
|
||||
const validTarget = this._state.withScrollPosition(update);
|
||||
this._smoothScrolling = SmoothScrollingOperation.start(this._state, validTarget, this._smoothScrollDuration);
|
||||
}
|
||||
// Begin smooth scrolling animation
|
||||
this._smoothScrolling.animationFrameDisposable = this._scheduleAtNextAnimationFrame(() => {
|
||||
if (!this._smoothScrolling) {
|
||||
return;
|
||||
}
|
||||
this._smoothScrolling.animationFrameDisposable = null;
|
||||
this._performSmoothScrolling();
|
||||
});
|
||||
}
|
||||
hasPendingScrollAnimation() {
|
||||
return Boolean(this._smoothScrolling);
|
||||
}
|
||||
_performSmoothScrolling() {
|
||||
if (!this._smoothScrolling) {
|
||||
return;
|
||||
}
|
||||
const update = this._smoothScrolling.tick();
|
||||
const newState = this._state.withScrollPosition(update);
|
||||
this._setState(newState, true);
|
||||
if (!this._smoothScrolling) {
|
||||
// Looks like someone canceled the smooth scrolling
|
||||
// from the scroll event handler
|
||||
return;
|
||||
}
|
||||
if (update.isDone) {
|
||||
this._smoothScrolling.dispose();
|
||||
this._smoothScrolling = null;
|
||||
return;
|
||||
}
|
||||
// Continue smooth scrolling animation
|
||||
this._smoothScrolling.animationFrameDisposable = this._scheduleAtNextAnimationFrame(() => {
|
||||
if (!this._smoothScrolling) {
|
||||
return;
|
||||
}
|
||||
this._smoothScrolling.animationFrameDisposable = null;
|
||||
this._performSmoothScrolling();
|
||||
});
|
||||
}
|
||||
_setState(newState, inSmoothScrolling) {
|
||||
const oldState = this._state;
|
||||
if (oldState.equals(newState)) {
|
||||
// no change
|
||||
return;
|
||||
}
|
||||
this._state = newState;
|
||||
this._onScroll.fire(this._state.createScrollEvent(oldState, inSmoothScrolling));
|
||||
}
|
||||
}
|
||||
class SmoothScrollingUpdate {
|
||||
constructor(scrollLeft, scrollTop, isDone) {
|
||||
this.scrollLeft = scrollLeft;
|
||||
this.scrollTop = scrollTop;
|
||||
this.isDone = isDone;
|
||||
}
|
||||
}
|
||||
function createEaseOutCubic(from, to) {
|
||||
const delta = to - from;
|
||||
return function (completion) {
|
||||
return from + delta * easeOutCubic(completion);
|
||||
};
|
||||
}
|
||||
function createComposed(a, b, cut) {
|
||||
return function (completion) {
|
||||
if (completion < cut) {
|
||||
return a(completion / cut);
|
||||
}
|
||||
return b((completion - cut) / (1 - cut));
|
||||
};
|
||||
}
|
||||
class SmoothScrollingOperation {
|
||||
constructor(from, to, startTime, duration) {
|
||||
this.from = from;
|
||||
this.to = to;
|
||||
this.duration = duration;
|
||||
this.startTime = startTime;
|
||||
this.animationFrameDisposable = null;
|
||||
this._initAnimations();
|
||||
}
|
||||
_initAnimations() {
|
||||
this.scrollLeft = this._initAnimation(this.from.scrollLeft, this.to.scrollLeft, this.to.width);
|
||||
this.scrollTop = this._initAnimation(this.from.scrollTop, this.to.scrollTop, this.to.height);
|
||||
}
|
||||
_initAnimation(from, to, viewportSize) {
|
||||
const delta = Math.abs(from - to);
|
||||
if (delta > 2.5 * viewportSize) {
|
||||
let stop1, stop2;
|
||||
if (from < to) {
|
||||
// scroll to 75% of the viewportSize
|
||||
stop1 = from + 0.75 * viewportSize;
|
||||
stop2 = to - 0.75 * viewportSize;
|
||||
}
|
||||
else {
|
||||
stop1 = from - 0.75 * viewportSize;
|
||||
stop2 = to + 0.75 * viewportSize;
|
||||
}
|
||||
return createComposed(createEaseOutCubic(from, stop1), createEaseOutCubic(stop2, to), 0.33);
|
||||
}
|
||||
return createEaseOutCubic(from, to);
|
||||
}
|
||||
dispose() {
|
||||
if (this.animationFrameDisposable !== null) {
|
||||
this.animationFrameDisposable.dispose();
|
||||
this.animationFrameDisposable = null;
|
||||
}
|
||||
}
|
||||
acceptScrollDimensions(state) {
|
||||
this.to = state.withScrollPosition(this.to);
|
||||
this._initAnimations();
|
||||
}
|
||||
tick() {
|
||||
return this._tick(Date.now());
|
||||
}
|
||||
_tick(now) {
|
||||
const completion = (now - this.startTime) / this.duration;
|
||||
if (completion < 1) {
|
||||
const newScrollLeft = this.scrollLeft(completion);
|
||||
const newScrollTop = this.scrollTop(completion);
|
||||
return new SmoothScrollingUpdate(newScrollLeft, newScrollTop, false);
|
||||
}
|
||||
return new SmoothScrollingUpdate(this.to.scrollLeft, this.to.scrollTop, true);
|
||||
}
|
||||
combine(from, to, duration) {
|
||||
return SmoothScrollingOperation.start(from, to, duration);
|
||||
}
|
||||
static start(from, to, duration) {
|
||||
// +10 / -10 : pretend the animation already started for a quicker response to a scroll request
|
||||
duration = duration + 10;
|
||||
const startTime = Date.now() - 10;
|
||||
return new SmoothScrollingOperation(from, to, startTime, duration);
|
||||
}
|
||||
}
|
||||
function easeInCubic(t) {
|
||||
return Math.pow(t, 3);
|
||||
}
|
||||
function easeOutCubic(t) {
|
||||
return 1 - easeInCubic(1 - t);
|
||||
}
|
||||
|
||||
export { ScrollState, Scrollable, SmoothScrollingOperation, SmoothScrollingUpdate };
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
import { containsUppercaseCharacter } from './strings.js';
|
||||
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
function buildReplaceStringWithCasePreserved(matches, pattern) {
|
||||
if (matches && (matches[0] !== '')) {
|
||||
const containsHyphens = validateSpecificSpecialCharacter(matches, pattern, '-');
|
||||
const containsUnderscores = validateSpecificSpecialCharacter(matches, pattern, '_');
|
||||
if (containsHyphens && !containsUnderscores) {
|
||||
return buildReplaceStringForSpecificSpecialCharacter(matches, pattern, '-');
|
||||
}
|
||||
else if (!containsHyphens && containsUnderscores) {
|
||||
return buildReplaceStringForSpecificSpecialCharacter(matches, pattern, '_');
|
||||
}
|
||||
if (matches[0].toUpperCase() === matches[0]) {
|
||||
return pattern.toUpperCase();
|
||||
}
|
||||
else if (matches[0].toLowerCase() === matches[0]) {
|
||||
return pattern.toLowerCase();
|
||||
}
|
||||
else if (containsUppercaseCharacter(matches[0][0]) && pattern.length > 0) {
|
||||
return pattern[0].toUpperCase() + pattern.substr(1);
|
||||
}
|
||||
else if (matches[0][0].toUpperCase() !== matches[0][0] && pattern.length > 0) {
|
||||
return pattern[0].toLowerCase() + pattern.substr(1);
|
||||
}
|
||||
else {
|
||||
// we don't understand its pattern yet.
|
||||
return pattern;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return pattern;
|
||||
}
|
||||
}
|
||||
function validateSpecificSpecialCharacter(matches, pattern, specialCharacter) {
|
||||
const doesContainSpecialCharacter = matches[0].indexOf(specialCharacter) !== -1 && pattern.indexOf(specialCharacter) !== -1;
|
||||
return doesContainSpecialCharacter && matches[0].split(specialCharacter).length === pattern.split(specialCharacter).length;
|
||||
}
|
||||
function buildReplaceStringForSpecificSpecialCharacter(matches, pattern, specialCharacter) {
|
||||
const splitPatternAtSpecialCharacter = pattern.split(specialCharacter);
|
||||
const splitMatchAtSpecialCharacter = matches[0].split(specialCharacter);
|
||||
let replaceString = '';
|
||||
splitPatternAtSpecialCharacter.forEach((splitValue, index) => {
|
||||
replaceString += buildReplaceStringWithCasePreserved([splitMatchAtSpecialCharacter[index]], splitValue) + specialCharacter;
|
||||
});
|
||||
return replaceString.slice(0, -1);
|
||||
}
|
||||
|
||||
export { buildReplaceStringWithCasePreserved };
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
import { equalsIgnoreCase } from './strings.js';
|
||||
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
var Severity;
|
||||
(function (Severity) {
|
||||
Severity[Severity["Ignore"] = 0] = "Ignore";
|
||||
Severity[Severity["Info"] = 1] = "Info";
|
||||
Severity[Severity["Warning"] = 2] = "Warning";
|
||||
Severity[Severity["Error"] = 3] = "Error";
|
||||
})(Severity || (Severity = {}));
|
||||
(function (Severity) {
|
||||
const _error = 'error';
|
||||
const _warning = 'warning';
|
||||
const _warn = 'warn';
|
||||
const _info = 'info';
|
||||
const _ignore = 'ignore';
|
||||
/**
|
||||
* Parses 'error', 'warning', 'warn', 'info' in call casings
|
||||
* and falls back to ignore.
|
||||
*/
|
||||
function fromValue(value) {
|
||||
if (!value) {
|
||||
return Severity.Ignore;
|
||||
}
|
||||
if (equalsIgnoreCase(_error, value)) {
|
||||
return Severity.Error;
|
||||
}
|
||||
if (equalsIgnoreCase(_warning, value) || equalsIgnoreCase(_warn, value)) {
|
||||
return Severity.Warning;
|
||||
}
|
||||
if (equalsIgnoreCase(_info, value)) {
|
||||
return Severity.Info;
|
||||
}
|
||||
return Severity.Ignore;
|
||||
}
|
||||
Severity.fromValue = fromValue;
|
||||
function toString(severity) {
|
||||
switch (severity) {
|
||||
case Severity.Error: return _error;
|
||||
case Severity.Warning: return _warning;
|
||||
case Severity.Info: return _info;
|
||||
default: return _ignore;
|
||||
}
|
||||
}
|
||||
Severity.toString = toString;
|
||||
})(Severity || (Severity = {}));
|
||||
var Severity$1 = Severity;
|
||||
|
||||
export { Severity$1 as default };
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
const performanceNow = globalThis.performance.now.bind(globalThis.performance);
|
||||
class StopWatch {
|
||||
static create(highResolution) {
|
||||
return new StopWatch(highResolution);
|
||||
}
|
||||
constructor(highResolution) {
|
||||
this._now = highResolution === false ? Date.now : performanceNow;
|
||||
this._startTime = this._now();
|
||||
this._stopTime = -1;
|
||||
}
|
||||
stop() {
|
||||
this._stopTime = this._now();
|
||||
}
|
||||
reset() {
|
||||
this._startTime = this._now();
|
||||
this._stopTime = -1;
|
||||
}
|
||||
elapsed() {
|
||||
if (this._stopTime !== -1) {
|
||||
return this._stopTime - this._startTime;
|
||||
}
|
||||
return this._now() - this._startTime;
|
||||
}
|
||||
}
|
||||
|
||||
export { StopWatch };
|
||||
+834
File diff suppressed because one or more lines are too long
+10
@@ -0,0 +1,10 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
/**
|
||||
* Can be passed into the Delayed to defer using a microtask
|
||||
* */
|
||||
const MicrotaskDelay = Symbol('MicrotaskDelay');
|
||||
|
||||
export { MicrotaskDelay };
|
||||
+674
@@ -0,0 +1,674 @@
|
||||
import { assert } from './assert.js';
|
||||
import { compareIgnoreCase, compare, compareSubstring, compareSubstringIgnoreCase } from './strings.js';
|
||||
|
||||
class StringIterator {
|
||||
constructor() {
|
||||
this._value = '';
|
||||
this._pos = 0;
|
||||
}
|
||||
reset(key) {
|
||||
this._value = key;
|
||||
this._pos = 0;
|
||||
return this;
|
||||
}
|
||||
next() {
|
||||
this._pos += 1;
|
||||
return this;
|
||||
}
|
||||
hasNext() {
|
||||
return this._pos < this._value.length - 1;
|
||||
}
|
||||
cmp(a) {
|
||||
const aCode = a.charCodeAt(0);
|
||||
const thisCode = this._value.charCodeAt(this._pos);
|
||||
return aCode - thisCode;
|
||||
}
|
||||
value() {
|
||||
return this._value[this._pos];
|
||||
}
|
||||
}
|
||||
class ConfigKeysIterator {
|
||||
constructor(_caseSensitive = true) {
|
||||
this._caseSensitive = _caseSensitive;
|
||||
}
|
||||
reset(key) {
|
||||
this._value = key;
|
||||
this._from = 0;
|
||||
this._to = 0;
|
||||
return this.next();
|
||||
}
|
||||
hasNext() {
|
||||
return this._to < this._value.length;
|
||||
}
|
||||
next() {
|
||||
// this._data = key.split(/[\\/]/).filter(s => !!s);
|
||||
this._from = this._to;
|
||||
let justSeps = true;
|
||||
for (; this._to < this._value.length; this._to++) {
|
||||
const ch = this._value.charCodeAt(this._to);
|
||||
if (ch === 46 /* CharCode.Period */) {
|
||||
if (justSeps) {
|
||||
this._from++;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
justSeps = false;
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
cmp(a) {
|
||||
return this._caseSensitive
|
||||
? compareSubstring(a, this._value, 0, a.length, this._from, this._to)
|
||||
: compareSubstringIgnoreCase(a, this._value, 0, a.length, this._from, this._to);
|
||||
}
|
||||
value() {
|
||||
return this._value.substring(this._from, this._to);
|
||||
}
|
||||
}
|
||||
class PathIterator {
|
||||
constructor(_splitOnBackslash = true, _caseSensitive = true) {
|
||||
this._splitOnBackslash = _splitOnBackslash;
|
||||
this._caseSensitive = _caseSensitive;
|
||||
}
|
||||
reset(key) {
|
||||
this._from = 0;
|
||||
this._to = 0;
|
||||
this._value = key;
|
||||
this._valueLen = key.length;
|
||||
for (let pos = key.length - 1; pos >= 0; pos--, this._valueLen--) {
|
||||
const ch = this._value.charCodeAt(pos);
|
||||
if (!(ch === 47 /* CharCode.Slash */ || this._splitOnBackslash && ch === 92 /* CharCode.Backslash */)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return this.next();
|
||||
}
|
||||
hasNext() {
|
||||
return this._to < this._valueLen;
|
||||
}
|
||||
next() {
|
||||
// this._data = key.split(/[\\/]/).filter(s => !!s);
|
||||
this._from = this._to;
|
||||
let justSeps = true;
|
||||
for (; this._to < this._valueLen; this._to++) {
|
||||
const ch = this._value.charCodeAt(this._to);
|
||||
if (ch === 47 /* CharCode.Slash */ || this._splitOnBackslash && ch === 92 /* CharCode.Backslash */) {
|
||||
if (justSeps) {
|
||||
this._from++;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
justSeps = false;
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
cmp(a) {
|
||||
return this._caseSensitive
|
||||
? compareSubstring(a, this._value, 0, a.length, this._from, this._to)
|
||||
: compareSubstringIgnoreCase(a, this._value, 0, a.length, this._from, this._to);
|
||||
}
|
||||
value() {
|
||||
return this._value.substring(this._from, this._to);
|
||||
}
|
||||
}
|
||||
class UriIterator {
|
||||
constructor(_ignorePathCasing, _ignoreQueryAndFragment) {
|
||||
this._ignorePathCasing = _ignorePathCasing;
|
||||
this._ignoreQueryAndFragment = _ignoreQueryAndFragment;
|
||||
this._states = [];
|
||||
this._stateIdx = 0;
|
||||
}
|
||||
reset(key) {
|
||||
this._value = key;
|
||||
this._states = [];
|
||||
if (this._value.scheme) {
|
||||
this._states.push(1 /* UriIteratorState.Scheme */);
|
||||
}
|
||||
if (this._value.authority) {
|
||||
this._states.push(2 /* UriIteratorState.Authority */);
|
||||
}
|
||||
if (this._value.path) {
|
||||
this._pathIterator = new PathIterator(false, !this._ignorePathCasing(key));
|
||||
this._pathIterator.reset(key.path);
|
||||
if (this._pathIterator.value()) {
|
||||
this._states.push(3 /* UriIteratorState.Path */);
|
||||
}
|
||||
}
|
||||
if (!this._ignoreQueryAndFragment(key)) {
|
||||
if (this._value.query) {
|
||||
this._states.push(4 /* UriIteratorState.Query */);
|
||||
}
|
||||
if (this._value.fragment) {
|
||||
this._states.push(5 /* UriIteratorState.Fragment */);
|
||||
}
|
||||
}
|
||||
this._stateIdx = 0;
|
||||
return this;
|
||||
}
|
||||
next() {
|
||||
if (this._states[this._stateIdx] === 3 /* UriIteratorState.Path */ && this._pathIterator.hasNext()) {
|
||||
this._pathIterator.next();
|
||||
}
|
||||
else {
|
||||
this._stateIdx += 1;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
hasNext() {
|
||||
return (this._states[this._stateIdx] === 3 /* UriIteratorState.Path */ && this._pathIterator.hasNext())
|
||||
|| this._stateIdx < this._states.length - 1;
|
||||
}
|
||||
cmp(a) {
|
||||
if (this._states[this._stateIdx] === 1 /* UriIteratorState.Scheme */) {
|
||||
return compareIgnoreCase(a, this._value.scheme);
|
||||
}
|
||||
else if (this._states[this._stateIdx] === 2 /* UriIteratorState.Authority */) {
|
||||
return compareIgnoreCase(a, this._value.authority);
|
||||
}
|
||||
else if (this._states[this._stateIdx] === 3 /* UriIteratorState.Path */) {
|
||||
return this._pathIterator.cmp(a);
|
||||
}
|
||||
else if (this._states[this._stateIdx] === 4 /* UriIteratorState.Query */) {
|
||||
return compare(a, this._value.query);
|
||||
}
|
||||
else if (this._states[this._stateIdx] === 5 /* UriIteratorState.Fragment */) {
|
||||
return compare(a, this._value.fragment);
|
||||
}
|
||||
throw new Error();
|
||||
}
|
||||
value() {
|
||||
if (this._states[this._stateIdx] === 1 /* UriIteratorState.Scheme */) {
|
||||
return this._value.scheme;
|
||||
}
|
||||
else if (this._states[this._stateIdx] === 2 /* UriIteratorState.Authority */) {
|
||||
return this._value.authority;
|
||||
}
|
||||
else if (this._states[this._stateIdx] === 3 /* UriIteratorState.Path */) {
|
||||
return this._pathIterator.value();
|
||||
}
|
||||
else if (this._states[this._stateIdx] === 4 /* UriIteratorState.Query */) {
|
||||
return this._value.query;
|
||||
}
|
||||
else if (this._states[this._stateIdx] === 5 /* UriIteratorState.Fragment */) {
|
||||
return this._value.fragment;
|
||||
}
|
||||
throw new Error();
|
||||
}
|
||||
}
|
||||
class Undef {
|
||||
static { this.Val = Symbol('undefined_placeholder'); }
|
||||
static wrap(value) {
|
||||
return value === undefined ? Undef.Val : value;
|
||||
}
|
||||
static unwrap(value) {
|
||||
return value === Undef.Val ? undefined : value;
|
||||
}
|
||||
}
|
||||
class TernarySearchTreeNode {
|
||||
constructor() {
|
||||
this.height = 1;
|
||||
this.value = undefined;
|
||||
this.key = undefined;
|
||||
this.left = undefined;
|
||||
this.mid = undefined;
|
||||
this.right = undefined;
|
||||
}
|
||||
rotateLeft() {
|
||||
const tmp = this.right;
|
||||
this.right = tmp.left;
|
||||
tmp.left = this;
|
||||
this.updateHeight();
|
||||
tmp.updateHeight();
|
||||
return tmp;
|
||||
}
|
||||
rotateRight() {
|
||||
const tmp = this.left;
|
||||
this.left = tmp.right;
|
||||
tmp.right = this;
|
||||
this.updateHeight();
|
||||
tmp.updateHeight();
|
||||
return tmp;
|
||||
}
|
||||
updateHeight() {
|
||||
this.height = 1 + Math.max(this.heightLeft, this.heightRight);
|
||||
}
|
||||
balanceFactor() {
|
||||
return this.heightRight - this.heightLeft;
|
||||
}
|
||||
get heightLeft() {
|
||||
return this.left?.height ?? 0;
|
||||
}
|
||||
get heightRight() {
|
||||
return this.right?.height ?? 0;
|
||||
}
|
||||
}
|
||||
class TernarySearchTree {
|
||||
static forUris(ignorePathCasing = () => false, ignoreQueryAndFragment = () => false) {
|
||||
return new TernarySearchTree(new UriIterator(ignorePathCasing, ignoreQueryAndFragment));
|
||||
}
|
||||
static forStrings() {
|
||||
return new TernarySearchTree(new StringIterator());
|
||||
}
|
||||
static forConfigKeys() {
|
||||
return new TernarySearchTree(new ConfigKeysIterator());
|
||||
}
|
||||
constructor(segments) {
|
||||
this._iter = segments;
|
||||
}
|
||||
clear() {
|
||||
this._root = undefined;
|
||||
}
|
||||
set(key, element) {
|
||||
const iter = this._iter.reset(key);
|
||||
let node;
|
||||
if (!this._root) {
|
||||
this._root = new TernarySearchTreeNode();
|
||||
this._root.segment = iter.value();
|
||||
}
|
||||
const stack = [];
|
||||
// find insert_node
|
||||
node = this._root;
|
||||
while (true) {
|
||||
const val = iter.cmp(node.segment);
|
||||
if (val > 0) {
|
||||
// left
|
||||
if (!node.left) {
|
||||
node.left = new TernarySearchTreeNode();
|
||||
node.left.segment = iter.value();
|
||||
}
|
||||
stack.push([-1 /* Dir.Left */, node]);
|
||||
node = node.left;
|
||||
}
|
||||
else if (val < 0) {
|
||||
// right
|
||||
if (!node.right) {
|
||||
node.right = new TernarySearchTreeNode();
|
||||
node.right.segment = iter.value();
|
||||
}
|
||||
stack.push([1 /* Dir.Right */, node]);
|
||||
node = node.right;
|
||||
}
|
||||
else if (iter.hasNext()) {
|
||||
// mid
|
||||
iter.next();
|
||||
if (!node.mid) {
|
||||
node.mid = new TernarySearchTreeNode();
|
||||
node.mid.segment = iter.value();
|
||||
}
|
||||
stack.push([0 /* Dir.Mid */, node]);
|
||||
node = node.mid;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// set value
|
||||
const oldElement = Undef.unwrap(node.value);
|
||||
node.value = Undef.wrap(element);
|
||||
node.key = key;
|
||||
// balance
|
||||
for (let i = stack.length - 1; i >= 0; i--) {
|
||||
const node = stack[i][1];
|
||||
node.updateHeight();
|
||||
const bf = node.balanceFactor();
|
||||
if (bf < -1 || bf > 1) {
|
||||
// needs rotate
|
||||
const d1 = stack[i][0];
|
||||
const d2 = stack[i + 1][0];
|
||||
if (d1 === 1 /* Dir.Right */ && d2 === 1 /* Dir.Right */) {
|
||||
//right, right -> rotate left
|
||||
stack[i][1] = node.rotateLeft();
|
||||
}
|
||||
else if (d1 === -1 /* Dir.Left */ && d2 === -1 /* Dir.Left */) {
|
||||
// left, left -> rotate right
|
||||
stack[i][1] = node.rotateRight();
|
||||
}
|
||||
else if (d1 === 1 /* Dir.Right */ && d2 === -1 /* Dir.Left */) {
|
||||
// right, left -> double rotate right, left
|
||||
node.right = stack[i + 1][1] = stack[i + 1][1].rotateRight();
|
||||
stack[i][1] = node.rotateLeft();
|
||||
}
|
||||
else if (d1 === -1 /* Dir.Left */ && d2 === 1 /* Dir.Right */) {
|
||||
// left, right -> double rotate left, right
|
||||
node.left = stack[i + 1][1] = stack[i + 1][1].rotateLeft();
|
||||
stack[i][1] = node.rotateRight();
|
||||
}
|
||||
else {
|
||||
throw new Error();
|
||||
}
|
||||
// patch path to parent
|
||||
if (i > 0) {
|
||||
switch (stack[i - 1][0]) {
|
||||
case -1 /* Dir.Left */:
|
||||
stack[i - 1][1].left = stack[i][1];
|
||||
break;
|
||||
case 1 /* Dir.Right */:
|
||||
stack[i - 1][1].right = stack[i][1];
|
||||
break;
|
||||
case 0 /* Dir.Mid */:
|
||||
stack[i - 1][1].mid = stack[i][1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
this._root = stack[0][1];
|
||||
}
|
||||
}
|
||||
}
|
||||
return oldElement;
|
||||
}
|
||||
get(key) {
|
||||
return Undef.unwrap(this._getNode(key)?.value);
|
||||
}
|
||||
_getNode(key) {
|
||||
const iter = this._iter.reset(key);
|
||||
let node = this._root;
|
||||
while (node) {
|
||||
const val = iter.cmp(node.segment);
|
||||
if (val > 0) {
|
||||
// left
|
||||
node = node.left;
|
||||
}
|
||||
else if (val < 0) {
|
||||
// right
|
||||
node = node.right;
|
||||
}
|
||||
else if (iter.hasNext()) {
|
||||
// mid
|
||||
iter.next();
|
||||
node = node.mid;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return node;
|
||||
}
|
||||
has(key) {
|
||||
const node = this._getNode(key);
|
||||
return !(node?.value === undefined && node?.mid === undefined);
|
||||
}
|
||||
delete(key) {
|
||||
return this._delete(key, false);
|
||||
}
|
||||
deleteSuperstr(key) {
|
||||
return this._delete(key, true);
|
||||
}
|
||||
_delete(key, superStr) {
|
||||
const iter = this._iter.reset(key);
|
||||
const stack = [];
|
||||
let node = this._root;
|
||||
// find node
|
||||
while (node) {
|
||||
const val = iter.cmp(node.segment);
|
||||
if (val > 0) {
|
||||
// left
|
||||
stack.push([-1 /* Dir.Left */, node]);
|
||||
node = node.left;
|
||||
}
|
||||
else if (val < 0) {
|
||||
// right
|
||||
stack.push([1 /* Dir.Right */, node]);
|
||||
node = node.right;
|
||||
}
|
||||
else if (iter.hasNext()) {
|
||||
// mid
|
||||
iter.next();
|
||||
stack.push([0 /* Dir.Mid */, node]);
|
||||
node = node.mid;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!node) {
|
||||
// node not found
|
||||
return;
|
||||
}
|
||||
if (superStr) {
|
||||
// removing children, reset height
|
||||
node.left = undefined;
|
||||
node.mid = undefined;
|
||||
node.right = undefined;
|
||||
node.height = 1;
|
||||
}
|
||||
else {
|
||||
// removing element
|
||||
node.key = undefined;
|
||||
node.value = undefined;
|
||||
}
|
||||
// BST node removal
|
||||
if (!node.mid && !node.value) {
|
||||
if (node.left && node.right) {
|
||||
// full node
|
||||
// replace deleted-node with the min-node of the right branch.
|
||||
// If there is no true min-node leave things as they are
|
||||
const stack2 = [[1 /* Dir.Right */, node]];
|
||||
const min = this._min(node.right, stack2);
|
||||
if (min.key) {
|
||||
node.key = min.key;
|
||||
node.value = min.value;
|
||||
node.segment = min.segment;
|
||||
// remove NODE (inorder successor can only have right child)
|
||||
const newChild = min.right;
|
||||
if (stack2.length > 1) {
|
||||
const [dir, parent] = stack2[stack2.length - 1];
|
||||
switch (dir) {
|
||||
case -1 /* Dir.Left */:
|
||||
parent.left = newChild;
|
||||
break;
|
||||
case 0 /* Dir.Mid */: assert(false);
|
||||
case 1 /* Dir.Right */: assert(false);
|
||||
}
|
||||
}
|
||||
else {
|
||||
node.right = newChild;
|
||||
}
|
||||
// balance right branch and UPDATE parent pointer for stack
|
||||
const newChild2 = this._balanceByStack(stack2);
|
||||
if (stack.length > 0) {
|
||||
const [dir, parent] = stack[stack.length - 1];
|
||||
switch (dir) {
|
||||
case -1 /* Dir.Left */:
|
||||
parent.left = newChild2;
|
||||
break;
|
||||
case 0 /* Dir.Mid */:
|
||||
parent.mid = newChild2;
|
||||
break;
|
||||
case 1 /* Dir.Right */:
|
||||
parent.right = newChild2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
this._root = newChild2;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// empty or half empty
|
||||
const newChild = node.left ?? node.right;
|
||||
if (stack.length > 0) {
|
||||
const [dir, parent] = stack[stack.length - 1];
|
||||
switch (dir) {
|
||||
case -1 /* Dir.Left */:
|
||||
parent.left = newChild;
|
||||
break;
|
||||
case 0 /* Dir.Mid */:
|
||||
parent.mid = newChild;
|
||||
break;
|
||||
case 1 /* Dir.Right */:
|
||||
parent.right = newChild;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
this._root = newChild;
|
||||
}
|
||||
}
|
||||
}
|
||||
// AVL balance
|
||||
this._root = this._balanceByStack(stack) ?? this._root;
|
||||
}
|
||||
_min(node, stack) {
|
||||
while (node.left) {
|
||||
stack.push([-1 /* Dir.Left */, node]);
|
||||
node = node.left;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
_balanceByStack(stack) {
|
||||
for (let i = stack.length - 1; i >= 0; i--) {
|
||||
const node = stack[i][1];
|
||||
node.updateHeight();
|
||||
const bf = node.balanceFactor();
|
||||
if (bf > 1) {
|
||||
// right heavy
|
||||
if (node.right.balanceFactor() >= 0) {
|
||||
// right, right -> rotate left
|
||||
stack[i][1] = node.rotateLeft();
|
||||
}
|
||||
else {
|
||||
// right, left -> double rotate
|
||||
node.right = node.right.rotateRight();
|
||||
stack[i][1] = node.rotateLeft();
|
||||
}
|
||||
}
|
||||
else if (bf < -1) {
|
||||
// left heavy
|
||||
if (node.left.balanceFactor() <= 0) {
|
||||
// left, left -> rotate right
|
||||
stack[i][1] = node.rotateRight();
|
||||
}
|
||||
else {
|
||||
// left, right -> double rotate
|
||||
node.left = node.left.rotateLeft();
|
||||
stack[i][1] = node.rotateRight();
|
||||
}
|
||||
}
|
||||
// patch path to parent
|
||||
if (i > 0) {
|
||||
switch (stack[i - 1][0]) {
|
||||
case -1 /* Dir.Left */:
|
||||
stack[i - 1][1].left = stack[i][1];
|
||||
break;
|
||||
case 1 /* Dir.Right */:
|
||||
stack[i - 1][1].right = stack[i][1];
|
||||
break;
|
||||
case 0 /* Dir.Mid */:
|
||||
stack[i - 1][1].mid = stack[i][1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return stack[0][1];
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
findSubstr(key) {
|
||||
const iter = this._iter.reset(key);
|
||||
let node = this._root;
|
||||
let candidate = undefined;
|
||||
while (node) {
|
||||
const val = iter.cmp(node.segment);
|
||||
if (val > 0) {
|
||||
// left
|
||||
node = node.left;
|
||||
}
|
||||
else if (val < 0) {
|
||||
// right
|
||||
node = node.right;
|
||||
}
|
||||
else if (iter.hasNext()) {
|
||||
// mid
|
||||
iter.next();
|
||||
candidate = Undef.unwrap(node.value) || candidate;
|
||||
node = node.mid;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return node && Undef.unwrap(node.value) || candidate;
|
||||
}
|
||||
findSuperstr(key) {
|
||||
return this._findSuperstrOrElement(key, false);
|
||||
}
|
||||
_findSuperstrOrElement(key, allowValue) {
|
||||
const iter = this._iter.reset(key);
|
||||
let node = this._root;
|
||||
while (node) {
|
||||
const val = iter.cmp(node.segment);
|
||||
if (val > 0) {
|
||||
// left
|
||||
node = node.left;
|
||||
}
|
||||
else if (val < 0) {
|
||||
// right
|
||||
node = node.right;
|
||||
}
|
||||
else if (iter.hasNext()) {
|
||||
// mid
|
||||
iter.next();
|
||||
node = node.mid;
|
||||
}
|
||||
else {
|
||||
// collect
|
||||
if (!node.mid) {
|
||||
if (allowValue) {
|
||||
return Undef.unwrap(node.value);
|
||||
}
|
||||
else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return this._entries(node.mid);
|
||||
}
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
forEach(callback) {
|
||||
for (const [key, value] of this) {
|
||||
callback(value, key);
|
||||
}
|
||||
}
|
||||
*[Symbol.iterator]() {
|
||||
yield* this._entries(this._root);
|
||||
}
|
||||
_entries(node) {
|
||||
const result = [];
|
||||
this._dfsEntries(node, result);
|
||||
return result[Symbol.iterator]();
|
||||
}
|
||||
_dfsEntries(node, bucket) {
|
||||
// DFS
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
if (node.left) {
|
||||
this._dfsEntries(node.left, bucket);
|
||||
}
|
||||
if (node.value !== undefined) {
|
||||
bucket.push([node.key, Undef.unwrap(node.value)]);
|
||||
}
|
||||
if (node.mid) {
|
||||
this._dfsEntries(node.mid, bucket);
|
||||
}
|
||||
if (node.right) {
|
||||
this._dfsEntries(node.right, bucket);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export { ConfigKeysIterator, PathIterator, StringIterator, TernarySearchTree, UriIterator };
|
||||
+179
@@ -0,0 +1,179 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
function countMapFrom(values) {
|
||||
const map = new Map();
|
||||
for (const value of values) {
|
||||
map.set(value, (map.get(value) ?? 0) + 1);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
/**
|
||||
* Implementation of tf-idf (term frequency-inverse document frequency) for a set of
|
||||
* documents where each document contains one or more chunks of text.
|
||||
* Each document is identified by a key, and the score for each document is computed
|
||||
* by taking the max score over all the chunks in the document.
|
||||
*/
|
||||
class TfIdfCalculator {
|
||||
constructor() {
|
||||
/**
|
||||
* Total number of chunks
|
||||
*/
|
||||
this.chunkCount = 0;
|
||||
this.chunkOccurrences = new Map();
|
||||
this.documents = new Map();
|
||||
}
|
||||
calculateScores(query, token) {
|
||||
const embedding = this.computeEmbedding(query);
|
||||
const idfCache = new Map();
|
||||
const scores = [];
|
||||
// For each document, generate one score
|
||||
for (const [key, doc] of this.documents) {
|
||||
if (token.isCancellationRequested) {
|
||||
return [];
|
||||
}
|
||||
for (const chunk of doc.chunks) {
|
||||
const score = this.computeSimilarityScore(chunk, embedding, idfCache);
|
||||
if (score > 0) {
|
||||
scores.push({ key, score });
|
||||
}
|
||||
}
|
||||
}
|
||||
return scores;
|
||||
}
|
||||
/**
|
||||
* Count how many times each term (word) appears in a string.
|
||||
*/
|
||||
static termFrequencies(input) {
|
||||
return countMapFrom(TfIdfCalculator.splitTerms(input));
|
||||
}
|
||||
/**
|
||||
* Break a string into terms (words).
|
||||
*/
|
||||
static *splitTerms(input) {
|
||||
const normalize = (word) => word.toLowerCase();
|
||||
// Only match on words that are at least 3 characters long and start with a letter
|
||||
for (const [word] of input.matchAll(/\b\p{Letter}[\p{Letter}\d]{2,}\b/gu)) {
|
||||
yield normalize(word);
|
||||
const camelParts = word.replace(/([a-z])([A-Z])/g, '$1 $2').split(/\s+/g);
|
||||
if (camelParts.length > 1) {
|
||||
for (const part of camelParts) {
|
||||
// Require at least 3 letters in the parts of a camel case word
|
||||
if (part.length > 2 && /\p{Letter}{3,}/gu.test(part)) {
|
||||
yield normalize(part);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
updateDocuments(documents) {
|
||||
for (const { key } of documents) {
|
||||
this.deleteDocument(key);
|
||||
}
|
||||
for (const doc of documents) {
|
||||
const chunks = [];
|
||||
for (const text of doc.textChunks) {
|
||||
// TODO: See if we can compute the tf lazily
|
||||
// The challenge is that we need to also update the `chunkOccurrences`
|
||||
// and all of those updates need to get flushed before the real TF-IDF of
|
||||
// anything is computed.
|
||||
const tf = TfIdfCalculator.termFrequencies(text);
|
||||
// Update occurrences list
|
||||
for (const term of tf.keys()) {
|
||||
this.chunkOccurrences.set(term, (this.chunkOccurrences.get(term) ?? 0) + 1);
|
||||
}
|
||||
chunks.push({ text, tf });
|
||||
}
|
||||
this.chunkCount += chunks.length;
|
||||
this.documents.set(doc.key, { chunks });
|
||||
}
|
||||
return this;
|
||||
}
|
||||
deleteDocument(key) {
|
||||
const doc = this.documents.get(key);
|
||||
if (!doc) {
|
||||
return;
|
||||
}
|
||||
this.documents.delete(key);
|
||||
this.chunkCount -= doc.chunks.length;
|
||||
// Update term occurrences for the document
|
||||
for (const chunk of doc.chunks) {
|
||||
for (const term of chunk.tf.keys()) {
|
||||
const currentOccurrences = this.chunkOccurrences.get(term);
|
||||
if (typeof currentOccurrences === 'number') {
|
||||
const newOccurrences = currentOccurrences - 1;
|
||||
if (newOccurrences <= 0) {
|
||||
this.chunkOccurrences.delete(term);
|
||||
}
|
||||
else {
|
||||
this.chunkOccurrences.set(term, newOccurrences);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
computeSimilarityScore(chunk, queryEmbedding, idfCache) {
|
||||
// Compute the dot product between the chunk's embedding and the query embedding
|
||||
// Note that the chunk embedding is computed lazily on a per-term basis.
|
||||
// This lets us skip a large number of calculations because the majority
|
||||
// of chunks do not share any terms with the query.
|
||||
let sum = 0;
|
||||
for (const [term, termTfidf] of Object.entries(queryEmbedding)) {
|
||||
const chunkTf = chunk.tf.get(term);
|
||||
if (!chunkTf) {
|
||||
// Term does not appear in chunk so it has no contribution
|
||||
continue;
|
||||
}
|
||||
let chunkIdf = idfCache.get(term);
|
||||
if (typeof chunkIdf !== 'number') {
|
||||
chunkIdf = this.computeIdf(term);
|
||||
idfCache.set(term, chunkIdf);
|
||||
}
|
||||
const chunkTfidf = chunkTf * chunkIdf;
|
||||
sum += chunkTfidf * termTfidf;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
computeEmbedding(input) {
|
||||
const tf = TfIdfCalculator.termFrequencies(input);
|
||||
return this.computeTfidf(tf);
|
||||
}
|
||||
computeIdf(term) {
|
||||
const chunkOccurrences = this.chunkOccurrences.get(term) ?? 0;
|
||||
return chunkOccurrences > 0
|
||||
? Math.log((this.chunkCount + 1) / chunkOccurrences)
|
||||
: 0;
|
||||
}
|
||||
computeTfidf(termFrequencies) {
|
||||
const embedding = Object.create(null);
|
||||
for (const [word, occurrences] of termFrequencies) {
|
||||
const idf = this.computeIdf(word);
|
||||
if (idf > 0) {
|
||||
embedding[word] = occurrences * idf;
|
||||
}
|
||||
}
|
||||
return embedding;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Normalize the scores to be between 0 and 1 and sort them decending.
|
||||
* @param scores array of scores from {@link TfIdfCalculator.calculateScores}
|
||||
* @returns normalized scores
|
||||
*/
|
||||
function normalizeTfIdfScores(scores) {
|
||||
// copy of scores
|
||||
const result = scores.slice(0);
|
||||
// sort descending
|
||||
result.sort((a, b) => b.score - a.score);
|
||||
// normalize
|
||||
const max = result[0]?.score ?? 0;
|
||||
if (max > 0) {
|
||||
for (const score of result) {
|
||||
score.score /= max;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export { TfIdfCalculator, normalizeTfIdfScores };
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
import { Codicon } from './codicons.js';
|
||||
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
var ThemeColor;
|
||||
(function (ThemeColor) {
|
||||
function isThemeColor(obj) {
|
||||
return !!obj && typeof obj === 'object' && typeof obj.id === 'string';
|
||||
}
|
||||
ThemeColor.isThemeColor = isThemeColor;
|
||||
})(ThemeColor || (ThemeColor = {}));
|
||||
var ThemeIcon;
|
||||
(function (ThemeIcon) {
|
||||
ThemeIcon.iconNameSegment = '[A-Za-z0-9]+';
|
||||
ThemeIcon.iconNameExpression = '[A-Za-z0-9-]+';
|
||||
ThemeIcon.iconModifierExpression = '~[A-Za-z]+';
|
||||
ThemeIcon.iconNameCharacter = '[A-Za-z0-9~-]';
|
||||
const ThemeIconIdRegex = new RegExp(`^(${ThemeIcon.iconNameExpression})(${ThemeIcon.iconModifierExpression})?$`);
|
||||
function asClassNameArray(icon) {
|
||||
const match = ThemeIconIdRegex.exec(icon.id);
|
||||
if (!match) {
|
||||
return asClassNameArray(Codicon.error);
|
||||
}
|
||||
const [, id, modifier] = match;
|
||||
const classNames = ['codicon', 'codicon-' + id];
|
||||
if (modifier) {
|
||||
classNames.push('codicon-modifier-' + modifier.substring(1));
|
||||
}
|
||||
return classNames;
|
||||
}
|
||||
ThemeIcon.asClassNameArray = asClassNameArray;
|
||||
function asClassName(icon) {
|
||||
return asClassNameArray(icon).join(' ');
|
||||
}
|
||||
ThemeIcon.asClassName = asClassName;
|
||||
function asCSSSelector(icon) {
|
||||
return '.' + asClassNameArray(icon).join('.');
|
||||
}
|
||||
ThemeIcon.asCSSSelector = asCSSSelector;
|
||||
function isThemeIcon(obj) {
|
||||
return !!obj && typeof obj === 'object' && typeof obj.id === 'string' && (typeof obj.color === 'undefined' || ThemeColor.isThemeColor(obj.color));
|
||||
}
|
||||
ThemeIcon.isThemeIcon = isThemeIcon;
|
||||
const _regexFromString = new RegExp(`^\\$\\((${ThemeIcon.iconNameExpression}(?:${ThemeIcon.iconModifierExpression})?)\\)$`);
|
||||
function fromString(str) {
|
||||
const match = _regexFromString.exec(str);
|
||||
if (!match) {
|
||||
return undefined;
|
||||
}
|
||||
const [, name] = match;
|
||||
return { id: name };
|
||||
}
|
||||
ThemeIcon.fromString = fromString;
|
||||
function fromId(id) {
|
||||
return { id };
|
||||
}
|
||||
ThemeIcon.fromId = fromId;
|
||||
function modify(icon, modifier) {
|
||||
let id = icon.id;
|
||||
const tildeIndex = id.lastIndexOf('~');
|
||||
if (tildeIndex !== -1) {
|
||||
id = id.substring(0, tildeIndex);
|
||||
}
|
||||
if (modifier) {
|
||||
id = `${id}~${modifier}`;
|
||||
}
|
||||
return { id };
|
||||
}
|
||||
ThemeIcon.modify = modify;
|
||||
function getModifier(icon) {
|
||||
const tildeIndex = icon.id.lastIndexOf('~');
|
||||
if (tildeIndex !== -1) {
|
||||
return icon.id.substring(tildeIndex + 1);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
ThemeIcon.getModifier = getModifier;
|
||||
function isEqual(ti1, ti2) {
|
||||
return ti1.id === ti2.id && ti1.color?.id === ti2.color?.id;
|
||||
}
|
||||
ThemeIcon.isEqual = isEqual;
|
||||
/**
|
||||
* Returns whether specified icon is defined and has 'file' ID.
|
||||
*/
|
||||
function isFile(icon) {
|
||||
return icon?.id === Codicon.file.id;
|
||||
}
|
||||
ThemeIcon.isFile = isFile;
|
||||
/**
|
||||
* Returns whether specified icon is defined and has 'folder' ID.
|
||||
*/
|
||||
function isFolder(icon) {
|
||||
return icon?.id === Codicon.folder.id;
|
||||
}
|
||||
ThemeIcon.isFolder = isFolder;
|
||||
})(ThemeIcon || (ThemeIcon = {}));
|
||||
|
||||
export { ThemeColor, ThemeIcon };
|
||||
+140
@@ -0,0 +1,140 @@
|
||||
import { assert } from './assert.js';
|
||||
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
/**
|
||||
* @returns whether the provided parameter is a JavaScript String or not.
|
||||
*/
|
||||
function isString(str) {
|
||||
return (typeof str === 'string');
|
||||
}
|
||||
/**
|
||||
* @returns whether the provided parameter is a JavaScript Array and each element in the array satisfies the provided type guard.
|
||||
*/
|
||||
function isArrayOf(value, check) {
|
||||
return Array.isArray(value) && value.every(check);
|
||||
}
|
||||
/**
|
||||
* @returns whether the provided parameter is of type `object` but **not**
|
||||
* `null`, an `array`, a `regexp`, nor a `date`.
|
||||
*/
|
||||
function isObject(obj) {
|
||||
// The method can't do a type cast since there are type (like strings) which
|
||||
// are subclasses of any put not positvely matched by the function. Hence type
|
||||
// narrowing results in wrong results.
|
||||
return typeof obj === 'object'
|
||||
&& obj !== null
|
||||
&& !Array.isArray(obj)
|
||||
&& !(obj instanceof RegExp)
|
||||
&& !(obj instanceof Date);
|
||||
}
|
||||
/**
|
||||
* @returns whether the provided parameter is of type `Buffer` or Uint8Array dervived type
|
||||
*/
|
||||
function isTypedArray(obj) {
|
||||
const TypedArray = Object.getPrototypeOf(Uint8Array);
|
||||
return typeof obj === 'object'
|
||||
&& obj instanceof TypedArray;
|
||||
}
|
||||
/**
|
||||
* In **contrast** to just checking `typeof` this will return `false` for `NaN`.
|
||||
* @returns whether the provided parameter is a JavaScript Number or not.
|
||||
*/
|
||||
function isNumber(obj) {
|
||||
return (typeof obj === 'number' && !isNaN(obj));
|
||||
}
|
||||
/**
|
||||
* @returns whether the provided parameter is an Iterable, casting to the given generic
|
||||
*/
|
||||
function isIterable(obj) {
|
||||
// eslint-disable-next-line local/code-no-any-casts
|
||||
return !!obj && typeof obj[Symbol.iterator] === 'function';
|
||||
}
|
||||
/**
|
||||
* @returns whether the provided parameter is a JavaScript Boolean or not.
|
||||
*/
|
||||
function isBoolean(obj) {
|
||||
return (obj === true || obj === false);
|
||||
}
|
||||
/**
|
||||
* @returns whether the provided parameter is undefined.
|
||||
*/
|
||||
function isUndefined(obj) {
|
||||
return (typeof obj === 'undefined');
|
||||
}
|
||||
/**
|
||||
* @returns whether the provided parameter is defined.
|
||||
*/
|
||||
function isDefined(arg) {
|
||||
return !isUndefinedOrNull(arg);
|
||||
}
|
||||
/**
|
||||
* @returns whether the provided parameter is undefined or null.
|
||||
*/
|
||||
function isUndefinedOrNull(obj) {
|
||||
return (isUndefined(obj) || obj === null);
|
||||
}
|
||||
function assertType(condition, type) {
|
||||
if (!condition) {
|
||||
throw new Error(type ? `Unexpected type, expected '${type}'` : 'Unexpected type');
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Asserts that the argument passed in is neither undefined nor null.
|
||||
*
|
||||
* @see {@link assertDefined} for a similar utility that leverages TS assertion functions to narrow down the type of `arg` to be non-nullable.
|
||||
*/
|
||||
function assertReturnsDefined(arg) {
|
||||
assert(arg !== null && arg !== undefined, 'Argument is `undefined` or `null`.');
|
||||
return arg;
|
||||
}
|
||||
/**
|
||||
* @returns whether the provided parameter is a JavaScript Function or not.
|
||||
*/
|
||||
function isFunction(obj) {
|
||||
return (typeof obj === 'function');
|
||||
}
|
||||
function validateConstraints(args, constraints) {
|
||||
const len = Math.min(args.length, constraints.length);
|
||||
for (let i = 0; i < len; i++) {
|
||||
validateConstraint(args[i], constraints[i]);
|
||||
}
|
||||
}
|
||||
function validateConstraint(arg, constraint) {
|
||||
if (isString(constraint)) {
|
||||
if (typeof arg !== constraint) {
|
||||
throw new Error(`argument does not match constraint: typeof ${constraint}`);
|
||||
}
|
||||
}
|
||||
else if (isFunction(constraint)) {
|
||||
try {
|
||||
if (arg instanceof constraint) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch {
|
||||
// ignore
|
||||
}
|
||||
// eslint-disable-next-line local/code-no-any-casts
|
||||
if (!isUndefinedOrNull(arg) && arg.constructor === constraint) {
|
||||
return;
|
||||
}
|
||||
if (constraint.length === 1 && constraint.call(undefined, arg) === true) {
|
||||
return;
|
||||
}
|
||||
throw new Error(`argument does not match one of these constraints: arg instanceof constraint, arg.constructor === constraint, nor constraint(arg) === true`);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Helper type assertion that safely upcasts a type to a supertype.
|
||||
*
|
||||
* This can be used to make sure the argument correctly conforms to the subtype while still being able to pass it
|
||||
* to contexts that expects the supertype.
|
||||
*/
|
||||
function upcast(x) {
|
||||
return x;
|
||||
}
|
||||
|
||||
export { assertReturnsDefined, assertType, isArrayOf, isBoolean, isDefined, isFunction, isIterable, isNumber, isObject, isString, isTypedArray, isUndefined, isUndefinedOrNull, upcast, validateConstraint, validateConstraints };
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user