Delete unused files

This commit is contained in:
Daniel Imms
2024-07-09 13:29:20 -07:00
parent 86c875f7e9
commit c33ed46161
88 changed files with 0 additions and 20344 deletions
-70
View File
@@ -1,70 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { mainWindow } from 'vs/base/browser/window';
import { getErrorMessage } from 'vs/base/common/errors';
import { Emitter } from 'vs/base/common/event';
import { Disposable, toDisposable } from 'vs/base/common/lifecycle';
export class BroadcastDataChannel<T> extends Disposable {
private broadcastChannel: BroadcastChannel | undefined;
private readonly _onDidReceiveData = this._register(new Emitter<T>());
readonly onDidReceiveData = this._onDidReceiveData.event;
constructor(private readonly channelName: string) {
super();
// Use BroadcastChannel
if ('BroadcastChannel' in mainWindow) {
try {
this.broadcastChannel = new BroadcastChannel(channelName);
const listener = (event: MessageEvent) => {
this._onDidReceiveData.fire(event.data);
};
this.broadcastChannel.addEventListener('message', listener);
this._register(toDisposable(() => {
if (this.broadcastChannel) {
this.broadcastChannel.removeEventListener('message', listener);
this.broadcastChannel.close();
}
}));
} catch (error) {
console.warn('Error while creating broadcast channel. Falling back to localStorage.', getErrorMessage(error));
}
}
// BroadcastChannel is not supported. Use storage.
if (!this.broadcastChannel) {
this.channelName = `BroadcastDataChannel.${channelName}`;
this.createBroadcastChannel();
}
}
private createBroadcastChannel(): void {
const listener = (event: StorageEvent) => {
if (event.key === this.channelName && event.newValue) {
this._onDidReceiveData.fire(JSON.parse(event.newValue));
}
};
mainWindow.addEventListener('storage', listener);
this._register(toDisposable(() => mainWindow.removeEventListener('storage', listener)));
}
/**
* Sends the data to other BroadcastChannel objects set up for this channel. Data can be structured objects, e.g. nested objects and arrays.
* @param data data to broadcast
*/
postData(data: T): void {
if (this.broadcastChannel) {
this.broadcastChannel.postMessage(data);
} else {
// remove previous changes so that event is triggered even if new changes are same as old changes
localStorage.removeItem(this.channelName);
localStorage.setItem(this.channelName, JSON.stringify(data));
}
}
}
-174
View File
@@ -1,174 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { createTrustedTypesPolicy } from 'vs/base/browser/trustedTypes';
import { onUnexpectedError } from 'vs/base/common/errors';
import { COI } from 'vs/base/common/network';
import { IWorker, IWorkerCallback, IWorkerFactory, logOnceWebWorkerWarning } from 'vs/base/common/worker/simpleWorker';
import { Disposable, toDisposable } from 'vs/base/common/lifecycle';
const ttPolicy = createTrustedTypesPolicy('defaultWorkerFactory', { createScriptURL: value => value });
export function createBlobWorker(blobUrl: string, options?: WorkerOptions): Worker {
if (!blobUrl.startsWith('blob:')) {
throw new URIError('Not a blob-url: ' + blobUrl);
}
return new Worker(ttPolicy ? ttPolicy.createScriptURL(blobUrl) as unknown as string : blobUrl, options);
}
function getWorker(label: string): Worker | Promise<Worker> {
// Option for hosts to overwrite the worker script (used in the standalone editor)
interface IMonacoEnvironment {
getWorker?(moduleId: string, label: string): Worker | Promise<Worker>;
getWorkerUrl?(moduleId: string, label: string): string;
}
const monacoEnvironment: IMonacoEnvironment | undefined = (globalThis as any).MonacoEnvironment;
if (monacoEnvironment) {
if (typeof monacoEnvironment.getWorker === 'function') {
return monacoEnvironment.getWorker('workerMain.js', label);
}
if (typeof monacoEnvironment.getWorkerUrl === 'function') {
const workerUrl = monacoEnvironment.getWorkerUrl('workerMain.js', label);
return new Worker(ttPolicy ? ttPolicy.createScriptURL(workerUrl) as unknown as string : workerUrl, { name: label });
}
}
// ESM-comment-begin
if (typeof require === 'function') {
// check if the JS lives on a different origin
const workerMain = require.toUrl('vs/base/worker/workerMain.js'); // explicitly using require.toUrl(), see https://github.com/microsoft/vscode/issues/107440#issuecomment-698982321
const workerUrl = getWorkerBootstrapUrl(workerMain, label);
return new Worker(ttPolicy ? ttPolicy.createScriptURL(workerUrl) as unknown as string : workerUrl, { name: label });
}
// ESM-comment-end
throw new Error(`You must define a function MonacoEnvironment.getWorkerUrl or MonacoEnvironment.getWorker`);
}
// ESM-comment-begin
export function getWorkerBootstrapUrl(scriptPath: string, label: string): string {
if (/^((http:)|(https:)|(file:))/.test(scriptPath) && scriptPath.substring(0, globalThis.origin.length) !== globalThis.origin) {
// this is the cross-origin case
// i.e. the webpage is running at a different origin than where the scripts are loaded from
} else {
const start = scriptPath.lastIndexOf('?');
const end = scriptPath.lastIndexOf('#', start);
const params = start > 0
? new URLSearchParams(scriptPath.substring(start + 1, ~end ? end : undefined))
: new URLSearchParams();
COI.addSearchParam(params, true, true);
const search = params.toString();
if (!search) {
scriptPath = `${scriptPath}#${label}`;
} else {
scriptPath = `${scriptPath}?${params.toString()}#${label}`;
}
}
const factoryModuleId = 'vs/base/worker/defaultWorkerFactory.js';
const workerBaseUrl = require.toUrl(factoryModuleId).slice(0, -factoryModuleId.length); // explicitly using require.toUrl(), see https://github.com/microsoft/vscode/issues/107440#issuecomment-698982321
const blob = new Blob([[
`/*${label}*/`,
`globalThis.MonacoEnvironment = { baseUrl: '${workerBaseUrl}' };`,
// VSCODE_GLOBALS: NLS
`globalThis._VSCODE_NLS_MESSAGES = ${JSON.stringify(globalThis._VSCODE_NLS_MESSAGES)};`,
`globalThis._VSCODE_NLS_LANGUAGE = ${JSON.stringify(globalThis._VSCODE_NLS_LANGUAGE)};`,
`const ttPolicy = globalThis.trustedTypes?.createPolicy('defaultWorkerFactory', { createScriptURL: value => value });`,
`importScripts(ttPolicy?.createScriptURL('${scriptPath}') ?? '${scriptPath}');`,
`/*${label}*/`
].join('')], { type: 'application/javascript' });
return URL.createObjectURL(blob);
}
// ESM-comment-end
function isPromiseLike<T>(obj: any): obj is PromiseLike<T> {
if (typeof obj.then === 'function') {
return true;
}
return false;
}
/**
* A worker that uses HTML5 web workers so that is has
* its own global scope and its own thread.
*/
class WebWorker extends Disposable implements IWorker {
private readonly id: number;
private readonly label: string;
private worker: Promise<Worker> | null;
constructor(moduleId: string, id: number, label: string, onMessageCallback: IWorkerCallback, onErrorCallback: (err: any) => void) {
super();
this.id = id;
this.label = label;
const workerOrPromise = getWorker(label);
if (isPromiseLike(workerOrPromise)) {
this.worker = workerOrPromise;
} else {
this.worker = Promise.resolve(workerOrPromise);
}
this.postMessage(moduleId, []);
this.worker.then((w) => {
w.onmessage = function (ev) {
onMessageCallback(ev.data);
};
w.onmessageerror = onErrorCallback;
if (typeof w.addEventListener === 'function') {
w.addEventListener('error', onErrorCallback);
}
});
this._register(toDisposable(() => {
this.worker?.then(w => {
w.onmessage = null;
w.onmessageerror = null;
w.removeEventListener('error', onErrorCallback);
w.terminate();
});
this.worker = null;
}));
}
public getId(): number {
return this.id;
}
public postMessage(message: any, transfer: Transferable[]): void {
this.worker?.then(w => {
try {
w.postMessage(message, transfer);
} catch (err) {
onUnexpectedError(err);
onUnexpectedError(new Error(`FAILED to post message to '${this.label}'-worker`, { cause: err }));
}
});
}
}
export class DefaultWorkerFactory implements IWorkerFactory {
private static LAST_WORKER_ID = 0;
private _label: string | undefined;
private _webWorkerFailedBeforeError: any;
constructor(label: string | undefined) {
this._label = label;
this._webWorkerFailedBeforeError = false;
}
public create(moduleId: string, onMessageCallback: IWorkerCallback, onErrorCallback: (err: any) => void): IWorker {
const workerId = (++DefaultWorkerFactory.LAST_WORKER_ID);
if (this._webWorkerFailedBeforeError) {
throw this._webWorkerFailedBeforeError;
}
return new WebWorker(moduleId, workerId, this._label || 'anonymous' + workerId, onMessageCallback, (err) => {
logOnceWebWorkerWarning(err);
this._webWorkerFailedBeforeError = err;
onErrorCallback(err);
});
}
}
-108
View File
@@ -1,108 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
// https://wicg.github.io/webusb/
export interface UsbDeviceData {
readonly deviceClass: number;
readonly deviceProtocol: number;
readonly deviceSubclass: number;
readonly deviceVersionMajor: number;
readonly deviceVersionMinor: number;
readonly deviceVersionSubminor: number;
readonly manufacturerName?: string;
readonly productId: number;
readonly productName?: string;
readonly serialNumber?: string;
readonly usbVersionMajor: number;
readonly usbVersionMinor: number;
readonly usbVersionSubminor: number;
readonly vendorId: number;
}
export async function requestUsbDevice(options?: { filters?: unknown[] }): Promise<UsbDeviceData | undefined> {
const usb = (navigator as any).usb;
if (!usb) {
return undefined;
}
const device = await usb.requestDevice({ filters: options?.filters ?? [] });
if (!device) {
return undefined;
}
return {
deviceClass: device.deviceClass,
deviceProtocol: device.deviceProtocol,
deviceSubclass: device.deviceSubclass,
deviceVersionMajor: device.deviceVersionMajor,
deviceVersionMinor: device.deviceVersionMinor,
deviceVersionSubminor: device.deviceVersionSubminor,
manufacturerName: device.manufacturerName,
productId: device.productId,
productName: device.productName,
serialNumber: device.serialNumber,
usbVersionMajor: device.usbVersionMajor,
usbVersionMinor: device.usbVersionMinor,
usbVersionSubminor: device.usbVersionSubminor,
vendorId: device.vendorId,
};
}
// https://wicg.github.io/serial/
export interface SerialPortData {
readonly usbVendorId?: number | undefined;
readonly usbProductId?: number | undefined;
}
export async function requestSerialPort(options?: { filters?: unknown[] }): Promise<SerialPortData | undefined> {
const serial = (navigator as any).serial;
if (!serial) {
return undefined;
}
const port = await serial.requestPort({ filters: options?.filters ?? [] });
if (!port) {
return undefined;
}
const info = port.getInfo();
return {
usbVendorId: info.usbVendorId,
usbProductId: info.usbProductId
};
}
// https://wicg.github.io/webhid/
export interface HidDeviceData {
readonly opened: boolean;
readonly vendorId: number;
readonly productId: number;
readonly productName: string;
readonly collections: [];
}
export async function requestHidDevice(options?: { filters?: unknown[] }): Promise<HidDeviceData | undefined> {
const hid = (navigator as any).hid;
if (!hid) {
return undefined;
}
const devices = await hid.requestDevice({ filters: options?.filters ?? [] });
if (!devices.length) {
return undefined;
}
const device = devices[0];
return {
opened: device.opened,
vendorId: device.vendorId,
productId: device.productId,
productName: device.productName,
collections: device.collections
};
}
-110
View File
@@ -1,110 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { addDisposableListener, getWindow } from 'vs/base/browser/dom';
import { Disposable } from 'vs/base/common/lifecycle';
import { Mimes } from 'vs/base/common/mime';
/**
* A helper that will execute a provided function when the provided HTMLElement receives
* dragover event for 800ms. If the drag is aborted before, the callback will not be triggered.
*/
export class DelayedDragHandler extends Disposable {
private timeout: any;
constructor(container: HTMLElement, callback: () => void) {
super();
this._register(addDisposableListener(container, 'dragover', e => {
e.preventDefault(); // needed so that the drop event fires (https://stackoverflow.com/questions/21339924/drop-event-not-firing-in-chrome)
if (!this.timeout) {
this.timeout = setTimeout(() => {
callback();
this.timeout = null;
}, 800);
}
}));
['dragleave', 'drop', 'dragend'].forEach(type => {
this._register(addDisposableListener(container, type, () => {
this.clearDragTimeout();
}));
});
}
private clearDragTimeout(): void {
if (this.timeout) {
clearTimeout(this.timeout);
this.timeout = null;
}
}
override dispose(): void {
super.dispose();
this.clearDragTimeout();
}
}
// Common data transfers
export const DataTransfers = {
/**
* Application specific resource transfer type
*/
RESOURCES: 'ResourceURLs',
/**
* Browser specific transfer type to download
*/
DOWNLOAD_URL: 'DownloadURL',
/**
* Browser specific transfer type for files
*/
FILES: 'Files',
/**
* Typically transfer type for copy/paste transfers.
*/
TEXT: Mimes.text,
/**
* Internal type used to pass around text/uri-list data.
*
* This is needed to work around https://bugs.chromium.org/p/chromium/issues/detail?id=239745.
*/
INTERNAL_URI_LIST: 'application/vnd.code.uri-list',
};
export function applyDragImage(event: DragEvent, label: string | null, clazz: string, backgroundColor?: string | null, foregroundColor?: string | null): void {
const dragImage = document.createElement('div');
dragImage.className = clazz;
dragImage.textContent = label;
if (foregroundColor) {
dragImage.style.color = foregroundColor;
}
if (backgroundColor) {
dragImage.style.background = backgroundColor;
}
if (event.dataTransfer) {
const ownerDocument = getWindow(event).document;
ownerDocument.body.appendChild(dragImage);
event.dataTransfer.setDragImage(dragImage, -10, -10);
// Removes the element when the DND operation is done
setTimeout(() => dragImage.remove(), 0);
}
}
export interface IDragAndDropData {
update(dataTransfer: DataTransfer): void;
getData(): unknown;
}
-17
View File
@@ -1,17 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { createStyleSheet2 } from 'vs/base/browser/dom';
import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle';
import { autorun, IObservable } from 'vs/base/common/observable';
export function createStyleSheetFromObservable(css: IObservable<string>): IDisposable {
const store = new DisposableStore();
const w = store.add(createStyleSheet2());
store.add(autorun(reader => {
w.setStyle(css.read(reader));
}));
return store;
}
-50
View File
@@ -1,50 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { GestureEvent } from 'vs/base/browser/touch';
import { Emitter, Event as BaseEvent } from 'vs/base/common/event';
import { IDisposable } from 'vs/base/common/lifecycle';
export type EventHandler = HTMLElement | HTMLDocument | Window;
export interface IDomEvent {
<K extends keyof HTMLElementEventMap>(element: EventHandler, type: K, useCapture?: boolean): BaseEvent<HTMLElementEventMap[K]>;
(element: EventHandler, type: string, useCapture?: boolean): BaseEvent<unknown>;
}
export interface DOMEventMap extends HTMLElementEventMap, DocumentEventMap, WindowEventMap {
'-monaco-gesturetap': GestureEvent;
'-monaco-gesturechange': GestureEvent;
'-monaco-gesturestart': GestureEvent;
'-monaco-gesturesend': GestureEvent;
'-monaco-gesturecontextmenu': GestureEvent;
'compositionstart': CompositionEvent;
'compositionupdate': CompositionEvent;
'compositionend': CompositionEvent;
}
export class DomEmitter<K extends keyof DOMEventMap> implements IDisposable {
private emitter: Emitter<DOMEventMap[K]>;
get event(): BaseEvent<DOMEventMap[K]> {
return this.emitter.event;
}
constructor(element: Window & typeof globalThis, type: WindowEventMap, useCapture?: boolean);
constructor(element: Document, type: DocumentEventMap, useCapture?: boolean);
constructor(element: EventHandler, type: K, useCapture?: boolean);
constructor(element: EventHandler, type: K, useCapture?: boolean) {
const fn = (e: Event) => this.emitter.fire(e as DOMEventMap[K]);
this.emitter = new Emitter({
onWillAddFirstListener: () => element.addEventListener(type, fn, useCapture),
onDidRemoveLastListener: () => element.removeEventListener(type, fn, useCapture)
});
}
dispose(): void {
this.emitter.dispose();
}
}
-16
View File
@@ -1,16 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { isMacintosh, isWindows } from 'vs/base/common/platform';
/**
* The best font-family to be used in CSS based on the platform:
* - Windows: Segoe preferred, fallback to sans-serif
* - macOS: standard system font, fallback to sans-serif
* - Linux: standard system font preferred, fallback to Ubuntu fonts
*
* Note: this currently does not adjust for different locales.
*/
export const DEFAULT_FONT_FAMILY = isWindows ? '"Segoe WPC", "Segoe UI", sans-serif' : isMacintosh ? '-apple-system, BlinkMacSystemFont, sans-serif' : 'system-ui, "Ubuntu", "Droid Sans", sans-serif';
@@ -1,226 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as DOM from 'vs/base/browser/dom';
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { IMouseEvent } from 'vs/base/browser/mouseEvent';
import { DisposableStore } from 'vs/base/common/lifecycle';
export interface IContentActionHandler {
callback: (content: string, event: IMouseEvent | IKeyboardEvent) => void;
readonly disposables: DisposableStore;
}
export interface FormattedTextRenderOptions {
readonly className?: string;
readonly inline?: boolean;
readonly actionHandler?: IContentActionHandler;
readonly renderCodeSegments?: boolean;
}
export function renderText(text: string, options: FormattedTextRenderOptions = {}): HTMLElement {
const element = createElement(options);
element.textContent = text;
return element;
}
export function renderFormattedText(formattedText: string, options: FormattedTextRenderOptions = {}): HTMLElement {
const element = createElement(options);
_renderFormattedText(element, parseFormattedText(formattedText, !!options.renderCodeSegments), options.actionHandler, options.renderCodeSegments);
return element;
}
export function createElement(options: FormattedTextRenderOptions): HTMLElement {
const tagName = options.inline ? 'span' : 'div';
const element = document.createElement(tagName);
if (options.className) {
element.className = options.className;
}
return element;
}
class StringStream {
private source: string;
private index: number;
constructor(source: string) {
this.source = source;
this.index = 0;
}
public eos(): boolean {
return this.index >= this.source.length;
}
public next(): string {
const next = this.peek();
this.advance();
return next;
}
public peek(): string {
return this.source[this.index];
}
public advance(): void {
this.index++;
}
}
const enum FormatType {
Invalid,
Root,
Text,
Bold,
Italics,
Action,
ActionClose,
Code,
NewLine
}
interface IFormatParseTree {
type: FormatType;
content?: string;
index?: number;
children?: IFormatParseTree[];
}
function _renderFormattedText(element: Node, treeNode: IFormatParseTree, actionHandler?: IContentActionHandler, renderCodeSegments?: boolean) {
let child: Node | undefined;
if (treeNode.type === FormatType.Text) {
child = document.createTextNode(treeNode.content || '');
} else if (treeNode.type === FormatType.Bold) {
child = document.createElement('b');
} else if (treeNode.type === FormatType.Italics) {
child = document.createElement('i');
} else if (treeNode.type === FormatType.Code && renderCodeSegments) {
child = document.createElement('code');
} else if (treeNode.type === FormatType.Action && actionHandler) {
const a = document.createElement('a');
actionHandler.disposables.add(DOM.addStandardDisposableListener(a, 'click', (event) => {
actionHandler.callback(String(treeNode.index), event);
}));
child = a;
} else if (treeNode.type === FormatType.NewLine) {
child = document.createElement('br');
} else if (treeNode.type === FormatType.Root) {
child = element;
}
if (child && element !== child) {
element.appendChild(child);
}
if (child && Array.isArray(treeNode.children)) {
treeNode.children.forEach((nodeChild) => {
_renderFormattedText(child, nodeChild, actionHandler, renderCodeSegments);
});
}
}
function parseFormattedText(content: string, parseCodeSegments: boolean): IFormatParseTree {
const root: IFormatParseTree = {
type: FormatType.Root,
children: []
};
let actionViewItemIndex = 0;
let current = root;
const stack: IFormatParseTree[] = [];
const stream = new StringStream(content);
while (!stream.eos()) {
let next = stream.next();
const isEscapedFormatType = (next === '\\' && formatTagType(stream.peek(), parseCodeSegments) !== FormatType.Invalid);
if (isEscapedFormatType) {
next = stream.next(); // unread the backslash if it escapes a format tag type
}
if (!isEscapedFormatType && isFormatTag(next, parseCodeSegments) && next === stream.peek()) {
stream.advance();
if (current.type === FormatType.Text) {
current = stack.pop()!;
}
const type = formatTagType(next, parseCodeSegments);
if (current.type === type || (current.type === FormatType.Action && type === FormatType.ActionClose)) {
current = stack.pop()!;
} else {
const newCurrent: IFormatParseTree = {
type: type,
children: []
};
if (type === FormatType.Action) {
newCurrent.index = actionViewItemIndex;
actionViewItemIndex++;
}
current.children!.push(newCurrent);
stack.push(current);
current = newCurrent;
}
} else if (next === '\n') {
if (current.type === FormatType.Text) {
current = stack.pop()!;
}
current.children!.push({
type: FormatType.NewLine
});
} else {
if (current.type !== FormatType.Text) {
const textCurrent: IFormatParseTree = {
type: FormatType.Text,
content: next
};
current.children!.push(textCurrent);
stack.push(current);
current = textCurrent;
} else {
current.content += next;
}
}
}
if (current.type === FormatType.Text) {
current = stack.pop()!;
}
if (stack.length) {
// incorrectly formatted string literal
}
return root;
}
function isFormatTag(char: string, supportCodeSegments: boolean): boolean {
return formatTagType(char, supportCodeSegments) !== FormatType.Invalid;
}
function formatTagType(char: string, supportCodeSegments: boolean): FormatType {
switch (char) {
case '*':
return FormatType.Bold;
case '_':
return FormatType.Italics;
case '[':
return FormatType.Action;
case ']':
return FormatType.ActionClose;
case '`':
return supportCodeSegments ? FormatType.Code : FormatType.Invalid;
default:
return FormatType.Invalid;
}
}
-32
View File
@@ -1,32 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { VSBuffer } from 'vs/base/common/buffer';
import { StringSHA1, toHexString } from 'vs/base/common/hash';
export async function sha1Hex(str: string): Promise<string> {
// Prefer to use browser's crypto module
if (globalThis?.crypto?.subtle) {
// Careful to use `dontUseNodeBuffer` when passing the
// buffer to the browser `crypto` API. Users reported
// native crashes in certain cases that we could trace
// back to passing node.js `Buffer` around
// (https://github.com/microsoft/vscode/issues/114227)
const buffer = VSBuffer.fromString(str, { dontUseNodeBuffer: true }).buffer;
const hash = await globalThis.crypto.subtle.digest({ name: 'sha-1' }, buffer);
return toHexString(hash);
}
// Otherwise fallback to `StringSHA1`
else {
const computer = new StringSHA1();
computer.update(str);
return computer.digest();
}
}
-20
View File
@@ -1,20 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Event } from 'vs/base/common/event';
export interface IHistoryNavigationWidget {
readonly element: HTMLElement;
showPreviousValue(): void;
showNextValue(): void;
onDidFocus: Event<void>;
onDidBlur: Event<void>;
}
-177
View File
@@ -1,177 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { toErrorMessage } from 'vs/base/common/errorMessage';
import { ErrorNoTelemetry, getErrorMessage } from 'vs/base/common/errors';
import { mark } from 'vs/base/common/performance';
class MissingStoresError extends Error {
constructor(readonly db: IDBDatabase) {
super('Missing stores');
}
}
export class DBClosedError extends Error {
readonly code = 'DBClosed';
constructor(dbName: string) {
super(`IndexedDB database '${dbName}' is closed.`);
}
}
export class IndexedDB {
static async create(name: string, version: number | undefined, stores: string[]): Promise<IndexedDB> {
const database = await IndexedDB.openDatabase(name, version, stores);
return new IndexedDB(database, name);
}
private static async openDatabase(name: string, version: number | undefined, stores: string[]): Promise<IDBDatabase> {
mark(`code/willOpenDatabase/${name}`);
try {
return await IndexedDB.doOpenDatabase(name, version, stores);
} catch (err) {
if (err instanceof MissingStoresError) {
console.info(`Attempting to recreate the IndexedDB once.`, name);
try {
// Try to delete the db
await IndexedDB.deleteDatabase(err.db);
} catch (error) {
console.error(`Error while deleting the IndexedDB`, getErrorMessage(error));
throw error;
}
return await IndexedDB.doOpenDatabase(name, version, stores);
}
throw err;
} finally {
mark(`code/didOpenDatabase/${name}`);
}
}
private static doOpenDatabase(name: string, version: number | undefined, stores: string[]): Promise<IDBDatabase> {
return new Promise((c, e) => {
const request = indexedDB.open(name, version);
request.onerror = () => e(request.error);
request.onsuccess = () => {
const db = request.result;
for (const store of stores) {
if (!db.objectStoreNames.contains(store)) {
console.error(`Error while opening IndexedDB. Could not find '${store}'' object store`);
e(new MissingStoresError(db));
return;
}
}
c(db);
};
request.onupgradeneeded = () => {
const db = request.result;
for (const store of stores) {
if (!db.objectStoreNames.contains(store)) {
db.createObjectStore(store);
}
}
};
});
}
private static deleteDatabase(database: IDBDatabase): Promise<void> {
return new Promise((c, e) => {
// Close any opened connections
database.close();
// Delete the db
const deleteRequest = indexedDB.deleteDatabase(database.name);
deleteRequest.onerror = (err) => e(deleteRequest.error);
deleteRequest.onsuccess = () => c();
});
}
private database: IDBDatabase | null = null;
private readonly pendingTransactions: IDBTransaction[] = [];
constructor(database: IDBDatabase, private readonly name: string) {
this.database = database;
}
hasPendingTransactions(): boolean {
return this.pendingTransactions.length > 0;
}
close(): void {
if (this.pendingTransactions.length) {
this.pendingTransactions.splice(0, this.pendingTransactions.length).forEach(transaction => transaction.abort());
}
this.database?.close();
this.database = null;
}
runInTransaction<T>(store: string, transactionMode: IDBTransactionMode, dbRequestFn: (store: IDBObjectStore) => IDBRequest<T>[]): Promise<T[]>;
runInTransaction<T>(store: string, transactionMode: IDBTransactionMode, dbRequestFn: (store: IDBObjectStore) => IDBRequest<T>): Promise<T>;
async runInTransaction<T>(store: string, transactionMode: IDBTransactionMode, dbRequestFn: (store: IDBObjectStore) => IDBRequest<T> | IDBRequest<T>[]): Promise<T | T[]> {
if (!this.database) {
throw new DBClosedError(this.name);
}
const transaction = this.database.transaction(store, transactionMode);
this.pendingTransactions.push(transaction);
return new Promise<T | T[]>((c, e) => {
transaction.oncomplete = () => {
if (Array.isArray(request)) {
c(request.map(r => r.result));
} else {
c(request.result);
}
};
transaction.onerror = () => e(transaction.error ? ErrorNoTelemetry.fromError(transaction.error) : new ErrorNoTelemetry('unknown error'));
transaction.onabort = () => e(transaction.error ? ErrorNoTelemetry.fromError(transaction.error) : new ErrorNoTelemetry('unknown error'));
const request = dbRequestFn(transaction.objectStore(store));
}).finally(() => this.pendingTransactions.splice(this.pendingTransactions.indexOf(transaction), 1));
}
async getKeyValues<V>(store: string, isValid: (value: unknown) => value is V): Promise<Map<string, V>> {
if (!this.database) {
throw new DBClosedError(this.name);
}
const transaction = this.database.transaction(store, 'readonly');
this.pendingTransactions.push(transaction);
return new Promise<Map<string, V>>(resolve => {
const items = new Map<string, V>();
const objectStore = transaction.objectStore(store);
// Open a IndexedDB Cursor to iterate over key/values
const cursor = objectStore.openCursor();
if (!cursor) {
return resolve(items); // this means the `ItemTable` was empty
}
// Iterate over rows of `ItemTable` until the end
cursor.onsuccess = () => {
if (cursor.result) {
// Keep cursor key/value in our map
if (isValid(cursor.result.value)) {
items.set(cursor.result.key.toString(), cursor.result.value);
}
// Advance cursor to next row
cursor.result.continue();
} else {
resolve(items); // reached end of table
}
};
// Error handlers
const onError = (error: Error | null) => {
console.error(`IndexedDB getKeyValues(): ${toErrorMessage(error, true)}`);
resolve(items);
};
cursor.onerror = () => onError(cursor.error);
transaction.onerror = () => onError(transaction.error);
}).finally(() => this.pendingTransactions.splice(this.pendingTransactions.indexOf(transaction), 1));
}
}
-9
View File
@@ -1,9 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.monaco-aria-container {
position: absolute; /* try to hide from window but not from screen readers */
left:-999em;
}
-163
View File
@@ -1,163 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as dom from 'vs/base/browser/dom';
// import 'vs/css!./aria';
// Use a max length since we are inserting the whole msg in the DOM and that can cause browsers to freeze for long messages #94233
const MAX_MESSAGE_LENGTH = 20000;
let ariaContainer: HTMLElement;
let alertContainer: HTMLElement;
let alertContainer2: HTMLElement;
let statusContainer: HTMLElement;
let statusContainer2: HTMLElement;
export function setARIAContainer(parent: HTMLElement) {
ariaContainer = document.createElement('div');
ariaContainer.className = 'monaco-aria-container';
const createAlertContainer = () => {
const element = document.createElement('div');
element.className = 'monaco-alert';
element.setAttribute('role', 'alert');
element.setAttribute('aria-atomic', 'true');
ariaContainer.appendChild(element);
return element;
};
alertContainer = createAlertContainer();
alertContainer2 = createAlertContainer();
const createStatusContainer = () => {
const element = document.createElement('div');
element.className = 'monaco-status';
element.setAttribute('aria-live', 'polite');
element.setAttribute('aria-atomic', 'true');
ariaContainer.appendChild(element);
return element;
};
statusContainer = createStatusContainer();
statusContainer2 = createStatusContainer();
parent.appendChild(ariaContainer);
}
/**
* Given the provided message, will make sure that it is read as alert to screen readers.
*/
export function alert(msg: string): void {
if (!ariaContainer) {
return;
}
// Use alternate containers such that duplicated messages get read out by screen readers #99466
if (alertContainer.textContent !== msg) {
dom.clearNode(alertContainer2);
insertMessage(alertContainer, msg);
} else {
dom.clearNode(alertContainer);
insertMessage(alertContainer2, msg);
}
}
/**
* Given the provided message, will make sure that it is read as status to screen readers.
*/
export function status(msg: string): void {
if (!ariaContainer) {
return;
}
if (statusContainer.textContent !== msg) {
dom.clearNode(statusContainer2);
insertMessage(statusContainer, msg);
} else {
dom.clearNode(statusContainer);
insertMessage(statusContainer2, msg);
}
}
function insertMessage(target: HTMLElement, msg: string): void {
dom.clearNode(target);
if (msg.length > MAX_MESSAGE_LENGTH) {
msg = msg.substr(0, MAX_MESSAGE_LENGTH);
}
target.textContent = msg;
// See https://www.paciellogroup.com/blog/2012/06/html5-accessibility-chops-aria-rolealert-browser-support/
target.style.visibility = 'hidden';
target.style.visibility = 'visible';
}
// Copied from @types/react which original came from https://www.w3.org/TR/wai-aria-1.1/#role_definitions
export type AriaRole =
| 'alert'
| 'alertdialog'
| 'application'
| 'article'
| 'banner'
| 'button'
| 'cell'
| 'checkbox'
| 'columnheader'
| 'combobox'
| 'complementary'
| 'contentinfo'
| 'definition'
| 'dialog'
| 'directory'
| 'document'
| 'feed'
| 'figure'
| 'form'
| 'grid'
| 'gridcell'
| 'group'
| 'heading'
| 'img'
| 'link'
| 'list'
| 'listbox'
| 'listitem'
| 'log'
| 'main'
| 'marquee'
| 'math'
| 'menu'
| 'menubar'
| 'menuitem'
| 'menuitemcheckbox'
| 'menuitemradio'
| 'navigation'
| 'none'
| 'note'
| 'option'
| 'presentation'
| 'progressbar'
| 'radio'
| 'radiogroup'
| 'region'
| 'row'
| 'rowgroup'
| 'rowheader'
| 'scrollbar'
| 'search'
| 'searchbox'
| 'separator'
| 'slider'
| 'spinbutton'
| 'status'
| 'switch'
| 'tab'
| 'table'
| 'tablist'
| 'tabpanel'
| 'term'
| 'textbox'
| 'timer'
| 'toolbar'
| 'tooltip'
| 'tree'
| 'treegrid'
| 'treeitem'
| (string & {}) // Prevent type collapsing to `string`
;
@@ -1,36 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.monaco-breadcrumbs {
user-select: none;
-webkit-user-select: none;
display: flex;
flex-direction: row;
flex-wrap: nowrap;
justify-content: flex-start;
outline-style: none;
}
.monaco-breadcrumbs .monaco-breadcrumb-item {
display: flex;
align-items: center;
flex: 0 1 auto;
white-space: nowrap;
cursor: pointer;
align-self: center;
height: 100%;
outline: none;
}
.monaco-breadcrumbs.disabled .monaco-breadcrumb-item {
cursor: default;
}
.monaco-breadcrumbs .monaco-breadcrumb-item .codicon-breadcrumb-separator {
color: inherit;
}
.monaco-breadcrumbs .monaco-breadcrumb-item:first-of-type::before {
content: ' ';
}
@@ -1,356 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as dom from 'vs/base/browser/dom';
import { IMouseEvent } from 'vs/base/browser/mouseEvent';
import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
import { commonPrefixLength } from 'vs/base/common/arrays';
import { ThemeIcon } from 'vs/base/common/themables';
import { Emitter, Event } from 'vs/base/common/event';
import { DisposableStore, dispose, IDisposable } from 'vs/base/common/lifecycle';
import { ScrollbarVisibility } from 'vs/base/common/scrollable';
// import 'vs/css!./breadcrumbsWidget';
export abstract class BreadcrumbsItem {
abstract dispose(): void;
abstract equals(other: BreadcrumbsItem): boolean;
abstract render(container: HTMLElement): void;
}
export interface IBreadcrumbsWidgetStyles {
readonly breadcrumbsBackground: string | undefined;
readonly breadcrumbsForeground: string | undefined;
readonly breadcrumbsHoverForeground: string | undefined;
readonly breadcrumbsFocusForeground: string | undefined;
readonly breadcrumbsFocusAndSelectionForeground: string | undefined;
}
export interface IBreadcrumbsItemEvent {
type: 'select' | 'focus';
item: BreadcrumbsItem;
node: HTMLElement;
payload: any;
}
export class BreadcrumbsWidget {
private readonly _disposables = new DisposableStore();
private readonly _domNode: HTMLDivElement;
private readonly _scrollable: DomScrollableElement;
private readonly _onDidSelectItem = new Emitter<IBreadcrumbsItemEvent>();
private readonly _onDidFocusItem = new Emitter<IBreadcrumbsItemEvent>();
private readonly _onDidChangeFocus = new Emitter<boolean>();
readonly onDidSelectItem: Event<IBreadcrumbsItemEvent> = this._onDidSelectItem.event;
readonly onDidFocusItem: Event<IBreadcrumbsItemEvent> = this._onDidFocusItem.event;
readonly onDidChangeFocus: Event<boolean> = this._onDidChangeFocus.event;
private readonly _items = new Array<BreadcrumbsItem>();
private readonly _nodes = new Array<HTMLDivElement>();
private readonly _freeNodes = new Array<HTMLDivElement>();
private readonly _separatorIcon: ThemeIcon;
private _enabled: boolean = true;
private _focusedItemIdx: number = -1;
private _selectedItemIdx: number = -1;
private _pendingDimLayout: IDisposable | undefined;
private _pendingLayout: IDisposable | undefined;
private _dimension: dom.Dimension | undefined;
constructor(
container: HTMLElement,
horizontalScrollbarSize: number,
separatorIcon: ThemeIcon,
styles: IBreadcrumbsWidgetStyles
) {
this._domNode = document.createElement('div');
this._domNode.className = 'monaco-breadcrumbs';
this._domNode.tabIndex = 0;
this._domNode.setAttribute('role', 'list');
this._scrollable = new DomScrollableElement(this._domNode, {
vertical: ScrollbarVisibility.Hidden,
horizontal: ScrollbarVisibility.Auto,
horizontalScrollbarSize,
useShadows: false,
scrollYToX: true
});
this._separatorIcon = separatorIcon;
this._disposables.add(this._scrollable);
this._disposables.add(dom.addStandardDisposableListener(this._domNode, 'click', e => this._onClick(e)));
container.appendChild(this._scrollable.getDomNode());
const styleElement = dom.createStyleSheet(this._domNode);
this._style(styleElement, styles);
const focusTracker = dom.trackFocus(this._domNode);
this._disposables.add(focusTracker);
this._disposables.add(focusTracker.onDidBlur(_ => this._onDidChangeFocus.fire(false)));
this._disposables.add(focusTracker.onDidFocus(_ => this._onDidChangeFocus.fire(true)));
}
setHorizontalScrollbarSize(size: number) {
this._scrollable.updateOptions({
horizontalScrollbarSize: size
});
}
dispose(): void {
this._disposables.dispose();
this._pendingLayout?.dispose();
this._pendingDimLayout?.dispose();
this._onDidSelectItem.dispose();
this._onDidFocusItem.dispose();
this._onDidChangeFocus.dispose();
this._domNode.remove();
this._nodes.length = 0;
this._freeNodes.length = 0;
}
layout(dim: dom.Dimension | undefined): void {
if (dim && dom.Dimension.equals(dim, this._dimension)) {
return;
}
if (dim) {
// only measure
this._pendingDimLayout?.dispose();
this._pendingDimLayout = this._updateDimensions(dim);
} else {
this._pendingLayout?.dispose();
this._pendingLayout = this._updateScrollbar();
}
}
private _updateDimensions(dim: dom.Dimension): IDisposable {
const disposables = new DisposableStore();
disposables.add(dom.modify(dom.getWindow(this._domNode), () => {
this._dimension = dim;
this._domNode.style.width = `${dim.width}px`;
this._domNode.style.height = `${dim.height}px`;
disposables.add(this._updateScrollbar());
}));
return disposables;
}
private _updateScrollbar(): IDisposable {
return dom.measure(dom.getWindow(this._domNode), () => {
dom.measure(dom.getWindow(this._domNode), () => { // double RAF
this._scrollable.setRevealOnScroll(false);
this._scrollable.scanDomNode();
this._scrollable.setRevealOnScroll(true);
});
});
}
private _style(styleElement: HTMLStyleElement, style: IBreadcrumbsWidgetStyles): void {
let content = '';
if (style.breadcrumbsBackground) {
content += `.monaco-breadcrumbs { background-color: ${style.breadcrumbsBackground}}`;
}
if (style.breadcrumbsForeground) {
content += `.monaco-breadcrumbs .monaco-breadcrumb-item { color: ${style.breadcrumbsForeground}}\n`;
}
if (style.breadcrumbsFocusForeground) {
content += `.monaco-breadcrumbs .monaco-breadcrumb-item.focused { color: ${style.breadcrumbsFocusForeground}}\n`;
}
if (style.breadcrumbsFocusAndSelectionForeground) {
content += `.monaco-breadcrumbs .monaco-breadcrumb-item.focused.selected { color: ${style.breadcrumbsFocusAndSelectionForeground}}\n`;
}
if (style.breadcrumbsHoverForeground) {
content += `.monaco-breadcrumbs:not(.disabled ) .monaco-breadcrumb-item:hover:not(.focused):not(.selected) { color: ${style.breadcrumbsHoverForeground}}\n`;
}
styleElement.innerText = content;
}
setEnabled(value: boolean) {
this._enabled = value;
this._domNode.classList.toggle('disabled', !this._enabled);
}
domFocus(): void {
const idx = this._focusedItemIdx >= 0 ? this._focusedItemIdx : this._items.length - 1;
if (idx >= 0 && idx < this._items.length) {
this._focus(idx, undefined);
} else {
this._domNode.focus();
}
}
isDOMFocused(): boolean {
return dom.isAncestorOfActiveElement(this._domNode);
}
getFocused(): BreadcrumbsItem {
return this._items[this._focusedItemIdx];
}
setFocused(item: BreadcrumbsItem | undefined, payload?: any): void {
this._focus(this._items.indexOf(item!), payload);
}
focusPrev(payload?: any): any {
if (this._focusedItemIdx > 0) {
this._focus(this._focusedItemIdx - 1, payload);
}
}
focusNext(payload?: any): any {
if (this._focusedItemIdx + 1 < this._nodes.length) {
this._focus(this._focusedItemIdx + 1, payload);
}
}
private _focus(nth: number, payload: any): void {
this._focusedItemIdx = -1;
for (let i = 0; i < this._nodes.length; i++) {
const node = this._nodes[i];
if (i !== nth) {
node.classList.remove('focused');
} else {
this._focusedItemIdx = i;
node.classList.add('focused');
node.focus();
}
}
this._reveal(this._focusedItemIdx, true);
this._onDidFocusItem.fire({ type: 'focus', item: this._items[this._focusedItemIdx], node: this._nodes[this._focusedItemIdx], payload });
}
reveal(item: BreadcrumbsItem): void {
const idx = this._items.indexOf(item);
if (idx >= 0) {
this._reveal(idx, false);
}
}
revealLast(): void {
this._reveal(this._items.length - 1, false);
}
private _reveal(nth: number, minimal: boolean): void {
if (nth < 0 || nth >= this._nodes.length) {
return;
}
const node = this._nodes[nth];
if (!node) {
return;
}
const { width } = this._scrollable.getScrollDimensions();
const { scrollLeft } = this._scrollable.getScrollPosition();
if (!minimal || node.offsetLeft > scrollLeft + width || node.offsetLeft < scrollLeft) {
this._scrollable.setRevealOnScroll(false);
this._scrollable.setScrollPosition({ scrollLeft: node.offsetLeft });
this._scrollable.setRevealOnScroll(true);
}
}
getSelection(): BreadcrumbsItem {
return this._items[this._selectedItemIdx];
}
setSelection(item: BreadcrumbsItem | undefined, payload?: any): void {
this._select(this._items.indexOf(item!), payload);
}
private _select(nth: number, payload: any): void {
this._selectedItemIdx = -1;
for (let i = 0; i < this._nodes.length; i++) {
const node = this._nodes[i];
if (i !== nth) {
node.classList.remove('selected');
} else {
this._selectedItemIdx = i;
node.classList.add('selected');
}
}
this._onDidSelectItem.fire({ type: 'select', item: this._items[this._selectedItemIdx], node: this._nodes[this._selectedItemIdx], payload });
}
getItems(): readonly BreadcrumbsItem[] {
return this._items;
}
setItems(items: BreadcrumbsItem[]): void {
let prefix: number | undefined;
let removed: BreadcrumbsItem[] = [];
try {
prefix = commonPrefixLength(this._items, items, (a, b) => a.equals(b));
removed = this._items.splice(prefix, this._items.length - prefix, ...items.slice(prefix));
this._render(prefix);
dispose(removed);
this._focus(-1, undefined);
} catch (e) {
const newError = new Error(`BreadcrumbsItem#setItems: newItems: ${items.length}, prefix: ${prefix}, removed: ${removed.length}`);
newError.name = e.name;
newError.stack = e.stack;
throw newError;
}
}
private _render(start: number): void {
let didChange = false;
for (; start < this._items.length && start < this._nodes.length; start++) {
const item = this._items[start];
const node = this._nodes[start];
this._renderItem(item, node);
didChange = true;
}
// case a: more nodes -> remove them
while (start < this._nodes.length) {
const free = this._nodes.pop();
if (free) {
this._freeNodes.push(free);
free.remove();
didChange = true;
}
}
// case b: more items -> render them
for (; start < this._items.length; start++) {
const item = this._items[start];
const node = this._freeNodes.length > 0 ? this._freeNodes.pop() : document.createElement('div');
if (node) {
this._renderItem(item, node);
this._domNode.appendChild(node);
this._nodes.push(node);
didChange = true;
}
}
if (didChange) {
this.layout(undefined);
}
}
private _renderItem(item: BreadcrumbsItem, container: HTMLDivElement): void {
dom.clearNode(container);
container.className = '';
try {
item.render(container);
} catch (err) {
container.innerText = '<<RENDER ERROR>>';
console.error(err);
}
container.tabIndex = -1;
container.setAttribute('role', 'listitem');
container.classList.add('monaco-breadcrumb-item');
const iconContainer = dom.$(ThemeIcon.asCSSSelector(this._separatorIcon));
container.appendChild(iconContainer);
}
private _onClick(event: IMouseEvent): void {
if (!this._enabled) {
return;
}
for (let el: HTMLElement | null = event.target; el; el = el.parentElement) {
const idx = this._nodes.indexOf(el as HTMLDivElement);
if (idx >= 0) {
this._focus(idx, event);
this._select(idx, event);
break;
}
}
}
}
@@ -1,225 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { $, IDomNodePagePosition } from 'vs/base/browser/dom';
import { IView, IViewSize } from 'vs/base/browser/ui/grid/grid';
import { IBoundarySashes } from 'vs/base/browser/ui/sash/sash';
import { DistributeSizing, ISplitViewStyles, IView as ISplitViewView, Orientation, SplitView } from 'vs/base/browser/ui/splitview/splitview';
import { Color } from 'vs/base/common/color';
import { Event } from 'vs/base/common/event';
import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle';
export interface CenteredViewState {
// width of the fixed centered layout
targetWidth: number;
// proportional size of left margin
leftMarginRatio: number;
// proportional size of right margin
rightMarginRatio: number;
}
const defaultState: CenteredViewState = {
targetWidth: 900,
leftMarginRatio: 0.1909,
rightMarginRatio: 0.1909,
};
const distributeSizing: DistributeSizing = { type: 'distribute' };
function createEmptyView(background: Color | undefined): ISplitViewView<{ top: number; left: number }> {
const element = $('.centered-layout-margin');
element.style.height = '100%';
if (background) {
element.style.backgroundColor = background.toString();
}
return {
element,
layout: () => undefined,
minimumSize: 60,
maximumSize: Number.POSITIVE_INFINITY,
onDidChange: Event.None
};
}
function toSplitViewView(view: IView, getHeight: () => number): ISplitViewView<{ top: number; left: number }> {
return {
element: view.element,
get maximumSize() { return view.maximumWidth; },
get minimumSize() { return view.minimumWidth; },
onDidChange: Event.map(view.onDidChange, e => e && e.width),
layout: (size, offset, ctx) => view.layout(size, getHeight(), ctx?.top ?? 0, (ctx?.left ?? 0) + offset)
};
}
export interface ICenteredViewStyles extends ISplitViewStyles {
background: Color;
}
export class CenteredViewLayout implements IDisposable {
private splitView?: SplitView<{ top: number; left: number }>;
private lastLayoutPosition: IDomNodePagePosition = { width: 0, height: 0, left: 0, top: 0 };
private style!: ICenteredViewStyles;
private didLayout = false;
private emptyViews: ISplitViewView<{ top: number; left: number }>[] | undefined;
private readonly splitViewDisposables = new DisposableStore();
constructor(
private container: HTMLElement,
private view: IView,
public state: CenteredViewState = { ...defaultState },
private centeredLayoutFixedWidth: boolean = false
) {
this.container.appendChild(this.view.element);
// Make sure to hide the split view overflow like sashes #52892
this.container.style.overflow = 'hidden';
}
get minimumWidth(): number { return this.splitView ? this.splitView.minimumSize : this.view.minimumWidth; }
get maximumWidth(): number { return this.splitView ? this.splitView.maximumSize : this.view.maximumWidth; }
get minimumHeight(): number { return this.view.minimumHeight; }
get maximumHeight(): number { return this.view.maximumHeight; }
get onDidChange(): Event<IViewSize | undefined> { return this.view.onDidChange; }
private _boundarySashes: IBoundarySashes = {};
get boundarySashes(): IBoundarySashes { return this._boundarySashes; }
set boundarySashes(boundarySashes: IBoundarySashes) {
this._boundarySashes = boundarySashes;
if (!this.splitView) {
return;
}
this.splitView.orthogonalStartSash = boundarySashes.top;
this.splitView.orthogonalEndSash = boundarySashes.bottom;
}
layout(width: number, height: number, top: number, left: number): void {
this.lastLayoutPosition = { width, height, top, left };
if (this.splitView) {
this.splitView.layout(width, this.lastLayoutPosition);
if (!this.didLayout || this.centeredLayoutFixedWidth) {
this.resizeSplitViews();
}
} else {
this.view.layout(width, height, top, left);
}
this.didLayout = true;
}
private resizeSplitViews(): void {
if (!this.splitView) {
return;
}
if (this.centeredLayoutFixedWidth) {
const centerViewWidth = Math.min(this.lastLayoutPosition.width, this.state.targetWidth);
const marginWidthFloat = (this.lastLayoutPosition.width - centerViewWidth) / 2;
this.splitView.resizeView(0, Math.floor(marginWidthFloat));
this.splitView.resizeView(1, centerViewWidth);
this.splitView.resizeView(2, Math.ceil(marginWidthFloat));
} else {
const leftMargin = this.state.leftMarginRatio * this.lastLayoutPosition.width;
const rightMargin = this.state.rightMarginRatio * this.lastLayoutPosition.width;
const center = this.lastLayoutPosition.width - leftMargin - rightMargin;
this.splitView.resizeView(0, leftMargin);
this.splitView.resizeView(1, center);
this.splitView.resizeView(2, rightMargin);
}
}
setFixedWidth(option: boolean) {
this.centeredLayoutFixedWidth = option;
if (!!this.splitView) {
this.updateState();
this.resizeSplitViews();
}
}
private updateState() {
if (!!this.splitView) {
this.state.targetWidth = this.splitView.getViewSize(1);
this.state.leftMarginRatio = this.splitView.getViewSize(0) / this.lastLayoutPosition.width;
this.state.rightMarginRatio = this.splitView.getViewSize(2) / this.lastLayoutPosition.width;
}
}
isActive(): boolean {
return !!this.splitView;
}
styles(style: ICenteredViewStyles): void {
this.style = style;
if (this.splitView && this.emptyViews) {
this.splitView.style(this.style);
this.emptyViews[0].element.style.backgroundColor = this.style.background.toString();
this.emptyViews[1].element.style.backgroundColor = this.style.background.toString();
}
}
activate(active: boolean): void {
if (active === this.isActive()) {
return;
}
if (active) {
this.view.element.remove();
this.splitView = new SplitView(this.container, {
inverseAltBehavior: true,
orientation: Orientation.HORIZONTAL,
styles: this.style
});
this.splitView.orthogonalStartSash = this.boundarySashes.top;
this.splitView.orthogonalEndSash = this.boundarySashes.bottom;
this.splitViewDisposables.add(this.splitView.onDidSashChange(() => {
if (!!this.splitView) {
this.updateState();
}
}));
this.splitViewDisposables.add(this.splitView.onDidSashReset(() => {
this.state = { ...defaultState };
this.resizeSplitViews();
}));
this.splitView.layout(this.lastLayoutPosition.width, this.lastLayoutPosition);
const backgroundColor = this.style ? this.style.background : undefined;
this.emptyViews = [createEmptyView(backgroundColor), createEmptyView(backgroundColor)];
this.splitView.addView(this.emptyViews[0], distributeSizing, 0);
this.splitView.addView(toSplitViewView(this.view, () => this.lastLayoutPosition.height), distributeSizing, 1);
this.splitView.addView(this.emptyViews[1], distributeSizing, 2);
this.resizeSplitViews();
} else {
this.splitView?.el.remove();
this.splitViewDisposables.clear();
this.splitView?.dispose();
this.splitView = undefined;
this.emptyViews = undefined;
this.container.appendChild(this.view.element);
this.view.layout(this.lastLayoutPosition.width, this.lastLayoutPosition.height, this.lastLayoutPosition.top, this.lastLayoutPosition.left);
}
}
isDefault(state: CenteredViewState): boolean {
if (this.centeredLayoutFixedWidth) {
return state.targetWidth === defaultState.targetWidth;
} else {
return state.leftMarginRatio === defaultState.leftMarginRatio
&& state.rightMarginRatio === defaultState.rightMarginRatio;
}
}
dispose(): void {
this.splitViewDisposables.dispose();
if (this.splitView) {
this.splitView.dispose();
this.splitView = undefined;
}
}
}
@@ -1,33 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.codicon-wrench-subaction {
opacity: 0.5;
}
@keyframes codicon-spin {
100% {
transform:rotate(360deg);
}
}
.codicon-sync.codicon-modifier-spin,
.codicon-loading.codicon-modifier-spin,
.codicon-gear.codicon-modifier-spin,
.codicon-notebook-state-executing.codicon-modifier-spin {
/* Use steps to throttle FPS to reduce CPU usage */
animation: codicon-spin 1.5s steps(30) infinite;
}
.codicon-modifier-disabled {
opacity: 0.4;
}
/* custom speed & easing for loading icon */
.codicon-loading,
.codicon-tree-item-loading::before {
animation-duration: 1s !important;
animation-timing-function: cubic-bezier(0.53, 0.21, 0.29, 0.67) !important;
}
@@ -1,25 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
@font-face {
font-family: "codicon";
font-display: block;
src: url("./codicon.ttf?5d4d76ab2ce5108968ad644d591a16a6") format("truetype");
}
.codicon[class*='codicon-'] {
font: normal normal normal 16px/1 codicon;
display: inline-block;
text-decoration: none;
text-rendering: auto;
text-align: center;
text-transform: none;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
user-select: none;
-webkit-user-select: none;
}
/* icon rules are dynamically created by the platform theme service (see iconsStyleSheet.ts) */
@@ -1,7 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
// import 'vs/css!./codicon/codicon';
// import 'vs/css!./codicon/codicon-modifiers';
@@ -1,24 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.monaco-count-badge {
padding: 3px 6px;
border-radius: 11px;
font-size: 11px;
min-width: 18px;
min-height: 18px;
line-height: 11px;
font-weight: normal;
text-align: center;
display: inline-block;
box-sizing: border-box;
}
.monaco-count-badge.long {
padding: 2px 3px;
border-radius: 2px;
min-height: auto;
line-height: normal;
}
@@ -1,69 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { $, append } from 'vs/base/browser/dom';
import { format } from 'vs/base/common/strings';
// import 'vs/css!./countBadge';
export interface ICountBadgeOptions {
readonly count?: number;
readonly countFormat?: string;
readonly titleFormat?: string;
}
export interface ICountBadgeStyles {
readonly badgeBackground: string | undefined;
readonly badgeForeground: string | undefined;
readonly badgeBorder: string | undefined;
}
export const unthemedCountStyles: ICountBadgeStyles = {
badgeBackground: '#4D4D4D',
badgeForeground: '#FFFFFF',
badgeBorder: undefined
};
export class CountBadge {
private element: HTMLElement;
private count: number = 0;
private countFormat: string;
private titleFormat: string;
constructor(container: HTMLElement, private readonly options: ICountBadgeOptions, private readonly styles: ICountBadgeStyles) {
this.element = append(container, $('.monaco-count-badge'));
this.countFormat = this.options.countFormat || '{0}';
this.titleFormat = this.options.titleFormat || '';
this.setCount(this.options.count || 0);
}
setCount(count: number) {
this.count = count;
this.render();
}
setCountFormat(countFormat: string) {
this.countFormat = countFormat;
this.render();
}
setTitleFormat(titleFormat: string) {
this.titleFormat = titleFormat;
this.render();
}
private render() {
this.element.textContent = format(this.countFormat, this.count);
this.element.title = format(this.titleFormat, this.count);
this.element.style.backgroundColor = this.styles.badgeBackground ?? '';
this.element.style.color = this.styles.badgeForeground ?? '';
if (this.styles.badgeBorder) {
this.element.style.border = `1px solid ${this.styles.badgeBorder}`;
}
}
}
-947
View File
@@ -1,947 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IBoundarySashes, Orientation } from 'vs/base/browser/ui/sash/sash';
import { equals, tail2 as tail } from 'vs/base/common/arrays';
import { Event } from 'vs/base/common/event';
import { Disposable } from 'vs/base/common/lifecycle';
// import 'vs/css!./gridview';
import { Box, GridView, IGridViewOptions, IGridViewStyles, IView as IGridViewView, IViewSize, orthogonal, Sizing as GridViewSizing, GridLocation } from './gridview';
import type { SplitView, AutoSizing as SplitViewAutoSizing } from 'vs/base/browser/ui/splitview/splitview';
export type { IViewSize };
export { LayoutPriority, Orientation, orthogonal } from './gridview';
export const enum Direction {
Up,
Down,
Left,
Right
}
function oppositeDirection(direction: Direction): Direction {
switch (direction) {
case Direction.Up: return Direction.Down;
case Direction.Down: return Direction.Up;
case Direction.Left: return Direction.Right;
case Direction.Right: return Direction.Left;
}
}
/**
* The interface to implement for views within a {@link Grid}.
*/
export interface IView extends IGridViewView {
/**
* The preferred width for when the user double clicks a sash
* adjacent to this view.
*/
readonly preferredWidth?: number;
/**
* The preferred height for when the user double clicks a sash
* adjacent to this view.
*/
readonly preferredHeight?: number;
}
export interface GridLeafNode<T extends IView> {
readonly view: T;
readonly box: Box;
readonly cachedVisibleSize: number | undefined;
readonly maximized: boolean;
}
export interface GridBranchNode<T extends IView> {
readonly children: GridNode<T>[];
readonly box: Box;
}
export type GridNode<T extends IView> = GridLeafNode<T> | GridBranchNode<T>;
export function isGridBranchNode<T extends IView>(node: GridNode<T>): node is GridBranchNode<T> {
return !!(node as any).children;
}
function getGridNode<T extends IView>(node: GridNode<T>, location: GridLocation): GridNode<T> {
if (location.length === 0) {
return node;
}
if (!isGridBranchNode(node)) {
throw new Error('Invalid location');
}
const [index, ...rest] = location;
return getGridNode(node.children[index], rest);
}
interface Range {
readonly start: number;
readonly end: number;
}
function intersects(one: Range, other: Range): boolean {
return !(one.start >= other.end || other.start >= one.end);
}
interface Boundary {
readonly offset: number;
readonly range: Range;
}
function getBoxBoundary(box: Box, direction: Direction): Boundary {
const orientation = getDirectionOrientation(direction);
const offset = direction === Direction.Up ? box.top :
direction === Direction.Right ? box.left + box.width :
direction === Direction.Down ? box.top + box.height :
box.left;
const range = {
start: orientation === Orientation.HORIZONTAL ? box.top : box.left,
end: orientation === Orientation.HORIZONTAL ? box.top + box.height : box.left + box.width
};
return { offset, range };
}
function findAdjacentBoxLeafNodes<T extends IView>(boxNode: GridNode<T>, direction: Direction, boundary: Boundary): GridLeafNode<T>[] {
const result: GridLeafNode<T>[] = [];
function _(boxNode: GridNode<T>, direction: Direction, boundary: Boundary): void {
if (isGridBranchNode(boxNode)) {
for (const child of boxNode.children) {
_(child, direction, boundary);
}
} else {
const { offset, range } = getBoxBoundary(boxNode.box, direction);
if (offset === boundary.offset && intersects(range, boundary.range)) {
result.push(boxNode);
}
}
}
_(boxNode, direction, boundary);
return result;
}
function getLocationOrientation(rootOrientation: Orientation, location: GridLocation): Orientation {
return location.length % 2 === 0 ? orthogonal(rootOrientation) : rootOrientation;
}
function getDirectionOrientation(direction: Direction): Orientation {
return direction === Direction.Up || direction === Direction.Down ? Orientation.VERTICAL : Orientation.HORIZONTAL;
}
export function getRelativeLocation(rootOrientation: Orientation, location: GridLocation, direction: Direction): GridLocation {
const orientation = getLocationOrientation(rootOrientation, location);
const directionOrientation = getDirectionOrientation(direction);
if (orientation === directionOrientation) {
let [rest, index] = tail(location);
if (direction === Direction.Right || direction === Direction.Down) {
index += 1;
}
return [...rest, index];
} else {
const index = (direction === Direction.Right || direction === Direction.Down) ? 1 : 0;
return [...location, index];
}
}
function indexInParent(element: HTMLElement): number {
const parentElement = element.parentElement;
if (!parentElement) {
throw new Error('Invalid grid element');
}
let el = parentElement.firstElementChild;
let index = 0;
while (el !== element && el !== parentElement.lastElementChild && el) {
el = el.nextElementSibling;
index++;
}
return index;
}
/**
* Find the grid location of a specific DOM element by traversing the parent
* chain and finding each child index on the way.
*
* This will break as soon as DOM structures of the Splitview or Gridview change.
*/
function getGridLocation(element: HTMLElement): GridLocation {
const parentElement = element.parentElement;
if (!parentElement) {
throw new Error('Invalid grid element');
}
if (/\bmonaco-grid-view\b/.test(parentElement.className)) {
return [];
}
const index = indexInParent(parentElement);
const ancestor = parentElement.parentElement!.parentElement!.parentElement!.parentElement!;
return [...getGridLocation(ancestor), index];
}
export type DistributeSizing = { type: 'distribute' };
export type SplitSizing = { type: 'split' };
export type AutoSizing = { type: 'auto' };
export type InvisibleSizing = { type: 'invisible'; cachedVisibleSize: number };
export type Sizing = DistributeSizing | SplitSizing | AutoSizing | InvisibleSizing;
export namespace Sizing {
export const Distribute: DistributeSizing = { type: 'distribute' };
export const Split: SplitSizing = { type: 'split' };
export const Auto: AutoSizing = { type: 'auto' };
export function Invisible(cachedVisibleSize: number): InvisibleSizing { return { type: 'invisible', cachedVisibleSize }; }
}
export interface IGridStyles extends IGridViewStyles { }
export interface IGridOptions extends IGridViewOptions { }
/**
* The {@link Grid} exposes a Grid widget in a friendlier API than the underlying
* {@link GridView} widget. Namely, all mutation operations are addressed by the
* model elements, rather than indexes.
*
* It support the same features as the {@link GridView}.
*/
export class Grid<T extends IView = IView> extends Disposable {
protected gridview: GridView;
private views = new Map<T, HTMLElement>();
/**
* The orientation of the grid. Matches the orientation of the root
* {@link SplitView} in the grid's {@link GridLocation} model.
*/
get orientation(): Orientation { return this.gridview.orientation; }
set orientation(orientation: Orientation) { this.gridview.orientation = orientation; }
/**
* The width of the grid.
*/
get width(): number { return this.gridview.width; }
/**
* The height of the grid.
*/
get height(): number { return this.gridview.height; }
/**
* The minimum width of the grid.
*/
get minimumWidth(): number { return this.gridview.minimumWidth; }
/**
* The minimum height of the grid.
*/
get minimumHeight(): number { return this.gridview.minimumHeight; }
/**
* The maximum width of the grid.
*/
get maximumWidth(): number { return this.gridview.maximumWidth; }
/**
* The maximum height of the grid.
*/
get maximumHeight(): number { return this.gridview.maximumHeight; }
/**
* Fires whenever a view within the grid changes its size constraints.
*/
readonly onDidChange: Event<{ width: number; height: number } | undefined>;
/**
* Fires whenever the user scrolls a {@link SplitView} within
* the grid.
*/
readonly onDidScroll: Event<void>;
/**
* A collection of sashes perpendicular to each edge of the grid.
* Corner sashes will be created for each intersection.
*/
get boundarySashes(): IBoundarySashes { return this.gridview.boundarySashes; }
set boundarySashes(boundarySashes: IBoundarySashes) { this.gridview.boundarySashes = boundarySashes; }
/**
* Enable/disable edge snapping across all grid views.
*/
set edgeSnapping(edgeSnapping: boolean) { this.gridview.edgeSnapping = edgeSnapping; }
/**
* The DOM element for this view.
*/
get element(): HTMLElement { return this.gridview.element; }
private didLayout = false;
readonly onDidChangeViewMaximized: Event<boolean>;
/**
* Create a new {@link Grid}. A grid must *always* have a view
* inside.
*
* @param view An initial view for this Grid.
*/
constructor(view: T | GridView, options: IGridOptions = {}) {
super();
if (view instanceof GridView) {
this.gridview = view;
this.gridview.getViewMap(this.views);
} else {
this.gridview = new GridView(options);
}
this._register(this.gridview);
this._register(this.gridview.onDidSashReset(this.onDidSashReset, this));
if (!(view instanceof GridView)) {
this._addView(view, 0, [0]);
}
this.onDidChange = this.gridview.onDidChange;
this.onDidScroll = this.gridview.onDidScroll;
this.onDidChangeViewMaximized = this.gridview.onDidChangeViewMaximized;
}
style(styles: IGridStyles): void {
this.gridview.style(styles);
}
/**
* Layout the {@link Grid}.
*
* Optionally provide a `top` and `left` positions, those will propagate
* as an origin for positions passed to {@link IView.layout}.
*
* @param width The width of the {@link Grid}.
* @param height The height of the {@link Grid}.
* @param top Optional, the top location of the {@link Grid}.
* @param left Optional, the left location of the {@link Grid}.
*/
layout(width: number, height: number, top: number = 0, left: number = 0): void {
this.gridview.layout(width, height, top, left);
this.didLayout = true;
}
/**
* Add a {@link IView view} to this {@link Grid}, based on another reference view.
*
* Take this grid as an example:
*
* ```
* +-----+---------------+
* | A | B |
* +-----+---------+-----+
* | C | |
* +---------------+ D |
* | E | |
* +---------------+-----+
* ```
*
* Calling `addView(X, Sizing.Distribute, C, Direction.Right)` will make the following
* changes:
*
* ```
* +-----+---------------+
* | A | B |
* +-----+-+-------+-----+
* | C | X | |
* +-------+-------+ D |
* | E | |
* +---------------+-----+
* ```
*
* Or `addView(X, Sizing.Distribute, D, Direction.Down)`:
*
* ```
* +-----+---------------+
* | A | B |
* +-----+---------+-----+
* | C | D |
* +---------------+-----+
* | E | X |
* +---------------+-----+
* ```
*
* @param newView The view to add.
* @param size Either a fixed size, or a dynamic {@link Sizing} strategy.
* @param referenceView Another view to place this new view next to.
* @param direction The direction the new view should be placed next to the reference view.
*/
addView(newView: T, size: number | Sizing, referenceView: T, direction: Direction): void {
if (this.views.has(newView)) {
throw new Error('Can\'t add same view twice');
}
const orientation = getDirectionOrientation(direction);
if (this.views.size === 1 && this.orientation !== orientation) {
this.orientation = orientation;
}
const referenceLocation = this.getViewLocation(referenceView);
const location = getRelativeLocation(this.gridview.orientation, referenceLocation, direction);
let viewSize: number | GridViewSizing;
if (typeof size === 'number') {
viewSize = size;
} else if (size.type === 'split') {
const [, index] = tail(referenceLocation);
viewSize = GridViewSizing.Split(index);
} else if (size.type === 'distribute') {
viewSize = GridViewSizing.Distribute;
} else if (size.type === 'auto') {
const [, index] = tail(referenceLocation);
viewSize = GridViewSizing.Auto(index);
} else {
viewSize = size;
}
this._addView(newView, viewSize, location);
}
private addViewAt(newView: T, size: number | DistributeSizing | InvisibleSizing, location: GridLocation): void {
if (this.views.has(newView)) {
throw new Error('Can\'t add same view twice');
}
let viewSize: number | GridViewSizing;
if (typeof size === 'number') {
viewSize = size;
} else if (size.type === 'distribute') {
viewSize = GridViewSizing.Distribute;
} else {
viewSize = size;
}
this._addView(newView, viewSize, location);
}
protected _addView(newView: T, size: number | GridViewSizing, location: GridLocation): void {
this.views.set(newView, newView.element);
this.gridview.addView(newView, size, location);
}
/**
* Remove a {@link IView view} from this {@link Grid}.
*
* @param view The {@link IView view} to remove.
* @param sizing Whether to distribute other {@link IView view}'s sizes.
*/
removeView(view: T, sizing?: Sizing): void {
if (this.views.size === 1) {
throw new Error('Can\'t remove last view');
}
const location = this.getViewLocation(view);
let gridViewSizing: DistributeSizing | SplitViewAutoSizing | undefined;
if (sizing?.type === 'distribute') {
gridViewSizing = GridViewSizing.Distribute;
} else if (sizing?.type === 'auto') {
const index = location[location.length - 1];
gridViewSizing = GridViewSizing.Auto(index === 0 ? 1 : index - 1);
}
this.gridview.removeView(location, gridViewSizing);
this.views.delete(view);
}
/**
* Move a {@link IView view} to another location in the grid.
*
* @remarks See {@link Grid.addView}.
*
* @param view The {@link IView view} to move.
* @param sizing Either a fixed size, or a dynamic {@link Sizing} strategy.
* @param referenceView Another view to place the view next to.
* @param direction The direction the view should be placed next to the reference view.
*/
moveView(view: T, sizing: number | Sizing, referenceView: T, direction: Direction): void {
const sourceLocation = this.getViewLocation(view);
const [sourceParentLocation, from] = tail(sourceLocation);
const referenceLocation = this.getViewLocation(referenceView);
const targetLocation = getRelativeLocation(this.gridview.orientation, referenceLocation, direction);
const [targetParentLocation, to] = tail(targetLocation);
if (equals(sourceParentLocation, targetParentLocation)) {
this.gridview.moveView(sourceParentLocation, from, to);
} else {
this.removeView(view, typeof sizing === 'number' ? undefined : sizing);
this.addView(view, sizing, referenceView, direction);
}
}
/**
* Move a {@link IView view} to another location in the grid.
*
* @remarks Internal method, do not use without knowing what you're doing.
* @remarks See {@link GridView.moveView}.
*
* @param view The {@link IView view} to move.
* @param location The {@link GridLocation location} to insert the view on.
*/
moveViewTo(view: T, location: GridLocation): void {
const sourceLocation = this.getViewLocation(view);
const [sourceParentLocation, from] = tail(sourceLocation);
const [targetParentLocation, to] = tail(location);
if (equals(sourceParentLocation, targetParentLocation)) {
this.gridview.moveView(sourceParentLocation, from, to);
} else {
const size = this.getViewSize(view);
const orientation = getLocationOrientation(this.gridview.orientation, sourceLocation);
const cachedViewSize = this.getViewCachedVisibleSize(view);
const sizing = typeof cachedViewSize === 'undefined'
? (orientation === Orientation.HORIZONTAL ? size.width : size.height)
: Sizing.Invisible(cachedViewSize);
this.removeView(view);
this.addViewAt(view, sizing, location);
}
}
/**
* Swap two {@link IView views} within the {@link Grid}.
*
* @param from One {@link IView view}.
* @param to Another {@link IView view}.
*/
swapViews(from: T, to: T): void {
const fromLocation = this.getViewLocation(from);
const toLocation = this.getViewLocation(to);
return this.gridview.swapViews(fromLocation, toLocation);
}
/**
* Resize a {@link IView view}.
*
* @param view The {@link IView view} to resize.
* @param size The size the view should be.
*/
resizeView(view: T, size: IViewSize): void {
const location = this.getViewLocation(view);
return this.gridview.resizeView(location, size);
}
/**
* Returns whether all other {@link IView views} are at their minimum size.
*
* @param view The reference {@link IView view}.
*/
isViewExpanded(view: T): boolean {
const location = this.getViewLocation(view);
return this.gridview.isViewExpanded(location);
}
/**
* Returns whether the {@link IView view} is maximized.
*
* @param view The reference {@link IView view}.
*/
isViewMaximized(view: T): boolean {
const location = this.getViewLocation(view);
return this.gridview.isViewMaximized(location);
}
/**
* Returns whether the {@link IView view} is maximized.
*
* @param view The reference {@link IView view}.
*/
hasMaximizedView(): boolean {
return this.gridview.hasMaximizedView();
}
/**
* Get the size of a {@link IView view}.
*
* @param view The {@link IView view}. Provide `undefined` to get the size
* of the grid itself.
*/
getViewSize(view?: T): IViewSize {
if (!view) {
return this.gridview.getViewSize();
}
const location = this.getViewLocation(view);
return this.gridview.getViewSize(location);
}
/**
* Get the cached visible size of a {@link IView view}. This was the size
* of the view at the moment it last became hidden.
*
* @param view The {@link IView view}.
*/
getViewCachedVisibleSize(view: T): number | undefined {
const location = this.getViewLocation(view);
return this.gridview.getViewCachedVisibleSize(location);
}
/**
* Maximizes the specified view and hides all other views.
* @param view The view to maximize.
*/
maximizeView(view: T) {
if (this.views.size < 2) {
throw new Error('At least two views are required to maximize a view');
}
const location = this.getViewLocation(view);
this.gridview.maximizeView(location);
}
exitMaximizedView(): void {
this.gridview.exitMaximizedView();
}
/**
* Expand the size of a {@link IView view} by collapsing all other views
* to their minimum sizes.
*
* @param view The {@link IView view}.
*/
expandView(view: T): void {
const location = this.getViewLocation(view);
this.gridview.expandView(location);
}
/**
* Distribute the size among all {@link IView views} within the entire
* grid or within a single {@link SplitView}.
*/
distributeViewSizes(): void {
this.gridview.distributeViewSizes();
}
/**
* Returns whether a {@link IView view} is visible.
*
* @param view The {@link IView view}.
*/
isViewVisible(view: T): boolean {
const location = this.getViewLocation(view);
return this.gridview.isViewVisible(location);
}
/**
* Set the visibility state of a {@link IView view}.
*
* @param view The {@link IView view}.
*/
setViewVisible(view: T, visible: boolean): void {
const location = this.getViewLocation(view);
this.gridview.setViewVisible(location, visible);
}
/**
* Returns a descriptor for the entire grid.
*/
getViews(): GridBranchNode<T> {
return this.gridview.getView() as GridBranchNode<T>;
}
/**
* Utility method to return the collection all views which intersect
* a view's edge.
*
* @param view The {@link IView view}.
* @param direction Which direction edge to be considered.
* @param wrap Whether the grid wraps around (from right to left, from bottom to top).
*/
getNeighborViews(view: T, direction: Direction, wrap: boolean = false): T[] {
if (!this.didLayout) {
throw new Error('Can\'t call getNeighborViews before first layout');
}
const location = this.getViewLocation(view);
const root = this.getViews();
const node = getGridNode(root, location);
let boundary = getBoxBoundary(node.box, direction);
if (wrap) {
if (direction === Direction.Up && node.box.top === 0) {
boundary = { offset: root.box.top + root.box.height, range: boundary.range };
} else if (direction === Direction.Right && node.box.left + node.box.width === root.box.width) {
boundary = { offset: 0, range: boundary.range };
} else if (direction === Direction.Down && node.box.top + node.box.height === root.box.height) {
boundary = { offset: 0, range: boundary.range };
} else if (direction === Direction.Left && node.box.left === 0) {
boundary = { offset: root.box.left + root.box.width, range: boundary.range };
}
}
return findAdjacentBoxLeafNodes(root, oppositeDirection(direction), boundary)
.map(node => node.view);
}
private getViewLocation(view: T): GridLocation {
const element = this.views.get(view);
if (!element) {
throw new Error('View not found');
}
return getGridLocation(element);
}
private onDidSashReset(location: GridLocation): void {
const resizeToPreferredSize = (location: GridLocation): boolean => {
const node = this.gridview.getView(location) as GridNode<T>;
if (isGridBranchNode(node)) {
return false;
}
const direction = getLocationOrientation(this.orientation, location);
const size = direction === Orientation.HORIZONTAL ? node.view.preferredWidth : node.view.preferredHeight;
if (typeof size !== 'number') {
return false;
}
const viewSize = direction === Orientation.HORIZONTAL ? { width: Math.round(size) } : { height: Math.round(size) };
this.gridview.resizeView(location, viewSize);
return true;
};
if (resizeToPreferredSize(location)) {
return;
}
const [parentLocation, index] = tail(location);
if (resizeToPreferredSize([...parentLocation, index + 1])) {
return;
}
this.gridview.distributeViewSizes(parentLocation);
}
}
export interface ISerializableView extends IView {
toJSON(): object;
}
export interface IViewDeserializer<T extends ISerializableView> {
fromJSON(json: any): T;
}
export interface ISerializedLeafNode {
type: 'leaf';
data: any;
size: number;
visible?: boolean;
maximized?: boolean;
}
export interface ISerializedBranchNode {
type: 'branch';
data: ISerializedNode[];
size: number;
visible?: boolean;
}
export type ISerializedNode = ISerializedLeafNode | ISerializedBranchNode;
export interface ISerializedGrid {
root: ISerializedNode;
orientation: Orientation;
width: number;
height: number;
}
/**
* A {@link Grid} which can serialize itself.
*/
export class SerializableGrid<T extends ISerializableView> extends Grid<T> {
private static serializeNode<T extends ISerializableView>(node: GridNode<T>, orientation: Orientation): ISerializedNode {
const size = orientation === Orientation.VERTICAL ? node.box.width : node.box.height;
if (!isGridBranchNode(node)) {
const serializedLeafNode: ISerializedLeafNode = { type: 'leaf', data: node.view.toJSON(), size };
if (typeof node.cachedVisibleSize === 'number') {
serializedLeafNode.size = node.cachedVisibleSize;
serializedLeafNode.visible = false;
} else if (node.maximized) {
serializedLeafNode.maximized = true;
}
return serializedLeafNode;
}
const data = node.children.map(c => SerializableGrid.serializeNode(c, orthogonal(orientation)));
if (data.some(c => c.visible !== false)) {
return { type: 'branch', data: data, size };
}
return { type: 'branch', data: data, size, visible: false };
}
/**
* Construct a new {@link SerializableGrid} from a JSON object.
*
* @param json The JSON object.
* @param deserializer A deserializer which can revive each view.
* @returns A new {@link SerializableGrid} instance.
*/
static deserialize<T extends ISerializableView>(json: ISerializedGrid, deserializer: IViewDeserializer<T>, options: IGridOptions = {}): SerializableGrid<T> {
if (typeof json.orientation !== 'number') {
throw new Error('Invalid JSON: \'orientation\' property must be a number.');
} else if (typeof json.width !== 'number') {
throw new Error('Invalid JSON: \'width\' property must be a number.');
} else if (typeof json.height !== 'number') {
throw new Error('Invalid JSON: \'height\' property must be a number.');
}
const gridview = GridView.deserialize(json, deserializer, options);
const result = new SerializableGrid<T>(gridview, options);
return result;
}
/**
* Construct a new {@link SerializableGrid} from a grid descriptor.
*
* @param gridDescriptor A grid descriptor in which leaf nodes point to actual views.
* @returns A new {@link SerializableGrid} instance.
*/
static from<T extends ISerializableView>(gridDescriptor: GridDescriptor<T>, options: IGridOptions = {}): SerializableGrid<T> {
return SerializableGrid.deserialize(createSerializedGrid(gridDescriptor), { fromJSON: view => view }, options);
}
/**
* Useful information in order to proportionally restore view sizes
* upon the very first layout call.
*/
private initialLayoutContext: boolean = true;
/**
* Serialize this grid into a JSON object.
*/
serialize(): ISerializedGrid {
return {
root: SerializableGrid.serializeNode(this.getViews(), this.orientation),
orientation: this.orientation,
width: this.width,
height: this.height
};
}
override layout(width: number, height: number, top: number = 0, left: number = 0): void {
super.layout(width, height, top, left);
if (this.initialLayoutContext) {
this.initialLayoutContext = false;
this.gridview.trySet2x2();
}
}
}
export type GridLeafNodeDescriptor<T> = { size?: number; data?: any };
export type GridBranchNodeDescriptor<T> = { size?: number; groups: GridNodeDescriptor<T>[] };
export type GridNodeDescriptor<T> = GridBranchNodeDescriptor<T> | GridLeafNodeDescriptor<T>;
export type GridDescriptor<T> = { orientation: Orientation } & GridBranchNodeDescriptor<T>;
function isGridBranchNodeDescriptor<T>(nodeDescriptor: GridNodeDescriptor<T>): nodeDescriptor is GridBranchNodeDescriptor<T> {
return !!(nodeDescriptor as GridBranchNodeDescriptor<T>).groups;
}
export function sanitizeGridNodeDescriptor<T>(nodeDescriptor: GridNodeDescriptor<T>, rootNode: boolean): void {
if (!rootNode && (nodeDescriptor as any).groups && (nodeDescriptor as any).groups.length <= 1) {
(nodeDescriptor as any).groups = undefined;
}
if (!isGridBranchNodeDescriptor(nodeDescriptor)) {
return;
}
let totalDefinedSize = 0;
let totalDefinedSizeCount = 0;
for (const child of nodeDescriptor.groups) {
sanitizeGridNodeDescriptor(child, false);
if (child.size) {
totalDefinedSize += child.size;
totalDefinedSizeCount++;
}
}
const totalUndefinedSize = totalDefinedSizeCount > 0 ? totalDefinedSize : 1;
const totalUndefinedSizeCount = nodeDescriptor.groups.length - totalDefinedSizeCount;
const eachUndefinedSize = totalUndefinedSize / totalUndefinedSizeCount;
for (const child of nodeDescriptor.groups) {
if (!child.size) {
child.size = eachUndefinedSize;
}
}
}
function createSerializedNode<T>(nodeDescriptor: GridNodeDescriptor<T>): ISerializedNode {
if (isGridBranchNodeDescriptor(nodeDescriptor)) {
return { type: 'branch', data: nodeDescriptor.groups.map(c => createSerializedNode(c)), size: nodeDescriptor.size! };
} else {
return { type: 'leaf', data: nodeDescriptor.data, size: nodeDescriptor.size! };
}
}
function getDimensions(node: ISerializedNode, orientation: Orientation): { width?: number; height?: number } {
if (node.type === 'branch') {
const childrenDimensions = node.data.map(c => getDimensions(c, orthogonal(orientation)));
if (orientation === Orientation.VERTICAL) {
const width = node.size || (childrenDimensions.length === 0 ? undefined : Math.max(...childrenDimensions.map(d => d.width || 0)));
const height = childrenDimensions.length === 0 ? undefined : childrenDimensions.reduce((r, d) => r + (d.height || 0), 0);
return { width, height };
} else {
const width = childrenDimensions.length === 0 ? undefined : childrenDimensions.reduce((r, d) => r + (d.width || 0), 0);
const height = node.size || (childrenDimensions.length === 0 ? undefined : Math.max(...childrenDimensions.map(d => d.height || 0)));
return { width, height };
}
} else {
const width = orientation === Orientation.VERTICAL ? node.size : undefined;
const height = orientation === Orientation.VERTICAL ? undefined : node.size;
return { width, height };
}
}
/**
* Creates a new JSON object from a {@link GridDescriptor}, which can
* be deserialized by {@link SerializableGrid.deserialize}.
*/
export function createSerializedGrid<T>(gridDescriptor: GridDescriptor<T>): ISerializedGrid {
sanitizeGridNodeDescriptor(gridDescriptor, true);
const root = createSerializedNode(gridDescriptor);
const { width, height } = getDimensions(root, gridDescriptor.orientation);
return {
root,
orientation: gridDescriptor.orientation,
width: width || 1,
height: height || 1
};
}
-16
View File
@@ -1,16 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.monaco-grid-view {
position: relative;
overflow: hidden;
width: 100%;
height: 100%;
}
.monaco-grid-branch-node {
width: 100%;
height: 100%;
}
File diff suppressed because it is too large Load Diff
@@ -1,8 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.monaco-mouse-cursor-text {
cursor: text;
}
@@ -1,8 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
// import 'vs/css!./mouseCursor';
export const MOUSE_CURSOR_TEXT_CSS_CLASS_NAME = `monaco-mouse-cursor-text`;
@@ -1,23 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IDisposable } from 'vs/base/common/lifecycle';
export interface IScopedAccessibilityProgressSignalDelegate extends IDisposable { }
const nullScopedAccessibilityProgressSignalFactory = () => ({
msLoopTime: -1,
msDelayTime: -1,
dispose: () => { },
});
let progressAccessibilitySignalSchedulerFactory: (msDelayTime: number, msLoopTime?: number) => IScopedAccessibilityProgressSignalDelegate = nullScopedAccessibilityProgressSignalFactory;
export function setProgressAcccessibilitySignalScheduler(progressAccessibilitySignalScheduler: (msDelayTime: number, msLoopTime?: number) => IScopedAccessibilityProgressSignalDelegate) {
progressAccessibilitySignalSchedulerFactory = progressAccessibilitySignalScheduler;
}
export function getProgressAcccessibilitySignalScheduler(msDelayTime: number, msLoopTime?: number): IScopedAccessibilityProgressSignalDelegate {
return progressAccessibilitySignalSchedulerFactory(msDelayTime, msLoopTime);
}
@@ -1,61 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.monaco-progress-container {
width: 100%;
height: 2px;
overflow: hidden; /* keep progress bit in bounds */
}
.monaco-progress-container .progress-bit {
width: 2%;
height: 2px;
position: absolute;
left: 0;
display: none;
}
.monaco-progress-container.active .progress-bit {
display: inherit;
}
.monaco-progress-container.discrete .progress-bit {
left: 0;
transition: width 100ms linear;
}
.monaco-progress-container.discrete.done .progress-bit {
width: 100%;
}
.monaco-progress-container.infinite .progress-bit {
animation-name: progress;
animation-duration: 4s;
animation-iteration-count: infinite;
transform: translate3d(0px, 0px, 0px);
animation-timing-function: linear;
}
.monaco-progress-container.infinite.infinite-long-running .progress-bit {
/*
The more smooth `linear` timing function can cause
higher GPU consumption as indicated in
https://github.com/microsoft/vscode/issues/97900 &
https://github.com/microsoft/vscode/issues/138396
*/
animation-timing-function: steps(100);
}
/**
* The progress bit has a width: 2% (1/50) of the parent container. The animation moves it from 0% to 100% of
* that container. Since translateX is relative to the progress bit size, we have to multiple it with
* its relative size to the parent container:
* parent width: 5000%
* bit width: 100%
* translateX should be as follow:
* 50%: 5000% * 50% - 50% (set to center) = 2450%
* 100%: 5000% * 100% - 100% (do not overflow) = 4900%
*/
@keyframes progress { from { transform: translateX(0%) scaleX(1) } 50% { transform: translateX(2500%) scaleX(3) } to { transform: translateX(4900%) scaleX(1) } }
@@ -1,224 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { hide, show } from 'vs/base/browser/dom';
import { getProgressAcccessibilitySignalScheduler } from 'vs/base/browser/ui/progressbar/progressAccessibilitySignal';
import { RunOnceScheduler } from 'vs/base/common/async';
import { Disposable, IDisposable, MutableDisposable } from 'vs/base/common/lifecycle';
import { isNumber } from 'vs/base/common/types';
// import 'vs/css!./progressbar';
const CSS_DONE = 'done';
const CSS_ACTIVE = 'active';
const CSS_INFINITE = 'infinite';
const CSS_INFINITE_LONG_RUNNING = 'infinite-long-running';
const CSS_DISCRETE = 'discrete';
export interface IProgressBarOptions extends IProgressBarStyles {
}
export interface IProgressBarStyles {
progressBarBackground: string | undefined;
}
export const unthemedProgressBarOptions: IProgressBarOptions = {
progressBarBackground: undefined
};
/**
* A progress bar with support for infinite or discrete progress.
*/
export class ProgressBar extends Disposable {
/**
* After a certain time of showing the progress bar, switch
* to long-running mode and throttle animations to reduce
* the pressure on the GPU process.
*
* https://github.com/microsoft/vscode/issues/97900
* https://github.com/microsoft/vscode/issues/138396
*/
private static readonly LONG_RUNNING_INFINITE_THRESHOLD = 10000;
private static readonly PROGRESS_SIGNAL_DEFAULT_DELAY = 3000;
private workedVal: number;
private element!: HTMLElement;
private bit!: HTMLElement;
private totalWork: number | undefined;
private showDelayedScheduler: RunOnceScheduler;
private longRunningScheduler: RunOnceScheduler;
private readonly progressSignal = this._register(new MutableDisposable<IDisposable>());
constructor(container: HTMLElement, options?: IProgressBarOptions) {
super();
this.workedVal = 0;
this.showDelayedScheduler = this._register(new RunOnceScheduler(() => show(this.element), 0));
this.longRunningScheduler = this._register(new RunOnceScheduler(() => this.infiniteLongRunning(), ProgressBar.LONG_RUNNING_INFINITE_THRESHOLD));
this.create(container, options);
}
private create(container: HTMLElement, options?: IProgressBarOptions): void {
this.element = document.createElement('div');
this.element.classList.add('monaco-progress-container');
this.element.setAttribute('role', 'progressbar');
this.element.setAttribute('aria-valuemin', '0');
container.appendChild(this.element);
this.bit = document.createElement('div');
this.bit.classList.add('progress-bit');
this.bit.style.backgroundColor = options?.progressBarBackground || '#0E70C0';
this.element.appendChild(this.bit);
}
private off(): void {
this.bit.style.width = 'inherit';
this.bit.style.opacity = '1';
this.element.classList.remove(CSS_ACTIVE, CSS_INFINITE, CSS_INFINITE_LONG_RUNNING, CSS_DISCRETE);
this.workedVal = 0;
this.totalWork = undefined;
this.longRunningScheduler.cancel();
this.progressSignal.clear();
}
/**
* Indicates to the progress bar that all work is done.
*/
done(): ProgressBar {
return this.doDone(true);
}
/**
* Stops the progressbar from showing any progress instantly without fading out.
*/
stop(): ProgressBar {
return this.doDone(false);
}
private doDone(delayed: boolean): ProgressBar {
this.element.classList.add(CSS_DONE);
// discrete: let it grow to 100% width and hide afterwards
if (!this.element.classList.contains(CSS_INFINITE)) {
this.bit.style.width = 'inherit';
if (delayed) {
setTimeout(() => this.off(), 200);
} else {
this.off();
}
}
// infinite: let it fade out and hide afterwards
else {
this.bit.style.opacity = '0';
if (delayed) {
setTimeout(() => this.off(), 200);
} else {
this.off();
}
}
return this;
}
/**
* Use this mode to indicate progress that has no total number of work units.
*/
infinite(): ProgressBar {
this.bit.style.width = '2%';
this.bit.style.opacity = '1';
this.element.classList.remove(CSS_DISCRETE, CSS_DONE, CSS_INFINITE_LONG_RUNNING);
this.element.classList.add(CSS_ACTIVE, CSS_INFINITE);
this.longRunningScheduler.schedule();
return this;
}
private infiniteLongRunning(): void {
this.element.classList.add(CSS_INFINITE_LONG_RUNNING);
}
/**
* Tells the progress bar the total number of work. Use in combination with workedVal() to let
* the progress bar show the actual progress based on the work that is done.
*/
total(value: number): ProgressBar {
this.workedVal = 0;
this.totalWork = value;
this.element.setAttribute('aria-valuemax', value.toString());
return this;
}
/**
* Finds out if this progress bar is configured with total work
*/
hasTotal(): boolean {
return isNumber(this.totalWork);
}
/**
* Tells the progress bar that an increment of work has been completed.
*/
worked(value: number): ProgressBar {
value = Math.max(1, Number(value));
return this.doSetWorked(this.workedVal + value);
}
/**
* Tells the progress bar the total amount of work that has been completed.
*/
setWorked(value: number): ProgressBar {
value = Math.max(1, Number(value));
return this.doSetWorked(value);
}
private doSetWorked(value: number): ProgressBar {
const totalWork = this.totalWork || 100;
this.workedVal = value;
this.workedVal = Math.min(totalWork, this.workedVal);
this.element.classList.remove(CSS_INFINITE, CSS_INFINITE_LONG_RUNNING, CSS_DONE);
this.element.classList.add(CSS_ACTIVE, CSS_DISCRETE);
this.element.setAttribute('aria-valuenow', value.toString());
this.bit.style.width = 100 * (this.workedVal / (totalWork)) + '%';
return this;
}
getContainer(): HTMLElement {
return this.element;
}
show(delay?: number): void {
this.showDelayedScheduler.cancel();
this.progressSignal.value = getProgressAcccessibilitySignalScheduler(ProgressBar.PROGRESS_SIGNAL_DEFAULT_DELAY);
if (typeof delay === 'number') {
this.showDelayedScheduler.schedule(delay);
} else {
show(this.element);
}
}
hide(): void {
hide(this.element);
this.showDelayedScheduler.cancel();
this.progressSignal.clear();
}
}
@@ -1,190 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Dimension } from 'vs/base/browser/dom';
import { Orientation, OrthogonalEdge, Sash, SashState } from 'vs/base/browser/ui/sash/sash';
import { Emitter, Event } from 'vs/base/common/event';
import { DisposableStore } from 'vs/base/common/lifecycle';
export interface IResizeEvent {
dimension: Dimension;
done: boolean;
north?: boolean;
east?: boolean;
south?: boolean;
west?: boolean;
}
export class ResizableHTMLElement {
readonly domNode: HTMLElement;
private readonly _onDidWillResize = new Emitter<void>();
readonly onDidWillResize: Event<void> = this._onDidWillResize.event;
private readonly _onDidResize = new Emitter<IResizeEvent>();
readonly onDidResize: Event<IResizeEvent> = this._onDidResize.event;
private readonly _northSash: Sash;
private readonly _eastSash: Sash;
private readonly _southSash: Sash;
private readonly _westSash: Sash;
private readonly _sashListener = new DisposableStore();
private _size = new Dimension(0, 0);
private _minSize = new Dimension(0, 0);
private _maxSize = new Dimension(Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER);
private _preferredSize?: Dimension;
constructor() {
this.domNode = document.createElement('div');
this._eastSash = new Sash(this.domNode, { getVerticalSashLeft: () => this._size.width }, { orientation: Orientation.VERTICAL });
this._westSash = new Sash(this.domNode, { getVerticalSashLeft: () => 0 }, { orientation: Orientation.VERTICAL });
this._northSash = new Sash(this.domNode, { getHorizontalSashTop: () => 0 }, { orientation: Orientation.HORIZONTAL, orthogonalEdge: OrthogonalEdge.North });
this._southSash = new Sash(this.domNode, { getHorizontalSashTop: () => this._size.height }, { orientation: Orientation.HORIZONTAL, orthogonalEdge: OrthogonalEdge.South });
this._northSash.orthogonalStartSash = this._westSash;
this._northSash.orthogonalEndSash = this._eastSash;
this._southSash.orthogonalStartSash = this._westSash;
this._southSash.orthogonalEndSash = this._eastSash;
let currentSize: Dimension | undefined;
let deltaY = 0;
let deltaX = 0;
this._sashListener.add(Event.any(this._northSash.onDidStart, this._eastSash.onDidStart, this._southSash.onDidStart, this._westSash.onDidStart)(() => {
if (currentSize === undefined) {
this._onDidWillResize.fire();
currentSize = this._size;
deltaY = 0;
deltaX = 0;
}
}));
this._sashListener.add(Event.any(this._northSash.onDidEnd, this._eastSash.onDidEnd, this._southSash.onDidEnd, this._westSash.onDidEnd)(() => {
if (currentSize !== undefined) {
currentSize = undefined;
deltaY = 0;
deltaX = 0;
this._onDidResize.fire({ dimension: this._size, done: true });
}
}));
this._sashListener.add(this._eastSash.onDidChange(e => {
if (currentSize) {
deltaX = e.currentX - e.startX;
this.layout(currentSize.height + deltaY, currentSize.width + deltaX);
this._onDidResize.fire({ dimension: this._size, done: false, east: true });
}
}));
this._sashListener.add(this._westSash.onDidChange(e => {
if (currentSize) {
deltaX = -(e.currentX - e.startX);
this.layout(currentSize.height + deltaY, currentSize.width + deltaX);
this._onDidResize.fire({ dimension: this._size, done: false, west: true });
}
}));
this._sashListener.add(this._northSash.onDidChange(e => {
if (currentSize) {
deltaY = -(e.currentY - e.startY);
this.layout(currentSize.height + deltaY, currentSize.width + deltaX);
this._onDidResize.fire({ dimension: this._size, done: false, north: true });
}
}));
this._sashListener.add(this._southSash.onDidChange(e => {
if (currentSize) {
deltaY = e.currentY - e.startY;
this.layout(currentSize.height + deltaY, currentSize.width + deltaX);
this._onDidResize.fire({ dimension: this._size, done: false, south: true });
}
}));
this._sashListener.add(Event.any(this._eastSash.onDidReset, this._westSash.onDidReset)(e => {
if (this._preferredSize) {
this.layout(this._size.height, this._preferredSize.width);
this._onDidResize.fire({ dimension: this._size, done: true });
}
}));
this._sashListener.add(Event.any(this._northSash.onDidReset, this._southSash.onDidReset)(e => {
if (this._preferredSize) {
this.layout(this._preferredSize.height, this._size.width);
this._onDidResize.fire({ dimension: this._size, done: true });
}
}));
}
dispose(): void {
this._northSash.dispose();
this._southSash.dispose();
this._eastSash.dispose();
this._westSash.dispose();
this._sashListener.dispose();
this._onDidResize.dispose();
this._onDidWillResize.dispose();
this.domNode.remove();
}
enableSashes(north: boolean, east: boolean, south: boolean, west: boolean): void {
this._northSash.state = north ? SashState.Enabled : SashState.Disabled;
this._eastSash.state = east ? SashState.Enabled : SashState.Disabled;
this._southSash.state = south ? SashState.Enabled : SashState.Disabled;
this._westSash.state = west ? SashState.Enabled : SashState.Disabled;
}
layout(height: number = this.size.height, width: number = this.size.width): void {
const { height: minHeight, width: minWidth } = this._minSize;
const { height: maxHeight, width: maxWidth } = this._maxSize;
height = Math.max(minHeight, Math.min(maxHeight, height));
width = Math.max(minWidth, Math.min(maxWidth, width));
const newSize = new Dimension(width, height);
if (!Dimension.equals(newSize, this._size)) {
this.domNode.style.height = height + 'px';
this.domNode.style.width = width + 'px';
this._size = newSize;
this._northSash.layout();
this._eastSash.layout();
this._southSash.layout();
this._westSash.layout();
}
}
clearSashHoverState(): void {
this._eastSash.clearSashHoverState();
this._westSash.clearSashHoverState();
this._northSash.clearSashHoverState();
this._southSash.clearSashHoverState();
}
get size() {
return this._size;
}
set maxSize(value: Dimension) {
this._maxSize = value;
}
get maxSize() {
return this._maxSize;
}
set minSize(value: Dimension) {
this._minSize = value;
}
get minSize() {
return this._minSize;
}
set preferredSize(value: Dimension | undefined) {
this._preferredSize = value;
}
get preferredSize() {
return this._preferredSize;
}
}
-149
View File
@@ -1,149 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
:root {
--vscode-sash-size: 4px;
--vscode-sash-hover-size: 4px;
}
.monaco-sash {
position: absolute;
z-index: 35;
touch-action: none;
}
.monaco-sash.disabled {
pointer-events: none;
}
.monaco-sash.mac.vertical {
cursor: col-resize;
}
.monaco-sash.vertical.minimum {
cursor: e-resize;
}
.monaco-sash.vertical.maximum {
cursor: w-resize;
}
.monaco-sash.mac.horizontal {
cursor: row-resize;
}
.monaco-sash.horizontal.minimum {
cursor: s-resize;
}
.monaco-sash.horizontal.maximum {
cursor: n-resize;
}
.monaco-sash.disabled {
cursor: default !important;
pointer-events: none !important;
}
.monaco-sash.vertical {
cursor: ew-resize;
top: 0;
width: var(--vscode-sash-size);
height: 100%;
}
.monaco-sash.horizontal {
cursor: ns-resize;
left: 0;
width: 100%;
height: var(--vscode-sash-size);
}
.monaco-sash:not(.disabled) > .orthogonal-drag-handle {
content: " ";
height: calc(var(--vscode-sash-size) * 2);
width: calc(var(--vscode-sash-size) * 2);
z-index: 100;
display: block;
cursor: all-scroll;
position: absolute;
}
.monaco-sash.horizontal.orthogonal-edge-north:not(.disabled)
> .orthogonal-drag-handle.start,
.monaco-sash.horizontal.orthogonal-edge-south:not(.disabled)
> .orthogonal-drag-handle.end {
cursor: nwse-resize;
}
.monaco-sash.horizontal.orthogonal-edge-north:not(.disabled)
> .orthogonal-drag-handle.end,
.monaco-sash.horizontal.orthogonal-edge-south:not(.disabled)
> .orthogonal-drag-handle.start {
cursor: nesw-resize;
}
.monaco-sash.vertical > .orthogonal-drag-handle.start {
left: calc(var(--vscode-sash-size) * -0.5);
top: calc(var(--vscode-sash-size) * -1);
}
.monaco-sash.vertical > .orthogonal-drag-handle.end {
left: calc(var(--vscode-sash-size) * -0.5);
bottom: calc(var(--vscode-sash-size) * -1);
}
.monaco-sash.horizontal > .orthogonal-drag-handle.start {
top: calc(var(--vscode-sash-size) * -0.5);
left: calc(var(--vscode-sash-size) * -1);
}
.monaco-sash.horizontal > .orthogonal-drag-handle.end {
top: calc(var(--vscode-sash-size) * -0.5);
right: calc(var(--vscode-sash-size) * -1);
}
.monaco-sash:before {
content: '';
pointer-events: none;
position: absolute;
width: 100%;
height: 100%;
background: transparent;
}
.monaco-workbench:not(.reduce-motion) .monaco-sash:before {
transition: background-color 0.1s ease-out;
}
.monaco-sash.hover:before,
.monaco-sash.active:before {
background: var(--vscode-sash-hoverBorder);
}
.monaco-sash.vertical:before {
width: var(--vscode-sash-hover-size);
left: calc(50% - (var(--vscode-sash-hover-size) / 2));
}
.monaco-sash.horizontal:before {
height: var(--vscode-sash-hover-size);
top: calc(50% - (var(--vscode-sash-hover-size) / 2));
}
.pointer-events-disabled {
pointer-events: none !important;
}
/** Debug **/
.monaco-sash.debug {
background: cyan;
}
.monaco-sash.debug.disabled {
background: rgba(0, 255, 255, 0.2);
}
.monaco-sash.debug:not(.disabled) > .orthogonal-drag-handle {
background: red;
}
-688
View File
@@ -1,688 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { $, append, createStyleSheet, EventHelper, EventLike, getWindow, isHTMLElement } from 'vs/base/browser/dom';
import { DomEmitter } from 'vs/base/browser/event';
import { EventType, Gesture } from 'vs/base/browser/touch';
import { Delayer } from 'vs/base/common/async';
import { memoize } from 'vs/base/common/decorators';
import { Emitter, Event } from 'vs/base/common/event';
import { Disposable, DisposableStore, toDisposable } from 'vs/base/common/lifecycle';
import { isMacintosh } from 'vs/base/common/platform';
// import 'vs/css!./sash';
/**
* Allow the sashes to be visible at runtime.
* @remark Use for development purposes only.
*/
const DEBUG = false;
// DEBUG = Boolean("true"); // done "weirdly" so that a lint warning prevents you from pushing this
/**
* A vertical sash layout provider provides position and height for a sash.
*/
export interface IVerticalSashLayoutProvider {
getVerticalSashLeft(sash: Sash): number;
getVerticalSashTop?(sash: Sash): number;
getVerticalSashHeight?(sash: Sash): number;
}
/**
* A vertical sash layout provider provides position and width for a sash.
*/
export interface IHorizontalSashLayoutProvider {
getHorizontalSashTop(sash: Sash): number;
getHorizontalSashLeft?(sash: Sash): number;
getHorizontalSashWidth?(sash: Sash): number;
}
type ISashLayoutProvider = IVerticalSashLayoutProvider | IHorizontalSashLayoutProvider;
export interface ISashEvent {
readonly startX: number;
readonly currentX: number;
readonly startY: number;
readonly currentY: number;
readonly altKey: boolean;
}
export enum OrthogonalEdge {
North = 'north',
South = 'south',
East = 'east',
West = 'west'
}
export interface IBoundarySashes {
readonly top?: Sash;
readonly right?: Sash;
readonly bottom?: Sash;
readonly left?: Sash;
}
export interface ISashOptions {
/**
* Whether a sash is horizontal or vertical.
*/
readonly orientation: Orientation;
/**
* The width or height of a vertical or horizontal sash, respectively.
*/
readonly size?: number;
/**
* A reference to another sash, perpendicular to this one, which
* aligns at the start of this one. A corner sash will be created
* automatically at that location.
*
* The start of a horizontal sash is its left-most position.
* The start of a vertical sash is its top-most position.
*/
readonly orthogonalStartSash?: Sash;
/**
* A reference to another sash, perpendicular to this one, which
* aligns at the end of this one. A corner sash will be created
* automatically at that location.
*
* The end of a horizontal sash is its right-most position.
* The end of a vertical sash is its bottom-most position.
*/
readonly orthogonalEndSash?: Sash;
/**
* Provides a hint as to what mouse cursor to use whenever the user
* hovers over a corner sash provided by this and an orthogonal sash.
*/
readonly orthogonalEdge?: OrthogonalEdge;
}
export interface IVerticalSashOptions extends ISashOptions {
readonly orientation: Orientation.VERTICAL;
}
export interface IHorizontalSashOptions extends ISashOptions {
readonly orientation: Orientation.HORIZONTAL;
}
export const enum Orientation {
VERTICAL,
HORIZONTAL
}
export const enum SashState {
/**
* Disable any UI interaction.
*/
Disabled,
/**
* Allow dragging down or to the right, depending on the sash orientation.
*
* Some OSs allow customizing the mouse cursor differently whenever
* some resizable component can't be any smaller, but can be larger.
*/
AtMinimum,
/**
* Allow dragging up or to the left, depending on the sash orientation.
*
* Some OSs allow customizing the mouse cursor differently whenever
* some resizable component can't be any larger, but can be smaller.
*/
AtMaximum,
/**
* Enable dragging.
*/
Enabled
}
let globalSize = 4;
const onDidChangeGlobalSize = new Emitter<number>();
export function setGlobalSashSize(size: number): void {
globalSize = size;
onDidChangeGlobalSize.fire(size);
}
let globalHoverDelay = 300;
const onDidChangeHoverDelay = new Emitter<number>();
export function setGlobalHoverDelay(size: number): void {
globalHoverDelay = size;
onDidChangeHoverDelay.fire(size);
}
interface PointerEvent extends EventLike {
readonly pageX: number;
readonly pageY: number;
readonly altKey: boolean;
readonly target: EventTarget | null;
readonly initialTarget?: EventTarget | undefined;
}
interface IPointerEventFactory {
readonly onPointerMove: Event<PointerEvent>;
readonly onPointerUp: Event<PointerEvent>;
dispose(): void;
}
class MouseEventFactory implements IPointerEventFactory {
private readonly disposables = new DisposableStore();
constructor(private el: HTMLElement) { }
@memoize
get onPointerMove(): Event<PointerEvent> {
return this.disposables.add(new DomEmitter(getWindow(this.el), 'mousemove')).event;
}
@memoize
get onPointerUp(): Event<PointerEvent> {
return this.disposables.add(new DomEmitter(getWindow(this.el), 'mouseup')).event;
}
dispose(): void {
this.disposables.dispose();
}
}
class GestureEventFactory implements IPointerEventFactory {
private readonly disposables = new DisposableStore();
@memoize
get onPointerMove(): Event<PointerEvent> {
return this.disposables.add(new DomEmitter(this.el, EventType.Change)).event;
}
@memoize
get onPointerUp(): Event<PointerEvent> {
return this.disposables.add(new DomEmitter(this.el, EventType.End)).event;
}
constructor(private el: HTMLElement) { }
dispose(): void {
this.disposables.dispose();
}
}
class OrthogonalPointerEventFactory implements IPointerEventFactory {
@memoize
get onPointerMove(): Event<PointerEvent> {
return this.factory.onPointerMove;
}
@memoize
get onPointerUp(): Event<PointerEvent> {
return this.factory.onPointerUp;
}
constructor(private factory: IPointerEventFactory) { }
dispose(): void {
// noop
}
}
const PointerEventsDisabledCssClass = 'pointer-events-disabled';
/**
* The {@link Sash} is the UI component which allows the user to resize other
* components. It's usually an invisible horizontal or vertical line which, when
* hovered, becomes highlighted and can be dragged along the perpendicular dimension
* to its direction.
*
* Features:
* - Touch event handling
* - Corner sash support
* - Hover with different mouse cursor support
* - Configurable hover size
* - Linked sash support, for 2x2 corner sashes
*/
export class Sash extends Disposable {
private el: HTMLElement;
private layoutProvider: ISashLayoutProvider;
private orientation: Orientation;
private size: number;
private hoverDelay = globalHoverDelay;
private hoverDelayer = this._register(new Delayer(this.hoverDelay));
private _state: SashState = SashState.Enabled;
private readonly onDidEnablementChange = this._register(new Emitter<SashState>());
private readonly _onDidStart = this._register(new Emitter<ISashEvent>());
private readonly _onDidChange = this._register(new Emitter<ISashEvent>());
private readonly _onDidReset = this._register(new Emitter<void>());
private readonly _onDidEnd = this._register(new Emitter<void>());
private readonly orthogonalStartSashDisposables = this._register(new DisposableStore());
private _orthogonalStartSash: Sash | undefined;
private readonly orthogonalStartDragHandleDisposables = this._register(new DisposableStore());
private _orthogonalStartDragHandle: HTMLElement | undefined;
private readonly orthogonalEndSashDisposables = this._register(new DisposableStore());
private _orthogonalEndSash: Sash | undefined;
private readonly orthogonalEndDragHandleDisposables = this._register(new DisposableStore());
private _orthogonalEndDragHandle: HTMLElement | undefined;
get state(): SashState { return this._state; }
get orthogonalStartSash(): Sash | undefined { return this._orthogonalStartSash; }
get orthogonalEndSash(): Sash | undefined { return this._orthogonalEndSash; }
/**
* The state of a sash defines whether it can be interacted with by the user
* as well as what mouse cursor to use, when hovered.
*/
set state(state: SashState) {
if (this._state === state) {
return;
}
this.el.classList.toggle('disabled', state === SashState.Disabled);
this.el.classList.toggle('minimum', state === SashState.AtMinimum);
this.el.classList.toggle('maximum', state === SashState.AtMaximum);
this._state = state;
this.onDidEnablementChange.fire(state);
}
/**
* An event which fires whenever the user starts dragging this sash.
*/
readonly onDidStart: Event<ISashEvent> = this._onDidStart.event;
/**
* An event which fires whenever the user moves the mouse while
* dragging this sash.
*/
readonly onDidChange: Event<ISashEvent> = this._onDidChange.event;
/**
* An event which fires whenever the user double clicks this sash.
*/
readonly onDidReset: Event<void> = this._onDidReset.event;
/**
* An event which fires whenever the user stops dragging this sash.
*/
readonly onDidEnd: Event<void> = this._onDidEnd.event;
/**
* A linked sash will be forwarded the same user interactions and events
* so it moves exactly the same way as this sash.
*
* Useful in 2x2 grids. Not meant for widespread usage.
*/
linkedSash: Sash | undefined = undefined;
/**
* A reference to another sash, perpendicular to this one, which
* aligns at the start of this one. A corner sash will be created
* automatically at that location.
*
* The start of a horizontal sash is its left-most position.
* The start of a vertical sash is its top-most position.
*/
set orthogonalStartSash(sash: Sash | undefined) {
if (this._orthogonalStartSash === sash) {
return;
}
this.orthogonalStartDragHandleDisposables.clear();
this.orthogonalStartSashDisposables.clear();
if (sash) {
const onChange = (state: SashState) => {
this.orthogonalStartDragHandleDisposables.clear();
if (state !== SashState.Disabled) {
this._orthogonalStartDragHandle = append(this.el, $('.orthogonal-drag-handle.start'));
this.orthogonalStartDragHandleDisposables.add(toDisposable(() => this._orthogonalStartDragHandle!.remove()));
this.orthogonalStartDragHandleDisposables.add(new DomEmitter(this._orthogonalStartDragHandle, 'mouseenter')).event
(() => Sash.onMouseEnter(sash), undefined, this.orthogonalStartDragHandleDisposables);
this.orthogonalStartDragHandleDisposables.add(new DomEmitter(this._orthogonalStartDragHandle, 'mouseleave')).event
(() => Sash.onMouseLeave(sash), undefined, this.orthogonalStartDragHandleDisposables);
}
};
this.orthogonalStartSashDisposables.add(sash.onDidEnablementChange.event(onChange, this));
onChange(sash.state);
}
this._orthogonalStartSash = sash;
}
/**
* A reference to another sash, perpendicular to this one, which
* aligns at the end of this one. A corner sash will be created
* automatically at that location.
*
* The end of a horizontal sash is its right-most position.
* The end of a vertical sash is its bottom-most position.
*/
set orthogonalEndSash(sash: Sash | undefined) {
if (this._orthogonalEndSash === sash) {
return;
}
this.orthogonalEndDragHandleDisposables.clear();
this.orthogonalEndSashDisposables.clear();
if (sash) {
const onChange = (state: SashState) => {
this.orthogonalEndDragHandleDisposables.clear();
if (state !== SashState.Disabled) {
this._orthogonalEndDragHandle = append(this.el, $('.orthogonal-drag-handle.end'));
this.orthogonalEndDragHandleDisposables.add(toDisposable(() => this._orthogonalEndDragHandle!.remove()));
this.orthogonalEndDragHandleDisposables.add(new DomEmitter(this._orthogonalEndDragHandle, 'mouseenter')).event
(() => Sash.onMouseEnter(sash), undefined, this.orthogonalEndDragHandleDisposables);
this.orthogonalEndDragHandleDisposables.add(new DomEmitter(this._orthogonalEndDragHandle, 'mouseleave')).event
(() => Sash.onMouseLeave(sash), undefined, this.orthogonalEndDragHandleDisposables);
}
};
this.orthogonalEndSashDisposables.add(sash.onDidEnablementChange.event(onChange, this));
onChange(sash.state);
}
this._orthogonalEndSash = sash;
}
/**
* Create a new vertical sash.
*
* @param container A DOM node to append the sash to.
* @param verticalLayoutProvider A vertical layout provider.
* @param options The options.
*/
constructor(container: HTMLElement, verticalLayoutProvider: IVerticalSashLayoutProvider, options: IVerticalSashOptions);
/**
* Create a new horizontal sash.
*
* @param container A DOM node to append the sash to.
* @param horizontalLayoutProvider A horizontal layout provider.
* @param options The options.
*/
constructor(container: HTMLElement, horizontalLayoutProvider: IHorizontalSashLayoutProvider, options: IHorizontalSashOptions);
constructor(container: HTMLElement, layoutProvider: ISashLayoutProvider, options: ISashOptions) {
super();
this.el = append(container, $('.monaco-sash'));
if (options.orthogonalEdge) {
this.el.classList.add(`orthogonal-edge-${options.orthogonalEdge}`);
}
if (isMacintosh) {
this.el.classList.add('mac');
}
const onMouseDown = this._register(new DomEmitter(this.el, 'mousedown')).event;
this._register(onMouseDown(e => this.onPointerStart(e, new MouseEventFactory(container)), this));
const onMouseDoubleClick = this._register(new DomEmitter(this.el, 'dblclick')).event;
this._register(onMouseDoubleClick(this.onPointerDoublePress, this));
const onMouseEnter = this._register(new DomEmitter(this.el, 'mouseenter')).event;
this._register(onMouseEnter(() => Sash.onMouseEnter(this)));
const onMouseLeave = this._register(new DomEmitter(this.el, 'mouseleave')).event;
this._register(onMouseLeave(() => Sash.onMouseLeave(this)));
this._register(Gesture.addTarget(this.el));
const onTouchStart = this._register(new DomEmitter(this.el, EventType.Start)).event;
this._register(onTouchStart(e => this.onPointerStart(e, new GestureEventFactory(this.el)), this));
const onTap = this._register(new DomEmitter(this.el, EventType.Tap)).event;
let doubleTapTimeout: any = undefined;
this._register(onTap(event => {
if (doubleTapTimeout) {
clearTimeout(doubleTapTimeout);
doubleTapTimeout = undefined;
this.onPointerDoublePress(event);
return;
}
clearTimeout(doubleTapTimeout);
doubleTapTimeout = setTimeout(() => doubleTapTimeout = undefined, 250);
}, this));
if (typeof options.size === 'number') {
this.size = options.size;
if (options.orientation === Orientation.VERTICAL) {
this.el.style.width = `${this.size}px`;
} else {
this.el.style.height = `${this.size}px`;
}
} else {
this.size = globalSize;
this._register(onDidChangeGlobalSize.event(size => {
this.size = size;
this.layout();
}));
}
this._register(onDidChangeHoverDelay.event(delay => this.hoverDelay = delay));
this.layoutProvider = layoutProvider;
this.orthogonalStartSash = options.orthogonalStartSash;
this.orthogonalEndSash = options.orthogonalEndSash;
this.orientation = options.orientation || Orientation.VERTICAL;
if (this.orientation === Orientation.HORIZONTAL) {
this.el.classList.add('horizontal');
this.el.classList.remove('vertical');
} else {
this.el.classList.remove('horizontal');
this.el.classList.add('vertical');
}
this.el.classList.toggle('debug', DEBUG);
this.layout();
}
private onPointerStart(event: PointerEvent, pointerEventFactory: IPointerEventFactory): void {
EventHelper.stop(event);
let isMultisashResize = false;
if (!(event as any).__orthogonalSashEvent) {
const orthogonalSash = this.getOrthogonalSash(event);
if (orthogonalSash) {
isMultisashResize = true;
(event as any).__orthogonalSashEvent = true;
orthogonalSash.onPointerStart(event, new OrthogonalPointerEventFactory(pointerEventFactory));
}
}
if (this.linkedSash && !(event as any).__linkedSashEvent) {
(event as any).__linkedSashEvent = true;
this.linkedSash.onPointerStart(event, new OrthogonalPointerEventFactory(pointerEventFactory));
}
if (!this.state) {
return;
}
const iframes = this.el.ownerDocument.getElementsByTagName('iframe');
for (const iframe of iframes) {
iframe.classList.add(PointerEventsDisabledCssClass); // disable mouse events on iframes as long as we drag the sash
}
const startX = event.pageX;
const startY = event.pageY;
const altKey = event.altKey;
const startEvent: ISashEvent = { startX, currentX: startX, startY, currentY: startY, altKey };
this.el.classList.add('active');
this._onDidStart.fire(startEvent);
// fix https://github.com/microsoft/vscode/issues/21675
const style = createStyleSheet(this.el);
const updateStyle = () => {
let cursor = '';
if (isMultisashResize) {
cursor = 'all-scroll';
} else if (this.orientation === Orientation.HORIZONTAL) {
if (this.state === SashState.AtMinimum) {
cursor = 's-resize';
} else if (this.state === SashState.AtMaximum) {
cursor = 'n-resize';
} else {
cursor = isMacintosh ? 'row-resize' : 'ns-resize';
}
} else {
if (this.state === SashState.AtMinimum) {
cursor = 'e-resize';
} else if (this.state === SashState.AtMaximum) {
cursor = 'w-resize';
} else {
cursor = isMacintosh ? 'col-resize' : 'ew-resize';
}
}
style.textContent = `* { cursor: ${cursor} !important; }`;
};
const disposables = new DisposableStore();
updateStyle();
if (!isMultisashResize) {
this.onDidEnablementChange.event(updateStyle, null, disposables);
}
const onPointerMove = (e: PointerEvent) => {
EventHelper.stop(e, false);
const event: ISashEvent = { startX, currentX: e.pageX, startY, currentY: e.pageY, altKey };
this._onDidChange.fire(event);
};
const onPointerUp = (e: PointerEvent) => {
EventHelper.stop(e, false);
style.remove();
this.el.classList.remove('active');
this._onDidEnd.fire();
disposables.dispose();
for (const iframe of iframes) {
iframe.classList.remove(PointerEventsDisabledCssClass);
}
};
pointerEventFactory.onPointerMove(onPointerMove, null, disposables);
pointerEventFactory.onPointerUp(onPointerUp, null, disposables);
disposables.add(pointerEventFactory);
}
private onPointerDoublePress(e: MouseEvent): void {
const orthogonalSash = this.getOrthogonalSash(e);
if (orthogonalSash) {
orthogonalSash._onDidReset.fire();
}
if (this.linkedSash) {
this.linkedSash._onDidReset.fire();
}
this._onDidReset.fire();
}
private static onMouseEnter(sash: Sash, fromLinkedSash: boolean = false): void {
if (sash.el.classList.contains('active')) {
sash.hoverDelayer.cancel();
sash.el.classList.add('hover');
} else {
sash.hoverDelayer.trigger(() => sash.el.classList.add('hover'), sash.hoverDelay).then(undefined, () => { });
}
if (!fromLinkedSash && sash.linkedSash) {
Sash.onMouseEnter(sash.linkedSash, true);
}
}
private static onMouseLeave(sash: Sash, fromLinkedSash: boolean = false): void {
sash.hoverDelayer.cancel();
sash.el.classList.remove('hover');
if (!fromLinkedSash && sash.linkedSash) {
Sash.onMouseLeave(sash.linkedSash, true);
}
}
/**
* Forcefully stop any user interactions with this sash.
* Useful when hiding a parent component, while the user is still
* interacting with the sash.
*/
clearSashHoverState(): void {
Sash.onMouseLeave(this);
}
/**
* Layout the sash. The sash will size and position itself
* based on its provided {@link ISashLayoutProvider layout provider}.
*/
layout(): void {
if (this.orientation === Orientation.VERTICAL) {
const verticalProvider = (<IVerticalSashLayoutProvider>this.layoutProvider);
this.el.style.left = verticalProvider.getVerticalSashLeft(this) - (this.size / 2) + 'px';
if (verticalProvider.getVerticalSashTop) {
this.el.style.top = verticalProvider.getVerticalSashTop(this) + 'px';
}
if (verticalProvider.getVerticalSashHeight) {
this.el.style.height = verticalProvider.getVerticalSashHeight(this) + 'px';
}
} else {
const horizontalProvider = (<IHorizontalSashLayoutProvider>this.layoutProvider);
this.el.style.top = horizontalProvider.getHorizontalSashTop(this) - (this.size / 2) + 'px';
if (horizontalProvider.getHorizontalSashLeft) {
this.el.style.left = horizontalProvider.getHorizontalSashLeft(this) + 'px';
}
if (horizontalProvider.getHorizontalSashWidth) {
this.el.style.width = horizontalProvider.getHorizontalSashWidth(this) + 'px';
}
}
}
private getOrthogonalSash(e: PointerEvent): Sash | undefined {
const target = e.initialTarget ?? e.target;
if (!target || !(isHTMLElement(target))) {
return undefined;
}
if (target.classList.contains('orthogonal-drag-handle')) {
return target.classList.contains('start') ? this.orthogonalStartSash : this.orthogonalEndSash;
}
return undefined;
}
override dispose(): void {
super.dispose();
this.el.remove();
}
}
@@ -1,152 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.monaco-pane-view {
width: 100%;
height: 100%;
}
.monaco-pane-view .pane {
overflow: hidden;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
}
.monaco-pane-view .pane.horizontal:not(.expanded) {
flex-direction: row;
}
.monaco-pane-view .pane > .pane-header {
height: 22px;
font-size: 11px;
font-weight: bold;
overflow: hidden;
display: flex;
cursor: pointer;
align-items: center;
box-sizing: border-box;
}
.monaco-pane-view .pane > .pane-header.not-collapsible {
cursor: default;
}
.monaco-pane-view .pane > .pane-header > .title {
text-transform: uppercase;
}
.monaco-pane-view .pane.horizontal:not(.expanded) > .pane-header {
flex-direction: column;
height: 100%;
width: 22px;
}
.monaco-pane-view .pane > .pane-header > .codicon:first-of-type {
margin: 0 2px;
}
.monaco-pane-view .pane.horizontal:not(.expanded) > .pane-header > .codicon:first-of-type {
margin: 2px;
}
/* TODO: actions should be part of the pane, but they aren't yet */
.monaco-pane-view .pane > .pane-header > .actions {
display: none;
margin-left: auto;
}
.monaco-pane-view .pane > .pane-header > .actions .action-item {
margin-right: 4px;
}
.monaco-pane-view .pane > .pane-header > .actions .action-label {
padding: 2px;
}
/* TODO: actions should be part of the pane, but they aren't yet */
.monaco-pane-view .pane:hover > .pane-header.expanded > .actions,
.monaco-pane-view .pane:focus-within > .pane-header.expanded > .actions,
.monaco-pane-view .pane > .pane-header.actions-always-visible.expanded > .actions,
.monaco-pane-view .pane > .pane-header.focused.expanded > .actions {
display: initial;
}
.monaco-pane-view .pane > .pane-header .monaco-action-bar .action-item.select-container {
cursor: default;
}
.monaco-pane-view .pane > .pane-header .action-item .monaco-select-box {
cursor: pointer;
min-width: 110px;
min-height: 18px;
padding: 2px 23px 2px 8px;
}
.linux .monaco-pane-view .pane > .pane-header .action-item .monaco-select-box,
.windows .monaco-pane-view .pane > .pane-header .action-item .monaco-select-box {
padding: 0px 23px 0px 8px;
}
/* Bold font style does not go well with CJK fonts */
.monaco-pane-view:lang(zh-Hans) .pane > .pane-header,
.monaco-pane-view:lang(zh-Hant) .pane > .pane-header,
.monaco-pane-view:lang(ja) .pane > .pane-header,
.monaco-pane-view:lang(ko) .pane > .pane-header {
font-weight: normal;
}
.monaco-pane-view .pane > .pane-header.hidden {
display: none;
}
.monaco-pane-view .pane > .pane-body {
overflow: hidden;
flex: 1;
}
/* Animation */
.monaco-pane-view.animated .split-view-view {
transition-duration: 0.15s;
transition-timing-function: ease-out;
}
.reduce-motion .monaco-pane-view .split-view-view {
transition-duration: 0s !important;
}
.monaco-pane-view.animated.vertical .split-view-view {
transition-property: height;
}
.monaco-pane-view.animated.horizontal .split-view-view {
transition-property: width;
}
#monaco-pane-drop-overlay {
position: absolute;
z-index: 10000;
width: 100%;
height: 100%;
left: 0;
box-sizing: border-box;
}
#monaco-pane-drop-overlay > .pane-overlay-indicator {
position: absolute;
width: 100%;
height: 100%;
min-height: 22px;
min-width: 19px;
pointer-events: none; /* very important to not take events away from the parent */
transition: opacity 150ms ease-out;
}
#monaco-pane-drop-overlay > .pane-overlay-indicator.overlay-move-transition {
transition: top 70ms ease-out, left 70ms ease-out, width 70ms ease-out, height 70ms ease-out, opacity 150ms ease-out;
}
@@ -1,681 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { isFirefox } from 'vs/base/browser/browser';
import { DataTransfers } from 'vs/base/browser/dnd';
import { $, addDisposableListener, append, clearNode, EventHelper, EventType, getWindow, isHTMLElement, trackFocus } from 'vs/base/browser/dom';
import { DomEmitter } from 'vs/base/browser/event';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { Gesture, EventType as TouchEventType } from 'vs/base/browser/touch';
import { IBoundarySashes, Orientation } from 'vs/base/browser/ui/sash/sash';
import { Color, RGBA } from 'vs/base/common/color';
import { Emitter, Event } from 'vs/base/common/event';
import { KeyCode } from 'vs/base/common/keyCodes';
import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle';
import { ScrollEvent } from 'vs/base/common/scrollable';
// import 'vs/css!./paneview';
import { localize } from 'vs/nls';
import { IView, Sizing, SplitView } from './splitview';
export interface IPaneOptions {
minimumBodySize?: number;
maximumBodySize?: number;
expanded?: boolean;
orientation?: Orientation;
title: string;
titleDescription?: string;
}
export interface IPaneStyles {
readonly dropBackground: string | undefined;
readonly headerForeground: string | undefined;
readonly headerBackground: string | undefined;
readonly headerBorder: string | undefined;
readonly leftBorder: string | undefined;
}
/**
* A Pane is a structured SplitView view.
*
* WARNING: You must call `render()` after you construct it.
* It can't be done automatically at the end of the ctor
* because of the order of property initialization in TypeScript.
* Subclasses wouldn't be able to set own properties
* before the `render()` call, thus forbidding their use.
*/
export abstract class Pane extends Disposable implements IView {
private static readonly HEADER_SIZE = 22;
readonly element: HTMLElement;
private header!: HTMLElement;
private body!: HTMLElement;
protected _expanded: boolean;
protected _orientation: Orientation;
private expandedSize: number | undefined = undefined;
private _headerVisible = true;
private _collapsible = true;
private _bodyRendered = false;
private _minimumBodySize: number;
private _maximumBodySize: number;
private _ariaHeaderLabel: string;
private styles: IPaneStyles = {
dropBackground: undefined,
headerBackground: undefined,
headerBorder: undefined,
headerForeground: undefined,
leftBorder: undefined
};
private animationTimer: number | undefined = undefined;
private readonly _onDidChange = this._register(new Emitter<number | undefined>());
readonly onDidChange: Event<number | undefined> = this._onDidChange.event;
private readonly _onDidChangeExpansionState = this._register(new Emitter<boolean>());
readonly onDidChangeExpansionState: Event<boolean> = this._onDidChangeExpansionState.event;
get ariaHeaderLabel(): string {
return this._ariaHeaderLabel;
}
set ariaHeaderLabel(newLabel: string) {
this._ariaHeaderLabel = newLabel;
this.header.setAttribute('aria-label', this.ariaHeaderLabel);
}
get draggableElement(): HTMLElement {
return this.header;
}
get dropTargetElement(): HTMLElement {
return this.element;
}
get dropBackground(): string | undefined {
return this.styles.dropBackground;
}
get minimumBodySize(): number {
return this._minimumBodySize;
}
set minimumBodySize(size: number) {
this._minimumBodySize = size;
this._onDidChange.fire(undefined);
}
get maximumBodySize(): number {
return this._maximumBodySize;
}
set maximumBodySize(size: number) {
this._maximumBodySize = size;
this._onDidChange.fire(undefined);
}
private get headerSize(): number {
return this.headerVisible ? Pane.HEADER_SIZE : 0;
}
get minimumSize(): number {
const headerSize = this.headerSize;
const expanded = !this.headerVisible || this.isExpanded();
const minimumBodySize = expanded ? this.minimumBodySize : 0;
return headerSize + minimumBodySize;
}
get maximumSize(): number {
const headerSize = this.headerSize;
const expanded = !this.headerVisible || this.isExpanded();
const maximumBodySize = expanded ? this.maximumBodySize : 0;
return headerSize + maximumBodySize;
}
orthogonalSize: number = 0;
constructor(options: IPaneOptions) {
super();
this._expanded = typeof options.expanded === 'undefined' ? true : !!options.expanded;
this._orientation = typeof options.orientation === 'undefined' ? Orientation.VERTICAL : options.orientation;
this._ariaHeaderLabel = localize('viewSection', "{0} Section", options.title);
this._minimumBodySize = typeof options.minimumBodySize === 'number' ? options.minimumBodySize : this._orientation === Orientation.HORIZONTAL ? 200 : 120;
this._maximumBodySize = typeof options.maximumBodySize === 'number' ? options.maximumBodySize : Number.POSITIVE_INFINITY;
this.element = $('.pane');
}
isExpanded(): boolean {
return this._expanded;
}
setExpanded(expanded: boolean): boolean {
if (!expanded && !this.collapsible) {
return false;
}
if (this._expanded === !!expanded) {
return false;
}
this.element?.classList.toggle('expanded', expanded);
this._expanded = !!expanded;
this.updateHeader();
if (expanded) {
if (!this._bodyRendered) {
this.renderBody(this.body);
this._bodyRendered = true;
}
if (typeof this.animationTimer === 'number') {
getWindow(this.element).clearTimeout(this.animationTimer);
}
append(this.element, this.body);
} else {
this.animationTimer = getWindow(this.element).setTimeout(() => {
this.body.remove();
}, 200);
}
this._onDidChangeExpansionState.fire(expanded);
this._onDidChange.fire(expanded ? this.expandedSize : undefined);
return true;
}
get headerVisible(): boolean {
return this._headerVisible;
}
set headerVisible(visible: boolean) {
if (this._headerVisible === !!visible) {
return;
}
this._headerVisible = !!visible;
this.updateHeader();
this._onDidChange.fire(undefined);
}
get collapsible(): boolean {
return this._collapsible;
}
set collapsible(collapsible: boolean) {
if (this._collapsible === !!collapsible) {
return;
}
this._collapsible = !!collapsible;
this.updateHeader();
}
get orientation(): Orientation {
return this._orientation;
}
set orientation(orientation: Orientation) {
if (this._orientation === orientation) {
return;
}
this._orientation = orientation;
if (this.element) {
this.element.classList.toggle('horizontal', this.orientation === Orientation.HORIZONTAL);
this.element.classList.toggle('vertical', this.orientation === Orientation.VERTICAL);
}
if (this.header) {
this.updateHeader();
}
}
render(): void {
this.element.classList.toggle('expanded', this.isExpanded());
this.element.classList.toggle('horizontal', this.orientation === Orientation.HORIZONTAL);
this.element.classList.toggle('vertical', this.orientation === Orientation.VERTICAL);
this.header = $('.pane-header');
append(this.element, this.header);
this.header.setAttribute('tabindex', '0');
// Use role button so the aria-expanded state gets read https://github.com/microsoft/vscode/issues/95996
this.header.setAttribute('role', 'button');
this.header.setAttribute('aria-label', this.ariaHeaderLabel);
this.renderHeader(this.header);
const focusTracker = trackFocus(this.header);
this._register(focusTracker);
this._register(focusTracker.onDidFocus(() => this.header.classList.add('focused'), null));
this._register(focusTracker.onDidBlur(() => this.header.classList.remove('focused'), null));
this.updateHeader();
const eventDisposables = this._register(new DisposableStore());
const onKeyDown = this._register(new DomEmitter(this.header, 'keydown'));
const onHeaderKeyDown = Event.map(onKeyDown.event, e => new StandardKeyboardEvent(e), eventDisposables);
this._register(Event.filter(onHeaderKeyDown, e => e.keyCode === KeyCode.Enter || e.keyCode === KeyCode.Space, eventDisposables)(() => this.setExpanded(!this.isExpanded()), null));
this._register(Event.filter(onHeaderKeyDown, e => e.keyCode === KeyCode.LeftArrow, eventDisposables)(() => this.setExpanded(false), null));
this._register(Event.filter(onHeaderKeyDown, e => e.keyCode === KeyCode.RightArrow, eventDisposables)(() => this.setExpanded(true), null));
this._register(Gesture.addTarget(this.header));
[EventType.CLICK, TouchEventType.Tap].forEach(eventType => {
this._register(addDisposableListener(this.header, eventType, e => {
if (!e.defaultPrevented) {
this.setExpanded(!this.isExpanded());
}
}));
});
this.body = append(this.element, $('.pane-body'));
// Only render the body if it will be visible
// Otherwise, render it when the pane is expanded
if (!this._bodyRendered && this.isExpanded()) {
this.renderBody(this.body);
this._bodyRendered = true;
}
if (!this.isExpanded()) {
this.body.remove();
}
}
layout(size: number): void {
const headerSize = this.headerVisible ? Pane.HEADER_SIZE : 0;
const width = this._orientation === Orientation.VERTICAL ? this.orthogonalSize : size;
const height = this._orientation === Orientation.VERTICAL ? size - headerSize : this.orthogonalSize - headerSize;
if (this.isExpanded()) {
this.body.classList.toggle('wide', width >= 600);
this.layoutBody(height, width);
this.expandedSize = size;
}
}
style(styles: IPaneStyles): void {
this.styles = styles;
if (!this.header) {
return;
}
this.updateHeader();
}
protected updateHeader(): void {
const expanded = !this.headerVisible || this.isExpanded();
if (this.collapsible) {
this.header.setAttribute('tabindex', '0');
this.header.setAttribute('role', 'button');
} else {
this.header.removeAttribute('tabindex');
this.header.removeAttribute('role');
}
this.header.style.lineHeight = `${this.headerSize}px`;
this.header.classList.toggle('hidden', !this.headerVisible);
this.header.classList.toggle('expanded', expanded);
this.header.classList.toggle('not-collapsible', !this.collapsible);
this.header.setAttribute('aria-expanded', String(expanded));
this.header.style.color = this.collapsible ? this.styles.headerForeground ?? '' : '';
this.header.style.backgroundColor = (this.collapsible ? this.styles.headerBackground : 'transparent') ?? '';
this.header.style.borderTop = this.styles.headerBorder && this.orientation === Orientation.VERTICAL ? `1px solid ${this.styles.headerBorder}` : '';
this.element.style.borderLeft = this.styles.leftBorder && this.orientation === Orientation.HORIZONTAL ? `1px solid ${this.styles.leftBorder}` : '';
}
protected abstract renderHeader(container: HTMLElement): void;
protected abstract renderBody(container: HTMLElement): void;
protected abstract layoutBody(height: number, width: number): void;
}
interface IDndContext {
draggable: PaneDraggable | null;
}
class PaneDraggable extends Disposable {
private static readonly DefaultDragOverBackgroundColor = new Color(new RGBA(128, 128, 128, 0.5));
private dragOverCounter = 0; // see https://github.com/microsoft/vscode/issues/14470
private _onDidDrop = this._register(new Emitter<{ from: Pane; to: Pane }>());
readonly onDidDrop = this._onDidDrop.event;
constructor(private pane: Pane, private dnd: IPaneDndController, private context: IDndContext) {
super();
pane.draggableElement.draggable = true;
this._register(addDisposableListener(pane.draggableElement, 'dragstart', e => this.onDragStart(e)));
this._register(addDisposableListener(pane.dropTargetElement, 'dragenter', e => this.onDragEnter(e)));
this._register(addDisposableListener(pane.dropTargetElement, 'dragleave', e => this.onDragLeave(e)));
this._register(addDisposableListener(pane.dropTargetElement, 'dragend', e => this.onDragEnd(e)));
this._register(addDisposableListener(pane.dropTargetElement, 'drop', e => this.onDrop(e)));
}
private onDragStart(e: DragEvent): void {
if (!this.dnd.canDrag(this.pane) || !e.dataTransfer) {
e.preventDefault();
e.stopPropagation();
return;
}
e.dataTransfer.effectAllowed = 'move';
if (isFirefox) {
// Firefox: requires to set a text data transfer to get going
e.dataTransfer?.setData(DataTransfers.TEXT, this.pane.draggableElement.textContent || '');
}
const dragImage = append(this.pane.element.ownerDocument.body, $('.monaco-drag-image', {}, this.pane.draggableElement.textContent || ''));
e.dataTransfer.setDragImage(dragImage, -10, -10);
setTimeout(() => dragImage.remove(), 0);
this.context.draggable = this;
}
private onDragEnter(e: DragEvent): void {
if (!this.context.draggable || this.context.draggable === this) {
return;
}
if (!this.dnd.canDrop(this.context.draggable.pane, this.pane)) {
return;
}
this.dragOverCounter++;
this.render();
}
private onDragLeave(e: DragEvent): void {
if (!this.context.draggable || this.context.draggable === this) {
return;
}
if (!this.dnd.canDrop(this.context.draggable.pane, this.pane)) {
return;
}
this.dragOverCounter--;
if (this.dragOverCounter === 0) {
this.render();
}
}
private onDragEnd(e: DragEvent): void {
if (!this.context.draggable) {
return;
}
this.dragOverCounter = 0;
this.render();
this.context.draggable = null;
}
private onDrop(e: DragEvent): void {
if (!this.context.draggable) {
return;
}
EventHelper.stop(e);
this.dragOverCounter = 0;
this.render();
if (this.dnd.canDrop(this.context.draggable.pane, this.pane) && this.context.draggable !== this) {
this._onDidDrop.fire({ from: this.context.draggable.pane, to: this.pane });
}
this.context.draggable = null;
}
private render(): void {
let backgroundColor: string | null = null;
if (this.dragOverCounter > 0) {
backgroundColor = this.pane.dropBackground ?? PaneDraggable.DefaultDragOverBackgroundColor.toString();
}
this.pane.dropTargetElement.style.backgroundColor = backgroundColor || '';
}
}
export interface IPaneDndController {
canDrag(pane: Pane): boolean;
canDrop(pane: Pane, overPane: Pane): boolean;
}
export class DefaultPaneDndController implements IPaneDndController {
canDrag(pane: Pane): boolean {
return true;
}
canDrop(pane: Pane, overPane: Pane): boolean {
return true;
}
}
export interface IPaneViewOptions {
dnd?: IPaneDndController;
orientation?: Orientation;
}
interface IPaneItem {
pane: Pane;
disposable: IDisposable;
}
export class PaneView extends Disposable {
private dnd: IPaneDndController | undefined;
private dndContext: IDndContext = { draggable: null };
readonly element: HTMLElement;
private paneItems: IPaneItem[] = [];
private orthogonalSize: number = 0;
private size: number = 0;
private splitview: SplitView;
private animationTimer: number | undefined = undefined;
private _onDidDrop = this._register(new Emitter<{ from: Pane; to: Pane }>());
readonly onDidDrop: Event<{ from: Pane; to: Pane }> = this._onDidDrop.event;
orientation: Orientation;
private boundarySashes: IBoundarySashes | undefined;
readonly onDidSashChange: Event<number>;
readonly onDidSashReset: Event<number>;
readonly onDidScroll: Event<ScrollEvent>;
constructor(container: HTMLElement, options: IPaneViewOptions = {}) {
super();
this.dnd = options.dnd;
this.orientation = options.orientation ?? Orientation.VERTICAL;
this.element = append(container, $('.monaco-pane-view'));
this.splitview = this._register(new SplitView(this.element, { orientation: this.orientation }));
this.onDidSashReset = this.splitview.onDidSashReset;
this.onDidSashChange = this.splitview.onDidSashChange;
this.onDidScroll = this.splitview.onDidScroll;
const eventDisposables = this._register(new DisposableStore());
const onKeyDown = this._register(new DomEmitter(this.element, 'keydown'));
const onHeaderKeyDown = Event.map(Event.filter(onKeyDown.event, e => isHTMLElement(e.target) && e.target.classList.contains('pane-header'), eventDisposables), e => new StandardKeyboardEvent(e), eventDisposables);
this._register(Event.filter(onHeaderKeyDown, e => e.keyCode === KeyCode.UpArrow, eventDisposables)(() => this.focusPrevious()));
this._register(Event.filter(onHeaderKeyDown, e => e.keyCode === KeyCode.DownArrow, eventDisposables)(() => this.focusNext()));
}
addPane(pane: Pane, size: number, index = this.splitview.length): void {
const disposables = new DisposableStore();
pane.onDidChangeExpansionState(this.setupAnimation, this, disposables);
const paneItem = { pane: pane, disposable: disposables };
this.paneItems.splice(index, 0, paneItem);
pane.orientation = this.orientation;
pane.orthogonalSize = this.orthogonalSize;
this.splitview.addView(pane, size, index);
if (this.dnd) {
const draggable = new PaneDraggable(pane, this.dnd, this.dndContext);
disposables.add(draggable);
disposables.add(draggable.onDidDrop(this._onDidDrop.fire, this._onDidDrop));
}
}
removePane(pane: Pane): void {
const index = this.paneItems.findIndex(item => item.pane === pane);
if (index === -1) {
return;
}
this.splitview.removeView(index, pane.isExpanded() ? Sizing.Distribute : undefined);
const paneItem = this.paneItems.splice(index, 1)[0];
paneItem.disposable.dispose();
}
movePane(from: Pane, to: Pane): void {
const fromIndex = this.paneItems.findIndex(item => item.pane === from);
const toIndex = this.paneItems.findIndex(item => item.pane === to);
if (fromIndex === -1 || toIndex === -1) {
return;
}
const [paneItem] = this.paneItems.splice(fromIndex, 1);
this.paneItems.splice(toIndex, 0, paneItem);
this.splitview.moveView(fromIndex, toIndex);
}
resizePane(pane: Pane, size: number): void {
const index = this.paneItems.findIndex(item => item.pane === pane);
if (index === -1) {
return;
}
this.splitview.resizeView(index, size);
}
getPaneSize(pane: Pane): number {
const index = this.paneItems.findIndex(item => item.pane === pane);
if (index === -1) {
return -1;
}
return this.splitview.getViewSize(index);
}
layout(height: number, width: number): void {
this.orthogonalSize = this.orientation === Orientation.VERTICAL ? width : height;
this.size = this.orientation === Orientation.HORIZONTAL ? width : height;
for (const paneItem of this.paneItems) {
paneItem.pane.orthogonalSize = this.orthogonalSize;
}
this.splitview.layout(this.size);
}
setBoundarySashes(sashes: IBoundarySashes) {
this.boundarySashes = sashes;
this.updateSplitviewOrthogonalSashes(sashes);
}
private updateSplitviewOrthogonalSashes(sashes: IBoundarySashes | undefined) {
if (this.orientation === Orientation.VERTICAL) {
this.splitview.orthogonalStartSash = sashes?.left;
this.splitview.orthogonalEndSash = sashes?.right;
} else {
this.splitview.orthogonalEndSash = sashes?.bottom;
}
}
flipOrientation(height: number, width: number): void {
this.orientation = this.orientation === Orientation.VERTICAL ? Orientation.HORIZONTAL : Orientation.VERTICAL;
const paneSizes = this.paneItems.map(pane => this.getPaneSize(pane.pane));
this.splitview.dispose();
clearNode(this.element);
this.splitview = this._register(new SplitView(this.element, { orientation: this.orientation }));
this.updateSplitviewOrthogonalSashes(this.boundarySashes);
const newOrthogonalSize = this.orientation === Orientation.VERTICAL ? width : height;
const newSize = this.orientation === Orientation.HORIZONTAL ? width : height;
this.paneItems.forEach((pane, index) => {
pane.pane.orthogonalSize = newOrthogonalSize;
pane.pane.orientation = this.orientation;
const viewSize = this.size === 0 ? 0 : (newSize * paneSizes[index]) / this.size;
this.splitview.addView(pane.pane, viewSize, index);
});
this.size = newSize;
this.orthogonalSize = newOrthogonalSize;
this.splitview.layout(this.size);
}
private setupAnimation(): void {
if (typeof this.animationTimer === 'number') {
getWindow(this.element).clearTimeout(this.animationTimer);
}
this.element.classList.add('animated');
this.animationTimer = getWindow(this.element).setTimeout(() => {
this.animationTimer = undefined;
this.element.classList.remove('animated');
}, 200);
}
private getPaneHeaderElements(): HTMLElement[] {
return [...this.element.querySelectorAll('.pane-header')] as HTMLElement[];
}
private focusPrevious(): void {
const headers = this.getPaneHeaderElements();
const index = headers.indexOf(this.element.ownerDocument.activeElement as HTMLElement);
if (index === -1) {
return;
}
headers[Math.max(index - 1, 0)].focus();
}
private focusNext(): void {
const headers = this.getPaneHeaderElements();
const index = headers.indexOf(this.element.ownerDocument.activeElement as HTMLElement);
if (index === -1) {
return;
}
headers[Math.min(index + 1, headers.length - 1)].focus();
}
override dispose(): void {
super.dispose();
this.paneItems.forEach(i => i.disposable.dispose());
}
}
@@ -1,70 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.monaco-split-view2 {
position: relative;
width: 100%;
height: 100%;
}
.monaco-split-view2 > .sash-container {
position: absolute;
width: 100%;
height: 100%;
pointer-events: none;
}
.monaco-split-view2 > .sash-container > .monaco-sash {
pointer-events: initial;
}
.monaco-split-view2 > .monaco-scrollable-element {
width: 100%;
height: 100%;
}
.monaco-split-view2 > .monaco-scrollable-element > .split-view-container {
width: 100%;
height: 100%;
white-space: nowrap;
position: relative;
}
.monaco-split-view2 > .monaco-scrollable-element > .split-view-container > .split-view-view {
white-space: initial;
position: absolute;
}
.monaco-split-view2 > .monaco-scrollable-element > .split-view-container > .split-view-view:not(.visible) {
display: none;
}
.monaco-split-view2.vertical > .monaco-scrollable-element > .split-view-container > .split-view-view {
width: 100%;
}
.monaco-split-view2.horizontal > .monaco-scrollable-element > .split-view-container > .split-view-view {
height: 100%;
}
.monaco-split-view2.separator-border > .monaco-scrollable-element > .split-view-container > .split-view-view:not(:first-child)::before {
content: ' ';
position: absolute;
top: 0;
left: 0;
z-index: 5;
pointer-events: none;
background-color: var(--separator-border);
}
.monaco-split-view2.separator-border.horizontal > .monaco-scrollable-element > .split-view-container > .split-view-view:not(:first-child)::before {
height: 100%;
width: 1px;
}
.monaco-split-view2.separator-border.vertical > .monaco-scrollable-element > .split-view-container > .split-view-view:not(:first-child)::before {
height: 1px;
width: 100%;
}
File diff suppressed because it is too large Load Diff
-271
View File
@@ -1,271 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Emitter, Event } from 'vs/base/common/event';
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
import * as nls from 'vs/nls';
export interface ITelemetryData {
readonly from?: string;
readonly target?: string;
[key: string]: unknown;
}
export type WorkbenchActionExecutedClassification = {
id: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The identifier of the action that was run.' };
from: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The name of the component the action was run from.' };
detail?: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Optional details about how the action was run, e.g which keybinding was used.' };
owner: 'bpasero';
comment: 'Provides insight into actions that are executed within the workbench.';
};
export type WorkbenchActionExecutedEvent = {
id: string;
from: string;
detail?: string;
};
export interface IAction {
readonly id: string;
label: string;
tooltip: string;
class: string | undefined;
enabled: boolean;
checked?: boolean;
run(...args: unknown[]): unknown;
}
export interface IActionRunner extends IDisposable {
readonly onDidRun: Event<IRunEvent>;
readonly onWillRun: Event<IRunEvent>;
run(action: IAction, context?: unknown): unknown;
}
export interface IActionChangeEvent {
readonly label?: string;
readonly tooltip?: string;
readonly class?: string;
readonly enabled?: boolean;
readonly checked?: boolean;
}
export class Action extends Disposable implements IAction {
protected _onDidChange = this._register(new Emitter<IActionChangeEvent>());
readonly onDidChange = this._onDidChange.event;
protected readonly _id: string;
protected _label: string;
protected _tooltip: string | undefined;
protected _cssClass: string | undefined;
protected _enabled: boolean = true;
protected _checked?: boolean;
protected readonly _actionCallback?: (event?: unknown) => unknown;
constructor(id: string, label: string = '', cssClass: string = '', enabled: boolean = true, actionCallback?: (event?: unknown) => unknown) {
super();
this._id = id;
this._label = label;
this._cssClass = cssClass;
this._enabled = enabled;
this._actionCallback = actionCallback;
}
get id(): string {
return this._id;
}
get label(): string {
return this._label;
}
set label(value: string) {
this._setLabel(value);
}
private _setLabel(value: string): void {
if (this._label !== value) {
this._label = value;
this._onDidChange.fire({ label: value });
}
}
get tooltip(): string {
return this._tooltip || '';
}
set tooltip(value: string) {
this._setTooltip(value);
}
protected _setTooltip(value: string): void {
if (this._tooltip !== value) {
this._tooltip = value;
this._onDidChange.fire({ tooltip: value });
}
}
get class(): string | undefined {
return this._cssClass;
}
set class(value: string | undefined) {
this._setClass(value);
}
protected _setClass(value: string | undefined): void {
if (this._cssClass !== value) {
this._cssClass = value;
this._onDidChange.fire({ class: value });
}
}
get enabled(): boolean {
return this._enabled;
}
set enabled(value: boolean) {
this._setEnabled(value);
}
protected _setEnabled(value: boolean): void {
if (this._enabled !== value) {
this._enabled = value;
this._onDidChange.fire({ enabled: value });
}
}
get checked(): boolean | undefined {
return this._checked;
}
set checked(value: boolean | undefined) {
this._setChecked(value);
}
protected _setChecked(value: boolean | undefined): void {
if (this._checked !== value) {
this._checked = value;
this._onDidChange.fire({ checked: value });
}
}
async run(event?: unknown, data?: ITelemetryData): Promise<void> {
if (this._actionCallback) {
await this._actionCallback(event);
}
}
}
export interface IRunEvent {
readonly action: IAction;
readonly error?: Error;
}
export class ActionRunner extends Disposable implements IActionRunner {
private readonly _onWillRun = this._register(new Emitter<IRunEvent>());
readonly onWillRun = this._onWillRun.event;
private readonly _onDidRun = this._register(new Emitter<IRunEvent>());
readonly onDidRun = this._onDidRun.event;
async run(action: IAction, context?: unknown): Promise<void> {
if (!action.enabled) {
return;
}
this._onWillRun.fire({ action });
let error: Error | undefined = undefined;
try {
await this.runAction(action, context);
} catch (e) {
error = e;
}
this._onDidRun.fire({ action, error });
}
protected async runAction(action: IAction, context?: unknown): Promise<void> {
await action.run(context);
}
}
export class Separator implements IAction {
/**
* Joins all non-empty lists of actions with separators.
*/
public static join(...actionLists: readonly IAction[][]) {
let out: IAction[] = [];
for (const list of actionLists) {
if (!list.length) {
// skip
} else if (out.length) {
out = [...out, new Separator(), ...list];
} else {
out = list;
}
}
return out;
}
static readonly ID = 'vs.actions.separator';
readonly id: string = Separator.ID;
readonly label: string = '';
readonly tooltip: string = '';
readonly class: string = 'separator';
readonly enabled: boolean = false;
readonly checked: boolean = false;
async run() { }
}
export class SubmenuAction implements IAction {
readonly id: string;
readonly label: string;
readonly class: string | undefined;
readonly tooltip: string = '';
readonly enabled: boolean = true;
readonly checked: undefined = undefined;
private readonly _actions: readonly IAction[];
get actions(): readonly IAction[] { return this._actions; }
constructor(id: string, label: string, actions: readonly IAction[], cssClass?: string) {
this.id = id;
this.label = label;
this.class = cssClass;
this._actions = actions;
}
async run(): Promise<void> { }
}
export class EmptySubmenuAction extends Action {
static readonly ID = 'vs.actions.empty';
constructor() {
super(EmptySubmenuAction.ID, nls.localize('submenu.empty', '(empty)'), undefined, false);
}
}
export function toAction(props: { id: string; label: string; tooltip?: string; enabled?: boolean; checked?: boolean; class?: string; run: Function }): IAction {
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: unknown[]) => props.run(...args),
};
}
-163
View File
@@ -1,163 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
// ESM-comment-begin
export const isESM = false;
// ESM-comment-end
// ESM-uncomment-begin
// export const isESM = true;
// ESM-uncomment-end
export const enum LoaderEventType {
LoaderAvailable = 1,
BeginLoadingScript = 10,
EndLoadingScriptOK = 11,
EndLoadingScriptError = 12,
BeginInvokeFactory = 21,
EndInvokeFactory = 22,
NodeBeginEvaluatingScript = 31,
NodeEndEvaluatingScript = 32,
NodeBeginNativeRequire = 33,
NodeEndNativeRequire = 34,
CachedDataFound = 60,
CachedDataMissed = 61,
CachedDataRejected = 62,
CachedDataCreated = 63,
}
export abstract class LoaderStats {
abstract get amdLoad(): [string, number][];
abstract get amdInvoke(): [string, number][];
abstract get nodeRequire(): [string, number][];
abstract get nodeEval(): [string, number][];
abstract get nodeRequireTotal(): number;
static get(): LoaderStats {
const amdLoadScript = new Map<string, number>();
const amdInvokeFactory = new Map<string, number>();
const nodeRequire = new Map<string, number>();
const nodeEval = new Map<string, number>();
function mark(map: Map<string, number>, stat: LoaderEvent) {
if (map.has(stat.detail)) {
// console.warn('BAD events, DOUBLE start', stat);
// map.delete(stat.detail);
return;
}
map.set(stat.detail, -stat.timestamp);
}
function diff(map: Map<string, number>, stat: LoaderEvent) {
const duration = map.get(stat.detail);
if (!duration) {
// console.warn('BAD events, end WITHOUT start', stat);
// map.delete(stat.detail);
return;
}
if (duration >= 0) {
// console.warn('BAD events, DOUBLE end', stat);
// map.delete(stat.detail);
return;
}
map.set(stat.detail, duration + stat.timestamp);
}
let stats: readonly LoaderEvent[] = [];
if (typeof require === 'function' && typeof require.getStats === 'function') {
stats = require.getStats().slice(0).sort((a, b) => a.timestamp - b.timestamp);
}
for (const stat of stats) {
switch (stat.type) {
case LoaderEventType.BeginLoadingScript:
mark(amdLoadScript, stat);
break;
case LoaderEventType.EndLoadingScriptOK:
case LoaderEventType.EndLoadingScriptError:
diff(amdLoadScript, stat);
break;
case LoaderEventType.BeginInvokeFactory:
mark(amdInvokeFactory, stat);
break;
case LoaderEventType.EndInvokeFactory:
diff(amdInvokeFactory, stat);
break;
case LoaderEventType.NodeBeginNativeRequire:
mark(nodeRequire, stat);
break;
case LoaderEventType.NodeEndNativeRequire:
diff(nodeRequire, stat);
break;
case LoaderEventType.NodeBeginEvaluatingScript:
mark(nodeEval, stat);
break;
case LoaderEventType.NodeEndEvaluatingScript:
diff(nodeEval, stat);
break;
}
}
let nodeRequireTotal = 0;
nodeRequire.forEach(value => nodeRequireTotal += value);
function to2dArray(map: Map<string, number>): [string, number][] {
const res: [string, number][] = [];
map.forEach((value, index) => res.push([index, value]));
return res;
}
return {
amdLoad: to2dArray(amdLoadScript),
amdInvoke: to2dArray(amdInvokeFactory),
nodeRequire: to2dArray(nodeRequire),
nodeEval: to2dArray(nodeEval),
nodeRequireTotal
};
}
static toMarkdownTable(header: string[], rows: Array<Array<{ toString(): string } | undefined>>): string {
let result = '';
const lengths: number[] = [];
header.forEach((cell, ci) => {
lengths[ci] = cell.length;
});
rows.forEach(row => {
row.forEach((cell, ci) => {
if (typeof cell === 'undefined') {
cell = row[ci] = '-';
}
const len = cell.toString().length;
lengths[ci] = Math.max(len, lengths[ci]);
});
});
// header
header.forEach((cell, ci) => { result += `| ${cell + ' '.repeat(lengths[ci] - cell.toString().length)} `; });
result += '|\n';
header.forEach((_cell, ci) => { result += `| ${'-'.repeat(lengths[ci])} `; });
result += '|\n';
// cells
rows.forEach(row => {
row.forEach((cell, ci) => {
if (typeof cell !== 'undefined') {
result += `| ${cell + ' '.repeat(lengths[ci] - cell.toString().length)} `;
}
});
result += '|\n';
});
return result;
}
}
-441
View File
@@ -1,441 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Lazy } from 'vs/base/common/lazy';
import * as streams from 'vs/base/common/stream';
declare const Buffer: any;
const hasBuffer = (typeof Buffer !== 'undefined');
const indexOfTable = new Lazy(() => new Uint8Array(256));
let textEncoder: TextEncoder | null;
let textDecoder: TextDecoder | null;
export class VSBuffer {
/**
* When running in a nodejs context, the backing store for the returned `VSBuffer` instance
* might use a nodejs Buffer allocated from node's Buffer pool, which is not transferrable.
*/
static alloc(byteLength: number): VSBuffer {
if (hasBuffer) {
return new VSBuffer(Buffer.allocUnsafe(byteLength));
} else {
return new VSBuffer(new Uint8Array(byteLength));
}
}
/**
* 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: Uint8Array): VSBuffer {
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);
}
/**
* When running in a nodejs context, the backing store for the returned `VSBuffer` instance
* might use a nodejs Buffer allocated from node's Buffer pool, which is not transferrable.
*/
static fromString(source: string, options?: { dontUseNodeBuffer?: boolean }): VSBuffer {
const dontUseNodeBuffer = options?.dontUseNodeBuffer || false;
if (!dontUseNodeBuffer && hasBuffer) {
return new VSBuffer(Buffer.from(source));
} else {
if (!textEncoder) {
textEncoder = new TextEncoder();
}
return new VSBuffer(textEncoder.encode(source));
}
}
/**
* When running in a nodejs context, the backing store for the returned `VSBuffer` instance
* might use a nodejs Buffer allocated from node's Buffer pool, which is not transferrable.
*/
static fromByteArray(source: number[]): VSBuffer {
const result = VSBuffer.alloc(source.length);
for (let i = 0, len = source.length; i < len; i++) {
result.buffer[i] = source[i];
}
return result;
}
/**
* When running in a nodejs context, the backing store for the returned `VSBuffer` instance
* might use a nodejs Buffer allocated from node's Buffer pool, which is not transferrable.
*/
static concat(buffers: VSBuffer[], totalLength?: number): VSBuffer {
if (typeof totalLength === 'undefined') {
totalLength = 0;
for (let i = 0, len = buffers.length; i < len; i++) {
totalLength += buffers[i].byteLength;
}
}
const ret = VSBuffer.alloc(totalLength);
let offset = 0;
for (let i = 0, len = buffers.length; i < len; i++) {
const element = buffers[i];
ret.set(element, offset);
offset += element.byteLength;
}
return ret;
}
readonly buffer: Uint8Array;
readonly byteLength: number;
private constructor(buffer: Uint8Array) {
this.buffer = buffer;
this.byteLength = this.buffer.byteLength;
}
/**
* When running in a nodejs context, the backing store for the returned `VSBuffer` instance
* might use a nodejs Buffer allocated from node's Buffer pool, which is not transferrable.
*/
clone(): VSBuffer {
const result = VSBuffer.alloc(this.byteLength);
result.set(this);
return result;
}
toString(): string {
if (hasBuffer) {
return this.buffer.toString();
} else {
if (!textDecoder) {
textDecoder = new TextDecoder();
}
return textDecoder.decode(this.buffer);
}
}
slice(start?: number, end?: number): VSBuffer {
// IMPORTANT: use subarray instead of slice because TypedArray#slice
// creates shallow copy and NodeBuffer#slice doesn't. The use of subarray
// ensures the same, performance, behaviour.
return new VSBuffer(this.buffer.subarray(start, end));
}
set(array: VSBuffer, offset?: number): void;
set(array: Uint8Array, offset?: number): void;
set(array: ArrayBuffer, offset?: number): void;
set(array: ArrayBufferView, offset?: number): void;
set(array: VSBuffer | Uint8Array | ArrayBuffer | ArrayBufferView, offset?: number): void;
set(array: VSBuffer | Uint8Array | ArrayBuffer | ArrayBufferView, offset?: number): void {
if (array instanceof VSBuffer) {
this.buffer.set(array.buffer, offset);
} else if (array instanceof Uint8Array) {
this.buffer.set(array, offset);
} else if (array instanceof ArrayBuffer) {
this.buffer.set(new Uint8Array(array), offset);
} else if (ArrayBuffer.isView(array)) {
this.buffer.set(new Uint8Array(array.buffer, array.byteOffset, array.byteLength), offset);
} else {
throw new Error(`Unknown argument 'array'`);
}
}
readUInt32BE(offset: number): number {
return readUInt32BE(this.buffer, offset);
}
writeUInt32BE(value: number, offset: number): void {
writeUInt32BE(this.buffer, value, offset);
}
readUInt32LE(offset: number): number {
return readUInt32LE(this.buffer, offset);
}
writeUInt32LE(value: number, offset: number): void {
writeUInt32LE(this.buffer, value, offset);
}
readUInt8(offset: number): number {
return readUInt8(this.buffer, offset);
}
writeUInt8(value: number, offset: number): void {
writeUInt8(this.buffer, value, offset);
}
indexOf(subarray: VSBuffer | Uint8Array, offset = 0) {
return binaryIndexOf(this.buffer, subarray instanceof VSBuffer ? subarray.buffer : subarray, offset);
}
}
/**
* Like String.indexOf, but works on Uint8Arrays.
* Uses the boyer-moore-horspool algorithm to be reasonably speedy.
*/
export function binaryIndexOf(haystack: Uint8Array, needle: Uint8Array, offset = 0): number {
const needleLen = needle.byteLength;
const haystackLen = haystack.byteLength;
if (needleLen === 0) {
return 0;
}
if (needleLen === 1) {
return haystack.indexOf(needle[0]);
}
if (needleLen > haystackLen - offset) {
return -1;
}
// find index of the subarray using boyer-moore-horspool algorithm
const table = indexOfTable.value;
table.fill(needle.length);
for (let i = 0; i < needle.length; i++) {
table[needle[i]] = needle.length - i - 1;
}
let i = offset + needle.length - 1;
let j = i;
let result = -1;
while (i < haystackLen) {
if (haystack[i] === needle[j]) {
if (j === 0) {
result = i;
break;
}
i--;
j--;
} else {
i += Math.max(needle.length - j, table[haystack[i]]);
j = needle.length - 1;
}
}
return result;
}
export function readUInt16LE(source: Uint8Array, offset: number): number {
return (
((source[offset + 0] << 0) >>> 0) |
((source[offset + 1] << 8) >>> 0)
);
}
export function writeUInt16LE(destination: Uint8Array, value: number, offset: number): void {
destination[offset + 0] = (value & 0b11111111);
value = value >>> 8;
destination[offset + 1] = (value & 0b11111111);
}
export function readUInt32BE(source: Uint8Array, offset: number): number {
return (
source[offset] * 2 ** 24
+ source[offset + 1] * 2 ** 16
+ source[offset + 2] * 2 ** 8
+ source[offset + 3]
);
}
export function writeUInt32BE(destination: Uint8Array, value: number, offset: number): void {
destination[offset + 3] = value;
value = value >>> 8;
destination[offset + 2] = value;
value = value >>> 8;
destination[offset + 1] = value;
value = value >>> 8;
destination[offset] = value;
}
export function readUInt32LE(source: Uint8Array, offset: number): number {
return (
((source[offset + 0] << 0) >>> 0) |
((source[offset + 1] << 8) >>> 0) |
((source[offset + 2] << 16) >>> 0) |
((source[offset + 3] << 24) >>> 0)
);
}
export function writeUInt32LE(destination: Uint8Array, value: number, offset: number): void {
destination[offset + 0] = (value & 0b11111111);
value = value >>> 8;
destination[offset + 1] = (value & 0b11111111);
value = value >>> 8;
destination[offset + 2] = (value & 0b11111111);
value = value >>> 8;
destination[offset + 3] = (value & 0b11111111);
}
export function readUInt8(source: Uint8Array, offset: number): number {
return source[offset];
}
export function writeUInt8(destination: Uint8Array, value: number, offset: number): void {
destination[offset] = value;
}
export interface VSBufferReadable extends streams.Readable<VSBuffer> { }
export interface VSBufferReadableStream extends streams.ReadableStream<VSBuffer> { }
export interface VSBufferWriteableStream extends streams.WriteableStream<VSBuffer> { }
export interface VSBufferReadableBufferedStream extends streams.ReadableBufferedStream<VSBuffer> { }
export function readableToBuffer(readable: VSBufferReadable): VSBuffer {
return streams.consumeReadable<VSBuffer>(readable, chunks => VSBuffer.concat(chunks));
}
export function bufferToReadable(buffer: VSBuffer): VSBufferReadable {
return streams.toReadable<VSBuffer>(buffer);
}
export function streamToBuffer(stream: streams.ReadableStream<VSBuffer>): Promise<VSBuffer> {
return streams.consumeStream<VSBuffer>(stream, chunks => VSBuffer.concat(chunks));
}
export async function bufferedStreamToBuffer(bufferedStream: streams.ReadableBufferedStream<VSBuffer>): Promise<VSBuffer> {
if (bufferedStream.ended) {
return VSBuffer.concat(bufferedStream.buffer);
}
return VSBuffer.concat([
// Include already read chunks...
...bufferedStream.buffer,
// ...and all additional chunks
await streamToBuffer(bufferedStream.stream)
]);
}
export function bufferToStream(buffer: VSBuffer): streams.ReadableStream<VSBuffer> {
return streams.toStream<VSBuffer>(buffer, chunks => VSBuffer.concat(chunks));
}
export function streamToBufferReadableStream(stream: streams.ReadableStreamEvents<Uint8Array | string>): streams.ReadableStream<VSBuffer> {
return streams.transform<Uint8Array | string, VSBuffer>(stream, { data: data => typeof data === 'string' ? VSBuffer.fromString(data) : VSBuffer.wrap(data) }, chunks => VSBuffer.concat(chunks));
}
export function newWriteableBufferStream(options?: streams.WriteableStreamOptions): streams.WriteableStream<VSBuffer> {
return streams.newWriteableStream<VSBuffer>(chunks => VSBuffer.concat(chunks), options);
}
export function prefixedBufferReadable(prefix: VSBuffer, readable: VSBufferReadable): VSBufferReadable {
return streams.prefixedReadable(prefix, readable, chunks => VSBuffer.concat(chunks));
}
export function prefixedBufferStream(prefix: VSBuffer, stream: VSBufferReadableStream): VSBufferReadableStream {
return streams.prefixedStream(prefix, stream, chunks => VSBuffer.concat(chunks));
}
/** Decodes base64 to a uint8 array. URL-encoded and unpadded base64 is allowed. */
export function decodeBase64(encoded: string) {
let building = 0;
let remainder = 0;
let bufi = 0;
// The simpler way to do this is `Uint8Array.from(atob(str), c => c.charCodeAt(0))`,
// but that's about 10-20x slower than this function in current Chromium versions.
const buffer = new Uint8Array(Math.floor(encoded.length / 4 * 3));
const append = (value: number) => {
switch (remainder) {
case 3:
buffer[bufi++] = building | value;
remainder = 0;
break;
case 2:
buffer[bufi++] = building | (value >>> 2);
building = value << 6;
remainder = 3;
break;
case 1:
buffer[bufi++] = building | (value >>> 4);
building = value << 4;
remainder = 2;
break;
default:
building = value << 2;
remainder = 1;
}
};
for (let i = 0; i < encoded.length; i++) {
const code = encoded.charCodeAt(i);
// See https://datatracker.ietf.org/doc/html/rfc4648#section-4
// This branchy code is about 3x faster than an indexOf on a base64 char string.
if (code >= 65 && code <= 90) {
append(code - 65); // A-Z starts ranges from char code 65 to 90
} else if (code >= 97 && code <= 122) {
append(code - 97 + 26); // a-z starts ranges from char code 97 to 122, starting at byte 26
} else if (code >= 48 && code <= 57) {
append(code - 48 + 52); // 0-9 starts ranges from char code 48 to 58, starting at byte 52
} else if (code === 43 || code === 45) {
append(62); // "+" or "-" for URLS
} else if (code === 47 || code === 95) {
append(63); // "/" or "_" for URLS
} else if (code === 61) {
break; // "="
} else {
throw new SyntaxError(`Unexpected base64 character ${encoded[i]}`);
}
}
const unpadded = bufi;
while (remainder > 0) {
append(0);
}
// slice is needed to account for overestimation due to padding
return VSBuffer.wrap(buffer).slice(0, unpadded);
}
const base64Alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
const base64UrlSafeAlphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_';
/** Encodes a buffer to a base64 string. */
export function encodeBase64({ buffer }: VSBuffer, padded = true, urlSafe = false) {
const dictionary = urlSafe ? base64UrlSafeAlphabet : base64Alphabet;
let output = '';
const remainder = buffer.byteLength % 3;
let i = 0;
for (; i < buffer.byteLength - remainder; i += 3) {
const a = buffer[i + 0];
const b = buffer[i + 1];
const c = buffer[i + 2];
output += dictionary[a >>> 2];
output += dictionary[(a << 4 | b >>> 4) & 0b111111];
output += dictionary[(b << 2 | c >>> 6) & 0b111111];
output += dictionary[c & 0b111111];
}
if (remainder === 1) {
const a = buffer[i + 0];
output += dictionary[a >>> 2];
output += dictionary[(a << 4) & 0b111111];
if (padded) { output += '=='; }
} else if (remainder === 2) {
const a = buffer[i + 0];
const b = buffer[i + 1];
output += dictionary[a >>> 2];
output += dictionary[(a << 4 | b >>> 4) & 0b111111];
output += dictionary[(b << 2) & 0b111111];
if (padded) { output += '='; }
}
return output;
}
-120
View File
@@ -1,120 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
import { IDisposable } from 'vs/base/common/lifecycle';
export interface CacheResult<T> extends IDisposable {
promise: Promise<T>;
}
export class Cache<T> {
private result: CacheResult<T> | null = null;
constructor(private task: (ct: CancellationToken) => Promise<T>) { }
get(): CacheResult<T> {
if (this.result) {
return this.result;
}
const cts = new CancellationTokenSource();
const promise = this.task(cts.token);
this.result = {
promise,
dispose: () => {
this.result = null;
cts.cancel();
cts.dispose();
}
};
return this.result;
}
}
export function identity<T>(t: T): T {
return t;
}
interface ICacheOptions<TArg> {
/**
* The cache key is used to identify the cache entry.
* Strict equality is used to compare cache keys.
*/
getCacheKey: (arg: TArg) => unknown;
}
/**
* Uses a LRU cache to make a given parametrized function cached.
* Caches just the last key/value.
*/
export class LRUCachedFunction<TArg, TComputed> {
private lastCache: TComputed | undefined = undefined;
private lastArgKey: unknown | undefined = undefined;
private readonly _fn: (arg: TArg) => TComputed;
private readonly _computeKey: (arg: TArg) => unknown;
constructor(fn: (arg: TArg) => TComputed);
constructor(options: ICacheOptions<TArg>, fn: (arg: TArg) => TComputed);
constructor(arg1: ICacheOptions<TArg> | ((arg: TArg) => TComputed), arg2?: (arg: TArg) => TComputed) {
if (typeof arg1 === 'function') {
this._fn = arg1;
this._computeKey = identity;
} else {
this._fn = arg2!;
this._computeKey = arg1.getCacheKey;
}
}
public get(arg: TArg): TComputed {
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.
*/
export class CachedFunction<TArg, TComputed> {
private readonly _map = new Map<TArg, TComputed>();
private readonly _map2 = new Map<unknown, TComputed>();
public get cachedValues(): ReadonlyMap<TArg, TComputed> {
return this._map;
}
private readonly _fn: (arg: TArg) => TComputed;
private readonly _computeKey: (arg: TArg) => unknown;
constructor(fn: (arg: TArg) => TComputed);
constructor(options: ICacheOptions<TArg>, fn: (arg: TArg) => TComputed);
constructor(arg1: ICacheOptions<TArg> | ((arg: TArg) => TComputed), arg2?: (arg: TArg) => TComputed) {
if (typeof arg1 === 'function') {
this._fn = arg1;
this._computeKey = identity;
} else {
this._fn = arg2!;
this._computeKey = arg1.getCacheKey;
}
}
public get(arg: TArg): TComputed {
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;
}
}
-28
View File
@@ -1,28 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ThemeIcon } from 'vs/base/common/themables';
import { isString } from 'vs/base/common/types';
const _codiconFontCharacters: { [id: string]: number } = Object.create(null);
export function register(id: string, fontCharacter: number | string): ThemeIcon {
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.
*/
export function getCodiconFontCharacters(): { [id: string]: number } {
return _codiconFontCharacters;
}
-633
View File
@@ -1,633 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { CharCode } from 'vs/base/common/charCode';
function roundFloat(number: number, decimalPoints: number): number {
const decimal = Math.pow(10, decimalPoints);
return Math.round(number * decimal) / decimal;
}
export class RGBA {
_rgbaBrand: void = undefined;
/**
* Red: integer in [0-255]
*/
readonly r: number;
/**
* Green: integer in [0-255]
*/
readonly g: number;
/**
* Blue: integer in [0-255]
*/
readonly b: number;
/**
* Alpha: float in [0-1]
*/
readonly a: number;
constructor(r: number, g: number, b: number, a: number = 1) {
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: RGBA, b: RGBA): boolean {
return a.r === b.r && a.g === b.g && a.b === b.b && a.a === b.a;
}
}
export class HSLA {
_hslaBrand: void = undefined;
/**
* Hue: integer in [0, 360]
*/
readonly h: number;
/**
* Saturation: float in [0, 1]
*/
readonly s: number;
/**
* Luminosity: float in [0, 1]
*/
readonly l: number;
/**
* Alpha: float in [0, 1]
*/
readonly a: number;
constructor(h: number, s: number, l: number, a: number) {
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: HSLA, b: HSLA): boolean {
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: RGBA): HSLA {
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);
}
private static _hue2rgb(p: number, q: number, t: number): number {
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: HSLA): RGBA {
const h = hsla.h / 360;
const { s, l, a } = hsla;
let r: number, g: number, b: number;
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);
}
}
export class HSVA {
_hsvaBrand: void = undefined;
/**
* Hue: integer in [0, 360]
*/
readonly h: number;
/**
* Saturation: float in [0, 1]
*/
readonly s: number;
/**
* Value: float in [0, 1]
*/
readonly v: number;
/**
* Alpha: float in [0, 1]
*/
readonly a: number;
constructor(h: number, s: number, v: number, a: number) {
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: HSVA, b: HSVA): boolean {
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: RGBA): HSVA {
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: number;
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: HSVA): RGBA {
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);
}
}
export class Color {
static fromHex(hex: string): Color {
return Color.Format.CSS.parseHex(hex) || Color.red;
}
static equals(a: Color | null, b: Color | null): boolean {
if (!a && !b) {
return true;
}
if (!a || !b) {
return false;
}
return a.equals(b);
}
readonly rgba: RGBA;
private _hsla?: HSLA;
get hsla(): HSLA {
if (this._hsla) {
return this._hsla;
} else {
return HSLA.fromRGBA(this.rgba);
}
}
private _hsva?: HSVA;
get hsva(): HSVA {
if (this._hsva) {
return this._hsva;
}
return HSVA.fromRGBA(this.rgba);
}
constructor(arg: RGBA | HSLA | HSVA) {
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: Color | null): boolean {
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(): number {
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);
}
private static _relativeLuminanceForComponent(color: number): number {
const c = color / 255;
return (c <= 0.03928) ? c / 12.92 : Math.pow(((c + 0.055) / 1.055), 2.4);
}
/**
* http://www.w3.org/TR/WCAG20/#contrast-ratiodef
* Returns the contrast ration number in the set [1, 21].
*/
getContrastRatio(another: Color): number {
const lum1 = this.getRelativeLuminance();
const lum2 = another.getRelativeLuminance();
return lum1 > lum2 ? (lum1 + 0.05) / (lum2 + 0.05) : (lum2 + 0.05) / (lum1 + 0.05);
}
/**
* http://24ways.org/2010/calculating-color-contrast
* Return 'true' if darker color otherwise 'false'
*/
isDarker(): boolean {
const yiq = (this.rgba.r * 299 + this.rgba.g * 587 + this.rgba.b * 114) / 1000;
return yiq < 128;
}
/**
* http://24ways.org/2010/calculating-color-contrast
* Return 'true' if lighter color otherwise 'false'
*/
isLighter(): boolean {
const yiq = (this.rgba.r * 299 + this.rgba.g * 587 + this.rgba.b * 114) / 1000;
return yiq >= 128;
}
isLighterThan(another: Color): boolean {
const lum1 = this.getRelativeLuminance();
const lum2 = another.getRelativeLuminance();
return lum1 > lum2;
}
isDarkerThan(another: Color): boolean {
const lum1 = this.getRelativeLuminance();
const lum2 = another.getRelativeLuminance();
return lum1 < lum2;
}
lighten(factor: number): Color {
return new Color(new HSLA(this.hsla.h, this.hsla.s, this.hsla.l + this.hsla.l * factor, this.hsla.a));
}
darken(factor: number): Color {
return new Color(new HSLA(this.hsla.h, this.hsla.s, this.hsla.l - this.hsla.l * factor, this.hsla.a));
}
transparent(factor: number): Color {
const { r, g, b, a } = this.rgba;
return new Color(new RGBA(r, g, b, a * factor));
}
isTransparent(): boolean {
return this.rgba.a === 0;
}
isOpaque(): boolean {
return this.rgba.a === 1;
}
opposite(): Color {
return new Color(new RGBA(255 - this.rgba.r, 255 - this.rgba.g, 255 - this.rgba.b, this.rgba.a));
}
blend(c: Color): Color {
const rgba = c.rgba;
// Convert to 0..1 opacity
const thisA = this.rgba.a;
const colorA = rgba.a;
const a = thisA + colorA * (1 - thisA);
if (a < 1e-6) {
return Color.transparent;
}
const r = this.rgba.r * thisA / a + rgba.r * colorA * (1 - thisA) / a;
const g = this.rgba.g * thisA / a + rgba.g * colorA * (1 - thisA) / a;
const b = this.rgba.b * thisA / a + rgba.b * colorA * (1 - thisA) / a;
return new Color(new RGBA(r, g, b, a));
}
makeOpaque(opaqueBackground: Color): Color {
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
));
}
flatten(...backgrounds: Color[]): Color {
const background = backgrounds.reduceRight((accumulator, color) => {
return Color._flatten(color, accumulator);
});
return Color._flatten(this, background);
}
private static _flatten(foreground: Color, background: Color) {
const backgroundAlpha = 1 - foreground.rgba.a;
return new Color(new RGBA(
backgroundAlpha * background.rgba.r + foreground.rgba.a * foreground.rgba.r,
backgroundAlpha * background.rgba.g + foreground.rgba.a * foreground.rgba.g,
backgroundAlpha * background.rgba.b + foreground.rgba.a * foreground.rgba.b
));
}
private _toString?: string;
toString(): string {
if (!this._toString) {
this._toString = Color.Format.CSS.format(this);
}
return this._toString;
}
static getLighterColor(of: Color, relative: Color, factor?: number): Color {
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: Color, relative: Color, factor?: number): Color {
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 readonly white = new Color(new RGBA(255, 255, 255, 1));
static readonly black = new Color(new RGBA(0, 0, 0, 1));
static readonly red = new Color(new RGBA(255, 0, 0, 1));
static readonly blue = new Color(new RGBA(0, 0, 255, 1));
static readonly green = new Color(new RGBA(0, 255, 0, 1));
static readonly cyan = new Color(new RGBA(0, 255, 255, 1));
static readonly lightgrey = new Color(new RGBA(211, 211, 211, 1));
static readonly transparent = new Color(new RGBA(0, 0, 0, 0));
}
export namespace Color {
export namespace Format {
export namespace CSS {
export function formatRGB(color: Color): string {
if (color.rgba.a === 1) {
return `rgb(${color.rgba.r}, ${color.rgba.g}, ${color.rgba.b})`;
}
return Color.Format.CSS.formatRGBA(color);
}
export function formatRGBA(color: Color): string {
return `rgba(${color.rgba.r}, ${color.rgba.g}, ${color.rgba.b}, ${+(color.rgba.a).toFixed(2)})`;
}
export function formatHSL(color: Color): string {
if (color.hsla.a === 1) {
return `hsl(${color.hsla.h}, ${(color.hsla.s * 100).toFixed(2)}%, ${(color.hsla.l * 100).toFixed(2)}%)`;
}
return Color.Format.CSS.formatHSLA(color);
}
export function formatHSLA(color: Color): string {
return `hsla(${color.hsla.h}, ${(color.hsla.s * 100).toFixed(2)}%, ${(color.hsla.l * 100).toFixed(2)}%, ${color.hsla.a.toFixed(2)})`;
}
function _toTwoDigitHex(n: number): string {
const r = n.toString(16);
return r.length !== 2 ? '0' + r : r;
}
/**
* Formats the color as #RRGGBB
*/
export function formatHex(color: Color): string {
return `#${_toTwoDigitHex(color.rgba.r)}${_toTwoDigitHex(color.rgba.g)}${_toTwoDigitHex(color.rgba.b)}`;
}
/**
* Formats the color as #RRGGBBAA
* If 'compact' is set, colors without transparancy will be printed as #RRGGBB
*/
export function formatHexA(color: Color, compact = false): string {
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))}`;
}
/**
* The default format will use HEX if opaque and RGBA otherwise.
*/
export function format(color: Color): string {
if (color.isOpaque()) {
return Color.Format.CSS.formatHex(color);
}
return Color.Format.CSS.formatRGBA(color);
}
/**
* 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).
*/
export function parseHex(hex: string): Color | null {
const length = hex.length;
if (length === 0) {
// Invalid color
return null;
}
if (hex.charCodeAt(0) !== 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;
}
function _parseHexDigit(charCode: CharCode): number {
switch (charCode) {
case CharCode.Digit0: return 0;
case CharCode.Digit1: return 1;
case CharCode.Digit2: return 2;
case CharCode.Digit3: return 3;
case CharCode.Digit4: return 4;
case CharCode.Digit5: return 5;
case CharCode.Digit6: return 6;
case CharCode.Digit7: return 7;
case CharCode.Digit8: return 8;
case CharCode.Digit9: return 9;
case CharCode.a: return 10;
case CharCode.A: return 10;
case CharCode.b: return 11;
case CharCode.B: return 11;
case CharCode.c: return 12;
case CharCode.C: return 12;
case CharCode.d: return 13;
case CharCode.D: return 13;
case CharCode.e: return 14;
case CharCode.E: return 14;
case CharCode.f: return 15;
case CharCode.F: return 15;
}
return 0;
}
}
}
}
-355
View File
@@ -1,355 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Lazy } from 'vs/base/common/lazy';
import { sep } from 'vs/base/common/path';
// 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: Lazy<{ collator: Intl.Collator; collatorIsNumeric: boolean }> = new Lazy(() => {
const collator = new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' });
return {
collator,
collatorIsNumeric: collator.resolvedOptions().numeric
};
});
// A collator with numeric sorting enabled.
const intlFileNameCollatorNumeric: Lazy<{ collator: Intl.Collator }> = new Lazy(() => {
const collator = new Intl.Collator(undefined, { numeric: true });
return {
collator
};
});
// A collator with numeric sorting enabled, and sensitivity to accents and diacritics but not case.
const intlFileNameCollatorNumericCaseInsensitive: Lazy<{ collator: Intl.Collator }> = new Lazy(() => {
const collator = new Intl.Collator(undefined, { numeric: true, sensitivity: 'accent' });
return {
collator
};
});
/** Compares filenames without distinguishing the name from the extension. Disambiguates by unicode comparison. */
export function compareFileNames(one: string | null, other: string | null, caseSensitive = false): number {
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;
}
/** Compares full filenames without grouping by case. */
export function compareFileNamesDefault(one: string | null, other: string | null): number {
const collatorNumeric = intlFileNameCollatorNumeric.value.collator;
one = one || '';
other = other || '';
return compareAndDisambiguateByLength(collatorNumeric, one, other);
}
/** Compares full filenames grouping uppercase names before lowercase. */
export function compareFileNamesUpper(one: string | null, other: string | null) {
const collatorNumeric = intlFileNameCollatorNumeric.value.collator;
one = one || '';
other = other || '';
return compareCaseUpperFirst(one, other) || compareAndDisambiguateByLength(collatorNumeric, one, other);
}
/** Compares full filenames grouping lowercase names before uppercase. */
export function compareFileNamesLower(one: string | null, other: string | null) {
const collatorNumeric = intlFileNameCollatorNumeric.value.collator;
one = one || '';
other = other || '';
return compareCaseLowerFirst(one, other) || compareAndDisambiguateByLength(collatorNumeric, one, other);
}
/** Compares full filenames by unicode value. */
export function compareFileNamesUnicode(one: string | null, other: string | null) {
one = one || '';
other = other || '';
if (one === other) {
return 0;
}
return one < other ? -1 : 1;
}
/** Compares filenames by extension, then by name. Disambiguates by unicode comparison. */
export function compareFileExtensions(one: string | null, other: string | null): number {
const [oneName, oneExtension] = extractNameAndExtension(one);
const [otherName, otherExtension] = extractNameAndExtension(other);
let result = intlFileNameCollatorBaseNumeric.value.collator.compare(oneExtension, otherExtension);
if (result === 0) {
// Using the numeric option will make compare(`foo1`, `foo01`) === 0. Disambiguate.
if (intlFileNameCollatorBaseNumeric.value.collatorIsNumeric && oneExtension !== otherExtension) {
return oneExtension < otherExtension ? -1 : 1;
}
// Extensions are equal, compare filenames
result = intlFileNameCollatorBaseNumeric.value.collator.compare(oneName, otherName);
if (intlFileNameCollatorBaseNumeric.value.collatorIsNumeric && result === 0 && oneName !== otherName) {
return oneName < otherName ? -1 : 1;
}
}
return result;
}
/** Compares filenames by extension, then by full filename. Mixes uppercase and lowercase names together. */
export function compareFileExtensionsDefault(one: string | null, other: string | null): number {
one = one || '';
other = other || '';
const oneExtension = extractExtension(one);
const otherExtension = extractExtension(other);
const collatorNumeric = intlFileNameCollatorNumeric.value.collator;
const collatorNumericCaseInsensitive = intlFileNameCollatorNumericCaseInsensitive.value.collator;
return compareAndDisambiguateByLength(collatorNumericCaseInsensitive, oneExtension, otherExtension) ||
compareAndDisambiguateByLength(collatorNumeric, one, other);
}
/** Compares filenames by extension, then case, then full filename. Groups uppercase names before lowercase. */
export function compareFileExtensionsUpper(one: string | null, other: string | null): number {
one = one || '';
other = other || '';
const oneExtension = extractExtension(one);
const otherExtension = extractExtension(other);
const collatorNumeric = intlFileNameCollatorNumeric.value.collator;
const collatorNumericCaseInsensitive = intlFileNameCollatorNumericCaseInsensitive.value.collator;
return compareAndDisambiguateByLength(collatorNumericCaseInsensitive, oneExtension, otherExtension) ||
compareCaseUpperFirst(one, other) ||
compareAndDisambiguateByLength(collatorNumeric, one, other);
}
/** Compares filenames by extension, then case, then full filename. Groups lowercase names before uppercase. */
export function compareFileExtensionsLower(one: string | null, other: string | null): number {
one = one || '';
other = other || '';
const oneExtension = extractExtension(one);
const otherExtension = extractExtension(other);
const collatorNumeric = intlFileNameCollatorNumeric.value.collator;
const collatorNumericCaseInsensitive = intlFileNameCollatorNumericCaseInsensitive.value.collator;
return compareAndDisambiguateByLength(collatorNumericCaseInsensitive, oneExtension, otherExtension) ||
compareCaseLowerFirst(one, other) ||
compareAndDisambiguateByLength(collatorNumeric, one, other);
}
/** Compares filenames by case-insensitive extension unicode value, then by full filename unicode value. */
export function compareFileExtensionsUnicode(one: string | null, other: string | null) {
one = one || '';
other = other || '';
const oneExtension = extractExtension(one).toLowerCase();
const otherExtension = extractExtension(other).toLowerCase();
// Check for extension differences
if (oneExtension !== otherExtension) {
return oneExtension < otherExtension ? -1 : 1;
}
// Check for full filename differences.
if (one !== other) {
return one < other ? -1 : 1;
}
return 0;
}
const FileNameMatch = /^(.*?)(\.([^.]*))?$/;
/** Extracts the name and extension from a full filename, with optional special handling for dotfiles */
function extractNameAndExtension(str?: string | null, dotfilesAsNames = false): [string, string] {
const match = str ? FileNameMatch.exec(str) as Array<string> : ([] as Array<string>);
let result: [string, string] = [(match && match[1]) || '', (match && match[3]) || ''];
// if the dotfilesAsNames option is selected, treat an empty filename with an extension
// or a filename that starts with a dot, as a dotfile name
if (dotfilesAsNames && (!result[0] && result[1] || result[0] && result[0].charAt(0) === '.')) {
result = [result[0] + '.' + result[1], ''];
}
return result;
}
/** Extracts the extension from a full filename. Treats dotfiles as names, not extensions. */
function extractExtension(str?: string | null): string {
const match = str ? FileNameMatch.exec(str) as Array<string> : ([] as Array<string>);
return (match && match[1] && match[1].charAt(0) !== '.' && match[3]) || '';
}
function compareAndDisambiguateByLength(collator: Intl.Collator, one: string, other: string) {
// Check for differences
const result = collator.compare(one, other);
if (result !== 0) {
return result;
}
// In a numeric comparison, `foo1` and `foo01` will compare as equivalent.
// Disambiguate by sorting the shorter string first.
if (one.length !== other.length) {
return one.length < other.length ? -1 : 1;
}
return 0;
}
/** @returns `true` if the string is starts with a lowercase letter. Otherwise, `false`. */
function startsWithLower(string: string) {
const character = string.charAt(0);
return (character.toLocaleUpperCase() !== character) ? true : false;
}
/** @returns `true` if the string starts with an uppercase letter. Otherwise, `false`. */
function startsWithUpper(string: string) {
const character = string.charAt(0);
return (character.toLocaleLowerCase() !== character) ? true : false;
}
/**
* Compares the case of the provided strings - lowercase before uppercase
*
* @returns
* ```text
* -1 if one is lowercase and other is uppercase
* 1 if one is uppercase and other is lowercase
* 0 otherwise
* ```
*/
function compareCaseLowerFirst(one: string, other: string): number {
if (startsWithLower(one) && startsWithUpper(other)) {
return -1;
}
return (startsWithUpper(one) && startsWithLower(other)) ? 1 : 0;
}
/**
* Compares the case of the provided strings - uppercase before lowercase
*
* @returns
* ```text
* -1 if one is uppercase and other is lowercase
* 1 if one is lowercase and other is uppercase
* 0 otherwise
* ```
*/
function compareCaseUpperFirst(one: string, other: string): number {
if (startsWithUpper(one) && startsWithLower(other)) {
return -1;
}
return (startsWithLower(one) && startsWithUpper(other)) ? 1 : 0;
}
function comparePathComponents(one: string, other: string, caseSensitive = false): number {
if (!caseSensitive) {
one = one && one.toLowerCase();
other = other && other.toLowerCase();
}
if (one === other) {
return 0;
}
return one < other ? -1 : 1;
}
export function comparePaths(one: string, other: string, caseSensitive = false): number {
const oneParts = one.split(sep);
const otherParts = other.split(sep);
const lastOne = oneParts.length - 1;
const lastOther = otherParts.length - 1;
let endOne: boolean, endOther: boolean;
for (let i = 0; ; i++) {
endOne = lastOne === i;
endOther = lastOther === i;
if (endOne && endOther) {
return compareFileNames(oneParts[i], otherParts[i], caseSensitive);
} else if (endOne) {
return -1;
} else if (endOther) {
return 1;
}
const result = comparePathComponents(oneParts[i], otherParts[i], caseSensitive);
if (result !== 0) {
return result;
}
}
}
export function compareAnything(one: string, other: string, lookFor: string): number {
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);
}
export function compareByPrefix(one: string, other: string, lookFor: string): number {
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;
}
-69
View File
@@ -1,69 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { BugIndicatingError } from 'vs/base/common/errors';
/*
* This file contains helper classes to manage control flow.
*/
/**
* Prevents code from being re-entrant.
*/
export class ReentrancyBarrier {
private _isOccupied = false;
/**
* Calls `runner` if the barrier is not occupied.
* During the call, the barrier becomes occupied.
*/
public runExclusivelyOrSkip(runner: () => void): void {
if (this._isOccupied) {
return;
}
this._isOccupied = true;
try {
runner();
} finally {
this._isOccupied = false;
}
}
/**
* Calls `runner`. If the barrier is occupied, throws an error.
* During the call, the barrier becomes active.
*/
public runExclusivelyOrThrow(runner: () => void): void {
if (this._isOccupied) {
throw new BugIndicatingError(`ReentrancyBarrier: reentrant call detected!`);
}
this._isOccupied = true;
try {
runner();
} finally {
this._isOccupied = false;
}
}
/**
* Indicates if some runner occupies this barrier.
*/
public get isOccupied() {
return this._isOccupied;
}
public makeExclusiveOrSkip<TFunction extends Function>(fn: TFunction): TFunction {
return ((...args: any[]) => {
if (this._isOccupied) {
return;
}
this._isOccupied = true;
try {
return fn(...args);
} finally {
this._isOccupied = false;
}
}) as any;
}
}
-242
View File
@@ -1,242 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { localize } from 'vs/nls';
const minute = 60;
const hour = minute * 60;
const day = hour * 24;
const week = day * 7;
const month = day * 30;
const year = day * 365;
/**
* Create a localized difference of the time between now and the specified date.
* @param date The date to generate the difference from.
* @param appendAgoLabel Whether to append the " ago" to the end.
* @param useFullTimeWords Whether to use full words (eg. seconds) instead of
* shortened (eg. secs).
* @param disallowNow Whether to disallow the string "now" when the difference
* is less than 30 seconds.
*/
export function fromNow(date: number | Date, appendAgoLabel?: boolean, useFullTimeWords?: boolean, disallowNow?: boolean): string {
if (typeof date !== 'number') {
date = date.getTime();
}
const seconds = Math.round((new Date().getTime() - date) / 1000);
if (seconds < -30) {
return localize('date.fromNow.in', 'in {0}', fromNow(new Date().getTime() + seconds * 1000, false));
}
if (!disallowNow && seconds < 30) {
return localize('date.fromNow.now', 'now');
}
let value: number;
if (seconds < minute) {
value = seconds;
if (appendAgoLabel) {
if (value === 1) {
return useFullTimeWords
? localize('date.fromNow.seconds.singular.ago.fullWord', '{0} second ago', value)
: localize('date.fromNow.seconds.singular.ago', '{0} sec ago', value);
} else {
return useFullTimeWords
? localize('date.fromNow.seconds.plural.ago.fullWord', '{0} seconds ago', value)
: localize('date.fromNow.seconds.plural.ago', '{0} secs ago', value);
}
} else {
if (value === 1) {
return useFullTimeWords
? localize('date.fromNow.seconds.singular.fullWord', '{0} second', value)
: localize('date.fromNow.seconds.singular', '{0} sec', value);
} else {
return useFullTimeWords
? localize('date.fromNow.seconds.plural.fullWord', '{0} seconds', value)
: localize('date.fromNow.seconds.plural', '{0} secs', value);
}
}
}
if (seconds < hour) {
value = Math.floor(seconds / minute);
if (appendAgoLabel) {
if (value === 1) {
return useFullTimeWords
? localize('date.fromNow.minutes.singular.ago.fullWord', '{0} minute ago', value)
: localize('date.fromNow.minutes.singular.ago', '{0} min ago', value);
} else {
return useFullTimeWords
? localize('date.fromNow.minutes.plural.ago.fullWord', '{0} minutes ago', value)
: localize('date.fromNow.minutes.plural.ago', '{0} mins ago', value);
}
} else {
if (value === 1) {
return useFullTimeWords
? localize('date.fromNow.minutes.singular.fullWord', '{0} minute', value)
: localize('date.fromNow.minutes.singular', '{0} min', value);
} else {
return useFullTimeWords
? localize('date.fromNow.minutes.plural.fullWord', '{0} minutes', value)
: localize('date.fromNow.minutes.plural', '{0} mins', value);
}
}
}
if (seconds < day) {
value = Math.floor(seconds / hour);
if (appendAgoLabel) {
if (value === 1) {
return useFullTimeWords
? localize('date.fromNow.hours.singular.ago.fullWord', '{0} hour ago', value)
: localize('date.fromNow.hours.singular.ago', '{0} hr ago', value);
} else {
return useFullTimeWords
? localize('date.fromNow.hours.plural.ago.fullWord', '{0} hours ago', value)
: localize('date.fromNow.hours.plural.ago', '{0} hrs ago', value);
}
} else {
if (value === 1) {
return useFullTimeWords
? localize('date.fromNow.hours.singular.fullWord', '{0} hour', value)
: localize('date.fromNow.hours.singular', '{0} hr', value);
} else {
return useFullTimeWords
? localize('date.fromNow.hours.plural.fullWord', '{0} hours', value)
: localize('date.fromNow.hours.plural', '{0} hrs', value);
}
}
}
if (seconds < week) {
value = Math.floor(seconds / day);
if (appendAgoLabel) {
return value === 1
? localize('date.fromNow.days.singular.ago', '{0} day ago', value)
: localize('date.fromNow.days.plural.ago', '{0} days ago', value);
} else {
return value === 1
? localize('date.fromNow.days.singular', '{0} day', value)
: localize('date.fromNow.days.plural', '{0} days', value);
}
}
if (seconds < month) {
value = Math.floor(seconds / week);
if (appendAgoLabel) {
if (value === 1) {
return useFullTimeWords
? localize('date.fromNow.weeks.singular.ago.fullWord', '{0} week ago', value)
: localize('date.fromNow.weeks.singular.ago', '{0} wk ago', value);
} else {
return useFullTimeWords
? localize('date.fromNow.weeks.plural.ago.fullWord', '{0} weeks ago', value)
: localize('date.fromNow.weeks.plural.ago', '{0} wks ago', value);
}
} else {
if (value === 1) {
return useFullTimeWords
? localize('date.fromNow.weeks.singular.fullWord', '{0} week', value)
: localize('date.fromNow.weeks.singular', '{0} wk', value);
} else {
return useFullTimeWords
? localize('date.fromNow.weeks.plural.fullWord', '{0} weeks', value)
: localize('date.fromNow.weeks.plural', '{0} wks', value);
}
}
}
if (seconds < year) {
value = Math.floor(seconds / month);
if (appendAgoLabel) {
if (value === 1) {
return useFullTimeWords
? localize('date.fromNow.months.singular.ago.fullWord', '{0} month ago', value)
: localize('date.fromNow.months.singular.ago', '{0} mo ago', value);
} else {
return useFullTimeWords
? localize('date.fromNow.months.plural.ago.fullWord', '{0} months ago', value)
: localize('date.fromNow.months.plural.ago', '{0} mos ago', value);
}
} else {
if (value === 1) {
return useFullTimeWords
? localize('date.fromNow.months.singular.fullWord', '{0} month', value)
: localize('date.fromNow.months.singular', '{0} mo', value);
} else {
return useFullTimeWords
? localize('date.fromNow.months.plural.fullWord', '{0} months', value)
: localize('date.fromNow.months.plural', '{0} mos', value);
}
}
}
value = Math.floor(seconds / year);
if (appendAgoLabel) {
if (value === 1) {
return useFullTimeWords
? localize('date.fromNow.years.singular.ago.fullWord', '{0} year ago', value)
: localize('date.fromNow.years.singular.ago', '{0} yr ago', value);
} else {
return useFullTimeWords
? localize('date.fromNow.years.plural.ago.fullWord', '{0} years ago', value)
: localize('date.fromNow.years.plural.ago', '{0} yrs ago', value);
}
} else {
if (value === 1) {
return useFullTimeWords
? localize('date.fromNow.years.singular.fullWord', '{0} year', value)
: localize('date.fromNow.years.singular', '{0} yr', value);
} else {
return useFullTimeWords
? localize('date.fromNow.years.plural.fullWord', '{0} years', value)
: localize('date.fromNow.years.plural', '{0} yrs', value);
}
}
}
/**
* Gets a readable duration with intelligent/lossy precision. For example "40ms" or "3.040s")
* @param ms The duration to get in milliseconds.
* @param useFullTimeWords Whether to use full words (eg. seconds) instead of
* shortened (eg. secs).
*/
export function getDurationString(ms: number, useFullTimeWords?: boolean) {
const seconds = Math.abs(ms / 1000);
if (seconds < 1) {
return useFullTimeWords
? localize('duration.ms.full', '{0} milliseconds', ms)
: localize('duration.ms', '{0}ms', ms);
}
if (seconds < minute) {
return useFullTimeWords
? localize('duration.s.full', '{0} seconds', Math.round(ms) / 1000)
: localize('duration.s', '{0}s', Math.round(ms) / 1000);
}
if (seconds < hour) {
return useFullTimeWords
? localize('duration.m.full', '{0} minutes', Math.round(ms / (1000 * minute)))
: localize('duration.m', '{0} mins', Math.round(ms / (1000 * minute)));
}
if (seconds < day) {
return useFullTimeWords
? localize('duration.h.full', '{0} hours', Math.round(ms / (1000 * hour)))
: localize('duration.h', '{0} hrs', Math.round(ms / (1000 * hour)));
}
return localize('duration.d', '{0} days', Math.round(ms / (1000 * day)));
}
export function toLocalISOString(date: Date): string {
return date.getFullYear() +
'-' + String(date.getMonth() + 1).padStart(2, '0') +
'-' + String(date.getDate()).padStart(2, '0') +
'T' + String(date.getHours()).padStart(2, '0') +
':' + String(date.getMinutes()).padStart(2, '0') +
':' + String(date.getSeconds()).padStart(2, '0') +
'.' + (date.getMilliseconds() / 1000).toFixed(3).slice(2, 5) +
'Z';
}
@@ -1,101 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { env } from 'vs/base/common/process';
// Define the enumeration for Desktop Environments
enum DesktopEnvironment {
UNKNOWN = 'UNKNOWN',
CINNAMON = 'CINNAMON',
DEEPIN = 'DEEPIN',
GNOME = 'GNOME',
KDE3 = 'KDE3',
KDE4 = 'KDE4',
KDE5 = 'KDE5',
KDE6 = 'KDE6',
PANTHEON = 'PANTHEON',
UNITY = 'UNITY',
XFCE = 'XFCE',
UKUI = 'UKUI',
LXQT = 'LXQT',
}
const kXdgCurrentDesktopEnvVar = 'XDG_CURRENT_DESKTOP';
const kKDESessionEnvVar = 'KDE_SESSION_VERSION';
export function getDesktopEnvironment(): DesktopEnvironment {
const xdgCurrentDesktop = env[kXdgCurrentDesktopEnvVar];
if (xdgCurrentDesktop) {
const values = xdgCurrentDesktop.split(':').map(value => value.trim()).filter(value => value.length > 0);
for (const value of values) {
switch (value) {
case 'Unity': {
const desktopSessionUnity = env['DESKTOP_SESSION'];
if (desktopSessionUnity && desktopSessionUnity.includes('gnome-fallback')) {
return DesktopEnvironment.GNOME;
}
return DesktopEnvironment.UNITY;
}
case 'Deepin':
return DesktopEnvironment.DEEPIN;
case 'GNOME':
return DesktopEnvironment.GNOME;
case 'X-Cinnamon':
return DesktopEnvironment.CINNAMON;
case 'KDE': {
const kdeSession = env[kKDESessionEnvVar];
if (kdeSession === '5') { return DesktopEnvironment.KDE5; }
if (kdeSession === '6') { return DesktopEnvironment.KDE6; }
return DesktopEnvironment.KDE4;
}
case 'Pantheon':
return DesktopEnvironment.PANTHEON;
case 'XFCE':
return DesktopEnvironment.XFCE;
case 'UKUI':
return DesktopEnvironment.UKUI;
case 'LXQt':
return DesktopEnvironment.LXQT;
}
}
}
const desktopSession = env['DESKTOP_SESSION'];
if (desktopSession) {
switch (desktopSession) {
case 'deepin':
return DesktopEnvironment.DEEPIN;
case 'gnome':
case 'mate':
return DesktopEnvironment.GNOME;
case 'kde4':
case 'kde-plasma':
return DesktopEnvironment.KDE4;
case 'kde':
if (kKDESessionEnvVar in env) {
return DesktopEnvironment.KDE4;
}
return DesktopEnvironment.KDE3;
case 'xfce':
case 'xubuntu':
return DesktopEnvironment.XFCE;
case 'ukui':
return DesktopEnvironment.UKUI;
}
}
if ('GNOME_DESKTOP_SESSION_ID' in env) {
return DesktopEnvironment.GNOME;
}
if ('KDE_FULL_SESSION' in env) {
if (kKDESessionEnvVar in env) {
return DesktopEnvironment.KDE4;
}
return DesktopEnvironment.KDE3;
}
return DesktopEnvironment.UNKNOWN;
}
-113
View File
@@ -1,113 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as arrays from 'vs/base/common/arrays';
import * as types from 'vs/base/common/types';
import * as nls from 'vs/nls';
import { IAction } from 'vs/base/common/actions';
function exceptionToErrorMessage(exception: any, verbose: boolean): string {
if (verbose && (exception.stack || exception.stacktrace)) {
return nls.localize('stackTrace.format', "{0}: {1}", detectSystemErrorMessage(exception), stackToString(exception.stack) || stackToString(exception.stacktrace));
}
return detectSystemErrorMessage(exception);
}
function stackToString(stack: string[] | string | undefined): string | undefined {
if (Array.isArray(stack)) {
return stack.join('\n');
}
return stack;
}
function detectSystemErrorMessage(exception: any): string {
// 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 nls.localize('nodeExceptionMessage', "A system error occurred ({0})", exception.message);
}
return exception.message || nls.localize('error.defaultMessage', "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.
*/
export function toErrorMessage(error: any = null, verbose: boolean = false): string {
if (!error) {
return nls.localize('error.defaultMessage', "An unknown error occurred. Please consult the log for more details.");
}
if (Array.isArray(error)) {
const errors: any[] = arrays.coalesce(error);
const msg = toErrorMessage(errors[0], verbose);
if (errors.length > 1) {
return nls.localize('error.moreErrors', "{0} ({1} errors in total)", msg, errors.length);
}
return msg;
}
if (types.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 nls.localize('error.defaultMessage', "An unknown error occurred. Please consult the log for more details.");
}
export interface IErrorWithActions extends Error {
actions: IAction[];
}
export function isErrorWithActions(obj: unknown): obj is IErrorWithActions {
const candidate = obj as IErrorWithActions | undefined;
return candidate instanceof Error && Array.isArray(candidate.actions);
}
export function createErrorWithActions(messageOrError: string | Error, actions: IAction[]): IErrorWithActions {
let error: IErrorWithActions;
if (typeof messageOrError === 'string') {
error = new Error(messageOrError) as IErrorWithActions;
} else {
error = messageOrError as IErrorWithActions;
}
error.actions = actions;
return error;
}
-423
View File
@@ -1,423 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { CharCode } from 'vs/base/common/charCode';
import { isAbsolute, join, normalize, posix, sep } from 'vs/base/common/path';
import { isWindows } from 'vs/base/common/platform';
import { equalsIgnoreCase, rtrim, startsWithIgnoreCase } from 'vs/base/common/strings';
import { isNumber } from 'vs/base/common/types';
export function isPathSeparator(code: number) {
return code === CharCode.Slash || code === 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.
*/
export function toSlashes(osPath: string) {
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.
*/
export function toPosixPath(osPath: string) {
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\`
*/
export function getRoot(path: string, sep: string = posix.sep): string {
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) === 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 '';
}
/**
* Check if the path follows this pattern: `\\hostname\sharename`.
*
* @see https://msdn.microsoft.com/en-us/library/gg465305.aspx
* @return A boolean indication if the path is a UNC path, on none-windows
* always false.
*/
export function isUNC(path: string): boolean {
if (!isWindows) {
// UNC is a windows concept
return false;
}
if (!path || path.length < 5) {
// at least \\a\b
return false;
}
let code = path.charCodeAt(0);
if (code !== CharCode.Backslash) {
return false;
}
code = path.charCodeAt(1);
if (code !== CharCode.Backslash) {
return false;
}
let pos = 2;
const start = pos;
for (; pos < path.length; pos++) {
code = path.charCodeAt(pos);
if (code === CharCode.Backslash) {
break;
}
}
if (start === pos) {
return false;
}
code = path.charCodeAt(pos + 1);
if (isNaN(code) || code === CharCode.Backslash) {
return false;
}
return true;
}
// Reference: https://en.wikipedia.org/wiki/Filename
const WINDOWS_INVALID_FILE_CHARS = /[\\/:\*\?"<>\|]/g;
const UNIX_INVALID_FILE_CHARS = /[/]/g;
const WINDOWS_FORBIDDEN_NAMES = /^(con|prn|aux|clock\$|nul|lpt[0-9]|com[0-9])(\.(.*?))?$/i;
export function isValidBasename(name: string | null | undefined, isWindowsOS: boolean = isWindows): boolean {
const invalidFileChars = isWindowsOS ? WINDOWS_INVALID_FILE_CHARS : UNIX_INVALID_FILE_CHARS;
if (!name || name.length === 0 || /^\s+$/.test(name)) {
return false; // require a name that is not just whitespace
}
invalidFileChars.lastIndex = 0; // the holy grail of software development
if (invalidFileChars.test(name)) {
return false; // check for certain invalid file characters
}
if (isWindowsOS && WINDOWS_FORBIDDEN_NAMES.test(name)) {
return false; // check for certain invalid file names
}
if (name === '.' || name === '..') {
return false; // check for reserved values
}
if (isWindowsOS && name[name.length - 1] === '.') {
return false; // Windows: file cannot end with a "."
}
if (isWindowsOS && name.length !== name.trim().length) {
return false; // Windows: file cannot end with a whitespace
}
if (name.length > 255) {
return false; // most file systems do not allow files > 255 length
}
return true;
}
/**
* @deprecated please use `IUriIdentityService.extUri.isEqual` 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.
*/
export function isEqual(pathA: string, pathB: string, ignoreCase?: boolean): boolean {
const identityEquals = (pathA === pathB);
if (!ignoreCase || identityEquals) {
return identityEquals;
}
if (!pathA || !pathB) {
return false;
}
return equalsIgnoreCase(pathA, pathB);
}
/**
* @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.
*/
export function isEqualOrParent(base: string, parentCandidate: string, ignoreCase?: boolean, separator = sep): boolean {
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;
}
export function isWindowsDriveLetter(char0: number): boolean {
return char0 >= CharCode.A && char0 <= CharCode.Z || char0 >= CharCode.a && char0 <= CharCode.z;
}
export function sanitizeFilePath(candidate: string, cwd: string): string {
// Special case: allow to open a drive letter without trailing backslash
if (isWindows && candidate.endsWith(':')) {
candidate += sep;
}
// Ensure absolute
if (!isAbsolute(candidate)) {
candidate = join(cwd, candidate);
}
// Ensure normalized
candidate = normalize(candidate);
// Ensure no trailing slash/backslash
return removeTrailingPathSeparator(candidate);
}
export function removeTrailingPathSeparator(candidate: string): string {
if (isWindows) {
candidate = rtrim(candidate, sep);
// Special case: allow to open drive root ('C:\')
if (candidate.endsWith(':')) {
candidate += sep;
}
} else {
candidate = rtrim(candidate, sep);
// Special case: allow to open root ('/')
if (!candidate) {
candidate = sep;
}
}
return candidate;
}
export function isRootOrDriveLetter(path: string): boolean {
const pathNormalized = normalize(path);
if (isWindows) {
if (path.length > 3) {
return false;
}
return hasDriveLetter(pathNormalized) &&
(path.length === 2 || pathNormalized.charCodeAt(2) === CharCode.Backslash);
}
return pathNormalized === posix.sep;
}
export function hasDriveLetter(path: string, isWindowsOS: boolean = isWindows): boolean {
if (isWindowsOS) {
return isWindowsDriveLetter(path.charCodeAt(0)) && path.charCodeAt(1) === CharCode.Colon;
}
return false;
}
export function getDriveLetter(path: string, isWindowsOS: boolean = isWindows): string | undefined {
return hasDriveLetter(path, isWindowsOS) ? path[0] : undefined;
}
export function indexOfPath(path: string, candidate: string, ignoreCase?: boolean): number {
if (candidate.length > path.length) {
return -1;
}
if (path === candidate) {
return 0;
}
if (ignoreCase) {
path = path.toLowerCase();
candidate = candidate.toLowerCase();
}
return path.indexOf(candidate);
}
export interface IPathWithLineAndColumn {
path: string;
line?: number;
column?: number;
}
export function parseLineAndColumnAware(rawPath: string): IPathWithLineAndColumn {
const segments = rawPath.split(':'); // C:\file.txt:<line>:<column>
let path: string | undefined = undefined;
let line: number | undefined = undefined;
let column: number | undefined = undefined;
for (const segment of segments) {
const segmentAsNumber = Number(segment);
if (!isNumber(segmentAsNumber)) {
path = !!path ? [path, segment].join(':') : segment; // a colon can well be part of a path (e.g. C:\...)
} else if (line === undefined) {
line = segmentAsNumber;
} else if (column === undefined) {
column = segmentAsNumber;
}
}
if (!path) {
throw new Error('Format for `--goto` should be: `FILE:LINE(:COLUMN)`');
}
return {
path,
line: line !== undefined ? line : undefined,
column: column !== undefined ? column : line !== undefined ? 1 : undefined // if we have a line, make sure column is also set
};
}
const pathChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
const windowsSafePathFirstChars = 'BDEFGHIJKMOQRSTUVWXYZbdefghijkmoqrstuvwxyz0123456789';
export function randomPath(parent?: string, prefix?: string, randomLength = 8): string {
let suffix = '';
for (let i = 0; i < randomLength; i++) {
let pathCharsTouse: string;
if (i === 0 && isWindows && !prefix && (randomLength === 3 || randomLength === 4)) {
// Windows has certain reserved file names that cannot be used, such
// as AUX, CON, PRN, etc. We want to avoid generating a random name
// that matches that pattern, so we use a different set of characters
// for the first character of the name that does not include any of
// the reserved names first characters.
pathCharsTouse = windowsSafePathFirstChars;
} else {
pathCharsTouse = pathChars;
}
suffix += pathCharsTouse.charAt(Math.floor(Math.random() * pathCharsTouse.length));
}
let randomFileName: string;
if (prefix) {
randomFileName = `${prefix}-${suffix}`;
} else {
randomFileName = suffix;
}
if (parent) {
return join(parent, randomFileName);
}
return randomFileName;
}
-737
View File
@@ -1,737 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { equals } from 'vs/base/common/arrays';
import { isThenable } from 'vs/base/common/async';
import { CharCode } from 'vs/base/common/charCode';
import { isEqualOrParent } from 'vs/base/common/extpath';
import { basename, extname, posix, sep } from 'vs/base/common/path';
import { isLinux } from 'vs/base/common/platform';
import { escapeRegExpCharacters, ltrim } from 'vs/base/common/strings';
export interface IRelativePattern {
/**
* A base file path to which this pattern will be matched against relatively.
*/
readonly base: string;
/**
* A file glob pattern like `*.{ts,js}` that will be matched on file paths
* relative to the base path.
*
* Example: Given a base of `/home/work/folder` and a file path of `/home/work/folder/index.js`,
* the file glob pattern will match on `index.js`.
*/
readonly pattern: string;
}
export interface IExpression {
[pattern: string]: boolean | SiblingClause;
}
export function getEmptyExpression(): IExpression {
return Object.create(null);
}
interface SiblingClause {
when: string;
}
export const GLOBSTAR = '**';
export 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: number, isLastPattern?: boolean): string {
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}+` : ''})*?`;
}
}
export function splitGlobAware(pattern: string, splitChar: string): string[] {
if (!pattern) {
return [];
}
const segments: string[] = [];
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: string): string {
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: string;
// 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
export type ParsedPattern = (path: string, basename?: string) => boolean;
// The `ParsedExpression` returns a `Promise`
// iff `hasSibling` returns a `Promise`.
export type ParsedExpression = (path: string, basename?: string, hasSibling?: (name: string) => boolean | Promise<boolean>) => string | null | Promise<string | null> /* the matching pattern */;
interface IGlobOptions {
/**
* Simplify patterns for use as exclusion filters during
* tree traversal to skip entire subtrees. Cannot be used
* outside of a tree traversal.
*/
trimForExclusions?: boolean;
}
interface ParsedStringPattern {
(path: string, basename?: string): string | null | Promise<string | null> /* the matching pattern */;
basenames?: string[];
patterns?: string[];
allBasenames?: string[];
allPaths?: string[];
}
interface ParsedExpressionPattern {
(path: string, basename?: string, name?: string, hasSibling?: (name: string) => boolean | Promise<boolean>): string | null | Promise<string | null> /* the matching pattern */;
requiresSiblings?: boolean;
allBasenames?: string[];
allPaths?: string[];
}
const FALSE = function () {
return false;
};
const NULL = function (): string | null {
return null;
};
function trimForExclusions(pattern: string, options: IGlobOptions): string {
return options.trimForExclusions && pattern.endsWith('/**') ? pattern.substr(0, pattern.length - 2) : pattern; // dropping **, tailing / is dropped later
}
// common pattern: **/*.txt just need endsWith check
function trivia1(base: string, pattern: string): ParsedStringPattern {
return function (path: string, basename?: string) {
return typeof path === 'string' && path.endsWith(base) ? pattern : null;
};
}
// common pattern: **/some.txt just need basename check
function trivia2(base: string, pattern: string): ParsedStringPattern {
const slashBase = `/${base}`;
const backslashBase = `\\${base}`;
const parsedPattern: ParsedStringPattern = function (path: string, basename?: string) {
if (typeof path !== 'string') {
return null;
}
if (basename) {
return basename === base ? pattern : null;
}
return path === base || path.endsWith(slashBase) || path.endsWith(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: string, options: IGlobOptions): ParsedStringPattern {
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: ParsedStringPattern = function (path: string, basename?: string) {
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, [] as string[]);
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: string, pattern: string, matchPathEnds: boolean): ParsedStringPattern {
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: ParsedStringPattern;
if (matchPathEnds) {
parsedPattern = function (path: string, basename?: string) {
return typeof path === 'string' && ((path === nativePath || path.endsWith(nativePathEnd)) || !usingPosixSep && (path === targetPath || path.endsWith(targetPathEnd))) ? pattern : null;
};
} else {
parsedPattern = function (path: string, basename?: string) {
return typeof path === 'string' && (path === nativePath || (!usingPosixSep && path === targetPath)) ? pattern : null;
};
}
parsedPattern.allPaths = [(matchPathEnds ? '*/' : './') + targetPath];
return parsedPattern;
}
function toRegExp(pattern: string): ParsedStringPattern {
try {
const regExp = new RegExp(`^${parseRegExp(pattern)}$`);
return function (path: string) {
regExp.lastIndex = 0; // reset RegExp to its initial state to reuse it!
return typeof path === 'string' && regExp.test(path) ? pattern : null;
};
} catch (error) {
return NULL;
}
}
/**
* Simplified glob matching. Supports a subset of glob patterns:
* * `*` to match zero or more characters in a path segment
* * `?` to match on one character in a path segment
* * `**` to match any number of path segments, including none
* * `{}` to group conditions (e.g. *.{ts,js} matches all TypeScript and JavaScript files)
* * `[]` to declare a range of characters to match in a path segment (e.g., `example.[0-9]` to match on `example.0`, `example.1`, …)
* * `[!...]` to negate a range of characters to match in a path segment (e.g., `example.[!0-9]` to match on `example.a`, `example.b`, but not `example.0`)
*/
export function match(pattern: string | IRelativePattern, path: string): boolean;
export function match(expression: IExpression, path: string, hasSibling?: (name: string) => boolean): string /* the matching pattern */;
export function match(arg1: string | IExpression | IRelativePattern, path: string, hasSibling?: (name: string) => boolean): boolean | string | null | Promise<string | null> {
if (!arg1 || typeof path !== 'string') {
return false;
}
return parse(arg1)(path, undefined, hasSibling);
}
/**
* Simplified glob matching. Supports a subset of glob patterns:
* * `*` to match zero or more characters in a path segment
* * `?` to match on one character in a path segment
* * `**` to match any number of path segments, including none
* * `{}` to group conditions (e.g. *.{ts,js} matches all TypeScript and JavaScript files)
* * `[]` to declare a range of characters to match in a path segment (e.g., `example.[0-9]` to match on `example.0`, `example.1`, …)
* * `[!...]` to negate a range of characters to match in a path segment (e.g., `example.[!0-9]` to match on `example.a`, `example.b`, but not `example.0`)
*/
export function parse(pattern: string | IRelativePattern, options?: IGlobOptions): ParsedPattern;
export function parse(expression: IExpression, options?: IGlobOptions): ParsedExpression;
export function parse(arg1: string | IExpression | IRelativePattern, options?: IGlobOptions): ParsedPattern | ParsedExpression;
export function parse(arg1: string | IExpression | IRelativePattern, options: IGlobOptions = {}): ParsedPattern | ParsedExpression {
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: ParsedPattern & { allBasenames?: string[]; allPaths?: string[] } = function (path: string, basename?: string) {
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(<IExpression>arg1, options);
}
export function isRelativePattern(obj: unknown): obj is IRelativePattern {
const rp = obj as IRelativePattern | undefined | null;
if (!rp) {
return false;
}
return typeof rp.base === 'string' && typeof rp.pattern === 'string';
}
export function getBasenameTerms(patternOrExpression: ParsedPattern | ParsedExpression): string[] {
return (<ParsedStringPattern>patternOrExpression).allBasenames || [];
}
export function getPathTerms(patternOrExpression: ParsedPattern | ParsedExpression): string[] {
return (<ParsedStringPattern>patternOrExpression).allPaths || [];
}
function parsedExpression(expression: IExpression, options: IGlobOptions): ParsedExpression {
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 => !!(<ParsedExpressionPattern>parsedPattern).requiresSiblings)) {
if (patternsLength === 1) {
return parsedPatterns[0] as ParsedStringPattern;
}
const resultExpression: ParsedStringPattern = function (path: string, basename?: string) {
let resultPromises: Promise<string | null>[] | undefined = 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, [] as string[]);
if (allPaths.length) {
resultExpression.allPaths = allPaths;
}
return resultExpression;
}
const resultExpression: ParsedStringPattern = function (path: string, base?: string, hasSibling?: (name: string) => boolean | Promise<boolean>) {
let name: string | undefined = undefined;
let resultPromises: Promise<string | null>[] | undefined = undefined;
for (let i = 0, n = parsedPatterns.length; i < n; i++) {
// Pattern matches path
const parsedPattern = (<ParsedExpressionPattern>parsedPatterns[i]);
if (parsedPattern.requiresSiblings && hasSibling) {
if (!base) {
base = basename(path);
}
if (!name) {
name = base.substr(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, [] as string[]);
if (allPaths.length) {
resultExpression.allPaths = allPaths;
}
return resultExpression;
}
function parseExpressionPattern(pattern: string, value: boolean | SiblingClause, options: IGlobOptions): (ParsedStringPattern | ParsedExpressionPattern) {
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: ParsedExpressionPattern = (path: string, basename?: string, name?: string, hasSibling?: (name: string) => boolean | Promise<boolean>) => {
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: Array<ParsedStringPattern | ParsedExpressionPattern>, result?: string): Array<ParsedStringPattern | ParsedExpressionPattern> {
const basenamePatterns = parsedPatterns.filter(parsedPattern => !!(<ParsedStringPattern>parsedPattern).basenames);
if (basenamePatterns.length < 2) {
return parsedPatterns;
}
const basenames = basenamePatterns.reduce<string[]>((all, current) => {
const basenames = (<ParsedStringPattern>current).basenames;
return basenames ? all.concat(basenames) : all;
}, [] as string[]);
let patterns: string[];
if (result) {
patterns = [];
for (let i = 0, n = basenames.length; i < n; i++) {
patterns.push(result);
}
} else {
patterns = basenamePatterns.reduce((all, current) => {
const patterns = (<ParsedStringPattern>current).patterns;
return patterns ? all.concat(patterns) : all;
}, [] as string[]);
}
const aggregate: ParsedStringPattern = function (path: string, basename?: string) {
if (typeof path !== 'string') {
return null;
}
if (!basename) {
let i: number;
for (i = path.length; i > 0; i--) {
const ch = path.charCodeAt(i - 1);
if (ch === CharCode.Slash || ch === CharCode.Backslash) {
break;
}
}
basename = path.substr(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 => !(<ParsedStringPattern>parsedPattern).basenames);
aggregatedPatterns.push(aggregate);
return aggregatedPatterns;
}
export function patternsEquals(patternsA: Array<string | IRelativePattern> | undefined, patternsB: Array<string | IRelativePattern> | undefined): boolean {
return equals(patternsA, patternsB, (a, b) => {
if (typeof a === 'string' && typeof b === 'string') {
return a === b;
}
if (typeof a !== 'string' && typeof b !== 'string') {
return a.base === b.base && a.pattern === b.pattern;
}
return false;
});
}
-31
View File
@@ -1,31 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
export class HierarchicalKind {
public static readonly sep = '.';
public static readonly None = new HierarchicalKind('@@none@@'); // Special kind that matches nothing
public static readonly Empty = new HierarchicalKind('');
constructor(
public readonly value: string
) { }
public equals(other: HierarchicalKind): boolean {
return this.value === other.value;
}
public contains(other: HierarchicalKind): boolean {
return this.equals(other) || this.value === '' || other.value.startsWith(this.value + HierarchicalKind.sep);
}
public intersects(other: HierarchicalKind): boolean {
return this.contains(other) || other.contains(this);
}
public append(...parts: string[]): HierarchicalKind {
return new HierarchicalKind((this.value ? [this.value, ...parts] : parts).join(HierarchicalKind.sep));
}
}
-277
View File
@@ -1,277 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { SetWithKey } from 'vs/base/common/collections';
import { ArrayNavigator, INavigator } from 'vs/base/common/navigator';
export class HistoryNavigator<T> implements INavigator<T> {
private _history!: Set<T>;
private _limit: number;
private _navigator!: ArrayNavigator<T>;
constructor(history: readonly T[] = [], limit: number = 10) {
this._initialize(history);
this._limit = limit;
this._onChange();
}
public getHistory(): T[] {
return this._elements;
}
public add(t: T) {
this._history.delete(t);
this._history.add(t);
this._onChange();
}
public next(): T | null {
// This will navigate past the end of the last element, and in that case the input should be cleared
return this._navigator.next();
}
public previous(): T | null {
if (this._currentPosition() !== 0) {
return this._navigator.previous();
}
return null;
}
public current(): T | null {
return this._navigator.current();
}
public first(): T | null {
return this._navigator.first();
}
public last(): T | null {
return this._navigator.last();
}
public isFirst(): boolean {
return this._currentPosition() === 0;
}
public isLast(): boolean {
return this._currentPosition() >= this._elements.length - 1;
}
public isNowhere(): boolean {
return this._navigator.current() === null;
}
public has(t: T): boolean {
return this._history.has(t);
}
public clear(): void {
this._initialize([]);
this._onChange();
}
private _onChange() {
this._reduceToLimit();
const elements = this._elements;
this._navigator = new ArrayNavigator(elements, 0, elements.length, elements.length);
}
private _reduceToLimit() {
const data = this._elements;
if (data.length > this._limit) {
this._initialize(data.slice(data.length - this._limit));
}
}
private _currentPosition(): number {
const currentElement = this._navigator.current();
if (!currentElement) {
return -1;
}
return this._elements.indexOf(currentElement);
}
private _initialize(history: readonly T[]): void {
this._history = new Set();
for (const entry of history) {
this._history.add(entry);
}
}
private get _elements(): T[] {
const elements: T[] = [];
this._history.forEach(e => elements.push(e));
return elements;
}
}
interface HistoryNode<T> {
value: T;
previous: HistoryNode<T> | undefined;
next: HistoryNode<T> | undefined;
}
/**
* The right way to use HistoryNavigator2 is for the last item in the list to be the user's uncommitted current text. eg empty string, or whatever has been typed. Then
* the user can navigate away from the last item through the list, and back to it. When updating the last item, call replaceLast.
*/
export class HistoryNavigator2<T> {
private valueSet: Set<T>;
private head: HistoryNode<T>;
private tail: HistoryNode<T>;
private cursor: HistoryNode<T>;
private _size: number;
get size(): number { return this._size; }
constructor(history: readonly T[], private capacity: number = 10, private identityFn: (t: T) => any = t => t) {
if (history.length < 1) {
throw new Error('not supported');
}
this._size = 1;
this.head = this.tail = this.cursor = {
value: history[0],
previous: undefined,
next: undefined
};
this.valueSet = new SetWithKey<T>([history[0]], identityFn);
for (let i = 1; i < history.length; i++) {
this.add(history[i]);
}
}
add(value: T): void {
const node: HistoryNode<T> = {
value,
previous: this.tail,
next: undefined
};
this.tail.next = node;
this.tail = node;
this.cursor = this.tail;
this._size++;
if (this.valueSet.has(value)) {
this._deleteFromList(value);
} else {
this.valueSet.add(value);
}
while (this._size > this.capacity) {
this.valueSet.delete(this.head.value);
this.head = this.head.next!;
this.head.previous = undefined;
this._size--;
}
}
/**
* @returns old last value
*/
replaceLast(value: T): T {
if (this.identityFn(this.tail.value) === this.identityFn(value)) {
return value;
}
const oldValue = this.tail.value;
this.valueSet.delete(oldValue);
this.tail.value = value;
if (this.valueSet.has(value)) {
this._deleteFromList(value);
} else {
this.valueSet.add(value);
}
return oldValue;
}
prepend(value: T): void {
if (this._size === this.capacity || this.valueSet.has(value)) {
return;
}
const node: HistoryNode<T> = {
value,
previous: undefined,
next: this.head
};
this.head.previous = node;
this.head = node;
this._size++;
this.valueSet.add(value);
}
isAtEnd(): boolean {
return this.cursor === this.tail;
}
current(): T {
return this.cursor.value;
}
previous(): T {
if (this.cursor.previous) {
this.cursor = this.cursor.previous;
}
return this.cursor.value;
}
next(): T {
if (this.cursor.next) {
this.cursor = this.cursor.next;
}
return this.cursor.value;
}
has(t: T): boolean {
return this.valueSet.has(t);
}
resetCursor(): T {
this.cursor = this.tail;
return this.cursor.value;
}
*[Symbol.iterator](): Iterator<T> {
let node: HistoryNode<T> | undefined = this.head;
while (node) {
yield node.value;
node = node.next;
}
}
private _deleteFromList(value: T): void {
let temp = this.head;
const valueKey = this.identityFn(value);
while (temp !== this.tail) {
if (this.identityFn(temp.value) === valueKey) {
if (temp === this.head) {
this.head = this.head.next!;
this.head.previous = undefined;
} else {
temp.previous!.next = temp.next;
temp.next!.previous = temp.previous;
}
this._size--;
}
temp = temp.next!;
}
}
}
-112
View File
@@ -1,112 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IDisposable } from 'vs/base/common/lifecycle';
import { env } from 'vs/base/common/process';
export function isHotReloadEnabled(): boolean {
return env && !!env['VSCODE_DEV'];
}
export function registerHotReloadHandler(handler: HotReloadHandler): IDisposable {
if (!isHotReloadEnabled()) {
return { dispose() { } };
} else {
const handlers = registerGlobalHotReloadHandler();
handlers.add(handler);
return {
dispose() { handlers.delete(handler); }
};
}
}
/**
* Takes the old exports of the module to reload and returns a function to apply the new exports.
* If `undefined` is returned, this handler is not able to handle the module.
*
* If no handler can apply the new exports, the module will not be reloaded.
*/
export type HotReloadHandler = (args: { oldExports: Record<string, unknown>; newSrc: string; config: IHotReloadConfig }) => AcceptNewExportsHandler | undefined;
export type AcceptNewExportsHandler = (newExports: Record<string, unknown>) => boolean;
export type IHotReloadConfig = HotReloadConfig;
function registerGlobalHotReloadHandler() {
if (!hotReloadHandlers) {
hotReloadHandlers = new Set();
}
const g = globalThis as unknown as GlobalThisAddition;
if (!g.$hotReload_applyNewExports) {
g.$hotReload_applyNewExports = args => {
const args2 = { config: { mode: undefined }, ...args };
const results: AcceptNewExportsHandler[] = [];
for (const h of hotReloadHandlers!) {
const result = h(args2);
if (result) {
results.push(result);
}
}
if (results.length > 0) {
return newExports => {
let result = false;
for (const r of results) {
if (r(newExports)) {
result = true;
}
}
return result;
};
}
return undefined;
};
}
return hotReloadHandlers;
}
let hotReloadHandlers: Set<(args: { oldExports: Record<string, unknown>; newSrc: string; config: HotReloadConfig }) => AcceptNewExportsFn | undefined> | undefined = undefined;
interface HotReloadConfig {
mode?: 'patch-prototype' | undefined;
}
interface GlobalThisAddition {
$hotReload_applyNewExports?(args: { oldExports: Record<string, unknown>; newSrc: string; config?: HotReloadConfig }): AcceptNewExportsFn | undefined;
}
type AcceptNewExportsFn = (newExports: Record<string, unknown>) => boolean;
if (isHotReloadEnabled()) {
// This code does not run in production.
registerHotReloadHandler(({ oldExports, newSrc, config }) => {
if (config.mode !== 'patch-prototype') {
return undefined;
}
return newExports => {
for (const key in newExports) {
const exportedItem = newExports[key];
console.log(`[hot-reload] Patching prototype methods of '${key}'`, { exportedItem });
if (typeof exportedItem === 'function' && exportedItem.prototype) {
const oldExportedItem = oldExports[key];
if (oldExportedItem) {
for (const prop of Object.getOwnPropertyNames(exportedItem.prototype)) {
const descriptor = Object.getOwnPropertyDescriptor(exportedItem.prototype, prop)!;
const oldDescriptor = Object.getOwnPropertyDescriptor((oldExportedItem as any).prototype, prop);
if (descriptor?.value?.toString() !== oldDescriptor?.value?.toString()) {
console.log(`[hot-reload] Patching prototype method '${key}.${prop}'`);
}
Object.defineProperty((oldExportedItem as any).prototype, prop, descriptor);
}
newExports[key] = oldExportedItem;
}
}
}
return true;
};
});
}
-30
View File
@@ -1,30 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { isHotReloadEnabled, registerHotReloadHandler } from 'vs/base/common/hotReload';
import { IReader, observableSignalFromEvent } from 'vs/base/common/observable';
export function readHotReloadableExport<T>(value: T, reader: IReader | undefined): T {
observeHotReloadableExports([value], reader);
return value;
}
export function observeHotReloadableExports(values: any[], reader: IReader | undefined): void {
if (isHotReloadEnabled()) {
const o = observableSignalFromEvent(
'reload',
event => registerHotReloadHandler(({ oldExports }) => {
if (![...Object.values(oldExports)].some(v => values.includes(v))) {
return undefined;
}
return (_newExports) => {
event(undefined);
return true;
};
})
);
o.read(reader);
}
}
-21
View File
@@ -1,21 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
export class IdGenerator {
private _prefix: string;
private _lastId: number;
constructor(prefix: string) {
this._prefix = prefix;
this._lastId = 0;
}
public nextId(): string {
return this._prefix + (++this._lastId);
}
}
export const defaultGenerator = new IdGenerator('id#');
-36
View File
@@ -1,36 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Emitter } from 'vs/base/common/event';
export class IMEImpl {
private readonly _onDidChange = new Emitter<void>();
public readonly onDidChange = this._onDidChange.event;
private _enabled = true;
public get enabled() {
return this._enabled;
}
/**
* Enable IME
*/
public enable(): void {
this._enabled = true;
this._onDidChange.fire();
}
/**
* Disable IME
*/
public disable(): void {
this._enabled = false;
this._onDidChange.fire();
}
}
export const IME = new IMEImpl();
File diff suppressed because it is too large Load Diff
-176
View File
@@ -1,176 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { findNodeAtLocation, JSONPath, Node, ParseError, parseTree, Segment } from './json';
import { Edit, format, FormattingOptions, isEOL } from './jsonFormatter';
export function removeProperty(text: string, path: JSONPath, formattingOptions: FormattingOptions): Edit[] {
return setProperty(text, path, undefined, formattingOptions);
}
export function setProperty(text: string, originalPath: JSONPath, value: any, formattingOptions: FormattingOptions, getInsertionIndex?: (properties: string[]) => number): Edit[] {
const path = originalPath.slice();
const errors: ParseError[] = [];
const root = parseTree(text, errors);
let parent: Node | undefined = undefined;
let lastSegment: Segment | undefined = undefined;
while (path.length > 0) {
lastSegment = path.pop();
parent = findNodeAtLocation(root, path);
if (parent === undefined && value !== undefined) {
if (typeof lastSegment === 'string') {
value = { [lastSegment]: value };
} else {
value = [value];
}
} else {
break;
}
}
if (!parent) {
// empty document
if (value === undefined) { // delete
return []; // property does not exist, nothing to do
}
return withFormatting(text, { offset: root ? root.offset : 0, length: root ? root.length : 0, content: JSON.stringify(value) }, formattingOptions);
} else if (parent.type === 'object' && typeof lastSegment === 'string' && Array.isArray(parent.children)) {
const existing = findNodeAtLocation(parent, [lastSegment]);
if (existing !== undefined) {
if (value === undefined) { // delete
if (!existing.parent) {
throw new Error('Malformed AST');
}
const propertyIndex = parent.children.indexOf(existing.parent);
let removeBegin: number;
let removeEnd = existing.parent.offset + existing.parent.length;
if (propertyIndex > 0) {
// remove the comma of the previous node
const previous = parent.children[propertyIndex - 1];
removeBegin = previous.offset + previous.length;
} else {
removeBegin = parent.offset + 1;
if (parent.children.length > 1) {
// remove the comma of the next node
const next = parent.children[1];
removeEnd = next.offset;
}
}
return withFormatting(text, { offset: removeBegin, length: removeEnd - removeBegin, content: '' }, formattingOptions);
} else {
// set value of existing property
return withFormatting(text, { offset: existing.offset, length: existing.length, content: JSON.stringify(value) }, formattingOptions);
}
} else {
if (value === undefined) { // delete
return []; // property does not exist, nothing to do
}
const newProperty = `${JSON.stringify(lastSegment)}: ${JSON.stringify(value)}`;
const index = getInsertionIndex ? getInsertionIndex(parent.children.map(p => p.children![0].value)) : parent.children.length;
let edit: Edit;
if (index > 0) {
const previous = parent.children[index - 1];
edit = { offset: previous.offset + previous.length, length: 0, content: ',' + newProperty };
} else if (parent.children.length === 0) {
edit = { offset: parent.offset + 1, length: 0, content: newProperty };
} else {
edit = { offset: parent.offset + 1, length: 0, content: newProperty + ',' };
}
return withFormatting(text, edit, formattingOptions);
}
} else if (parent.type === 'array' && typeof lastSegment === 'number' && Array.isArray(parent.children)) {
if (value !== undefined) {
// Insert
const newProperty = `${JSON.stringify(value)}`;
let edit: Edit;
if (parent.children.length === 0 || lastSegment === 0) {
edit = { offset: parent.offset + 1, length: 0, content: parent.children.length === 0 ? newProperty : newProperty + ',' };
} else {
const index = lastSegment === -1 || lastSegment > parent.children.length ? parent.children.length : lastSegment;
const previous = parent.children[index - 1];
edit = { offset: previous.offset + previous.length, length: 0, content: ',' + newProperty };
}
return withFormatting(text, edit, formattingOptions);
} else {
//Removal
const removalIndex = lastSegment;
const toRemove = parent.children[removalIndex];
let edit: Edit;
if (parent.children.length === 1) {
// only item
edit = { offset: parent.offset + 1, length: parent.length - 2, content: '' };
} else if (parent.children.length - 1 === removalIndex) {
// last item
const previous = parent.children[removalIndex - 1];
const offset = previous.offset + previous.length;
const parentEndOffset = parent.offset + parent.length;
edit = { offset, length: parentEndOffset - 2 - offset, content: '' };
} else {
edit = { offset: toRemove.offset, length: parent.children[removalIndex + 1].offset - toRemove.offset, content: '' };
}
return withFormatting(text, edit, formattingOptions);
}
} else {
throw new Error(`Can not add ${typeof lastSegment !== 'number' ? 'index' : 'property'} to parent of type ${parent.type}`);
}
}
export function withFormatting(text: string, edit: Edit, formattingOptions: FormattingOptions): Edit[] {
// apply the edit
let newText = applyEdit(text, edit);
// format the new text
let begin = edit.offset;
let end = edit.offset + edit.content.length;
if (edit.length === 0 || edit.content.length === 0) { // insert or remove
while (begin > 0 && !isEOL(newText, begin - 1)) {
begin--;
}
while (end < newText.length && !isEOL(newText, end)) {
end++;
}
}
const edits = format(newText, { offset: begin, length: end - begin }, formattingOptions);
// apply the formatting edits and track the begin and end offsets of the changes
for (let i = edits.length - 1; i >= 0; i--) {
const curr = edits[i];
newText = applyEdit(newText, curr);
begin = Math.min(begin, curr.offset);
end = Math.max(end, curr.offset + curr.length);
end += curr.content.length - curr.length;
}
// create a single edit with all changes
const editLength = text.length - (newText.length - end) - begin;
return [{ offset: begin, length: editLength, content: newText.substring(begin, end) }];
}
export function applyEdit(text: string, edit: Edit): string {
return text.substring(0, edit.offset) + edit.content + text.substring(edit.offset + edit.length);
}
export function applyEdits(text: string, edits: Edit[]): string {
const sortedEdits = edits.slice(0).sort((a, b) => {
const diff = a.offset - b.offset;
if (diff === 0) {
return a.length - b.length;
}
return diff;
});
let lastModifiedOffset = text.length;
for (let i = sortedEdits.length - 1; i >= 0; i--) {
const e = sortedEdits[i];
if (e.offset + e.length <= lastModifiedOffset) {
text = applyEdit(text, e);
} else {
throw new Error('Overlapping edit');
}
lastModifiedOffset = e.offset;
}
return text;
}
-26
View File
@@ -1,26 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
/**
* Extracted from json.ts to keep json nls free.
*/
import { localize } from 'vs/nls';
import { ParseErrorCode } from './json';
export function getParseErrorMessage(errorCode: ParseErrorCode): string {
switch (errorCode) {
case ParseErrorCode.InvalidSymbol: return localize('error.invalidSymbol', 'Invalid symbol');
case ParseErrorCode.InvalidNumberFormat: return localize('error.invalidNumberFormat', 'Invalid number format');
case ParseErrorCode.PropertyNameExpected: return localize('error.propertyNameExpected', 'Property name expected');
case ParseErrorCode.ValueExpected: return localize('error.valueExpected', 'Value expected');
case ParseErrorCode.ColonExpected: return localize('error.colonExpected', 'Colon expected');
case ParseErrorCode.CommaExpected: return localize('error.commaExpected', 'Comma expected');
case ParseErrorCode.CloseBraceExpected: return localize('error.closeBraceExpected', 'Closing brace expected');
case ParseErrorCode.CloseBracketExpected: return localize('error.closeBracketExpected', 'Closing bracket expected');
case ParseErrorCode.EndOfFileExpected: return localize('error.endOfFileExpected', 'End of file expected');
default:
return '';
}
}
-261
View File
@@ -1,261 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { createScanner, ScanError, SyntaxKind } from './json';
export interface FormattingOptions {
/**
* If indentation is based on spaces (`insertSpaces` = true), then what is the number of spaces that make an indent?
*/
tabSize?: number;
/**
* Is indentation based on spaces?
*/
insertSpaces?: boolean;
/**
* The default 'end of line' character. If not set, '\n' is used as default.
*/
eol?: string;
}
/**
* Represents a text modification
*/
export interface Edit {
/**
* The start offset of the modification.
*/
offset: number;
/**
* The length of the modification. Must not be negative. Empty length represents an *insert*.
*/
length: number;
/**
* The new content. Empty content represents a *remove*.
*/
content: string;
}
/**
* A text range in the document
*/
export interface Range {
/**
* The start offset of the range.
*/
offset: number;
/**
* The length of the range. Must not be negative.
*/
length: number;
}
export function format(documentText: string, range: Range | undefined, options: FormattingOptions): Edit[] {
let initialIndentLevel: number;
let formatText: string;
let formatTextStart: number;
let rangeStart: number;
let rangeEnd: number;
if (range) {
rangeStart = range.offset;
rangeEnd = rangeStart + range.length;
formatTextStart = rangeStart;
while (formatTextStart > 0 && !isEOL(documentText, formatTextStart - 1)) {
formatTextStart--;
}
let endOffset = rangeEnd;
while (endOffset < documentText.length && !isEOL(documentText, endOffset)) {
endOffset++;
}
formatText = documentText.substring(formatTextStart, endOffset);
initialIndentLevel = computeIndentLevel(formatText, options);
} else {
formatText = documentText;
initialIndentLevel = 0;
formatTextStart = 0;
rangeStart = 0;
rangeEnd = documentText.length;
}
const eol = getEOL(options, documentText);
let lineBreak = false;
let indentLevel = 0;
let indentValue: string;
if (options.insertSpaces) {
indentValue = repeat(' ', options.tabSize || 4);
} else {
indentValue = '\t';
}
const scanner = createScanner(formatText, false);
let hasError = false;
function newLineAndIndent(): string {
return eol + repeat(indentValue, initialIndentLevel + indentLevel);
}
function scanNext(): SyntaxKind {
let token = scanner.scan();
lineBreak = false;
while (token === SyntaxKind.Trivia || token === SyntaxKind.LineBreakTrivia) {
lineBreak = lineBreak || (token === SyntaxKind.LineBreakTrivia);
token = scanner.scan();
}
hasError = token === SyntaxKind.Unknown || scanner.getTokenError() !== ScanError.None;
return token;
}
const editOperations: Edit[] = [];
function addEdit(text: string, startOffset: number, endOffset: number) {
if (!hasError && startOffset < rangeEnd && endOffset > rangeStart && documentText.substring(startOffset, endOffset) !== text) {
editOperations.push({ offset: startOffset, length: endOffset - startOffset, content: text });
}
}
let firstToken = scanNext();
if (firstToken !== SyntaxKind.EOF) {
const firstTokenStart = scanner.getTokenOffset() + formatTextStart;
const initialIndent = repeat(indentValue, initialIndentLevel);
addEdit(initialIndent, formatTextStart, firstTokenStart);
}
while (firstToken !== SyntaxKind.EOF) {
let firstTokenEnd = scanner.getTokenOffset() + scanner.getTokenLength() + formatTextStart;
let secondToken = scanNext();
let replaceContent = '';
while (!lineBreak && (secondToken === SyntaxKind.LineCommentTrivia || secondToken === SyntaxKind.BlockCommentTrivia)) {
// comments on the same line: keep them on the same line, but ignore them otherwise
const commentTokenStart = scanner.getTokenOffset() + formatTextStart;
addEdit(' ', firstTokenEnd, commentTokenStart);
firstTokenEnd = scanner.getTokenOffset() + scanner.getTokenLength() + formatTextStart;
replaceContent = secondToken === SyntaxKind.LineCommentTrivia ? newLineAndIndent() : '';
secondToken = scanNext();
}
if (secondToken === SyntaxKind.CloseBraceToken) {
if (firstToken !== SyntaxKind.OpenBraceToken) {
indentLevel--;
replaceContent = newLineAndIndent();
}
} else if (secondToken === SyntaxKind.CloseBracketToken) {
if (firstToken !== SyntaxKind.OpenBracketToken) {
indentLevel--;
replaceContent = newLineAndIndent();
}
} else {
switch (firstToken) {
case SyntaxKind.OpenBracketToken:
case SyntaxKind.OpenBraceToken:
indentLevel++;
replaceContent = newLineAndIndent();
break;
case SyntaxKind.CommaToken:
case SyntaxKind.LineCommentTrivia:
replaceContent = newLineAndIndent();
break;
case SyntaxKind.BlockCommentTrivia:
if (lineBreak) {
replaceContent = newLineAndIndent();
} else {
// symbol following comment on the same line: keep on same line, separate with ' '
replaceContent = ' ';
}
break;
case SyntaxKind.ColonToken:
replaceContent = ' ';
break;
case SyntaxKind.StringLiteral:
if (secondToken === SyntaxKind.ColonToken) {
replaceContent = '';
break;
}
// fall through
case SyntaxKind.NullKeyword:
case SyntaxKind.TrueKeyword:
case SyntaxKind.FalseKeyword:
case SyntaxKind.NumericLiteral:
case SyntaxKind.CloseBraceToken:
case SyntaxKind.CloseBracketToken:
if (secondToken === SyntaxKind.LineCommentTrivia || secondToken === SyntaxKind.BlockCommentTrivia) {
replaceContent = ' ';
} else if (secondToken !== SyntaxKind.CommaToken && secondToken !== SyntaxKind.EOF) {
hasError = true;
}
break;
case SyntaxKind.Unknown:
hasError = true;
break;
}
if (lineBreak && (secondToken === SyntaxKind.LineCommentTrivia || secondToken === SyntaxKind.BlockCommentTrivia)) {
replaceContent = newLineAndIndent();
}
}
const secondTokenStart = scanner.getTokenOffset() + formatTextStart;
addEdit(replaceContent, firstTokenEnd, secondTokenStart);
firstToken = secondToken;
}
return editOperations;
}
/**
* Creates a formatted string out of the object passed as argument, using the given formatting options
* @param any The object to stringify and format
* @param options The formatting options to use
*/
export function toFormattedString(obj: any, options: FormattingOptions) {
const content = JSON.stringify(obj, undefined, options.insertSpaces ? options.tabSize || 4 : '\t');
if (options.eol !== undefined) {
return content.replace(/\r\n|\r|\n/g, options.eol);
}
return content;
}
function repeat(s: string, count: number): string {
let result = '';
for (let i = 0; i < count; i++) {
result += s;
}
return result;
}
function computeIndentLevel(content: string, options: FormattingOptions): number {
let i = 0;
let nChars = 0;
const tabSize = options.tabSize || 4;
while (i < content.length) {
const ch = content.charAt(i);
if (ch === ' ') {
nChars++;
} else if (ch === '\t') {
nChars += tabSize;
} else {
break;
}
i++;
}
return Math.floor(nChars / tabSize);
}
export function getEOL(options: FormattingOptions, text: string): string {
for (let i = 0; i < text.length; i++) {
const ch = text.charAt(i);
if (ch === '\r') {
if (i + 1 < text.length && text.charAt(i + 1) === '\n') {
return '\r\n';
}
return '\r';
} else if (ch === '\n') {
return '\n';
}
}
return (options && options.eol) || '\n';
}
export function isEOL(text: string, offset: number) {
return '\r\n'.indexOf(text.charAt(offset)) !== -1;
}
-267
View File
@@ -1,267 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
export type JSONSchemaType = 'string' | 'number' | 'integer' | 'boolean' | 'null' | 'array' | 'object';
export interface IJSONSchema {
id?: string;
$id?: string;
$schema?: string;
type?: JSONSchemaType | JSONSchemaType[];
title?: string;
default?: any;
definitions?: IJSONSchemaMap;
description?: string;
properties?: IJSONSchemaMap;
patternProperties?: IJSONSchemaMap;
additionalProperties?: boolean | IJSONSchema;
minProperties?: number;
maxProperties?: number;
dependencies?: IJSONSchemaMap | { [prop: string]: string[] };
items?: IJSONSchema | IJSONSchema[];
minItems?: number;
maxItems?: number;
uniqueItems?: boolean;
additionalItems?: boolean | IJSONSchema;
pattern?: string;
minLength?: number;
maxLength?: number;
minimum?: number;
maximum?: number;
exclusiveMinimum?: boolean | number;
exclusiveMaximum?: boolean | number;
multipleOf?: number;
required?: string[];
$ref?: string;
anyOf?: IJSONSchema[];
allOf?: IJSONSchema[];
oneOf?: IJSONSchema[];
not?: IJSONSchema;
enum?: any[];
format?: string;
// schema draft 06
const?: any;
contains?: IJSONSchema;
propertyNames?: IJSONSchema;
examples?: any[];
// schema draft 07
$comment?: string;
if?: IJSONSchema;
then?: IJSONSchema;
else?: IJSONSchema;
// schema 2019-09
unevaluatedProperties?: boolean | IJSONSchema;
unevaluatedItems?: boolean | IJSONSchema;
minContains?: number;
maxContains?: number;
deprecated?: boolean;
dependentRequired?: { [prop: string]: string[] };
dependentSchemas?: IJSONSchemaMap;
$defs?: { [name: string]: IJSONSchema };
$anchor?: string;
$recursiveRef?: string;
$recursiveAnchor?: string;
$vocabulary?: any;
// schema 2020-12
prefixItems?: IJSONSchema[];
$dynamicRef?: string;
$dynamicAnchor?: string;
// VSCode extensions
defaultSnippets?: IJSONSchemaSnippet[];
errorMessage?: string;
patternErrorMessage?: string;
deprecationMessage?: string;
markdownDeprecationMessage?: string;
enumDescriptions?: string[];
markdownEnumDescriptions?: string[];
markdownDescription?: string;
doNotSuggest?: boolean;
suggestSortText?: string;
allowComments?: boolean;
allowTrailingCommas?: boolean;
}
export interface IJSONSchemaMap {
[name: string]: IJSONSchema;
}
export interface IJSONSchemaSnippet {
label?: string;
description?: string;
body?: any; // a object that will be JSON stringified
bodyText?: string; // an already stringified JSON object that can contain new lines (\n) and tabs (\t)
}
/**
* Converts a basic JSON schema to a TypeScript type.
*
* TODO: only supports basic schemas. Doesn't support all JSON schema features.
*/
export type SchemaToType<T> = T extends { type: 'string' }
? string
: T extends { type: 'number' }
? number
: T extends { type: 'boolean' }
? boolean
: T extends { type: 'null' }
? null
: T extends { type: 'object'; properties: infer P }
? { [K in keyof P]: SchemaToType<P[K]> }
: T extends { type: 'array'; items: infer I }
? Array<SchemaToType<I>>
: never;
interface Equals { schemas: IJSONSchema[]; id?: string }
export function getCompressedContent(schema: IJSONSchema): string {
let hasDups = false;
// visit all schema nodes and collect the ones that are equal
const equalsByString = new Map<string, Equals>();
const nodeToEquals = new Map<IJSONSchema, Equals>();
const visitSchemas = (next: IJSONSchema) => {
if (schema === next) {
return true;
}
const val = JSON.stringify(next);
if (val.length < 30) {
// the $ref takes around 25 chars, so we don't save anything
return true;
}
const eq = equalsByString.get(val);
if (!eq) {
const newEq = { schemas: [next] };
equalsByString.set(val, newEq);
nodeToEquals.set(next, newEq);
return true;
}
eq.schemas.push(next);
nodeToEquals.set(next, eq);
hasDups = true;
return false;
};
traverseNodes(schema, visitSchemas);
equalsByString.clear();
if (!hasDups) {
return JSON.stringify(schema);
}
let defNodeName = '$defs';
while (schema.hasOwnProperty(defNodeName)) {
defNodeName += '_';
}
// used to collect all schemas that are later put in `$defs`. The index in the array is the id of the schema.
const definitions: IJSONSchema[] = [];
function stringify(root: IJSONSchema): string {
return JSON.stringify(root, (_key: string, value: any) => {
if (value !== root) {
const eq = nodeToEquals.get(value);
if (eq && eq.schemas.length > 1) {
if (!eq.id) {
eq.id = `_${definitions.length}`;
definitions.push(eq.schemas[0]);
}
return { $ref: `#/${defNodeName}/${eq.id}` };
}
}
return value;
});
}
// stringify the schema and replace duplicate subtrees with $ref
// this will add new items to the definitions array
const str = stringify(schema);
// now stringify the definitions. Each invication of stringify cann add new items to the definitions array, so the length can grow while we iterate
const defStrings: string[] = [];
for (let i = 0; i < definitions.length; i++) {
defStrings.push(`"_${i}":${stringify(definitions[i])}`);
}
if (defStrings.length) {
return `${str.substring(0, str.length - 1)},"${defNodeName}":{${defStrings.join(',')}}}`;
}
return str;
}
type IJSONSchemaRef = IJSONSchema | boolean;
function isObject(thing: any): thing is object {
return typeof thing === 'object' && thing !== null;
}
/*
* Traverse a JSON schema and visit each schema node
*/
function traverseNodes(root: IJSONSchema, visit: (schema: IJSONSchema) => boolean) {
if (!root || typeof root !== 'object') {
return;
}
const collectEntries = (...entries: (IJSONSchemaRef | undefined)[]) => {
for (const entry of entries) {
if (isObject(entry)) {
toWalk.push(entry);
}
}
};
const collectMapEntries = (...maps: (IJSONSchemaMap | undefined)[]) => {
for (const map of maps) {
if (isObject(map)) {
for (const key in map) {
const entry = map[key];
if (isObject(entry)) {
toWalk.push(entry);
}
}
}
}
};
const collectArrayEntries = (...arrays: (IJSONSchemaRef[] | undefined)[]) => {
for (const array of arrays) {
if (Array.isArray(array)) {
for (const entry of array) {
if (isObject(entry)) {
toWalk.push(entry);
}
}
}
}
};
const collectEntryOrArrayEntries = (items: (IJSONSchemaRef[] | IJSONSchemaRef | undefined)) => {
if (Array.isArray(items)) {
for (const entry of items) {
if (isObject(entry)) {
toWalk.push(entry);
}
}
} else if (isObject(items)) {
toWalk.push(items);
}
};
const toWalk: IJSONSchema[] = [root];
let next = toWalk.pop();
while (next) {
const visitChildern = visit(next);
if (visitChildern) {
collectEntries(next.additionalItems, next.additionalProperties, next.not, next.contains, next.propertyNames, next.if, next.then, next.else, next.unevaluatedItems, next.unevaluatedProperties);
collectMapEntries(next.definitions, next.$defs, next.properties, next.patternProperties, <IJSONSchemaMap>next.dependencies, next.dependentSchemas);
collectArrayEntries(next.anyOf, next.allOf, next.oneOf, next.prefixItems);
collectEntryOrArrayEntries(next.items);
}
next = toWalk.pop();
}
}
-24
View File
@@ -1,24 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
/**
* A drop-in replacement for JSON.parse that can parse
* JSON with comments and trailing commas.
*
* @param content the content to strip comments from
* @returns the parsed content as JSON
*/
export function parse(content: string): any;
/**
* Strips single and multi line JavaScript comments from JSON
* content. Ignores characters in strings BUT doesn't support
* string continuation across multiple lines since it is not
* supported in JSON.
*
* @param content the content to strip comments from
* @returns the content without comments
*/
export function stripComments(content: string): string;
-89
View File
@@ -1,89 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
/// <reference path="../../../typings/require.d.ts" />
//@ts-check
'use strict';
// ESM-uncomment-begin
// const module = { exports: {} };
// ESM-uncomment-end
(function () {
function factory() {
// First group matches a double quoted string
// Second group matches a single quoted string
// Third group matches a multi line comment
// Forth group matches a single line comment
// Fifth group matches a trailing comma
const regexp = /("[^"\\]*(?:\\.[^"\\]*)*")|('[^'\\]*(?:\\.[^'\\]*)*')|(\/\*[^\/\*]*(?:(?:\*|\/)[^\/\*]*)*?\*\/)|(\/{2,}.*?(?:(?:\r?\n)|$))|(,\s*[}\]])/g;
/**
* @param {string} content
* @returns {string}
*/
function stripComments(content) {
return content.replace(regexp, function (match, _m1, _m2, m3, m4, m5) {
// Only one of m1, m2, m3, m4, m5 matches
if (m3) {
// A block comment. Replace with nothing
return '';
} else if (m4) {
// Since m4 is a single line comment is is at least of length 2 (e.g. //)
// If it ends in \r?\n then keep it.
const length = m4.length;
if (m4[length - 1] === '\n') {
return m4[length - 2] === '\r' ? '\r\n' : '\n';
}
else {
return '';
}
} else if (m5) {
// Remove the trailing comma
return match.substring(1);
} else {
// We match a string
return match;
}
});
}
/**
* @param {string} content
* @returns {any}
*/
function parse(content) {
const commentsStripped = stripComments(content);
try {
return JSON.parse(commentsStripped);
} catch (error) {
const trailingCommasStriped = commentsStripped.replace(/,\s*([}\]])/g, '$1');
return JSON.parse(trailingCommasStriped);
}
}
return {
stripComments,
parse
};
}
if (typeof define === 'function') {
// amd
define([], function () { return factory(); });
} else if (typeof module === 'object' && typeof module.exports === 'object') {
// commonjs
module.exports = factory();
} else {
console.trace('jsonc defined in UNKNOWN context (neither requirejs or commonjs)');
}
})();
// ESM-uncomment-begin
// export const stripComments = module.exports.stripComments;
// export const parse = module.exports.parse;
// ESM-uncomment-end
-184
View File
@@ -1,184 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Modifiers } from 'vs/base/common/keybindings';
import { OperatingSystem } from 'vs/base/common/platform';
import * as nls from 'vs/nls';
export interface ModifierLabels {
readonly ctrlKey: string;
readonly shiftKey: string;
readonly altKey: string;
readonly metaKey: string;
readonly separator: string;
}
export interface KeyLabelProvider<T extends Modifiers> {
(keybinding: T): string | null;
}
export class ModifierLabelProvider {
public readonly modifierLabels: ModifierLabels[];
constructor(mac: ModifierLabels, windows: ModifierLabels, linux: ModifierLabels = windows) {
this.modifierLabels = [null!]; // index 0 will never me accessed.
this.modifierLabels[OperatingSystem.Macintosh] = mac;
this.modifierLabels[OperatingSystem.Windows] = windows;
this.modifierLabels[OperatingSystem.Linux] = linux;
}
public toLabel<T extends Modifiers>(OS: OperatingSystem, chords: readonly T[], keyLabelProvider: KeyLabelProvider<T>): string | null {
if (chords.length === 0) {
return null;
}
const result: string[] = [];
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.
*/
export const UILabelProvider = new ModifierLabelProvider(
{
ctrlKey: '\u2303',
shiftKey: '⇧',
altKey: '⌥',
metaKey: '⌘',
separator: '',
},
{
ctrlKey: nls.localize({ key: 'ctrlKey', comment: ['This is the short form for the Control key on the keyboard'] }, "Ctrl"),
shiftKey: nls.localize({ key: 'shiftKey', comment: ['This is the short form for the Shift key on the keyboard'] }, "Shift"),
altKey: nls.localize({ key: 'altKey', comment: ['This is the short form for the Alt key on the keyboard'] }, "Alt"),
metaKey: nls.localize({ key: 'windowsKey', comment: ['This is the short form for the Windows key on the keyboard'] }, "Windows"),
separator: '+',
},
{
ctrlKey: nls.localize({ key: 'ctrlKey', comment: ['This is the short form for the Control key on the keyboard'] }, "Ctrl"),
shiftKey: nls.localize({ key: 'shiftKey', comment: ['This is the short form for the Shift key on the keyboard'] }, "Shift"),
altKey: nls.localize({ key: 'altKey', comment: ['This is the short form for the Alt key on the keyboard'] }, "Alt"),
metaKey: nls.localize({ key: 'superKey', comment: ['This is the short form for the Super key on the keyboard'] }, "Super"),
separator: '+',
}
);
/**
* A label provider that prints modifiers in a suitable format for ARIA.
*/
export const AriaLabelProvider = new ModifierLabelProvider(
{
ctrlKey: nls.localize({ key: 'ctrlKey.long', comment: ['This is the long form for the Control key on the keyboard'] }, "Control"),
shiftKey: nls.localize({ key: 'shiftKey.long', comment: ['This is the long form for the Shift key on the keyboard'] }, "Shift"),
altKey: nls.localize({ key: 'optKey.long', comment: ['This is the long form for the Alt/Option key on the keyboard'] }, "Option"),
metaKey: nls.localize({ key: 'cmdKey.long', comment: ['This is the long form for the Command key on the keyboard'] }, "Command"),
separator: '+',
},
{
ctrlKey: nls.localize({ key: 'ctrlKey.long', comment: ['This is the long form for the Control key on the keyboard'] }, "Control"),
shiftKey: nls.localize({ key: 'shiftKey.long', comment: ['This is the long form for the Shift key on the keyboard'] }, "Shift"),
altKey: nls.localize({ key: 'altKey.long', comment: ['This is the long form for the Alt key on the keyboard'] }, "Alt"),
metaKey: nls.localize({ key: 'windowsKey.long', comment: ['This is the long form for the Windows key on the keyboard'] }, "Windows"),
separator: '+',
},
{
ctrlKey: nls.localize({ key: 'ctrlKey.long', comment: ['This is the long form for the Control key on the keyboard'] }, "Control"),
shiftKey: nls.localize({ key: 'shiftKey.long', comment: ['This is the long form for the Shift key on the keyboard'] }, "Shift"),
altKey: nls.localize({ key: 'altKey.long', comment: ['This is the long form for the Alt key on the keyboard'] }, "Alt"),
metaKey: nls.localize({ key: 'superKey.long', comment: ['This is the long form for the Super key on the keyboard'] }, "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
*/
export 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.
*/
export 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: Modifiers, key: string, labels: ModifierLabels): string {
if (key === null) {
return '';
}
const result: string[] = [];
// 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);
}
-102
View File
@@ -1,102 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { KeyCodeUtils, ScanCodeUtils } from 'vs/base/common/keyCodes';
import { KeyCodeChord, ScanCodeChord, Keybinding, Chord } from 'vs/base/common/keybindings';
export class KeybindingParser {
private static _readModifiers(input: string) {
input = input.toLowerCase().trim();
let ctrl = false;
let shift = false;
let alt = false;
let meta = false;
let matchedModifier: boolean;
do {
matchedModifier = false;
if (/^ctrl(\+|\-)/.test(input)) {
ctrl = true;
input = input.substr('ctrl-'.length);
matchedModifier = true;
}
if (/^shift(\+|\-)/.test(input)) {
shift = true;
input = input.substr('shift-'.length);
matchedModifier = true;
}
if (/^alt(\+|\-)/.test(input)) {
alt = true;
input = input.substr('alt-'.length);
matchedModifier = true;
}
if (/^meta(\+|\-)/.test(input)) {
meta = true;
input = input.substr('meta-'.length);
matchedModifier = true;
}
if (/^win(\+|\-)/.test(input)) {
meta = true;
input = input.substr('win-'.length);
matchedModifier = true;
}
if (/^cmd(\+|\-)/.test(input)) {
meta = true;
input = input.substr('cmd-'.length);
matchedModifier = true;
}
} while (matchedModifier);
let key: string;
const firstSpaceIdx = input.indexOf(' ');
if (firstSpaceIdx > 0) {
key = input.substring(0, firstSpaceIdx);
input = input.substring(firstSpaceIdx);
} else {
key = input;
input = '';
}
return {
remains: input,
ctrl,
shift,
alt,
meta,
key
};
}
private static parseChord(input: string): [Chord, string] {
const mods = this._readModifiers(input);
const scanCodeMatch = mods.key.match(/^\[([^\]]+)\]$/);
if (scanCodeMatch) {
const strScanCode = scanCodeMatch[1];
const scanCode = ScanCodeUtils.lowerCaseToEnum(strScanCode);
return [new ScanCodeChord(mods.ctrl, mods.shift, mods.alt, mods.meta, scanCode), mods.remains];
}
const keyCode = KeyCodeUtils.fromUserSettings(mods.key);
return [new KeyCodeChord(mods.ctrl, mods.shift, mods.alt, mods.meta, keyCode), mods.remains];
}
static parseKeybinding(input: string): Keybinding | null {
if (!input) {
return null;
}
const chords: Chord[] = [];
let chord: Chord;
while (input.length > 0) {
[chord, input] = this.parseChord(input);
chords.push(chord);
}
return (chords.length > 0 ? new Keybinding(chords) : null);
}
}
-55
View File
@@ -1,55 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { memoize } from 'vs/base/common/decorators';
export interface ILink {
readonly label: string;
readonly href: string;
readonly title?: string;
}
export type LinkedTextNode = string | ILink;
export class LinkedText {
constructor(readonly nodes: LinkedTextNode[]) { }
@memoize
toString(): string {
return this.nodes.map(node => typeof node === 'string' ? node : node.label).join('');
}
}
const LINK_REGEX = /\[([^\]]+)\]\(((?:https?:\/\/|command:|file:)[^\)\s]+)(?: (["'])(.+?)(\3))?\)/gi;
export function parseLinkedText(text: string): LinkedText {
const result: LinkedTextNode[] = [];
let index = 0;
let match: RegExpExecArray | null;
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);
}
-126
View File
@@ -1,126 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { extname } from 'vs/base/common/path';
export const Mimes = Object.freeze({
text: 'text/plain',
binary: 'application/octet-stream',
unknown: 'application/unknown',
markdown: 'text/markdown',
latex: 'text/latex',
uriList: 'text/uri-list',
});
interface MapExtToMediaMimes {
[index: string]: string;
}
const mapExtToTextMimes: MapExtToMediaMimes = {
'.css': 'text/css',
'.csv': 'text/csv',
'.htm': 'text/html',
'.html': 'text/html',
'.ics': 'text/calendar',
'.js': 'text/javascript',
'.mjs': 'text/javascript',
'.txt': 'text/plain',
'.xml': 'text/xml'
};
// Known media mimes that we can handle
const mapExtToMediaMimes: MapExtToMediaMimes = {
'.aac': 'audio/x-aac',
'.avi': 'video/x-msvideo',
'.bmp': 'image/bmp',
'.flv': 'video/x-flv',
'.gif': 'image/gif',
'.ico': 'image/x-icon',
'.jpe': 'image/jpg',
'.jpeg': 'image/jpg',
'.jpg': 'image/jpg',
'.m1v': 'video/mpeg',
'.m2a': 'audio/mpeg',
'.m2v': 'video/mpeg',
'.m3a': 'audio/mpeg',
'.mid': 'audio/midi',
'.midi': 'audio/midi',
'.mk3d': 'video/x-matroska',
'.mks': 'video/x-matroska',
'.mkv': 'video/x-matroska',
'.mov': 'video/quicktime',
'.movie': 'video/x-sgi-movie',
'.mp2': 'audio/mpeg',
'.mp2a': 'audio/mpeg',
'.mp3': 'audio/mpeg',
'.mp4': 'video/mp4',
'.mp4a': 'audio/mp4',
'.mp4v': 'video/mp4',
'.mpe': 'video/mpeg',
'.mpeg': 'video/mpeg',
'.mpg': 'video/mpeg',
'.mpg4': 'video/mp4',
'.mpga': 'audio/mpeg',
'.oga': 'audio/ogg',
'.ogg': 'audio/ogg',
'.opus': 'audio/opus',
'.ogv': 'video/ogg',
'.png': 'image/png',
'.psd': 'image/vnd.adobe.photoshop',
'.qt': 'video/quicktime',
'.spx': 'audio/ogg',
'.svg': 'image/svg+xml',
'.tga': 'image/x-tga',
'.tif': 'image/tiff',
'.tiff': 'image/tiff',
'.wav': 'audio/x-wav',
'.webm': 'video/webm',
'.webp': 'image/webp',
'.wma': 'audio/x-ms-wma',
'.wmv': 'video/x-ms-wmv',
'.woff': 'application/font-woff',
};
export function getMediaOrTextMime(path: string): string | undefined {
const ext = extname(path);
const textMime = mapExtToTextMimes[ext.toLowerCase()];
if (textMime !== undefined) {
return textMime;
} else {
return getMediaMime(path);
}
}
export function getMediaMime(path: string): string | undefined {
const ext = extname(path);
return mapExtToMediaMimes[ext.toLowerCase()];
}
export function getExtensionForMimeType(mimeType: string): string | undefined {
for (const extension in mapExtToMediaMimes) {
if (mapExtToMediaMimes[extension] === mimeType) {
return extension;
}
}
return undefined;
}
const _simplePattern = /^(.+)\/(.+?)(;.+)?$/;
export function normalizeMimeType(mimeType: string): string;
export function normalizeMimeType(mimeType: string, strict: true): string | undefined;
export function normalizeMimeType(mimeType: string, strict?: true): string | undefined {
const match = _simplePattern.exec(mimeType);
if (!match) {
return strict
? undefined
: mimeType;
}
// https://datatracker.ietf.org/doc/html/rfc2045#section-5.1
// media and subtype must ALWAYS be lowercase, parameter not
return `${match[1].toLowerCase()}/${match[2].toLowerCase()}${match[3] ?? ''}`;
}
-50
View File
@@ -1,50 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
export interface INavigator<T> {
current(): T | null;
previous(): T | null;
first(): T | null;
last(): T | null;
next(): T | null;
}
export class ArrayNavigator<T> implements INavigator<T> {
constructor(
private readonly items: readonly T[],
protected start: number = 0,
protected end: number = items.length,
protected index = start - 1
) { }
current(): T | null {
if (this.index === this.start - 1 || this.index === this.end) {
return null;
}
return this.items[this.index];
}
next(): T | null {
this.index = Math.min(this.index + 1, this.end);
return this.current();
}
previous(): T | null {
this.index = Math.max(this.index - 1, this.start - 1);
return this.current();
}
first(): T | null {
this.index = this.start;
return this.current();
}
last(): T | null {
this.index = this.end - 1;
return this.current();
}
}
-128
View File
@@ -1,128 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
export namespace Schemas {
/**
* A schema that is used for models that exist in memory
* only and that have no correspondence on a server or such.
*/
export const inMemory = 'inmemory';
/**
* A schema that is used for setting files
*/
export const vscode = 'vscode';
/**
* A schema that is used for internal private files
*/
export const internal = 'private';
/**
* A walk-through document.
*/
export const walkThrough = 'walkThrough';
/**
* An embedded code snippet.
*/
export const walkThroughSnippet = 'walkThroughSnippet';
export const http = 'http';
export const https = 'https';
export const file = 'file';
export const mailto = 'mailto';
export const untitled = 'untitled';
export const data = 'data';
export const command = 'command';
export const vscodeRemote = 'vscode-remote';
export const vscodeRemoteResource = 'vscode-remote-resource';
export const vscodeManagedRemoteResource = 'vscode-managed-remote-resource';
export const vscodeUserData = 'vscode-userdata';
export const vscodeCustomEditor = 'vscode-custom-editor';
export const vscodeNotebookCell = 'vscode-notebook-cell';
export const vscodeNotebookCellMetadata = 'vscode-notebook-cell-metadata';
export const vscodeNotebookCellOutput = 'vscode-notebook-cell-output';
export const vscodeInteractiveInput = 'vscode-interactive-input';
export const vscodeSettings = 'vscode-settings';
export const vscodeWorkspaceTrust = 'vscode-workspace-trust';
export const vscodeTerminal = 'vscode-terminal';
/** Scheme used for code blocks in chat. */
export const vscodeChatCodeBlock = 'vscode-chat-code-block';
/**
* Scheme used for backing documents created by copilot for chat.
*/
export const vscodeCopilotBackingChatCodeBlock = 'vscode-copilot-chat-code-block';
/** Scheme used for LHS of code compare (aka diff) blocks in chat. */
export const vscodeChatCodeCompareBlock = 'vscode-chat-code-compare-block';
/** Scheme used for the chat input editor. */
export const vscodeChatSesssion = 'vscode-chat-editor';
/**
* Scheme used internally for webviews that aren't linked to a resource (i.e. not custom editors)
*/
export const webviewPanel = 'webview-panel';
/**
* Scheme used for loading the wrapper html and script in webviews.
*/
export const vscodeWebview = 'vscode-webview';
/**
* Scheme used for extension pages
*/
export const extension = 'extension';
/**
* Scheme used as a replacement of `file` scheme to load
* files with our custom protocol handler (desktop only).
*/
export const vscodeFileResource = 'vscode-file';
/**
* Scheme used for temporary resources
*/
export const tmp = 'tmp';
/**
* Scheme used vs live share
*/
export const vsls = 'vsls';
/**
* Scheme used for the Source Control commit input's text document
*/
export const vscodeSourceControl = 'vscode-scm';
/**
* Scheme used for input box for creating comments.
*/
export const commentsInput = 'comment';
/**
* Scheme used for special rendering of settings in the release notes
*/
export const codeSetting = 'code-setting';
}
-274
View File
@@ -1,274 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { isTypedArray, isObject, isUndefinedOrNull } from 'vs/base/common/types';
export function deepClone<T>(obj: T): T {
if (!obj || typeof obj !== 'object') {
return obj;
}
if (obj instanceof RegExp) {
return obj;
}
const result: any = Array.isArray(obj) ? [] : {};
Object.entries(obj).forEach(([key, value]) => {
result[key] = value && typeof value === 'object' ? deepClone(value) : value;
});
return result;
}
export function deepFreeze<T>(obj: T): T {
if (!obj || typeof obj !== 'object') {
return obj;
}
const stack: any[] = [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;
export function cloneAndChange(obj: any, changer: (orig: any) => any): any {
return _cloneAndChange(obj, changer, new Set());
}
function _cloneAndChange(obj: any, changer: (orig: any) => any, seen: Set<any>): any {
if (isUndefinedOrNull(obj)) {
return obj;
}
const changed = changer(obj);
if (typeof changed !== 'undefined') {
return changed;
}
if (Array.isArray(obj)) {
const r1: any[] = [];
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 as any)[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).
*/
export function mixin(destination: any, source: any, overwrite: boolean = true): any {
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;
}
export function equals(one: any, other: any): boolean {
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: number;
let key: string;
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: string[] = [];
for (key in one) {
oneKeys.push(key);
}
oneKeys.sort();
const otherKeys: string[] = [];
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;
}
/**
* Calls `JSON.Stringify` with a replacer to break apart any circular references.
* This prevents `JSON`.stringify` from throwing the exception
* "Uncaught TypeError: Converting circular structure to JSON"
*/
export function safeStringify(obj: any): string {
const seen = new Set<any>();
return JSON.stringify(obj, (key, value) => {
if (isObject(value) || Array.isArray(value)) {
if (seen.has(value)) {
return '[Circular]';
} else {
seen.add(value);
}
}
if (typeof value === 'bigint') {
return `[BigInt ${value.toString()}]`;
}
return value;
});
}
type obj = { [key: string]: any };
/**
* Returns an object that has keys for each value that is different in the base object. Keys
* that do not exist in the target but in the base object are not considered.
*
* Note: This is not a deep-diffing method, so the values are strictly taken into the resulting
* object if they differ.
*
* @param base the object to diff against
* @param obj the object to use for diffing
*/
export function distinct(base: obj, target: obj): obj {
const result = Object.create(null);
if (!base || !target) {
return result;
}
const targetKeys = Object.keys(target);
targetKeys.forEach(k => {
const baseValue = base[k];
const targetValue = target[k];
if (!equals(baseValue, targetValue)) {
result[k] = targetValue;
}
});
return result;
}
export function getCaseInsensitive(target: obj, key: string): any {
const lowercaseKey = key.toLowerCase();
const equivalentKey = Object.keys(target).find(k => k.toLowerCase() === lowercaseKey);
return equivalentKey ? target[equivalentKey] : target[key];
}
export function filter(obj: obj, predicate: (key: string, value: any) => boolean): obj {
const result = Object.create(null);
for (const [key, value] of Object.entries(obj)) {
if (predicate(key, value)) {
result[key] = value;
}
}
return result;
}
export function getAllPropertyNames(obj: object): string[] {
let res: string[] = [];
while (Object.prototype !== obj) {
res = res.concat(Object.getOwnPropertyNames(obj));
obj = Object.getPrototypeOf(obj);
}
return res;
}
export function getAllMethodNames(obj: object): string[] {
const methods: string[] = [];
for (const prop of getAllPropertyNames(obj)) {
if (typeof (obj as any)[prop] === 'function') {
methods.push(prop);
}
}
return methods;
}
export function createProxyObject<T extends object>(methodNames: string[], invoke: (method: string, args: unknown[]) => unknown): T {
const createProxyMethod = (method: string): () => unknown => {
return function () {
const args = Array.prototype.slice.call(arguments, 0);
return invoke(method, args);
};
};
const result = {} as T;
for (const methodName of methodNames) {
(<any>result)[methodName] = createProxyMethod(methodName);
}
return result;
}
export function mapValues<T extends {}, R>(obj: T, fn: (value: T[keyof T], key: string) => R): { [K in keyof T]: R } {
const result: { [key: string]: R } = {};
for (const [key, value] of Object.entries(obj)) {
result[key] = fn(<T[keyof T]>value, key);
}
return result as { [K in keyof T]: R };
}
-189
View File
@@ -1,189 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { range } from 'vs/base/common/arrays';
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
import { CancellationError } from 'vs/base/common/errors';
/**
* A Pager is a stateless abstraction over a paged collection.
*/
export interface IPager<T> {
firstPage: T[];
total: number;
pageSize: number;
getPage(pageIndex: number, cancellationToken: CancellationToken): Promise<T[]>;
}
interface IPage<T> {
isResolved: boolean;
promise: Promise<void> | null;
cts: CancellationTokenSource | null;
promiseIndexes: Set<number>;
elements: T[];
}
function createPage<T>(elements?: T[]): IPage<T> {
return {
isResolved: !!elements,
promise: null,
cts: null,
promiseIndexes: new Set<number>(),
elements: elements || []
};
}
/**
* A PagedModel is a stateful model over an abstracted paged collection.
*/
export interface IPagedModel<T> {
length: number;
isResolved(index: number): boolean;
get(index: number): T;
resolve(index: number, cancellationToken: CancellationToken): Promise<T>;
}
export function singlePagePager<T>(elements: T[]): IPager<T> {
return {
firstPage: elements,
total: elements.length,
pageSize: elements.length,
getPage: (pageIndex: number, cancellationToken: CancellationToken): Promise<T[]> => {
return Promise.resolve(elements);
}
};
}
export class PagedModel<T> implements IPagedModel<T> {
private pager: IPager<T>;
private pages: IPage<T>[] = [];
get length(): number { return this.pager.total; }
constructor(arg: IPager<T> | T[]) {
this.pager = Array.isArray(arg) ? singlePagePager<T>(arg) : arg;
const totalPages = Math.ceil(this.pager.total / this.pager.pageSize);
this.pages = [
createPage(this.pager.firstPage.slice()),
...range(totalPages - 1).map(() => createPage<T>())
];
}
isResolved(index: number): boolean {
const pageIndex = Math.floor(index / this.pager.pageSize);
const page = this.pages[pageIndex];
return !!page.isResolved;
}
get(index: number): T {
const pageIndex = Math.floor(index / this.pager.pageSize);
const indexInPage = index % this.pager.pageSize;
const page = this.pages[pageIndex];
return page.elements[indexInPage];
}
resolve(index: number, cancellationToken: CancellationToken): Promise<T> {
if (cancellationToken.isCancellationRequested) {
return Promise.reject(new CancellationError());
}
const pageIndex = Math.floor(index / this.pager.pageSize);
const indexInPage = index % this.pager.pageSize;
const page = this.pages[pageIndex];
if (page.isResolved) {
return Promise.resolve(page.elements[indexInPage]);
}
if (!page.promise) {
page.cts = new CancellationTokenSource();
page.promise = this.pager.getPage(pageIndex, page.cts.token)
.then(elements => {
page.elements = elements;
page.isResolved = true;
page.promise = null;
page.cts = null;
}, err => {
page.isResolved = false;
page.promise = null;
page.cts = null;
return Promise.reject(err);
});
}
const listener = cancellationToken.onCancellationRequested(() => {
if (!page.cts) {
return;
}
page.promiseIndexes.delete(index);
if (page.promiseIndexes.size === 0) {
page.cts.cancel();
}
});
page.promiseIndexes.add(index);
return page.promise.then(() => page.elements[indexInPage])
.finally(() => listener.dispose());
}
}
export class DelayedPagedModel<T> implements IPagedModel<T> {
get length(): number { return this.model.length; }
constructor(private model: IPagedModel<T>, private timeout: number = 500) { }
isResolved(index: number): boolean {
return this.model.isResolved(index);
}
get(index: number): T {
return this.model.get(index);
}
resolve(index: number, cancellationToken: CancellationToken): Promise<T> {
return new Promise((c, e) => {
if (cancellationToken.isCancellationRequested) {
return e(new CancellationError());
}
const timer = setTimeout(() => {
if (cancellationToken.isCancellationRequested) {
return e(new CancellationError());
}
timeoutCancellation.dispose();
this.model.resolve(index, cancellationToken).then(c, e);
}, this.timeout);
const timeoutCancellation = cancellationToken.onCancellationRequested(() => {
clearTimeout(timer);
timeoutCancellation.dispose();
e(new CancellationError());
});
});
}
}
/**
* Similar to array.map, `mapPager` lets you map the elements of an
* abstract paged collection to another type.
*/
export function mapPager<T, R>(pager: IPager<T>, fn: (t: T) => R): IPager<R> {
return {
firstPage: pager.firstPage.map(fn),
total: pager.total,
pageSize: pager.pageSize,
getPage: (pageIndex, token) => pager.getPage(pageIndex, token).then(r => r.map(fn))
};
}
-79
View File
@@ -1,79 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
export const enum ValidationState {
OK = 0,
Info = 1,
Warning = 2,
Error = 3,
Fatal = 4
}
export class ValidationStatus {
private _state: ValidationState;
constructor() {
this._state = ValidationState.OK;
}
public get state(): ValidationState {
return this._state;
}
public set state(value: ValidationState) {
if (value > this._state) {
this._state = value;
}
}
public isOK(): boolean {
return this._state === ValidationState.OK;
}
public isFatal(): boolean {
return this._state === ValidationState.Fatal;
}
}
export interface IProblemReporter {
info(message: string): void;
warn(message: string): void;
error(message: string): void;
fatal(message: string): void;
status: ValidationStatus;
}
export abstract class Parser {
private _problemReporter: IProblemReporter;
constructor(problemReporter: IProblemReporter) {
this._problemReporter = problemReporter;
}
public reset(): void {
this._problemReporter.status.state = ValidationState.OK;
}
public get problemReporter(): IProblemReporter {
return this._problemReporter;
}
public info(message: string): void {
this._problemReporter.info(message);
}
public warn(message: string): void {
this._problemReporter.warn(message);
}
public error(message: string): void {
this._problemReporter.error(message);
}
public fatal(message: string): void {
this._problemReporter.fatal(message);
}
}
File diff suppressed because it is too large Load Diff
-16
View File
@@ -1,16 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
export interface PerformanceMark {
readonly name: string;
readonly startTime: number;
}
export function mark(name: string): void;
/**
* Returns all marks, sorted by `startTime`.
*/
export function getMarks(): PerformanceMark[];
-135
View File
@@ -1,135 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
//@ts-check
'use strict';
// ESM-uncomment-begin
// const module = { exports: {} };
// ESM-uncomment-end
(function () {
/**
* @returns {{mark(name:string):void, getMarks():{name:string, startTime:number}[]}}
*/
function _definePolyfillMarks(timeOrigin) {
const _data = [];
if (typeof timeOrigin === 'number') {
_data.push('code/timeOrigin', timeOrigin);
}
function mark(name) {
_data.push(name, Date.now());
}
function getMarks() {
const result = [];
for (let i = 0; i < _data.length; i += 2) {
result.push({
name: _data[i],
startTime: _data[i + 1],
});
}
return result;
}
return { mark, getMarks };
}
/**
* @returns {{mark(name:string):void, getMarks():{name:string, startTime:number}[]}}
*/
function _define() {
// Identify browser environment when following property is not present
// https://nodejs.org/dist/latest-v16.x/docs/api/perf_hooks.html#performancenodetiming
// @ts-ignore
if (typeof performance === 'object' && typeof performance.mark === 'function' && !performance.nodeTiming) {
// in a browser context, reuse performance-util
if (typeof performance.timeOrigin !== 'number' && !performance.timing) {
// safari & webworker: because there is no timeOrigin and no workaround
// we use the `Date.now`-based polyfill.
return _definePolyfillMarks();
} else {
// use "native" performance for mark and getMarks
return {
mark(name) {
performance.mark(name);
},
getMarks() {
let timeOrigin = performance.timeOrigin;
if (typeof timeOrigin !== 'number') {
// safari: there is no timerOrigin but in renderers there is the timing-property
// see https://bugs.webkit.org/show_bug.cgi?id=174862
timeOrigin = performance.timing.navigationStart || performance.timing.redirectStart || performance.timing.fetchStart;
}
const result = [{ name: 'code/timeOrigin', startTime: Math.round(timeOrigin) }];
for (const entry of performance.getEntriesByType('mark')) {
result.push({
name: entry.name,
startTime: Math.round(timeOrigin + entry.startTime)
});
}
return result;
}
};
}
} else if (typeof process === 'object') {
// node.js: use the normal polyfill but add the timeOrigin
// from the node perf_hooks API as very first mark
const timeOrigin = performance?.timeOrigin ?? Math.round((require.__$__nodeRequire || require)('perf_hooks').performance.timeOrigin);
return _definePolyfillMarks(timeOrigin);
} else {
// unknown environment
console.trace('perf-util loaded in UNKNOWN environment');
return _definePolyfillMarks();
}
}
function _factory(sharedObj) {
if (!sharedObj.MonacoPerformanceMarks) {
sharedObj.MonacoPerformanceMarks = _define();
}
return sharedObj.MonacoPerformanceMarks;
}
// This module can be loaded in an amd and commonjs-context.
// Because we want both instances to use the same perf-data
// we store them globally
// eslint-disable-next-line no-var
var sharedObj;
if (typeof global === 'object') {
// nodejs
sharedObj = global;
} else if (typeof self === 'object') {
// browser
sharedObj = self;
} else {
sharedObj = {};
}
if (typeof define === 'function') {
// amd
define([], function () { return _factory(sharedObj); });
} else if (typeof module === 'object' && typeof module.exports === 'object') {
// commonjs
module.exports = _factory(sharedObj);
} else {
console.trace('perf-util defined in UNKNOWN context (neither requirejs or commonjs)');
// @ts-ignore
sharedObj.perf = _factory(sharedObj);
}
})();
// ESM-uncomment-begin
// export const mark = module.exports.mark;
// export const getMarks = module.exports.getMarks;
// ESM-uncomment-end
-13
View File
@@ -1,13 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
/**
* @returns Returns a random port between 1025 and 65535.
*/
export function randomPort(): number {
const min = 1025;
const max = 65535;
return min + Math.floor((max - min) * Math.random());
}
-252
View File
@@ -1,252 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Iterable } from 'vs/base/common/iterator';
const unset = Symbol('unset');
export interface IPrefixTreeNode<T> {
/** Possible children of the node. */
children?: ReadonlyMap<string, Node<T>>;
/** The value if data exists for this node in the tree. Mutable. */
value: T | undefined;
}
/**
* A simple prefix tree implementation where a value is stored based on
* well-defined prefix segments.
*/
export class WellDefinedPrefixTree<V> {
private readonly root = new Node<V>();
private _size = 0;
public get size() {
return this._size;
}
/** Gets the top-level nodes of the tree */
public get nodes(): Iterable<IPrefixTreeNode<V>> {
return this.root.children?.values() || Iterable.empty();
}
/** Gets the top-level nodes of the tree */
public get entries(): Iterable<[string, IPrefixTreeNode<V>]> {
return this.root.children?.entries() || Iterable.empty();
}
/**
* Inserts a new value in the prefix tree.
* @param onNode - called for each node as we descend to the insertion point,
* including the insertion point itself.
*/
insert(key: Iterable<string>, value: V, onNode?: (n: IPrefixTreeNode<V>) => void): void {
this.opNode(key, n => n._value = value, onNode);
}
/** Mutates a value in the prefix tree. */
mutate(key: Iterable<string>, mutate: (value?: V) => V): void {
this.opNode(key, n => n._value = mutate(n._value === unset ? undefined : n._value));
}
/** Mutates nodes along the path in the prefix tree. */
mutatePath(key: Iterable<string>, mutate: (node: IPrefixTreeNode<V>) => void): void {
this.opNode(key, () => { }, n => mutate(n));
}
/** Deletes a node from the prefix tree, returning the value it contained. */
delete(key: Iterable<string>): V | undefined {
const path = this.getPathToKey(key);
if (!path) {
return;
}
let i = path.length - 1;
const value = path[i].node._value;
if (value === unset) {
return; // not actually a real node
}
this._size--;
path[i].node._value = unset;
for (; i > 0; i--) {
const { node, part } = path[i];
if (node.children?.size || node._value !== unset) {
break;
}
path[i - 1].node.children!.delete(part);
}
return value;
}
/** Deletes a subtree from the prefix tree, returning the values they contained. */
*deleteRecursive(key: Iterable<string>): Iterable<V> {
const path = this.getPathToKey(key);
if (!path) {
return;
}
const subtree = path[path.length - 1].node;
// important: run the deletion before we start to yield results, so that
// it still runs even if the caller doesn't consumer the iterator
for (let i = path.length - 1; i > 0; i--) {
const parent = path[i - 1];
parent.node.children!.delete(path[i].part);
if (parent.node.children!.size > 0 || parent.node._value !== unset) {
break;
}
}
for (const node of bfsIterate(subtree)) {
if (node._value !== unset) {
this._size--;
yield node._value;
}
}
}
/** Gets a value from the tree. */
find(key: Iterable<string>): V | undefined {
let node = this.root;
for (const segment of key) {
const next = node.children?.get(segment);
if (!next) {
return undefined;
}
node = next;
}
return node._value === unset ? undefined : node._value;
}
/** Gets whether the tree has the key, or a parent of the key, already inserted. */
hasKeyOrParent(key: Iterable<string>): boolean {
let node = this.root;
for (const segment of key) {
const next = node.children?.get(segment);
if (!next) {
return false;
}
if (next._value !== unset) {
return true;
}
node = next;
}
return false;
}
/** Gets whether the tree has the given key or any children. */
hasKeyOrChildren(key: Iterable<string>): boolean {
let node = this.root;
for (const segment of key) {
const next = node.children?.get(segment);
if (!next) {
return false;
}
node = next;
}
return true;
}
/** Gets whether the tree has the given key. */
hasKey(key: Iterable<string>): boolean {
let node = this.root;
for (const segment of key) {
const next = node.children?.get(segment);
if (!next) {
return false;
}
node = next;
}
return node._value !== unset;
}
private getPathToKey(key: Iterable<string>) {
const path = [{ part: '', node: this.root }];
let i = 0;
for (const part of key) {
const node = path[i].node.children?.get(part);
if (!node) {
return; // node not in tree
}
path.push({ part, node });
i++;
}
return path;
}
private opNode(key: Iterable<string>, fn: (node: Node<V>) => void, onDescend?: (node: Node<V>) => void): void {
let node = this.root;
for (const part of key) {
if (!node.children) {
const next = new Node<V>();
node.children = new Map([[part, next]]);
node = next;
} else if (!node.children.has(part)) {
const next = new Node<V>();
node.children.set(part, next);
node = next;
} else {
node = node.children.get(part)!;
}
onDescend?.(node);
}
const sizeBefore = node._value === unset ? 0 : 1;
fn(node);
const sizeAfter = node._value === unset ? 0 : 1;
this._size += sizeAfter - sizeBefore;
}
/** Returns an iterable of the tree values in no defined order. */
*values() {
for (const { _value } of bfsIterate(this.root)) {
if (_value !== unset) {
yield _value;
}
}
}
}
function* bfsIterate<T>(root: Node<T>): Iterable<Node<T>> {
const stack = [root];
while (stack.length > 0) {
const node = stack.pop()!;
yield node;
if (node.children) {
for (const child of node.children.values()) {
stack.push(child);
}
}
}
}
class Node<T> implements IPrefixTreeNode<T> {
public children?: Map<string, Node<T>>;
public get value() {
return this._value === unset ? undefined : this._value;
}
public set value(value: T | undefined) {
this._value = value === undefined ? unset : value;
}
public _value: T | typeof unset = unset;
}
-76
View File
@@ -1,76 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { INodeProcess, isMacintosh, isWindows } from 'vs/base/common/platform';
let safeProcess: Omit<INodeProcess, 'arch'> & { arch: string | undefined };
declare const process: INodeProcess;
// Native sandbox environment
const vscodeGlobal = (globalThis as any).vscode;
if (typeof vscodeGlobal !== 'undefined' && typeof vscodeGlobal.process !== 'undefined') {
const sandboxProcess: INodeProcess = 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') {
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
*/
export 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 `{}`.
*/
export const env = safeProcess.env;
/**
* Provides safe access to the `platform` property in node.js, sandboxed or web
* environments.
*/
export const platform = safeProcess.platform;
/**
* Provides safe access to the `arch` method in node.js, sandboxed or web
* environments.
* Note: `arch` is `undefined` in web
*/
export const arch = safeProcess.arch;
-148
View File
@@ -1,148 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IProcessEnvironment, isLinux } from 'vs/base/common/platform';
/**
* Options to be passed to the external program or shell.
*/
export interface CommandOptions {
/**
* The current working directory of the executed program or shell.
* If omitted VSCode's current workspace root is used.
*/
cwd?: string;
/**
* The environment of the executed program or shell. If omitted
* the parent process' environment is used.
*/
env?: { [key: string]: string };
}
export interface Executable {
/**
* The command to be executed. Can be an external program or a shell
* command.
*/
command: string;
/**
* Specifies whether the command is a shell command and therefore must
* be executed in a shell interpreter (e.g. cmd.exe, bash, ...).
*/
isShellCommand: boolean;
/**
* The arguments passed to the command.
*/
args: string[];
/**
* The command options used when the command is executed. Can be omitted.
*/
options?: CommandOptions;
}
export interface ForkOptions extends CommandOptions {
execArgv?: string[];
}
export const enum Source {
stdout,
stderr
}
/**
* The data send via a success callback
*/
export interface SuccessData {
error?: Error;
cmdCode?: number;
terminated?: boolean;
}
/**
* The data send via a error callback
*/
export interface ErrorData {
error?: Error;
terminated?: boolean;
stdout?: string;
stderr?: string;
}
export interface TerminateResponse {
success: boolean;
code?: TerminateResponseCode;
error?: any;
}
export const enum TerminateResponseCode {
Success = 0,
Unknown = 1,
AccessDenied = 2,
ProcessNotFound = 3,
}
export interface ProcessItem {
name: string;
cmd: string;
pid: number;
ppid: number;
load: number;
mem: number;
children?: ProcessItem[];
}
/**
* Sanitizes a VS Code process environment by removing all Electron/VS Code-related values.
*/
export function sanitizeProcessEnvironment(env: IProcessEnvironment, ...preserve: string[]): void {
const set = preserve.reduce<Record<string, boolean>>((set, key) => {
set[key] = true;
return set;
}, {});
const keysToRemove = [
/^ELECTRON_.+$/,
/^VSCODE_(?!(PORTABLE|SHELL_LOGIN|ENV_REPLACE|ENV_APPEND|ENV_PREPEND)).+$/,
/^SNAP(|_.*)$/,
/^GDK_PIXBUF_.+$/,
];
const envKeys = Object.keys(env);
envKeys
.filter(key => !set[key])
.forEach(envKey => {
for (let i = 0; i < keysToRemove.length; i++) {
if (envKey.search(keysToRemove[i]) !== -1) {
delete env[envKey];
break;
}
}
});
}
/**
* Remove dangerous environment variables that have caused crashes
* in forked processes (i.e. in ELECTRON_RUN_AS_NODE processes)
*
* @param env The env object to change
*/
export function removeDangerousEnvVariables(env: IProcessEnvironment | undefined): void {
if (!env) {
return;
}
// Unset `DEBUG`, as an invalid value might lead to process crashes
// See https://github.com/microsoft/vscode/issues/130072
delete env['DEBUG'];
if (isLinux) {
// Unset `LD_PRELOAD`, as it might lead to process crashes
// See https://github.com/microsoft/vscode/issues/134177
delete env['LD_PRELOAD'];
}
}
-314
View File
@@ -1,314 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IStringDictionary } from 'vs/base/common/collections';
import { PlatformName } from 'vs/base/common/platform';
export interface IBuiltInExtension {
readonly name: string;
readonly version: string;
readonly repo: string;
readonly metadata: any;
}
export interface IProductWalkthrough {
id: string;
steps: IProductWalkthroughStep[];
}
export interface IProductWalkthroughStep {
id: string;
title: string;
when: string;
description: string;
media:
| { type: 'image'; path: string | { hc: string; hcLight?: string; light: string; dark: string }; altText: string }
| { type: 'svg'; path: string; altText: string }
| { type: 'markdown'; path: string };
}
export interface IFeaturedExtension {
readonly id: string;
readonly title: string;
readonly description: string;
readonly imagePath: string;
}
export type ConfigurationSyncStore = {
url: string;
insidersUrl: string;
stableUrl: string;
canSwitch?: boolean;
authenticationProviders: IStringDictionary<{ scopes: string[] }>;
};
export type ExtensionUntrustedWorkspaceSupport = {
readonly default?: boolean | 'limited';
readonly override?: boolean | 'limited';
};
export type ExtensionVirtualWorkspaceSupport = {
readonly default?: boolean;
readonly override?: boolean;
};
export interface IProductConfiguration {
readonly version: string;
readonly date?: string;
readonly quality?: string;
readonly commit?: string;
readonly nameShort: string;
readonly nameLong: string;
readonly win32AppUserModelId?: string;
readonly win32MutexName?: string;
readonly win32RegValueName?: string;
readonly applicationName: string;
readonly embedderIdentifier?: string;
readonly urlProtocol: string;
readonly dataFolderName: string; // location for extensions (e.g. ~/.vscode-insiders)
readonly builtInExtensions?: IBuiltInExtension[];
readonly walkthroughMetadata?: IProductWalkthrough[];
readonly featuredExtensions?: IFeaturedExtension[];
readonly downloadUrl?: string;
readonly updateUrl?: string;
readonly webUrl?: string;
readonly webEndpointUrlTemplate?: string;
readonly webviewContentExternalBaseUrlTemplate?: string;
readonly target?: string;
readonly nlsCoreBaseUrl?: string;
readonly settingsSearchBuildId?: number;
readonly settingsSearchUrl?: string;
readonly tasConfig?: {
endpoint: string;
telemetryEventName: string;
assignmentContextTelemetryPropertyName: string;
};
readonly extensionsGallery?: {
readonly serviceUrl: string;
readonly servicePPEUrl?: string;
readonly searchUrl?: string;
readonly itemUrl: string;
readonly publisherUrl: string;
readonly resourceUrlTemplate: string;
readonly controlUrl: string;
readonly nlsBaseUrl: string;
};
readonly extensionRecommendations?: IStringDictionary<IExtensionRecommendations>;
readonly configBasedExtensionTips?: IStringDictionary<IConfigBasedExtensionTip>;
readonly exeBasedExtensionTips?: IStringDictionary<IExeBasedExtensionTip>;
readonly remoteExtensionTips?: IStringDictionary<IRemoteExtensionTip>;
readonly virtualWorkspaceExtensionTips?: IStringDictionary<IVirtualWorkspaceExtensionTip>;
readonly extensionKeywords?: IStringDictionary<string[]>;
readonly keymapExtensionTips?: readonly string[];
readonly webExtensionTips?: readonly string[];
readonly languageExtensionTips?: readonly string[];
readonly trustedExtensionUrlPublicKeys?: IStringDictionary<string[]>;
readonly trustedExtensionAuthAccess?: string[] | IStringDictionary<string[]>;
readonly trustedExtensionProtocolHandlers?: readonly string[];
readonly commandPaletteSuggestedCommandIds?: string[];
readonly crashReporter?: {
readonly companyName: string;
readonly productName: string;
};
readonly removeTelemetryMachineId?: boolean;
readonly enabledTelemetryLevels?: { error: boolean; usage: boolean };
readonly enableTelemetry?: boolean;
readonly openToWelcomeMainPage?: boolean;
readonly aiConfig?: {
readonly ariaKey: string;
};
readonly documentationUrl?: string;
readonly serverDocumentationUrl?: string;
readonly releaseNotesUrl?: string;
readonly keyboardShortcutsUrlMac?: string;
readonly keyboardShortcutsUrlLinux?: string;
readonly keyboardShortcutsUrlWin?: string;
readonly introductoryVideosUrl?: string;
readonly tipsAndTricksUrl?: string;
readonly newsletterSignupUrl?: string;
readonly youTubeUrl?: string;
readonly requestFeatureUrl?: string;
readonly reportIssueUrl?: string;
readonly reportMarketplaceIssueUrl?: string;
readonly licenseUrl?: string;
readonly serverLicenseUrl?: string;
readonly privacyStatementUrl?: string;
readonly showTelemetryOptOut?: boolean;
readonly serverGreeting?: string[];
readonly serverLicense?: string[];
readonly serverLicensePrompt?: string;
readonly serverApplicationName: string;
readonly serverDataFolderName?: string;
readonly tunnelApplicationName?: string;
readonly tunnelApplicationConfig?: ITunnelApplicationConfig;
readonly npsSurveyUrl?: string;
readonly cesSurveyUrl?: string;
readonly surveys?: readonly ISurveyData[];
readonly checksums?: { [path: string]: string };
readonly checksumFailMoreInfoUrl?: string;
readonly appCenter?: IAppCenterConfiguration;
readonly portable?: string;
readonly extensionKind?: { readonly [extensionId: string]: ('ui' | 'workspace' | 'web')[] };
readonly extensionPointExtensionKind?: { readonly [extensionPointId: string]: ('ui' | 'workspace' | 'web')[] };
readonly extensionSyncedKeys?: { readonly [extensionId: string]: string[] };
readonly extensionsEnabledWithApiProposalVersion?: string[];
readonly extensionEnabledApiProposals?: { readonly [extensionId: string]: string[] };
readonly extensionUntrustedWorkspaceSupport?: { readonly [extensionId: string]: ExtensionUntrustedWorkspaceSupport };
readonly extensionVirtualWorkspacesSupport?: { readonly [extensionId: string]: ExtensionVirtualWorkspaceSupport };
readonly msftInternalDomains?: string[];
readonly linkProtectionTrustedDomains?: readonly string[];
readonly 'configurationSync.store'?: ConfigurationSyncStore;
readonly 'editSessions.store'?: Omit<ConfigurationSyncStore, 'insidersUrl' | 'stableUrl'>;
readonly darwinUniversalAssetId?: string;
readonly profileTemplatesUrl?: string;
readonly commonlyUsedSettings?: string[];
readonly aiGeneratedWorkspaceTrust?: IAiGeneratedWorkspaceTrust;
readonly gitHubEntitlement?: IGitHubEntitlement;
readonly chatWelcomeView?: IChatWelcomeView;
readonly chatParticipantRegistry?: string;
}
export interface ITunnelApplicationConfig {
authenticationProviders: IStringDictionary<{ scopes: string[] }>;
editorWebUrl: string;
extension: IRemoteExtensionTip;
}
export interface IExtensionRecommendations {
readonly onFileOpen: IFileOpenCondition[];
readonly onSettingsEditorOpen?: ISettingsEditorOpenCondition;
}
export interface ISettingsEditorOpenCondition {
readonly prerelease?: boolean | string;
}
export interface IExtensionRecommendationCondition {
readonly important?: boolean;
readonly whenInstalled?: string[];
readonly whenNotInstalled?: string[];
}
export type IFileOpenCondition = IFileLanguageCondition | IFilePathCondition | IFileContentCondition;
export interface IFileLanguageCondition extends IExtensionRecommendationCondition {
readonly languages: string[];
}
export interface IFilePathCondition extends IExtensionRecommendationCondition {
readonly pathGlob: string;
}
export type IFileContentCondition = (IFileLanguageCondition | IFilePathCondition) & { readonly contentPattern: string };
export interface IAppCenterConfiguration {
readonly 'win32-x64': string;
readonly 'win32-arm64': string;
readonly 'linux-x64': string;
readonly 'darwin': string;
readonly 'darwin-universal': string;
readonly 'darwin-arm64': string;
}
export interface IConfigBasedExtensionTip {
configPath: string;
configName: string;
configScheme?: string;
recommendations: IStringDictionary<{
name: string;
contentPattern?: string;
important?: boolean;
isExtensionPack?: boolean;
whenNotInstalled?: string[];
}>;
}
export interface IExeBasedExtensionTip {
friendlyName: string;
windowsPath?: string;
important?: boolean;
recommendations: IStringDictionary<{ name: string; important?: boolean; isExtensionPack?: boolean; whenNotInstalled?: string[] }>;
}
export interface IRemoteExtensionTip {
friendlyName: string;
extensionId: string;
supportedPlatforms?: PlatformName[];
startEntry?: {
helpLink: string;
startConnectLabel: string;
startCommand: string;
priority: number;
};
}
export interface IVirtualWorkspaceExtensionTip {
friendlyName: string;
extensionId: string;
supportedPlatforms?: PlatformName[];
startEntry: {
helpLink: string;
startConnectLabel: string;
startCommand: string;
priority: number;
};
}
export interface ISurveyData {
surveyId: string;
surveyUrl: string;
languageId: string;
editCount: number;
userProbability: number;
}
export interface IAiGeneratedWorkspaceTrust {
readonly title: string;
readonly checkboxText: string;
readonly trustOption: string;
readonly dontTrustOption: string;
readonly startupTrustRequestLearnMore: string;
}
export interface IGitHubEntitlement {
providerId: string;
command: { title: string; titleWithoutPlaceHolder: string; action: string; when: string };
entitlementUrl: string;
extensionId: string;
enablementKey: string;
confirmationMessage: string;
confirmationAction: string;
}
export interface IChatWelcomeView {
welcomeViewId: string;
welcomeViewTitle: string;
welcomeViewContent: string;
}
-60
View File
@@ -1,60 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
export interface IRange {
start: number;
end: number;
}
export interface IRangedGroup {
range: IRange;
size: number;
}
export namespace Range {
/**
* Returns the intersection between two ranges as a range itself.
* Returns `{ start: 0, end: 0 }` if the intersection is empty.
*/
export function intersect(one: IRange, other: IRange): IRange {
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 };
}
export function isEmpty(range: IRange): boolean {
return range.end - range.start <= 0;
}
export function intersects(one: IRange, other: IRange): boolean {
return !isEmpty(intersect(one, other));
}
export function relativeComplement(one: IRange, other: IRange): IRange[] {
const result: IRange[] = [];
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;
}
}
-48
View File
@@ -1,48 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as strings from './strings';
export function buildReplaceStringWithCasePreserved(matches: string[] | null, pattern: string): string {
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 (strings.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: string[], pattern: string, specialCharacter: string): boolean {
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: string[], pattern: string, specialCharacter: string): string {
const splitPatternAtSpecialCharacter = pattern.split(specialCharacter);
const splitMatchAtSpecialCharacter = matches[0].split(specialCharacter);
let replaceString: string = '';
splitPatternAtSpecialCharacter.forEach((splitValue, index) => {
replaceString += buildReplaceStringWithCasePreserved([splitMatchAtSpecialCharacter[index]], splitValue) + specialCharacter;
});
return replaceString.slice(0, -1);
}
-56
View File
@@ -1,56 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as strings from 'vs/base/common/strings';
enum Severity {
Ignore = 0,
Info = 1,
Warning = 2,
Error = 3
}
namespace 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.
*/
export function fromValue(value: string): Severity {
if (!value) {
return Severity.Ignore;
}
if (strings.equalsIgnoreCase(_error, value)) {
return Severity.Error;
}
if (strings.equalsIgnoreCase(_warning, value) || strings.equalsIgnoreCase(_warn, value)) {
return Severity.Warning;
}
if (strings.equalsIgnoreCase(_info, value)) {
return Severity.Info;
}
return Severity.Ignore;
}
export function toString(severity: Severity): string {
switch (severity) {
case Severity.Error: return _error;
case Severity.Warning: return _warning;
case Severity.Info: return _info;
default: return _ignore;
}
}
}
export default Severity;
-204
View File
@@ -1,204 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
class Node<K, V> {
readonly forward: Node<K, V>[];
constructor(readonly level: number, readonly key: K, public value: V) {
this.forward = [];
}
}
const NIL: undefined = undefined;
interface Comparator<K> {
(a: K, b: K): number;
}
export class SkipList<K, V> implements Map<K, V> {
readonly [Symbol.toStringTag] = 'SkipList';
private _maxLevel: number;
private _level: number = 0;
private _header: Node<K, V>;
private _size: number = 0;
/**
*
* @param capacity Capacity at which the list performs best
*/
constructor(
readonly comparator: (a: K, b: K) => number,
capacity: number = 2 ** 16
) {
this._maxLevel = Math.max(1, Math.log2(capacity) | 0);
this._header = <any>new Node(this._maxLevel, NIL, NIL);
}
get size(): number {
return this._size;
}
clear(): void {
this._header = <any>new Node(this._maxLevel, NIL, NIL);
this._size = 0;
}
has(key: K): boolean {
return Boolean(SkipList._search(this, key, this.comparator));
}
get(key: K): V | undefined {
return SkipList._search(this, key, this.comparator)?.value;
}
set(key: K, value: V): this {
if (SkipList._insert(this, key, value, this.comparator)) {
this._size += 1;
}
return this;
}
delete(key: K): boolean {
const didDelete = SkipList._delete(this, key, this.comparator);
if (didDelete) {
this._size -= 1;
}
return didDelete;
}
// --- iteration
forEach(callbackfn: (value: V, key: K, map: Map<K, V>) => void, thisArg?: any): void {
let node = this._header.forward[0];
while (node) {
callbackfn.call(thisArg, node.value, node.key, this);
node = node.forward[0];
}
}
[Symbol.iterator](): IterableIterator<[K, V]> {
return this.entries();
}
*entries(): IterableIterator<[K, V]> {
let node = this._header.forward[0];
while (node) {
yield [node.key, node.value];
node = node.forward[0];
}
}
*keys(): IterableIterator<K> {
let node = this._header.forward[0];
while (node) {
yield node.key;
node = node.forward[0];
}
}
*values(): IterableIterator<V> {
let node = this._header.forward[0];
while (node) {
yield node.value;
node = node.forward[0];
}
}
toString(): string {
// debug string...
let result = '[SkipList]:';
let node = this._header.forward[0];
while (node) {
result += `node(${node.key}, ${node.value}, lvl:${node.level})`;
node = node.forward[0];
}
return result;
}
// from https://www.epaperpress.com/sortsearch/download/skiplist.pdf
private static _search<K, V>(list: SkipList<K, V>, searchKey: K, comparator: Comparator<K>) {
let x = list._header;
for (let i = list._level - 1; i >= 0; i--) {
while (x.forward[i] && comparator(x.forward[i].key, searchKey) < 0) {
x = x.forward[i];
}
}
x = x.forward[0];
if (x && comparator(x.key, searchKey) === 0) {
return x;
}
return undefined;
}
private static _insert<K, V>(list: SkipList<K, V>, searchKey: K, value: V, comparator: Comparator<K>) {
const update: Node<K, V>[] = [];
let x = list._header;
for (let i = list._level - 1; i >= 0; i--) {
while (x.forward[i] && comparator(x.forward[i].key, searchKey) < 0) {
x = x.forward[i];
}
update[i] = x;
}
x = x.forward[0];
if (x && comparator(x.key, searchKey) === 0) {
// update
x.value = value;
return false;
} else {
// insert
const lvl = SkipList._randomLevel(list);
if (lvl > list._level) {
for (let i = list._level; i < lvl; i++) {
update[i] = list._header;
}
list._level = lvl;
}
x = new Node<K, V>(lvl, searchKey, value);
for (let i = 0; i < lvl; i++) {
x.forward[i] = update[i].forward[i];
update[i].forward[i] = x;
}
return true;
}
}
private static _randomLevel(list: SkipList<any, any>, p: number = 0.5): number {
let lvl = 1;
while (Math.random() < p && lvl < list._maxLevel) {
lvl += 1;
}
return lvl;
}
private static _delete<K, V>(list: SkipList<K, V>, searchKey: K, comparator: Comparator<K>) {
const update: Node<K, V>[] = [];
let x = list._header;
for (let i = list._level - 1; i >= 0; i--) {
while (x.forward[i] && comparator(x.forward[i].key, searchKey) < 0) {
x = x.forward[i];
}
update[i] = x;
}
x = x.forward[0];
if (!x || comparator(x.key, searchKey) !== 0) {
// not found
return false;
}
for (let i = 0; i < list._level; i++) {
if (update[i].forward[i] !== x) {
break;
}
update[i].forward[i] = x.forward[i];
}
while (list._level > 0 && list._header.forward[list._level - 1] === NIL) {
list._level -= 1;
}
return true;
}
}
-772
View File
@@ -1,772 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { CancellationToken } from 'vs/base/common/cancellation';
import { onUnexpectedError } from 'vs/base/common/errors';
import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle';
/**
* The payload that flows in readable stream events.
*/
export type ReadableStreamEventPayload<T> = T | Error | 'end';
export interface ReadableStreamEvents<T> {
/**
* The 'data' event is emitted whenever the stream is
* relinquishing ownership of a chunk of data to a consumer.
*
* NOTE: PLEASE UNDERSTAND THAT ADDING A DATA LISTENER CAN
* TURN THE STREAM INTO FLOWING MODE. IT IS THEREFOR THE
* LAST LISTENER THAT SHOULD BE ADDED AND NOT THE FIRST
*
* Use `listenStream` as a helper method to listen to
* stream events in the right order.
*/
on(event: 'data', callback: (data: T) => void): void;
/**
* Emitted when any error occurs.
*/
on(event: 'error', callback: (err: Error) => void): void;
/**
* The 'end' event is emitted when there is no more data
* to be consumed from the stream. The 'end' event will
* not be emitted unless the data is completely consumed.
*/
on(event: 'end', callback: () => void): void;
}
/**
* A interface that emulates the API shape of a node.js readable
* stream for use in native and web environments.
*/
export interface ReadableStream<T> extends ReadableStreamEvents<T> {
/**
* Stops emitting any events until resume() is called.
*/
pause(): void;
/**
* Starts emitting events again after pause() was called.
*/
resume(): void;
/**
* Destroys the stream and stops emitting any event.
*/
destroy(): void;
/**
* Allows to remove a listener that was previously added.
*/
removeListener(event: string, callback: Function): void;
}
/**
* A interface that emulates the API shape of a node.js readable
* for use in native and web environments.
*/
export interface Readable<T> {
/**
* Read data from the underlying source. Will return
* null to indicate that no more data can be read.
*/
read(): T | null;
}
export function isReadable<T>(obj: unknown): obj is Readable<T> {
const candidate = obj as Readable<T> | undefined;
if (!candidate) {
return false;
}
return typeof candidate.read === 'function';
}
/**
* A interface that emulates the API shape of a node.js writeable
* stream for use in native and web environments.
*/
export interface WriteableStream<T> extends ReadableStream<T> {
/**
* Writing data to the stream will trigger the on('data')
* event listener if the stream is flowing and buffer the
* data otherwise until the stream is flowing.
*
* If a `highWaterMark` is configured and writing to the
* stream reaches this mark, a promise will be returned
* that should be awaited on before writing more data.
* Otherwise there is a risk of buffering a large number
* of data chunks without consumer.
*/
write(data: T): void | Promise<void>;
/**
* Signals an error to the consumer of the stream via the
* on('error') handler if the stream is flowing.
*
* NOTE: call `end` to signal that the stream has ended,
* this DOES NOT happen automatically from `error`.
*/
error(error: Error): void;
/**
* Signals the end of the stream to the consumer. If the
* result is provided, will trigger the on('data') event
* listener if the stream is flowing and buffer the data
* otherwise until the stream is flowing.
*/
end(result?: T): void;
}
/**
* A stream that has a buffer already read. Returns the original stream
* that was read as well as the chunks that got read.
*
* The `ended` flag indicates if the stream has been fully consumed.
*/
export interface ReadableBufferedStream<T> {
/**
* The original stream that is being read.
*/
stream: ReadableStream<T>;
/**
* An array of chunks already read from this stream.
*/
buffer: T[];
/**
* Signals if the stream has ended or not. If not, consumers
* should continue to read from the stream until consumed.
*/
ended: boolean;
}
export function isReadableStream<T>(obj: unknown): obj is ReadableStream<T> {
const candidate = obj as ReadableStream<T> | undefined;
if (!candidate) {
return false;
}
return [candidate.on, candidate.pause, candidate.resume, candidate.destroy].every(fn => typeof fn === 'function');
}
export function isReadableBufferedStream<T>(obj: unknown): obj is ReadableBufferedStream<T> {
const candidate = obj as ReadableBufferedStream<T> | undefined;
if (!candidate) {
return false;
}
return isReadableStream(candidate.stream) && Array.isArray(candidate.buffer) && typeof candidate.ended === 'boolean';
}
export interface IReducer<T, R = T> {
(data: T[]): R;
}
export interface IDataTransformer<Original, Transformed> {
(data: Original): Transformed;
}
export interface IErrorTransformer {
(error: Error): Error;
}
export interface ITransformer<Original, Transformed> {
data: IDataTransformer<Original, Transformed>;
error?: IErrorTransformer;
}
export function newWriteableStream<T>(reducer: IReducer<T>, options?: WriteableStreamOptions): WriteableStream<T> {
return new WriteableStreamImpl<T>(reducer, options);
}
export interface WriteableStreamOptions {
/**
* The number of objects to buffer before WriteableStream#write()
* signals back that the buffer is full. Can be used to reduce
* the memory pressure when the stream is not flowing.
*/
highWaterMark?: number;
}
class WriteableStreamImpl<T> implements WriteableStream<T> {
private readonly state = {
flowing: false,
ended: false,
destroyed: false
};
private readonly buffer = {
data: [] as T[],
error: [] as Error[]
};
private readonly listeners = {
data: [] as { (data: T): void }[],
error: [] as { (error: Error): void }[],
end: [] as { (): void }[]
};
private readonly pendingWritePromises: Function[] = [];
constructor(private reducer: IReducer<T>, private options?: WriteableStreamOptions) { }
pause(): void {
if (this.state.destroyed) {
return;
}
this.state.flowing = false;
}
resume(): void {
if (this.state.destroyed) {
return;
}
if (!this.state.flowing) {
this.state.flowing = true;
// emit buffered events
this.flowData();
this.flowErrors();
this.flowEnd();
}
}
write(data: T): void | Promise<void> {
if (this.state.destroyed) {
return;
}
// flowing: directly send the data to listeners
if (this.state.flowing) {
this.emitData(data);
}
// not yet flowing: buffer data until flowing
else {
this.buffer.data.push(data);
// highWaterMark: if configured, signal back when buffer reached limits
if (typeof this.options?.highWaterMark === 'number' && this.buffer.data.length > this.options.highWaterMark) {
return new Promise(resolve => this.pendingWritePromises.push(resolve));
}
}
}
error(error: Error): void {
if (this.state.destroyed) {
return;
}
// flowing: directly send the error to listeners
if (this.state.flowing) {
this.emitError(error);
}
// not yet flowing: buffer errors until flowing
else {
this.buffer.error.push(error);
}
}
end(result?: T): void {
if (this.state.destroyed) {
return;
}
// end with data if provided
if (typeof result !== 'undefined') {
this.write(result);
}
// flowing: send end event to listeners
if (this.state.flowing) {
this.emitEnd();
this.destroy();
}
// not yet flowing: remember state
else {
this.state.ended = true;
}
}
private emitData(data: T): void {
this.listeners.data.slice(0).forEach(listener => listener(data)); // slice to avoid listener mutation from delivering event
}
private emitError(error: Error): void {
if (this.listeners.error.length === 0) {
onUnexpectedError(error); // nobody listened to this error so we log it as unexpected
} else {
this.listeners.error.slice(0).forEach(listener => listener(error)); // slice to avoid listener mutation from delivering event
}
}
private emitEnd(): void {
this.listeners.end.slice(0).forEach(listener => listener()); // slice to avoid listener mutation from delivering event
}
on(event: 'data', callback: (data: T) => void): void;
on(event: 'error', callback: (err: Error) => void): void;
on(event: 'end', callback: () => void): void;
on(event: 'data' | 'error' | 'end', callback: (arg0?: any) => void): void {
if (this.state.destroyed) {
return;
}
switch (event) {
case 'data':
this.listeners.data.push(callback);
// switch into flowing mode as soon as the first 'data'
// listener is added and we are not yet in flowing mode
this.resume();
break;
case 'end':
this.listeners.end.push(callback);
// emit 'end' event directly if we are flowing
// and the end has already been reached
//
// finish() when it went through
if (this.state.flowing && this.flowEnd()) {
this.destroy();
}
break;
case 'error':
this.listeners.error.push(callback);
// emit buffered 'error' events unless done already
// now that we know that we have at least one listener
if (this.state.flowing) {
this.flowErrors();
}
break;
}
}
removeListener(event: string, callback: Function): void {
if (this.state.destroyed) {
return;
}
let listeners: unknown[] | undefined = undefined;
switch (event) {
case 'data':
listeners = this.listeners.data;
break;
case 'end':
listeners = this.listeners.end;
break;
case 'error':
listeners = this.listeners.error;
break;
}
if (listeners) {
const index = listeners.indexOf(callback);
if (index >= 0) {
listeners.splice(index, 1);
}
}
}
private flowData(): void {
if (this.buffer.data.length > 0) {
const fullDataBuffer = this.reducer(this.buffer.data);
this.emitData(fullDataBuffer);
this.buffer.data.length = 0;
// When the buffer is empty, resolve all pending writers
const pendingWritePromises = [...this.pendingWritePromises];
this.pendingWritePromises.length = 0;
pendingWritePromises.forEach(pendingWritePromise => pendingWritePromise());
}
}
private flowErrors(): void {
if (this.listeners.error.length > 0) {
for (const error of this.buffer.error) {
this.emitError(error);
}
this.buffer.error.length = 0;
}
}
private flowEnd(): boolean {
if (this.state.ended) {
this.emitEnd();
return this.listeners.end.length > 0;
}
return false;
}
destroy(): void {
if (!this.state.destroyed) {
this.state.destroyed = true;
this.state.ended = true;
this.buffer.data.length = 0;
this.buffer.error.length = 0;
this.listeners.data.length = 0;
this.listeners.error.length = 0;
this.listeners.end.length = 0;
this.pendingWritePromises.length = 0;
}
}
}
/**
* Helper to fully read a T readable into a T.
*/
export function consumeReadable<T>(readable: Readable<T>, reducer: IReducer<T>): T {
const chunks: T[] = [];
let chunk: T | null;
while ((chunk = readable.read()) !== null) {
chunks.push(chunk);
}
return reducer(chunks);
}
/**
* Helper to read a T readable up to a maximum of chunks. If the limit is
* reached, will return a readable instead to ensure all data can still
* be read.
*/
export function peekReadable<T>(readable: Readable<T>, reducer: IReducer<T>, maxChunks: number): T | Readable<T> {
const chunks: T[] = [];
let chunk: T | null | undefined = undefined;
while ((chunk = readable.read()) !== null && chunks.length < maxChunks) {
chunks.push(chunk);
}
// If the last chunk is null, it means we reached the end of
// the readable and return all the data at once
if (chunk === null && chunks.length > 0) {
return reducer(chunks);
}
// Otherwise, we still have a chunk, it means we reached the maxChunks
// value and as such we return a new Readable that first returns
// the existing read chunks and then continues with reading from
// the underlying readable.
return {
read: () => {
// First consume chunks from our array
if (chunks.length > 0) {
return chunks.shift()!;
}
// Then ensure to return our last read chunk
if (typeof chunk !== 'undefined') {
const lastReadChunk = chunk;
// explicitly use undefined here to indicate that we consumed
// the chunk, which could have either been null or valued.
chunk = undefined;
return lastReadChunk;
}
// Finally delegate back to the Readable
return readable.read();
}
};
}
/**
* Helper to fully read a T stream into a T or consuming
* a stream fully, awaiting all the events without caring
* about the data.
*/
export function consumeStream<T, R = T>(stream: ReadableStreamEvents<T>, reducer: IReducer<T, R>): Promise<R>;
export function consumeStream(stream: ReadableStreamEvents<unknown>): Promise<undefined>;
export function consumeStream<T, R = T>(stream: ReadableStreamEvents<T>, reducer?: IReducer<T, R>): Promise<R | undefined> {
return new Promise((resolve, reject) => {
const chunks: T[] = [];
listenStream(stream, {
onData: chunk => {
if (reducer) {
chunks.push(chunk);
}
},
onError: error => {
if (reducer) {
reject(error);
} else {
resolve(undefined);
}
},
onEnd: () => {
if (reducer) {
resolve(reducer(chunks));
} else {
resolve(undefined);
}
}
});
});
}
export interface IStreamListener<T> {
/**
* The 'data' event is emitted whenever the stream is
* relinquishing ownership of a chunk of data to a consumer.
*/
onData(data: T): void;
/**
* Emitted when any error occurs.
*/
onError(err: Error): void;
/**
* The 'end' event is emitted when there is no more data
* to be consumed from the stream. The 'end' event will
* not be emitted unless the data is completely consumed.
*/
onEnd(): void;
}
/**
* Helper to listen to all events of a T stream in proper order.
*/
export function listenStream<T>(stream: ReadableStreamEvents<T>, listener: IStreamListener<T>, token?: CancellationToken): void {
stream.on('error', error => {
if (!token?.isCancellationRequested) {
listener.onError(error);
}
});
stream.on('end', () => {
if (!token?.isCancellationRequested) {
listener.onEnd();
}
});
// Adding the `data` listener will turn the stream
// into flowing mode. As such it is important to
// add this listener last (DO NOT CHANGE!)
stream.on('data', data => {
if (!token?.isCancellationRequested) {
listener.onData(data);
}
});
}
/**
* Helper to peek up to `maxChunks` into a stream. The return type signals if
* the stream has ended or not. If not, caller needs to add a `data` listener
* to continue reading.
*/
export function peekStream<T>(stream: ReadableStream<T>, maxChunks: number): Promise<ReadableBufferedStream<T>> {
return new Promise((resolve, reject) => {
const streamListeners = new DisposableStore();
const buffer: T[] = [];
// Data Listener
const dataListener = (chunk: T) => {
// Add to buffer
buffer.push(chunk);
// We reached maxChunks and thus need to return
if (buffer.length > maxChunks) {
// Dispose any listeners and ensure to pause the
// stream so that it can be consumed again by caller
streamListeners.dispose();
stream.pause();
return resolve({ stream, buffer, ended: false });
}
};
// Error Listener
const errorListener = (error: Error) => {
streamListeners.dispose();
return reject(error);
};
// End Listener
const endListener = () => {
streamListeners.dispose();
return resolve({ stream, buffer, ended: true });
};
streamListeners.add(toDisposable(() => stream.removeListener('error', errorListener)));
stream.on('error', errorListener);
streamListeners.add(toDisposable(() => stream.removeListener('end', endListener)));
stream.on('end', endListener);
// Important: leave the `data` listener last because
// this can turn the stream into flowing mode and we
// want `error` events to be received as well.
streamListeners.add(toDisposable(() => stream.removeListener('data', dataListener)));
stream.on('data', dataListener);
});
}
/**
* Helper to create a readable stream from an existing T.
*/
export function toStream<T>(t: T, reducer: IReducer<T>): ReadableStream<T> {
const stream = newWriteableStream<T>(reducer);
stream.end(t);
return stream;
}
/**
* Helper to create an empty stream
*/
export function emptyStream(): ReadableStream<never> {
const stream = newWriteableStream<never>(() => { throw new Error('not supported'); });
stream.end();
return stream;
}
/**
* Helper to convert a T into a Readable<T>.
*/
export function toReadable<T>(t: T): Readable<T> {
let consumed = false;
return {
read: () => {
if (consumed) {
return null;
}
consumed = true;
return t;
}
};
}
/**
* Helper to transform a readable stream into another stream.
*/
export function transform<Original, Transformed>(stream: ReadableStreamEvents<Original>, transformer: ITransformer<Original, Transformed>, reducer: IReducer<Transformed>): ReadableStream<Transformed> {
const target = newWriteableStream<Transformed>(reducer);
listenStream(stream, {
onData: data => target.write(transformer.data(data)),
onError: error => target.error(transformer.error ? transformer.error(error) : error),
onEnd: () => target.end()
});
return target;
}
/**
* Helper to take an existing readable that will
* have a prefix injected to the beginning.
*/
export function prefixedReadable<T>(prefix: T, readable: Readable<T>, reducer: IReducer<T>): Readable<T> {
let prefixHandled = false;
return {
read: () => {
const chunk = readable.read();
// Handle prefix only once
if (!prefixHandled) {
prefixHandled = true;
// If we have also a read-result, make
// sure to reduce it to a single result
if (chunk !== null) {
return reducer([prefix, chunk]);
}
// Otherwise, just return prefix directly
return prefix;
}
return chunk;
}
};
}
/**
* Helper to take an existing stream that will
* have a prefix injected to the beginning.
*/
export function prefixedStream<T>(prefix: T, stream: ReadableStream<T>, reducer: IReducer<T>): ReadableStream<T> {
let prefixHandled = false;
const target = newWriteableStream<T>(reducer);
listenStream(stream, {
onData: data => {
// Handle prefix only once
if (!prefixHandled) {
prefixHandled = true;
return target.write(reducer([prefix, data]));
}
return target.write(data);
},
onError: error => target.error(error),
onEnd: () => {
// Handle prefix only once
if (!prefixHandled) {
prefixHandled = true;
target.write(prefix);
}
target.end();
}
});
return target;
}
-240
View File
@@ -1,240 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { CancellationToken } from 'vs/base/common/cancellation';
type SparseEmbedding = Record</* word */ string, /* weight */number>;
type TermFrequencies = Map</* word */ string, /*occurrences*/ number>;
type DocumentOccurrences = Map</* word */ string, /*documentOccurrences*/ number>;
function countMapFrom<K>(values: Iterable<K>): Map<K, number> {
const map = new Map<K, number>();
for (const value of values) {
map.set(value, (map.get(value) ?? 0) + 1);
}
return map;
}
interface DocumentChunkEntry {
readonly text: string;
readonly tf: TermFrequencies;
}
export interface TfIdfDocument {
readonly key: string;
readonly textChunks: readonly string[];
}
export interface TfIdfScore {
readonly key: string;
/**
* An unbounded number.
*/
readonly score: number;
}
export interface NormalizedTfIdfScore {
readonly key: string;
/**
* A number between 0 and 1.
*/
readonly score: number;
}
/**
* 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.
*/
export class TfIdfCalculator {
calculateScores(query: string, token: CancellationToken): TfIdfScore[] {
const embedding = this.computeEmbedding(query);
const idfCache = new Map<string, number>();
const scores: TfIdfScore[] = [];
// 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.
*/
private static termFrequencies(input: string): TermFrequencies {
return countMapFrom(TfIdfCalculator.splitTerms(input));
}
/**
* Break a string into terms (words).
*/
private static *splitTerms(input: string): Iterable<string> {
const normalize = (word: string) => 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);
}
}
}
}
}
/**
* Total number of chunks
*/
private chunkCount = 0;
private readonly chunkOccurrences: DocumentOccurrences = new Map</* word */ string, /*documentOccurrences*/ number>();
private readonly documents = new Map</* key */ string, {
readonly chunks: ReadonlyArray<DocumentChunkEntry>;
}>();
updateDocuments(documents: ReadonlyArray<TfIdfDocument>): this {
for (const { key } of documents) {
this.deleteDocument(key);
}
for (const doc of documents) {
const chunks: Array<{ text: string; tf: TermFrequencies }> = [];
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: string) {
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);
}
}
}
}
}
private computeSimilarityScore(chunk: DocumentChunkEntry, queryEmbedding: SparseEmbedding, idfCache: Map<string, number>): number {
// 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;
}
private computeEmbedding(input: string): SparseEmbedding {
const tf = TfIdfCalculator.termFrequencies(input);
return this.computeTfidf(tf);
}
private computeIdf(term: string): number {
const chunkOccurrences = this.chunkOccurrences.get(term) ?? 0;
return chunkOccurrences > 0
? Math.log((this.chunkCount + 1) / chunkOccurrences)
: 0;
}
private computeTfidf(termFrequencies: TermFrequencies): SparseEmbedding {
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
*/
export function normalizeTfIdfScores(scores: TfIdfScore[]): NormalizedTfIdfScore[] {
// copy of scores
const result = scores.slice(0) as { score: number }[];
// 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 as TfIdfScore[];
}
-250
View File
@@ -1,250 +0,0 @@
/*---------------------------------------------------------------------------------------------
* 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.
*/
export function isString(str: unknown): str is string {
return (typeof str === 'string');
}
/**
* @returns whether the provided parameter is a JavaScript Array and each element in the array is a string.
*/
export function isStringArray(value: unknown): value is string[] {
return Array.isArray(value) && (<unknown[]>value).every(elem => isString(elem));
}
/**
* @returns whether the provided parameter is of type `object` but **not**
* `null`, an `array`, a `regexp`, nor a `date`.
*/
export function isObject(obj: unknown): obj is Object {
// 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
*/
export function isTypedArray(obj: unknown): obj is Object {
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.
*/
export function isNumber(obj: unknown): obj is number {
return (typeof obj === 'number' && !isNaN(obj));
}
/**
* @returns whether the provided parameter is an Iterable, casting to the given generic
*/
export function isIterable<T>(obj: unknown): obj is Iterable<T> {
return !!obj && typeof (obj as any)[Symbol.iterator] === 'function';
}
/**
* @returns whether the provided parameter is a JavaScript Boolean or not.
*/
export function isBoolean(obj: unknown): obj is boolean {
return (obj === true || obj === false);
}
/**
* @returns whether the provided parameter is undefined.
*/
export function isUndefined(obj: unknown): obj is undefined {
return (typeof obj === 'undefined');
}
/**
* @returns whether the provided parameter is defined.
*/
export function isDefined<T>(arg: T | null | undefined): arg is T {
return !isUndefinedOrNull(arg);
}
/**
* @returns whether the provided parameter is undefined or null.
*/
export function isUndefinedOrNull(obj: unknown): obj is undefined | null {
return (isUndefined(obj) || obj === null);
}
export function assertType(condition: unknown, type?: string): asserts condition {
if (!condition) {
throw new Error(type ? `Unexpected type, expected '${type}'` : 'Unexpected type');
}
}
/**
* Asserts that the argument passed in is neither undefined nor null.
*/
export function assertIsDefined<T>(arg: T | null | undefined): T {
if (isUndefinedOrNull(arg)) {
throw new Error('Assertion Failed: argument is undefined or null');
}
return arg;
}
/**
* Asserts that each argument passed in is neither undefined nor null.
*/
export function assertAllDefined<T1, T2>(t1: T1 | null | undefined, t2: T2 | null | undefined): [T1, T2];
export function assertAllDefined<T1, T2, T3>(t1: T1 | null | undefined, t2: T2 | null | undefined, t3: T3 | null | undefined): [T1, T2, T3];
export function assertAllDefined<T1, T2, T3, T4>(t1: T1 | null | undefined, t2: T2 | null | undefined, t3: T3 | null | undefined, t4: T4 | null | undefined): [T1, T2, T3, T4];
export function assertAllDefined(...args: (unknown | null | undefined)[]): unknown[] {
const result = [];
for (let i = 0; i < args.length; i++) {
const arg = args[i];
if (isUndefinedOrNull(arg)) {
throw new Error(`Assertion Failed: argument at index ${i} is undefined or null`);
}
result.push(arg);
}
return result;
}
const hasOwnProperty = Object.prototype.hasOwnProperty;
/**
* @returns whether the provided parameter is an empty JavaScript Object or not.
*/
export function isEmptyObject(obj: unknown): obj is object {
if (!isObject(obj)) {
return false;
}
for (const key in obj) {
if (hasOwnProperty.call(obj, key)) {
return false;
}
}
return true;
}
/**
* @returns whether the provided parameter is a JavaScript Function or not.
*/
export function isFunction(obj: unknown): obj is Function {
return (typeof obj === 'function');
}
/**
* @returns whether the provided parameters is are JavaScript Function or not.
*/
export function areFunctions(...objects: unknown[]): boolean {
return objects.length > 0 && objects.every(isFunction);
}
export type TypeConstraint = string | Function;
export function validateConstraints(args: unknown[], constraints: Array<TypeConstraint | undefined>): void {
const len = Math.min(args.length, constraints.length);
for (let i = 0; i < len; i++) {
validateConstraint(args[i], constraints[i]);
}
}
export function validateConstraint(arg: unknown, constraint: TypeConstraint | undefined): void {
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
}
if (!isUndefinedOrNull(arg) && (arg as any).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`);
}
}
type AddFirstParameterToFunction<T, TargetFunctionsReturnType, FirstParameter> = T extends (...args: any[]) => TargetFunctionsReturnType ?
// Function: add param to function
(firstArg: FirstParameter, ...args: Parameters<T>) => ReturnType<T> :
// Else: just leave as is
T;
/**
* Allows to add a first parameter to functions of a type.
*/
export type AddFirstParameterToFunctions<Target, TargetFunctionsReturnType, FirstParameter> = {
// For every property
[K in keyof Target]: AddFirstParameterToFunction<Target[K], TargetFunctionsReturnType, FirstParameter>;
};
/**
* Given an object with all optional properties, requires at least one to be defined.
* i.e. AtLeastOne<MyObject>;
*/
export type AtLeastOne<T, U = { [K in keyof T]: Pick<T, K> }> = Partial<T> & U[keyof U];
/**
* Only picks the non-optional properties of a type.
*/
export type OmitOptional<T> = { [K in keyof T as T[K] extends Required<T>[K] ? K : never]: T[K] };
/**
* A type that removed readonly-less from all properties of `T`
*/
export type Mutable<T> = {
-readonly [P in keyof T]: T[P]
};
/**
* A single object or an array of the objects.
*/
export type SingleOrMany<T> = T | T[];
/**
* A type that recursively makes all properties of `T` required
*/
export type DeepRequiredNonNullable<T> = {
[P in keyof T]-?: T[P] extends object ? DeepRequiredNonNullable<T[P]> : Required<NonNullable<T[P]>>;
};
/**
* Represents a type that is a partial version of a given type `T`, where all properties are optional and can be deeply nested.
*/
export type DeepPartial<T> = {
[P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : Partial<T[P]>;
};
/**
* Represents a type that is a partial version of a given type `T`, except a subset.
*/
export type PartialExcept<T, K extends keyof T> = Partial<Omit<T, K>> & Pick<T, K>;
-81
View File
@@ -1,81 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
const _UUIDPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
export function isUUID(value: string): boolean {
return _UUIDPattern.test(value);
}
declare const crypto: undefined | {
//https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues#browser_compatibility
getRandomValues?(data: Uint8Array): Uint8Array;
//https://developer.mozilla.org/en-US/docs/Web/API/Crypto/randomUUID#browser_compatibility
randomUUID?(): string;
};
export const generateUuid = (function (): () => string {
// use `randomUUID` if possible
if (typeof crypto === 'object' && typeof crypto.randomUUID === 'function') {
return crypto.randomUUID.bind(crypto);
}
// use `randomValues` if possible
let getRandomValues: (bucket: Uint8Array) => Uint8Array;
if (typeof crypto === 'object' && typeof crypto.getRandomValues === 'function') {
getRandomValues = crypto.getRandomValues.bind(crypto);
} else {
getRandomValues = function (bucket: Uint8Array): Uint8Array {
for (let i = 0; i < bucket.length; i++) {
bucket[i] = Math.floor(Math.random() * 256);
}
return bucket;
};
}
// prep-work
const _data = new Uint8Array(16);
const _hex: string[] = [];
for (let i = 0; i < 256; i++) {
_hex.push(i.toString(16).padStart(2, '0'));
}
return function generateUuid(): string {
// get data
getRandomValues(_data);
// set version bits
_data[6] = (_data[6] & 0x0f) | 0x40;
_data[8] = (_data[8] & 0x3f) | 0x80;
// print as string
let i = 0;
let result = '';
result += _hex[_data[i++]];
result += _hex[_data[i++]];
result += _hex[_data[i++]];
result += _hex[_data[i++]];
result += '-';
result += _hex[_data[i++]];
result += _hex[_data[i++]];
result += '-';
result += _hex[_data[i++]];
result += _hex[_data[i++]];
result += '-';
result += _hex[_data[i++]];
result += _hex[_data[i++]];
result += '-';
result += _hex[_data[i++]];
result += _hex[_data[i++]];
result += _hex[_data[i++]];
result += _hex[_data[i++]];
result += _hex[_data[i++]];
result += _hex[_data[i++]];
return result;
};
})();
-87
View File
@@ -1,87 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { isObject } from 'vs/base/common/types';
interface IVerifier<T> {
verify(value: unknown): T;
}
abstract class Verifier<T> implements IVerifier<T> {
constructor(protected readonly defaultValue: T) { }
verify(value: unknown): T {
if (!this.isType(value)) {
return this.defaultValue;
}
return value;
}
protected abstract isType(value: unknown): value is T;
}
export class BooleanVerifier extends Verifier<boolean> {
protected isType(value: unknown): value is boolean {
return typeof value === 'boolean';
}
}
export class NumberVerifier extends Verifier<number> {
protected isType(value: unknown): value is number {
return typeof value === 'number';
}
}
export class SetVerifier<T> extends Verifier<Set<T>> {
protected isType(value: unknown): value is Set<T> {
return value instanceof Set;
}
}
export class EnumVerifier<T> extends Verifier<T> {
private readonly allowedValues: ReadonlyArray<T>;
constructor(defaultValue: T, allowedValues: ReadonlyArray<T>) {
super(defaultValue);
this.allowedValues = allowedValues;
}
protected isType(value: unknown): value is T {
return this.allowedValues.includes(value as T);
}
}
export class ObjectVerifier<T extends Object> extends Verifier<T> {
constructor(defaultValue: T, private readonly verifier: { [K in keyof T]: IVerifier<T[K]> }) {
super(defaultValue);
}
override verify(value: unknown): T {
if (!this.isType(value)) {
return this.defaultValue;
}
return verifyObject<T>(this.verifier, value);
}
protected isType(value: unknown): value is T {
return isObject(value);
}
}
export function verifyObject<T extends Object>(verifiers: { [K in keyof T]: IVerifier<T[K]> }, value: Object): T {
const result = Object.create(null);
for (const key in verifiers) {
if (Object.hasOwnProperty.call(verifiers, key)) {
const verifier = verifiers[key];
result[key] = verifier.verify((value as any)[key]);
}
}
return result;
}