mirror of
https://github.com/coder/code-server.git
synced 2026-05-05 12:05:18 +02:00
chore(vscode): update to 1.54.2
This commit is contained in:
13
lib/vscode/src/bootstrap.js
vendored
13
lib/vscode/src/bootstrap.js
vendored
@@ -170,6 +170,9 @@
|
||||
return nlsConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {typeof import('./vs/base/parts/sandbox/electron-sandbox/globals') | undefined}
|
||||
*/
|
||||
function safeGlobals() {
|
||||
const globals = (typeof self === 'object' ? self : typeof global === 'object' ? global : {});
|
||||
|
||||
@@ -177,7 +180,7 @@
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {NodeJS.Process | undefined}
|
||||
* @returns {import('./vs/base/parts/sandbox/electron-sandbox/globals').IPartialNodeProcess | NodeJS.Process}
|
||||
*/
|
||||
function safeProcess() {
|
||||
if (typeof process !== 'undefined') {
|
||||
@@ -188,16 +191,20 @@
|
||||
if (globals) {
|
||||
return globals.process; // Native environment (sandboxed)
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Electron.IpcRenderer | undefined}
|
||||
* @returns {import('./vs/base/parts/sandbox/electron-sandbox/electronTypes').IpcRenderer | undefined}
|
||||
*/
|
||||
function safeIpcRenderer() {
|
||||
const globals = safeGlobals();
|
||||
if (globals) {
|
||||
return globals.ipcRenderer;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -236,7 +243,7 @@
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
|
||||
|
||||
//#region ApplicationInsights
|
||||
|
||||
|
||||
@@ -9,14 +9,14 @@
|
||||
const perf = require('./vs/base/common/performance');
|
||||
perf.mark('code/didStartMain');
|
||||
|
||||
const lp = require('./vs/base/node/languagePacks');
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const os = require('os');
|
||||
const { getNLSConfiguration } = require('./vs/base/node/languagePacks');
|
||||
const bootstrap = require('./bootstrap');
|
||||
const bootstrapNode = require('./bootstrap-node');
|
||||
const paths = require('./paths');
|
||||
/** @type {Partial<import('./vs/platform/product/common/productService').IProductConfiguration> & { applicationName: string}} */
|
||||
const { getDefaultUserDataPath } = require('./vs/base/node/userDataPath');
|
||||
/** @type {Partial<import('./vs/platform/product/common/productService').IProductConfiguration>} */
|
||||
const product = require('../product.json');
|
||||
const { app, protocol, crashReporter } = require('electron');
|
||||
|
||||
@@ -38,70 +38,8 @@ app.setPath('userData', userDataPath);
|
||||
// Configure static command line arguments
|
||||
const argvConfig = configureCommandlineSwitchesSync(args);
|
||||
|
||||
// If a crash-reporter-directory is specified we store the crash reports
|
||||
// in the specified directory and don't upload them to the crash server.
|
||||
let crashReporterDirectory = args['crash-reporter-directory'];
|
||||
let submitURL = '';
|
||||
if (crashReporterDirectory) {
|
||||
crashReporterDirectory = path.normalize(crashReporterDirectory);
|
||||
|
||||
if (!path.isAbsolute(crashReporterDirectory)) {
|
||||
console.error(`The path '${crashReporterDirectory}' specified for --crash-reporter-directory must be absolute.`);
|
||||
app.exit(1);
|
||||
}
|
||||
|
||||
if (!fs.existsSync(crashReporterDirectory)) {
|
||||
try {
|
||||
fs.mkdirSync(crashReporterDirectory);
|
||||
} catch (error) {
|
||||
console.error(`The path '${crashReporterDirectory}' specified for --crash-reporter-directory does not seem to exist or cannot be created.`);
|
||||
app.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Crashes are stored in the crashDumps directory by default, so we
|
||||
// need to change that directory to the provided one
|
||||
console.log(`Found --crash-reporter-directory argument. Setting crashDumps directory to be '${crashReporterDirectory}'`);
|
||||
app.setPath('crashDumps', crashReporterDirectory);
|
||||
} else {
|
||||
const appCenter = product.appCenter;
|
||||
// Disable Appcenter crash reporting if
|
||||
// * --crash-reporter-directory is specified
|
||||
// * enable-crash-reporter runtime argument is set to 'false'
|
||||
// * --disable-crash-reporter command line parameter is set
|
||||
if (appCenter && argvConfig['enable-crash-reporter'] && !args['disable-crash-reporter']) {
|
||||
const isWindows = (process.platform === 'win32');
|
||||
const isLinux = (process.platform === 'linux');
|
||||
const crashReporterId = argvConfig['crash-reporter-id'];
|
||||
const uuidPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
||||
if (uuidPattern.test(crashReporterId)) {
|
||||
submitURL = isWindows ? appCenter[process.arch === 'ia32' ? 'win32-ia32' : 'win32-x64'] : isLinux ? appCenter[`linux-x64`] : appCenter.darwin;
|
||||
submitURL = submitURL.concat('&uid=', crashReporterId, '&iid=', crashReporterId, '&sid=', crashReporterId);
|
||||
// Send the id for child node process that are explicitly starting crash reporter.
|
||||
// For vscode this is ExtensionHost process currently.
|
||||
const argv = process.argv;
|
||||
const endOfArgsMarkerIndex = argv.indexOf('--');
|
||||
if (endOfArgsMarkerIndex === -1) {
|
||||
argv.push('--crash-reporter-id', crashReporterId);
|
||||
} else {
|
||||
// if the we have an argument "--" (end of argument marker)
|
||||
// we cannot add arguments at the end. rather, we add
|
||||
// arguments before the "--" marker.
|
||||
argv.splice(endOfArgsMarkerIndex, 0, '--crash-reporter-id', crashReporterId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Start crash reporter for all processes
|
||||
const productName = (product.crashReporter ? product.crashReporter.productName : undefined) || product.nameShort;
|
||||
const companyName = (product.crashReporter ? product.crashReporter.companyName : undefined) || 'Microsoft';
|
||||
crashReporter.start({
|
||||
companyName: companyName,
|
||||
productName: process.env['VSCODE_DEV'] ? `${productName} Dev` : productName,
|
||||
submitURL,
|
||||
uploadToServer: !crashReporterDirectory
|
||||
});
|
||||
// Configure crash reporter
|
||||
configureCrashReporter();
|
||||
|
||||
// Set logs path before app 'ready' event if running portable
|
||||
// to ensure that no 'logs' folder is created on disk at a
|
||||
@@ -118,29 +56,14 @@ setCurrentWorkingDirectory();
|
||||
protocol.registerSchemesAsPrivileged([
|
||||
{
|
||||
scheme: 'vscode-webview',
|
||||
privileges: {
|
||||
standard: true,
|
||||
secure: true,
|
||||
supportFetchAPI: true,
|
||||
corsEnabled: true,
|
||||
}
|
||||
privileges: { standard: true, secure: true, supportFetchAPI: true, corsEnabled: true }
|
||||
}, {
|
||||
scheme: 'vscode-webview-resource',
|
||||
privileges: {
|
||||
secure: true,
|
||||
standard: true,
|
||||
supportFetchAPI: true,
|
||||
corsEnabled: true,
|
||||
}
|
||||
privileges: { secure: true, standard: true, supportFetchAPI: true, corsEnabled: true }
|
||||
},
|
||||
{
|
||||
scheme: 'vscode-file',
|
||||
privileges: {
|
||||
secure: true,
|
||||
standard: true,
|
||||
supportFetchAPI: true,
|
||||
corsEnabled: true
|
||||
}
|
||||
privileges: { secure: true, standard: true, supportFetchAPI: true, corsEnabled: true }
|
||||
}
|
||||
]);
|
||||
|
||||
@@ -161,7 +84,7 @@ let nlsConfigurationPromise = undefined;
|
||||
const metaDataFile = path.join(__dirname, 'nls.metadata.json');
|
||||
const locale = getUserDefinedLocale(argvConfig);
|
||||
if (locale) {
|
||||
nlsConfigurationPromise = lp.getNLSConfiguration(product.commit, userDataPath, metaDataFile, locale);
|
||||
nlsConfigurationPromise = getNLSConfiguration(product.commit, userDataPath, metaDataFile, locale);
|
||||
}
|
||||
|
||||
// Load our code once ready
|
||||
@@ -381,6 +304,106 @@ function getArgvConfigPath() {
|
||||
return path.join(os.homedir(), dataFolderName, 'argv.json');
|
||||
}
|
||||
|
||||
function configureCrashReporter() {
|
||||
|
||||
// If a crash-reporter-directory is specified we store the crash reports
|
||||
// in the specified directory and don't upload them to the crash server.
|
||||
let crashReporterDirectory = args['crash-reporter-directory'];
|
||||
let submitURL = '';
|
||||
if (crashReporterDirectory) {
|
||||
crashReporterDirectory = path.normalize(crashReporterDirectory);
|
||||
|
||||
if (!path.isAbsolute(crashReporterDirectory)) {
|
||||
console.error(`The path '${crashReporterDirectory}' specified for --crash-reporter-directory must be absolute.`);
|
||||
app.exit(1);
|
||||
}
|
||||
|
||||
if (!fs.existsSync(crashReporterDirectory)) {
|
||||
try {
|
||||
fs.mkdirSync(crashReporterDirectory);
|
||||
} catch (error) {
|
||||
console.error(`The path '${crashReporterDirectory}' specified for --crash-reporter-directory does not seem to exist or cannot be created.`);
|
||||
app.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Crashes are stored in the crashDumps directory by default, so we
|
||||
// need to change that directory to the provided one
|
||||
console.log(`Found --crash-reporter-directory argument. Setting crashDumps directory to be '${crashReporterDirectory}'`);
|
||||
app.setPath('crashDumps', crashReporterDirectory);
|
||||
}
|
||||
|
||||
// Otherwise we configure the crash reporter from product.json
|
||||
else {
|
||||
const appCenter = product.appCenter;
|
||||
// Disable Appcenter crash reporting if
|
||||
// * --crash-reporter-directory is specified
|
||||
// * enable-crash-reporter runtime argument is set to 'false'
|
||||
// * --disable-crash-reporter command line parameter is set
|
||||
if (appCenter && argvConfig['enable-crash-reporter'] && !args['disable-crash-reporter']) {
|
||||
const isWindows = (process.platform === 'win32');
|
||||
const isLinux = (process.platform === 'linux');
|
||||
const isDarwin = (process.platform === 'darwin');
|
||||
const crashReporterId = argvConfig['crash-reporter-id'];
|
||||
const uuidPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
||||
if (uuidPattern.test(crashReporterId)) {
|
||||
if (isWindows) {
|
||||
switch (process.arch) {
|
||||
case 'ia32':
|
||||
submitURL = appCenter['win32-ia32'];
|
||||
break;
|
||||
case 'x64':
|
||||
submitURL = appCenter['win32-x64'];
|
||||
break;
|
||||
case 'arm64':
|
||||
submitURL = appCenter['win32-arm64'];
|
||||
break;
|
||||
}
|
||||
} else if (isDarwin) {
|
||||
if (product.darwinUniversalAssetId) {
|
||||
submitURL = appCenter['darwin-universal'];
|
||||
} else {
|
||||
switch (process.arch) {
|
||||
case 'x64':
|
||||
submitURL = appCenter['darwin'];
|
||||
break;
|
||||
case 'arm64':
|
||||
submitURL = appCenter['darwin-arm64'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (isLinux) {
|
||||
submitURL = appCenter['linux-x64'];
|
||||
}
|
||||
submitURL = submitURL.concat('&uid=', crashReporterId, '&iid=', crashReporterId, '&sid=', crashReporterId);
|
||||
// Send the id for child node process that are explicitly starting crash reporter.
|
||||
// For vscode this is ExtensionHost process currently.
|
||||
const argv = process.argv;
|
||||
const endOfArgsMarkerIndex = argv.indexOf('--');
|
||||
if (endOfArgsMarkerIndex === -1) {
|
||||
argv.push('--crash-reporter-id', crashReporterId);
|
||||
} else {
|
||||
// if the we have an argument "--" (end of argument marker)
|
||||
// we cannot add arguments at the end. rather, we add
|
||||
// arguments before the "--" marker.
|
||||
argv.splice(endOfArgsMarkerIndex, 0, '--crash-reporter-id', crashReporterId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Start crash reporter for all processes
|
||||
const productName = (product.crashReporter ? product.crashReporter.productName : undefined) || product.nameShort;
|
||||
const companyName = (product.crashReporter ? product.crashReporter.companyName : undefined) || 'Microsoft';
|
||||
crashReporter.start({
|
||||
companyName: companyName,
|
||||
productName: process.env['VSCODE_DEV'] ? `${productName} Dev` : productName,
|
||||
submitURL,
|
||||
uploadToServer: !crashReporterDirectory,
|
||||
compress: true
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import('./vs/platform/environment/common/argv').NativeParsedArgs} cliArgs
|
||||
* @returns {string | null}
|
||||
@@ -411,7 +434,7 @@ function getUserDataPath(cliArgs) {
|
||||
return path.join(portable.portableDataPath, 'user-data');
|
||||
}
|
||||
|
||||
return path.resolve(cliArgs['user-data-dir'] || paths.getDefaultUserDataPath());
|
||||
return path.resolve(cliArgs['user-data-dir'] || getDefaultUserDataPath());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -566,7 +589,7 @@ async function resolveNlsConfiguration() {
|
||||
// See above the comment about the loader and case sensitiviness
|
||||
appLocale = appLocale.toLowerCase();
|
||||
|
||||
nlsConfiguration = await lp.getNLSConfiguration(product.commit, userDataPath, metaDataFile, appLocale);
|
||||
nlsConfiguration = await getNLSConfiguration(product.commit, userDataPath, metaDataFile, appLocale);
|
||||
if (!nlsConfiguration) {
|
||||
nlsConfiguration = { locale: appLocale, availableLanguages: {} };
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
//@ts-check
|
||||
'use strict';
|
||||
|
||||
const pkg = require('../package.json');
|
||||
const path = require('path');
|
||||
const os = require('os');
|
||||
|
||||
/**
|
||||
* @returns {string}
|
||||
*/
|
||||
function getDefaultUserDataPath() {
|
||||
|
||||
// Support global VSCODE_APPDATA environment variable
|
||||
let appDataPath = process.env['VSCODE_APPDATA'];
|
||||
|
||||
// Otherwise check per platform
|
||||
if (!appDataPath) {
|
||||
switch (process.platform) {
|
||||
case 'win32':
|
||||
appDataPath = process.env['APPDATA'];
|
||||
if (!appDataPath) {
|
||||
const userProfile = process.env['USERPROFILE'];
|
||||
if (typeof userProfile !== 'string') {
|
||||
throw new Error('Windows: Unexpected undefined %USERPROFILE% environment variable');
|
||||
}
|
||||
appDataPath = path.join(userProfile, 'AppData', 'Roaming');
|
||||
}
|
||||
break;
|
||||
case 'darwin':
|
||||
appDataPath = path.join(os.homedir(), 'Library', 'Application Support');
|
||||
break;
|
||||
case 'linux':
|
||||
appDataPath = process.env['XDG_CONFIG_HOME'] || path.join(os.homedir(), '.config');
|
||||
break;
|
||||
default:
|
||||
throw new Error('Platform not supported');
|
||||
}
|
||||
}
|
||||
|
||||
return path.join(appDataPath, pkg.name);
|
||||
}
|
||||
|
||||
exports.getDefaultUserDataPath = getDefaultUserDataPath;
|
||||
@@ -13,6 +13,12 @@
|
||||
"sinon",
|
||||
"winreg",
|
||||
"trusted-types"
|
||||
],
|
||||
"plugins": [
|
||||
{
|
||||
"name": "tsec",
|
||||
"exemptionConfig": "./tsec.exemptions.json"
|
||||
}
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
|
||||
@@ -1,5 +1,31 @@
|
||||
{
|
||||
"ban-eval-calls": [
|
||||
"vs/workbench/api/worker/extHostExtensionService.ts"
|
||||
],
|
||||
"ban-function-calls": [
|
||||
"vs/workbench/api/worker/extHostExtensionService.ts",
|
||||
"vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts",
|
||||
"vs/workbench/services/keybinding/test/electron-browser/keyboardMapperTestUtils.ts"
|
||||
],
|
||||
"ban-trustedtypes-createpolicy": [
|
||||
"**/*.ts"
|
||||
"vs/base/browser/dom.ts",
|
||||
"vs/base/browser/markdownRenderer.ts",
|
||||
"vs/base/worker/defaultWorkerFactory.ts",
|
||||
"vs/base/worker/workerMain.ts",
|
||||
"vs/editor/browser/core/markdownRenderer.ts",
|
||||
"vs/editor/browser/view/domLineBreaksComputer.ts",
|
||||
"vs/editor/browser/view/viewLayer.ts",
|
||||
"vs/editor/browser/widget/diffEditorWidget.ts",
|
||||
"vs/editor/browser/widget/diffReview.ts",
|
||||
"vs/editor/standalone/browser/colorizer.ts",
|
||||
"vs/workbench/api/worker/extHostExtensionService.ts",
|
||||
"vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts",
|
||||
"vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts",
|
||||
"vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts",
|
||||
"vs/workbench/services/extensions/worker/extensionHostWorkerMain.ts"
|
||||
],
|
||||
"ban-worker-calls": [
|
||||
"vs/base/worker/defaultWorkerFactory.ts",
|
||||
"vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -110,13 +110,13 @@ export const onDidChangeFullscreen = WindowManager.INSTANCE.onDidChangeFullscree
|
||||
|
||||
const userAgent = navigator.userAgent;
|
||||
|
||||
export const isEdgeLegacy = (userAgent.indexOf('Edge/') >= 0);
|
||||
export const isFirefox = (userAgent.indexOf('Firefox') >= 0);
|
||||
export const isWebKit = (userAgent.indexOf('AppleWebKit') >= 0);
|
||||
export const isChrome = (userAgent.indexOf('Chrome') >= 0);
|
||||
export const isSafari = (!isChrome && (userAgent.indexOf('Safari') >= 0));
|
||||
export const isWebkitWebView = (!isChrome && !isSafari && isWebKit);
|
||||
export const isIPad = (userAgent.indexOf('iPad') >= 0 || (isSafari && navigator.maxTouchPoints > 0));
|
||||
export const isEdgeLegacyWebView = isEdgeLegacy && (userAgent.indexOf('WebView/') >= 0);
|
||||
export const isEdgeLegacyWebView = (userAgent.indexOf('Edge/') >= 0) && (userAgent.indexOf('WebView/') >= 0);
|
||||
export const isElectron = (userAgent.indexOf('Electron/') >= 0);
|
||||
export const isAndroid = (userAgent.indexOf('Android') >= 0);
|
||||
export const isStandalone = (window.matchMedia && window.matchMedia('(display-mode: standalone)').matches);
|
||||
|
||||
@@ -25,19 +25,7 @@ export const BrowserFeatures = {
|
||||
readText: (
|
||||
platform.isNative
|
||||
|| !!(navigator && navigator.clipboard && navigator.clipboard.readText)
|
||||
),
|
||||
richText: (() => {
|
||||
if (browser.isEdgeLegacy) {
|
||||
let index = navigator.userAgent.indexOf('Edge/');
|
||||
let version = parseInt(navigator.userAgent.substring(index + 5, navigator.userAgent.indexOf('.', index)), 10);
|
||||
|
||||
if (!version || (version >= 12 && version <= 16)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
})()
|
||||
)
|
||||
},
|
||||
keyboard: (() => {
|
||||
if (platform.isNative || browser.isStandalone) {
|
||||
|
||||
@@ -3,9 +3,10 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IAction, IActionRunner, IActionViewItem } from 'vs/base/common/actions';
|
||||
import { IAction, IActionRunner } from 'vs/base/common/actions';
|
||||
import { ResolvedKeybinding } from 'vs/base/common/keyCodes';
|
||||
import { AnchorAlignment, AnchorAxisAlignment } from 'vs/base/browser/ui/contextview/contextview';
|
||||
import { IActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
|
||||
export interface IContextMenuEvent {
|
||||
readonly shiftKey?: boolean;
|
||||
|
||||
@@ -284,7 +284,7 @@ export function modify(callback: () => void): IDisposable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a throttled listener. `handler` is fired at most every 16ms or with the next animation frame (if browser supports it).
|
||||
* Add a throttled listener. `handler` is fired at most every 8.33333ms or with the next animation frame (if browser supports it).
|
||||
*/
|
||||
export interface IEventMerger<R, E> {
|
||||
(lastEvent: R | null, currentEvent: E): R;
|
||||
@@ -295,7 +295,7 @@ export interface DOMEvent {
|
||||
stopPropagation(): void;
|
||||
}
|
||||
|
||||
const MINIMUM_TIME_MS = 16;
|
||||
const MINIMUM_TIME_MS = 8;
|
||||
const DEFAULT_EVENT_MERGER: IEventMerger<DOMEvent, DOMEvent> = function (lastEvent: DOMEvent | null, currentEvent: DOMEvent) {
|
||||
return currentEvent;
|
||||
};
|
||||
@@ -841,7 +841,7 @@ export const EventType = {
|
||||
MOUSE_OUT: 'mouseout',
|
||||
MOUSE_ENTER: 'mouseenter',
|
||||
MOUSE_LEAVE: 'mouseleave',
|
||||
MOUSE_WHEEL: browser.isEdgeLegacy ? 'mousewheel' : 'wheel',
|
||||
MOUSE_WHEEL: 'wheel',
|
||||
POINTER_UP: 'pointerup',
|
||||
POINTER_DOWN: 'pointerdown',
|
||||
POINTER_MOVE: 'pointermove',
|
||||
@@ -1230,6 +1230,10 @@ export function asCSSUrl(uri: URI): string {
|
||||
return `url('${FileAccess.asBrowserUri(uri).toString(true).replace(/'/g, '%27')}')`;
|
||||
}
|
||||
|
||||
export function asCSSPropertyValue(value: string) {
|
||||
return `'${value.replace(/'/g, '%27')}'`;
|
||||
}
|
||||
|
||||
export function triggerDownload(dataOrUri: Uint8Array | URI, name: string): void {
|
||||
|
||||
// If the data is provided as Buffer, we create a
|
||||
@@ -1471,7 +1475,7 @@ export class ModifierKeyEmitter extends Emitter<IModifierKeyStatus> {
|
||||
metaKey: false
|
||||
};
|
||||
|
||||
this._subscriptions.add(domEvent(document.body, 'keydown', true)(e => {
|
||||
this._subscriptions.add(domEvent(window, 'keydown', true)(e => {
|
||||
|
||||
const event = new StandardKeyboardEvent(e);
|
||||
// If Alt-key keydown event is repeated, ignore it #112347
|
||||
@@ -1505,7 +1509,7 @@ export class ModifierKeyEmitter extends Emitter<IModifierKeyStatus> {
|
||||
}
|
||||
}));
|
||||
|
||||
this._subscriptions.add(domEvent(document.body, 'keyup', true)(e => {
|
||||
this._subscriptions.add(domEvent(window, 'keyup', true)(e => {
|
||||
if (!e.altKey && this._keyStatus.altKey) {
|
||||
this._keyStatus.lastKeyReleased = 'alt';
|
||||
} else if (!e.ctrlKey && this._keyStatus.ctrlKey) {
|
||||
|
||||
@@ -31,10 +31,12 @@ export interface CancellableEvent {
|
||||
stopPropagation(): void;
|
||||
}
|
||||
|
||||
export function stopEvent<T extends CancellableEvent>(event: T): T {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
return event;
|
||||
}
|
||||
|
||||
export function stop<T extends CancellableEvent>(event: BaseEvent<T>): BaseEvent<T> {
|
||||
return BaseEvent.map(event, e => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
return e;
|
||||
});
|
||||
}
|
||||
return BaseEvent.map(event, stopEvent);
|
||||
}
|
||||
|
||||
@@ -383,5 +383,16 @@ export function renderMarkdownAsPlaintext(markdown: IMarkdownString) {
|
||||
if (value.length > 100_000) {
|
||||
value = `${value.substr(0, 100_000)}…`;
|
||||
}
|
||||
return sanitizeRenderedMarkdown({ isTrusted: false }, marked.parse(value, { renderer })).toString();
|
||||
|
||||
const unescapeInfo = new Map<string, string>([
|
||||
['"', '"'],
|
||||
['&', '&'],
|
||||
[''', '\''],
|
||||
['<', '<'],
|
||||
['>', '>'],
|
||||
]);
|
||||
|
||||
const html = marked.parse(value, { renderer }).replace(/&(#\d+|[a-zA-Z]+);/g, m => unescapeInfo.get(m) ?? m);
|
||||
|
||||
return sanitizeRenderedMarkdown({ isTrusted: false }, html).toString();
|
||||
}
|
||||
|
||||
@@ -8,13 +8,14 @@ import * as platform from 'vs/base/common/platform';
|
||||
import * as nls from 'vs/nls';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { SelectBox, ISelectOptionItem, ISelectBoxOptions } from 'vs/base/browser/ui/selectBox/selectBox';
|
||||
import { IAction, IActionRunner, Action, IActionChangeEvent, ActionRunner, Separator, IActionViewItem } from 'vs/base/common/actions';
|
||||
import { IAction, IActionRunner, Action, IActionChangeEvent, ActionRunner, Separator } from 'vs/base/common/actions';
|
||||
import * as types from 'vs/base/common/types';
|
||||
import { EventType as TouchEventType, Gesture } from 'vs/base/browser/touch';
|
||||
import { IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview';
|
||||
import { DataTransfers } from 'vs/base/browser/dnd';
|
||||
import { isFirefox } from 'vs/base/browser/browser';
|
||||
import { $, addDisposableListener, append, EventHelper, EventLike, EventType, removeTabIndexAndUpdateFocus } from 'vs/base/browser/dom';
|
||||
import { $, addDisposableListener, append, EventHelper, EventLike, EventType } from 'vs/base/browser/dom';
|
||||
import { IActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
|
||||
export interface IBaseActionViewItemOptions {
|
||||
draggable?: boolean;
|
||||
@@ -163,8 +164,11 @@ export class BaseActionViewItem extends Disposable implements IActionViewItem {
|
||||
this.actionRunner.run(this._action, context);
|
||||
}
|
||||
|
||||
// Only set the tabIndex on the element once it is about to get focused
|
||||
// That way this element wont be a tab stop when it is not needed #106441
|
||||
focus(): void {
|
||||
if (this.element) {
|
||||
this.element.tabIndex = 0;
|
||||
this.element.focus();
|
||||
this.element.classList.add('focused');
|
||||
}
|
||||
@@ -173,10 +177,21 @@ export class BaseActionViewItem extends Disposable implements IActionViewItem {
|
||||
blur(): void {
|
||||
if (this.element) {
|
||||
this.element.blur();
|
||||
this.element.tabIndex = -1;
|
||||
this.element.classList.remove('focused');
|
||||
}
|
||||
}
|
||||
|
||||
setFocusable(focusable: boolean): void {
|
||||
if (this.element) {
|
||||
this.element.tabIndex = focusable ? 0 : -1;
|
||||
}
|
||||
}
|
||||
|
||||
get trapsArrowNavigation(): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected updateEnabled(): void {
|
||||
// implement in subclass
|
||||
}
|
||||
@@ -259,14 +274,27 @@ export class ActionViewItem extends BaseActionViewItem {
|
||||
this.updateChecked();
|
||||
}
|
||||
|
||||
// Only set the tabIndex on the element once it is about to get focused
|
||||
// That way this element wont be a tab stop when it is not needed #106441
|
||||
focus(): void {
|
||||
super.focus();
|
||||
|
||||
if (this.label) {
|
||||
this.label.tabIndex = 0;
|
||||
this.label.focus();
|
||||
}
|
||||
}
|
||||
|
||||
blur(): void {
|
||||
if (this.label) {
|
||||
this.label.tabIndex = -1;
|
||||
}
|
||||
}
|
||||
|
||||
setFocusable(focusable: boolean): void {
|
||||
if (this.label) {
|
||||
this.label.tabIndex = focusable ? 0 : -1;
|
||||
}
|
||||
}
|
||||
|
||||
updateLabel(): void {
|
||||
if (this.options.label && this.label) {
|
||||
this.label.textContent = this.getAction().label;
|
||||
@@ -320,7 +348,6 @@ export class ActionViewItem extends BaseActionViewItem {
|
||||
if (this.label) {
|
||||
this.label.removeAttribute('aria-disabled');
|
||||
this.label.classList.remove('disabled');
|
||||
this.label.tabIndex = 0;
|
||||
}
|
||||
|
||||
if (this.element) {
|
||||
@@ -330,7 +357,6 @@ export class ActionViewItem extends BaseActionViewItem {
|
||||
if (this.label) {
|
||||
this.label.setAttribute('aria-disabled', 'true');
|
||||
this.label.classList.add('disabled');
|
||||
removeTabIndexAndUpdateFocus(this.label);
|
||||
}
|
||||
|
||||
if (this.element) {
|
||||
|
||||
@@ -55,6 +55,7 @@
|
||||
}
|
||||
|
||||
.monaco-action-bar .action-item.disabled .action-label,
|
||||
.monaco-action-bar .action-item.disabled .action-label::before,
|
||||
.monaco-action-bar .action-item.disabled .action-label:hover {
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'vs/css!./actionbar';
|
||||
import { Disposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { IAction, IActionRunner, ActionRunner, IRunEvent, Separator, IActionViewItem, IActionViewItemProvider } from 'vs/base/common/actions';
|
||||
import { Disposable, dispose, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { IAction, IActionRunner, ActionRunner, IRunEvent, Separator } from 'vs/base/common/actions';
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
import * as types from 'vs/base/common/types';
|
||||
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
@@ -13,6 +13,19 @@ import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { IActionViewItemOptions, ActionViewItem, BaseActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems';
|
||||
|
||||
export interface IActionViewItem extends IDisposable {
|
||||
actionRunner: IActionRunner;
|
||||
setActionContext(context: any): void;
|
||||
render(element: HTMLElement): void;
|
||||
isEnabled(): boolean;
|
||||
focus(fromRight?: boolean): void; // TODO@isidorn what is this?
|
||||
blur(): void;
|
||||
}
|
||||
|
||||
export interface IActionViewItemProvider {
|
||||
(action: IAction): IActionViewItem | undefined;
|
||||
}
|
||||
|
||||
export const enum ActionsOrientation {
|
||||
HORIZONTAL,
|
||||
HORIZONTAL_REVERSE,
|
||||
@@ -35,7 +48,7 @@ export interface IActionBarOptions {
|
||||
readonly triggerKeys?: ActionTrigger;
|
||||
readonly allowContextMenu?: boolean;
|
||||
readonly preventLoopNavigation?: boolean;
|
||||
readonly ignoreOrientationForPreviousAndNextKey?: boolean;
|
||||
readonly focusOnlyEnabledItems?: boolean;
|
||||
}
|
||||
|
||||
export interface IActionOptions extends IActionViewItemOptions {
|
||||
@@ -63,6 +76,8 @@ export class ActionBar extends Disposable implements IActionRunner {
|
||||
// Trigger Key Tracking
|
||||
private triggerKeyDown: boolean = false;
|
||||
|
||||
private focusable: boolean = true;
|
||||
|
||||
// Elements
|
||||
domNode: HTMLElement;
|
||||
protected actionsList: HTMLElement;
|
||||
@@ -117,22 +132,22 @@ export class ActionBar extends Disposable implements IActionRunner {
|
||||
|
||||
switch (this._orientation) {
|
||||
case ActionsOrientation.HORIZONTAL:
|
||||
previousKeys = this.options.ignoreOrientationForPreviousAndNextKey ? [KeyCode.LeftArrow, KeyCode.UpArrow] : [KeyCode.LeftArrow];
|
||||
nextKeys = this.options.ignoreOrientationForPreviousAndNextKey ? [KeyCode.RightArrow, KeyCode.DownArrow] : [KeyCode.RightArrow];
|
||||
previousKeys = [KeyCode.LeftArrow];
|
||||
nextKeys = [KeyCode.RightArrow];
|
||||
break;
|
||||
case ActionsOrientation.HORIZONTAL_REVERSE:
|
||||
previousKeys = this.options.ignoreOrientationForPreviousAndNextKey ? [KeyCode.RightArrow, KeyCode.DownArrow] : [KeyCode.RightArrow];
|
||||
nextKeys = this.options.ignoreOrientationForPreviousAndNextKey ? [KeyCode.LeftArrow, KeyCode.UpArrow] : [KeyCode.LeftArrow];
|
||||
previousKeys = [KeyCode.RightArrow];
|
||||
nextKeys = [KeyCode.LeftArrow];
|
||||
this.domNode.className += ' reverse';
|
||||
break;
|
||||
case ActionsOrientation.VERTICAL:
|
||||
previousKeys = this.options.ignoreOrientationForPreviousAndNextKey ? [KeyCode.LeftArrow, KeyCode.UpArrow] : [KeyCode.UpArrow];
|
||||
nextKeys = this.options.ignoreOrientationForPreviousAndNextKey ? [KeyCode.RightArrow, KeyCode.DownArrow] : [KeyCode.DownArrow];
|
||||
previousKeys = [KeyCode.UpArrow];
|
||||
nextKeys = [KeyCode.DownArrow];
|
||||
this.domNode.className += ' vertical';
|
||||
break;
|
||||
case ActionsOrientation.VERTICAL_REVERSE:
|
||||
previousKeys = this.options.ignoreOrientationForPreviousAndNextKey ? [KeyCode.RightArrow, KeyCode.DownArrow] : [KeyCode.DownArrow];
|
||||
nextKeys = this.options.ignoreOrientationForPreviousAndNextKey ? [KeyCode.LeftArrow, KeyCode.UpArrow] : [KeyCode.UpArrow];
|
||||
previousKeys = [KeyCode.DownArrow];
|
||||
nextKeys = [KeyCode.UpArrow];
|
||||
this.domNode.className += ' vertical reverse';
|
||||
break;
|
||||
}
|
||||
@@ -140,6 +155,7 @@ export class ActionBar extends Disposable implements IActionRunner {
|
||||
this._register(DOM.addDisposableListener(this.domNode, DOM.EventType.KEY_DOWN, e => {
|
||||
const event = new StandardKeyboardEvent(e);
|
||||
let eventHandled = true;
|
||||
const focusedItem = typeof this.focusedItem === 'number' ? this.viewItems[this.focusedItem] : undefined;
|
||||
|
||||
if (previousKeys && (event.equals(previousKeys[0]) || event.equals(previousKeys[1]))) {
|
||||
eventHandled = this.focusPrevious();
|
||||
@@ -147,6 +163,8 @@ export class ActionBar extends Disposable implements IActionRunner {
|
||||
eventHandled = this.focusNext();
|
||||
} else if (event.equals(KeyCode.Escape) && this.cancelHasListener) {
|
||||
this._onDidCancel.fire();
|
||||
} else if (event.equals(KeyCode.Tab) && focusedItem instanceof BaseActionViewItem && focusedItem.trapsArrowNavigation) {
|
||||
this.focusNext();
|
||||
} else if (this.isTriggerKeyEvent(event)) {
|
||||
// Staying out of the else branch even if not triggered
|
||||
if (this._triggerKeys.keyDown) {
|
||||
@@ -216,6 +234,25 @@ export class ActionBar extends Disposable implements IActionRunner {
|
||||
}
|
||||
}
|
||||
|
||||
// Some action bars should not be focusable at times
|
||||
// When an action bar is not focusable make sure to make all the elements inside it not focusable
|
||||
// When an action bar is focusable again, make sure the first item can be focused
|
||||
setFocusable(focusable: boolean): void {
|
||||
this.focusable = focusable;
|
||||
if (this.focusable) {
|
||||
const firstEnabled = this.viewItems.find(vi => vi instanceof BaseActionViewItem && vi.isEnabled());
|
||||
if (firstEnabled instanceof BaseActionViewItem) {
|
||||
firstEnabled.setFocusable(true);
|
||||
}
|
||||
} else {
|
||||
this.viewItems.forEach(vi => {
|
||||
if (vi instanceof BaseActionViewItem) {
|
||||
vi.setFocusable(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private isTriggerKeyEvent(event: StandardKeyboardEvent): boolean {
|
||||
let ret = false;
|
||||
this._triggerKeys.keys.forEach(keyCode => {
|
||||
@@ -294,6 +331,11 @@ export class ActionBar extends Disposable implements IActionRunner {
|
||||
item.setActionContext(this.context);
|
||||
item.render(actionViewItemElement);
|
||||
|
||||
if (this.focusable && item instanceof BaseActionViewItem && this.viewItems.length === 0) {
|
||||
// We need to allow for the first enabled item to be focused on using tab navigation #106441
|
||||
item.setFocusable(true);
|
||||
}
|
||||
|
||||
if (index === null || index < 0 || index >= this.actionsList.children.length) {
|
||||
this.actionsList.appendChild(actionViewItemElement);
|
||||
this.viewItems.push(item);
|
||||
@@ -305,7 +347,7 @@ export class ActionBar extends Disposable implements IActionRunner {
|
||||
index++;
|
||||
}
|
||||
});
|
||||
if (this.focusedItem) {
|
||||
if (typeof this.focusedItem === 'number') {
|
||||
// After a clear actions might be re-added to simply toggle some actions. We should preserve focus #97128
|
||||
this.focus(this.focusedItem);
|
||||
}
|
||||
@@ -390,8 +432,8 @@ export class ActionBar extends Disposable implements IActionRunner {
|
||||
|
||||
const startIndex = this.focusedItem;
|
||||
let item: IActionViewItem;
|
||||
|
||||
do {
|
||||
|
||||
if (this.options.preventLoopNavigation && this.focusedItem + 1 >= this.viewItems.length) {
|
||||
this.focusedItem = startIndex;
|
||||
return false;
|
||||
@@ -399,11 +441,7 @@ export class ActionBar extends Disposable implements IActionRunner {
|
||||
|
||||
this.focusedItem = (this.focusedItem + 1) % this.viewItems.length;
|
||||
item = this.viewItems[this.focusedItem];
|
||||
} while (this.focusedItem !== startIndex && !item.isEnabled());
|
||||
|
||||
if (this.focusedItem === startIndex && !item.isEnabled()) {
|
||||
this.focusedItem = undefined;
|
||||
}
|
||||
} while (this.focusedItem !== startIndex && this.options.focusOnlyEnabledItems && !item.isEnabled());
|
||||
|
||||
this.updateFocus();
|
||||
return true;
|
||||
@@ -419,7 +457,6 @@ export class ActionBar extends Disposable implements IActionRunner {
|
||||
|
||||
do {
|
||||
this.focusedItem = this.focusedItem - 1;
|
||||
|
||||
if (this.focusedItem < 0) {
|
||||
if (this.options.preventLoopNavigation) {
|
||||
this.focusedItem = startIndex;
|
||||
@@ -428,13 +465,9 @@ export class ActionBar extends Disposable implements IActionRunner {
|
||||
|
||||
this.focusedItem = this.viewItems.length - 1;
|
||||
}
|
||||
|
||||
item = this.viewItems[this.focusedItem];
|
||||
} while (this.focusedItem !== startIndex && !item.isEnabled());
|
||||
} while (this.focusedItem !== startIndex && this.options.focusOnlyEnabledItems && !item.isEnabled());
|
||||
|
||||
if (this.focusedItem === startIndex && !item.isEnabled()) {
|
||||
this.focusedItem = undefined;
|
||||
}
|
||||
|
||||
this.updateFocus(true);
|
||||
return true;
|
||||
@@ -450,12 +483,20 @@ export class ActionBar extends Disposable implements IActionRunner {
|
||||
const actionViewItem = item;
|
||||
|
||||
if (i === this.focusedItem) {
|
||||
if (types.isFunction(actionViewItem.isEnabled)) {
|
||||
if (actionViewItem.isEnabled() && types.isFunction(actionViewItem.focus)) {
|
||||
actionViewItem.focus(fromRight);
|
||||
} else {
|
||||
this.actionsList.focus({ preventScroll });
|
||||
}
|
||||
let focusItem = true;
|
||||
|
||||
if (!types.isFunction(actionViewItem.focus)) {
|
||||
focusItem = false;
|
||||
}
|
||||
|
||||
if (this.options.focusOnlyEnabledItems && types.isFunction(item.isEnabled) && !item.isEnabled()) {
|
||||
focusItem = false;
|
||||
}
|
||||
|
||||
if (focusItem) {
|
||||
actionViewItem.focus(fromRight);
|
||||
} else {
|
||||
this.actionsList.focus({ preventScroll });
|
||||
}
|
||||
} else {
|
||||
if (types.isFunction(actionViewItem.blur)) {
|
||||
|
||||
@@ -12,7 +12,7 @@ import { Event as BaseEvent, Emitter } from 'vs/base/common/event';
|
||||
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { Gesture, EventType as TouchEventType } from 'vs/base/browser/touch';
|
||||
import { renderLabelWithIcons } from 'vs/base/browser/ui/iconLabel/iconLabels';
|
||||
import { addDisposableListener, IFocusTracker, EventType, EventHelper, trackFocus, reset, removeTabIndexAndUpdateFocus } from 'vs/base/browser/dom';
|
||||
import { addDisposableListener, IFocusTracker, EventType, EventHelper, trackFocus, reset } from 'vs/base/browser/dom';
|
||||
import { IContextMenuProvider } from 'vs/base/browser/contextmenu';
|
||||
import { Action, IAction, IActionRunner } from 'vs/base/common/actions';
|
||||
import { CSSIcon, Codicon } from 'vs/base/common/codicons';
|
||||
@@ -214,7 +214,6 @@ export class Button extends Disposable implements IButton {
|
||||
} else {
|
||||
this._element.classList.add('disabled');
|
||||
this._element.setAttribute('aria-disabled', String(true));
|
||||
removeTabIndexAndUpdateFocus(this._element);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'vs/css!./checkbox';
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
import { Widget } from 'vs/base/browser/ui/widget';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
@@ -19,6 +18,7 @@ export interface ICheckboxOpts extends ICheckboxStyles {
|
||||
readonly icon?: CSSIcon;
|
||||
readonly title: string;
|
||||
readonly isChecked: boolean;
|
||||
readonly notFocusable?: boolean;
|
||||
}
|
||||
|
||||
export interface ICheckboxStyles {
|
||||
@@ -51,7 +51,8 @@ export class CheckboxActionViewItem extends BaseActionViewItem {
|
||||
this.checkbox = new Checkbox({
|
||||
actionClassName: this._action.class,
|
||||
isChecked: this._action.checked,
|
||||
title: this._action.label
|
||||
title: this._action.label,
|
||||
notFocusable: true
|
||||
});
|
||||
this.disposables.add(this.checkbox);
|
||||
this.disposables.add(this.checkbox.onChange(() => this._action.checked = !!this.checkbox && this.checkbox.checked, this));
|
||||
@@ -74,6 +75,26 @@ export class CheckboxActionViewItem extends BaseActionViewItem {
|
||||
}
|
||||
}
|
||||
|
||||
focus(): void {
|
||||
if (this.checkbox) {
|
||||
this.checkbox.domNode.tabIndex = 0;
|
||||
this.checkbox.focus();
|
||||
}
|
||||
}
|
||||
|
||||
blur(): void {
|
||||
if (this.checkbox) {
|
||||
this.checkbox.domNode.tabIndex = -1;
|
||||
this.checkbox.domNode.blur();
|
||||
}
|
||||
}
|
||||
|
||||
setFocusable(focusable: boolean): void {
|
||||
if (this.checkbox) {
|
||||
this.checkbox.domNode.tabIndex = focusable ? 0 : -1;
|
||||
}
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.disposables.dispose();
|
||||
super.dispose();
|
||||
@@ -113,7 +134,9 @@ export class Checkbox extends Widget {
|
||||
this.domNode = document.createElement('div');
|
||||
this.domNode.title = this._opts.title;
|
||||
this.domNode.classList.add(...classes);
|
||||
this.domNode.tabIndex = 0;
|
||||
if (!this._opts.notFocusable) {
|
||||
this.domNode.tabIndex = 0;
|
||||
}
|
||||
this.domNode.setAttribute('role', 'checkbox');
|
||||
this.domNode.setAttribute('aria-checked', String(this._checked));
|
||||
this.domNode.setAttribute('aria-label', this._opts.title);
|
||||
@@ -187,12 +210,10 @@ export class Checkbox extends Widget {
|
||||
}
|
||||
|
||||
enable(): void {
|
||||
this.domNode.tabIndex = 0;
|
||||
this.domNode.setAttribute('aria-disabled', String(false));
|
||||
}
|
||||
|
||||
disable(): void {
|
||||
DOM.removeTabIndexAndUpdateFocus(this.domNode);
|
||||
this.domNode.setAttribute('aria-disabled', String(true));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
.codicon-sync.codicon-modifier-spin, .codicon-loading.codicon-modifier-spin{
|
||||
.codicon-sync.codicon-modifier-spin, .codicon-loading.codicon-modifier-spin, .codicon-gear.codicon-modifier-spin {
|
||||
/* Use steps to throttle FPS to reduce CPU usage */
|
||||
animation: codicon-spin 1.5s steps(30) infinite;
|
||||
}
|
||||
|
||||
Binary file not shown.
@@ -13,5 +13,5 @@ export function formatRule(c: Codicon) {
|
||||
while (def instanceof Codicon) {
|
||||
def = def.definition;
|
||||
}
|
||||
return `.codicon-${c.id}:before { content: '${def.character}'; }`;
|
||||
return `.codicon-${c.id}:before { content: '${def.fontCharacter}'; }`;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'vs/css!./dropdown';
|
||||
import { Action, IAction, IActionRunner, IActionViewItemProvider } from 'vs/base/common/actions';
|
||||
import { Action, IAction, IActionRunner } from 'vs/base/common/actions';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview';
|
||||
import { ResolvedKeybinding } from 'vs/base/common/keyCodes';
|
||||
@@ -14,6 +14,7 @@ import { ActionViewItem, BaseActionViewItem, IActionViewItemOptions, IBaseAction
|
||||
import { IActionProvider, DropdownMenu, IDropdownMenuOptions, ILabelRenderer } from 'vs/base/browser/ui/dropdown/dropdown';
|
||||
import { IContextMenuProvider } from 'vs/base/browser/contextmenu';
|
||||
import { Codicon } from 'vs/base/common/codicons';
|
||||
import { IActionViewItemProvider } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
|
||||
export interface IKeybindingProvider {
|
||||
(action: IAction): ResolvedKeybinding | undefined;
|
||||
@@ -78,7 +79,6 @@ export class DropdownMenuActionViewItem extends BaseActionViewItem {
|
||||
|
||||
this.element.classList.add(...classNames);
|
||||
|
||||
this.element.tabIndex = 0;
|
||||
this.element.setAttribute('role', 'button');
|
||||
this.element.setAttribute('aria-haspopup', 'true');
|
||||
this.element.setAttribute('aria-expanded', 'false');
|
||||
@@ -173,12 +173,14 @@ export class ActionWithDropdownActionViewItem extends ActionViewItem {
|
||||
const menuActionsProvider = {
|
||||
getActions: () => {
|
||||
const actionsProvider = (<IActionWithDropdownActionViewItemOptions>this.options).menuActionsOrProvider;
|
||||
return [this._action, ...(Array.isArray(actionsProvider) ? actionsProvider : actionsProvider.getActions())];
|
||||
return [this._action, ...(Array.isArray(actionsProvider)
|
||||
? actionsProvider
|
||||
: (actionsProvider as IActionProvider).getActions()) // TODO: microsoft/TypeScript#42768
|
||||
];
|
||||
}
|
||||
};
|
||||
this.dropdownMenuActionViewItem = new DropdownMenuActionViewItem(this._register(new Action('dropdownAction', undefined)), menuActionsProvider, this.contextMenuProvider, { classNames: ['dropdown', ...Codicon.dropDownButton.classNamesArray, ...(<IActionWithDropdownActionViewItemOptions>this.options).menuActionClassNames || []] });
|
||||
this.dropdownMenuActionViewItem.render(this.element);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -20,5 +20,4 @@ export interface IHoverDelegateOptions {
|
||||
|
||||
export interface IHoverDelegate {
|
||||
showHover(options: IHoverDelegateOptions): IDisposable | undefined;
|
||||
hideHover(): void;
|
||||
}
|
||||
|
||||
@@ -192,13 +192,14 @@ export class IconLabel extends Disposable {
|
||||
}
|
||||
}
|
||||
|
||||
private static adjustXAndShowCustomHover(hoverOptions: IHoverDelegateOptions | undefined, mouseX: number | undefined, hoverDelegate: IHoverDelegate, isHovering: boolean) {
|
||||
private static adjustXAndShowCustomHover(hoverOptions: IHoverDelegateOptions | undefined, mouseX: number | undefined, hoverDelegate: IHoverDelegate, isHovering: boolean): IDisposable | undefined {
|
||||
if (hoverOptions && isHovering) {
|
||||
if (mouseX !== undefined) {
|
||||
(<IHoverDelegateTarget>hoverOptions.target).x = mouseX + 10;
|
||||
}
|
||||
hoverDelegate.showHover(hoverOptions);
|
||||
return hoverDelegate.showHover(hoverOptions);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private getTooltipForCustom(markdownTooltip: string | IIconLabelMarkdownString): (token: CancellationToken) => Promise<string | IMarkdownString | undefined> {
|
||||
@@ -224,17 +225,22 @@ export class IconLabel extends Disposable {
|
||||
let mouseX: number | undefined;
|
||||
let isHovering = false;
|
||||
let tokenSource: CancellationTokenSource;
|
||||
let hoverDisposable: IDisposable | undefined;
|
||||
function mouseOver(this: HTMLElement, e: MouseEvent): any {
|
||||
if (isHovering) {
|
||||
return;
|
||||
}
|
||||
tokenSource = new CancellationTokenSource();
|
||||
function mouseLeaveOrDown(this: HTMLElement, e: MouseEvent): any {
|
||||
isHovering = false;
|
||||
hoverOptions = undefined;
|
||||
tokenSource.dispose(true);
|
||||
mouseLeaveDisposable.dispose();
|
||||
mouseDownDisposable.dispose();
|
||||
if ((e.type === dom.EventType.MOUSE_DOWN) || (<any>e).fromElement === htmlElement) {
|
||||
hoverDisposable?.dispose();
|
||||
hoverDisposable = undefined;
|
||||
isHovering = false;
|
||||
hoverOptions = undefined;
|
||||
tokenSource.dispose(true);
|
||||
mouseLeaveDisposable.dispose();
|
||||
mouseDownDisposable.dispose();
|
||||
}
|
||||
}
|
||||
const mouseLeaveDisposable = domEvent(htmlElement, dom.EventType.MOUSE_LEAVE, true)(mouseLeaveOrDown.bind(htmlElement));
|
||||
const mouseDownDisposable = domEvent(htmlElement, dom.EventType.MOUSE_DOWN, true)(mouseLeaveOrDown.bind(htmlElement));
|
||||
@@ -257,7 +263,7 @@ export class IconLabel extends Disposable {
|
||||
target,
|
||||
anchorPosition: AnchorPosition.BELOW
|
||||
};
|
||||
IconLabel.adjustXAndShowCustomHover(hoverOptions, mouseX, hoverDelegate, isHovering);
|
||||
hoverDisposable = IconLabel.adjustXAndShowCustomHover(hoverOptions, mouseX, hoverDelegate, isHovering);
|
||||
|
||||
const resolvedTooltip = (await tooltip(tokenSource.token)) ?? (!isString(markdownTooltip) ? markdownTooltip.markdownNotSupportedFallback : undefined);
|
||||
if (resolvedTooltip) {
|
||||
@@ -267,11 +273,13 @@ export class IconLabel extends Disposable {
|
||||
anchorPosition: AnchorPosition.BELOW
|
||||
};
|
||||
// awaiting the tooltip could take a while. Make sure we're still hovering.
|
||||
IconLabel.adjustXAndShowCustomHover(hoverOptions, mouseX, hoverDelegate, isHovering);
|
||||
} else {
|
||||
hoverDelegate.hideHover();
|
||||
hoverDisposable = IconLabel.adjustXAndShowCustomHover(hoverOptions, mouseX, hoverDelegate, isHovering);
|
||||
} else if (hoverDisposable) {
|
||||
hoverDisposable.dispose();
|
||||
hoverDisposable = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
mouseMoveDisposable.dispose();
|
||||
}, hoverDelay);
|
||||
|
||||
@@ -6,8 +6,7 @@
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import { CSSIcon } from 'vs/base/common/codicons';
|
||||
|
||||
const labelWithIconsRegex = /(\\)?\$\(([a-z\-]+(?:~[a-z\-]+)?)\)/gi;
|
||||
|
||||
const labelWithIconsRegex = new RegExp(`(\\\\)?\\$\\((${CSSIcon.iconNameExpression}(?:${CSSIcon.iconModifierExpression})?)\\)`, 'g');
|
||||
export function renderLabelWithIcons(text: string): Array<HTMLSpanElement | string> {
|
||||
const elements = new Array<HTMLSpanElement | string>();
|
||||
let match: RegExpMatchArray | null;
|
||||
|
||||
@@ -89,7 +89,6 @@
|
||||
padding: 0.4em;
|
||||
font-size: 12px;
|
||||
line-height: 17px;
|
||||
min-height: 34px;
|
||||
margin-top: -1px;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
@@ -292,6 +292,9 @@ export class InputBox extends Widget {
|
||||
|
||||
if (range) {
|
||||
this.input.setSelectionRange(range.start, range.end);
|
||||
if (range.end === this.input.value.length) {
|
||||
this.input.scrollLeft = this.input.scrollWidth;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ export interface IListVirtualDelegate<T> {
|
||||
}
|
||||
|
||||
export interface IListRenderer<T, TTemplateData> {
|
||||
templateId: string;
|
||||
readonly templateId: string;
|
||||
renderTemplate(container: HTMLElement): TTemplateData;
|
||||
renderElement(element: T, index: number, templateData: TTemplateData, height: number | undefined): void;
|
||||
disposeElement?(element: T, index: number, templateData: TTemplateData, height: number | undefined): void;
|
||||
|
||||
@@ -64,6 +64,7 @@ export interface IListViewOptions<T> extends IListViewOptionsUpdate {
|
||||
readonly mouseSupport?: boolean;
|
||||
readonly accessibilityProvider?: IListViewAccessibilityProvider<T>;
|
||||
readonly transformOptimization?: boolean;
|
||||
readonly alwaysConsumeMouseWheel?: boolean;
|
||||
}
|
||||
|
||||
const DefaultOptions = {
|
||||
@@ -80,7 +81,8 @@ const DefaultOptions = {
|
||||
drop() { }
|
||||
},
|
||||
horizontalScrolling: false,
|
||||
transformOptimization: true
|
||||
transformOptimization: true,
|
||||
alwaysConsumeMouseWheel: true,
|
||||
};
|
||||
|
||||
export class ElementsDragAndDropData<T, TContext = void> implements IDragAndDropData {
|
||||
@@ -327,6 +329,7 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
|
||||
|
||||
this.scrollable = new Scrollable(getOrDefault(options, o => o.smoothScrolling, false) ? 125 : 0, cb => scheduleAtNextAnimationFrame(cb));
|
||||
this.scrollableElement = this.disposables.add(new SmoothScrollableElement(this.rowsContainer, {
|
||||
alwaysConsumeMouseWheel: getOrDefault(options, o => o.alwaysConsumeMouseWheel, DefaultOptions.alwaysConsumeMouseWheel),
|
||||
horizontal: ScrollbarVisibility.Auto,
|
||||
vertical: getOrDefault(options, o => o.verticalScrollMode, DefaultOptions.verticalScrollMode),
|
||||
useShadows: getOrDefault(options, o => o.useShadows, DefaultOptions.useShadows),
|
||||
@@ -433,7 +436,7 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
|
||||
const removeRange = Range.intersect(previousRenderRange, deleteRange);
|
||||
|
||||
// try to reuse rows, avoid removing them from DOM
|
||||
const rowsToDispose = new Map<string, [IRow, T, number, number][]>();
|
||||
const rowsToDispose = new Map<string, IRow[]>();
|
||||
for (let i = removeRange.start; i < removeRange.end; i++) {
|
||||
const item = this.items[i];
|
||||
item.dragStartDisposable.dispose();
|
||||
@@ -446,7 +449,13 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
|
||||
rowsToDispose.set(item.templateId, rows);
|
||||
}
|
||||
|
||||
rows.push([item.row, item.element, i, item.size]);
|
||||
const renderer = this.renderers.get(item.templateId);
|
||||
|
||||
if (renderer && renderer.disposeElement) {
|
||||
renderer.disposeElement(item.element, i, item.row.templateData, item.size);
|
||||
}
|
||||
|
||||
rows.push(item.row);
|
||||
}
|
||||
|
||||
item.row = null;
|
||||
@@ -476,8 +485,8 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
|
||||
if (start === 0 && deleteCount >= this.items.length) {
|
||||
this.rangeMap = new RangeMap();
|
||||
this.rangeMap.splice(0, 0, inserted);
|
||||
deleted = this.items;
|
||||
this.items = inserted;
|
||||
deleted = [];
|
||||
} else {
|
||||
this.rangeMap.splice(start, deleteCount, inserted);
|
||||
deleted = this.items.splice(start, deleteCount, ...inserted);
|
||||
@@ -509,31 +518,13 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
|
||||
for (let i = range.start; i < range.end; i++) {
|
||||
const item = this.items[i];
|
||||
const rows = rowsToDispose.get(item.templateId);
|
||||
const rowData = rows?.pop();
|
||||
|
||||
if (!rowData) {
|
||||
this.insertItemInDOM(i, beforeElement);
|
||||
} else {
|
||||
const [row, element, index, size] = rowData;
|
||||
const renderer = this.renderers.get(item.templateId);
|
||||
|
||||
if (renderer && renderer.disposeElement) {
|
||||
renderer.disposeElement(element, index, row.templateData, size);
|
||||
}
|
||||
|
||||
this.insertItemInDOM(i, beforeElement, row);
|
||||
}
|
||||
const row = rows?.pop();
|
||||
this.insertItemInDOM(i, beforeElement, row);
|
||||
}
|
||||
}
|
||||
|
||||
for (const [templateId, rows] of rowsToDispose) {
|
||||
for (const [row, element, index, size] of rows) {
|
||||
const renderer = this.renderers.get(templateId);
|
||||
|
||||
if (renderer && renderer.disposeElement) {
|
||||
renderer.disposeElement(element, index, row.templateData, size);
|
||||
}
|
||||
|
||||
for (const rows of rowsToDispose.values()) {
|
||||
for (const row of rows) {
|
||||
this.cache.release(row);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ import { Gesture } from 'vs/base/browser/touch';
|
||||
import { KeyCode } from 'vs/base/common/keyCodes';
|
||||
import { StandardKeyboardEvent, IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
import { Event, Emitter, EventBufferer } from 'vs/base/common/event';
|
||||
import { domEvent } from 'vs/base/browser/event';
|
||||
import { domEvent, stopEvent } from 'vs/base/browser/event';
|
||||
import { IListVirtualDelegate, IListRenderer, IListEvent, IListContextMenuEvent, IListMouseEvent, IListTouchEvent, IListGestureEvent, IIdentityProvider, IKeyboardNavigationLabelProvider, IListDragAndDrop, IListDragOverReaction, ListError, IKeyboardNavigationDelegate } from './list';
|
||||
import { ListView, IListViewOptions, IListViewDragAndDrop, IListViewAccessibilityProvider, IListViewOptionsUpdate } from './listView';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
@@ -761,6 +761,11 @@ export class DefaultStyleController implements IStyleController {
|
||||
`);
|
||||
}
|
||||
|
||||
if (styles.listInactiveFocusForeground) {
|
||||
content.push(`.monaco-list${suffix} .monaco-list-row.focused { color: ${styles.listInactiveFocusForeground}; }`);
|
||||
content.push(`.monaco-list${suffix} .monaco-list-row.focused:hover { color: ${styles.listInactiveFocusForeground}; }`); // overwrite :hover style in this case!
|
||||
}
|
||||
|
||||
if (styles.listInactiveFocusBackground) {
|
||||
content.push(`.monaco-list${suffix} .monaco-list-row.focused { background-color: ${styles.listInactiveFocusBackground}; }`);
|
||||
content.push(`.monaco-list${suffix} .monaco-list-row.focused:hover { background-color: ${styles.listInactiveFocusBackground}; }`); // overwrite :hover style in this case!
|
||||
@@ -776,7 +781,7 @@ export class DefaultStyleController implements IStyleController {
|
||||
}
|
||||
|
||||
if (styles.listHoverBackground) {
|
||||
content.push(`.monaco-list${suffix}:not(.drop-target) .monaco-list-row:hover:not(.selected):not(.focused) { background-color: ${styles.listHoverBackground}; }`);
|
||||
content.push(`.monaco-list${suffix}:not(.drop-target) .monaco-list-row:hover:not(.selected):not(.focused) { background-color: ${styles.listHoverBackground}; }`);
|
||||
}
|
||||
|
||||
if (styles.listHoverForeground) {
|
||||
@@ -826,6 +831,14 @@ export class DefaultStyleController implements IStyleController {
|
||||
content.push(`.monaco-list-type-filter { box-shadow: 1px 1px 1px ${styles.listMatchesShadow}; }`);
|
||||
}
|
||||
|
||||
if (styles.tableColumnsBorder) {
|
||||
content.push(`
|
||||
.monaco-table:hover > .monaco-split-view2,
|
||||
.monaco-table:hover > .monaco-split-view2 .monaco-sash.vertical::before {
|
||||
border-color: ${styles.tableColumnsBorder};
|
||||
}`);
|
||||
}
|
||||
|
||||
this.styleElement.textContent = content.join('\n');
|
||||
}
|
||||
}
|
||||
@@ -854,6 +867,7 @@ export interface IListOptions<T> {
|
||||
readonly additionalScrollHeight?: number;
|
||||
readonly transformOptimization?: boolean;
|
||||
readonly smoothScrolling?: boolean;
|
||||
readonly alwaysConsumeMouseWheel?: boolean;
|
||||
}
|
||||
|
||||
export interface IListStyles {
|
||||
@@ -866,6 +880,7 @@ export interface IListStyles {
|
||||
listFocusAndSelectionForeground?: Color;
|
||||
listInactiveSelectionBackground?: Color;
|
||||
listInactiveSelectionForeground?: Color;
|
||||
listInactiveFocusForeground?: Color;
|
||||
listInactiveFocusBackground?: Color;
|
||||
listHoverBackground?: Color;
|
||||
listHoverForeground?: Color;
|
||||
@@ -879,6 +894,7 @@ export interface IListStyles {
|
||||
listFilterWidgetNoMatchesOutline?: Color;
|
||||
listMatchesShadow?: Color;
|
||||
treeIndentGuidesStroke?: Color;
|
||||
tableColumnsBorder?: Color;
|
||||
}
|
||||
|
||||
const defaultStyles: IListStyles = {
|
||||
@@ -890,7 +906,8 @@ const defaultStyles: IListStyles = {
|
||||
listInactiveSelectionBackground: Color.fromHex('#3F3F46'),
|
||||
listHoverBackground: Color.fromHex('#2A2D2E'),
|
||||
listDropBackground: Color.fromHex('#383B3D'),
|
||||
treeIndentGuidesStroke: Color.fromHex('#a9a9a9')
|
||||
treeIndentGuidesStroke: Color.fromHex('#a9a9a9'),
|
||||
tableColumnsBorder: Color.fromHex('#cccccc').transparent(0.2)
|
||||
};
|
||||
|
||||
const DefaultOptions: IListOptions<any> = {
|
||||
@@ -1148,35 +1165,43 @@ export class List<T> implements ISpliceable<T>, IThemable, IDisposable {
|
||||
get onTouchStart(): Event<IListTouchEvent<T>> { return this.view.onTouchStart; }
|
||||
get onTap(): Event<IListGestureEvent<T>> { return this.view.onTap; }
|
||||
|
||||
private didJustPressContextMenuKey: boolean = false;
|
||||
/**
|
||||
* Possible context menu trigger events:
|
||||
* - ContextMenu key
|
||||
* - Shift F10
|
||||
* - Ctrl Option Shift M (macOS with VoiceOver)
|
||||
* - Mouse right click
|
||||
*/
|
||||
@memoize get onContextMenu(): Event<IListContextMenuEvent<T>> {
|
||||
const fromKeydown = Event.chain(domEvent(this.view.domNode, 'keydown'))
|
||||
let didJustPressContextMenuKey = false;
|
||||
|
||||
const fromKeyDown = Event.chain(domEvent(this.view.domNode, 'keydown'))
|
||||
.map(e => new StandardKeyboardEvent(e))
|
||||
.filter(e => this.didJustPressContextMenuKey = e.keyCode === KeyCode.ContextMenu || (e.shiftKey && e.keyCode === KeyCode.F10))
|
||||
.filter(e => { e.preventDefault(); e.stopPropagation(); return false; })
|
||||
.filter(e => didJustPressContextMenuKey = e.keyCode === KeyCode.ContextMenu || (e.shiftKey && e.keyCode === KeyCode.F10))
|
||||
.map(stopEvent)
|
||||
.filter(() => false)
|
||||
.event as Event<any>;
|
||||
|
||||
const fromKeyup = Event.chain(domEvent(this.view.domNode, 'keyup'))
|
||||
.filter(() => {
|
||||
const didJustPressContextMenuKey = this.didJustPressContextMenuKey;
|
||||
this.didJustPressContextMenuKey = false;
|
||||
return didJustPressContextMenuKey;
|
||||
})
|
||||
.filter(() => this.getFocus().length > 0 && !!this.view.domElement(this.getFocus()[0]))
|
||||
.map(browserEvent => {
|
||||
const index = this.getFocus()[0];
|
||||
const element = this.view.element(index);
|
||||
const anchor = this.view.domElement(index) as HTMLElement;
|
||||
const fromKeyUp = Event.chain(domEvent(this.view.domNode, 'keyup'))
|
||||
.forEach(() => didJustPressContextMenuKey = false)
|
||||
.map(e => new StandardKeyboardEvent(e))
|
||||
.filter(e => e.keyCode === KeyCode.ContextMenu || (e.shiftKey && e.keyCode === KeyCode.F10))
|
||||
.map(stopEvent)
|
||||
.map(({ browserEvent }) => {
|
||||
const focus = this.getFocus();
|
||||
const index = focus.length ? focus[0] : undefined;
|
||||
const element = typeof index !== 'undefined' ? this.view.element(index) : undefined;
|
||||
const anchor = typeof index !== 'undefined' ? this.view.domElement(index) as HTMLElement : this.view.domNode;
|
||||
return { index, element, anchor, browserEvent };
|
||||
})
|
||||
.event;
|
||||
|
||||
const fromMouse = Event.chain(this.view.onContextMenu)
|
||||
.filter(() => !this.didJustPressContextMenuKey)
|
||||
.filter(_ => !didJustPressContextMenuKey)
|
||||
.map(({ element, index, browserEvent }) => ({ element, index, anchor: { x: browserEvent.clientX + 1, y: browserEvent.clientY }, browserEvent }))
|
||||
.event;
|
||||
|
||||
return Event.any<IListContextMenuEvent<T>>(fromKeydown, fromKeyup, fromMouse);
|
||||
return Event.any<IListContextMenuEvent<T>>(fromKeyDown, fromKeyUp, fromMouse);
|
||||
}
|
||||
|
||||
get onKeyDown(): Event<KeyboardEvent> { return domEvent(this.view.domNode, 'keydown'); }
|
||||
@@ -1380,7 +1405,7 @@ export class List<T> implements ISpliceable<T>, IThemable, IDisposable {
|
||||
}
|
||||
|
||||
domFocus(): void {
|
||||
this.view.domNode.focus();
|
||||
this.view.domNode.focus({ preventScroll: true });
|
||||
}
|
||||
|
||||
layout(height?: number, width?: number): void {
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import * as strings from 'vs/base/common/strings';
|
||||
import { IActionRunner, IAction, SubmenuAction, Separator, IActionViewItemProvider, EmptySubmenuAction } from 'vs/base/common/actions';
|
||||
import { ActionBar, ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { IActionRunner, IAction, SubmenuAction, Separator, EmptySubmenuAction } from 'vs/base/common/actions';
|
||||
import { ActionBar, ActionsOrientation, IActionViewItemProvider } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { ResolvedKeybinding, KeyCode } from 'vs/base/common/keyCodes';
|
||||
import { EventType, EventHelper, EventLike, removeTabIndexAndUpdateFocus, isAncestor, addDisposableListener, append, $, clearNode, createStyleSheet, isInShadowDOM, getActiveElement, Dimension, IDomNodePagePosition } from 'vs/base/browser/dom';
|
||||
import { EventType, EventHelper, EventLike, isAncestor, addDisposableListener, append, $, clearNode, createStyleSheet, isInShadowDOM, getActiveElement, Dimension, IDomNodePagePosition } from 'vs/base/browser/dom';
|
||||
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
import { RunOnceScheduler } from 'vs/base/common/async';
|
||||
import { DisposableStore } from 'vs/base/common/lifecycle';
|
||||
@@ -86,6 +86,7 @@ export class Menu extends ActionBar {
|
||||
context: options.context,
|
||||
actionRunner: options.actionRunner,
|
||||
ariaLabel: options.ariaLabel,
|
||||
focusOnlyEnabledItems: true,
|
||||
triggerKeys: { keys: [KeyCode.Enter, ...(isMacintosh || isLinux ? [KeyCode.Space] : [])], keyDown: true }
|
||||
});
|
||||
|
||||
@@ -617,20 +618,23 @@ class BaseMenuActionViewItem extends BaseActionViewItem {
|
||||
if (this.getAction().enabled) {
|
||||
if (this.element) {
|
||||
this.element.classList.remove('disabled');
|
||||
this.element.removeAttribute('aria-disabled');
|
||||
}
|
||||
|
||||
if (this.item) {
|
||||
this.item.classList.remove('disabled');
|
||||
this.item.removeAttribute('aria-disabled');
|
||||
this.item.tabIndex = 0;
|
||||
}
|
||||
} else {
|
||||
if (this.element) {
|
||||
this.element.classList.add('disabled');
|
||||
this.element.setAttribute('aria-disabled', 'true');
|
||||
}
|
||||
|
||||
if (this.item) {
|
||||
this.item.classList.add('disabled');
|
||||
removeTabIndexAndUpdateFocus(this.item);
|
||||
this.item.setAttribute('aria-disabled', 'true');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
}
|
||||
|
||||
.menubar .menubar-menu-items-holder {
|
||||
position: absolute;
|
||||
position: fixed;
|
||||
left: 0px;
|
||||
opacity: 1;
|
||||
z-index: 2000;
|
||||
|
||||
@@ -746,7 +746,7 @@ export class MenuBar extends Disposable {
|
||||
private setUnfocusedState(): void {
|
||||
if (this.options.visibility === 'toggle' || this.options.visibility === 'hidden') {
|
||||
this.focusState = MenubarState.HIDDEN;
|
||||
} else if (this.options.visibility === 'default' && browser.isFullscreen()) {
|
||||
} else if (this.options.visibility === 'classic' && browser.isFullscreen()) {
|
||||
this.focusState = MenubarState.HIDDEN;
|
||||
} else {
|
||||
this.focusState = MenubarState.VISIBLE;
|
||||
@@ -838,6 +838,22 @@ export class MenuBar extends Disposable {
|
||||
this._mnemonicsInUse = value;
|
||||
}
|
||||
|
||||
private get shouldAltKeyFocus(): boolean {
|
||||
if (isMacintosh) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this.options.disableAltFocus) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (this.options.visibility === 'toggle') {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public get onVisibilityChange(): Event<boolean> {
|
||||
return this._onVisibilityChange.event;
|
||||
}
|
||||
@@ -869,7 +885,7 @@ export class MenuBar extends Disposable {
|
||||
}
|
||||
|
||||
// Prevent alt-key default if the menu is not hidden and we use alt to focus
|
||||
if (modifierKeyStatus.event && !this.options.disableAltFocus) {
|
||||
if (modifierKeyStatus.event && this.shouldAltKeyFocus) {
|
||||
if (ScanCodeUtils.toEnum(modifierKeyStatus.event.code) === ScanCode.AltLeft) {
|
||||
modifierKeyStatus.event.preventDefault();
|
||||
}
|
||||
@@ -885,7 +901,7 @@ export class MenuBar extends Disposable {
|
||||
// Clean alt key press and release
|
||||
if (allModifiersReleased && modifierKeyStatus.lastKeyPressed === 'alt' && modifierKeyStatus.lastKeyReleased === 'alt') {
|
||||
if (!this.awaitingAltRelease) {
|
||||
if (!this.isFocused && !(this.options.disableAltFocus && this.options.visibility !== 'toggle')) {
|
||||
if (!this.isFocused && this.shouldAltKeyFocus) {
|
||||
this.mnemonicsInUse = true;
|
||||
this.focusedMenu = { index: this.numMenusShown > 0 ? 0 : MenuBar.OVERFLOW_INDEX };
|
||||
this.focusState = MenubarState.FOCUSED;
|
||||
|
||||
@@ -60,8 +60,7 @@
|
||||
height: var(--sash-size);
|
||||
}
|
||||
|
||||
.monaco-sash:not(.disabled).orthogonal-start::before,
|
||||
.monaco-sash:not(.disabled).orthogonal-end::after {
|
||||
.monaco-sash:not(.disabled) > .orthogonal-drag-handle {
|
||||
content: " ";
|
||||
height: calc(var(--sash-size) * 2);
|
||||
width: calc(var(--sash-size) * 2);
|
||||
@@ -71,30 +70,34 @@
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.monaco-sash.horizontal.orthogonal-edge-north:not(.disabled).orthogonal-start::before,
|
||||
.monaco-sash.horizontal.orthogonal-edge-south:not(.disabled).orthogonal-end::after {
|
||||
.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-end::after,
|
||||
.monaco-sash.horizontal.orthogonal-edge-south:not(.disabled).orthogonal-start::before {
|
||||
.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.orthogonal-start.vertical::before {
|
||||
left: -calc(var(--sash-size) / 2);
|
||||
.monaco-sash.vertical > .orthogonal-drag-handle.start {
|
||||
left: calc(var(--sash-size) / -2);
|
||||
top: calc(var(--sash-size) * -1);
|
||||
}
|
||||
.monaco-sash.orthogonal-end.vertical::after {
|
||||
left: -calc(var(--sash-size) / 2);
|
||||
.monaco-sash.vertical > .orthogonal-drag-handle.end {
|
||||
left: calc(var(--sash-size) / -2);
|
||||
bottom: calc(var(--sash-size) * -1);
|
||||
}
|
||||
.monaco-sash.orthogonal-start.horizontal::before {
|
||||
top: -calc(var(--sash-size) / 2);
|
||||
.monaco-sash.horizontal > .orthogonal-drag-handle.start {
|
||||
top: calc(var(--sash-size) / -2);
|
||||
left: calc(var(--sash-size) * -1);
|
||||
}
|
||||
.monaco-sash.orthogonal-end.horizontal::after {
|
||||
top: -calc(var(--sash-size) / 2);
|
||||
.monaco-sash.horizontal > .orthogonal-drag-handle.end {
|
||||
top: calc(var(--sash-size) / -2);
|
||||
right: calc(var(--sash-size) * -1);
|
||||
}
|
||||
|
||||
@@ -113,7 +116,6 @@
|
||||
background: rgba(0, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
.monaco-sash.debug:not(.disabled).orthogonal-start::before,
|
||||
.monaco-sash.debug:not(.disabled).orthogonal-end::after {
|
||||
.monaco-sash.debug:not(.disabled) > .orthogonal-drag-handle {
|
||||
background: red;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'vs/css!./sash';
|
||||
import { IDisposable, dispose, Disposable, DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { IDisposable, dispose, Disposable, DisposableStore, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { isMacintosh } from 'vs/base/common/platform';
|
||||
import * as types from 'vs/base/common/types';
|
||||
import { EventType, GestureEvent, Gesture } from 'vs/base/browser/touch';
|
||||
@@ -12,8 +12,10 @@ import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { getElementsByTagName, EventHelper, createStyleSheet, addDisposableListener, append, $ } from 'vs/base/browser/dom';
|
||||
import { domEvent } from 'vs/base/browser/event';
|
||||
import { Delayer } from 'vs/base/common/async';
|
||||
|
||||
const DEBUG = false;
|
||||
let DEBUG = false;
|
||||
// DEBUG = Boolean("true"); // done "weirdly" so that a lint warning prevents you from pushing this
|
||||
|
||||
export interface ISashLayoutProvider { }
|
||||
|
||||
@@ -86,6 +88,7 @@ export class Sash extends Disposable {
|
||||
private hidden: boolean;
|
||||
private orientation!: Orientation;
|
||||
private size: number;
|
||||
private hoverDelayer = this._register(new Delayer(300));
|
||||
|
||||
private _state: SashState = SashState.Enabled;
|
||||
get state(): SashState { return this._state; }
|
||||
@@ -121,15 +124,29 @@ export class Sash extends Disposable {
|
||||
|
||||
private readonly orthogonalStartSashDisposables = this._register(new DisposableStore());
|
||||
private _orthogonalStartSash: Sash | undefined;
|
||||
private readonly orthogonalStartDragHandleDisposables = this._register(new DisposableStore());
|
||||
private _orthogonalStartDragHandle: HTMLElement | undefined;
|
||||
get orthogonalStartSash(): Sash | undefined { return this._orthogonalStartSash; }
|
||||
set orthogonalStartSash(sash: Sash | undefined) {
|
||||
this.orthogonalStartDragHandleDisposables.clear();
|
||||
this.orthogonalStartSashDisposables.clear();
|
||||
|
||||
if (sash) {
|
||||
this.orthogonalStartSashDisposables.add(sash.onDidEnablementChange(this.onOrthogonalStartSashEnablementChange, this));
|
||||
this.onOrthogonalStartSashEnablementChange(sash.state);
|
||||
} else {
|
||||
this.onOrthogonalStartSashEnablementChange(SashState.Disabled);
|
||||
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()));
|
||||
domEvent(this._orthogonalStartDragHandle, 'mouseenter')
|
||||
(() => Sash.onMouseEnter(sash), undefined, this.orthogonalStartDragHandleDisposables);
|
||||
domEvent(this._orthogonalStartDragHandle, 'mouseleave')
|
||||
(() => Sash.onMouseLeave(sash), undefined, this.orthogonalStartDragHandleDisposables);
|
||||
}
|
||||
};
|
||||
|
||||
this.orthogonalStartSashDisposables.add(sash.onDidEnablementChange(onChange, this));
|
||||
onChange(sash.state);
|
||||
}
|
||||
|
||||
this._orthogonalStartSash = sash;
|
||||
@@ -137,15 +154,29 @@ export class Sash extends Disposable {
|
||||
|
||||
private readonly orthogonalEndSashDisposables = this._register(new DisposableStore());
|
||||
private _orthogonalEndSash: Sash | undefined;
|
||||
private readonly orthogonalEndDragHandleDisposables = this._register(new DisposableStore());
|
||||
private _orthogonalEndDragHandle: HTMLElement | undefined;
|
||||
get orthogonalEndSash(): Sash | undefined { return this._orthogonalEndSash; }
|
||||
set orthogonalEndSash(sash: Sash | undefined) {
|
||||
this.orthogonalEndDragHandleDisposables.clear();
|
||||
this.orthogonalEndSashDisposables.clear();
|
||||
|
||||
if (sash) {
|
||||
this.orthogonalEndSashDisposables.add(sash.onDidEnablementChange(this.onOrthogonalEndSashEnablementChange, this));
|
||||
this.onOrthogonalEndSashEnablementChange(sash.state);
|
||||
} else {
|
||||
this.onOrthogonalEndSashEnablementChange(SashState.Disabled);
|
||||
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()));
|
||||
domEvent(this._orthogonalEndDragHandle, 'mouseenter')
|
||||
(() => Sash.onMouseEnter(sash), undefined, this.orthogonalEndDragHandleDisposables);
|
||||
domEvent(this._orthogonalEndDragHandle, 'mouseleave')
|
||||
(() => Sash.onMouseLeave(sash), undefined, this.orthogonalEndDragHandleDisposables);
|
||||
}
|
||||
};
|
||||
|
||||
this.orthogonalEndSashDisposables.add(sash.onDidEnablementChange(onChange, this));
|
||||
onChange(sash.state);
|
||||
}
|
||||
|
||||
this._orthogonalEndSash = sash;
|
||||
@@ -168,6 +199,8 @@ export class Sash extends Disposable {
|
||||
|
||||
this._register(domEvent(this.el, 'mousedown')(this.onMouseDown, this));
|
||||
this._register(domEvent(this.el, 'dblclick')(this.onMouseDoubleClick, this));
|
||||
this._register(domEvent(this.el, 'mouseenter')(() => Sash.onMouseEnter(this)));
|
||||
this._register(domEvent(this.el, 'mouseleave')(() => Sash.onMouseLeave(this)));
|
||||
|
||||
this._register(Gesture.addTarget(this.el));
|
||||
this._register(domEvent(this.el, EventType.Start)(this.onTouchStart, this));
|
||||
@@ -359,12 +392,34 @@ export class Sash extends Disposable {
|
||||
}
|
||||
}));
|
||||
|
||||
listeners.push(addDisposableListener(this.el, EventType.End, (event: GestureEvent) => {
|
||||
listeners.push(addDisposableListener(this.el, EventType.End, () => {
|
||||
this._onDidEnd.fire();
|
||||
dispose(listeners);
|
||||
}));
|
||||
}
|
||||
|
||||
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'));
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
layout(): void {
|
||||
if (this.orientation === Orientation.VERTICAL) {
|
||||
const verticalProvider = (<IVerticalSashLayoutProvider>this.layoutProvider);
|
||||
@@ -407,27 +462,13 @@ export class Sash extends Disposable {
|
||||
return this.hidden;
|
||||
}
|
||||
|
||||
private onOrthogonalStartSashEnablementChange(state: SashState): void {
|
||||
this.el.classList.toggle('orthogonal-start', state !== SashState.Disabled);
|
||||
}
|
||||
|
||||
private onOrthogonalEndSashEnablementChange(state: SashState): void {
|
||||
this.el.classList.toggle('orthogonal-end', state !== SashState.Disabled);
|
||||
}
|
||||
|
||||
private getOrthogonalSash(e: MouseEvent): Sash | undefined {
|
||||
if (this.orientation === Orientation.VERTICAL) {
|
||||
if (e.offsetY <= this.size) {
|
||||
return this.orthogonalStartSash;
|
||||
} else if (e.offsetY >= this.el.clientHeight - this.size) {
|
||||
return this.orthogonalEndSash;
|
||||
}
|
||||
} else {
|
||||
if (e.offsetX <= this.size) {
|
||||
return this.orthogonalStartSash;
|
||||
} else if (e.offsetX >= this.el.clientWidth - this.size) {
|
||||
return this.orthogonalEndSash;
|
||||
}
|
||||
if (!e.target || !(e.target instanceof HTMLElement)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (e.target.classList.contains('orthogonal-drag-handle')) {
|
||||
return e.target.classList.contains('start') ? this.orthogonalStartSash : this.orthogonalEndSash;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
|
||||
@@ -736,8 +736,8 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi
|
||||
|
||||
this._register(onSelectDropDownKeyDown.filter(e => e.keyCode === KeyCode.Enter).on(e => this.onEnter(e), this));
|
||||
this._register(onSelectDropDownKeyDown.filter(e => e.keyCode === KeyCode.Escape).on(e => this.onEscape(e), this));
|
||||
this._register(onSelectDropDownKeyDown.filter(e => e.keyCode === KeyCode.UpArrow).on(this.onUpArrow, this));
|
||||
this._register(onSelectDropDownKeyDown.filter(e => e.keyCode === KeyCode.DownArrow).on(this.onDownArrow, this));
|
||||
this._register(onSelectDropDownKeyDown.filter(e => e.keyCode === KeyCode.UpArrow).on(e => this.onUpArrow(e), this));
|
||||
this._register(onSelectDropDownKeyDown.filter(e => e.keyCode === KeyCode.DownArrow).on(e => this.onDownArrow(e), this));
|
||||
this._register(onSelectDropDownKeyDown.filter(e => e.keyCode === KeyCode.PageDown).on(this.onPageDown, this));
|
||||
this._register(onSelectDropDownKeyDown.filter(e => e.keyCode === KeyCode.PageUp).on(this.onPageUp, this));
|
||||
this._register(onSelectDropDownKeyDown.filter(e => e.keyCode === KeyCode.Home).on(this.onHome, this));
|
||||
@@ -916,8 +916,9 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi
|
||||
}
|
||||
|
||||
// List navigation - have to handle a disabled option (jump over)
|
||||
private onDownArrow(): void {
|
||||
private onDownArrow(e: StandardKeyboardEvent): void {
|
||||
if (this.selected < this.options.length - 1) {
|
||||
dom.EventHelper.stop(e, true);
|
||||
|
||||
// Skip disabled options
|
||||
const nextOptionDisabled = this.options[this.selected + 1].isDisabled;
|
||||
@@ -937,8 +938,9 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi
|
||||
}
|
||||
}
|
||||
|
||||
private onUpArrow(): void {
|
||||
private onUpArrow(e: StandardKeyboardEvent): void {
|
||||
if (this.selected > 0) {
|
||||
dom.EventHelper.stop(e, true);
|
||||
// Skip disabled options
|
||||
const previousOptionDisabled = this.options[this.selected - 1].isDisabled;
|
||||
if (previousOptionDisabled && this.selected > 1) {
|
||||
|
||||
@@ -54,6 +54,7 @@
|
||||
|
||||
/* 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;
|
||||
|
||||
@@ -432,7 +432,7 @@ export class PaneView extends Disposable {
|
||||
|
||||
private dnd: IPaneDndController | undefined;
|
||||
private dndContext: IDndContext = { draggable: null };
|
||||
private el: HTMLElement;
|
||||
readonly element: HTMLElement;
|
||||
private paneItems: IPaneItem[] = [];
|
||||
private orthogonalSize: number = 0;
|
||||
private size: number = 0;
|
||||
@@ -450,8 +450,8 @@ export class PaneView extends Disposable {
|
||||
|
||||
this.dnd = options.dnd;
|
||||
this.orientation = options.orientation ?? Orientation.VERTICAL;
|
||||
this.el = append(container, $('.monaco-pane-view'));
|
||||
this.splitview = this._register(new SplitView(this.el, { orientation: this.orientation }));
|
||||
this.element = append(container, $('.monaco-pane-view'));
|
||||
this.splitview = this._register(new SplitView(this.element, { orientation: this.orientation }));
|
||||
this.onDidSashChange = this.splitview.onDidSashChange;
|
||||
}
|
||||
|
||||
@@ -534,9 +534,9 @@ export class PaneView extends Disposable {
|
||||
const paneSizes = this.paneItems.map(pane => this.getPaneSize(pane.pane));
|
||||
|
||||
this.splitview.dispose();
|
||||
clearNode(this.el);
|
||||
clearNode(this.element);
|
||||
|
||||
this.splitview = this._register(new SplitView(this.el, { orientation: this.orientation }));
|
||||
this.splitview = this._register(new SplitView(this.element, { orientation: this.orientation }));
|
||||
|
||||
const newOrthogonalSize = this.orientation === Orientation.VERTICAL ? width : height;
|
||||
const newSize = this.orientation === Orientation.HORIZONTAL ? width : height;
|
||||
@@ -560,11 +560,11 @@ export class PaneView extends Disposable {
|
||||
window.clearTimeout(this.animationTimer);
|
||||
}
|
||||
|
||||
this.el.classList.add('animated');
|
||||
this.element.classList.add('animated');
|
||||
|
||||
this.animationTimer = window.setTimeout(() => {
|
||||
this.animationTimer = undefined;
|
||||
this.el.classList.remove('animated');
|
||||
this.element.classList.remove('animated');
|
||||
}, 200);
|
||||
}
|
||||
|
||||
|
||||
@@ -33,6 +33,8 @@ export interface ISplitViewOptions<TLayoutContext = undefined> {
|
||||
readonly inverseAltBehavior?: boolean;
|
||||
readonly proportionalLayout?: boolean; // default true,
|
||||
readonly descriptor?: ISplitViewDescriptor<TLayoutContext>;
|
||||
readonly scrollbarVisibility?: ScrollbarVisibility;
|
||||
readonly getSashOrthogonalSize?: () => number;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -200,7 +202,7 @@ export namespace Sizing {
|
||||
export function Invisible(cachedVisibleSize: number): InvisibleSizing { return { type: 'invisible', cachedVisibleSize }; }
|
||||
}
|
||||
|
||||
export interface ISplitViewDescriptor<TLayoutContext> {
|
||||
export interface ISplitViewDescriptor<TLayoutContext = undefined> {
|
||||
size: number;
|
||||
views: {
|
||||
visible?: boolean;
|
||||
@@ -227,6 +229,7 @@ export class SplitView<TLayoutContext = undefined> extends Disposable {
|
||||
private state: State = State.Idle;
|
||||
private inverseAltBehavior: boolean;
|
||||
private proportionalLayout: boolean;
|
||||
private readonly getSashOrthogonalSize: { (): number } | undefined;
|
||||
|
||||
private _onDidSashChange = this._register(new Emitter<number>());
|
||||
readonly onDidSashChange = this._onDidSashChange.event;
|
||||
@@ -298,6 +301,7 @@ export class SplitView<TLayoutContext = undefined> extends Disposable {
|
||||
this.orientation = types.isUndefined(options.orientation) ? Orientation.VERTICAL : options.orientation;
|
||||
this.inverseAltBehavior = !!options.inverseAltBehavior;
|
||||
this.proportionalLayout = types.isUndefined(options.proportionalLayout) ? true : !!options.proportionalLayout;
|
||||
this.getSashOrthogonalSize = options.getSashOrthogonalSize;
|
||||
|
||||
this.el = document.createElement('div');
|
||||
this.el.classList.add('monaco-split-view2');
|
||||
@@ -309,8 +313,8 @@ export class SplitView<TLayoutContext = undefined> extends Disposable {
|
||||
|
||||
this.scrollable = new Scrollable(125, scheduleAtNextAnimationFrame);
|
||||
this.scrollableElement = this._register(new SmoothScrollableElement(this.viewContainer, {
|
||||
vertical: this.orientation === Orientation.VERTICAL ? ScrollbarVisibility.Auto : ScrollbarVisibility.Hidden,
|
||||
horizontal: this.orientation === Orientation.HORIZONTAL ? ScrollbarVisibility.Auto : ScrollbarVisibility.Hidden
|
||||
vertical: this.orientation === Orientation.VERTICAL ? (options.scrollbarVisibility ?? ScrollbarVisibility.Auto) : ScrollbarVisibility.Hidden,
|
||||
horizontal: this.orientation === Orientation.HORIZONTAL ? (options.scrollbarVisibility ?? ScrollbarVisibility.Auto) : ScrollbarVisibility.Hidden
|
||||
}, this.scrollable));
|
||||
|
||||
this._register(this.scrollableElement.onScroll(e => {
|
||||
@@ -706,17 +710,11 @@ export class SplitView<TLayoutContext = undefined> extends Disposable {
|
||||
|
||||
// Add sash
|
||||
if (this.viewItems.length > 1) {
|
||||
let opts = { orthogonalStartSash: this.orthogonalStartSash, orthogonalEndSash: this.orthogonalEndSash };
|
||||
|
||||
const sash = this.orientation === Orientation.VERTICAL
|
||||
? new Sash(this.sashContainer, { getHorizontalSashTop: (sash: Sash) => this.getSashPosition(sash) }, {
|
||||
orientation: Orientation.HORIZONTAL,
|
||||
orthogonalStartSash: this.orthogonalStartSash,
|
||||
orthogonalEndSash: this.orthogonalEndSash
|
||||
})
|
||||
: new Sash(this.sashContainer, { getVerticalSashLeft: (sash: Sash) => this.getSashPosition(sash) }, {
|
||||
orientation: Orientation.VERTICAL,
|
||||
orthogonalStartSash: this.orthogonalStartSash,
|
||||
orthogonalEndSash: this.orthogonalEndSash
|
||||
});
|
||||
? new Sash(this.sashContainer, { getHorizontalSashTop: s => this.getSashPosition(s), getHorizontalSashWidth: this.getSashOrthogonalSize }, { ...opts, orientation: Orientation.HORIZONTAL })
|
||||
: new Sash(this.sashContainer, { getVerticalSashLeft: s => this.getSashPosition(s), getVerticalSashHeight: this.getSashOrthogonalSize }, { ...opts, orientation: Orientation.VERTICAL });
|
||||
|
||||
const sashEventMapper = this.orientation === Orientation.VERTICAL
|
||||
? (e: IBaseSashEvent) => ({ sash, start: e.startY, current: e.currentY, alt: e.altKey })
|
||||
|
||||
66
lib/vscode/src/vs/base/browser/ui/table/table.css
Normal file
66
lib/vscode/src/vs/base/browser/ui/table/table.css
Normal file
@@ -0,0 +1,66 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.monaco-table {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.monaco-table > .monaco-split-view2 {
|
||||
border-bottom: 1px solid transparent;
|
||||
}
|
||||
|
||||
.monaco-table > .monaco-list {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.monaco-table-tr {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.monaco-table-th {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
font-weight: bold;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.monaco-table-th,
|
||||
.monaco-table-td {
|
||||
box-sizing: border-box;
|
||||
padding-left: 10px;
|
||||
flex-shrink: 0;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.monaco-table-th[data-col-index="0"],
|
||||
.monaco-table-td[data-col-index="0"] {
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.monaco-table > .monaco-split-view2 .monaco-sash.vertical::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: calc(var(--sash-size) / 2);
|
||||
width: 0;
|
||||
border-left: 1px solid transparent;
|
||||
}
|
||||
|
||||
.monaco-table > .monaco-split-view2,
|
||||
.monaco-table > .monaco-split-view2 .monaco-sash.vertical::before {
|
||||
transition: border-color 0.2s ease-out;
|
||||
}
|
||||
/*
|
||||
.monaco-table:hover > .monaco-split-view2,
|
||||
.monaco-table:hover > .monaco-split-view2 .monaco-sash.vertical::before {
|
||||
border-color: rgba(204, 204, 204, 0.2);
|
||||
} */
|
||||
40
lib/vscode/src/vs/base/browser/ui/table/table.ts
Normal file
40
lib/vscode/src/vs/base/browser/ui/table/table.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IListContextMenuEvent, IListEvent, IListGestureEvent, IListMouseEvent, IListRenderer, IListTouchEvent } from 'vs/base/browser/ui/list/list';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
|
||||
export interface ITableColumn<TRow, TCell> {
|
||||
readonly label: string;
|
||||
readonly tooltip?: string;
|
||||
readonly weight: number;
|
||||
readonly templateId: string;
|
||||
|
||||
readonly minimumWidth?: number;
|
||||
readonly maximumWidth?: number;
|
||||
readonly onDidChangeWidthConstraints?: Event<void>;
|
||||
|
||||
project(row: TRow): TCell;
|
||||
}
|
||||
|
||||
export interface ITableVirtualDelegate<TRow> {
|
||||
readonly headerRowHeight: number;
|
||||
getHeight(row: TRow): number;
|
||||
}
|
||||
|
||||
export interface ITableRenderer<TCell, TTemplateData> extends IListRenderer<TCell, TTemplateData> { }
|
||||
|
||||
export interface ITableEvent<TRow> extends IListEvent<TRow> { }
|
||||
export interface ITableMouseEvent<TRow> extends IListMouseEvent<TRow> { }
|
||||
export interface ITableTouchEvent<TRow> extends IListTouchEvent<TRow> { }
|
||||
export interface ITableGestureEvent<TRow> extends IListGestureEvent<TRow> { }
|
||||
export interface ITableContextMenuEvent<TRow> extends IListContextMenuEvent<TRow> { }
|
||||
|
||||
export class TableError extends Error {
|
||||
|
||||
constructor(user: string, message: string) {
|
||||
super(`TableError [${user}] ${message}`);
|
||||
}
|
||||
}
|
||||
329
lib/vscode/src/vs/base/browser/ui/table/tableWidget.ts
Normal file
329
lib/vscode/src/vs/base/browser/ui/table/tableWidget.ts
Normal file
@@ -0,0 +1,329 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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!./table';
|
||||
import { IListOptions, IListOptionsUpdate, IListStyles, List } from 'vs/base/browser/ui/list/listWidget';
|
||||
import { ITableColumn, ITableContextMenuEvent, ITableEvent, ITableGestureEvent, ITableMouseEvent, ITableRenderer, ITableTouchEvent, ITableVirtualDelegate } from 'vs/base/browser/ui/table/table';
|
||||
import { ISpliceable } from 'vs/base/common/sequence';
|
||||
import { IThemable } from 'vs/base/common/styler';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { $, append, clearNode, createStyleSheet, getContentHeight, getContentWidth } from 'vs/base/browser/dom';
|
||||
import { ISplitViewDescriptor, IView, Orientation, SplitView } from 'vs/base/browser/ui/splitview/splitview';
|
||||
import { IListRenderer, IListVirtualDelegate } from 'vs/base/browser/ui/list/list';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { ScrollbarVisibility, ScrollEvent } from 'vs/base/common/scrollable';
|
||||
|
||||
// TODO@joao
|
||||
type TCell = any;
|
||||
|
||||
interface RowTemplateData {
|
||||
readonly container: HTMLElement;
|
||||
readonly cellContainers: HTMLElement[];
|
||||
readonly cellTemplateData: unknown[];
|
||||
}
|
||||
|
||||
class TableListRenderer<TRow> implements IListRenderer<TRow, RowTemplateData> {
|
||||
|
||||
static TemplateId = 'row';
|
||||
readonly templateId = TableListRenderer.TemplateId;
|
||||
private renderers: ITableRenderer<TCell, unknown>[];
|
||||
private renderedTemplates = new Set<RowTemplateData>();
|
||||
|
||||
constructor(
|
||||
private columns: ITableColumn<TRow, TCell>[],
|
||||
renderers: ITableRenderer<TCell, unknown>[],
|
||||
private getColumnSize: (index: number) => number
|
||||
) {
|
||||
const rendererMap = new Map(renderers.map(r => [r.templateId, r]));
|
||||
this.renderers = [];
|
||||
|
||||
for (const column of columns) {
|
||||
const renderer = rendererMap.get(column.templateId);
|
||||
|
||||
if (!renderer) {
|
||||
throw new Error(`Table cell renderer for template id ${column.templateId} not found.`);
|
||||
}
|
||||
|
||||
this.renderers.push(renderer);
|
||||
}
|
||||
}
|
||||
|
||||
renderTemplate(container: HTMLElement) {
|
||||
const rowContainer = append(container, $('.monaco-table-tr'));
|
||||
const cellContainers: HTMLElement[] = [];
|
||||
const cellTemplateData: unknown[] = [];
|
||||
|
||||
for (let i = 0; i < this.columns.length; i++) {
|
||||
const renderer = this.renderers[i];
|
||||
const cellContainer = append(rowContainer, $('.monaco-table-td', { 'data-col-index': i }));
|
||||
|
||||
cellContainer.style.width = `${this.getColumnSize(i)}px`;
|
||||
cellContainers.push(cellContainer);
|
||||
cellTemplateData.push(renderer.renderTemplate(cellContainer));
|
||||
}
|
||||
|
||||
const result = { container, cellContainers, cellTemplateData };
|
||||
this.renderedTemplates.add(result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
renderElement(element: TRow, index: number, templateData: RowTemplateData, height: number | undefined): void {
|
||||
for (let i = 0; i < this.columns.length; i++) {
|
||||
const column = this.columns[i];
|
||||
const cell = column.project(element);
|
||||
const renderer = this.renderers[i];
|
||||
renderer.renderElement(cell, index, templateData.cellTemplateData[i], height);
|
||||
}
|
||||
}
|
||||
|
||||
disposeElement(element: TRow, index: number, templateData: RowTemplateData, height: number | undefined): void {
|
||||
for (let i = 0; i < this.columns.length; i++) {
|
||||
const renderer = this.renderers[i];
|
||||
|
||||
if (renderer.disposeElement) {
|
||||
const column = this.columns[i];
|
||||
const cell = column.project(element);
|
||||
|
||||
renderer.disposeElement(cell, index, templateData.cellTemplateData[i], height);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
disposeTemplate(templateData: RowTemplateData): void {
|
||||
for (let i = 0; i < this.columns.length; i++) {
|
||||
const renderer = this.renderers[i];
|
||||
renderer.disposeTemplate(templateData.cellTemplateData[i]);
|
||||
}
|
||||
|
||||
clearNode(templateData.container);
|
||||
this.renderedTemplates.delete(templateData);
|
||||
}
|
||||
|
||||
layoutColumn(index: number, size: number): void {
|
||||
for (const { cellContainers } of this.renderedTemplates) {
|
||||
cellContainers[index].style.width = `${size}px`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function asListVirtualDelegate<TRow>(delegate: ITableVirtualDelegate<TRow>): IListVirtualDelegate<TRow> {
|
||||
return {
|
||||
getHeight(row) { return delegate.getHeight(row); },
|
||||
getTemplateId() { return TableListRenderer.TemplateId; },
|
||||
};
|
||||
}
|
||||
|
||||
class ColumnHeader<TRow, TCell> implements IView {
|
||||
|
||||
readonly element: HTMLElement;
|
||||
|
||||
get minimumSize() { return this.column.minimumWidth ?? 120; }
|
||||
get maximumSize() { return this.column.maximumWidth ?? Number.POSITIVE_INFINITY; }
|
||||
get onDidChange() { return this.column.onDidChangeWidthConstraints ?? Event.None; }
|
||||
|
||||
private _onDidLayout = new Emitter<[number, number]>();
|
||||
readonly onDidLayout = this._onDidLayout.event;
|
||||
|
||||
constructor(readonly column: ITableColumn<TRow, TCell>, private index: number) {
|
||||
this.element = $('.monaco-table-th', { 'data-col-index': index, title: column.tooltip }, column.label);
|
||||
}
|
||||
|
||||
layout(size: number): void {
|
||||
this._onDidLayout.fire([this.index, size]);
|
||||
}
|
||||
}
|
||||
|
||||
export interface ITableOptions<TRow> extends IListOptions<TRow> { }
|
||||
export interface ITableOptionsUpdate extends IListOptionsUpdate { }
|
||||
export interface ITableStyles extends IListStyles { }
|
||||
|
||||
export class Table<TRow> implements ISpliceable<TRow>, IThemable, IDisposable {
|
||||
|
||||
private static InstanceCount = 0;
|
||||
readonly domId = `table_id_${++Table.InstanceCount}`;
|
||||
|
||||
readonly domNode: HTMLElement;
|
||||
private splitview: SplitView;
|
||||
private list: List<TRow>;
|
||||
private columnLayoutDisposable: IDisposable;
|
||||
private cachedHeight: number = 0;
|
||||
private styleElement: HTMLStyleElement;
|
||||
|
||||
get onDidChangeFocus(): Event<ITableEvent<TRow>> { return this.list.onDidChangeFocus; }
|
||||
get onDidChangeSelection(): Event<ITableEvent<TRow>> { return this.list.onDidChangeSelection; }
|
||||
|
||||
get onDidScroll(): Event<ScrollEvent> { return this.list.onDidScroll; }
|
||||
get onMouseClick(): Event<ITableMouseEvent<TRow>> { return this.list.onMouseClick; }
|
||||
get onMouseDblClick(): Event<ITableMouseEvent<TRow>> { return this.list.onMouseDblClick; }
|
||||
get onMouseMiddleClick(): Event<ITableMouseEvent<TRow>> { return this.list.onMouseMiddleClick; }
|
||||
get onPointer(): Event<ITableMouseEvent<TRow>> { return this.list.onPointer; }
|
||||
get onMouseUp(): Event<ITableMouseEvent<TRow>> { return this.list.onMouseUp; }
|
||||
get onMouseDown(): Event<ITableMouseEvent<TRow>> { return this.list.onMouseDown; }
|
||||
get onMouseOver(): Event<ITableMouseEvent<TRow>> { return this.list.onMouseOver; }
|
||||
get onMouseMove(): Event<ITableMouseEvent<TRow>> { return this.list.onMouseMove; }
|
||||
get onMouseOut(): Event<ITableMouseEvent<TRow>> { return this.list.onMouseOut; }
|
||||
get onTouchStart(): Event<ITableTouchEvent<TRow>> { return this.list.onTouchStart; }
|
||||
get onTap(): Event<ITableGestureEvent<TRow>> { return this.list.onTap; }
|
||||
get onContextMenu(): Event<ITableContextMenuEvent<TRow>> { return this.list.onContextMenu; }
|
||||
|
||||
get onDidFocus(): Event<void> { return this.list.onDidFocus; }
|
||||
get onDidBlur(): Event<void> { return this.list.onDidBlur; }
|
||||
|
||||
get scrollTop(): number { return this.list.scrollTop; }
|
||||
set scrollTop(scrollTop: number) { this.list.scrollTop = scrollTop; }
|
||||
get scrollLeft(): number { return this.list.scrollLeft; }
|
||||
set scrollLeft(scrollLeft: number) { this.list.scrollLeft = scrollLeft; }
|
||||
get scrollHeight(): number { return this.list.scrollHeight; }
|
||||
get renderHeight(): number { return this.list.renderHeight; }
|
||||
get onDidDispose(): Event<void> { return this.list.onDidDispose; }
|
||||
|
||||
constructor(
|
||||
user: string,
|
||||
container: HTMLElement,
|
||||
private virtualDelegate: ITableVirtualDelegate<TRow>,
|
||||
columns: ITableColumn<TRow, TCell>[],
|
||||
renderers: ITableRenderer<TCell, unknown>[],
|
||||
_options?: ITableOptions<TRow>
|
||||
) {
|
||||
this.domNode = append(container, $(`.monaco-table.${this.domId}`));
|
||||
|
||||
const headers = columns.map((c, i) => new ColumnHeader(c, i));
|
||||
const descriptor: ISplitViewDescriptor = {
|
||||
size: headers.reduce((a, b) => a + b.column.weight, 0),
|
||||
views: headers.map(view => ({ size: view.column.weight, view }))
|
||||
};
|
||||
|
||||
this.splitview = new SplitView(this.domNode, {
|
||||
orientation: Orientation.HORIZONTAL,
|
||||
scrollbarVisibility: ScrollbarVisibility.Hidden,
|
||||
getSashOrthogonalSize: () => this.cachedHeight,
|
||||
descriptor
|
||||
});
|
||||
|
||||
this.splitview.el.style.height = `${virtualDelegate.headerRowHeight}px`;
|
||||
this.splitview.el.style.lineHeight = `${virtualDelegate.headerRowHeight}px`;
|
||||
|
||||
const renderer = new TableListRenderer(columns, renderers, i => this.splitview.getViewSize(i));
|
||||
this.list = new List(user, this.domNode, asListVirtualDelegate(virtualDelegate), [renderer], _options);
|
||||
|
||||
this.columnLayoutDisposable = Event.any(...headers.map(h => h.onDidLayout))
|
||||
(([index, size]) => renderer.layoutColumn(index, size));
|
||||
|
||||
this.styleElement = createStyleSheet(this.domNode);
|
||||
this.style({});
|
||||
}
|
||||
|
||||
updateOptions(options: ITableOptionsUpdate): void {
|
||||
this.list.updateOptions(options);
|
||||
}
|
||||
|
||||
splice(start: number, deleteCount: number, elements: TRow[] = []): void {
|
||||
this.list.splice(start, deleteCount, elements);
|
||||
}
|
||||
|
||||
rerender(): void {
|
||||
this.list.rerender();
|
||||
}
|
||||
|
||||
row(index: number): TRow {
|
||||
return this.list.element(index);
|
||||
}
|
||||
|
||||
indexOf(element: TRow): number {
|
||||
return this.list.indexOf(element);
|
||||
}
|
||||
|
||||
get length(): number {
|
||||
return this.list.length;
|
||||
}
|
||||
|
||||
getHTMLElement(): HTMLElement {
|
||||
return this.domNode;
|
||||
}
|
||||
|
||||
layout(height?: number, width?: number): void {
|
||||
height = height ?? getContentHeight(this.domNode);
|
||||
width = width ?? getContentWidth(this.domNode);
|
||||
|
||||
this.cachedHeight = height;
|
||||
this.splitview.layout(width);
|
||||
this.list.layout(height - this.virtualDelegate.headerRowHeight, width);
|
||||
}
|
||||
|
||||
toggleKeyboardNavigation(): void {
|
||||
this.list.toggleKeyboardNavigation();
|
||||
}
|
||||
|
||||
style(styles: ITableStyles): void {
|
||||
const content: string[] = [];
|
||||
|
||||
content.push(`.monaco-table.${this.domId} > .monaco-split-view2 .monaco-sash.vertical::before {
|
||||
top: ${this.virtualDelegate.headerRowHeight + 1}px;
|
||||
height: calc(100% - ${this.virtualDelegate.headerRowHeight}px);
|
||||
}`);
|
||||
|
||||
this.styleElement.textContent = content.join('\n');
|
||||
this.list.style(styles);
|
||||
}
|
||||
|
||||
domFocus(): void {
|
||||
this.list.domFocus();
|
||||
}
|
||||
|
||||
getSelectedElements(): TRow[] {
|
||||
return this.list.getSelectedElements();
|
||||
}
|
||||
|
||||
setSelection(indexes: number[], browserEvent?: UIEvent): void {
|
||||
this.list.setSelection(indexes, browserEvent);
|
||||
}
|
||||
|
||||
getSelection(): number[] {
|
||||
return this.list.getSelection();
|
||||
}
|
||||
|
||||
setFocus(indexes: number[], browserEvent?: UIEvent): void {
|
||||
this.list.setFocus(indexes, browserEvent);
|
||||
}
|
||||
|
||||
focusNext(n = 1, loop = false, browserEvent?: UIEvent): void {
|
||||
this.list.focusNext(n, loop, browserEvent);
|
||||
}
|
||||
|
||||
focusPrevious(n = 1, loop = false, browserEvent?: UIEvent): void {
|
||||
this.list.focusPrevious(n, loop, browserEvent);
|
||||
}
|
||||
|
||||
focusNextPage(browserEvent?: UIEvent): void {
|
||||
this.list.focusNextPage(browserEvent);
|
||||
}
|
||||
|
||||
focusPreviousPage(browserEvent?: UIEvent): void {
|
||||
this.list.focusPreviousPage(browserEvent);
|
||||
}
|
||||
|
||||
focusFirst(browserEvent?: UIEvent): void {
|
||||
this.list.focusFirst(browserEvent);
|
||||
}
|
||||
|
||||
focusLast(browserEvent?: UIEvent): void {
|
||||
this.list.focusLast(browserEvent);
|
||||
}
|
||||
|
||||
getFocus(): number[] {
|
||||
return this.list.getFocus();
|
||||
}
|
||||
|
||||
reveal(index: number, relativeTop?: number): void {
|
||||
this.list.reveal(index, relativeTop);
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.splitview.dispose();
|
||||
this.list.dispose();
|
||||
this.columnLayoutDisposable.dispose();
|
||||
}
|
||||
}
|
||||
@@ -5,8 +5,8 @@
|
||||
|
||||
import 'vs/css!./toolbar';
|
||||
import * as nls from 'vs/nls';
|
||||
import { Action, IActionRunner, IAction, IActionViewItemProvider, SubmenuAction } from 'vs/base/common/actions';
|
||||
import { ActionBar, ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { Action, IActionRunner, IAction, SubmenuAction } from 'vs/base/common/actions';
|
||||
import { ActionBar, ActionsOrientation, IActionViewItemProvider } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { ResolvedKeybinding } from 'vs/base/common/keyCodes';
|
||||
import { Disposable, DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview';
|
||||
|
||||
@@ -961,7 +961,7 @@ export interface IAbstractTreeOptionsUpdate extends ITreeRendererOptions {
|
||||
readonly filterOnType?: boolean;
|
||||
readonly smoothScrolling?: boolean;
|
||||
readonly horizontalScrolling?: boolean;
|
||||
readonly expandOnlyOnDoubleClick?: boolean;
|
||||
readonly expandOnDoubleClick?: boolean;
|
||||
readonly expandOnlyOnTwistieClick?: boolean | ((e: any) => boolean); // e is T
|
||||
}
|
||||
|
||||
@@ -1121,7 +1121,7 @@ class TreeNodeListMouseController<T, TFilterData, TRef> extends MouseController<
|
||||
return super.onViewPointer(e);
|
||||
}
|
||||
|
||||
if (this.tree.expandOnlyOnDoubleClick && e.browserEvent.detail !== 2 && !onTwistie) {
|
||||
if (!this.tree.expandOnDoubleClick && e.browserEvent.detail === 2) {
|
||||
return super.onViewPointer(e);
|
||||
}
|
||||
|
||||
@@ -1129,6 +1129,7 @@ class TreeNodeListMouseController<T, TFilterData, TRef> extends MouseController<
|
||||
const model = ((this.tree as any).model as ITreeModel<T, TFilterData, TRef>); // internal
|
||||
const location = model.getNodeLocation(node);
|
||||
const recursive = e.browserEvent.altKey;
|
||||
this.tree.setFocus([location]);
|
||||
model.setCollapsed(location, undefined, recursive);
|
||||
|
||||
if (expandOnlyOnTwistieClick && onTwistie) {
|
||||
@@ -1142,7 +1143,7 @@ class TreeNodeListMouseController<T, TFilterData, TRef> extends MouseController<
|
||||
protected onDoubleClick(e: IListMouseEvent<ITreeNode<T, TFilterData>>): void {
|
||||
const onTwistie = (e.browserEvent.target as HTMLElement).classList.contains('monaco-tl-twistie');
|
||||
|
||||
if (onTwistie) {
|
||||
if (onTwistie || !this.tree.expandOnDoubleClick) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1262,8 +1263,8 @@ export abstract class AbstractTree<T, TFilterData, TRef> implements IDisposable
|
||||
get filterOnType(): boolean { return !!this._options.filterOnType; }
|
||||
get onDidChangeTypeFilterPattern(): Event<string> { return this.typeFilterController ? this.typeFilterController.onDidChangePattern : Event.None; }
|
||||
|
||||
get expandOnlyOnDoubleClick(): boolean { return this._options.expandOnlyOnDoubleClick ?? false; }
|
||||
get expandOnlyOnTwistieClick(): boolean | ((e: T) => boolean) { return typeof this._options.expandOnlyOnTwistieClick === 'undefined' ? false : this._options.expandOnlyOnTwistieClick; }
|
||||
get expandOnDoubleClick(): boolean { return typeof this._options.expandOnDoubleClick === 'undefined' ? true : this._options.expandOnDoubleClick; }
|
||||
get expandOnlyOnTwistieClick(): boolean | ((e: T) => boolean) { return typeof this._options.expandOnlyOnTwistieClick === 'undefined' ? true : this._options.expandOnlyOnTwistieClick; }
|
||||
|
||||
private readonly _onDidUpdateOptions = new Emitter<IAbstractTreeOptions<T, TFilterData>>();
|
||||
readonly onDidUpdateOptions: Event<IAbstractTreeOptions<T, TFilterData>> = this._onDidUpdateOptions.event;
|
||||
|
||||
@@ -9,7 +9,7 @@ import { IListVirtualDelegate, IIdentityProvider, IListDragAndDrop, IListDragOve
|
||||
import { ITreeElement, ITreeNode, ITreeRenderer, ITreeEvent, ITreeMouseEvent, ITreeContextMenuEvent, ITreeSorter, ICollapseStateChangeEvent, IAsyncDataSource, ITreeDragAndDrop, TreeError, WeakMapper, ITreeFilter, TreeVisibility, TreeFilterResult } from 'vs/base/browser/ui/tree/tree';
|
||||
import { IDisposable, dispose, DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { timeout, CancelablePromise, createCancelablePromise } from 'vs/base/common/async';
|
||||
import { timeout, CancelablePromise, createCancelablePromise, Promises } from 'vs/base/common/async';
|
||||
import { IListStyles } from 'vs/base/browser/ui/list/listWidget';
|
||||
import { Iterable } from 'vs/base/common/iterator';
|
||||
import { IDragAndDropData } from 'vs/base/browser/dnd';
|
||||
@@ -740,7 +740,7 @@ export class AsyncDataTree<TInput, T, TFilterData = void> implements IDisposable
|
||||
const childrenToRefresh = await this.doRefreshNode(node, recursive, viewStateContext);
|
||||
node.stale = false;
|
||||
|
||||
await Promise.all(childrenToRefresh.map(child => this.doRefreshSubTree(child, recursive, viewStateContext)));
|
||||
await Promises.settled(childrenToRefresh.map(child => this.doRefreshSubTree(child, recursive, viewStateContext)));
|
||||
} finally {
|
||||
done!();
|
||||
}
|
||||
@@ -990,16 +990,16 @@ export class AsyncDataTree<TInput, T, TFilterData = void> implements IDisposable
|
||||
|
||||
const expanded: string[] = [];
|
||||
const root = this.tree.getNode();
|
||||
const queue = [root];
|
||||
const stack = [root];
|
||||
|
||||
while (queue.length > 0) {
|
||||
const node = queue.shift()!;
|
||||
while (stack.length > 0) {
|
||||
const node = stack.pop()!;
|
||||
|
||||
if (node !== root && node.collapsible && !node.collapsed) {
|
||||
expanded.push(getId(node.element!.element as T));
|
||||
}
|
||||
|
||||
queue.push(...node.children);
|
||||
stack.push(...node.children);
|
||||
}
|
||||
|
||||
return { focus, selection, expanded, scrollTop: this.scrollTop };
|
||||
|
||||
@@ -56,6 +56,10 @@
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.monaco-tl-twistie::before {
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
.monaco-tl-twistie.collapsed::before {
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ export class ObjectTree<T extends NonNullable<any>, TFilterData = void> extends
|
||||
this.model.updateElementHeight(element, height);
|
||||
}
|
||||
|
||||
resort(element: T, recursive = true): void {
|
||||
resort(element: T | null, recursive = true): void {
|
||||
this.model.resort(element, recursive);
|
||||
}
|
||||
|
||||
|
||||
@@ -39,19 +39,6 @@ export interface IActionRunner extends IDisposable {
|
||||
readonly onBeforeRun: Event<IRunEvent>;
|
||||
}
|
||||
|
||||
export interface IActionViewItem extends IDisposable {
|
||||
actionRunner: IActionRunner;
|
||||
setActionContext(context: any): void;
|
||||
render(element: any /* HTMLElement */): void;
|
||||
isEnabled(): boolean;
|
||||
focus(fromRight?: boolean): void; // TODO@isidorn what is this?
|
||||
blur(): void;
|
||||
}
|
||||
|
||||
export interface IActionViewItemProvider {
|
||||
(action: IAction): IActionViewItem | undefined;
|
||||
}
|
||||
|
||||
export interface IActionChangeEvent {
|
||||
readonly label?: string;
|
||||
readonly tooltip?: string;
|
||||
@@ -205,25 +192,6 @@ export class ActionRunner extends Disposable implements IActionRunner {
|
||||
}
|
||||
}
|
||||
|
||||
export class RadioGroup extends Disposable {
|
||||
|
||||
constructor(readonly actions: Action[]) {
|
||||
super();
|
||||
|
||||
for (const action of actions) {
|
||||
this._register(action.onDidChange(e => {
|
||||
if (e.checked && action.checked) {
|
||||
for (const candidate of actions) {
|
||||
if (candidate !== action) {
|
||||
candidate.checked = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class Separator extends Action {
|
||||
|
||||
static readonly ID = 'vs.actions.separator';
|
||||
@@ -235,17 +203,6 @@ export class Separator extends Action {
|
||||
}
|
||||
}
|
||||
|
||||
export class ActionWithMenuAction extends Action {
|
||||
|
||||
get actions(): IAction[] {
|
||||
return this._actions;
|
||||
}
|
||||
|
||||
constructor(id: string, private _actions: IAction[], label?: string, cssClass?: string, enabled?: boolean, actionCallback?: (event?: any) => Promise<any>) {
|
||||
super(id, label, cssClass, enabled, actionCallback);
|
||||
}
|
||||
}
|
||||
|
||||
export class SubmenuAction implements IAction {
|
||||
|
||||
readonly id: string;
|
||||
|
||||
@@ -4,13 +4,14 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||
import * as errors from 'vs/base/common/errors';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { canceled, onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { Emitter, Event, Listener } from 'vs/base/common/event';
|
||||
import { IDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { LinkedList } from 'vs/base/common/linkedList';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
|
||||
export function isThenable<T>(obj: any): obj is Promise<T> {
|
||||
return obj && typeof (<Promise<any>>obj).then === 'function';
|
||||
export function isThenable<T>(obj: unknown): obj is Promise<T> {
|
||||
return !!obj && typeof (obj as unknown as Promise<T>).then === 'function';
|
||||
}
|
||||
|
||||
export interface CancelablePromise<T> extends Promise<T> {
|
||||
@@ -23,7 +24,7 @@ export function createCancelablePromise<T>(callback: (token: CancellationToken)
|
||||
const thenable = callback(source.token);
|
||||
const promise = new Promise<T>((resolve, reject) => {
|
||||
source.token.onCancellationRequested(() => {
|
||||
reject(errors.canceled());
|
||||
reject(canceled());
|
||||
});
|
||||
Promise.resolve(thenable).then(value => {
|
||||
source.dispose();
|
||||
@@ -165,10 +166,10 @@ export class Throttler {
|
||||
this.activePromise = promiseFactory();
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
this.activePromise!.then((result: any) => {
|
||||
this.activePromise!.then((result: T) => {
|
||||
this.activePromise = null;
|
||||
resolve(result);
|
||||
}, (err: any) => {
|
||||
}, (err: unknown) => {
|
||||
this.activePromise = null;
|
||||
reject(err);
|
||||
});
|
||||
@@ -178,7 +179,7 @@ export class Throttler {
|
||||
|
||||
export class Sequencer {
|
||||
|
||||
private current: Promise<any> = Promise.resolve(null);
|
||||
private current: Promise<unknown> = Promise.resolve(null);
|
||||
|
||||
queue<T>(promiseTask: ITask<Promise<T>>): Promise<T> {
|
||||
return this.current = this.current.then(() => promiseTask(), () => promiseTask());
|
||||
@@ -187,7 +188,7 @@ export class Sequencer {
|
||||
|
||||
export class SequencerByKey<TKey> {
|
||||
|
||||
private promiseMap = new Map<TKey, Promise<any>>();
|
||||
private promiseMap = new Map<TKey, Promise<unknown>>();
|
||||
|
||||
queue<T>(key: TKey, promiseTask: ITask<Promise<T>>): Promise<T> {
|
||||
const runningPromise = this.promiseMap.get(key) ?? Promise.resolve();
|
||||
@@ -282,7 +283,7 @@ export class Delayer<T> implements IDisposable {
|
||||
|
||||
if (this.completionPromise) {
|
||||
if (this.doReject) {
|
||||
this.doReject(errors.canceled());
|
||||
this.doReject(canceled());
|
||||
}
|
||||
this.completionPromise = null;
|
||||
}
|
||||
@@ -320,7 +321,7 @@ export class ThrottledDelayer<T> {
|
||||
}
|
||||
|
||||
trigger(promiseFactory: ITask<Promise<T>>, delay?: number): Promise<T> {
|
||||
return this.delayer.trigger(() => this.throttler.queue(promiseFactory), delay) as any as Promise<T>;
|
||||
return this.delayer.trigger(() => this.throttler.queue(promiseFactory), delay) as unknown as Promise<T>;
|
||||
}
|
||||
|
||||
isTriggered(): boolean {
|
||||
@@ -366,6 +367,25 @@ export class Barrier {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A barrier that is initially closed and then becomes opened permanently after a certain period of
|
||||
* time or when open is called explicitly
|
||||
*/
|
||||
export class AutoOpenBarrier extends Barrier {
|
||||
|
||||
private readonly _timeout: any;
|
||||
|
||||
constructor(autoOpenTimeMs: number) {
|
||||
super();
|
||||
this._timeout = setTimeout(() => this.open(), autoOpenTimeMs);
|
||||
}
|
||||
|
||||
open(): void {
|
||||
clearTimeout(this._timeout);
|
||||
super.open();
|
||||
}
|
||||
}
|
||||
|
||||
export function timeout(millis: number): CancelablePromise<void>;
|
||||
export function timeout(millis: number, token: CancellationToken): Promise<void>;
|
||||
export function timeout(millis: number, token?: CancellationToken): CancelablePromise<void> | Promise<void> {
|
||||
@@ -377,7 +397,7 @@ export function timeout(millis: number, token?: CancellationToken): CancelablePr
|
||||
const handle = setTimeout(resolve, millis);
|
||||
token.onCancellationRequested(() => {
|
||||
clearTimeout(handle);
|
||||
reject(errors.canceled());
|
||||
reject(canceled());
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -487,7 +507,7 @@ export function firstParallel<T>(promiseList: Promise<T>[], shouldStop: (t: T) =
|
||||
interface ILimitedTaskFactory<T> {
|
||||
factory: ITask<Promise<T>>;
|
||||
c: (value: T | Promise<T>) => void;
|
||||
e: (error?: any) => void;
|
||||
e: (error?: unknown) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -666,7 +686,7 @@ export class IntervalTimer implements IDisposable {
|
||||
|
||||
export class RunOnceScheduler {
|
||||
|
||||
protected runner: ((...args: any[]) => void) | null;
|
||||
protected runner: ((...args: unknown[]) => void) | null;
|
||||
|
||||
private timeoutToken: any;
|
||||
private timeout: number;
|
||||
@@ -826,7 +846,7 @@ export class IdleValue<T> {
|
||||
|
||||
private _didRun: boolean = false;
|
||||
private _value?: T;
|
||||
private _error: any;
|
||||
private _error: unknown;
|
||||
|
||||
constructor(executor: () => T) {
|
||||
this._executor = () => {
|
||||
@@ -1014,7 +1034,9 @@ export class IntervalCounter {
|
||||
|
||||
//#endregion
|
||||
|
||||
export type ValueCallback<T = any> = (value: T | Promise<T>) => void;
|
||||
//#region
|
||||
|
||||
export type ValueCallback<T = unknown> = (value: T | Promise<T>) => void;
|
||||
|
||||
/**
|
||||
* Creates a promise whose resolution or rejection can be controlled imperatively.
|
||||
@@ -1022,7 +1044,7 @@ export type ValueCallback<T = any> = (value: T | Promise<T>) => void;
|
||||
export class DeferredPromise<T> {
|
||||
|
||||
private completeCallback!: ValueCallback<T>;
|
||||
private errorCallback!: (err: any) => void;
|
||||
private errorCallback!: (err: unknown) => void;
|
||||
private rejected = false;
|
||||
private resolved = false;
|
||||
|
||||
@@ -1055,7 +1077,7 @@ export class DeferredPromise<T> {
|
||||
});
|
||||
}
|
||||
|
||||
public error(err: any) {
|
||||
public error(err: unknown) {
|
||||
return new Promise<void>(resolve => {
|
||||
this.errorCallback(err);
|
||||
this.rejected = true;
|
||||
@@ -1065,9 +1087,152 @@ export class DeferredPromise<T> {
|
||||
|
||||
public cancel() {
|
||||
new Promise<void>(resolve => {
|
||||
this.errorCallback(errors.canceled());
|
||||
this.errorCallback(canceled());
|
||||
this.rejected = true;
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region
|
||||
|
||||
export interface IWaitUntil {
|
||||
waitUntil(thenable: Promise<unknown>): void;
|
||||
}
|
||||
|
||||
export class AsyncEmitter<T extends IWaitUntil> extends Emitter<T> {
|
||||
|
||||
private _asyncDeliveryQueue?: LinkedList<[Listener<T>, Omit<T, 'waitUntil'>]>;
|
||||
|
||||
async fireAsync(data: Omit<T, 'waitUntil'>, token: CancellationToken, promiseJoin?: (p: Promise<unknown>, listener: Function) => Promise<unknown>): Promise<void> {
|
||||
if (!this._listeners) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this._asyncDeliveryQueue) {
|
||||
this._asyncDeliveryQueue = new LinkedList();
|
||||
}
|
||||
|
||||
for (const listener of this._listeners) {
|
||||
this._asyncDeliveryQueue.push([listener, data]);
|
||||
}
|
||||
|
||||
while (this._asyncDeliveryQueue.size > 0 && !token.isCancellationRequested) {
|
||||
|
||||
const [listener, data] = this._asyncDeliveryQueue.shift()!;
|
||||
const thenables: Promise<unknown>[] = [];
|
||||
|
||||
const event = <T>{
|
||||
...data,
|
||||
waitUntil: (p: Promise<unknown>): void => {
|
||||
if (Object.isFrozen(thenables)) {
|
||||
throw new Error('waitUntil can NOT be called asynchronous');
|
||||
}
|
||||
if (promiseJoin) {
|
||||
p = promiseJoin(p, typeof listener === 'function' ? listener : listener[0]);
|
||||
}
|
||||
thenables.push(p);
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
if (typeof listener === 'function') {
|
||||
listener.call(undefined, event);
|
||||
} else {
|
||||
listener[0].call(listener[1], event);
|
||||
}
|
||||
} catch (e) {
|
||||
onUnexpectedError(e);
|
||||
continue;
|
||||
}
|
||||
|
||||
// freeze thenables-collection to enforce sync-calls to
|
||||
// wait until and then wait for all thenables to resolve
|
||||
Object.freeze(thenables);
|
||||
await Promises.settled(thenables).catch(e => onUnexpectedError(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region Promises
|
||||
|
||||
export namespace Promises {
|
||||
|
||||
export interface IResolvedPromise<T> {
|
||||
status: 'fulfilled';
|
||||
value: T;
|
||||
}
|
||||
|
||||
export interface IRejectedPromise {
|
||||
status: 'rejected';
|
||||
reason: Error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface of https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/allSettled
|
||||
*/
|
||||
interface PromiseWithAllSettled<T> {
|
||||
allSettled<T>(promises: Promise<T>[]): Promise<ReadonlyArray<IResolvedPromise<T> | IRejectedPromise>>;
|
||||
}
|
||||
|
||||
/**
|
||||
* A polyfill of `Promise.allSettled`: returns after all promises have
|
||||
* resolved or rejected and provides access to each result or error
|
||||
* in the order of the original passed in promises array.
|
||||
* See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/allSettled
|
||||
*/
|
||||
export async function allSettled<T>(promises: Promise<T>[]): Promise<ReadonlyArray<IResolvedPromise<T> | IRejectedPromise>> {
|
||||
if (typeof (Promise as unknown as PromiseWithAllSettled<T>).allSettled === 'function') {
|
||||
return allSettledNative(promises); // in some environments we can benefit from native implementation
|
||||
}
|
||||
|
||||
return allSettledShim(promises);
|
||||
}
|
||||
|
||||
async function allSettledNative<T>(promises: Promise<T>[]): Promise<ReadonlyArray<IResolvedPromise<T> | IRejectedPromise>> {
|
||||
return (Promise as unknown as PromiseWithAllSettled<T>).allSettled(promises);
|
||||
}
|
||||
|
||||
async function allSettledShim<T>(promises: Promise<T>[]): Promise<ReadonlyArray<IResolvedPromise<T> | IRejectedPromise>> {
|
||||
return Promise.all(promises.map(promise => (promise.then(value => {
|
||||
const fulfilled: IResolvedPromise<T> = { status: 'fulfilled', value };
|
||||
|
||||
return fulfilled;
|
||||
}, error => {
|
||||
const rejected: IRejectedPromise = { status: 'rejected', reason: error };
|
||||
|
||||
return rejected;
|
||||
}))));
|
||||
}
|
||||
|
||||
/**
|
||||
* A drop-in replacement for `Promise.all` with the only difference
|
||||
* that the method awaits every promise to either fulfill or reject.
|
||||
*
|
||||
* Similar to `Promise.all`, only the first error will be returned
|
||||
* if any.
|
||||
*/
|
||||
export async function settled<T>(promises: Promise<T>[]): Promise<T[]> {
|
||||
let firstError: Error | undefined = undefined;
|
||||
|
||||
const result = await Promise.all(promises.map(promise => promise.then(value => value, error => {
|
||||
if (!firstError) {
|
||||
firstError = error;
|
||||
}
|
||||
|
||||
return undefined; // do not rethrow so that other promises can settle
|
||||
})));
|
||||
|
||||
if (typeof firstError !== 'undefined') {
|
||||
throw firstError;
|
||||
}
|
||||
|
||||
return result as unknown as T[]; // cast is needed and protected by the `throw` above
|
||||
}
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
{
|
||||
"name": "vs/base",
|
||||
"dependencies": [
|
||||
{
|
||||
"name": "vs",
|
||||
"internal": false
|
||||
}
|
||||
],
|
||||
"libs": [
|
||||
"lib.core.d.ts"
|
||||
],
|
||||
"sources": [
|
||||
"**/*.ts"
|
||||
],
|
||||
"declares": []
|
||||
}
|
||||
@@ -71,21 +71,26 @@ export interface CSSIcon {
|
||||
readonly id: string;
|
||||
}
|
||||
|
||||
|
||||
export namespace CSSIcon {
|
||||
export const iconIdRegex = /^(codicon\/)?([a-z\-]+)(?:~([a-z\-]+))?$/i;
|
||||
export const iconNameSegment = '[A-Za-z0-9]+';
|
||||
export const iconNameExpression = '[A-Za-z0-9\\-]+';
|
||||
export const iconModifierExpression = '~[A-Za-z]+';
|
||||
|
||||
const cssIconIdRegex = new RegExp(`^(${iconNameExpression})(${iconModifierExpression})?$`);
|
||||
|
||||
export function asClassNameArray(icon: CSSIcon): string[] {
|
||||
if (icon instanceof Codicon) {
|
||||
return ['codicon', 'codicon-' + icon.id];
|
||||
}
|
||||
const match = iconIdRegex.exec(icon.id);
|
||||
const match = cssIconIdRegex.exec(icon.id);
|
||||
if (!match) {
|
||||
return asClassNameArray(Codicon.error);
|
||||
}
|
||||
let [, , id, modifier] = match;
|
||||
let [, id, modifier] = match;
|
||||
const classNames = ['codicon', 'codicon-' + id];
|
||||
if (modifier) {
|
||||
classNames.push('codicon-modifier-' + modifier);
|
||||
classNames.push('codicon-modifier-' + modifier.substr(1));
|
||||
}
|
||||
return classNames;
|
||||
}
|
||||
@@ -102,443 +107,447 @@ export namespace CSSIcon {
|
||||
|
||||
|
||||
interface IconDefinition {
|
||||
character: string;
|
||||
fontCharacter: string;
|
||||
}
|
||||
|
||||
export namespace Codicon {
|
||||
|
||||
// built-in icons, with image name
|
||||
export const add = new Codicon('add', { character: '\\ea60' });
|
||||
export const plus = new Codicon('plus', { character: '\\ea60' });
|
||||
export const gistNew = new Codicon('gist-new', { character: '\\ea60' });
|
||||
export const repoCreate = new Codicon('repo-create', { character: '\\ea60' });
|
||||
export const lightbulb = new Codicon('lightbulb', { character: '\\ea61' });
|
||||
export const lightBulb = new Codicon('light-bulb', { character: '\\ea61' });
|
||||
export const repo = new Codicon('repo', { character: '\\ea62' });
|
||||
export const repoDelete = new Codicon('repo-delete', { character: '\\ea62' });
|
||||
export const gistFork = new Codicon('gist-fork', { character: '\\ea63' });
|
||||
export const repoForked = new Codicon('repo-forked', { character: '\\ea63' });
|
||||
export const gitPullRequest = new Codicon('git-pull-request', { character: '\\ea64' });
|
||||
export const gitPullRequestAbandoned = new Codicon('git-pull-request-abandoned', { character: '\\ea64' });
|
||||
export const recordKeys = new Codicon('record-keys', { character: '\\ea65' });
|
||||
export const keyboard = new Codicon('keyboard', { character: '\\ea65' });
|
||||
export const tag = new Codicon('tag', { character: '\\ea66' });
|
||||
export const tagAdd = new Codicon('tag-add', { character: '\\ea66' });
|
||||
export const tagRemove = new Codicon('tag-remove', { character: '\\ea66' });
|
||||
export const person = new Codicon('person', { character: '\\ea67' });
|
||||
export const personAdd = new Codicon('person-add', { character: '\\ea67' });
|
||||
export const personFollow = new Codicon('person-follow', { character: '\\ea67' });
|
||||
export const personOutline = new Codicon('person-outline', { character: '\\ea67' });
|
||||
export const personFilled = new Codicon('person-filled', { character: '\\ea67' });
|
||||
export const gitBranch = new Codicon('git-branch', { character: '\\ea68' });
|
||||
export const gitBranchCreate = new Codicon('git-branch-create', { character: '\\ea68' });
|
||||
export const gitBranchDelete = new Codicon('git-branch-delete', { character: '\\ea68' });
|
||||
export const sourceControl = new Codicon('source-control', { character: '\\ea68' });
|
||||
export const mirror = new Codicon('mirror', { character: '\\ea69' });
|
||||
export const mirrorPublic = new Codicon('mirror-public', { character: '\\ea69' });
|
||||
export const star = new Codicon('star', { character: '\\ea6a' });
|
||||
export const starAdd = new Codicon('star-add', { character: '\\ea6a' });
|
||||
export const starDelete = new Codicon('star-delete', { character: '\\ea6a' });
|
||||
export const starEmpty = new Codicon('star-empty', { character: '\\ea6a' });
|
||||
export const comment = new Codicon('comment', { character: '\\ea6b' });
|
||||
export const commentAdd = new Codicon('comment-add', { character: '\\ea6b' });
|
||||
export const alert = new Codicon('alert', { character: '\\ea6c' });
|
||||
export const warning = new Codicon('warning', { character: '\\ea6c' });
|
||||
export const search = new Codicon('search', { character: '\\ea6d' });
|
||||
export const searchSave = new Codicon('search-save', { character: '\\ea6d' });
|
||||
export const logOut = new Codicon('log-out', { character: '\\ea6e' });
|
||||
export const signOut = new Codicon('sign-out', { character: '\\ea6e' });
|
||||
export const logIn = new Codicon('log-in', { character: '\\ea6f' });
|
||||
export const signIn = new Codicon('sign-in', { character: '\\ea6f' });
|
||||
export const eye = new Codicon('eye', { character: '\\ea70' });
|
||||
export const eyeUnwatch = new Codicon('eye-unwatch', { character: '\\ea70' });
|
||||
export const eyeWatch = new Codicon('eye-watch', { character: '\\ea70' });
|
||||
export const circleFilled = new Codicon('circle-filled', { character: '\\ea71' });
|
||||
export const primitiveDot = new Codicon('primitive-dot', { character: '\\ea71' });
|
||||
export const closeDirty = new Codicon('close-dirty', { character: '\\ea71' });
|
||||
export const debugBreakpoint = new Codicon('debug-breakpoint', { character: '\\ea71' });
|
||||
export const debugBreakpointDisabled = new Codicon('debug-breakpoint-disabled', { character: '\\ea71' });
|
||||
export const debugHint = new Codicon('debug-hint', { character: '\\ea71' });
|
||||
export const primitiveSquare = new Codicon('primitive-square', { character: '\\ea72' });
|
||||
export const edit = new Codicon('edit', { character: '\\ea73' });
|
||||
export const pencil = new Codicon('pencil', { character: '\\ea73' });
|
||||
export const info = new Codicon('info', { character: '\\ea74' });
|
||||
export const issueOpened = new Codicon('issue-opened', { character: '\\ea74' });
|
||||
export const gistPrivate = new Codicon('gist-private', { character: '\\ea75' });
|
||||
export const gitForkPrivate = new Codicon('git-fork-private', { character: '\\ea75' });
|
||||
export const lock = new Codicon('lock', { character: '\\ea75' });
|
||||
export const mirrorPrivate = new Codicon('mirror-private', { character: '\\ea75' });
|
||||
export const close = new Codicon('close', { character: '\\ea76' });
|
||||
export const removeClose = new Codicon('remove-close', { character: '\\ea76' });
|
||||
export const x = new Codicon('x', { character: '\\ea76' });
|
||||
export const repoSync = new Codicon('repo-sync', { character: '\\ea77' });
|
||||
export const sync = new Codicon('sync', { character: '\\ea77' });
|
||||
export const clone = new Codicon('clone', { character: '\\ea78' });
|
||||
export const desktopDownload = new Codicon('desktop-download', { character: '\\ea78' });
|
||||
export const beaker = new Codicon('beaker', { character: '\\ea79' });
|
||||
export const microscope = new Codicon('microscope', { character: '\\ea79' });
|
||||
export const vm = new Codicon('vm', { character: '\\ea7a' });
|
||||
export const deviceDesktop = new Codicon('device-desktop', { character: '\\ea7a' });
|
||||
export const file = new Codicon('file', { character: '\\ea7b' });
|
||||
export const fileText = new Codicon('file-text', { character: '\\ea7b' });
|
||||
export const more = new Codicon('more', { character: '\\ea7c' });
|
||||
export const ellipsis = new Codicon('ellipsis', { character: '\\ea7c' });
|
||||
export const kebabHorizontal = new Codicon('kebab-horizontal', { character: '\\ea7c' });
|
||||
export const mailReply = new Codicon('mail-reply', { character: '\\ea7d' });
|
||||
export const reply = new Codicon('reply', { character: '\\ea7d' });
|
||||
export const organization = new Codicon('organization', { character: '\\ea7e' });
|
||||
export const organizationFilled = new Codicon('organization-filled', { character: '\\ea7e' });
|
||||
export const organizationOutline = new Codicon('organization-outline', { character: '\\ea7e' });
|
||||
export const newFile = new Codicon('new-file', { character: '\\ea7f' });
|
||||
export const fileAdd = new Codicon('file-add', { character: '\\ea7f' });
|
||||
export const newFolder = new Codicon('new-folder', { character: '\\ea80' });
|
||||
export const fileDirectoryCreate = new Codicon('file-directory-create', { character: '\\ea80' });
|
||||
export const trash = new Codicon('trash', { character: '\\ea81' });
|
||||
export const trashcan = new Codicon('trashcan', { character: '\\ea81' });
|
||||
export const history = new Codicon('history', { character: '\\ea82' });
|
||||
export const clock = new Codicon('clock', { character: '\\ea82' });
|
||||
export const folder = new Codicon('folder', { character: '\\ea83' });
|
||||
export const fileDirectory = new Codicon('file-directory', { character: '\\ea83' });
|
||||
export const symbolFolder = new Codicon('symbol-folder', { character: '\\ea83' });
|
||||
export const logoGithub = new Codicon('logo-github', { character: '\\ea84' });
|
||||
export const markGithub = new Codicon('mark-github', { character: '\\ea84' });
|
||||
export const github = new Codicon('github', { character: '\\ea84' });
|
||||
export const terminal = new Codicon('terminal', { character: '\\ea85' });
|
||||
export const console = new Codicon('console', { character: '\\ea85' });
|
||||
export const repl = new Codicon('repl', { character: '\\ea85' });
|
||||
export const zap = new Codicon('zap', { character: '\\ea86' });
|
||||
export const symbolEvent = new Codicon('symbol-event', { character: '\\ea86' });
|
||||
export const error = new Codicon('error', { character: '\\ea87' });
|
||||
export const stop = new Codicon('stop', { character: '\\ea87' });
|
||||
export const variable = new Codicon('variable', { character: '\\ea88' });
|
||||
export const symbolVariable = new Codicon('symbol-variable', { character: '\\ea88' });
|
||||
export const array = new Codicon('array', { character: '\\ea8a' });
|
||||
export const symbolArray = new Codicon('symbol-array', { character: '\\ea8a' });
|
||||
export const symbolModule = new Codicon('symbol-module', { character: '\\ea8b' });
|
||||
export const symbolPackage = new Codicon('symbol-package', { character: '\\ea8b' });
|
||||
export const symbolNamespace = new Codicon('symbol-namespace', { character: '\\ea8b' });
|
||||
export const symbolObject = new Codicon('symbol-object', { character: '\\ea8b' });
|
||||
export const symbolMethod = new Codicon('symbol-method', { character: '\\ea8c' });
|
||||
export const symbolFunction = new Codicon('symbol-function', { character: '\\ea8c' });
|
||||
export const symbolConstructor = new Codicon('symbol-constructor', { character: '\\ea8c' });
|
||||
export const symbolBoolean = new Codicon('symbol-boolean', { character: '\\ea8f' });
|
||||
export const symbolNull = new Codicon('symbol-null', { character: '\\ea8f' });
|
||||
export const symbolNumeric = new Codicon('symbol-numeric', { character: '\\ea90' });
|
||||
export const symbolNumber = new Codicon('symbol-number', { character: '\\ea90' });
|
||||
export const symbolStructure = new Codicon('symbol-structure', { character: '\\ea91' });
|
||||
export const symbolStruct = new Codicon('symbol-struct', { character: '\\ea91' });
|
||||
export const symbolParameter = new Codicon('symbol-parameter', { character: '\\ea92' });
|
||||
export const symbolTypeParameter = new Codicon('symbol-type-parameter', { character: '\\ea92' });
|
||||
export const symbolKey = new Codicon('symbol-key', { character: '\\ea93' });
|
||||
export const symbolText = new Codicon('symbol-text', { character: '\\ea93' });
|
||||
export const symbolReference = new Codicon('symbol-reference', { character: '\\ea94' });
|
||||
export const goToFile = new Codicon('go-to-file', { character: '\\ea94' });
|
||||
export const symbolEnum = new Codicon('symbol-enum', { character: '\\ea95' });
|
||||
export const symbolValue = new Codicon('symbol-value', { character: '\\ea95' });
|
||||
export const symbolRuler = new Codicon('symbol-ruler', { character: '\\ea96' });
|
||||
export const symbolUnit = new Codicon('symbol-unit', { character: '\\ea96' });
|
||||
export const activateBreakpoints = new Codicon('activate-breakpoints', { character: '\\ea97' });
|
||||
export const archive = new Codicon('archive', { character: '\\ea98' });
|
||||
export const arrowBoth = new Codicon('arrow-both', { character: '\\ea99' });
|
||||
export const arrowDown = new Codicon('arrow-down', { character: '\\ea9a' });
|
||||
export const arrowLeft = new Codicon('arrow-left', { character: '\\ea9b' });
|
||||
export const arrowRight = new Codicon('arrow-right', { character: '\\ea9c' });
|
||||
export const arrowSmallDown = new Codicon('arrow-small-down', { character: '\\ea9d' });
|
||||
export const arrowSmallLeft = new Codicon('arrow-small-left', { character: '\\ea9e' });
|
||||
export const arrowSmallRight = new Codicon('arrow-small-right', { character: '\\ea9f' });
|
||||
export const arrowSmallUp = new Codicon('arrow-small-up', { character: '\\eaa0' });
|
||||
export const arrowUp = new Codicon('arrow-up', { character: '\\eaa1' });
|
||||
export const bell = new Codicon('bell', { character: '\\eaa2' });
|
||||
export const bold = new Codicon('bold', { character: '\\eaa3' });
|
||||
export const book = new Codicon('book', { character: '\\eaa4' });
|
||||
export const bookmark = new Codicon('bookmark', { character: '\\eaa5' });
|
||||
export const debugBreakpointConditionalUnverified = new Codicon('debug-breakpoint-conditional-unverified', { character: '\\eaa6' });
|
||||
export const debugBreakpointConditional = new Codicon('debug-breakpoint-conditional', { character: '\\eaa7' });
|
||||
export const debugBreakpointConditionalDisabled = new Codicon('debug-breakpoint-conditional-disabled', { character: '\\eaa7' });
|
||||
export const debugBreakpointDataUnverified = new Codicon('debug-breakpoint-data-unverified', { character: '\\eaa8' });
|
||||
export const debugBreakpointData = new Codicon('debug-breakpoint-data', { character: '\\eaa9' });
|
||||
export const debugBreakpointDataDisabled = new Codicon('debug-breakpoint-data-disabled', { character: '\\eaa9' });
|
||||
export const debugBreakpointLogUnverified = new Codicon('debug-breakpoint-log-unverified', { character: '\\eaaa' });
|
||||
export const debugBreakpointLog = new Codicon('debug-breakpoint-log', { character: '\\eaab' });
|
||||
export const debugBreakpointLogDisabled = new Codicon('debug-breakpoint-log-disabled', { character: '\\eaab' });
|
||||
export const briefcase = new Codicon('briefcase', { character: '\\eaac' });
|
||||
export const broadcast = new Codicon('broadcast', { character: '\\eaad' });
|
||||
export const browser = new Codicon('browser', { character: '\\eaae' });
|
||||
export const bug = new Codicon('bug', { character: '\\eaaf' });
|
||||
export const calendar = new Codicon('calendar', { character: '\\eab0' });
|
||||
export const caseSensitive = new Codicon('case-sensitive', { character: '\\eab1' });
|
||||
export const check = new Codicon('check', { character: '\\eab2' });
|
||||
export const checklist = new Codicon('checklist', { character: '\\eab3' });
|
||||
export const chevronDown = new Codicon('chevron-down', { character: '\\eab4' });
|
||||
export const chevronLeft = new Codicon('chevron-left', { character: '\\eab5' });
|
||||
export const chevronRight = new Codicon('chevron-right', { character: '\\eab6' });
|
||||
export const chevronUp = new Codicon('chevron-up', { character: '\\eab7' });
|
||||
export const chromeClose = new Codicon('chrome-close', { character: '\\eab8' });
|
||||
export const chromeMaximize = new Codicon('chrome-maximize', { character: '\\eab9' });
|
||||
export const chromeMinimize = new Codicon('chrome-minimize', { character: '\\eaba' });
|
||||
export const chromeRestore = new Codicon('chrome-restore', { character: '\\eabb' });
|
||||
export const circleOutline = new Codicon('circle-outline', { character: '\\eabc' });
|
||||
export const debugBreakpointUnverified = new Codicon('debug-breakpoint-unverified', { character: '\\eabc' });
|
||||
export const circleSlash = new Codicon('circle-slash', { character: '\\eabd' });
|
||||
export const circuitBoard = new Codicon('circuit-board', { character: '\\eabe' });
|
||||
export const clearAll = new Codicon('clear-all', { character: '\\eabf' });
|
||||
export const clippy = new Codicon('clippy', { character: '\\eac0' });
|
||||
export const closeAll = new Codicon('close-all', { character: '\\eac1' });
|
||||
export const cloudDownload = new Codicon('cloud-download', { character: '\\eac2' });
|
||||
export const cloudUpload = new Codicon('cloud-upload', { character: '\\eac3' });
|
||||
export const code = new Codicon('code', { character: '\\eac4' });
|
||||
export const collapseAll = new Codicon('collapse-all', { character: '\\eac5' });
|
||||
export const colorMode = new Codicon('color-mode', { character: '\\eac6' });
|
||||
export const commentDiscussion = new Codicon('comment-discussion', { character: '\\eac7' });
|
||||
export const compareChanges = new Codicon('compare-changes', { character: '\\eafd' });
|
||||
export const creditCard = new Codicon('credit-card', { character: '\\eac9' });
|
||||
export const dash = new Codicon('dash', { character: '\\eacc' });
|
||||
export const dashboard = new Codicon('dashboard', { character: '\\eacd' });
|
||||
export const database = new Codicon('database', { character: '\\eace' });
|
||||
export const debugContinue = new Codicon('debug-continue', { character: '\\eacf' });
|
||||
export const debugDisconnect = new Codicon('debug-disconnect', { character: '\\ead0' });
|
||||
export const debugPause = new Codicon('debug-pause', { character: '\\ead1' });
|
||||
export const debugRestart = new Codicon('debug-restart', { character: '\\ead2' });
|
||||
export const debugStart = new Codicon('debug-start', { character: '\\ead3' });
|
||||
export const debugStepInto = new Codicon('debug-step-into', { character: '\\ead4' });
|
||||
export const debugStepOut = new Codicon('debug-step-out', { character: '\\ead5' });
|
||||
export const debugStepOver = new Codicon('debug-step-over', { character: '\\ead6' });
|
||||
export const debugStop = new Codicon('debug-stop', { character: '\\ead7' });
|
||||
export const debug = new Codicon('debug', { character: '\\ead8' });
|
||||
export const deviceCameraVideo = new Codicon('device-camera-video', { character: '\\ead9' });
|
||||
export const deviceCamera = new Codicon('device-camera', { character: '\\eada' });
|
||||
export const deviceMobile = new Codicon('device-mobile', { character: '\\eadb' });
|
||||
export const diffAdded = new Codicon('diff-added', { character: '\\eadc' });
|
||||
export const diffIgnored = new Codicon('diff-ignored', { character: '\\eadd' });
|
||||
export const diffModified = new Codicon('diff-modified', { character: '\\eade' });
|
||||
export const diffRemoved = new Codicon('diff-removed', { character: '\\eadf' });
|
||||
export const diffRenamed = new Codicon('diff-renamed', { character: '\\eae0' });
|
||||
export const diff = new Codicon('diff', { character: '\\eae1' });
|
||||
export const discard = new Codicon('discard', { character: '\\eae2' });
|
||||
export const editorLayout = new Codicon('editor-layout', { character: '\\eae3' });
|
||||
export const emptyWindow = new Codicon('empty-window', { character: '\\eae4' });
|
||||
export const exclude = new Codicon('exclude', { character: '\\eae5' });
|
||||
export const extensions = new Codicon('extensions', { character: '\\eae6' });
|
||||
export const eyeClosed = new Codicon('eye-closed', { character: '\\eae7' });
|
||||
export const fileBinary = new Codicon('file-binary', { character: '\\eae8' });
|
||||
export const fileCode = new Codicon('file-code', { character: '\\eae9' });
|
||||
export const fileMedia = new Codicon('file-media', { character: '\\eaea' });
|
||||
export const filePdf = new Codicon('file-pdf', { character: '\\eaeb' });
|
||||
export const fileSubmodule = new Codicon('file-submodule', { character: '\\eaec' });
|
||||
export const fileSymlinkDirectory = new Codicon('file-symlink-directory', { character: '\\eaed' });
|
||||
export const fileSymlinkFile = new Codicon('file-symlink-file', { character: '\\eaee' });
|
||||
export const fileZip = new Codicon('file-zip', { character: '\\eaef' });
|
||||
export const files = new Codicon('files', { character: '\\eaf0' });
|
||||
export const filter = new Codicon('filter', { character: '\\eaf1' });
|
||||
export const flame = new Codicon('flame', { character: '\\eaf2' });
|
||||
export const foldDown = new Codicon('fold-down', { character: '\\eaf3' });
|
||||
export const foldUp = new Codicon('fold-up', { character: '\\eaf4' });
|
||||
export const fold = new Codicon('fold', { character: '\\eaf5' });
|
||||
export const folderActive = new Codicon('folder-active', { character: '\\eaf6' });
|
||||
export const folderOpened = new Codicon('folder-opened', { character: '\\eaf7' });
|
||||
export const gear = new Codicon('gear', { character: '\\eaf8' });
|
||||
export const gift = new Codicon('gift', { character: '\\eaf9' });
|
||||
export const gistSecret = new Codicon('gist-secret', { character: '\\eafa' });
|
||||
export const gist = new Codicon('gist', { character: '\\eafb' });
|
||||
export const gitCommit = new Codicon('git-commit', { character: '\\eafc' });
|
||||
export const gitCompare = new Codicon('git-compare', { character: '\\eafd' });
|
||||
export const gitMerge = new Codicon('git-merge', { character: '\\eafe' });
|
||||
export const githubAction = new Codicon('github-action', { character: '\\eaff' });
|
||||
export const githubAlt = new Codicon('github-alt', { character: '\\eb00' });
|
||||
export const globe = new Codicon('globe', { character: '\\eb01' });
|
||||
export const grabber = new Codicon('grabber', { character: '\\eb02' });
|
||||
export const graph = new Codicon('graph', { character: '\\eb03' });
|
||||
export const gripper = new Codicon('gripper', { character: '\\eb04' });
|
||||
export const heart = new Codicon('heart', { character: '\\eb05' });
|
||||
export const home = new Codicon('home', { character: '\\eb06' });
|
||||
export const horizontalRule = new Codicon('horizontal-rule', { character: '\\eb07' });
|
||||
export const hubot = new Codicon('hubot', { character: '\\eb08' });
|
||||
export const inbox = new Codicon('inbox', { character: '\\eb09' });
|
||||
export const issueClosed = new Codicon('issue-closed', { character: '\\eb0a' });
|
||||
export const issueReopened = new Codicon('issue-reopened', { character: '\\eb0b' });
|
||||
export const issues = new Codicon('issues', { character: '\\eb0c' });
|
||||
export const italic = new Codicon('italic', { character: '\\eb0d' });
|
||||
export const jersey = new Codicon('jersey', { character: '\\eb0e' });
|
||||
export const json = new Codicon('json', { character: '\\eb0f' });
|
||||
export const kebabVertical = new Codicon('kebab-vertical', { character: '\\eb10' });
|
||||
export const key = new Codicon('key', { character: '\\eb11' });
|
||||
export const law = new Codicon('law', { character: '\\eb12' });
|
||||
export const lightbulbAutofix = new Codicon('lightbulb-autofix', { character: '\\eb13' });
|
||||
export const linkExternal = new Codicon('link-external', { character: '\\eb14' });
|
||||
export const link = new Codicon('link', { character: '\\eb15' });
|
||||
export const listOrdered = new Codicon('list-ordered', { character: '\\eb16' });
|
||||
export const listUnordered = new Codicon('list-unordered', { character: '\\eb17' });
|
||||
export const liveShare = new Codicon('live-share', { character: '\\eb18' });
|
||||
export const loading = new Codicon('loading', { character: '\\eb19' });
|
||||
export const location = new Codicon('location', { character: '\\eb1a' });
|
||||
export const mailRead = new Codicon('mail-read', { character: '\\eb1b' });
|
||||
export const mail = new Codicon('mail', { character: '\\eb1c' });
|
||||
export const markdown = new Codicon('markdown', { character: '\\eb1d' });
|
||||
export const megaphone = new Codicon('megaphone', { character: '\\eb1e' });
|
||||
export const mention = new Codicon('mention', { character: '\\eb1f' });
|
||||
export const milestone = new Codicon('milestone', { character: '\\eb20' });
|
||||
export const mortarBoard = new Codicon('mortar-board', { character: '\\eb21' });
|
||||
export const move = new Codicon('move', { character: '\\eb22' });
|
||||
export const multipleWindows = new Codicon('multiple-windows', { character: '\\eb23' });
|
||||
export const mute = new Codicon('mute', { character: '\\eb24' });
|
||||
export const noNewline = new Codicon('no-newline', { character: '\\eb25' });
|
||||
export const note = new Codicon('note', { character: '\\eb26' });
|
||||
export const octoface = new Codicon('octoface', { character: '\\eb27' });
|
||||
export const openPreview = new Codicon('open-preview', { character: '\\eb28' });
|
||||
export const package_ = new Codicon('package', { character: '\\eb29' });
|
||||
export const paintcan = new Codicon('paintcan', { character: '\\eb2a' });
|
||||
export const pin = new Codicon('pin', { character: '\\eb2b' });
|
||||
export const play = new Codicon('play', { character: '\\eb2c' });
|
||||
export const run = new Codicon('run', { character: '\\eb2c' });
|
||||
export const plug = new Codicon('plug', { character: '\\eb2d' });
|
||||
export const preserveCase = new Codicon('preserve-case', { character: '\\eb2e' });
|
||||
export const preview = new Codicon('preview', { character: '\\eb2f' });
|
||||
export const project = new Codicon('project', { character: '\\eb30' });
|
||||
export const pulse = new Codicon('pulse', { character: '\\eb31' });
|
||||
export const question = new Codicon('question', { character: '\\eb32' });
|
||||
export const quote = new Codicon('quote', { character: '\\eb33' });
|
||||
export const radioTower = new Codicon('radio-tower', { character: '\\eb34' });
|
||||
export const reactions = new Codicon('reactions', { character: '\\eb35' });
|
||||
export const references = new Codicon('references', { character: '\\eb36' });
|
||||
export const refresh = new Codicon('refresh', { character: '\\eb37' });
|
||||
export const regex = new Codicon('regex', { character: '\\eb38' });
|
||||
export const remoteExplorer = new Codicon('remote-explorer', { character: '\\eb39' });
|
||||
export const remote = new Codicon('remote', { character: '\\eb3a' });
|
||||
export const remove = new Codicon('remove', { character: '\\eb3b' });
|
||||
export const replaceAll = new Codicon('replace-all', { character: '\\eb3c' });
|
||||
export const replace = new Codicon('replace', { character: '\\eb3d' });
|
||||
export const repoClone = new Codicon('repo-clone', { character: '\\eb3e' });
|
||||
export const repoForcePush = new Codicon('repo-force-push', { character: '\\eb3f' });
|
||||
export const repoPull = new Codicon('repo-pull', { character: '\\eb40' });
|
||||
export const repoPush = new Codicon('repo-push', { character: '\\eb41' });
|
||||
export const report = new Codicon('report', { character: '\\eb42' });
|
||||
export const requestChanges = new Codicon('request-changes', { character: '\\eb43' });
|
||||
export const rocket = new Codicon('rocket', { character: '\\eb44' });
|
||||
export const rootFolderOpened = new Codicon('root-folder-opened', { character: '\\eb45' });
|
||||
export const rootFolder = new Codicon('root-folder', { character: '\\eb46' });
|
||||
export const rss = new Codicon('rss', { character: '\\eb47' });
|
||||
export const ruby = new Codicon('ruby', { character: '\\eb48' });
|
||||
export const saveAll = new Codicon('save-all', { character: '\\eb49' });
|
||||
export const saveAs = new Codicon('save-as', { character: '\\eb4a' });
|
||||
export const save = new Codicon('save', { character: '\\eb4b' });
|
||||
export const screenFull = new Codicon('screen-full', { character: '\\eb4c' });
|
||||
export const screenNormal = new Codicon('screen-normal', { character: '\\eb4d' });
|
||||
export const searchStop = new Codicon('search-stop', { character: '\\eb4e' });
|
||||
export const server = new Codicon('server', { character: '\\eb50' });
|
||||
export const settingsGear = new Codicon('settings-gear', { character: '\\eb51' });
|
||||
export const settings = new Codicon('settings', { character: '\\eb52' });
|
||||
export const shield = new Codicon('shield', { character: '\\eb53' });
|
||||
export const smiley = new Codicon('smiley', { character: '\\eb54' });
|
||||
export const sortPrecedence = new Codicon('sort-precedence', { character: '\\eb55' });
|
||||
export const splitHorizontal = new Codicon('split-horizontal', { character: '\\eb56' });
|
||||
export const splitVertical = new Codicon('split-vertical', { character: '\\eb57' });
|
||||
export const squirrel = new Codicon('squirrel', { character: '\\eb58' });
|
||||
export const starFull = new Codicon('star-full', { character: '\\eb59' });
|
||||
export const starHalf = new Codicon('star-half', { character: '\\eb5a' });
|
||||
export const symbolClass = new Codicon('symbol-class', { character: '\\eb5b' });
|
||||
export const symbolColor = new Codicon('symbol-color', { character: '\\eb5c' });
|
||||
export const symbolConstant = new Codicon('symbol-constant', { character: '\\eb5d' });
|
||||
export const symbolEnumMember = new Codicon('symbol-enum-member', { character: '\\eb5e' });
|
||||
export const symbolField = new Codicon('symbol-field', { character: '\\eb5f' });
|
||||
export const symbolFile = new Codicon('symbol-file', { character: '\\eb60' });
|
||||
export const symbolInterface = new Codicon('symbol-interface', { character: '\\eb61' });
|
||||
export const symbolKeyword = new Codicon('symbol-keyword', { character: '\\eb62' });
|
||||
export const symbolMisc = new Codicon('symbol-misc', { character: '\\eb63' });
|
||||
export const symbolOperator = new Codicon('symbol-operator', { character: '\\eb64' });
|
||||
export const symbolProperty = new Codicon('symbol-property', { character: '\\eb65' });
|
||||
export const wrench = new Codicon('wrench', { character: '\\eb65' });
|
||||
export const wrenchSubaction = new Codicon('wrench-subaction', { character: '\\eb65' });
|
||||
export const symbolSnippet = new Codicon('symbol-snippet', { character: '\\eb66' });
|
||||
export const tasklist = new Codicon('tasklist', { character: '\\eb67' });
|
||||
export const telescope = new Codicon('telescope', { character: '\\eb68' });
|
||||
export const textSize = new Codicon('text-size', { character: '\\eb69' });
|
||||
export const threeBars = new Codicon('three-bars', { character: '\\eb6a' });
|
||||
export const thumbsdown = new Codicon('thumbsdown', { character: '\\eb6b' });
|
||||
export const thumbsup = new Codicon('thumbsup', { character: '\\eb6c' });
|
||||
export const tools = new Codicon('tools', { character: '\\eb6d' });
|
||||
export const triangleDown = new Codicon('triangle-down', { character: '\\eb6e' });
|
||||
export const triangleLeft = new Codicon('triangle-left', { character: '\\eb6f' });
|
||||
export const triangleRight = new Codicon('triangle-right', { character: '\\eb70' });
|
||||
export const triangleUp = new Codicon('triangle-up', { character: '\\eb71' });
|
||||
export const twitter = new Codicon('twitter', { character: '\\eb72' });
|
||||
export const unfold = new Codicon('unfold', { character: '\\eb73' });
|
||||
export const unlock = new Codicon('unlock', { character: '\\eb74' });
|
||||
export const unmute = new Codicon('unmute', { character: '\\eb75' });
|
||||
export const unverified = new Codicon('unverified', { character: '\\eb76' });
|
||||
export const verified = new Codicon('verified', { character: '\\eb77' });
|
||||
export const versions = new Codicon('versions', { character: '\\eb78' });
|
||||
export const vmActive = new Codicon('vm-active', { character: '\\eb79' });
|
||||
export const vmOutline = new Codicon('vm-outline', { character: '\\eb7a' });
|
||||
export const vmRunning = new Codicon('vm-running', { character: '\\eb7b' });
|
||||
export const watch = new Codicon('watch', { character: '\\eb7c' });
|
||||
export const whitespace = new Codicon('whitespace', { character: '\\eb7d' });
|
||||
export const wholeWord = new Codicon('whole-word', { character: '\\eb7e' });
|
||||
export const window = new Codicon('window', { character: '\\eb7f' });
|
||||
export const wordWrap = new Codicon('word-wrap', { character: '\\eb80' });
|
||||
export const zoomIn = new Codicon('zoom-in', { character: '\\eb81' });
|
||||
export const zoomOut = new Codicon('zoom-out', { character: '\\eb82' });
|
||||
export const listFilter = new Codicon('list-filter', { character: '\\eb83' });
|
||||
export const listFlat = new Codicon('list-flat', { character: '\\eb84' });
|
||||
export const listSelection = new Codicon('list-selection', { character: '\\eb85' });
|
||||
export const selection = new Codicon('selection', { character: '\\eb85' });
|
||||
export const listTree = new Codicon('list-tree', { character: '\\eb86' });
|
||||
export const debugBreakpointFunctionUnverified = new Codicon('debug-breakpoint-function-unverified', { character: '\\eb87' });
|
||||
export const debugBreakpointFunction = new Codicon('debug-breakpoint-function', { character: '\\eb88' });
|
||||
export const debugBreakpointFunctionDisabled = new Codicon('debug-breakpoint-function-disabled', { character: '\\eb88' });
|
||||
export const debugStackframeActive = new Codicon('debug-stackframe-active', { character: '\\eb89' });
|
||||
export const debugStackframeDot = new Codicon('debug-stackframe-dot', { character: '\\eb8a' });
|
||||
export const debugStackframe = new Codicon('debug-stackframe', { character: '\\eb8b' });
|
||||
export const debugStackframeFocused = new Codicon('debug-stackframe-focused', { character: '\\eb8b' });
|
||||
export const debugBreakpointUnsupported = new Codicon('debug-breakpoint-unsupported', { character: '\\eb8c' });
|
||||
export const symbolString = new Codicon('symbol-string', { character: '\\eb8d' });
|
||||
export const debugReverseContinue = new Codicon('debug-reverse-continue', { character: '\\eb8e' });
|
||||
export const debugStepBack = new Codicon('debug-step-back', { character: '\\eb8f' });
|
||||
export const debugRestartFrame = new Codicon('debug-restart-frame', { character: '\\eb90' });
|
||||
export const callIncoming = new Codicon('call-incoming', { character: '\\eb92' });
|
||||
export const callOutgoing = new Codicon('call-outgoing', { character: '\\eb93' });
|
||||
export const menu = new Codicon('menu', { character: '\\eb94' });
|
||||
export const expandAll = new Codicon('expand-all', { character: '\\eb95' });
|
||||
export const feedback = new Codicon('feedback', { character: '\\eb96' });
|
||||
export const groupByRefType = new Codicon('group-by-ref-type', { character: '\\eb97' });
|
||||
export const ungroupByRefType = new Codicon('ungroup-by-ref-type', { character: '\\eb98' });
|
||||
export const account = new Codicon('account', { character: '\\eb99' });
|
||||
export const bellDot = new Codicon('bell-dot', { character: '\\eb9a' });
|
||||
export const debugConsole = new Codicon('debug-console', { character: '\\eb9b' });
|
||||
export const library = new Codicon('library', { character: '\\eb9c' });
|
||||
export const output = new Codicon('output', { character: '\\eb9d' });
|
||||
export const runAll = new Codicon('run-all', { character: '\\eb9e' });
|
||||
export const syncIgnored = new Codicon('sync-ignored', { character: '\\eb9f' });
|
||||
export const pinned = new Codicon('pinned', { character: '\\eba0' });
|
||||
export const githubInverted = new Codicon('github-inverted', { character: '\\eba1' });
|
||||
export const debugAlt = new Codicon('debug-alt', { character: '\\eb91' });
|
||||
export const serverProcess = new Codicon('server-process', { character: '\\eba2' });
|
||||
export const serverEnvironment = new Codicon('server-environment', { character: '\\eba3' });
|
||||
export const pass = new Codicon('pass', { character: '\\eba4' });
|
||||
export const stopCircle = new Codicon('stop-circle', { character: '\\eba5' });
|
||||
export const playCircle = new Codicon('play-circle', { character: '\\eba6' });
|
||||
export const record = new Codicon('record', { character: '\\eba7' });
|
||||
export const debugAltSmall = new Codicon('debug-alt-small', { character: '\\eba8' });
|
||||
export const vmConnect = new Codicon('vm-connect', { character: '\\eba9' });
|
||||
export const cloud = new Codicon('cloud', { character: '\\ebaa' });
|
||||
export const merge = new Codicon('merge', { character: '\\ebab' });
|
||||
export const exportIcon = new Codicon('export', { character: '\\ebac' });
|
||||
export const graphLeft = new Codicon('graph-left', { character: '\\ebad' });
|
||||
export const magnet = new Codicon('magnet', { character: '\\ebae' });
|
||||
export const notebook = new Codicon('notebook', { character: '\\ebaf' });
|
||||
export const redo = new Codicon('redo', { character: '\\ebb0' });
|
||||
export const checkAll = new Codicon('check-all', { character: '\\ebb1' });
|
||||
export const pinnedDirty = new Codicon('pinned-dirty', { character: '\\ebb2' });
|
||||
export const passFilled = new Codicon('pass-filled', { character: '\\ebb3' });
|
||||
export const circleLargeFilled = new Codicon('circle-large-filled', { character: '\\ebb4' });
|
||||
export const circleLargeOutline = new Codicon('circle-large-outline', { character: '\\ebb5' });
|
||||
export const combine = new Codicon('combine', { character: '\\ebb6' });
|
||||
export const gather = new Codicon('gather', { character: '\\ebb6' });
|
||||
export const table = new Codicon('table', { character: '\\ebb7' });
|
||||
export const variableGroup = new Codicon('variable-group', { character: '\\ebb8' });
|
||||
export const typeHierarchy = new Codicon('type-hierarchy', { character: '\\ebb9' });
|
||||
export const typeHierarchySub = new Codicon('type-hierarchy-sub', { character: '\\ebba' });
|
||||
export const typeHierarchySuper = new Codicon('type-hierarchy-super', { character: '\\ebbb' });
|
||||
export const gitPullRequestCreate = new Codicon('git-pull-request-create', { character: '\\ebbc' });
|
||||
export const add = new Codicon('add', { fontCharacter: '\\ea60' });
|
||||
export const plus = new Codicon('plus', { fontCharacter: '\\ea60' });
|
||||
export const gistNew = new Codicon('gist-new', { fontCharacter: '\\ea60' });
|
||||
export const repoCreate = new Codicon('repo-create', { fontCharacter: '\\ea60' });
|
||||
export const lightbulb = new Codicon('lightbulb', { fontCharacter: '\\ea61' });
|
||||
export const lightBulb = new Codicon('light-bulb', { fontCharacter: '\\ea61' });
|
||||
export const repo = new Codicon('repo', { fontCharacter: '\\ea62' });
|
||||
export const repoDelete = new Codicon('repo-delete', { fontCharacter: '\\ea62' });
|
||||
export const gistFork = new Codicon('gist-fork', { fontCharacter: '\\ea63' });
|
||||
export const repoForked = new Codicon('repo-forked', { fontCharacter: '\\ea63' });
|
||||
export const gitPullRequest = new Codicon('git-pull-request', { fontCharacter: '\\ea64' });
|
||||
export const gitPullRequestAbandoned = new Codicon('git-pull-request-abandoned', { fontCharacter: '\\ea64' });
|
||||
export const recordKeys = new Codicon('record-keys', { fontCharacter: '\\ea65' });
|
||||
export const keyboard = new Codicon('keyboard', { fontCharacter: '\\ea65' });
|
||||
export const tag = new Codicon('tag', { fontCharacter: '\\ea66' });
|
||||
export const tagAdd = new Codicon('tag-add', { fontCharacter: '\\ea66' });
|
||||
export const tagRemove = new Codicon('tag-remove', { fontCharacter: '\\ea66' });
|
||||
export const person = new Codicon('person', { fontCharacter: '\\ea67' });
|
||||
export const personAdd = new Codicon('person-add', { fontCharacter: '\\ea67' });
|
||||
export const personFollow = new Codicon('person-follow', { fontCharacter: '\\ea67' });
|
||||
export const personOutline = new Codicon('person-outline', { fontCharacter: '\\ea67' });
|
||||
export const personFilled = new Codicon('person-filled', { fontCharacter: '\\ea67' });
|
||||
export const gitBranch = new Codicon('git-branch', { fontCharacter: '\\ea68' });
|
||||
export const gitBranchCreate = new Codicon('git-branch-create', { fontCharacter: '\\ea68' });
|
||||
export const gitBranchDelete = new Codicon('git-branch-delete', { fontCharacter: '\\ea68' });
|
||||
export const sourceControl = new Codicon('source-control', { fontCharacter: '\\ea68' });
|
||||
export const mirror = new Codicon('mirror', { fontCharacter: '\\ea69' });
|
||||
export const mirrorPublic = new Codicon('mirror-public', { fontCharacter: '\\ea69' });
|
||||
export const star = new Codicon('star', { fontCharacter: '\\ea6a' });
|
||||
export const starAdd = new Codicon('star-add', { fontCharacter: '\\ea6a' });
|
||||
export const starDelete = new Codicon('star-delete', { fontCharacter: '\\ea6a' });
|
||||
export const starEmpty = new Codicon('star-empty', { fontCharacter: '\\ea6a' });
|
||||
export const comment = new Codicon('comment', { fontCharacter: '\\ea6b' });
|
||||
export const commentAdd = new Codicon('comment-add', { fontCharacter: '\\ea6b' });
|
||||
export const alert = new Codicon('alert', { fontCharacter: '\\ea6c' });
|
||||
export const warning = new Codicon('warning', { fontCharacter: '\\ea6c' });
|
||||
export const search = new Codicon('search', { fontCharacter: '\\ea6d' });
|
||||
export const searchSave = new Codicon('search-save', { fontCharacter: '\\ea6d' });
|
||||
export const logOut = new Codicon('log-out', { fontCharacter: '\\ea6e' });
|
||||
export const signOut = new Codicon('sign-out', { fontCharacter: '\\ea6e' });
|
||||
export const logIn = new Codicon('log-in', { fontCharacter: '\\ea6f' });
|
||||
export const signIn = new Codicon('sign-in', { fontCharacter: '\\ea6f' });
|
||||
export const eye = new Codicon('eye', { fontCharacter: '\\ea70' });
|
||||
export const eyeUnwatch = new Codicon('eye-unwatch', { fontCharacter: '\\ea70' });
|
||||
export const eyeWatch = new Codicon('eye-watch', { fontCharacter: '\\ea70' });
|
||||
export const circleFilled = new Codicon('circle-filled', { fontCharacter: '\\ea71' });
|
||||
export const primitiveDot = new Codicon('primitive-dot', { fontCharacter: '\\ea71' });
|
||||
export const closeDirty = new Codicon('close-dirty', { fontCharacter: '\\ea71' });
|
||||
export const debugBreakpoint = new Codicon('debug-breakpoint', { fontCharacter: '\\ea71' });
|
||||
export const debugBreakpointDisabled = new Codicon('debug-breakpoint-disabled', { fontCharacter: '\\ea71' });
|
||||
export const debugHint = new Codicon('debug-hint', { fontCharacter: '\\ea71' });
|
||||
export const primitiveSquare = new Codicon('primitive-square', { fontCharacter: '\\ea72' });
|
||||
export const edit = new Codicon('edit', { fontCharacter: '\\ea73' });
|
||||
export const pencil = new Codicon('pencil', { fontCharacter: '\\ea73' });
|
||||
export const info = new Codicon('info', { fontCharacter: '\\ea74' });
|
||||
export const issueOpened = new Codicon('issue-opened', { fontCharacter: '\\ea74' });
|
||||
export const gistPrivate = new Codicon('gist-private', { fontCharacter: '\\ea75' });
|
||||
export const gitForkPrivate = new Codicon('git-fork-private', { fontCharacter: '\\ea75' });
|
||||
export const lock = new Codicon('lock', { fontCharacter: '\\ea75' });
|
||||
export const mirrorPrivate = new Codicon('mirror-private', { fontCharacter: '\\ea75' });
|
||||
export const close = new Codicon('close', { fontCharacter: '\\ea76' });
|
||||
export const removeClose = new Codicon('remove-close', { fontCharacter: '\\ea76' });
|
||||
export const x = new Codicon('x', { fontCharacter: '\\ea76' });
|
||||
export const repoSync = new Codicon('repo-sync', { fontCharacter: '\\ea77' });
|
||||
export const sync = new Codicon('sync', { fontCharacter: '\\ea77' });
|
||||
export const clone = new Codicon('clone', { fontCharacter: '\\ea78' });
|
||||
export const desktopDownload = new Codicon('desktop-download', { fontCharacter: '\\ea78' });
|
||||
export const beaker = new Codicon('beaker', { fontCharacter: '\\ea79' });
|
||||
export const microscope = new Codicon('microscope', { fontCharacter: '\\ea79' });
|
||||
export const vm = new Codicon('vm', { fontCharacter: '\\ea7a' });
|
||||
export const deviceDesktop = new Codicon('device-desktop', { fontCharacter: '\\ea7a' });
|
||||
export const file = new Codicon('file', { fontCharacter: '\\ea7b' });
|
||||
export const fileText = new Codicon('file-text', { fontCharacter: '\\ea7b' });
|
||||
export const more = new Codicon('more', { fontCharacter: '\\ea7c' });
|
||||
export const ellipsis = new Codicon('ellipsis', { fontCharacter: '\\ea7c' });
|
||||
export const kebabHorizontal = new Codicon('kebab-horizontal', { fontCharacter: '\\ea7c' });
|
||||
export const mailReply = new Codicon('mail-reply', { fontCharacter: '\\ea7d' });
|
||||
export const reply = new Codicon('reply', { fontCharacter: '\\ea7d' });
|
||||
export const organization = new Codicon('organization', { fontCharacter: '\\ea7e' });
|
||||
export const organizationFilled = new Codicon('organization-filled', { fontCharacter: '\\ea7e' });
|
||||
export const organizationOutline = new Codicon('organization-outline', { fontCharacter: '\\ea7e' });
|
||||
export const newFile = new Codicon('new-file', { fontCharacter: '\\ea7f' });
|
||||
export const fileAdd = new Codicon('file-add', { fontCharacter: '\\ea7f' });
|
||||
export const newFolder = new Codicon('new-folder', { fontCharacter: '\\ea80' });
|
||||
export const fileDirectoryCreate = new Codicon('file-directory-create', { fontCharacter: '\\ea80' });
|
||||
export const trash = new Codicon('trash', { fontCharacter: '\\ea81' });
|
||||
export const trashcan = new Codicon('trashcan', { fontCharacter: '\\ea81' });
|
||||
export const history = new Codicon('history', { fontCharacter: '\\ea82' });
|
||||
export const clock = new Codicon('clock', { fontCharacter: '\\ea82' });
|
||||
export const folder = new Codicon('folder', { fontCharacter: '\\ea83' });
|
||||
export const fileDirectory = new Codicon('file-directory', { fontCharacter: '\\ea83' });
|
||||
export const symbolFolder = new Codicon('symbol-folder', { fontCharacter: '\\ea83' });
|
||||
export const logoGithub = new Codicon('logo-github', { fontCharacter: '\\ea84' });
|
||||
export const markGithub = new Codicon('mark-github', { fontCharacter: '\\ea84' });
|
||||
export const github = new Codicon('github', { fontCharacter: '\\ea84' });
|
||||
export const terminal = new Codicon('terminal', { fontCharacter: '\\ea85' });
|
||||
export const console = new Codicon('console', { fontCharacter: '\\ea85' });
|
||||
export const repl = new Codicon('repl', { fontCharacter: '\\ea85' });
|
||||
export const zap = new Codicon('zap', { fontCharacter: '\\ea86' });
|
||||
export const symbolEvent = new Codicon('symbol-event', { fontCharacter: '\\ea86' });
|
||||
export const error = new Codicon('error', { fontCharacter: '\\ea87' });
|
||||
export const stop = new Codicon('stop', { fontCharacter: '\\ea87' });
|
||||
export const variable = new Codicon('variable', { fontCharacter: '\\ea88' });
|
||||
export const symbolVariable = new Codicon('symbol-variable', { fontCharacter: '\\ea88' });
|
||||
export const array = new Codicon('array', { fontCharacter: '\\ea8a' });
|
||||
export const symbolArray = new Codicon('symbol-array', { fontCharacter: '\\ea8a' });
|
||||
export const symbolModule = new Codicon('symbol-module', { fontCharacter: '\\ea8b' });
|
||||
export const symbolPackage = new Codicon('symbol-package', { fontCharacter: '\\ea8b' });
|
||||
export const symbolNamespace = new Codicon('symbol-namespace', { fontCharacter: '\\ea8b' });
|
||||
export const symbolObject = new Codicon('symbol-object', { fontCharacter: '\\ea8b' });
|
||||
export const symbolMethod = new Codicon('symbol-method', { fontCharacter: '\\ea8c' });
|
||||
export const symbolFunction = new Codicon('symbol-function', { fontCharacter: '\\ea8c' });
|
||||
export const symbolConstructor = new Codicon('symbol-constructor', { fontCharacter: '\\ea8c' });
|
||||
export const symbolBoolean = new Codicon('symbol-boolean', { fontCharacter: '\\ea8f' });
|
||||
export const symbolNull = new Codicon('symbol-null', { fontCharacter: '\\ea8f' });
|
||||
export const symbolNumeric = new Codicon('symbol-numeric', { fontCharacter: '\\ea90' });
|
||||
export const symbolNumber = new Codicon('symbol-number', { fontCharacter: '\\ea90' });
|
||||
export const symbolStructure = new Codicon('symbol-structure', { fontCharacter: '\\ea91' });
|
||||
export const symbolStruct = new Codicon('symbol-struct', { fontCharacter: '\\ea91' });
|
||||
export const symbolParameter = new Codicon('symbol-parameter', { fontCharacter: '\\ea92' });
|
||||
export const symbolTypeParameter = new Codicon('symbol-type-parameter', { fontCharacter: '\\ea92' });
|
||||
export const symbolKey = new Codicon('symbol-key', { fontCharacter: '\\ea93' });
|
||||
export const symbolText = new Codicon('symbol-text', { fontCharacter: '\\ea93' });
|
||||
export const symbolReference = new Codicon('symbol-reference', { fontCharacter: '\\ea94' });
|
||||
export const goToFile = new Codicon('go-to-file', { fontCharacter: '\\ea94' });
|
||||
export const symbolEnum = new Codicon('symbol-enum', { fontCharacter: '\\ea95' });
|
||||
export const symbolValue = new Codicon('symbol-value', { fontCharacter: '\\ea95' });
|
||||
export const symbolRuler = new Codicon('symbol-ruler', { fontCharacter: '\\ea96' });
|
||||
export const symbolUnit = new Codicon('symbol-unit', { fontCharacter: '\\ea96' });
|
||||
export const activateBreakpoints = new Codicon('activate-breakpoints', { fontCharacter: '\\ea97' });
|
||||
export const archive = new Codicon('archive', { fontCharacter: '\\ea98' });
|
||||
export const arrowBoth = new Codicon('arrow-both', { fontCharacter: '\\ea99' });
|
||||
export const arrowDown = new Codicon('arrow-down', { fontCharacter: '\\ea9a' });
|
||||
export const arrowLeft = new Codicon('arrow-left', { fontCharacter: '\\ea9b' });
|
||||
export const arrowRight = new Codicon('arrow-right', { fontCharacter: '\\ea9c' });
|
||||
export const arrowSmallDown = new Codicon('arrow-small-down', { fontCharacter: '\\ea9d' });
|
||||
export const arrowSmallLeft = new Codicon('arrow-small-left', { fontCharacter: '\\ea9e' });
|
||||
export const arrowSmallRight = new Codicon('arrow-small-right', { fontCharacter: '\\ea9f' });
|
||||
export const arrowSmallUp = new Codicon('arrow-small-up', { fontCharacter: '\\eaa0' });
|
||||
export const arrowUp = new Codicon('arrow-up', { fontCharacter: '\\eaa1' });
|
||||
export const bell = new Codicon('bell', { fontCharacter: '\\eaa2' });
|
||||
export const bold = new Codicon('bold', { fontCharacter: '\\eaa3' });
|
||||
export const book = new Codicon('book', { fontCharacter: '\\eaa4' });
|
||||
export const bookmark = new Codicon('bookmark', { fontCharacter: '\\eaa5' });
|
||||
export const debugBreakpointConditionalUnverified = new Codicon('debug-breakpoint-conditional-unverified', { fontCharacter: '\\eaa6' });
|
||||
export const debugBreakpointConditional = new Codicon('debug-breakpoint-conditional', { fontCharacter: '\\eaa7' });
|
||||
export const debugBreakpointConditionalDisabled = new Codicon('debug-breakpoint-conditional-disabled', { fontCharacter: '\\eaa7' });
|
||||
export const debugBreakpointDataUnverified = new Codicon('debug-breakpoint-data-unverified', { fontCharacter: '\\eaa8' });
|
||||
export const debugBreakpointData = new Codicon('debug-breakpoint-data', { fontCharacter: '\\eaa9' });
|
||||
export const debugBreakpointDataDisabled = new Codicon('debug-breakpoint-data-disabled', { fontCharacter: '\\eaa9' });
|
||||
export const debugBreakpointLogUnverified = new Codicon('debug-breakpoint-log-unverified', { fontCharacter: '\\eaaa' });
|
||||
export const debugBreakpointLog = new Codicon('debug-breakpoint-log', { fontCharacter: '\\eaab' });
|
||||
export const debugBreakpointLogDisabled = new Codicon('debug-breakpoint-log-disabled', { fontCharacter: '\\eaab' });
|
||||
export const briefcase = new Codicon('briefcase', { fontCharacter: '\\eaac' });
|
||||
export const broadcast = new Codicon('broadcast', { fontCharacter: '\\eaad' });
|
||||
export const browser = new Codicon('browser', { fontCharacter: '\\eaae' });
|
||||
export const bug = new Codicon('bug', { fontCharacter: '\\eaaf' });
|
||||
export const calendar = new Codicon('calendar', { fontCharacter: '\\eab0' });
|
||||
export const caseSensitive = new Codicon('case-sensitive', { fontCharacter: '\\eab1' });
|
||||
export const check = new Codicon('check', { fontCharacter: '\\eab2' });
|
||||
export const checklist = new Codicon('checklist', { fontCharacter: '\\eab3' });
|
||||
export const chevronDown = new Codicon('chevron-down', { fontCharacter: '\\eab4' });
|
||||
export const chevronLeft = new Codicon('chevron-left', { fontCharacter: '\\eab5' });
|
||||
export const chevronRight = new Codicon('chevron-right', { fontCharacter: '\\eab6' });
|
||||
export const chevronUp = new Codicon('chevron-up', { fontCharacter: '\\eab7' });
|
||||
export const chromeClose = new Codicon('chrome-close', { fontCharacter: '\\eab8' });
|
||||
export const chromeMaximize = new Codicon('chrome-maximize', { fontCharacter: '\\eab9' });
|
||||
export const chromeMinimize = new Codicon('chrome-minimize', { fontCharacter: '\\eaba' });
|
||||
export const chromeRestore = new Codicon('chrome-restore', { fontCharacter: '\\eabb' });
|
||||
export const circleOutline = new Codicon('circle-outline', { fontCharacter: '\\eabc' });
|
||||
export const debugBreakpointUnverified = new Codicon('debug-breakpoint-unverified', { fontCharacter: '\\eabc' });
|
||||
export const circleSlash = new Codicon('circle-slash', { fontCharacter: '\\eabd' });
|
||||
export const circuitBoard = new Codicon('circuit-board', { fontCharacter: '\\eabe' });
|
||||
export const clearAll = new Codicon('clear-all', { fontCharacter: '\\eabf' });
|
||||
export const clippy = new Codicon('clippy', { fontCharacter: '\\eac0' });
|
||||
export const closeAll = new Codicon('close-all', { fontCharacter: '\\eac1' });
|
||||
export const cloudDownload = new Codicon('cloud-download', { fontCharacter: '\\eac2' });
|
||||
export const cloudUpload = new Codicon('cloud-upload', { fontCharacter: '\\eac3' });
|
||||
export const code = new Codicon('code', { fontCharacter: '\\eac4' });
|
||||
export const collapseAll = new Codicon('collapse-all', { fontCharacter: '\\eac5' });
|
||||
export const colorMode = new Codicon('color-mode', { fontCharacter: '\\eac6' });
|
||||
export const commentDiscussion = new Codicon('comment-discussion', { fontCharacter: '\\eac7' });
|
||||
export const compareChanges = new Codicon('compare-changes', { fontCharacter: '\\eafd' });
|
||||
export const creditCard = new Codicon('credit-card', { fontCharacter: '\\eac9' });
|
||||
export const dash = new Codicon('dash', { fontCharacter: '\\eacc' });
|
||||
export const dashboard = new Codicon('dashboard', { fontCharacter: '\\eacd' });
|
||||
export const database = new Codicon('database', { fontCharacter: '\\eace' });
|
||||
export const debugContinue = new Codicon('debug-continue', { fontCharacter: '\\eacf' });
|
||||
export const debugDisconnect = new Codicon('debug-disconnect', { fontCharacter: '\\ead0' });
|
||||
export const debugPause = new Codicon('debug-pause', { fontCharacter: '\\ead1' });
|
||||
export const debugRestart = new Codicon('debug-restart', { fontCharacter: '\\ead2' });
|
||||
export const debugStart = new Codicon('debug-start', { fontCharacter: '\\ead3' });
|
||||
export const debugStepInto = new Codicon('debug-step-into', { fontCharacter: '\\ead4' });
|
||||
export const debugStepOut = new Codicon('debug-step-out', { fontCharacter: '\\ead5' });
|
||||
export const debugStepOver = new Codicon('debug-step-over', { fontCharacter: '\\ead6' });
|
||||
export const debugStop = new Codicon('debug-stop', { fontCharacter: '\\ead7' });
|
||||
export const debug = new Codicon('debug', { fontCharacter: '\\ead8' });
|
||||
export const deviceCameraVideo = new Codicon('device-camera-video', { fontCharacter: '\\ead9' });
|
||||
export const deviceCamera = new Codicon('device-camera', { fontCharacter: '\\eada' });
|
||||
export const deviceMobile = new Codicon('device-mobile', { fontCharacter: '\\eadb' });
|
||||
export const diffAdded = new Codicon('diff-added', { fontCharacter: '\\eadc' });
|
||||
export const diffIgnored = new Codicon('diff-ignored', { fontCharacter: '\\eadd' });
|
||||
export const diffModified = new Codicon('diff-modified', { fontCharacter: '\\eade' });
|
||||
export const diffRemoved = new Codicon('diff-removed', { fontCharacter: '\\eadf' });
|
||||
export const diffRenamed = new Codicon('diff-renamed', { fontCharacter: '\\eae0' });
|
||||
export const diff = new Codicon('diff', { fontCharacter: '\\eae1' });
|
||||
export const discard = new Codicon('discard', { fontCharacter: '\\eae2' });
|
||||
export const editorLayout = new Codicon('editor-layout', { fontCharacter: '\\eae3' });
|
||||
export const emptyWindow = new Codicon('empty-window', { fontCharacter: '\\eae4' });
|
||||
export const exclude = new Codicon('exclude', { fontCharacter: '\\eae5' });
|
||||
export const extensions = new Codicon('extensions', { fontCharacter: '\\eae6' });
|
||||
export const eyeClosed = new Codicon('eye-closed', { fontCharacter: '\\eae7' });
|
||||
export const fileBinary = new Codicon('file-binary', { fontCharacter: '\\eae8' });
|
||||
export const fileCode = new Codicon('file-code', { fontCharacter: '\\eae9' });
|
||||
export const fileMedia = new Codicon('file-media', { fontCharacter: '\\eaea' });
|
||||
export const filePdf = new Codicon('file-pdf', { fontCharacter: '\\eaeb' });
|
||||
export const fileSubmodule = new Codicon('file-submodule', { fontCharacter: '\\eaec' });
|
||||
export const fileSymlinkDirectory = new Codicon('file-symlink-directory', { fontCharacter: '\\eaed' });
|
||||
export const fileSymlinkFile = new Codicon('file-symlink-file', { fontCharacter: '\\eaee' });
|
||||
export const fileZip = new Codicon('file-zip', { fontCharacter: '\\eaef' });
|
||||
export const files = new Codicon('files', { fontCharacter: '\\eaf0' });
|
||||
export const filter = new Codicon('filter', { fontCharacter: '\\eaf1' });
|
||||
export const flame = new Codicon('flame', { fontCharacter: '\\eaf2' });
|
||||
export const foldDown = new Codicon('fold-down', { fontCharacter: '\\eaf3' });
|
||||
export const foldUp = new Codicon('fold-up', { fontCharacter: '\\eaf4' });
|
||||
export const fold = new Codicon('fold', { fontCharacter: '\\eaf5' });
|
||||
export const folderActive = new Codicon('folder-active', { fontCharacter: '\\eaf6' });
|
||||
export const folderOpened = new Codicon('folder-opened', { fontCharacter: '\\eaf7' });
|
||||
export const gear = new Codicon('gear', { fontCharacter: '\\eaf8' });
|
||||
export const gift = new Codicon('gift', { fontCharacter: '\\eaf9' });
|
||||
export const gistSecret = new Codicon('gist-secret', { fontCharacter: '\\eafa' });
|
||||
export const gist = new Codicon('gist', { fontCharacter: '\\eafb' });
|
||||
export const gitCommit = new Codicon('git-commit', { fontCharacter: '\\eafc' });
|
||||
export const gitCompare = new Codicon('git-compare', { fontCharacter: '\\eafd' });
|
||||
export const gitMerge = new Codicon('git-merge', { fontCharacter: '\\eafe' });
|
||||
export const githubAction = new Codicon('github-action', { fontCharacter: '\\eaff' });
|
||||
export const githubAlt = new Codicon('github-alt', { fontCharacter: '\\eb00' });
|
||||
export const globe = new Codicon('globe', { fontCharacter: '\\eb01' });
|
||||
export const grabber = new Codicon('grabber', { fontCharacter: '\\eb02' });
|
||||
export const graph = new Codicon('graph', { fontCharacter: '\\eb03' });
|
||||
export const gripper = new Codicon('gripper', { fontCharacter: '\\eb04' });
|
||||
export const heart = new Codicon('heart', { fontCharacter: '\\eb05' });
|
||||
export const home = new Codicon('home', { fontCharacter: '\\eb06' });
|
||||
export const horizontalRule = new Codicon('horizontal-rule', { fontCharacter: '\\eb07' });
|
||||
export const hubot = new Codicon('hubot', { fontCharacter: '\\eb08' });
|
||||
export const inbox = new Codicon('inbox', { fontCharacter: '\\eb09' });
|
||||
export const issueClosed = new Codicon('issue-closed', { fontCharacter: '\\eb0a' });
|
||||
export const issueReopened = new Codicon('issue-reopened', { fontCharacter: '\\eb0b' });
|
||||
export const issues = new Codicon('issues', { fontCharacter: '\\eb0c' });
|
||||
export const italic = new Codicon('italic', { fontCharacter: '\\eb0d' });
|
||||
export const jersey = new Codicon('jersey', { fontCharacter: '\\eb0e' });
|
||||
export const json = new Codicon('json', { fontCharacter: '\\eb0f' });
|
||||
export const kebabVertical = new Codicon('kebab-vertical', { fontCharacter: '\\eb10' });
|
||||
export const key = new Codicon('key', { fontCharacter: '\\eb11' });
|
||||
export const law = new Codicon('law', { fontCharacter: '\\eb12' });
|
||||
export const lightbulbAutofix = new Codicon('lightbulb-autofix', { fontCharacter: '\\eb13' });
|
||||
export const linkExternal = new Codicon('link-external', { fontCharacter: '\\eb14' });
|
||||
export const link = new Codicon('link', { fontCharacter: '\\eb15' });
|
||||
export const listOrdered = new Codicon('list-ordered', { fontCharacter: '\\eb16' });
|
||||
export const listUnordered = new Codicon('list-unordered', { fontCharacter: '\\eb17' });
|
||||
export const liveShare = new Codicon('live-share', { fontCharacter: '\\eb18' });
|
||||
export const loading = new Codicon('loading', { fontCharacter: '\\eb19' });
|
||||
export const location = new Codicon('location', { fontCharacter: '\\eb1a' });
|
||||
export const mailRead = new Codicon('mail-read', { fontCharacter: '\\eb1b' });
|
||||
export const mail = new Codicon('mail', { fontCharacter: '\\eb1c' });
|
||||
export const markdown = new Codicon('markdown', { fontCharacter: '\\eb1d' });
|
||||
export const megaphone = new Codicon('megaphone', { fontCharacter: '\\eb1e' });
|
||||
export const mention = new Codicon('mention', { fontCharacter: '\\eb1f' });
|
||||
export const milestone = new Codicon('milestone', { fontCharacter: '\\eb20' });
|
||||
export const mortarBoard = new Codicon('mortar-board', { fontCharacter: '\\eb21' });
|
||||
export const move = new Codicon('move', { fontCharacter: '\\eb22' });
|
||||
export const multipleWindows = new Codicon('multiple-windows', { fontCharacter: '\\eb23' });
|
||||
export const mute = new Codicon('mute', { fontCharacter: '\\eb24' });
|
||||
export const noNewline = new Codicon('no-newline', { fontCharacter: '\\eb25' });
|
||||
export const note = new Codicon('note', { fontCharacter: '\\eb26' });
|
||||
export const octoface = new Codicon('octoface', { fontCharacter: '\\eb27' });
|
||||
export const openPreview = new Codicon('open-preview', { fontCharacter: '\\eb28' });
|
||||
export const package_ = new Codicon('package', { fontCharacter: '\\eb29' });
|
||||
export const paintcan = new Codicon('paintcan', { fontCharacter: '\\eb2a' });
|
||||
export const pin = new Codicon('pin', { fontCharacter: '\\eb2b' });
|
||||
export const play = new Codicon('play', { fontCharacter: '\\eb2c' });
|
||||
export const run = new Codicon('run', { fontCharacter: '\\eb2c' });
|
||||
export const plug = new Codicon('plug', { fontCharacter: '\\eb2d' });
|
||||
export const preserveCase = new Codicon('preserve-case', { fontCharacter: '\\eb2e' });
|
||||
export const preview = new Codicon('preview', { fontCharacter: '\\eb2f' });
|
||||
export const project = new Codicon('project', { fontCharacter: '\\eb30' });
|
||||
export const pulse = new Codicon('pulse', { fontCharacter: '\\eb31' });
|
||||
export const question = new Codicon('question', { fontCharacter: '\\eb32' });
|
||||
export const quote = new Codicon('quote', { fontCharacter: '\\eb33' });
|
||||
export const radioTower = new Codicon('radio-tower', { fontCharacter: '\\eb34' });
|
||||
export const reactions = new Codicon('reactions', { fontCharacter: '\\eb35' });
|
||||
export const references = new Codicon('references', { fontCharacter: '\\eb36' });
|
||||
export const refresh = new Codicon('refresh', { fontCharacter: '\\eb37' });
|
||||
export const regex = new Codicon('regex', { fontCharacter: '\\eb38' });
|
||||
export const remoteExplorer = new Codicon('remote-explorer', { fontCharacter: '\\eb39' });
|
||||
export const remote = new Codicon('remote', { fontCharacter: '\\eb3a' });
|
||||
export const remove = new Codicon('remove', { fontCharacter: '\\eb3b' });
|
||||
export const replaceAll = new Codicon('replace-all', { fontCharacter: '\\eb3c' });
|
||||
export const replace = new Codicon('replace', { fontCharacter: '\\eb3d' });
|
||||
export const repoClone = new Codicon('repo-clone', { fontCharacter: '\\eb3e' });
|
||||
export const repoForcePush = new Codicon('repo-force-push', { fontCharacter: '\\eb3f' });
|
||||
export const repoPull = new Codicon('repo-pull', { fontCharacter: '\\eb40' });
|
||||
export const repoPush = new Codicon('repo-push', { fontCharacter: '\\eb41' });
|
||||
export const report = new Codicon('report', { fontCharacter: '\\eb42' });
|
||||
export const requestChanges = new Codicon('request-changes', { fontCharacter: '\\eb43' });
|
||||
export const rocket = new Codicon('rocket', { fontCharacter: '\\eb44' });
|
||||
export const rootFolderOpened = new Codicon('root-folder-opened', { fontCharacter: '\\eb45' });
|
||||
export const rootFolder = new Codicon('root-folder', { fontCharacter: '\\eb46' });
|
||||
export const rss = new Codicon('rss', { fontCharacter: '\\eb47' });
|
||||
export const ruby = new Codicon('ruby', { fontCharacter: '\\eb48' });
|
||||
export const saveAll = new Codicon('save-all', { fontCharacter: '\\eb49' });
|
||||
export const saveAs = new Codicon('save-as', { fontCharacter: '\\eb4a' });
|
||||
export const save = new Codicon('save', { fontCharacter: '\\eb4b' });
|
||||
export const screenFull = new Codicon('screen-full', { fontCharacter: '\\eb4c' });
|
||||
export const screenNormal = new Codicon('screen-normal', { fontCharacter: '\\eb4d' });
|
||||
export const searchStop = new Codicon('search-stop', { fontCharacter: '\\eb4e' });
|
||||
export const server = new Codicon('server', { fontCharacter: '\\eb50' });
|
||||
export const settingsGear = new Codicon('settings-gear', { fontCharacter: '\\eb51' });
|
||||
export const settings = new Codicon('settings', { fontCharacter: '\\eb52' });
|
||||
export const shield = new Codicon('shield', { fontCharacter: '\\eb53' });
|
||||
export const smiley = new Codicon('smiley', { fontCharacter: '\\eb54' });
|
||||
export const sortPrecedence = new Codicon('sort-precedence', { fontCharacter: '\\eb55' });
|
||||
export const splitHorizontal = new Codicon('split-horizontal', { fontCharacter: '\\eb56' });
|
||||
export const splitVertical = new Codicon('split-vertical', { fontCharacter: '\\eb57' });
|
||||
export const squirrel = new Codicon('squirrel', { fontCharacter: '\\eb58' });
|
||||
export const starFull = new Codicon('star-full', { fontCharacter: '\\eb59' });
|
||||
export const starHalf = new Codicon('star-half', { fontCharacter: '\\eb5a' });
|
||||
export const symbolClass = new Codicon('symbol-class', { fontCharacter: '\\eb5b' });
|
||||
export const symbolColor = new Codicon('symbol-color', { fontCharacter: '\\eb5c' });
|
||||
export const symbolConstant = new Codicon('symbol-constant', { fontCharacter: '\\eb5d' });
|
||||
export const symbolEnumMember = new Codicon('symbol-enum-member', { fontCharacter: '\\eb5e' });
|
||||
export const symbolField = new Codicon('symbol-field', { fontCharacter: '\\eb5f' });
|
||||
export const symbolFile = new Codicon('symbol-file', { fontCharacter: '\\eb60' });
|
||||
export const symbolInterface = new Codicon('symbol-interface', { fontCharacter: '\\eb61' });
|
||||
export const symbolKeyword = new Codicon('symbol-keyword', { fontCharacter: '\\eb62' });
|
||||
export const symbolMisc = new Codicon('symbol-misc', { fontCharacter: '\\eb63' });
|
||||
export const symbolOperator = new Codicon('symbol-operator', { fontCharacter: '\\eb64' });
|
||||
export const symbolProperty = new Codicon('symbol-property', { fontCharacter: '\\eb65' });
|
||||
export const wrench = new Codicon('wrench', { fontCharacter: '\\eb65' });
|
||||
export const wrenchSubaction = new Codicon('wrench-subaction', { fontCharacter: '\\eb65' });
|
||||
export const symbolSnippet = new Codicon('symbol-snippet', { fontCharacter: '\\eb66' });
|
||||
export const tasklist = new Codicon('tasklist', { fontCharacter: '\\eb67' });
|
||||
export const telescope = new Codicon('telescope', { fontCharacter: '\\eb68' });
|
||||
export const textSize = new Codicon('text-size', { fontCharacter: '\\eb69' });
|
||||
export const threeBars = new Codicon('three-bars', { fontCharacter: '\\eb6a' });
|
||||
export const thumbsdown = new Codicon('thumbsdown', { fontCharacter: '\\eb6b' });
|
||||
export const thumbsup = new Codicon('thumbsup', { fontCharacter: '\\eb6c' });
|
||||
export const tools = new Codicon('tools', { fontCharacter: '\\eb6d' });
|
||||
export const triangleDown = new Codicon('triangle-down', { fontCharacter: '\\eb6e' });
|
||||
export const triangleLeft = new Codicon('triangle-left', { fontCharacter: '\\eb6f' });
|
||||
export const triangleRight = new Codicon('triangle-right', { fontCharacter: '\\eb70' });
|
||||
export const triangleUp = new Codicon('triangle-up', { fontCharacter: '\\eb71' });
|
||||
export const twitter = new Codicon('twitter', { fontCharacter: '\\eb72' });
|
||||
export const unfold = new Codicon('unfold', { fontCharacter: '\\eb73' });
|
||||
export const unlock = new Codicon('unlock', { fontCharacter: '\\eb74' });
|
||||
export const unmute = new Codicon('unmute', { fontCharacter: '\\eb75' });
|
||||
export const unverified = new Codicon('unverified', { fontCharacter: '\\eb76' });
|
||||
export const verified = new Codicon('verified', { fontCharacter: '\\eb77' });
|
||||
export const versions = new Codicon('versions', { fontCharacter: '\\eb78' });
|
||||
export const vmActive = new Codicon('vm-active', { fontCharacter: '\\eb79' });
|
||||
export const vmOutline = new Codicon('vm-outline', { fontCharacter: '\\eb7a' });
|
||||
export const vmRunning = new Codicon('vm-running', { fontCharacter: '\\eb7b' });
|
||||
export const watch = new Codicon('watch', { fontCharacter: '\\eb7c' });
|
||||
export const whitespace = new Codicon('whitespace', { fontCharacter: '\\eb7d' });
|
||||
export const wholeWord = new Codicon('whole-word', { fontCharacter: '\\eb7e' });
|
||||
export const window = new Codicon('window', { fontCharacter: '\\eb7f' });
|
||||
export const wordWrap = new Codicon('word-wrap', { fontCharacter: '\\eb80' });
|
||||
export const zoomIn = new Codicon('zoom-in', { fontCharacter: '\\eb81' });
|
||||
export const zoomOut = new Codicon('zoom-out', { fontCharacter: '\\eb82' });
|
||||
export const listFilter = new Codicon('list-filter', { fontCharacter: '\\eb83' });
|
||||
export const listFlat = new Codicon('list-flat', { fontCharacter: '\\eb84' });
|
||||
export const listSelection = new Codicon('list-selection', { fontCharacter: '\\eb85' });
|
||||
export const selection = new Codicon('selection', { fontCharacter: '\\eb85' });
|
||||
export const listTree = new Codicon('list-tree', { fontCharacter: '\\eb86' });
|
||||
export const debugBreakpointFunctionUnverified = new Codicon('debug-breakpoint-function-unverified', { fontCharacter: '\\eb87' });
|
||||
export const debugBreakpointFunction = new Codicon('debug-breakpoint-function', { fontCharacter: '\\eb88' });
|
||||
export const debugBreakpointFunctionDisabled = new Codicon('debug-breakpoint-function-disabled', { fontCharacter: '\\eb88' });
|
||||
export const debugStackframeActive = new Codicon('debug-stackframe-active', { fontCharacter: '\\eb89' });
|
||||
export const debugStackframeDot = new Codicon('debug-stackframe-dot', { fontCharacter: '\\eb8a' });
|
||||
export const debugStackframe = new Codicon('debug-stackframe', { fontCharacter: '\\eb8b' });
|
||||
export const debugStackframeFocused = new Codicon('debug-stackframe-focused', { fontCharacter: '\\eb8b' });
|
||||
export const debugBreakpointUnsupported = new Codicon('debug-breakpoint-unsupported', { fontCharacter: '\\eb8c' });
|
||||
export const symbolString = new Codicon('symbol-string', { fontCharacter: '\\eb8d' });
|
||||
export const debugReverseContinue = new Codicon('debug-reverse-continue', { fontCharacter: '\\eb8e' });
|
||||
export const debugStepBack = new Codicon('debug-step-back', { fontCharacter: '\\eb8f' });
|
||||
export const debugRestartFrame = new Codicon('debug-restart-frame', { fontCharacter: '\\eb90' });
|
||||
export const callIncoming = new Codicon('call-incoming', { fontCharacter: '\\eb92' });
|
||||
export const callOutgoing = new Codicon('call-outgoing', { fontCharacter: '\\eb93' });
|
||||
export const menu = new Codicon('menu', { fontCharacter: '\\eb94' });
|
||||
export const expandAll = new Codicon('expand-all', { fontCharacter: '\\eb95' });
|
||||
export const feedback = new Codicon('feedback', { fontCharacter: '\\eb96' });
|
||||
export const groupByRefType = new Codicon('group-by-ref-type', { fontCharacter: '\\eb97' });
|
||||
export const ungroupByRefType = new Codicon('ungroup-by-ref-type', { fontCharacter: '\\eb98' });
|
||||
export const account = new Codicon('account', { fontCharacter: '\\eb99' });
|
||||
export const bellDot = new Codicon('bell-dot', { fontCharacter: '\\eb9a' });
|
||||
export const debugConsole = new Codicon('debug-console', { fontCharacter: '\\eb9b' });
|
||||
export const library = new Codicon('library', { fontCharacter: '\\eb9c' });
|
||||
export const output = new Codicon('output', { fontCharacter: '\\eb9d' });
|
||||
export const runAll = new Codicon('run-all', { fontCharacter: '\\eb9e' });
|
||||
export const syncIgnored = new Codicon('sync-ignored', { fontCharacter: '\\eb9f' });
|
||||
export const pinned = new Codicon('pinned', { fontCharacter: '\\eba0' });
|
||||
export const githubInverted = new Codicon('github-inverted', { fontCharacter: '\\eba1' });
|
||||
export const debugAlt = new Codicon('debug-alt', { fontCharacter: '\\eb91' });
|
||||
export const serverProcess = new Codicon('server-process', { fontCharacter: '\\eba2' });
|
||||
export const serverEnvironment = new Codicon('server-environment', { fontCharacter: '\\eba3' });
|
||||
export const pass = new Codicon('pass', { fontCharacter: '\\eba4' });
|
||||
export const stopCircle = new Codicon('stop-circle', { fontCharacter: '\\eba5' });
|
||||
export const playCircle = new Codicon('play-circle', { fontCharacter: '\\eba6' });
|
||||
export const record = new Codicon('record', { fontCharacter: '\\eba7' });
|
||||
export const debugAltSmall = new Codicon('debug-alt-small', { fontCharacter: '\\eba8' });
|
||||
export const vmConnect = new Codicon('vm-connect', { fontCharacter: '\\eba9' });
|
||||
export const cloud = new Codicon('cloud', { fontCharacter: '\\ebaa' });
|
||||
export const merge = new Codicon('merge', { fontCharacter: '\\ebab' });
|
||||
export const exportIcon = new Codicon('export', { fontCharacter: '\\ebac' });
|
||||
export const graphLeft = new Codicon('graph-left', { fontCharacter: '\\ebad' });
|
||||
export const magnet = new Codicon('magnet', { fontCharacter: '\\ebae' });
|
||||
export const notebook = new Codicon('notebook', { fontCharacter: '\\ebaf' });
|
||||
export const redo = new Codicon('redo', { fontCharacter: '\\ebb0' });
|
||||
export const checkAll = new Codicon('check-all', { fontCharacter: '\\ebb1' });
|
||||
export const pinnedDirty = new Codicon('pinned-dirty', { fontCharacter: '\\ebb2' });
|
||||
export const passFilled = new Codicon('pass-filled', { fontCharacter: '\\ebb3' });
|
||||
export const circleLargeFilled = new Codicon('circle-large-filled', { fontCharacter: '\\ebb4' });
|
||||
export const circleLargeOutline = new Codicon('circle-large-outline', { fontCharacter: '\\ebb5' });
|
||||
export const combine = new Codicon('combine', { fontCharacter: '\\ebb6' });
|
||||
export const gather = new Codicon('gather', { fontCharacter: '\\ebb6' });
|
||||
export const table = new Codicon('table', { fontCharacter: '\\ebb7' });
|
||||
export const variableGroup = new Codicon('variable-group', { fontCharacter: '\\ebb8' });
|
||||
export const typeHierarchy = new Codicon('type-hierarchy', { fontCharacter: '\\ebb9' });
|
||||
export const typeHierarchySub = new Codicon('type-hierarchy-sub', { fontCharacter: '\\ebba' });
|
||||
export const typeHierarchySuper = new Codicon('type-hierarchy-super', { fontCharacter: '\\ebbb' });
|
||||
export const gitPullRequestCreate = new Codicon('git-pull-request-create', { fontCharacter: '\\ebbc' });
|
||||
export const runAbove = new Codicon('run-above', { fontCharacter: '\\ebbd' });
|
||||
export const runBelow = new Codicon('run-below', { fontCharacter: '\\ebbe' });
|
||||
export const notebookTemplate = new Codicon('notebook-template', { fontCharacter: '\\ebbf' });
|
||||
export const debugRerun = new Codicon('debug-rerun', { fontCharacter: '\\ebc0' });
|
||||
|
||||
export const dropDownButton = new Codicon('drop-down-button', Codicon.chevronDown.definition);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IAction } from 'vs/base/common/actions';
|
||||
|
||||
export interface ErrorListenerCallback {
|
||||
(error: any): void;
|
||||
}
|
||||
@@ -221,3 +223,31 @@ export class NotSupportedError extends Error {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class ExpectedError extends Error {
|
||||
readonly isExpected = true;
|
||||
}
|
||||
|
||||
export interface IErrorOptions {
|
||||
actions?: ReadonlyArray<IAction>;
|
||||
}
|
||||
|
||||
export interface IErrorWithActions {
|
||||
actions?: ReadonlyArray<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(message: string, options: IErrorOptions = Object.create(null)): Error & IErrorWithActions {
|
||||
const result = new Error(message);
|
||||
|
||||
if (options.actions) {
|
||||
(result as IErrorWithActions).actions = options.actions;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -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 { IAction } from 'vs/base/common/actions';
|
||||
|
||||
export interface IErrorOptions {
|
||||
actions?: ReadonlyArray<IAction>;
|
||||
}
|
||||
|
||||
export interface IErrorWithActions {
|
||||
actions?: ReadonlyArray<IAction>;
|
||||
}
|
||||
|
||||
export function isErrorWithActions(obj: unknown): obj is IErrorWithActions {
|
||||
return obj instanceof Error && Array.isArray((obj as IErrorWithActions).actions);
|
||||
}
|
||||
|
||||
export function createErrorWithActions(message: string, options: IErrorOptions = Object.create(null)): Error & IErrorWithActions {
|
||||
const result = new Error(message);
|
||||
|
||||
if (options.actions) {
|
||||
(<IErrorWithActions>result).actions = options.actions;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -7,7 +7,6 @@ import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { once as onceFn } from 'vs/base/common/functional';
|
||||
import { Disposable, IDisposable, toDisposable, combinedDisposable, DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { LinkedList } from 'vs/base/common/linkedList';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { StopWatch } from 'vs/base/common/stopwatch';
|
||||
|
||||
/**
|
||||
@@ -379,7 +378,7 @@ export namespace Event {
|
||||
}
|
||||
}
|
||||
|
||||
type Listener<T> = [(e: T) => void, any] | ((e: T) => void);
|
||||
export type Listener<T> = [(e: T) => void, any] | ((e: T) => void);
|
||||
|
||||
export interface EmitterOptions {
|
||||
onFirstListenerAdd?: Function;
|
||||
@@ -686,64 +685,6 @@ export class PauseableEmitter<T> extends Emitter<T> {
|
||||
}
|
||||
}
|
||||
|
||||
export interface IWaitUntil {
|
||||
waitUntil(thenable: Promise<any>): void;
|
||||
}
|
||||
|
||||
export class AsyncEmitter<T extends IWaitUntil> extends Emitter<T> {
|
||||
|
||||
private _asyncDeliveryQueue?: LinkedList<[Listener<T>, Omit<T, 'waitUntil'>]>;
|
||||
|
||||
async fireAsync(data: Omit<T, 'waitUntil'>, token: CancellationToken, promiseJoin?: (p: Promise<any>, listener: Function) => Promise<any>): Promise<void> {
|
||||
if (!this._listeners) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this._asyncDeliveryQueue) {
|
||||
this._asyncDeliveryQueue = new LinkedList();
|
||||
}
|
||||
|
||||
for (const listener of this._listeners) {
|
||||
this._asyncDeliveryQueue.push([listener, data]);
|
||||
}
|
||||
|
||||
while (this._asyncDeliveryQueue.size > 0 && !token.isCancellationRequested) {
|
||||
|
||||
const [listener, data] = this._asyncDeliveryQueue.shift()!;
|
||||
const thenables: Promise<any>[] = [];
|
||||
|
||||
const event = <T>{
|
||||
...data,
|
||||
waitUntil: (p: Promise<any>): void => {
|
||||
if (Object.isFrozen(thenables)) {
|
||||
throw new Error('waitUntil can NOT be called asynchronous');
|
||||
}
|
||||
if (promiseJoin) {
|
||||
p = promiseJoin(p, typeof listener === 'function' ? listener : listener[0]);
|
||||
}
|
||||
thenables.push(p);
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
if (typeof listener === 'function') {
|
||||
listener.call(undefined, event);
|
||||
} else {
|
||||
listener[0].call(listener[1], event);
|
||||
}
|
||||
} catch (e) {
|
||||
onUnexpectedError(e);
|
||||
continue;
|
||||
}
|
||||
|
||||
// freeze thenables-collection to enforce sync-calls to
|
||||
// wait until and then wait for all thenables to resolve
|
||||
Object.freeze(thenables);
|
||||
await Promise.all(thenables).catch(e => onUnexpectedError(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class EventMultiplexer<T> implements IDisposable {
|
||||
|
||||
private readonly emitter: Emitter<T>;
|
||||
|
||||
@@ -28,7 +28,6 @@ export function toSlashes(osPath: string) {
|
||||
* or `getRoot('\\server\shares\path') === \\server\shares\`
|
||||
*/
|
||||
export function getRoot(path: string, sep: string = posix.sep): string {
|
||||
|
||||
if (!path) {
|
||||
return '';
|
||||
}
|
||||
|
||||
@@ -547,8 +547,8 @@ export namespace FuzzyScore {
|
||||
*/
|
||||
export const Default: FuzzyScore = ([-100, 0]);
|
||||
|
||||
export function isDefault(score?: FuzzyScore): score is [-100, 0, 0] {
|
||||
return !score || (score[0] === -100 && score[1] === 0 && score[2] === 0);
|
||||
export function isDefault(score?: FuzzyScore): score is [-100, 0] {
|
||||
return !score || (score.length === 2 && score[0] === -100 && score[1] === 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,28 +3,26 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { CSSIcon } from 'vs/base/common/codicons';
|
||||
import { matchesFuzzy, IMatch } from 'vs/base/common/filters';
|
||||
import { ltrim } from 'vs/base/common/strings';
|
||||
|
||||
export const iconStartMarker = '$(';
|
||||
|
||||
const escapeIconsRegex = /(\\)?\$\([a-z0-9\-]+?(?:~[a-z0-9\-]*?)?\)/gi;
|
||||
const iconsRegex = new RegExp(`\\$\\(${CSSIcon.iconNameExpression}(?:${CSSIcon.iconModifierExpression})?\\)`, 'g'); // no capturing groups
|
||||
|
||||
const escapeIconsRegex = new RegExp(`(\\\\)?${iconsRegex.source}`, 'g');
|
||||
export function escapeIcons(text: string): string {
|
||||
return text.replace(escapeIconsRegex, (match, escaped) => escaped ? match : `\\${match}`);
|
||||
}
|
||||
|
||||
const markdownEscapedIconsRegex = /\\\$\([a-z0-9\-]+?(?:~[a-z0-9\-]*?)?\)/gi;
|
||||
const markdownEscapedIconsRegex = new RegExp(`\\\\${iconsRegex.source}`, 'g');
|
||||
export function markdownEscapeEscapedIcons(text: string): string {
|
||||
// Need to add an extra \ for escaping in markdown
|
||||
return text.replace(markdownEscapedIconsRegex, match => `\\${match}`);
|
||||
}
|
||||
|
||||
const markdownUnescapeIconsRegex = /(\\)?\$\\\(([a-z0-9\-]+?(?:~[a-z0-9\-]*?)?)\\\)/gi;
|
||||
export function markdownUnescapeIcons(text: string): string {
|
||||
return text.replace(markdownUnescapeIconsRegex, (match, escaped, iconId) => escaped ? match : `$(${iconId})`);
|
||||
}
|
||||
|
||||
const stripIconsRegex = /(\s)?(\\)?\$\([a-z0-9\-]+?(?:~[a-z0-9\-]*?)?\)(\s)?/gi;
|
||||
const stripIconsRegex = new RegExp(`(\\s)?(\\\\)?${iconsRegex.source}(\\s)?`, 'g');
|
||||
export function stripIcons(text: string): string {
|
||||
if (text.indexOf(iconStartMarker) === -1) {
|
||||
return text;
|
||||
|
||||
@@ -39,6 +39,18 @@ export namespace Iterable {
|
||||
return false;
|
||||
}
|
||||
|
||||
export function find<T, R extends T>(iterable: Iterable<T>, predicate: (t: T) => t is R): T | undefined;
|
||||
export function find<T>(iterable: Iterable<T>, predicate: (t: T) => boolean): T | undefined;
|
||||
export function find<T>(iterable: Iterable<T>, predicate: (t: T) => boolean): T | undefined {
|
||||
for (const element of iterable) {
|
||||
if (predicate(element)) {
|
||||
return element;
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function filter<T, R extends T>(iterable: Iterable<T>, predicate: (t: T) => t is R): Iterable<R>;
|
||||
export function filter<T>(iterable: Iterable<T>, predicate: (t: T) => boolean): Iterable<T>;
|
||||
export function* filter<T>(iterable: Iterable<T>, predicate: (t: T) => boolean): Iterable<T> {
|
||||
@@ -71,22 +83,30 @@ export namespace Iterable {
|
||||
}
|
||||
}
|
||||
|
||||
export function reduce<T, R>(iterable: Iterable<T>, reducer: (previousValue: R, currentValue: T) => R, initialValue: R): R {
|
||||
let value = initialValue;
|
||||
for (const element of iterable) {
|
||||
value = reducer(value, element);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an iterable slice of the array, with the same semantics as `array.slice()`.
|
||||
*/
|
||||
export function* slice<T>(iterable: ReadonlyArray<T>, from: number, to = iterable.length): Iterable<T> {
|
||||
export function* slice<T>(arr: ReadonlyArray<T>, from: number, to = arr.length): Iterable<T> {
|
||||
if (from < 0) {
|
||||
from += iterable.length;
|
||||
from += arr.length;
|
||||
}
|
||||
|
||||
if (to < 0) {
|
||||
to += iterable.length;
|
||||
} else if (to > iterable.length) {
|
||||
to = iterable.length;
|
||||
to += arr.length;
|
||||
} else if (to > arr.length) {
|
||||
to = arr.length;
|
||||
}
|
||||
|
||||
for (; from < to; from++) {
|
||||
yield iterable[from];
|
||||
yield arr[from];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,4 +135,25 @@ export namespace Iterable {
|
||||
|
||||
return [consumed, { [Symbol.iterator]() { return iterator; } }];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the iterables are the same length and all items are
|
||||
* equal using the comparator function.
|
||||
*/
|
||||
export function equals<T>(a: Iterable<T>, b: Iterable<T>, comparator = (at: T, bt: T) => at === bt) {
|
||||
const ai = a[Symbol.iterator]();
|
||||
const bi = b[Symbol.iterator]();
|
||||
while (true) {
|
||||
const an = ai.next();
|
||||
const bn = bi.next();
|
||||
|
||||
if (an.done !== bn.done) {
|
||||
return false;
|
||||
} else if (an.done) {
|
||||
return true;
|
||||
} else if (!comparator(an.value, bn.value)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -597,6 +597,17 @@ export abstract class ResolvedKeybinding {
|
||||
|
||||
/**
|
||||
* Returns the parts that should be used for dispatching.
|
||||
* Returns null for parts consisting of only modifier keys
|
||||
* @example keybinding "Shift" -> null
|
||||
* @example keybinding ("D" with shift == true) -> "shift+D"
|
||||
*/
|
||||
public abstract getDispatchParts(): (string | null)[];
|
||||
|
||||
/**
|
||||
* Returns the parts that should be used for dispatching single modifier keys
|
||||
* Returns null for parts that contain more than one modifier or a regular key.
|
||||
* @example keybinding "Shift" -> "shift"
|
||||
* @example keybinding ("D" with shift == true") -> null
|
||||
*/
|
||||
public abstract getSingleModifierDispatchParts(): (string | null)[];
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ export class MultiDisposeError extends Error {
|
||||
constructor(
|
||||
public readonly errors: any[]
|
||||
) {
|
||||
super(`Encounter errors while disposing of store. Errors: [${errors.join(', ')}]`);
|
||||
super(`Encountered errors while disposing of store. Errors: [${errors.join(', ')}]`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -231,9 +231,7 @@ export class MutableDisposable<T extends IDisposable> implements IDisposable {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._value) {
|
||||
this._value.dispose();
|
||||
}
|
||||
this._value?.dispose();
|
||||
if (value) {
|
||||
markTracked(value);
|
||||
}
|
||||
@@ -247,9 +245,7 @@ export class MutableDisposable<T extends IDisposable> implements IDisposable {
|
||||
dispose(): void {
|
||||
this._isDisposed = true;
|
||||
markTracked(this);
|
||||
if (this._value) {
|
||||
this._value.dispose();
|
||||
}
|
||||
this._value?.dispose();
|
||||
this._value = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -64,6 +64,8 @@ export namespace Schemas {
|
||||
|
||||
export const vscodeSettings = 'vscode-settings';
|
||||
|
||||
export const vscodeWorkspaceTrust = 'vscode-workspace-trust';
|
||||
|
||||
export const webviewPanel = 'webview-panel';
|
||||
|
||||
/**
|
||||
|
||||
@@ -187,18 +187,3 @@ export function mapPager<T, R>(pager: IPager<T>, fn: (t: T) => R): IPager<R> {
|
||||
getPage: (pageIndex, token) => pager.getPage(pageIndex, token).then(r => r.map(fn))
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges two pagers.
|
||||
*/
|
||||
export function mergePagers<T>(one: IPager<T>, other: IPager<T>): IPager<T> {
|
||||
return {
|
||||
firstPage: [...one.firstPage, ...other.firstPage],
|
||||
total: one.total + other.total,
|
||||
pageSize: one.pageSize + other.pageSize,
|
||||
getPage(pageIndex: number, token): Promise<T[]> {
|
||||
return Promise.all([one.getPage(pageIndex, token), other.getPage(pageIndex, token)])
|
||||
.then(([onePage, otherPage]) => [...onePage, ...otherPage]);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -247,7 +247,7 @@ export const setImmediate: ISetImmediate = (function defineSetImmediate() {
|
||||
globals.postMessage({ vscodeSetImmediateId: myId }, '*');
|
||||
};
|
||||
}
|
||||
if (nodeProcess) {
|
||||
if (nodeProcess && typeof nodeProcess.nextTick === 'function') {
|
||||
return nodeProcess.nextTick.bind(nodeProcess);
|
||||
}
|
||||
const _promise = Promise.resolve();
|
||||
|
||||
@@ -42,8 +42,8 @@ export interface IExtUri {
|
||||
/**
|
||||
* Tests whether a `candidate` URI is a parent or equal of a given `base` URI.
|
||||
*
|
||||
* @param base A uri which is "longer"
|
||||
* @param parentCandidate A uri which is "shorter" then `base`
|
||||
* @param base A uri which is "longer" or at least same length as `parentCandidate`
|
||||
* @param parentCandidate A uri which is "shorter" or up to same length as `base`
|
||||
* @param ignoreFragment Ignore the fragment (defaults to `false`)
|
||||
*/
|
||||
isEqualOrParent(base: URI, parentCandidate: URI, ignoreFragment?: boolean): boolean;
|
||||
|
||||
@@ -16,6 +16,13 @@ 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;
|
||||
|
||||
@@ -268,7 +275,7 @@ class WriteableStreamImpl<T> implements WriteableStream<T> {
|
||||
// end with data or error if provided
|
||||
if (result instanceof Error) {
|
||||
this.error(result);
|
||||
} else if (result) {
|
||||
} else if (typeof result !== 'undefined') {
|
||||
this.write(result);
|
||||
}
|
||||
|
||||
@@ -489,22 +496,74 @@ export function peekReadable<T>(readable: Readable<T>, reducer: IReducer<T>, max
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to fully read a T stream into a T.
|
||||
* 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>(stream: ReadableStreamEvents<T>, reducer: IReducer<T>): Promise<T> {
|
||||
export function consumeStream<T>(stream: ReadableStreamEvents<T>, reducer: IReducer<T>): Promise<T>;
|
||||
export function consumeStream(stream: ReadableStreamEvents<unknown>): Promise<undefined>;
|
||||
export function consumeStream<T>(stream: ReadableStreamEvents<T>, reducer?: IReducer<T>): Promise<T | undefined> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const chunks: T[] = [];
|
||||
|
||||
stream.on('error', error => reject(error));
|
||||
stream.on('end', () => resolve(reducer(chunks)));
|
||||
|
||||
// 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 => chunks.push(data));
|
||||
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>): void {
|
||||
stream.on('error', error => listener.onError(error));
|
||||
stream.on('end', () => 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 => 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
|
||||
@@ -513,9 +572,9 @@ export function consumeStream<T>(stream: ReadableStreamEvents<T>, reducer: IRedu
|
||||
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 buffer: T[] = [];
|
||||
const dataListener = (chunk: T) => {
|
||||
|
||||
// Add to buffer
|
||||
@@ -533,23 +592,27 @@ export function peekStream<T>(stream: ReadableStream<T>, maxChunks: number): Pro
|
||||
}
|
||||
};
|
||||
|
||||
streamListeners.add(toDisposable(() => stream.removeListener('data', dataListener)));
|
||||
stream.on('data', dataListener);
|
||||
|
||||
// Error Listener
|
||||
const errorListener = (error: Error) => {
|
||||
return reject(error);
|
||||
};
|
||||
|
||||
streamListeners.add(toDisposable(() => stream.removeListener('error', errorListener)));
|
||||
stream.on('error', errorListener);
|
||||
|
||||
// End Listener
|
||||
const endListener = () => {
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -589,46 +652,11 @@ export function toReadable<T>(t: T): Readable<T> {
|
||||
export function transform<Original, Transformed>(stream: ReadableStreamEvents<Original>, transformer: ITransformer<Original, Transformed>, reducer: IReducer<Transformed>): ReadableStream<Transformed> {
|
||||
const target = newWriteableStream<Transformed>(reducer);
|
||||
|
||||
stream.on('data', data => target.write(transformer.data(data)));
|
||||
stream.on('end', () => target.end());
|
||||
stream.on('error', error => target.error(transformer.error ? transformer.error(error) : error));
|
||||
listenStream(stream, {
|
||||
onData: data => target.write(transformer.data(data)),
|
||||
onError: error => target.error(transformer.error ? transformer.error(error) : error),
|
||||
onEnd: () => target.end()
|
||||
});
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
export interface IReadableStreamObservable {
|
||||
|
||||
/**
|
||||
* A promise to await the `end` or `error` event
|
||||
* of a stream.
|
||||
*/
|
||||
errorOrEnd: () => Promise<void>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to observe a stream for certain events through
|
||||
* a promise based API.
|
||||
*/
|
||||
export function observe(stream: ReadableStream<unknown>): IReadableStreamObservable {
|
||||
|
||||
// A stream is closed when it ended or errord
|
||||
// We install this listener right from the
|
||||
// beginning to catch the events early.
|
||||
const errorOrEnd = Promise.race([
|
||||
new Promise<void>(resolve => stream.on('end', () => resolve())),
|
||||
new Promise<void>(resolve => stream.on('error', () => resolve()))
|
||||
]);
|
||||
|
||||
return {
|
||||
errorOrEnd(): Promise<void> {
|
||||
|
||||
// We need to ensure the stream is flowing so that our
|
||||
// listeners are getting triggered. It is possible that
|
||||
// the stream is not flowing because no `data` listener
|
||||
// was attached yet.
|
||||
stream.resume();
|
||||
|
||||
return errorOrEnd;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -8,22 +8,22 @@ import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
/**
|
||||
* @returns whether the provided parameter is a JavaScript Array or not.
|
||||
*/
|
||||
export function isArray<T>(array: T | {}): array is T extends readonly any[] ? (unknown extends T ? never : readonly any[]) : any[] {
|
||||
export function isArray(array: any): array is any[] {
|
||||
return Array.isArray(array);
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns whether the provided parameter is a JavaScript String or not.
|
||||
*/
|
||||
export function isString(str: any): str is string {
|
||||
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: any): value is string[] {
|
||||
return Array.isArray(value) && (<any[]>value).every(elem => isString(elem));
|
||||
export function isStringArray(value: unknown): value is string[] {
|
||||
return Array.isArray(value) && (<unknown[]>value).every(elem => isString(elem));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -31,7 +31,7 @@ export function isStringArray(value: any): value is string[] {
|
||||
* @returns whether the provided parameter is of type `object` but **not**
|
||||
* `null`, an `array`, a `regexp`, nor a `date`.
|
||||
*/
|
||||
export function isObject(obj: any): obj is Object {
|
||||
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.
|
||||
@@ -46,21 +46,21 @@ export function isObject(obj: any): obj is Object {
|
||||
* 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: any): obj is number {
|
||||
export function isNumber(obj: unknown): obj is number {
|
||||
return (typeof obj === 'number' && !isNaN(obj));
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns whether the provided parameter is a JavaScript Boolean or not.
|
||||
*/
|
||||
export function isBoolean(obj: any): obj is boolean {
|
||||
export function isBoolean(obj: unknown): obj is boolean {
|
||||
return (obj === true || obj === false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns whether the provided parameter is undefined.
|
||||
*/
|
||||
export function isUndefined(obj: any): obj is undefined {
|
||||
export function isUndefined(obj: unknown): obj is undefined {
|
||||
return (typeof obj === 'undefined');
|
||||
}
|
||||
|
||||
@@ -74,12 +74,12 @@ export function isDefined<T>(arg: T | null | undefined): arg is T {
|
||||
/**
|
||||
* @returns whether the provided parameter is undefined or null.
|
||||
*/
|
||||
export function isUndefinedOrNull(obj: any): obj is undefined | null {
|
||||
export function isUndefinedOrNull(obj: unknown): obj is undefined | null {
|
||||
return (isUndefined(obj) || obj === null);
|
||||
}
|
||||
|
||||
|
||||
export function assertType(condition: any, type?: string): asserts condition {
|
||||
export function assertType(condition: unknown, type?: string): asserts condition {
|
||||
if (!condition) {
|
||||
throw new Error(type ? `Unexpected type, expected '${type}'` : 'Unexpected type');
|
||||
}
|
||||
@@ -123,7 +123,7 @@ const hasOwnProperty = Object.prototype.hasOwnProperty;
|
||||
/**
|
||||
* @returns whether the provided parameter is an empty JavaScript Object or not.
|
||||
*/
|
||||
export function isEmptyObject(obj: any): obj is any {
|
||||
export function isEmptyObject(obj: unknown): obj is object {
|
||||
if (!isObject(obj)) {
|
||||
return false;
|
||||
}
|
||||
@@ -140,27 +140,27 @@ export function isEmptyObject(obj: any): obj is any {
|
||||
/**
|
||||
* @returns whether the provided parameter is a JavaScript Function or not.
|
||||
*/
|
||||
export function isFunction(obj: any): obj is Function {
|
||||
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: any[]): boolean {
|
||||
export function areFunctions(...objects: unknown[]): boolean {
|
||||
return objects.length > 0 && objects.every(isFunction);
|
||||
}
|
||||
|
||||
export type TypeConstraint = string | Function;
|
||||
|
||||
export function validateConstraints(args: any[], constraints: Array<TypeConstraint | undefined>): void {
|
||||
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: any, constraint: TypeConstraint | undefined): void {
|
||||
export function validateConstraint(arg: unknown, constraint: TypeConstraint | undefined): void {
|
||||
|
||||
if (isString(constraint)) {
|
||||
if (typeof arg !== constraint) {
|
||||
@@ -174,7 +174,7 @@ export function validateConstraint(arg: any, constraint: TypeConstraint | undefi
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
if (!isUndefinedOrNull(arg) && arg.constructor === constraint) {
|
||||
if (!isUndefinedOrNull(arg) && (arg as any).constructor === constraint) {
|
||||
return;
|
||||
}
|
||||
if (constraint.length === 1 && constraint.call(undefined, arg) === true) {
|
||||
@@ -204,8 +204,8 @@ export function getAllMethodNames(obj: object): string[] {
|
||||
return methods;
|
||||
}
|
||||
|
||||
export function createProxyObject<T extends object>(methodNames: string[], invoke: (method: string, args: any[]) => any): T {
|
||||
const createProxyMethod = (method: string): () => any => {
|
||||
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);
|
||||
@@ -242,7 +242,7 @@ export type AddFirstParameterToFunctions<Target, TargetFunctionsReturnType, Firs
|
||||
[K in keyof Target]:
|
||||
|
||||
// Function: add param to function
|
||||
Target[K] extends (...args: any) => TargetFunctionsReturnType ? (firstArg: FirstParameter, ...args: Parameters<Target[K]>) => ReturnType<Target[K]> :
|
||||
Target[K] extends (...args: any[]) => TargetFunctionsReturnType ? (firstArg: FirstParameter, ...args: Parameters<Target[K]>) => ReturnType<Target[K]> :
|
||||
|
||||
// Else: just leave as is
|
||||
Target[K]
|
||||
|
||||
@@ -19,13 +19,22 @@ for (let i = 0; i < 256; i++) {
|
||||
|
||||
// todo@jrieken
|
||||
// 1. node nodejs use`crypto#randomBytes`, see: https://nodejs.org/docs/latest/api/crypto.html#crypto_crypto_randombytes_size_callback
|
||||
// 2. use browser-crypto
|
||||
const _fillRandomValues = function (bucket: Uint8Array): Uint8Array {
|
||||
for (let i = 0; i < bucket.length; i++) {
|
||||
bucket[i] = Math.floor(Math.random() * 256);
|
||||
}
|
||||
return bucket;
|
||||
};
|
||||
let _fillRandomValues: (bucket: Uint8Array) => Uint8Array;
|
||||
|
||||
declare const crypto: undefined | { getRandomValues(data: Uint8Array): Uint8Array };
|
||||
|
||||
if (typeof crypto === 'object' && typeof crypto.getRandomValues === 'function') {
|
||||
// browser
|
||||
_fillRandomValues = crypto.getRandomValues.bind(crypto);
|
||||
|
||||
} else {
|
||||
_fillRandomValues = function (bucket: Uint8Array): Uint8Array {
|
||||
for (let i = 0; i < bucket.length; i++) {
|
||||
bucket[i] = Math.floor(Math.random() * 256);
|
||||
}
|
||||
return bucket;
|
||||
};
|
||||
}
|
||||
|
||||
export function generateUuid(): string {
|
||||
// get data
|
||||
|
||||
@@ -7,7 +7,6 @@ import * as fs from 'fs';
|
||||
import { rtrim } from 'vs/base/common/strings';
|
||||
import { sep, join, normalize, dirname, basename } from 'vs/base/common/path';
|
||||
import { readdirSync } from 'vs/base/node/pfs';
|
||||
import { promisify } from 'util';
|
||||
|
||||
/**
|
||||
* Copied from: https://github.com/microsoft/vscode-node-debug/blob/master/src/node/pathUtilities.ts#L83
|
||||
@@ -53,7 +52,7 @@ export function realcaseSync(path: string): string | null {
|
||||
|
||||
export async function realpath(path: string): Promise<string> {
|
||||
try {
|
||||
return await promisify(fs.realpath)(path);
|
||||
return await fs.promises.realpath(path);
|
||||
} catch (error) {
|
||||
|
||||
// We hit an error calling fs.realpath(). Since fs.realpath() is doing some path normalization
|
||||
@@ -63,7 +62,7 @@ export async function realpath(path: string): Promise<string> {
|
||||
// to not resolve links but to simply see if the path is read accessible or not.
|
||||
const normalizedPath = normalizePath(path);
|
||||
|
||||
await promisify(fs.access)(normalizedPath, fs.constants.R_OK);
|
||||
await fs.promises.access(normalizedPath, fs.constants.R_OK);
|
||||
|
||||
return normalizedPath;
|
||||
}
|
||||
|
||||
@@ -2,87 +2,83 @@
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
/// <reference path="../../../typings/require.d.ts" />
|
||||
|
||||
//@ts-check
|
||||
|
||||
/**
|
||||
* @param {NodeRequire} nodeRequire
|
||||
* @param {typeof import('path')} path
|
||||
* @param {typeof import('fs')} fs
|
||||
* @param {typeof import('../common/performance')} perf
|
||||
*/
|
||||
function factory(nodeRequire, path, fs, perf) {
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @param {string} file
|
||||
* @returns {Promise<boolean>}
|
||||
* @param {NodeRequire} nodeRequire
|
||||
* @param {typeof import('path')} path
|
||||
* @param {typeof import('fs')} fs
|
||||
* @param {typeof import('../common/performance')} perf
|
||||
*/
|
||||
function exists(file) {
|
||||
return new Promise(c => fs.exists(file, c));
|
||||
}
|
||||
function factory(nodeRequire, path, fs, perf) {
|
||||
|
||||
/**
|
||||
* @param {string} file
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
function touch(file) {
|
||||
return new Promise((c, e) => { const d = new Date(); fs.utimes(file, d, d, err => err ? e(err) : c()); });
|
||||
}
|
||||
/**
|
||||
* @param {string} file
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
function exists(file) {
|
||||
return new Promise(c => fs.exists(file, c));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} file
|
||||
* @returns {Promise<object>}
|
||||
*/
|
||||
function lstat(file) {
|
||||
return new Promise((c, e) => fs.lstat(file, (err, stats) => err ? e(err) : c(stats)));
|
||||
}
|
||||
/**
|
||||
* @param {string} file
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
function touch(file) {
|
||||
return new Promise((c, e) => { const d = new Date(); fs.utimes(file, d, d, err => err ? e(err) : c()); });
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} dir
|
||||
* @returns {Promise<string[]>}
|
||||
*/
|
||||
function readdir(dir) {
|
||||
return new Promise((c, e) => fs.readdir(dir, (err, files) => err ? e(err) : c(files)));
|
||||
}
|
||||
/**
|
||||
* @param {string} dir
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
function mkdirp(dir) {
|
||||
return new Promise((c, e) => fs.mkdir(dir, { recursive: true }, err => (err && err.code !== 'EEXIST') ? e(err) : c(dir)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} dir
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
function mkdirp(dir) {
|
||||
return new Promise((c, e) => fs.mkdir(dir, { recursive: true }, err => (err && err.code !== 'EEXIST') ? e(err) : c(dir)));
|
||||
}
|
||||
/**
|
||||
* @param {string} location
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
function rimraf(location) {
|
||||
return new Promise((c, e) => fs.rmdir(location, { recursive: true }, err => (err && err.code !== 'ENOENT') ? e(err) : c()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} dir
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
function rmdir(dir) {
|
||||
return new Promise((c, e) => fs.rmdir(dir, err => err ? e(err) : c(undefined)));
|
||||
}
|
||||
/**
|
||||
* @param {string} file
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
function readFile(file) {
|
||||
return new Promise((c, e) => fs.readFile(file, 'utf8', (err, data) => err ? e(err) : c(data)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} file
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
function unlink(file) {
|
||||
return new Promise((c, e) => fs.unlink(file, err => err ? e(err) : c(undefined)));
|
||||
}
|
||||
/**
|
||||
* @param {string} file
|
||||
* @param {string} content
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
function writeFile(file, content) {
|
||||
return new Promise((c, e) => fs.writeFile(file, content, 'utf8', err => err ? e(err) : c()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} location
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
function rimraf(location) {
|
||||
return lstat(location).then(stat => {
|
||||
if (stat.isDirectory() && !stat.isSymbolicLink()) {
|
||||
return readdir(location)
|
||||
.then(children => Promise.all(children.map(child => rimraf(path.join(location, child)))))
|
||||
.then(() => rmdir(location));
|
||||
} else {
|
||||
return unlink(location);
|
||||
/**
|
||||
* @param {string} userDataPath
|
||||
* @returns {object}
|
||||
*/
|
||||
function getLanguagePackConfigurations(userDataPath) {
|
||||
const configFile = path.join(userDataPath, 'languagepacks.json');
|
||||
try {
|
||||
return nodeRequire(configFile);
|
||||
} catch (err) {
|
||||
// Do nothing. If we can't read the file we have no
|
||||
// language pack config.
|
||||
}
|
||||
<<<<<<< HEAD
|
||||
}, err => {
|
||||
if (err.code === 'ENOENT') {
|
||||
return undefined;
|
||||
@@ -135,183 +131,184 @@ function factory(nodeRequire, path, fs, perf) {
|
||||
} catch (err) {
|
||||
// Do nothing. If we can't read the file we have no
|
||||
// language pack config.
|
||||
=======
|
||||
return undefined;
|
||||
>>>>>>> e8cd17a97d8c58fffcbac05394b3ee2b3c72d384
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {object} config
|
||||
* @param {string} locale
|
||||
*/
|
||||
function resolveLanguagePackLocale(config, locale) {
|
||||
try {
|
||||
while (locale) {
|
||||
if (config[locale]) {
|
||||
return locale;
|
||||
} else {
|
||||
const index = locale.lastIndexOf('-');
|
||||
if (index > 0) {
|
||||
locale = locale.substring(0, index);
|
||||
/**
|
||||
* @param {object} config
|
||||
* @param {string} locale
|
||||
*/
|
||||
function resolveLanguagePackLocale(config, locale) {
|
||||
try {
|
||||
while (locale) {
|
||||
if (config[locale]) {
|
||||
return locale;
|
||||
} else {
|
||||
return undefined;
|
||||
const index = locale.lastIndexOf('-');
|
||||
if (index > 0) {
|
||||
locale = locale.substring(0, index);
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Resolving language pack configuration failed.', err);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Resolving language pack configuration failed.', err);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} commit
|
||||
* @param {string} userDataPath
|
||||
* @param {string} metaDataFile
|
||||
* @param {string} locale
|
||||
*/
|
||||
function getNLSConfiguration(commit, userDataPath, metaDataFile, locale) {
|
||||
if (locale === 'pseudo') {
|
||||
return Promise.resolve({ locale: locale, availableLanguages: {}, pseudo: true });
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (process.env['VSCODE_DEV']) {
|
||||
return Promise.resolve({ locale: locale, availableLanguages: {} });
|
||||
}
|
||||
|
||||
// We have a built version so we have extracted nls file. Try to find
|
||||
// the right file to use.
|
||||
|
||||
// Check if we have an English or English US locale. If so fall to default since that is our
|
||||
// English translation (we don't ship *.nls.en.json files)
|
||||
if (locale && (locale === 'en' || locale === 'en-us')) {
|
||||
return Promise.resolve({ locale: locale, availableLanguages: {} });
|
||||
}
|
||||
|
||||
const initialLocale = locale;
|
||||
|
||||
perf.mark('code/willGenerateNls');
|
||||
|
||||
const defaultResult = function (locale) {
|
||||
perf.mark('code/didGenerateNls');
|
||||
return Promise.resolve({ locale: locale, availableLanguages: {} });
|
||||
};
|
||||
try {
|
||||
if (!commit) {
|
||||
return defaultResult(initialLocale);
|
||||
/**
|
||||
* @param {string} commit
|
||||
* @param {string} userDataPath
|
||||
* @param {string} metaDataFile
|
||||
* @param {string} locale
|
||||
*/
|
||||
function getNLSConfiguration(commit, userDataPath, metaDataFile, locale) {
|
||||
if (locale === 'pseudo') {
|
||||
return Promise.resolve({ locale: locale, availableLanguages: {}, pseudo: true });
|
||||
}
|
||||
const configs = getLanguagePackConfigurations(userDataPath);
|
||||
if (!configs) {
|
||||
return defaultResult(initialLocale);
|
||||
|
||||
if (process.env['VSCODE_DEV']) {
|
||||
return Promise.resolve({ locale: locale, availableLanguages: {} });
|
||||
}
|
||||
locale = resolveLanguagePackLocale(configs, locale);
|
||||
if (!locale) {
|
||||
return defaultResult(initialLocale);
|
||||
|
||||
// We have a built version so we have extracted nls file. Try to find
|
||||
// the right file to use.
|
||||
|
||||
// Check if we have an English or English US locale. If so fall to default since that is our
|
||||
// English translation (we don't ship *.nls.en.json files)
|
||||
if (locale && (locale === 'en' || locale === 'en-us')) {
|
||||
return Promise.resolve({ locale: locale, availableLanguages: {} });
|
||||
}
|
||||
const packConfig = configs[locale];
|
||||
let mainPack;
|
||||
if (!packConfig || typeof packConfig.hash !== 'string' || !packConfig.translations || typeof (mainPack = packConfig.translations['vscode']) !== 'string') {
|
||||
return defaultResult(initialLocale);
|
||||
}
|
||||
return exists(mainPack).then(fileExists => {
|
||||
if (!fileExists) {
|
||||
|
||||
const initialLocale = locale;
|
||||
|
||||
perf.mark('code/willGenerateNls');
|
||||
|
||||
const defaultResult = function (locale) {
|
||||
perf.mark('code/didGenerateNls');
|
||||
return Promise.resolve({ locale: locale, availableLanguages: {} });
|
||||
};
|
||||
try {
|
||||
if (!commit) {
|
||||
return defaultResult(initialLocale);
|
||||
}
|
||||
const packId = packConfig.hash + '.' + locale;
|
||||
const cacheRoot = path.join(userDataPath, 'clp', packId);
|
||||
const coreLocation = path.join(cacheRoot, commit);
|
||||
const translationsConfigFile = path.join(cacheRoot, 'tcf.json');
|
||||
const corruptedFile = path.join(cacheRoot, 'corrupted.info');
|
||||
const result = {
|
||||
locale: initialLocale,
|
||||
availableLanguages: { '*': locale },
|
||||
_languagePackId: packId,
|
||||
_translationsConfigFile: translationsConfigFile,
|
||||
_cacheRoot: cacheRoot,
|
||||
_resolvedLanguagePackCoreLocation: coreLocation,
|
||||
_corruptedFile: corruptedFile
|
||||
};
|
||||
return exists(corruptedFile).then(corrupted => {
|
||||
// The nls cache directory is corrupted.
|
||||
let toDelete;
|
||||
if (corrupted) {
|
||||
toDelete = rimraf(cacheRoot);
|
||||
} else {
|
||||
toDelete = Promise.resolve(undefined);
|
||||
const configs = getLanguagePackConfigurations(userDataPath);
|
||||
if (!configs) {
|
||||
return defaultResult(initialLocale);
|
||||
}
|
||||
locale = resolveLanguagePackLocale(configs, locale);
|
||||
if (!locale) {
|
||||
return defaultResult(initialLocale);
|
||||
}
|
||||
const packConfig = configs[locale];
|
||||
let mainPack;
|
||||
if (!packConfig || typeof packConfig.hash !== 'string' || !packConfig.translations || typeof (mainPack = packConfig.translations['vscode']) !== 'string') {
|
||||
return defaultResult(initialLocale);
|
||||
}
|
||||
return exists(mainPack).then(fileExists => {
|
||||
if (!fileExists) {
|
||||
return defaultResult(initialLocale);
|
||||
}
|
||||
return toDelete.then(() => {
|
||||
return exists(coreLocation).then(fileExists => {
|
||||
if (fileExists) {
|
||||
// We don't wait for this. No big harm if we can't touch
|
||||
touch(coreLocation).catch(() => { });
|
||||
perf.mark('code/didGenerateNls');
|
||||
return result;
|
||||
}
|
||||
return mkdirp(coreLocation).then(() => {
|
||||
return Promise.all([readFile(metaDataFile), readFile(mainPack)]);
|
||||
}).then(values => {
|
||||
const metadata = JSON.parse(values[0]);
|
||||
const packData = JSON.parse(values[1]).contents;
|
||||
const bundles = Object.keys(metadata.bundles);
|
||||
const writes = [];
|
||||
for (const bundle of bundles) {
|
||||
const modules = metadata.bundles[bundle];
|
||||
const target = Object.create(null);
|
||||
for (const module of modules) {
|
||||
const keys = metadata.keys[module];
|
||||
const defaultMessages = metadata.messages[module];
|
||||
const translations = packData[module];
|
||||
let targetStrings;
|
||||
if (translations) {
|
||||
targetStrings = [];
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const elem = keys[i];
|
||||
const key = typeof elem === 'string' ? elem : elem.key;
|
||||
let translatedMessage = translations[key];
|
||||
if (translatedMessage === undefined) {
|
||||
translatedMessage = defaultMessages[i];
|
||||
}
|
||||
targetStrings.push(translatedMessage);
|
||||
}
|
||||
} else {
|
||||
targetStrings = defaultMessages;
|
||||
}
|
||||
target[module] = targetStrings;
|
||||
}
|
||||
writes.push(writeFile(path.join(coreLocation, bundle.replace(/\//g, '!') + '.nls.json'), JSON.stringify(target)));
|
||||
const packId = packConfig.hash + '.' + locale;
|
||||
const cacheRoot = path.join(userDataPath, 'clp', packId);
|
||||
const coreLocation = path.join(cacheRoot, commit);
|
||||
const translationsConfigFile = path.join(cacheRoot, 'tcf.json');
|
||||
const corruptedFile = path.join(cacheRoot, 'corrupted.info');
|
||||
const result = {
|
||||
locale: initialLocale,
|
||||
availableLanguages: { '*': locale },
|
||||
_languagePackId: packId,
|
||||
_translationsConfigFile: translationsConfigFile,
|
||||
_cacheRoot: cacheRoot,
|
||||
_resolvedLanguagePackCoreLocation: coreLocation,
|
||||
_corruptedFile: corruptedFile
|
||||
};
|
||||
return exists(corruptedFile).then(corrupted => {
|
||||
// The nls cache directory is corrupted.
|
||||
let toDelete;
|
||||
if (corrupted) {
|
||||
toDelete = rimraf(cacheRoot);
|
||||
} else {
|
||||
toDelete = Promise.resolve(undefined);
|
||||
}
|
||||
return toDelete.then(() => {
|
||||
return exists(coreLocation).then(fileExists => {
|
||||
if (fileExists) {
|
||||
// We don't wait for this. No big harm if we can't touch
|
||||
touch(coreLocation).catch(() => { });
|
||||
perf.mark('code/didGenerateNls');
|
||||
return result;
|
||||
}
|
||||
writes.push(writeFile(translationsConfigFile, JSON.stringify(packConfig.translations)));
|
||||
return Promise.all(writes);
|
||||
}).then(() => {
|
||||
perf.mark('code/didGenerateNls');
|
||||
return result;
|
||||
}).catch(err => {
|
||||
console.error('Generating translation files failed.', err);
|
||||
return defaultResult(locale);
|
||||
return mkdirp(coreLocation).then(() => {
|
||||
return Promise.all([readFile(metaDataFile), readFile(mainPack)]);
|
||||
}).then(values => {
|
||||
const metadata = JSON.parse(values[0]);
|
||||
const packData = JSON.parse(values[1]).contents;
|
||||
const bundles = Object.keys(metadata.bundles);
|
||||
const writes = [];
|
||||
for (const bundle of bundles) {
|
||||
const modules = metadata.bundles[bundle];
|
||||
const target = Object.create(null);
|
||||
for (const module of modules) {
|
||||
const keys = metadata.keys[module];
|
||||
const defaultMessages = metadata.messages[module];
|
||||
const translations = packData[module];
|
||||
let targetStrings;
|
||||
if (translations) {
|
||||
targetStrings = [];
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const elem = keys[i];
|
||||
const key = typeof elem === 'string' ? elem : elem.key;
|
||||
let translatedMessage = translations[key];
|
||||
if (translatedMessage === undefined) {
|
||||
translatedMessage = defaultMessages[i];
|
||||
}
|
||||
targetStrings.push(translatedMessage);
|
||||
}
|
||||
} else {
|
||||
targetStrings = defaultMessages;
|
||||
}
|
||||
target[module] = targetStrings;
|
||||
}
|
||||
writes.push(writeFile(path.join(coreLocation, bundle.replace(/\//g, '!') + '.nls.json'), JSON.stringify(target)));
|
||||
}
|
||||
writes.push(writeFile(translationsConfigFile, JSON.stringify(packConfig.translations)));
|
||||
return Promise.all(writes);
|
||||
}).then(() => {
|
||||
perf.mark('code/didGenerateNls');
|
||||
return result;
|
||||
}).catch(err => {
|
||||
console.error('Generating translation files failed.', err);
|
||||
return defaultResult(locale);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
} catch (err) {
|
||||
console.error('Generating translation files failed.', err);
|
||||
return defaultResult(locale);
|
||||
} catch (err) {
|
||||
console.error('Generating translation files failed.', err);
|
||||
return defaultResult(locale);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
getNLSConfiguration
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
getNLSConfiguration
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
if (typeof define === 'function') {
|
||||
// amd
|
||||
define(['path', 'fs', 'vs/base/common/performance'], function (path, fs, perf) { return factory(require.__$__nodeRequire, path, fs, perf); });
|
||||
} else if (typeof module === 'object' && typeof module.exports === 'object') {
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const perf = require('../common/performance');
|
||||
module.exports = factory(require, path, fs, perf);
|
||||
} else {
|
||||
throw new Error('Unknown context');
|
||||
}
|
||||
if (typeof define === 'function') {
|
||||
// amd
|
||||
define(['require', 'path', 'fs', 'vs/base/common/performance'], function (require, /** @type {typeof import('path')} */ path, /** @type {typeof import('fs')} */ fs, /** @type {typeof import('../common/performance')} */ perf) { return factory(require.__$__nodeRequire, path, fs, perf); });
|
||||
} else if (typeof module === 'object' && typeof module.exports === 'object') {
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const perf = require('../common/performance');
|
||||
module.exports = factory(require, path, fs, perf);
|
||||
} else {
|
||||
throw new Error('Unknown context');
|
||||
}
|
||||
}());
|
||||
|
||||
@@ -7,13 +7,14 @@ import * as fs from 'fs';
|
||||
import { tmpdir } from 'os';
|
||||
import { join } from 'vs/base/common/path';
|
||||
import { Queue } from 'vs/base/common/async';
|
||||
import { isMacintosh, isWindows } from 'vs/base/common/platform';
|
||||
import { isLinux, isMacintosh, isWindows } from 'vs/base/common/platform';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { promisify } from 'util';
|
||||
import { isRootOrDriveLetter } from 'vs/base/common/extpath';
|
||||
import { isEqualOrParent, isRootOrDriveLetter } from 'vs/base/common/extpath';
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
import { normalizeNFC } from 'vs/base/common/normalization';
|
||||
|
||||
//#region Constants
|
||||
|
||||
// See https://github.com/microsoft/vscode/issues/30180
|
||||
const WIN32_MAX_FILE_SIZE = 300 * 1024 * 1024; // 300 MB
|
||||
const GENERAL_MAX_FILE_SIZE = 16 * 1024 * 1024 * 1024; // 16 GB
|
||||
@@ -25,6 +26,10 @@ const GENERAL_MAX_HEAP_SIZE = 700 * 2 * 1024 * 1024; // 1400 MB
|
||||
export const MAX_FILE_SIZE = process.arch === 'ia32' ? WIN32_MAX_FILE_SIZE : GENERAL_MAX_FILE_SIZE;
|
||||
export const MAX_HEAP_SIZE = process.arch === 'ia32' ? WIN32_MAX_HEAP_SIZE : GENERAL_MAX_HEAP_SIZE;
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region rimraf
|
||||
|
||||
export enum RimRafMode {
|
||||
|
||||
/**
|
||||
@@ -40,12 +45,19 @@ export enum RimRafMode {
|
||||
MOVE
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to delete the provied path (either file or folder) recursively
|
||||
* with the options:
|
||||
* - `UNLINK`: direct removal from disk
|
||||
* - `MOVE`: faster variant that first moves the target to temp dir and then
|
||||
* deletes it in the background without waiting for that to finish.
|
||||
*/
|
||||
export async function rimraf(path: string, mode = RimRafMode.UNLINK): Promise<void> {
|
||||
if (isRootOrDriveLetter(path)) {
|
||||
throw new Error('rimraf - will refuse to recursively delete root');
|
||||
}
|
||||
|
||||
// delete: via unlink
|
||||
// delete: via rmDir
|
||||
if (mode === RimRafMode.UNLINK) {
|
||||
return rimrafUnlink(path);
|
||||
}
|
||||
@@ -54,32 +66,17 @@ export async function rimraf(path: string, mode = RimRafMode.UNLINK): Promise<vo
|
||||
return rimrafMove(path);
|
||||
}
|
||||
|
||||
async function rimrafUnlink(path: string): Promise<void> {
|
||||
async function rimrafMove(path: string): Promise<void> {
|
||||
try {
|
||||
const stat = await lstat(path);
|
||||
|
||||
// Folder delete (recursive) - NOT for symbolic links though!
|
||||
if (stat.isDirectory() && !stat.isSymbolicLink()) {
|
||||
|
||||
// Children
|
||||
const children = await readdir(path);
|
||||
await Promise.all(children.map(child => rimrafUnlink(join(path, child))));
|
||||
|
||||
// Folder
|
||||
await promisify(fs.rmdir)(path);
|
||||
const pathInTemp = join(tmpdir(), generateUuid());
|
||||
try {
|
||||
await fs.promises.rename(path, pathInTemp);
|
||||
} catch (error) {
|
||||
return rimrafUnlink(path); // if rename fails, delete without tmp dir
|
||||
}
|
||||
|
||||
// Single file delete
|
||||
else {
|
||||
|
||||
// chmod as needed to allow for unlink
|
||||
const mode = stat.mode;
|
||||
if (!(mode & fs.constants.S_IWUSR)) {
|
||||
await chmod(path, mode | fs.constants.S_IWUSR);
|
||||
}
|
||||
|
||||
return unlink(path);
|
||||
}
|
||||
// Delete but do not return as promise
|
||||
rimrafUnlink(pathInTemp).catch(error => {/* ignore */ });
|
||||
} catch (error) {
|
||||
if (error.code !== 'ENOENT') {
|
||||
throw error;
|
||||
@@ -87,22 +84,8 @@ async function rimrafUnlink(path: string): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
async function rimrafMove(path: string): Promise<void> {
|
||||
try {
|
||||
const pathInTemp = join(tmpdir(), generateUuid());
|
||||
try {
|
||||
await rename(path, pathInTemp);
|
||||
} catch (error) {
|
||||
return rimrafUnlink(path); // if rename fails, delete without tmp dir
|
||||
}
|
||||
|
||||
// Delete but do not return as promise
|
||||
rimrafUnlink(pathInTemp);
|
||||
} catch (error) {
|
||||
if (error.code !== 'ENOENT') {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
async function rimrafUnlink(path: string): Promise<void> {
|
||||
return fs.promises.rmdir(path, { recursive: true, maxRetries: 3 });
|
||||
}
|
||||
|
||||
export function rimrafSync(path: string): void {
|
||||
@@ -110,193 +93,281 @@ export function rimrafSync(path: string): void {
|
||||
throw new Error('rimraf - will refuse to recursively delete root');
|
||||
}
|
||||
|
||||
fs.rmdirSync(path, { recursive: true });
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region readdir with NFC support (macos)
|
||||
|
||||
export interface IDirent {
|
||||
name: string;
|
||||
|
||||
isFile(): boolean;
|
||||
isDirectory(): boolean;
|
||||
isSymbolicLink(): boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop-in replacement of `fs.readdir` with support
|
||||
* for converting from macOS NFD unicon form to NFC
|
||||
* (https://github.com/nodejs/node/issues/2165)
|
||||
*/
|
||||
export async function readdir(path: string): Promise<string[]>;
|
||||
export async function readdir(path: string, options: { withFileTypes: true }): Promise<IDirent[]>;
|
||||
export async function readdir(path: string, options?: { withFileTypes: true }): Promise<(string | IDirent)[]> {
|
||||
return handleDirectoryChildren(await (options ? safeReaddirWithFileTypes(path) : fs.promises.readdir(path)));
|
||||
}
|
||||
|
||||
async function safeReaddirWithFileTypes(path: string): Promise<IDirent[]> {
|
||||
try {
|
||||
const stat = fs.lstatSync(path);
|
||||
|
||||
// Folder delete (recursive) - NOT for symbolic links though!
|
||||
if (stat.isDirectory() && !stat.isSymbolicLink()) {
|
||||
|
||||
// Children
|
||||
const children = readdirSync(path);
|
||||
children.map(child => rimrafSync(join(path, child)));
|
||||
|
||||
// Folder
|
||||
fs.rmdirSync(path);
|
||||
}
|
||||
|
||||
// Single file delete
|
||||
else {
|
||||
|
||||
// chmod as needed to allow for unlink
|
||||
const mode = stat.mode;
|
||||
if (!(mode & fs.constants.S_IWUSR)) {
|
||||
fs.chmodSync(path, mode | fs.constants.S_IWUSR);
|
||||
}
|
||||
|
||||
return fs.unlinkSync(path);
|
||||
}
|
||||
return await fs.promises.readdir(path, { withFileTypes: true });
|
||||
} catch (error) {
|
||||
if (error.code !== 'ENOENT') {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function readdir(path: string): Promise<string[]> {
|
||||
return handleDirectoryChildren(await promisify(fs.readdir)(path));
|
||||
}
|
||||
|
||||
export async function readdirWithFileTypes(path: string): Promise<fs.Dirent[]> {
|
||||
const children = await promisify(fs.readdir)(path, { withFileTypes: true });
|
||||
|
||||
// Mac: uses NFD unicode form on disk, but we want NFC
|
||||
// See also https://github.com/nodejs/node/issues/2165
|
||||
if (isMacintosh) {
|
||||
for (const child of children) {
|
||||
child.name = normalizeNFC(child.name);
|
||||
}
|
||||
console.warn('[node.js fs] readdir with filetypes failed with error: ', error);
|
||||
}
|
||||
|
||||
return children;
|
||||
// Fallback to manually reading and resolving each
|
||||
// children of the folder in case we hit an error
|
||||
// previously.
|
||||
// This can only really happen on exotic file systems
|
||||
// such as explained in #115645 where we get entries
|
||||
// from `readdir` that we can later not `lstat`.
|
||||
const result: IDirent[] = [];
|
||||
const children = await readdir(path);
|
||||
for (const child of children) {
|
||||
let isFile = false;
|
||||
let isDirectory = false;
|
||||
let isSymbolicLink = false;
|
||||
|
||||
try {
|
||||
const lstat = await fs.promises.lstat(join(path, child));
|
||||
|
||||
isFile = lstat.isFile();
|
||||
isDirectory = lstat.isDirectory();
|
||||
isSymbolicLink = lstat.isSymbolicLink();
|
||||
} catch (error) {
|
||||
console.warn('[node.js fs] unexpected error from lstat after readdir: ', error);
|
||||
}
|
||||
|
||||
result.push({
|
||||
name: child,
|
||||
isFile: () => isFile,
|
||||
isDirectory: () => isDirectory,
|
||||
isSymbolicLink: () => isSymbolicLink
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop-in replacement of `fs.readdirSync` with support
|
||||
* for converting from macOS NFD unicon form to NFC
|
||||
* (https://github.com/nodejs/node/issues/2165)
|
||||
*/
|
||||
export function readdirSync(path: string): string[] {
|
||||
return handleDirectoryChildren(fs.readdirSync(path));
|
||||
}
|
||||
|
||||
function handleDirectoryChildren(children: string[]): string[] {
|
||||
// Mac: uses NFD unicode form on disk, but we want NFC
|
||||
// See also https://github.com/nodejs/node/issues/2165
|
||||
if (isMacintosh) {
|
||||
return children.map(child => normalizeNFC(child));
|
||||
}
|
||||
function handleDirectoryChildren(children: string[]): string[];
|
||||
function handleDirectoryChildren(children: IDirent[]): IDirent[];
|
||||
function handleDirectoryChildren(children: (string | IDirent)[]): (string | IDirent)[];
|
||||
function handleDirectoryChildren(children: (string | IDirent)[]): (string | IDirent)[] {
|
||||
return children.map(child => {
|
||||
|
||||
return children;
|
||||
}
|
||||
// Mac: uses NFD unicode form on disk, but we want NFC
|
||||
// See also https://github.com/nodejs/node/issues/2165
|
||||
|
||||
export function exists(path: string): Promise<boolean> {
|
||||
return promisify(fs.exists)(path);
|
||||
}
|
||||
|
||||
export function chmod(path: string, mode: number): Promise<void> {
|
||||
return promisify(fs.chmod)(path, mode);
|
||||
}
|
||||
|
||||
export function stat(path: string): Promise<fs.Stats> {
|
||||
return promisify(fs.stat)(path);
|
||||
}
|
||||
|
||||
export interface IStatAndLink {
|
||||
|
||||
// The stats of the file. If the file is a symbolic
|
||||
// link, the stats will be of that target file and
|
||||
// not the link itself.
|
||||
// If the file is a symbolic link pointing to a non
|
||||
// existing file, the stat will be of the link and
|
||||
// the `dangling` flag will indicate this.
|
||||
stat: fs.Stats;
|
||||
|
||||
// Will be provided if the resource is a symbolic link
|
||||
// on disk. Use the `dangling` flag to find out if it
|
||||
// points to a resource that does not exist on disk.
|
||||
symbolicLink?: { dangling: boolean };
|
||||
}
|
||||
|
||||
export async function statLink(path: string): Promise<IStatAndLink> {
|
||||
|
||||
// First stat the link
|
||||
let lstats: fs.Stats | undefined;
|
||||
try {
|
||||
lstats = await lstat(path);
|
||||
|
||||
// Return early if the stat is not a symbolic link at all
|
||||
if (!lstats.isSymbolicLink()) {
|
||||
return { stat: lstats };
|
||||
}
|
||||
} catch (error) {
|
||||
/* ignore - use stat() instead */
|
||||
}
|
||||
|
||||
// If the stat is a symbolic link or failed to stat, use fs.stat()
|
||||
// which for symbolic links will stat the target they point to
|
||||
try {
|
||||
const stats = await stat(path);
|
||||
|
||||
return { stat: stats, symbolicLink: lstats?.isSymbolicLink() ? { dangling: false } : undefined };
|
||||
} catch (error) {
|
||||
|
||||
// If the link points to a non-existing file we still want
|
||||
// to return it as result while setting dangling: true flag
|
||||
if (error.code === 'ENOENT' && lstats) {
|
||||
return { stat: lstats, symbolicLink: { dangling: true } };
|
||||
if (typeof child === 'string') {
|
||||
return isMacintosh ? normalizeNFC(child) : child;
|
||||
}
|
||||
|
||||
// Windows: workaround a node.js bug where reparse points
|
||||
// are not supported (https://github.com/nodejs/node/issues/36790)
|
||||
if (isWindows && error.code === 'EACCES' && lstats) {
|
||||
try {
|
||||
const stats = await stat(await readlink(path));
|
||||
child.name = isMacintosh ? normalizeNFC(child.name) : child.name;
|
||||
|
||||
return { stat: stats, symbolicLink: lstats.isSymbolicLink() ? { dangling: false } : undefined };
|
||||
} catch (error) {
|
||||
return child;
|
||||
});
|
||||
}
|
||||
|
||||
// If the link points to a non-existing file we still want
|
||||
// to return it as result while setting dangling: true flag
|
||||
if (error.code === 'ENOENT') {
|
||||
return { stat: lstats, symbolicLink: { dangling: true } };
|
||||
}
|
||||
/**
|
||||
* A convinience method to read all children of a path that
|
||||
* are directories.
|
||||
*/
|
||||
export async function readDirsInDir(dirPath: string): Promise<string[]> {
|
||||
const children = await readdir(dirPath);
|
||||
const directories: string[] = [];
|
||||
|
||||
throw error;
|
||||
for (const child of children) {
|
||||
if (await SymlinkSupport.existsDirectory(join(dirPath, child))) {
|
||||
directories.push(child);
|
||||
}
|
||||
}
|
||||
|
||||
return directories;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region whenDeleted()
|
||||
|
||||
/**
|
||||
* A `Promise` that resolves when the provided `path`
|
||||
* is deleted from disk.
|
||||
*/
|
||||
export function whenDeleted(path: string, intervalMs = 1000): Promise<void> {
|
||||
return new Promise<void>(resolve => {
|
||||
let running = false;
|
||||
const interval = setInterval(() => {
|
||||
if (!running) {
|
||||
running = true;
|
||||
fs.access(path, err => {
|
||||
running = false;
|
||||
|
||||
if (err) {
|
||||
clearInterval(interval);
|
||||
resolve(undefined);
|
||||
}
|
||||
});
|
||||
}
|
||||
}, intervalMs);
|
||||
});
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region Methods with symbolic links support
|
||||
|
||||
export namespace SymlinkSupport {
|
||||
|
||||
export interface IStats {
|
||||
|
||||
// The stats of the file. If the file is a symbolic
|
||||
// link, the stats will be of that target file and
|
||||
// not the link itself.
|
||||
// If the file is a symbolic link pointing to a non
|
||||
// existing file, the stat will be of the link and
|
||||
// the `dangling` flag will indicate this.
|
||||
stat: fs.Stats;
|
||||
|
||||
// Will be provided if the resource is a symbolic link
|
||||
// on disk. Use the `dangling` flag to find out if it
|
||||
// points to a resource that does not exist on disk.
|
||||
symbolicLink?: { dangling: boolean };
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the `fs.Stats` of the provided path. If the path is a
|
||||
* symbolic link, the `fs.Stats` will be from the target it points
|
||||
* to. If the target does not exist, `dangling: true` will be returned
|
||||
* as `symbolicLink` value.
|
||||
*/
|
||||
export async function stat(path: string): Promise<IStats> {
|
||||
|
||||
// First stat the link
|
||||
let lstats: fs.Stats | undefined;
|
||||
try {
|
||||
lstats = await fs.promises.lstat(path);
|
||||
|
||||
// Return early if the stat is not a symbolic link at all
|
||||
if (!lstats.isSymbolicLink()) {
|
||||
return { stat: lstats };
|
||||
}
|
||||
} catch (error) {
|
||||
/* ignore - use stat() instead */
|
||||
}
|
||||
|
||||
throw error;
|
||||
// If the stat is a symbolic link or failed to stat, use fs.stat()
|
||||
// which for symbolic links will stat the target they point to
|
||||
try {
|
||||
const stats = await fs.promises.stat(path);
|
||||
|
||||
return { stat: stats, symbolicLink: lstats?.isSymbolicLink() ? { dangling: false } : undefined };
|
||||
} catch (error) {
|
||||
|
||||
// If the link points to a non-existing file we still want
|
||||
// to return it as result while setting dangling: true flag
|
||||
if (error.code === 'ENOENT' && lstats) {
|
||||
return { stat: lstats, symbolicLink: { dangling: true } };
|
||||
}
|
||||
|
||||
// Windows: workaround a node.js bug where reparse points
|
||||
// are not supported (https://github.com/nodejs/node/issues/36790)
|
||||
if (isWindows && error.code === 'EACCES') {
|
||||
try {
|
||||
const stats = await fs.promises.stat(await fs.promises.readlink(path));
|
||||
|
||||
return { stat: stats, symbolicLink: { dangling: false } };
|
||||
} catch (error) {
|
||||
|
||||
// If the link points to a non-existing file we still want
|
||||
// to return it as result while setting dangling: true flag
|
||||
if (error.code === 'ENOENT' && lstats) {
|
||||
return { stat: lstats, symbolicLink: { dangling: true } };
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Figures out if the `path` exists and is a file with support
|
||||
* for symlinks.
|
||||
*
|
||||
* Note: this will return `false` for a symlink that exists on
|
||||
* disk but is dangling (pointing to a non-existing path).
|
||||
*
|
||||
* Use `exists` if you only care about the path existing on disk
|
||||
* or not without support for symbolic links.
|
||||
*/
|
||||
export async function existsFile(path: string): Promise<boolean> {
|
||||
try {
|
||||
const { stat, symbolicLink } = await SymlinkSupport.stat(path);
|
||||
|
||||
return stat.isFile() && symbolicLink?.dangling !== true;
|
||||
} catch (error) {
|
||||
// Ignore, path might not exist
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Figures out if the `path` exists and is a directory with support for
|
||||
* symlinks.
|
||||
*
|
||||
* Note: this will return `false` for a symlink that exists on
|
||||
* disk but is dangling (pointing to a non-existing path).
|
||||
*
|
||||
* Use `exists` if you only care about the path existing on disk
|
||||
* or not without support for symbolic links.
|
||||
*/
|
||||
export async function existsDirectory(path: string): Promise<boolean> {
|
||||
try {
|
||||
const { stat, symbolicLink } = await SymlinkSupport.stat(path);
|
||||
|
||||
return stat.isDirectory() && symbolicLink?.dangling !== true;
|
||||
} catch (error) {
|
||||
// Ignore, path might not exist
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export function lstat(path: string): Promise<fs.Stats> {
|
||||
return promisify(fs.lstat)(path);
|
||||
}
|
||||
//#endregion
|
||||
|
||||
export function rename(oldPath: string, newPath: string): Promise<void> {
|
||||
return promisify(fs.rename)(oldPath, newPath);
|
||||
}
|
||||
|
||||
export function renameIgnoreError(oldPath: string, newPath: string): Promise<void> {
|
||||
return new Promise(resolve => fs.rename(oldPath, newPath, () => resolve()));
|
||||
}
|
||||
|
||||
export function readlink(path: string): Promise<string> {
|
||||
return promisify(fs.readlink)(path);
|
||||
}
|
||||
|
||||
export function unlink(path: string): Promise<void> {
|
||||
return promisify(fs.unlink)(path);
|
||||
}
|
||||
|
||||
export function symlink(target: string, path: string, type?: string): Promise<void> {
|
||||
return promisify(fs.symlink)(target, path, type);
|
||||
}
|
||||
|
||||
export function truncate(path: string, len: number): Promise<void> {
|
||||
return promisify(fs.truncate)(path, len);
|
||||
}
|
||||
|
||||
export function readFile(path: string): Promise<Buffer>;
|
||||
export function readFile(path: string, encoding: string): Promise<string>;
|
||||
export function readFile(path: string, encoding?: string): Promise<Buffer | string> {
|
||||
return promisify(fs.readFile)(path, encoding);
|
||||
}
|
||||
|
||||
export async function mkdirp(path: string, mode?: number): Promise<void> {
|
||||
return promisify(fs.mkdir)(path, { mode, recursive: true });
|
||||
}
|
||||
|
||||
// According to node.js docs (https://nodejs.org/docs/v6.5.0/api/fs.html#fs_fs_writefile_file_data_options_callback)
|
||||
// it is not safe to call writeFile() on the same path multiple times without waiting for the callback to return.
|
||||
// Therefor we use a Queue on the path that is given to us to sequentialize calls to the same path properly.
|
||||
const writeFilePathQueues: Map<string, Queue<void>> = new Map();
|
||||
//#region Write File
|
||||
|
||||
/**
|
||||
* Same as `fs.writeFile` but with an additional call to
|
||||
* `fs.fdatasync` after writing to ensure changes are
|
||||
* flushed to disk.
|
||||
*
|
||||
* In addition, multiple writes to the same path are queued.
|
||||
*/
|
||||
export function writeFile(path: string, data: string, options?: IWriteFileOptions): Promise<void>;
|
||||
export function writeFile(path: string, data: Buffer, options?: IWriteFileOptions): Promise<void>;
|
||||
export function writeFile(path: string, data: Uint8Array, options?: IWriteFileOptions): Promise<void>;
|
||||
@@ -311,6 +382,11 @@ export function writeFile(path: string, data: string | Buffer | Uint8Array, opti
|
||||
});
|
||||
}
|
||||
|
||||
// According to node.js docs (https://nodejs.org/docs/v6.5.0/api/fs.html#fs_fs_writefile_file_data_options_callback)
|
||||
// it is not safe to call writeFile() on the same path multiple times without waiting for the callback to return.
|
||||
// Therefor we use a Queue on the path that is given to us to sequentialize calls to the same path properly.
|
||||
const writeFilePathQueues: Map<string, Queue<void>> = new Map();
|
||||
|
||||
function toQueueKey(path: string): string {
|
||||
let queueKey = path;
|
||||
if (isWindows || isMacintosh) {
|
||||
@@ -388,6 +464,11 @@ function doWriteFileAndFlush(path: string, data: string | Buffer | Uint8Array, o
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as `fs.writeFileSync` but with an additional call to
|
||||
* `fs.fdatasyncSync` after writing to ensure changes are
|
||||
* flushed to disk.
|
||||
*/
|
||||
export function writeFileSync(path: string, data: string | Buffer, options?: IWriteFileOptions): void {
|
||||
const ensuredOptions = ensureWriteOptions(options);
|
||||
|
||||
@@ -426,87 +507,48 @@ function ensureWriteOptions(options?: IWriteFileOptions): IEnsuredWriteFileOptio
|
||||
};
|
||||
}
|
||||
|
||||
export async function readDirsInDir(dirPath: string): Promise<string[]> {
|
||||
const children = await readdir(dirPath);
|
||||
const directories: string[] = [];
|
||||
//#endregion
|
||||
|
||||
for (const child of children) {
|
||||
if (await dirExists(join(dirPath, child))) {
|
||||
directories.push(child);
|
||||
}
|
||||
}
|
||||
|
||||
return directories;
|
||||
}
|
||||
|
||||
export async function dirExists(path: string): Promise<boolean> {
|
||||
try {
|
||||
const { stat, symbolicLink } = await statLink(path);
|
||||
|
||||
return stat.isDirectory() && symbolicLink?.dangling !== true;
|
||||
} catch (error) {
|
||||
// Ignore, path might not exist
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
export async function fileExists(path: string): Promise<boolean> {
|
||||
try {
|
||||
const { stat, symbolicLink } = await statLink(path);
|
||||
|
||||
return stat.isFile() && symbolicLink?.dangling !== true;
|
||||
} catch (error) {
|
||||
// Ignore, path might not exist
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
export function whenDeleted(path: string): Promise<void> {
|
||||
|
||||
// Complete when wait marker file is deleted
|
||||
return new Promise<void>(resolve => {
|
||||
let running = false;
|
||||
const interval = setInterval(() => {
|
||||
if (!running) {
|
||||
running = true;
|
||||
fs.exists(path, exists => {
|
||||
running = false;
|
||||
|
||||
if (!exists) {
|
||||
clearInterval(interval);
|
||||
resolve(undefined);
|
||||
}
|
||||
});
|
||||
}
|
||||
}, 1000);
|
||||
});
|
||||
}
|
||||
//#region Move / Copy
|
||||
|
||||
/**
|
||||
* A drop-in replacement for `fs.rename` that:
|
||||
* - updates the `mtime` of the `source` after the operation
|
||||
* - allows to move across multiple disks
|
||||
*/
|
||||
export async function move(source: string, target: string): Promise<void> {
|
||||
if (source === target) {
|
||||
return;
|
||||
return; // simulate node.js behaviour here and do a no-op if paths match
|
||||
}
|
||||
|
||||
// We have been updating `mtime` for move operations for files since the
|
||||
// beginning for reasons that are no longer quite clear, but changing
|
||||
// this could be risky as well. As such, trying to reason about it:
|
||||
// It is very common as developer to have file watchers enabled that watch
|
||||
// the current workspace for changes. Updating the `mtime` might make it
|
||||
// easier for these watchers to recognize an actual change. Since changing
|
||||
// a source code file also updates the `mtime`, moving a file should do so
|
||||
// as well because conceptually it is a change of a similar category.
|
||||
async function updateMtime(path: string): Promise<void> {
|
||||
const stat = await lstat(path);
|
||||
if (stat.isDirectory() || stat.isSymbolicLink()) {
|
||||
return; // only for files
|
||||
}
|
||||
|
||||
const fd = await promisify(fs.open)(path, 'a');
|
||||
try {
|
||||
await promisify(fs.futimes)(fd, stat.atime, new Date());
|
||||
} catch (error) {
|
||||
//ignore
|
||||
}
|
||||
const stat = await fs.promises.lstat(path);
|
||||
if (stat.isDirectory() || stat.isSymbolicLink()) {
|
||||
return; // only for files
|
||||
}
|
||||
|
||||
return promisify(fs.close)(fd);
|
||||
const fh = await fs.promises.open(path, 'a');
|
||||
try {
|
||||
await fh.utimes(stat.atime, new Date());
|
||||
} finally {
|
||||
await fh.close();
|
||||
}
|
||||
} catch (error) {
|
||||
// Ignore any error
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
await rename(source, target);
|
||||
await fs.promises.rename(source, target);
|
||||
await updateMtime(target);
|
||||
} catch (error) {
|
||||
|
||||
@@ -519,7 +561,7 @@ export async function move(source: string, target: string): Promise<void> {
|
||||
// 2.) The user tries to rename a file/folder that ends with a dot. This is not
|
||||
// really possible to move then, at least on UNC devices.
|
||||
if (source.toLowerCase() !== target.toLowerCase() && error.code === 'EXDEV' || source.endsWith('.')) {
|
||||
await copy(source, target);
|
||||
await copy(source, target, { preserveSymlinks: false /* copying to another device */ });
|
||||
await rimraf(source, RimRafMode.MOVE);
|
||||
await updateMtime(target);
|
||||
} else {
|
||||
@@ -528,74 +570,119 @@ export async function move(source: string, target: string): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
interface ICopyPayload {
|
||||
readonly root: { source: string, target: string };
|
||||
readonly options: { preserveSymlinks: boolean };
|
||||
readonly handledSourcePaths: Set<string>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively copies all of `source` to `target`.
|
||||
*
|
||||
* The options `preserveSymlinks` configures how symbolic
|
||||
* links should be handled when encountered. Set to
|
||||
* `false` to not preserve them and `true` otherwise.
|
||||
*/
|
||||
export async function copy(source: string, target: string, options: { preserveSymlinks: boolean }): Promise<void> {
|
||||
return doCopy(source, target, { root: { source, target }, options, handledSourcePaths: new Set<string>() });
|
||||
}
|
||||
|
||||
// When copying a file or folder, we want to preserve the mode
|
||||
// it had and as such provide it when creating. However, modes
|
||||
// can go beyond what we expect (see link below), so we mask it.
|
||||
// (https://github.com/nodejs/node-v0.x-archive/issues/3045#issuecomment-4862588)
|
||||
//
|
||||
// The `copy` method is very old so we should probably revisit
|
||||
// it's implementation and check wether this mask is still needed.
|
||||
const COPY_MODE_MASK = 0o777;
|
||||
|
||||
export async function copy(source: string, target: string, handledSourcesIn?: { [path: string]: boolean }): Promise<void> {
|
||||
async function doCopy(source: string, target: string, payload: ICopyPayload): Promise<void> {
|
||||
|
||||
// Keep track of paths already copied to prevent
|
||||
// cycles from symbolic links to cause issues
|
||||
const handledSources = handledSourcesIn ?? Object.create(null);
|
||||
if (handledSources[source]) {
|
||||
if (payload.handledSourcePaths.has(source)) {
|
||||
return;
|
||||
} else {
|
||||
handledSources[source] = true;
|
||||
payload.handledSourcePaths.add(source);
|
||||
}
|
||||
|
||||
const { stat, symbolicLink } = await statLink(source);
|
||||
if (symbolicLink?.dangling) {
|
||||
return; // skip over dangling symbolic links (https://github.com/microsoft/vscode/issues/111621)
|
||||
const { stat, symbolicLink } = await SymlinkSupport.stat(source);
|
||||
|
||||
// Symlink
|
||||
if (symbolicLink) {
|
||||
if (symbolicLink.dangling) {
|
||||
return; // do not copy dangling symbolic links (https://github.com/microsoft/vscode/issues/111621)
|
||||
}
|
||||
|
||||
// Try to re-create the symlink unless `preserveSymlinks: false`
|
||||
if (payload.options.preserveSymlinks) {
|
||||
try {
|
||||
return await doCopySymlink(source, target, payload);
|
||||
} catch (error) {
|
||||
// in any case of an error fallback to normal copy via dereferencing
|
||||
console.warn('[node.js fs] copy of symlink failed: ', error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!stat.isDirectory()) {
|
||||
// Folder
|
||||
if (stat.isDirectory()) {
|
||||
return doCopyDirectory(source, target, stat.mode & COPY_MODE_MASK, payload);
|
||||
}
|
||||
|
||||
// File or file-like
|
||||
else {
|
||||
return doCopyFile(source, target, stat.mode & COPY_MODE_MASK);
|
||||
}
|
||||
}
|
||||
|
||||
async function doCopyDirectory(source: string, target: string, mode: number, payload: ICopyPayload): Promise<void> {
|
||||
|
||||
// Create folder
|
||||
await mkdirp(target, stat.mode & COPY_MODE_MASK);
|
||||
await fs.promises.mkdir(target, { recursive: true, mode });
|
||||
|
||||
// Copy each file recursively
|
||||
const files = await readdir(source);
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
const file = files[i];
|
||||
await copy(join(source, file), join(target, file), handledSources);
|
||||
for (const file of files) {
|
||||
await doCopy(join(source, file), join(target, file), payload);
|
||||
}
|
||||
}
|
||||
|
||||
async function doCopyFile(source: string, target: string, mode: number): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const reader = fs.createReadStream(source);
|
||||
const writer = fs.createWriteStream(target, { mode });
|
||||
|
||||
let finished = false;
|
||||
const finish = (error?: Error) => {
|
||||
if (!finished) {
|
||||
finished = true;
|
||||
// Copy file
|
||||
await fs.promises.copyFile(source, target);
|
||||
|
||||
// in error cases, pass to callback
|
||||
if (error) {
|
||||
return reject(error);
|
||||
}
|
||||
|
||||
// we need to explicitly chmod because of https://github.com/nodejs/node/issues/1104
|
||||
fs.chmod(target, mode, error => error ? reject(error) : resolve());
|
||||
}
|
||||
};
|
||||
|
||||
// handle errors properly
|
||||
reader.once('error', error => finish(error));
|
||||
writer.once('error', error => finish(error));
|
||||
|
||||
// we are done (underlying fd has been closed)
|
||||
writer.once('close', () => finish());
|
||||
|
||||
// start piping
|
||||
reader.pipe(writer);
|
||||
});
|
||||
// restore mode (https://github.com/nodejs/node/issues/1104)
|
||||
await fs.promises.chmod(target, mode);
|
||||
}
|
||||
|
||||
async function doCopySymlink(source: string, target: string, payload: ICopyPayload): Promise<void> {
|
||||
|
||||
// Figure out link target
|
||||
let linkTarget = await fs.promises.readlink(source);
|
||||
|
||||
// Special case: the symlink points to a target that is
|
||||
// actually within the path that is being copied. In that
|
||||
// case we want the symlink to point to the target and
|
||||
// not the source
|
||||
if (isEqualOrParent(linkTarget, payload.root.source, !isLinux)) {
|
||||
linkTarget = join(payload.root.target, linkTarget.substr(payload.root.source.length + 1));
|
||||
}
|
||||
|
||||
// Create symlink
|
||||
await fs.promises.symlink(linkTarget, target);
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region Async FS Methods
|
||||
|
||||
export async function exists(path: string): Promise<boolean> {
|
||||
try {
|
||||
await fs.promises.access(path);
|
||||
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
@@ -8,18 +8,72 @@ import * as os from 'os';
|
||||
import * as path from 'vs/base/common/path';
|
||||
import { env } from 'vs/base/common/process';
|
||||
|
||||
const WindowsPowerShell64BitLabel = 'Windows PowerShell';
|
||||
const WindowsPowerShell32BitLabel = 'Windows PowerShell (x86)';
|
||||
|
||||
// This is required, since parseInt("7-preview") will return 7.
|
||||
const IntRegex: RegExp = /^\d+$/;
|
||||
|
||||
const PwshMsixRegex: RegExp = /^Microsoft.PowerShell_.*/;
|
||||
const PwshPreviewMsixRegex: RegExp = /^Microsoft.PowerShellPreview_.*/;
|
||||
|
||||
// The platform details descriptor for the platform we're on
|
||||
const isProcess64Bit: boolean = process.arch === 'x64';
|
||||
const isOS64Bit: boolean = isProcess64Bit || os.arch() === 'x64';
|
||||
const enum Arch {
|
||||
x64,
|
||||
x86,
|
||||
ARM
|
||||
}
|
||||
|
||||
let processArch: Arch;
|
||||
switch (process.arch) {
|
||||
case 'ia32':
|
||||
case 'x32':
|
||||
processArch = Arch.x86;
|
||||
break;
|
||||
case 'arm':
|
||||
case 'arm64':
|
||||
processArch = Arch.ARM;
|
||||
break;
|
||||
default:
|
||||
processArch = Arch.x64;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
Currently, here are the values for these environment variables on their respective archs:
|
||||
|
||||
On x86 process on x86:
|
||||
PROCESSOR_ARCHITECTURE is X86
|
||||
PROCESSOR_ARCHITEW6432 is undefined
|
||||
|
||||
On x86 process on x64:
|
||||
PROCESSOR_ARCHITECTURE is X86
|
||||
PROCESSOR_ARCHITEW6432 is AMD64
|
||||
|
||||
On x64 process on x64:
|
||||
PROCESSOR_ARCHITECTURE is AMD64
|
||||
PROCESSOR_ARCHITEW6432 is undefined
|
||||
|
||||
On ARM process on ARM:
|
||||
PROCESSOR_ARCHITECTURE is ARM64
|
||||
PROCESSOR_ARCHITEW6432 is undefined
|
||||
|
||||
On x86 process on ARM:
|
||||
PROCESSOR_ARCHITECTURE is X86
|
||||
PROCESSOR_ARCHITEW6432 is ARM64
|
||||
|
||||
On x64 process on ARM:
|
||||
PROCESSOR_ARCHITECTURE is ARM64
|
||||
PROCESSOR_ARCHITEW6432 is undefined
|
||||
*/
|
||||
let osArch: Arch;
|
||||
if (process.env['PROCESSOR_ARCHITEW6432']) {
|
||||
osArch = process.env['PROCESSOR_ARCHITEW6432'] === 'ARM64'
|
||||
? Arch.ARM
|
||||
: Arch.x64;
|
||||
} else if (process.env['PROCESSOR_ARCHITECTURE'] === 'ARM64') {
|
||||
osArch = Arch.ARM;
|
||||
} else if (process.env['PROCESSOR_ARCHITECTURE'] === 'X86') {
|
||||
osArch = Arch.x86;
|
||||
} else {
|
||||
osArch = Arch.x64;
|
||||
}
|
||||
|
||||
export interface IPowerShellExeDetails {
|
||||
readonly displayName: string;
|
||||
@@ -38,7 +92,7 @@ class PossiblePowerShellExe implements IPossiblePowerShellExe {
|
||||
|
||||
public async exists(): Promise<boolean> {
|
||||
if (this.knownToExist === undefined) {
|
||||
this.knownToExist = await pfs.fileExists(this.exePath);
|
||||
this.knownToExist = await pfs.SymlinkSupport.existsFile(this.exePath);
|
||||
}
|
||||
return this.knownToExist;
|
||||
}
|
||||
@@ -53,12 +107,12 @@ function getProgramFilesPath(
|
||||
}
|
||||
|
||||
// We might be a 64-bit process looking for 32-bit program files
|
||||
if (isProcess64Bit) {
|
||||
if (processArch === Arch.x64) {
|
||||
return env['ProgramFiles(x86)'] || null;
|
||||
}
|
||||
|
||||
// We might be a 32-bit process looking for 64-bit program files
|
||||
if (isOS64Bit) {
|
||||
if (osArch === Arch.x64) {
|
||||
return env.ProgramW6432 || null;
|
||||
}
|
||||
|
||||
@@ -66,28 +120,6 @@ function getProgramFilesPath(
|
||||
return null;
|
||||
}
|
||||
|
||||
function getSystem32Path({ useAlternateBitness = false }: { useAlternateBitness?: boolean } = {}): string {
|
||||
const windir: string = env.windir!;
|
||||
|
||||
if (!useAlternateBitness) {
|
||||
// Just use the native system bitness
|
||||
return path.join(windir, 'System32');
|
||||
}
|
||||
|
||||
// We might be a 64-bit process looking for 32-bit system32
|
||||
if (isProcess64Bit) {
|
||||
return path.join(windir, 'SysWOW64');
|
||||
}
|
||||
|
||||
// We might be a 32-bit process looking for 64-bit system32
|
||||
if (isOS64Bit) {
|
||||
return path.join(windir, 'Sysnative');
|
||||
}
|
||||
|
||||
// We're on a 32-bit Windows, so no alternate bitness
|
||||
return path.join(windir, 'System32');
|
||||
}
|
||||
|
||||
async function findPSCoreWindowsInstallation(
|
||||
{ useAlternateBitness = false, findPreview = false }:
|
||||
{ useAlternateBitness?: boolean; findPreview?: boolean } = {}): Promise<IPossiblePowerShellExe | null> {
|
||||
@@ -100,7 +132,7 @@ async function findPSCoreWindowsInstallation(
|
||||
const powerShellInstallBaseDir = path.join(programFilesPath, 'PowerShell');
|
||||
|
||||
// Ensure the base directory exists
|
||||
if (!await pfs.dirExists(powerShellInstallBaseDir)) {
|
||||
if (!await pfs.SymlinkSupport.existsDirectory(powerShellInstallBaseDir)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -142,7 +174,7 @@ async function findPSCoreWindowsInstallation(
|
||||
|
||||
// Now look for the file
|
||||
const exePath = path.join(powerShellInstallBaseDir, item, 'pwsh.exe');
|
||||
if (!await pfs.fileExists(exePath)) {
|
||||
if (!await pfs.SymlinkSupport.existsFile(exePath)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -169,7 +201,7 @@ async function findPSCoreMsix({ findPreview }: { findPreview?: boolean } = {}):
|
||||
// Find the base directory for MSIX application exe shortcuts
|
||||
const msixAppDir = path.join(env.LOCALAPPDATA, 'Microsoft', 'WindowsApps');
|
||||
|
||||
if (!await pfs.dirExists(msixAppDir)) {
|
||||
if (!await pfs.SymlinkSupport.existsDirectory(msixAppDir)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -179,11 +211,15 @@ async function findPSCoreMsix({ findPreview }: { findPreview?: boolean } = {}):
|
||||
: { pwshMsixDirRegex: PwshMsixRegex, pwshMsixName: 'PowerShell (Store)' };
|
||||
|
||||
// We should find only one such application, so return on the first one
|
||||
for (const subdir of await pfs.readdir(msixAppDir)) {
|
||||
if (pwshMsixDirRegex.test(subdir)) {
|
||||
const pwshMsixPath = path.join(msixAppDir, subdir, 'pwsh.exe');
|
||||
return new PossiblePowerShellExe(pwshMsixPath, pwshMsixName);
|
||||
try {
|
||||
for (const subdir of await pfs.readdir(msixAppDir)) {
|
||||
if (pwshMsixDirRegex.test(subdir)) {
|
||||
const pwshMsixPath = path.join(msixAppDir, subdir, 'pwsh.exe');
|
||||
return new PossiblePowerShellExe(pwshMsixPath, pwshMsixName);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn(`Unable to read MSIX directory (${msixAppDir}) because of the following error: ${err}`);
|
||||
}
|
||||
|
||||
// If we find nothing, return null
|
||||
@@ -196,33 +232,13 @@ function findPSCoreDotnetGlobalTool(): IPossiblePowerShellExe {
|
||||
return new PossiblePowerShellExe(dotnetGlobalToolExePath, '.NET Core PowerShell Global Tool');
|
||||
}
|
||||
|
||||
function findWinPS({ useAlternateBitness = false }: { useAlternateBitness?: boolean } = {}): IPossiblePowerShellExe | null {
|
||||
function findWinPS(): IPossiblePowerShellExe | null {
|
||||
const winPSPath = path.join(
|
||||
env.windir!,
|
||||
processArch === Arch.x86 && osArch !== Arch.x86 ? 'SysNative' : 'System32',
|
||||
'WindowsPowerShell', 'v1.0', 'powershell.exe');
|
||||
|
||||
// x86 and ARM only have one WinPS on them
|
||||
if (!isOS64Bit && useAlternateBitness) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const systemFolderPath = getSystem32Path({ useAlternateBitness });
|
||||
|
||||
const winPSPath = path.join(systemFolderPath, 'WindowsPowerShell', 'v1.0', 'powershell.exe');
|
||||
|
||||
let displayName: string;
|
||||
if (isProcess64Bit) {
|
||||
displayName = useAlternateBitness
|
||||
? WindowsPowerShell32BitLabel
|
||||
: WindowsPowerShell64BitLabel;
|
||||
} else if (isOS64Bit) {
|
||||
displayName = useAlternateBitness
|
||||
? WindowsPowerShell64BitLabel
|
||||
: WindowsPowerShell32BitLabel;
|
||||
} else {
|
||||
// NOTE: ARM Windows devices also have Windows PowerShell x86 on them. There is no
|
||||
// "ARM Windows PowerShell".
|
||||
displayName = WindowsPowerShell32BitLabel;
|
||||
}
|
||||
|
||||
return new PossiblePowerShellExe(winPSPath, displayName, true);
|
||||
return new PossiblePowerShellExe(winPSPath, 'Windows PowerShell', true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -276,18 +292,10 @@ async function* enumerateDefaultPowerShellInstallations(): AsyncIterable<IPossib
|
||||
}
|
||||
|
||||
// Finally, get Windows PowerShell
|
||||
|
||||
// Get the natural Windows PowerShell for the process bitness
|
||||
pwshExe = findWinPS();
|
||||
if (pwshExe) {
|
||||
yield pwshExe;
|
||||
}
|
||||
|
||||
// Get the alternate bitness Windows PowerShell
|
||||
pwshExe = findWinPS({ useAlternateBitness: true });
|
||||
if (pwshExe) {
|
||||
yield pwshExe;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import * as path from 'vs/base/common/path';
|
||||
import * as fs from 'fs';
|
||||
import { promisify } from 'util';
|
||||
import * as pfs from 'vs/base/node/pfs';
|
||||
import * as cp from 'child_process';
|
||||
import * as nls from 'vs/nls';
|
||||
import * as Types from 'vs/base/common/types';
|
||||
@@ -456,8 +456,8 @@ export namespace win32 {
|
||||
}
|
||||
|
||||
async function fileExists(path: string): Promise<boolean> {
|
||||
if (await promisify(fs.exists)(path)) {
|
||||
return !((await promisify(fs.stat)(path)).isDirectory());
|
||||
if (await pfs.exists(path)) {
|
||||
return !((await fs.promises.stat(path)).isDirectory());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
9
lib/vscode/src/vs/base/node/userDataPath.d.ts
vendored
Normal file
9
lib/vscode/src/vs/base/node/userDataPath.d.ts
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* Returns the user data path to use.
|
||||
*/
|
||||
export function getDefaultUserDataPath(): string;
|
||||
72
lib/vscode/src/vs/base/node/userDataPath.js
Normal file
72
lib/vscode/src/vs/base/node/userDataPath.js
Normal file
@@ -0,0 +1,72 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @param {typeof import('path')} path
|
||||
* @param {typeof import('os')} os
|
||||
* @param {string} productName
|
||||
*/
|
||||
function factory(path, os, productName) {
|
||||
|
||||
function getDefaultUserDataPath() {
|
||||
|
||||
// Support global VSCODE_APPDATA environment variable
|
||||
let appDataPath = process.env['VSCODE_APPDATA'];
|
||||
|
||||
// Otherwise check per platform
|
||||
if (!appDataPath) {
|
||||
switch (process.platform) {
|
||||
case 'win32':
|
||||
appDataPath = process.env['APPDATA'];
|
||||
if (!appDataPath) {
|
||||
const userProfile = process.env['USERPROFILE'];
|
||||
if (typeof userProfile !== 'string') {
|
||||
throw new Error('Windows: Unexpected undefined %USERPROFILE% environment variable');
|
||||
}
|
||||
appDataPath = path.join(userProfile, 'AppData', 'Roaming');
|
||||
}
|
||||
break;
|
||||
case 'darwin':
|
||||
appDataPath = path.join(os.homedir(), 'Library', 'Application Support');
|
||||
break;
|
||||
case 'linux':
|
||||
appDataPath = process.env['XDG_CONFIG_HOME'] || path.join(os.homedir(), '.config');
|
||||
break;
|
||||
default:
|
||||
throw new Error('Platform not supported');
|
||||
}
|
||||
}
|
||||
|
||||
return path.join(appDataPath, productName);
|
||||
}
|
||||
|
||||
return {
|
||||
getDefaultUserDataPath
|
||||
};
|
||||
}
|
||||
|
||||
if (typeof define === 'function') {
|
||||
define(['require', 'path', 'os', 'vs/base/common/network', 'vs/base/common/resources'], function (require, /** @type {typeof import('path')} */ path, /** @type {typeof import('os')} */ os, /** @type {typeof import('../common/network')} */ network, /** @type {typeof import("../common/resources")} */ resources) {
|
||||
const rootPath = resources.dirname(network.FileAccess.asFileUri('', require));
|
||||
const pkg = require.__$__nodeRequire(resources.joinPath(rootPath, 'package.json').fsPath);
|
||||
|
||||
return factory(path, os, pkg.name);
|
||||
}); // amd
|
||||
} else if (typeof module === 'object' && typeof module.exports === 'object') {
|
||||
const pkg = require('../../../../package.json');
|
||||
const path = require('path');
|
||||
const os = require('os');
|
||||
|
||||
module.exports = factory(path, os, pkg.name); // commonjs
|
||||
} else {
|
||||
throw new Error('Unknown context');
|
||||
}
|
||||
}());
|
||||
@@ -5,10 +5,10 @@
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import * as path from 'vs/base/common/path';
|
||||
import { createWriteStream, WriteStream } from 'fs';
|
||||
import { promises, createWriteStream, WriteStream } from 'fs';
|
||||
import { Readable } from 'stream';
|
||||
import { Sequencer, createCancelablePromise } from 'vs/base/common/async';
|
||||
import { mkdirp, rimraf } from 'vs/base/node/pfs';
|
||||
import { rimraf } from 'vs/base/node/pfs';
|
||||
import { open as _openZip, Entry, ZipFile } from 'yauzl';
|
||||
import * as yazl from 'yazl';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
@@ -86,7 +86,7 @@ function extractEntry(stream: Readable, fileName: string, mode: number, targetPa
|
||||
}
|
||||
});
|
||||
|
||||
return Promise.resolve(mkdirp(targetDirName)).then(() => new Promise<void>((c, e) => {
|
||||
return Promise.resolve(promises.mkdir(targetDirName, { recursive: true })).then(() => new Promise<void>((c, e) => {
|
||||
if (token.isCancellationRequested) {
|
||||
return;
|
||||
}
|
||||
@@ -149,7 +149,7 @@ function extractZip(zipfile: ZipFile, targetPath: string, options: IOptions, tok
|
||||
// directory file names end with '/'
|
||||
if (/\/$/.test(fileName)) {
|
||||
const targetFileName = path.join(targetPath, fileName);
|
||||
last = createCancelablePromise(token => mkdirp(targetFileName).then(() => readNextEntry(token)).then(undefined, e));
|
||||
last = createCancelablePromise(token => promises.mkdir(targetFileName, { recursive: true }).then(() => readNextEntry(token)).then(undefined, e));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -1023,146 +1023,141 @@ export class StaticRouter<TContext = string> implements IClientRouter<TContext>
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//#region createChannelReceiver / createChannelSender
|
||||
|
||||
/**
|
||||
* Use both `createChannelReceiver` and `createChannelSender`
|
||||
* for automated process <=> process communication over methods
|
||||
* and events. You do not need to spell out each method on both
|
||||
* sides, a proxy will take care of this.
|
||||
* Use ProxyChannels to automatically wrapping and unwrapping
|
||||
* services to/from IPC channels, instead of manually wrapping
|
||||
* each service method and event.
|
||||
*
|
||||
* Rules:
|
||||
* - if marshalling is enabled, only `URI` and `RegExp` is converted
|
||||
* Restrictions:
|
||||
* - If marshalling is enabled, only `URI` and `RegExp` is converted
|
||||
* automatically for you
|
||||
* - events must follow the naming convention `onUppercase`
|
||||
* - Events must follow the naming convention `onUpperCase`
|
||||
* - `CancellationToken` is currently not supported
|
||||
* - if a context is provided, you can use `AddFirstParameterToFunctions`
|
||||
* - If a context is provided, you can use `AddFirstParameterToFunctions`
|
||||
* utility to signal this in the receiving side type
|
||||
*/
|
||||
export namespace ProxyChannel {
|
||||
|
||||
export interface IBaseChannelOptions {
|
||||
export interface IProxyOptions {
|
||||
|
||||
/**
|
||||
* Disables automatic marshalling of `URI`.
|
||||
* If marshalling is disabled, `UriComponents`
|
||||
* must be used instead.
|
||||
*/
|
||||
disableMarshalling?: boolean;
|
||||
}
|
||||
|
||||
export interface IChannelReceiverOptions extends IBaseChannelOptions { }
|
||||
|
||||
export function createChannelReceiver(service: unknown, options?: IChannelReceiverOptions): IServerChannel {
|
||||
const handler = service as { [key: string]: unknown };
|
||||
const disableMarshalling = options && options.disableMarshalling;
|
||||
|
||||
// Buffer any event that should be supported by
|
||||
// iterating over all property keys and finding them
|
||||
const mapEventNameToEvent = new Map<string, Event<unknown>>();
|
||||
for (const key in handler) {
|
||||
if (propertyIsEvent(key)) {
|
||||
mapEventNameToEvent.set(key, Event.buffer(handler[key] as Event<unknown>, true));
|
||||
}
|
||||
/**
|
||||
* Disables automatic marshalling of `URI`.
|
||||
* If marshalling is disabled, `UriComponents`
|
||||
* must be used instead.
|
||||
*/
|
||||
disableMarshalling?: boolean;
|
||||
}
|
||||
|
||||
return new class implements IServerChannel {
|
||||
export interface ICreateServiceChannelOptions extends IProxyOptions { }
|
||||
|
||||
listen<T>(_: unknown, event: string): Event<T> {
|
||||
const eventImpl = mapEventNameToEvent.get(event);
|
||||
if (eventImpl) {
|
||||
return eventImpl as Event<T>;
|
||||
export function fromService(service: unknown, options?: ICreateServiceChannelOptions): IServerChannel {
|
||||
const handler = service as { [key: string]: unknown };
|
||||
const disableMarshalling = options && options.disableMarshalling;
|
||||
|
||||
// Buffer any event that should be supported by
|
||||
// iterating over all property keys and finding them
|
||||
const mapEventNameToEvent = new Map<string, Event<unknown>>();
|
||||
for (const key in handler) {
|
||||
if (propertyIsEvent(key)) {
|
||||
mapEventNameToEvent.set(key, Event.buffer(handler[key] as Event<unknown>, true));
|
||||
}
|
||||
|
||||
throw new Error(`Event not found: ${event}`);
|
||||
}
|
||||
|
||||
call(_: unknown, command: string, args?: any[]): Promise<any> {
|
||||
const target = handler[command];
|
||||
if (typeof target === 'function') {
|
||||
return new class implements IServerChannel {
|
||||
|
||||
// Revive unless marshalling disabled
|
||||
if (!disableMarshalling && Array.isArray(args)) {
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
args[i] = revive(args[i]);
|
||||
}
|
||||
listen<T>(_: unknown, event: string): Event<T> {
|
||||
const eventImpl = mapEventNameToEvent.get(event);
|
||||
if (eventImpl) {
|
||||
return eventImpl as Event<T>;
|
||||
}
|
||||
|
||||
return target.apply(handler, args);
|
||||
throw new Error(`Event not found: ${event}`);
|
||||
}
|
||||
|
||||
throw new Error(`Method not found: ${command}`);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export interface IChannelSenderOptions extends IBaseChannelOptions {
|
||||
|
||||
/**
|
||||
* If provided, will add the value of `context`
|
||||
* to each method call to the target.
|
||||
*/
|
||||
context?: unknown;
|
||||
|
||||
/**
|
||||
* If provided, will not proxy any of the properties
|
||||
* that are part of the Map but rather return that value.
|
||||
*/
|
||||
properties?: Map<string, unknown>;
|
||||
}
|
||||
|
||||
export function createChannelSender<T>(channel: IChannel, options?: IChannelSenderOptions): T {
|
||||
const disableMarshalling = options && options.disableMarshalling;
|
||||
|
||||
return new Proxy({}, {
|
||||
get(_target: T, propKey: PropertyKey) {
|
||||
if (typeof propKey === 'string') {
|
||||
|
||||
// Check for predefined values
|
||||
if (options?.properties?.has(propKey)) {
|
||||
return options.properties.get(propKey);
|
||||
}
|
||||
|
||||
// Event
|
||||
if (propertyIsEvent(propKey)) {
|
||||
return channel.listen(propKey);
|
||||
}
|
||||
|
||||
// Function
|
||||
return async function (...args: any[]) {
|
||||
|
||||
// Add context if any
|
||||
let methodArgs: any[];
|
||||
if (options && !isUndefinedOrNull(options.context)) {
|
||||
methodArgs = [options.context, ...args];
|
||||
} else {
|
||||
methodArgs = args;
|
||||
}
|
||||
|
||||
const result = await channel.call(propKey, methodArgs);
|
||||
call(_: unknown, command: string, args?: any[]): Promise<any> {
|
||||
const target = handler[command];
|
||||
if (typeof target === 'function') {
|
||||
|
||||
// Revive unless marshalling disabled
|
||||
if (!disableMarshalling) {
|
||||
return revive(result);
|
||||
if (!disableMarshalling && Array.isArray(args)) {
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
args[i] = revive(args[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
return target.apply(handler, args);
|
||||
}
|
||||
|
||||
throw new Error(`Method not found: ${command}`);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
throw new Error(`Property not found: ${String(propKey)}`);
|
||||
}
|
||||
}) as T;
|
||||
export interface ICreateProxyServiceOptions extends IProxyOptions {
|
||||
|
||||
/**
|
||||
* If provided, will add the value of `context`
|
||||
* to each method call to the target.
|
||||
*/
|
||||
context?: unknown;
|
||||
|
||||
/**
|
||||
* If provided, will not proxy any of the properties
|
||||
* that are part of the Map but rather return that value.
|
||||
*/
|
||||
properties?: Map<string, unknown>;
|
||||
}
|
||||
|
||||
export function toService<T>(channel: IChannel, options?: ICreateProxyServiceOptions): T {
|
||||
const disableMarshalling = options && options.disableMarshalling;
|
||||
|
||||
return new Proxy({}, {
|
||||
get(_target: T, propKey: PropertyKey) {
|
||||
if (typeof propKey === 'string') {
|
||||
|
||||
// Check for predefined values
|
||||
if (options?.properties?.has(propKey)) {
|
||||
return options.properties.get(propKey);
|
||||
}
|
||||
|
||||
// Event
|
||||
if (propertyIsEvent(propKey)) {
|
||||
return channel.listen(propKey);
|
||||
}
|
||||
|
||||
// Function
|
||||
return async function (...args: any[]) {
|
||||
|
||||
// Add context if any
|
||||
let methodArgs: any[];
|
||||
if (options && !isUndefinedOrNull(options.context)) {
|
||||
methodArgs = [options.context, ...args];
|
||||
} else {
|
||||
methodArgs = args;
|
||||
}
|
||||
|
||||
const result = await channel.call(propKey, methodArgs);
|
||||
|
||||
// Revive unless marshalling disabled
|
||||
if (!disableMarshalling) {
|
||||
return revive(result);
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
throw new Error(`Property not found: ${String(propKey)}`);
|
||||
}
|
||||
}) as T;
|
||||
}
|
||||
|
||||
function propertyIsEvent(name: string): boolean {
|
||||
// Assume a property is an event if it has a form of "onSomething"
|
||||
return name[0] === 'o' && name[1] === 'n' && strings.isUpperAsciiLetter(name.charCodeAt(2));
|
||||
}
|
||||
}
|
||||
|
||||
function propertyIsEvent(name: string): boolean {
|
||||
// Assume a property is an event if it has a form of "onSomething"
|
||||
return name[0] === 'o' && name[1] === 'n' && strings.isUpperAsciiLetter(name.charCodeAt(2));
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
|
||||
const colorTables = [
|
||||
['#2977B1', '#FC802D', '#34A13A', '#D3282F', '#9366BA'],
|
||||
['#8B564C', '#E177C0', '#7F7F7F', '#BBBE3D', '#2EBECD']
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ipcRenderer } from 'vs/base/parts/sandbox/electron-sandbox/globals';
|
||||
import { ipcRenderer } from 'electron';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { ClientConnectionEvent, IPCServer } from 'vs/base/parts/ipc/common/ipc';
|
||||
import { Protocol as MessagePortProtocol } from 'vs/base/parts/ipc/common/ipc.mp';
|
||||
@@ -39,6 +39,9 @@ export class Server extends IPCServer {
|
||||
};
|
||||
|
||||
// Send one port back to the requestor
|
||||
// Note: we intentionally use `electron` APIs here because
|
||||
// transferables like the `MessagePort` cannot be transfered
|
||||
// over preload scripts when `contextIsolation: true`
|
||||
ipcRenderer.postMessage('vscode:createMessageChannelResult', nonce, [outgoingPort]);
|
||||
|
||||
return result;
|
||||
@@ -71,10 +71,8 @@ export interface IIPCOptions {
|
||||
debugBrk?: number;
|
||||
|
||||
/**
|
||||
* See https://github.com/microsoft/vscode/issues/27665
|
||||
* Allows to pass in fresh execArgv to the forked process such that it doesn't inherit them from `process.execArgv`.
|
||||
* e.g. Launching the extension host process with `--inspect-brk=xxx` and then forking a process from the extension host
|
||||
* results in the forked process inheriting `--inspect-brk=xxx`.
|
||||
* If set, starts the fork with empty execArgv. If not set, execArgv from the parent proces are inherited,
|
||||
* except --inspect= and --inspect-brk= which are filtered as they would result in a port conflict.
|
||||
*/
|
||||
freshExecArgv?: boolean;
|
||||
|
||||
@@ -198,6 +196,12 @@ export class Client implements IChannelClient, IDisposable {
|
||||
forkOpts.execArgv = ['--nolazy', '--inspect-brk=' + this.options.debugBrk];
|
||||
}
|
||||
|
||||
if (forkOpts.execArgv === undefined) {
|
||||
// if not set, the forked process inherits the execArgv of the parent process
|
||||
// --inspect and --inspect-brk can not be inherited as the port would conflict
|
||||
forkOpts.execArgv = process.execArgv.filter(a => !/^--inspect(-brk)?=/.test(a)); // remove
|
||||
}
|
||||
|
||||
if (isMacintosh && forkOpts.env) {
|
||||
// Unset `DYLD_LIBRARY_PATH`, as it leads to process crashes
|
||||
// See https://github.com/microsoft/vscode/issues/105848
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { IChannel, IServerChannel, IMessagePassingProtocol, IPCServer, ClientConnectionEvent, IPCClient, createChannelReceiver, createChannelSender } from 'vs/base/parts/ipc/common/ipc';
|
||||
import { IChannel, IServerChannel, IMessagePassingProtocol, IPCServer, ClientConnectionEvent, IPCClient, ProxyChannel } from 'vs/base/parts/ipc/common/ipc';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||
import { canceled } from 'vs/base/common/errors';
|
||||
@@ -332,10 +332,10 @@ suite('Base IPC', function () {
|
||||
const testServer = new TestIPCServer();
|
||||
server = testServer;
|
||||
|
||||
server.registerChannel(TestChannelId, createChannelReceiver(service));
|
||||
server.registerChannel(TestChannelId, ProxyChannel.fromService(service));
|
||||
|
||||
client = testServer.createConnection('client1');
|
||||
ipcService = createChannelSender(client.getChannel(TestChannelId));
|
||||
ipcService = ProxyChannel.toService(client.getChannel(TestChannelId));
|
||||
});
|
||||
|
||||
teardown(function () {
|
||||
@@ -398,10 +398,10 @@ suite('Base IPC', function () {
|
||||
const testServer = new TestIPCServer();
|
||||
server = testServer;
|
||||
|
||||
server.registerChannel(TestChannelId, createChannelReceiver(service));
|
||||
server.registerChannel(TestChannelId, ProxyChannel.fromService(service));
|
||||
|
||||
client = testServer.createConnection('client1');
|
||||
ipcService = createChannelSender(client.getChannel(TestChannelId), { context: 'Super Context' });
|
||||
ipcService = ProxyChannel.toService(client.getChannel(TestChannelId), { context: 'Super Context' });
|
||||
});
|
||||
|
||||
teardown(function () {
|
||||
|
||||
@@ -11,7 +11,7 @@ suite('IPC, MessagePorts', () => {
|
||||
test('message port close event', async () => {
|
||||
const { port1, port2 } = new MessageChannel();
|
||||
|
||||
new MessagePortClient(port1, 'client1');
|
||||
const client1 = new MessagePortClient(port1, 'client1');
|
||||
const client2 = new MessagePortClient(port2, 'client2');
|
||||
|
||||
// This test ensures that Electron's API for the close event
|
||||
@@ -24,5 +24,7 @@ suite('IPC, MessagePorts', () => {
|
||||
client2.dispose();
|
||||
|
||||
assert.ok(await whenClosed);
|
||||
|
||||
client1.dispose();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -56,7 +56,7 @@ export interface IQuickInputStyles {
|
||||
countBadge: ICountBadgetyles;
|
||||
button: IButtonStyles;
|
||||
progressBar: IProgressBarStyles;
|
||||
list: IListStyles & { listInactiveFocusForeground?: Color; pickerGroupBorder?: Color; pickerGroupForeground?: Color; };
|
||||
list: IListStyles & { pickerGroupBorder?: Color; pickerGroupForeground?: Color; };
|
||||
}
|
||||
|
||||
export interface IQuickInputWidgetStyles {
|
||||
@@ -1706,10 +1706,6 @@ export class QuickInputController extends Disposable {
|
||||
this.ui.list.style(this.styles.list);
|
||||
|
||||
const content: string[] = [];
|
||||
if (this.styles.list.listInactiveFocusForeground) {
|
||||
content.push(`.monaco-list .monaco-list-row.focused { color: ${this.styles.list.listInactiveFocusForeground}; }`);
|
||||
content.push(`.monaco-list .monaco-list-row.focused:hover { color: ${this.styles.list.listInactiveFocusForeground}; }`); // overwrite :hover style in this case!
|
||||
}
|
||||
if (this.styles.list.pickerGroupBorder) {
|
||||
content.push(`.quick-input-list .quick-input-list-entry { border-top-color: ${this.styles.list.pickerGroupBorder}; }`);
|
||||
}
|
||||
|
||||
@@ -35,17 +35,6 @@
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {string} channel
|
||||
* @param {any} message
|
||||
* @param {MessagePort[]} transfer
|
||||
*/
|
||||
postMessage(channel, message, transfer) {
|
||||
if (validateIPC(channel)) {
|
||||
ipcRenderer.postMessage(channel, message, transfer);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {string} channel
|
||||
* @param {any[]} args
|
||||
@@ -88,6 +77,33 @@
|
||||
}
|
||||
},
|
||||
|
||||
ipcMessagePort: {
|
||||
|
||||
/**
|
||||
* @param {string} channelRequest
|
||||
* @param {string} channelResponse
|
||||
* @param {string} requestNonce
|
||||
*/
|
||||
connect(channelRequest, channelResponse, requestNonce) {
|
||||
if (validateIPC(channelRequest) && validateIPC(channelResponse)) {
|
||||
const responseListener = (/** @type {import('electron').IpcRendererEvent} */ e, /** @type {string} */ responseNonce) => {
|
||||
// validate that the nonce from the response is the same
|
||||
// as when requested. and if so, use `postMessage` to
|
||||
// send the `MessagePort` safely over, even when context
|
||||
// isolation is enabled
|
||||
if (requestNonce === responseNonce) {
|
||||
ipcRenderer.off(channelResponse, responseListener);
|
||||
window.postMessage(requestNonce, '*', e.ports);
|
||||
}
|
||||
};
|
||||
|
||||
// request message port from main and await result
|
||||
ipcRenderer.on(channelResponse, responseListener);
|
||||
ipcRenderer.send(channelRequest, requestNonce);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Support for subset of methods of Electron's `webFrame` type.
|
||||
*/
|
||||
|
||||
@@ -15,10 +15,12 @@ export interface IpcRendererEvent extends Event {
|
||||
|
||||
// Docs: https://electronjs.org/docs/api/structures/ipc-renderer-event
|
||||
|
||||
/**
|
||||
* A list of MessagePorts that were transferred with this message
|
||||
*/
|
||||
ports: MessagePort[];
|
||||
// Note: API with `Transferable` intentionally commented out because you
|
||||
// cannot transfer these when `contextIsolation: true`.
|
||||
// /**
|
||||
// * A list of MessagePorts that were transferred with this message
|
||||
// */
|
||||
// ports: MessagePort[];
|
||||
/**
|
||||
* The `IpcRenderer` instance that emitted the event originally
|
||||
*/
|
||||
@@ -93,20 +95,23 @@ export interface IpcRenderer {
|
||||
* If you do not need a response to the message, consider using `ipcRenderer.send`.
|
||||
*/
|
||||
invoke(channel: string, ...args: any[]): Promise<any>;
|
||||
/**
|
||||
* Send a message to the main process, optionally transferring ownership of zero or
|
||||
* more `MessagePort` objects.
|
||||
*
|
||||
* The transferred `MessagePort` objects will be available in the main process as
|
||||
* `MessagePortMain` objects by accessing the `ports` property of the emitted
|
||||
* event.
|
||||
*
|
||||
* For example:
|
||||
*
|
||||
* For more information on using `MessagePort` and `MessageChannel`, see the MDN
|
||||
* documentation.
|
||||
*/
|
||||
postMessage(channel: string, message: any, transfer?: MessagePort[]): void;
|
||||
|
||||
// Note: API with `Transferable` intentionally commented out because you
|
||||
// cannot transfer these when `contextIsolation: true`.
|
||||
// /**
|
||||
// * Send a message to the main process, optionally transferring ownership of zero or
|
||||
// * more `MessagePort` objects.
|
||||
// *
|
||||
// * The transferred `MessagePort` objects will be available in the main process as
|
||||
// * `MessagePortMain` objects by accessing the `ports` property of the emitted
|
||||
// * event.
|
||||
// *
|
||||
// * For example:
|
||||
// *
|
||||
// * For more information on using `MessagePort` and `MessageChannel`, see the MDN
|
||||
// * documentation.
|
||||
// */
|
||||
// postMessage(channel: string, message: any): void;
|
||||
}
|
||||
|
||||
export interface WebFrame {
|
||||
|
||||
@@ -3,10 +3,13 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { globals, INodeProcess, IProcessEnvironment } from 'vs/base/common/platform';
|
||||
import { globals, IProcessEnvironment } from 'vs/base/common/platform';
|
||||
import { ProcessMemoryInfo, CrashReporter, IpcRenderer, WebFrame } from 'vs/base/parts/sandbox/electron-sandbox/electronTypes';
|
||||
|
||||
export interface ISandboxNodeProcess extends INodeProcess {
|
||||
/**
|
||||
* In sandboxed renderers we cannot expose all of the `process` global of node.js
|
||||
*/
|
||||
export interface IPartialNodeProcess {
|
||||
|
||||
/**
|
||||
* The process.platform property returns a string identifying the operating system platform
|
||||
@@ -40,24 +43,6 @@ export interface ISandboxNodeProcess extends INodeProcess {
|
||||
*/
|
||||
readonly execPath: string;
|
||||
|
||||
/**
|
||||
* Resolve the true process environment to use and apply it to `process.env`.
|
||||
*
|
||||
* There are different layers of environment that will apply:
|
||||
* - `process.env`: this is the actual environment of the process before this method
|
||||
* - `shellEnv` : if the program was not started from a terminal, we resolve all shell
|
||||
* variables to get the same experience as if the program was started from
|
||||
* a terminal (Linux, macOS)
|
||||
* - `userEnv` : this is instance specific environment, e.g. if the user started the program
|
||||
* from a terminal and changed certain variables
|
||||
*
|
||||
* The order of overwrites is `process.env` < `shellEnv` < `userEnv`.
|
||||
*
|
||||
* It is critical that every process awaits this method early on startup to get the right
|
||||
* set of environment in `process.env`.
|
||||
*/
|
||||
resolveEnv(userEnv: IProcessEnvironment): Promise<void>;
|
||||
|
||||
/**
|
||||
* A listener on the process. Only a small subset of listener types are allowed.
|
||||
*/
|
||||
@@ -79,6 +64,28 @@ export interface ISandboxNodeProcess extends INodeProcess {
|
||||
getProcessMemoryInfo: () => Promise<ProcessMemoryInfo>;
|
||||
}
|
||||
|
||||
export interface ISandboxNodeProcess extends IPartialNodeProcess {
|
||||
|
||||
/**
|
||||
* A custom method we add to `process`: Resolve the true process environment to use and
|
||||
* apply it to `process.env`.
|
||||
*
|
||||
* There are different layers of environment that will apply:
|
||||
* - `process.env`: this is the actual environment of the process before this method
|
||||
* - `shellEnv` : if the program was not started from a terminal, we resolve all shell
|
||||
* variables to get the same experience as if the program was started from
|
||||
* a terminal (Linux, macOS)
|
||||
* - `userEnv` : this is instance specific environment, e.g. if the user started the program
|
||||
* from a terminal and changed certain variables
|
||||
*
|
||||
* The order of overwrites is `process.env` < `shellEnv` < `userEnv`.
|
||||
*
|
||||
* It is critical that every process awaits this method early on startup to get the right
|
||||
* set of environment in `process.env`.
|
||||
*/
|
||||
resolveEnv(userEnv: IProcessEnvironment): Promise<void>;
|
||||
}
|
||||
|
||||
export interface ISandboxContext {
|
||||
|
||||
/**
|
||||
@@ -87,7 +94,23 @@ export interface ISandboxContext {
|
||||
sandbox: boolean;
|
||||
}
|
||||
|
||||
export interface IpcMessagePort {
|
||||
|
||||
/**
|
||||
* Establish a connection via `MessagePort` to a target. The main process
|
||||
* will need to transfer the port over to the `channelResponse` after listening
|
||||
* to `channelRequest` with a payload of `requestNonce` so that the
|
||||
* source can correlate the response.
|
||||
*
|
||||
* The source should install a `window.on('message')` listener, ensuring `e.data`
|
||||
* matches `requestNonce`, `e.source` matches `window` and then receiving the
|
||||
* `MessagePort` via `e.ports[0]`.
|
||||
*/
|
||||
connect(channelRequest: string, channelResponse: string, requestNonce: string): void;
|
||||
}
|
||||
|
||||
export const ipcRenderer: IpcRenderer = globals.vscode.ipcRenderer;
|
||||
export const ipcMessagePort: IpcMessagePort = globals.vscode.ipcMessagePort;
|
||||
export const webFrame: WebFrame = globals.vscode.webFrame;
|
||||
export const crashReporter: CrashReporter = globals.vscode.crashReporter;
|
||||
export const process: ISandboxNodeProcess = globals.vscode.process;
|
||||
|
||||
@@ -4,12 +4,13 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { ipcRenderer, crashReporter, webFrame } from 'vs/base/parts/sandbox/electron-sandbox/globals';
|
||||
import { ipcRenderer, crashReporter, webFrame, process } from 'vs/base/parts/sandbox/electron-sandbox/globals';
|
||||
|
||||
suite('Sandbox', () => {
|
||||
test('globals', () => {
|
||||
assert.ok(ipcRenderer);
|
||||
assert.ok(crashReporter);
|
||||
assert.ok(webFrame);
|
||||
assert.ok(typeof ipcRenderer.invoke === 'function');
|
||||
assert.ok(typeof crashReporter.addExtraParameter === 'function');
|
||||
assert.ok(typeof webFrame.setZoomLevel === 'function');
|
||||
assert.ok(typeof process.platform === 'string');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -84,11 +84,13 @@ export class Storage extends Disposable implements IStorage {
|
||||
|
||||
private cache = new Map<string, string>();
|
||||
|
||||
private readonly flushDelayer = this._register(new ThrottledDelayer<void>(Storage.DEFAULT_FLUSH_DELAY));
|
||||
private readonly flushDelayer = new ThrottledDelayer<void>(Storage.DEFAULT_FLUSH_DELAY);
|
||||
|
||||
private pendingDeletes = new Set<string>();
|
||||
private pendingInserts = new Map<string, string>();
|
||||
|
||||
private pendingClose: Promise<void> | undefined = undefined;
|
||||
|
||||
private readonly whenFlushedCallbacks: Function[] = [];
|
||||
|
||||
constructor(
|
||||
@@ -256,10 +258,15 @@ export class Storage extends Disposable implements IStorage {
|
||||
}
|
||||
|
||||
async close(): Promise<void> {
|
||||
if (this.state === StorageState.Closed) {
|
||||
return; // return if already closed
|
||||
if (!this.pendingClose) {
|
||||
this.pendingClose = this.doClose();
|
||||
}
|
||||
|
||||
return this.pendingClose;
|
||||
}
|
||||
|
||||
private async doClose(): Promise<void> {
|
||||
|
||||
// Update state
|
||||
this.state = StorageState.Closed;
|
||||
|
||||
@@ -312,6 +319,13 @@ export class Storage extends Disposable implements IStorage {
|
||||
|
||||
return new Promise(resolve => this.whenFlushedCallbacks.push(resolve));
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.flushDelayer.cancel(); // workaround https://github.com/microsoft/vscode/issues/116777
|
||||
this.flushDelayer.dispose();
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
export class InMemoryStorageDatabase implements IStorageDatabase {
|
||||
|
||||
@@ -4,11 +4,12 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import type { Database, Statement } from 'vscode-sqlite3';
|
||||
import { promises } from 'fs';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { timeout } from 'vs/base/common/async';
|
||||
import { mapToString, setToString } from 'vs/base/common/map';
|
||||
import { basename } from 'vs/base/common/path';
|
||||
import { copy, renameIgnoreError, unlink } from 'vs/base/node/pfs';
|
||||
import { copy } from 'vs/base/node/pfs';
|
||||
import { IStorageDatabase, IStorageItemsChangeEvent, IUpdateRequest } from 'vs/base/parts/storage/common/storage';
|
||||
|
||||
interface IDatabaseConnection {
|
||||
@@ -186,7 +187,7 @@ export class SQLiteStorageDatabase implements IStorageDatabase {
|
||||
// Delete the existing DB. If the path does not exist or fails to
|
||||
// be deleted, we do not try to recover anymore because we assume
|
||||
// that the path is no longer writeable for us.
|
||||
return unlink(this.path).then(() => {
|
||||
return promises.unlink(this.path).then(() => {
|
||||
|
||||
// Re-open the DB fresh
|
||||
return this.doConnect(this.path).then(recoveryConnection => {
|
||||
@@ -216,7 +217,7 @@ export class SQLiteStorageDatabase implements IStorageDatabase {
|
||||
private backup(): Promise<void> {
|
||||
const backupPath = this.toBackupPath(this.path);
|
||||
|
||||
return copy(this.path, backupPath);
|
||||
return copy(this.path, backupPath, { preserveSymlinks: false });
|
||||
}
|
||||
|
||||
private toBackupPath(path: string): string {
|
||||
@@ -272,8 +273,12 @@ export class SQLiteStorageDatabase implements IStorageDatabase {
|
||||
// folder is really not writeable for us.
|
||||
//
|
||||
try {
|
||||
await unlink(path);
|
||||
await renameIgnoreError(this.toBackupPath(path), path);
|
||||
await promises.unlink(path);
|
||||
try {
|
||||
await promises.rename(this.toBackupPath(path), path);
|
||||
} catch (error) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
return await this.doConnect(path);
|
||||
} catch (error) {
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -30,7 +30,7 @@ suite('dom', () => {
|
||||
assert(element.classList.contains('far'));
|
||||
assert(!element.classList.contains('boo'));
|
||||
assert(element.classList.contains('foobar'));
|
||||
assert.equal(element.className, 'foobar far');
|
||||
assert.strictEqual(element.className, 'foobar far');
|
||||
|
||||
element = document.createElement('div');
|
||||
element.className = 'foobar boo far';
|
||||
@@ -39,19 +39,19 @@ suite('dom', () => {
|
||||
assert(!element.classList.contains('far'));
|
||||
assert(element.classList.contains('boo'));
|
||||
assert(element.classList.contains('foobar'));
|
||||
assert.equal(element.className, 'foobar boo');
|
||||
assert.strictEqual(element.className, 'foobar boo');
|
||||
|
||||
element.classList.remove('boo');
|
||||
assert(!element.classList.contains('far'));
|
||||
assert(!element.classList.contains('boo'));
|
||||
assert(element.classList.contains('foobar'));
|
||||
assert.equal(element.className, 'foobar');
|
||||
assert.strictEqual(element.className, 'foobar');
|
||||
|
||||
element.classList.remove('foobar');
|
||||
assert(!element.classList.contains('far'));
|
||||
assert(!element.classList.contains('boo'));
|
||||
assert(!element.classList.contains('foobar'));
|
||||
assert.equal(element.className, '');
|
||||
assert.strictEqual(element.className, '');
|
||||
});
|
||||
|
||||
test('removeClass should consider hyphens', function () {
|
||||
@@ -83,7 +83,7 @@ suite('dom', () => {
|
||||
const div = $('div');
|
||||
assert(div);
|
||||
assert(div instanceof HTMLElement);
|
||||
assert.equal(div.tagName, 'DIV');
|
||||
assert.strictEqual(div.tagName, 'DIV');
|
||||
assert(!div.firstChild);
|
||||
});
|
||||
|
||||
@@ -91,42 +91,42 @@ suite('dom', () => {
|
||||
const div = $('div#foo');
|
||||
assert(div);
|
||||
assert(div instanceof HTMLElement);
|
||||
assert.equal(div.tagName, 'DIV');
|
||||
assert.equal(div.id, 'foo');
|
||||
assert.strictEqual(div.tagName, 'DIV');
|
||||
assert.strictEqual(div.id, 'foo');
|
||||
});
|
||||
|
||||
test('should buld nodes with class-name', () => {
|
||||
const div = $('div.foo');
|
||||
assert(div);
|
||||
assert(div instanceof HTMLElement);
|
||||
assert.equal(div.tagName, 'DIV');
|
||||
assert.equal(div.className, 'foo');
|
||||
assert.strictEqual(div.tagName, 'DIV');
|
||||
assert.strictEqual(div.className, 'foo');
|
||||
});
|
||||
|
||||
test('should build nodes with attributes', () => {
|
||||
let div = $('div', { class: 'test' });
|
||||
assert.equal(div.className, 'test');
|
||||
assert.strictEqual(div.className, 'test');
|
||||
|
||||
div = $('div', undefined);
|
||||
assert.equal(div.className, '');
|
||||
assert.strictEqual(div.className, '');
|
||||
});
|
||||
|
||||
test('should build nodes with children', () => {
|
||||
let div = $('div', undefined, $('span', { id: 'demospan' }));
|
||||
let firstChild = div.firstChild as HTMLElement;
|
||||
assert.equal(firstChild.tagName, 'SPAN');
|
||||
assert.equal(firstChild.id, 'demospan');
|
||||
assert.strictEqual(firstChild.tagName, 'SPAN');
|
||||
assert.strictEqual(firstChild.id, 'demospan');
|
||||
|
||||
div = $('div', undefined, 'hello');
|
||||
|
||||
assert.equal(div.firstChild && div.firstChild.textContent, 'hello');
|
||||
assert.strictEqual(div.firstChild && div.firstChild.textContent, 'hello');
|
||||
});
|
||||
|
||||
test('should build nodes with text children', () => {
|
||||
let div = $('div', undefined, 'foobar');
|
||||
let firstChild = div.firstChild as HTMLElement;
|
||||
assert.equal(firstChild.tagName, undefined);
|
||||
assert.equal(firstChild.textContent, 'foobar');
|
||||
assert.strictEqual(firstChild.tagName, undefined);
|
||||
assert.strictEqual(firstChild.textContent, 'foobar');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -9,53 +9,53 @@ import { sha1Hex } from 'vs/base/browser/hash';
|
||||
|
||||
suite('Hash', () => {
|
||||
test('string', () => {
|
||||
assert.equal(hash('hello'), hash('hello'));
|
||||
assert.notEqual(hash('hello'), hash('world'));
|
||||
assert.notEqual(hash('hello'), hash('olleh'));
|
||||
assert.notEqual(hash('hello'), hash('Hello'));
|
||||
assert.notEqual(hash('hello'), hash('Hello '));
|
||||
assert.notEqual(hash('h'), hash('H'));
|
||||
assert.notEqual(hash('-'), hash('_'));
|
||||
assert.strictEqual(hash('hello'), hash('hello'));
|
||||
assert.notStrictEqual(hash('hello'), hash('world'));
|
||||
assert.notStrictEqual(hash('hello'), hash('olleh'));
|
||||
assert.notStrictEqual(hash('hello'), hash('Hello'));
|
||||
assert.notStrictEqual(hash('hello'), hash('Hello '));
|
||||
assert.notStrictEqual(hash('h'), hash('H'));
|
||||
assert.notStrictEqual(hash('-'), hash('_'));
|
||||
});
|
||||
|
||||
test('number', () => {
|
||||
assert.equal(hash(1), hash(1));
|
||||
assert.notEqual(hash(0), hash(1));
|
||||
assert.notEqual(hash(1), hash(-1));
|
||||
assert.notEqual(hash(0x12345678), hash(0x123456789));
|
||||
assert.strictEqual(hash(1), hash(1));
|
||||
assert.notStrictEqual(hash(0), hash(1));
|
||||
assert.notStrictEqual(hash(1), hash(-1));
|
||||
assert.notStrictEqual(hash(0x12345678), hash(0x123456789));
|
||||
});
|
||||
|
||||
test('boolean', () => {
|
||||
assert.equal(hash(true), hash(true));
|
||||
assert.notEqual(hash(true), hash(false));
|
||||
assert.strictEqual(hash(true), hash(true));
|
||||
assert.notStrictEqual(hash(true), hash(false));
|
||||
});
|
||||
|
||||
test('array', () => {
|
||||
assert.equal(hash([1, 2, 3]), hash([1, 2, 3]));
|
||||
assert.equal(hash(['foo', 'bar']), hash(['foo', 'bar']));
|
||||
assert.equal(hash([]), hash([]));
|
||||
assert.equal(hash([]), hash(new Array()));
|
||||
assert.notEqual(hash(['foo', 'bar']), hash(['bar', 'foo']));
|
||||
assert.notEqual(hash(['foo', 'bar']), hash(['bar', 'foo', null]));
|
||||
assert.notEqual(hash(['foo', 'bar', null]), hash(['bar', 'foo', null]));
|
||||
assert.notEqual(hash(['foo', 'bar']), hash(['bar', 'foo', undefined]));
|
||||
assert.notEqual(hash(['foo', 'bar', undefined]), hash(['bar', 'foo', undefined]));
|
||||
assert.notEqual(hash(['foo', 'bar', null]), hash(['foo', 'bar', undefined]));
|
||||
assert.strictEqual(hash([1, 2, 3]), hash([1, 2, 3]));
|
||||
assert.strictEqual(hash(['foo', 'bar']), hash(['foo', 'bar']));
|
||||
assert.strictEqual(hash([]), hash([]));
|
||||
assert.strictEqual(hash([]), hash(new Array()));
|
||||
assert.notStrictEqual(hash(['foo', 'bar']), hash(['bar', 'foo']));
|
||||
assert.notStrictEqual(hash(['foo', 'bar']), hash(['bar', 'foo', null]));
|
||||
assert.notStrictEqual(hash(['foo', 'bar', null]), hash(['bar', 'foo', null]));
|
||||
assert.notStrictEqual(hash(['foo', 'bar']), hash(['bar', 'foo', undefined]));
|
||||
assert.notStrictEqual(hash(['foo', 'bar', undefined]), hash(['bar', 'foo', undefined]));
|
||||
assert.notStrictEqual(hash(['foo', 'bar', null]), hash(['foo', 'bar', undefined]));
|
||||
});
|
||||
|
||||
test('object', () => {
|
||||
assert.equal(hash({}), hash({}));
|
||||
assert.equal(hash({}), hash(Object.create(null)));
|
||||
assert.equal(hash({ 'foo': 'bar' }), hash({ 'foo': 'bar' }));
|
||||
assert.equal(hash({ 'foo': 'bar', 'foo2': undefined }), hash({ 'foo2': undefined, 'foo': 'bar' }));
|
||||
assert.notEqual(hash({ 'foo': 'bar' }), hash({ 'foo': 'bar2' }));
|
||||
assert.notEqual(hash({}), hash([]));
|
||||
assert.strictEqual(hash({}), hash({}));
|
||||
assert.strictEqual(hash({}), hash(Object.create(null)));
|
||||
assert.strictEqual(hash({ 'foo': 'bar' }), hash({ 'foo': 'bar' }));
|
||||
assert.strictEqual(hash({ 'foo': 'bar', 'foo2': undefined }), hash({ 'foo2': undefined, 'foo': 'bar' }));
|
||||
assert.notStrictEqual(hash({ 'foo': 'bar' }), hash({ 'foo': 'bar2' }));
|
||||
assert.notStrictEqual(hash({}), hash([]));
|
||||
});
|
||||
|
||||
test('array - unexpected collision', function () {
|
||||
const a = hash([undefined, undefined, undefined, undefined, undefined]);
|
||||
const b = hash([undefined, undefined, 'HHHHHH', [{ line: 0, character: 0 }, { line: 0, character: 0 }], undefined]);
|
||||
assert.notEqual(a, b);
|
||||
assert.notStrictEqual(a, b);
|
||||
});
|
||||
|
||||
test('all different', () => {
|
||||
@@ -65,9 +65,9 @@ suite('Hash', () => {
|
||||
];
|
||||
const hashes: number[] = candidates.map(hash);
|
||||
for (let i = 0; i < hashes.length; i++) {
|
||||
assert.equal(hashes[i], hash(candidates[i])); // verify that repeated invocation returns the same hash
|
||||
assert.strictEqual(hashes[i], hash(candidates[i])); // verify that repeated invocation returns the same hash
|
||||
for (let k = i + 1; k < hashes.length; k++) {
|
||||
assert.notEqual(hashes[i], hashes[k], `Same hash ${hashes[i]} for ${JSON.stringify(candidates[i])} and ${JSON.stringify(candidates[k])}`);
|
||||
assert.notStrictEqual(hashes[i], hashes[k], `Same hash ${hashes[i]} for ${JSON.stringify(candidates[i])} and ${JSON.stringify(candidates[k])}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -79,11 +79,11 @@ suite('Hash', () => {
|
||||
const hash = new StringSHA1();
|
||||
hash.update(str);
|
||||
let actual = hash.digest();
|
||||
assert.equal(actual, expected);
|
||||
assert.strictEqual(actual, expected);
|
||||
|
||||
// Test with crypto.subtle
|
||||
actual = await sha1Hex(str);
|
||||
assert.equal(actual, expected);
|
||||
assert.strictEqual(actual, expected);
|
||||
}
|
||||
|
||||
test('sha1-1', () => {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { HighlightedLabel } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel';
|
||||
|
||||
@@ -13,50 +14,50 @@ suite('HighlightedLabel', () => {
|
||||
});
|
||||
|
||||
test('empty label', function () {
|
||||
assert.equal(label.element.innerHTML, '');
|
||||
assert.strictEqual(label.element.innerHTML, '');
|
||||
});
|
||||
|
||||
test('no decorations', function () {
|
||||
label.set('hello');
|
||||
assert.equal(label.element.innerHTML, '<span>hello</span>');
|
||||
assert.strictEqual(label.element.innerHTML, '<span>hello</span>');
|
||||
});
|
||||
|
||||
test('escape html', function () {
|
||||
label.set('hel<lo');
|
||||
assert.equal(label.element.innerHTML, '<span>hel<lo</span>');
|
||||
assert.strictEqual(label.element.innerHTML, '<span>hel<lo</span>');
|
||||
});
|
||||
|
||||
test('everything highlighted', function () {
|
||||
label.set('hello', [{ start: 0, end: 5 }]);
|
||||
assert.equal(label.element.innerHTML, '<span class="highlight">hello</span>');
|
||||
assert.strictEqual(label.element.innerHTML, '<span class="highlight">hello</span>');
|
||||
});
|
||||
|
||||
test('beginning highlighted', function () {
|
||||
label.set('hellothere', [{ start: 0, end: 5 }]);
|
||||
assert.equal(label.element.innerHTML, '<span class="highlight">hello</span><span>there</span>');
|
||||
assert.strictEqual(label.element.innerHTML, '<span class="highlight">hello</span><span>there</span>');
|
||||
});
|
||||
|
||||
test('ending highlighted', function () {
|
||||
label.set('goodbye', [{ start: 4, end: 7 }]);
|
||||
assert.equal(label.element.innerHTML, '<span>good</span><span class="highlight">bye</span>');
|
||||
assert.strictEqual(label.element.innerHTML, '<span>good</span><span class="highlight">bye</span>');
|
||||
});
|
||||
|
||||
test('middle highlighted', function () {
|
||||
label.set('foobarfoo', [{ start: 3, end: 6 }]);
|
||||
assert.equal(label.element.innerHTML, '<span>foo</span><span class="highlight">bar</span><span>foo</span>');
|
||||
assert.strictEqual(label.element.innerHTML, '<span>foo</span><span class="highlight">bar</span><span>foo</span>');
|
||||
});
|
||||
|
||||
test('escapeNewLines', () => {
|
||||
|
||||
let highlights = [{ start: 0, end: 5 }, { start: 7, end: 9 }, { start: 11, end: 12 }];// before,after,after
|
||||
let escaped = HighlightedLabel.escapeNewLines('ACTION\r\n_TYPE2', highlights);
|
||||
assert.equal(escaped, 'ACTION\u23CE_TYPE2');
|
||||
assert.deepEqual(highlights, [{ start: 0, end: 5 }, { start: 6, end: 8 }, { start: 10, end: 11 }]);
|
||||
assert.strictEqual(escaped, 'ACTION\u23CE_TYPE2');
|
||||
assert.deepStrictEqual(highlights, [{ start: 0, end: 5 }, { start: 6, end: 8 }, { start: 10, end: 11 }]);
|
||||
|
||||
highlights = [{ start: 5, end: 9 }, { start: 11, end: 12 }];//overlap,after
|
||||
escaped = HighlightedLabel.escapeNewLines('ACTION\r\n_TYPE2', highlights);
|
||||
assert.equal(escaped, 'ACTION\u23CE_TYPE2');
|
||||
assert.deepEqual(highlights, [{ start: 5, end: 8 }, { start: 10, end: 11 }]);
|
||||
assert.strictEqual(escaped, 'ACTION\u23CE_TYPE2');
|
||||
assert.deepStrictEqual(highlights, [{ start: 5, end: 8 }, { start: 10, end: 11 }]);
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3,45 +3,45 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { renderLabelWithIcons } from 'vs/base/browser/ui/iconLabel/iconLabels';
|
||||
import * as assert from 'assert';
|
||||
import { renderLabelWithIcons } from 'vs/base/browser/ui/iconLabel/iconLabels';
|
||||
|
||||
suite('renderLabelWithIcons', () => {
|
||||
|
||||
test('no icons', () => {
|
||||
const result = renderLabelWithIcons(' hello World .');
|
||||
|
||||
assert.equal(elementsToString(result), ' hello World .');
|
||||
assert.strictEqual(elementsToString(result), ' hello World .');
|
||||
});
|
||||
|
||||
test('icons only', () => {
|
||||
const result = renderLabelWithIcons('$(alert)');
|
||||
|
||||
assert.equal(elementsToString(result), '<span class="codicon codicon-alert"></span>');
|
||||
assert.strictEqual(elementsToString(result), '<span class="codicon codicon-alert"></span>');
|
||||
});
|
||||
|
||||
test('icon and non-icon strings', () => {
|
||||
const result = renderLabelWithIcons(` $(alert) Unresponsive`);
|
||||
|
||||
assert.equal(elementsToString(result), ' <span class="codicon codicon-alert"></span> Unresponsive');
|
||||
assert.strictEqual(elementsToString(result), ' <span class="codicon codicon-alert"></span> Unresponsive');
|
||||
});
|
||||
|
||||
test('multiple icons', () => {
|
||||
const result = renderLabelWithIcons('$(check)$(error)');
|
||||
|
||||
assert.equal(elementsToString(result), '<span class="codicon codicon-check"></span><span class="codicon codicon-error"></span>');
|
||||
assert.strictEqual(elementsToString(result), '<span class="codicon codicon-check"></span><span class="codicon codicon-error"></span>');
|
||||
});
|
||||
|
||||
test('escaped icons', () => {
|
||||
const result = renderLabelWithIcons('\\$(escaped)');
|
||||
|
||||
assert.equal(elementsToString(result), '$(escaped)');
|
||||
assert.strictEqual(elementsToString(result), '$(escaped)');
|
||||
});
|
||||
|
||||
test('icon with animation', () => {
|
||||
const result = renderLabelWithIcons('$(zip~anim)');
|
||||
|
||||
assert.equal(elementsToString(result), '<span class="codicon codicon-zip codicon-modifier-anim"></span>');
|
||||
assert.strictEqual(elementsToString(result), '<span class="codicon codicon-zip codicon-modifier-anim"></span>');
|
||||
});
|
||||
|
||||
const elementsToString = (elements: Array<HTMLElement | string>): string => {
|
||||
|
||||
@@ -9,20 +9,20 @@ import { layout, LayoutAnchorPosition } from 'vs/base/browser/ui/contextview/con
|
||||
suite('Contextview', function () {
|
||||
|
||||
test('layout', () => {
|
||||
assert.equal(layout(200, 20, { offset: 0, size: 0, position: LayoutAnchorPosition.Before }), 0);
|
||||
assert.equal(layout(200, 20, { offset: 50, size: 0, position: LayoutAnchorPosition.Before }), 50);
|
||||
assert.equal(layout(200, 20, { offset: 200, size: 0, position: LayoutAnchorPosition.Before }), 180);
|
||||
assert.strictEqual(layout(200, 20, { offset: 0, size: 0, position: LayoutAnchorPosition.Before }), 0);
|
||||
assert.strictEqual(layout(200, 20, { offset: 50, size: 0, position: LayoutAnchorPosition.Before }), 50);
|
||||
assert.strictEqual(layout(200, 20, { offset: 200, size: 0, position: LayoutAnchorPosition.Before }), 180);
|
||||
|
||||
assert.equal(layout(200, 20, { offset: 0, size: 0, position: LayoutAnchorPosition.After }), 0);
|
||||
assert.equal(layout(200, 20, { offset: 50, size: 0, position: LayoutAnchorPosition.After }), 30);
|
||||
assert.equal(layout(200, 20, { offset: 200, size: 0, position: LayoutAnchorPosition.After }), 180);
|
||||
assert.strictEqual(layout(200, 20, { offset: 0, size: 0, position: LayoutAnchorPosition.After }), 0);
|
||||
assert.strictEqual(layout(200, 20, { offset: 50, size: 0, position: LayoutAnchorPosition.After }), 30);
|
||||
assert.strictEqual(layout(200, 20, { offset: 200, size: 0, position: LayoutAnchorPosition.After }), 180);
|
||||
|
||||
assert.equal(layout(200, 20, { offset: 0, size: 50, position: LayoutAnchorPosition.Before }), 50);
|
||||
assert.equal(layout(200, 20, { offset: 50, size: 50, position: LayoutAnchorPosition.Before }), 100);
|
||||
assert.equal(layout(200, 20, { offset: 150, size: 50, position: LayoutAnchorPosition.Before }), 130);
|
||||
assert.strictEqual(layout(200, 20, { offset: 0, size: 50, position: LayoutAnchorPosition.Before }), 50);
|
||||
assert.strictEqual(layout(200, 20, { offset: 50, size: 50, position: LayoutAnchorPosition.Before }), 100);
|
||||
assert.strictEqual(layout(200, 20, { offset: 150, size: 50, position: LayoutAnchorPosition.Before }), 130);
|
||||
|
||||
assert.equal(layout(200, 20, { offset: 0, size: 50, position: LayoutAnchorPosition.After }), 50);
|
||||
assert.equal(layout(200, 20, { offset: 50, size: 50, position: LayoutAnchorPosition.After }), 30);
|
||||
assert.equal(layout(200, 20, { offset: 150, size: 50, position: LayoutAnchorPosition.After }), 130);
|
||||
assert.strictEqual(layout(200, 20, { offset: 0, size: 50, position: LayoutAnchorPosition.After }), 50);
|
||||
assert.strictEqual(layout(200, 20, { offset: 50, size: 50, position: LayoutAnchorPosition.After }), 30);
|
||||
assert.strictEqual(layout(200, 20, { offset: 150, size: 50, position: LayoutAnchorPosition.After }), 130);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -807,4 +807,187 @@ suite('Async', () => {
|
||||
assert.strictEqual(deferred.isRejected, true);
|
||||
});
|
||||
});
|
||||
|
||||
suite('Promises.allSettled', () => {
|
||||
test('resolves', async () => {
|
||||
const p1 = Promise.resolve(1);
|
||||
const p2 = async.timeout(1).then(() => 2);
|
||||
const p3 = async.timeout(2).then(() => 3);
|
||||
|
||||
const result = await async.Promises.allSettled<number>([p1, p2, p3]);
|
||||
|
||||
assert.strictEqual(result.length, 3);
|
||||
assert.deepStrictEqual(result[0], { status: 'fulfilled', value: 1 });
|
||||
assert.deepStrictEqual(result[1], { status: 'fulfilled', value: 2 });
|
||||
assert.deepStrictEqual(result[2], { status: 'fulfilled', value: 3 });
|
||||
});
|
||||
|
||||
test('resolves in order', async () => {
|
||||
const p1 = async.timeout(2).then(() => 1);
|
||||
const p2 = async.timeout(1).then(() => 2);
|
||||
const p3 = Promise.resolve(3);
|
||||
|
||||
const result = await async.Promises.allSettled<number>([p1, p2, p3]);
|
||||
|
||||
assert.strictEqual(result.length, 3);
|
||||
assert.deepStrictEqual(result[0], { status: 'fulfilled', value: 1 });
|
||||
assert.deepStrictEqual(result[1], { status: 'fulfilled', value: 2 });
|
||||
assert.deepStrictEqual(result[2], { status: 'fulfilled', value: 3 });
|
||||
});
|
||||
|
||||
test('rejects', async () => {
|
||||
const p1 = Promise.reject(1);
|
||||
|
||||
const p2Error = new Error('2');
|
||||
const p2 = async.timeout(1).then(() => { throw p2Error; });
|
||||
|
||||
const p3Error = new Error('3');
|
||||
const p3 = async.timeout(2).then(() => { throw p3Error; });
|
||||
|
||||
const result = await async.Promises.allSettled<number>([p1, p2, p3]);
|
||||
|
||||
assert.strictEqual(result.length, 3);
|
||||
assert.deepStrictEqual(result[0], { status: 'rejected', reason: 1 });
|
||||
assert.deepStrictEqual(result[1], { status: 'rejected', reason: p2Error });
|
||||
assert.deepStrictEqual(result[2], { status: 'rejected', reason: p3Error });
|
||||
});
|
||||
|
||||
test('rejects in order', async () => {
|
||||
const p1Error = new Error('1');
|
||||
const p1 = async.timeout(2).then(() => { throw p1Error; });
|
||||
|
||||
const p2Error = new Error('2');
|
||||
const p2 = async.timeout(1).then(() => { throw p2Error; });
|
||||
|
||||
const p3 = Promise.reject(3);
|
||||
|
||||
const result = await async.Promises.allSettled<number>([p1, p2, p3]);
|
||||
|
||||
assert.strictEqual(result.length, 3);
|
||||
assert.deepStrictEqual(result[0], { status: 'rejected', reason: p1Error });
|
||||
assert.deepStrictEqual(result[1], { status: 'rejected', reason: p2Error });
|
||||
assert.deepStrictEqual(result[2], { status: 'rejected', reason: 3 });
|
||||
});
|
||||
|
||||
test('resolves & rejects', async () => {
|
||||
const p1 = Promise.resolve(1);
|
||||
const p2Error = new Error('2');
|
||||
const p2 = async.timeout(1).then(() => { throw p2Error; });
|
||||
const p3 = async.timeout(2).then(() => 3);
|
||||
|
||||
const result = await async.Promises.allSettled<number>([p1, p2, p3]);
|
||||
|
||||
assert.strictEqual(result.length, 3);
|
||||
assert.deepStrictEqual(result[0], { status: 'fulfilled', value: 1 });
|
||||
assert.deepStrictEqual(result[1], { status: 'rejected', reason: p2Error });
|
||||
assert.deepStrictEqual(result[2], { status: 'fulfilled', value: 3 });
|
||||
});
|
||||
|
||||
test('resolves & rejects in order', async () => {
|
||||
const p1Error = new Error('2');
|
||||
const p1 = async.timeout(1).then(() => { throw p1Error; });
|
||||
const p2 = async.timeout(2).then(() => 2);
|
||||
const p3 = Promise.resolve(3);
|
||||
|
||||
const result = await async.Promises.allSettled<number>([p1, p2, p3]);
|
||||
|
||||
assert.strictEqual(result.length, 3);
|
||||
assert.deepStrictEqual(result[0], { status: 'rejected', reason: p1Error });
|
||||
assert.deepStrictEqual(result[1], { status: 'fulfilled', value: 2 });
|
||||
assert.deepStrictEqual(result[2], { status: 'fulfilled', value: 3 });
|
||||
});
|
||||
|
||||
test('can empty', async () => {
|
||||
const result = await async.Promises.allSettled<number>([]);
|
||||
|
||||
assert.strictEqual(result.length, 0);
|
||||
});
|
||||
});
|
||||
|
||||
suite('Promises.settled', () => {
|
||||
test('resolves', async () => {
|
||||
const p1 = Promise.resolve(1);
|
||||
const p2 = async.timeout(1).then(() => 2);
|
||||
const p3 = async.timeout(2).then(() => 3);
|
||||
|
||||
const result = await async.Promises.settled<number>([p1, p2, p3]);
|
||||
|
||||
assert.strictEqual(result.length, 3);
|
||||
assert.deepStrictEqual(result[0], 1);
|
||||
assert.deepStrictEqual(result[1], 2);
|
||||
assert.deepStrictEqual(result[2], 3);
|
||||
});
|
||||
|
||||
test('resolves in order', async () => {
|
||||
const p1 = async.timeout(2).then(() => 1);
|
||||
const p2 = async.timeout(1).then(() => 2);
|
||||
const p3 = Promise.resolve(3);
|
||||
|
||||
const result = await async.Promises.settled<number>([p1, p2, p3]);
|
||||
|
||||
assert.strictEqual(result.length, 3);
|
||||
assert.deepStrictEqual(result[0], 1);
|
||||
assert.deepStrictEqual(result[1], 2);
|
||||
assert.deepStrictEqual(result[2], 3);
|
||||
});
|
||||
|
||||
test('rejects with first error but handles all promises (all errors)', async () => {
|
||||
const p1 = Promise.reject(1);
|
||||
|
||||
let p2Handled = false;
|
||||
const p2Error = new Error('2');
|
||||
const p2 = async.timeout(1).then(() => {
|
||||
p2Handled = true;
|
||||
throw p2Error;
|
||||
});
|
||||
|
||||
let p3Handled = false;
|
||||
const p3Error = new Error('3');
|
||||
const p3 = async.timeout(2).then(() => {
|
||||
p3Handled = true;
|
||||
throw p3Error;
|
||||
});
|
||||
|
||||
let error: Error | undefined = undefined;
|
||||
try {
|
||||
await async.Promises.settled<number>([p1, p2, p3]);
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
|
||||
assert.ok(error);
|
||||
assert.notStrictEqual(error, p2Error);
|
||||
assert.notStrictEqual(error, p3Error);
|
||||
assert.ok(p2Handled);
|
||||
assert.ok(p3Handled);
|
||||
});
|
||||
|
||||
test('rejects with first error but handles all promises (1 error)', async () => {
|
||||
const p1 = Promise.resolve(1);
|
||||
|
||||
let p2Handled = false;
|
||||
const p2Error = new Error('2');
|
||||
const p2 = async.timeout(1).then(() => {
|
||||
p2Handled = true;
|
||||
throw p2Error;
|
||||
});
|
||||
|
||||
let p3Handled = false;
|
||||
const p3 = async.timeout(2).then(() => {
|
||||
p3Handled = true;
|
||||
return 3;
|
||||
});
|
||||
|
||||
let error: Error | undefined = undefined;
|
||||
try {
|
||||
await async.Promises.settled<number>([p1, p2, p3]);
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
|
||||
assert.strictEqual(error, p2Error);
|
||||
assert.ok(p2Handled);
|
||||
assert.ok(p3Handled);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import * as assert from 'assert';
|
||||
import { Event, Emitter, EventBufferer, EventMultiplexer, IWaitUntil, PauseableEmitter, AsyncEmitter } from 'vs/base/common/event';
|
||||
import { Event, Emitter, EventBufferer, EventMultiplexer, PauseableEmitter } from 'vs/base/common/event';
|
||||
import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import * as Errors from 'vs/base/common/errors';
|
||||
import { timeout } from 'vs/base/common/async';
|
||||
import { errorHandler, setUnexpectedErrorHandler } from 'vs/base/common/errors';
|
||||
import { AsyncEmitter, IWaitUntil, timeout } from 'vs/base/common/async';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
|
||||
namespace Samples {
|
||||
@@ -57,7 +57,7 @@ suite('Event', function () {
|
||||
// unhook listener
|
||||
subscription.dispose();
|
||||
doc.setText('boo');
|
||||
assert.equal(counter.count, 2);
|
||||
assert.strictEqual(counter.count, 2);
|
||||
});
|
||||
|
||||
|
||||
@@ -80,7 +80,7 @@ suite('Event', function () {
|
||||
subscription.dispose();
|
||||
|
||||
doc.setText('boo');
|
||||
assert.equal(counter.count, 2);
|
||||
assert.strictEqual(counter.count, 2);
|
||||
});
|
||||
|
||||
test('Emitter, store', function () {
|
||||
@@ -100,7 +100,7 @@ suite('Event', function () {
|
||||
subscription.dispose();
|
||||
|
||||
doc.setText('boo');
|
||||
assert.equal(counter.count, 2);
|
||||
assert.strictEqual(counter.count, 2);
|
||||
});
|
||||
|
||||
test('onFirstAdd|onLastRemove', () => {
|
||||
@@ -112,25 +112,25 @@ suite('Event', function () {
|
||||
onLastListenerRemove() { lastCount += 1; }
|
||||
});
|
||||
|
||||
assert.equal(firstCount, 0);
|
||||
assert.equal(lastCount, 0);
|
||||
assert.strictEqual(firstCount, 0);
|
||||
assert.strictEqual(lastCount, 0);
|
||||
|
||||
let subscription = a.event(function () { });
|
||||
assert.equal(firstCount, 1);
|
||||
assert.equal(lastCount, 0);
|
||||
assert.strictEqual(firstCount, 1);
|
||||
assert.strictEqual(lastCount, 0);
|
||||
|
||||
subscription.dispose();
|
||||
assert.equal(firstCount, 1);
|
||||
assert.equal(lastCount, 1);
|
||||
assert.strictEqual(firstCount, 1);
|
||||
assert.strictEqual(lastCount, 1);
|
||||
|
||||
subscription = a.event(function () { });
|
||||
assert.equal(firstCount, 2);
|
||||
assert.equal(lastCount, 1);
|
||||
assert.strictEqual(firstCount, 2);
|
||||
assert.strictEqual(lastCount, 1);
|
||||
});
|
||||
|
||||
test('throwingListener', () => {
|
||||
const origErrorHandler = Errors.errorHandler.getUnexpectedErrorHandler();
|
||||
Errors.setUnexpectedErrorHandler(() => null);
|
||||
const origErrorHandler = errorHandler.getUnexpectedErrorHandler();
|
||||
setUnexpectedErrorHandler(() => null);
|
||||
|
||||
try {
|
||||
let a = new Emitter<undefined>();
|
||||
@@ -143,10 +143,10 @@ suite('Event', function () {
|
||||
hit = true;
|
||||
});
|
||||
a.fire(undefined);
|
||||
assert.equal(hit, true);
|
||||
assert.strictEqual(hit, true);
|
||||
|
||||
} finally {
|
||||
Errors.setUnexpectedErrorHandler(origErrorHandler);
|
||||
setUnexpectedErrorHandler(origErrorHandler);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -162,15 +162,15 @@ suite('Event', function () {
|
||||
let reg2 = emitter.event(listener, context);
|
||||
|
||||
emitter.fire(undefined);
|
||||
assert.equal(counter, 2);
|
||||
assert.strictEqual(counter, 2);
|
||||
|
||||
reg1.dispose();
|
||||
emitter.fire(undefined);
|
||||
assert.equal(counter, 3);
|
||||
assert.strictEqual(counter, 3);
|
||||
|
||||
reg2.dispose();
|
||||
emitter.fire(undefined);
|
||||
assert.equal(counter, 3);
|
||||
assert.strictEqual(counter, 3);
|
||||
});
|
||||
|
||||
test('Debounce Event', function (done: () => void) {
|
||||
@@ -192,9 +192,9 @@ suite('Event', function () {
|
||||
assert.ok(keys, 'was not expecting keys.');
|
||||
if (count === 1) {
|
||||
doc.setText('4');
|
||||
assert.deepEqual(keys, ['1', '2', '3']);
|
||||
assert.deepStrictEqual(keys, ['1', '2', '3']);
|
||||
} else if (count === 2) {
|
||||
assert.deepEqual(keys, ['4']);
|
||||
assert.deepStrictEqual(keys, ['4']);
|
||||
done();
|
||||
}
|
||||
});
|
||||
@@ -217,7 +217,7 @@ suite('Event', function () {
|
||||
emitter.fire();
|
||||
|
||||
await timeout(1);
|
||||
assert.equal(calls, 1);
|
||||
assert.strictEqual(calls, 1);
|
||||
});
|
||||
|
||||
test('Debounce Event - leading', async function () {
|
||||
@@ -234,7 +234,7 @@ suite('Event', function () {
|
||||
emitter.fire();
|
||||
emitter.fire();
|
||||
await timeout(1);
|
||||
assert.equal(calls, 2);
|
||||
assert.strictEqual(calls, 2);
|
||||
});
|
||||
|
||||
test('Debounce Event - leading reset', async function () {
|
||||
@@ -248,7 +248,7 @@ suite('Event', function () {
|
||||
emitter.fire(1);
|
||||
|
||||
await timeout(1);
|
||||
assert.deepEqual(calls, [1, 1]);
|
||||
assert.deepStrictEqual(calls, [1, 1]);
|
||||
});
|
||||
|
||||
test('Emitter - In Order Delivery', function () {
|
||||
@@ -258,7 +258,7 @@ suite('Event', function () {
|
||||
if (event === 'e1') {
|
||||
a.fire('e2');
|
||||
// assert that all events are delivered at this point
|
||||
assert.deepEqual(listener2Events, ['e1', 'e2']);
|
||||
assert.deepStrictEqual(listener2Events, ['e1', 'e2']);
|
||||
}
|
||||
});
|
||||
a.event(function listener2(event) {
|
||||
@@ -267,7 +267,7 @@ suite('Event', function () {
|
||||
a.fire('e1');
|
||||
|
||||
// assert that all events are delivered in order
|
||||
assert.deepEqual(listener2Events, ['e1', 'e2']);
|
||||
assert.deepStrictEqual(listener2Events, ['e1', 'e2']);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -283,9 +283,9 @@ suite('AsyncEmitter', function () {
|
||||
let emitter = new AsyncEmitter<E>();
|
||||
|
||||
emitter.event(e => {
|
||||
assert.equal(e.foo, true);
|
||||
assert.equal(e.bar, 1);
|
||||
assert.equal(typeof e.waitUntil, 'function');
|
||||
assert.strictEqual(e.foo, true);
|
||||
assert.strictEqual(e.bar, 1);
|
||||
assert.strictEqual(typeof e.waitUntil, 'function');
|
||||
});
|
||||
|
||||
emitter.fireAsync({ foo: true, bar: 1, }, CancellationToken.None);
|
||||
@@ -303,20 +303,20 @@ suite('AsyncEmitter', function () {
|
||||
|
||||
emitter.event(e => {
|
||||
e.waitUntil(timeout(10).then(_ => {
|
||||
assert.equal(globalState, 0);
|
||||
assert.strictEqual(globalState, 0);
|
||||
globalState += 1;
|
||||
}));
|
||||
});
|
||||
|
||||
emitter.event(e => {
|
||||
e.waitUntil(timeout(1).then(_ => {
|
||||
assert.equal(globalState, 1);
|
||||
assert.strictEqual(globalState, 1);
|
||||
globalState += 1;
|
||||
}));
|
||||
});
|
||||
|
||||
await emitter.fireAsync({ foo: true }, CancellationToken.None);
|
||||
assert.equal(globalState, 2);
|
||||
assert.strictEqual(globalState, 2);
|
||||
});
|
||||
|
||||
test('sequential, in-order delivery', async function () {
|
||||
@@ -332,7 +332,7 @@ suite('AsyncEmitter', function () {
|
||||
e.waitUntil(timeout(10).then(async _ => {
|
||||
if (e.foo === 1) {
|
||||
await emitter.fireAsync({ foo: 2 }, CancellationToken.None);
|
||||
assert.deepEqual(events, [1, 2]);
|
||||
assert.deepStrictEqual(events, [1, 2]);
|
||||
done = true;
|
||||
}
|
||||
}));
|
||||
@@ -349,8 +349,8 @@ suite('AsyncEmitter', function () {
|
||||
});
|
||||
|
||||
test('catch errors', async function () {
|
||||
const origErrorHandler = Errors.errorHandler.getUnexpectedErrorHandler();
|
||||
Errors.setUnexpectedErrorHandler(() => null);
|
||||
const origErrorHandler = errorHandler.getUnexpectedErrorHandler();
|
||||
setUnexpectedErrorHandler(() => null);
|
||||
|
||||
interface E extends IWaitUntil {
|
||||
foo: boolean;
|
||||
@@ -367,16 +367,17 @@ suite('AsyncEmitter', function () {
|
||||
emitter.event(e => {
|
||||
globalState += 1;
|
||||
e.waitUntil(timeout(10));
|
||||
e.waitUntil(timeout(20).then(() => globalState++)); // multiple `waitUntil` are supported and awaited on
|
||||
});
|
||||
|
||||
await emitter.fireAsync({ foo: true }, CancellationToken.None).then(() => {
|
||||
assert.equal(globalState, 2);
|
||||
assert.strictEqual(globalState, 3);
|
||||
}).catch(e => {
|
||||
console.log(e);
|
||||
assert.ok(false);
|
||||
});
|
||||
|
||||
Errors.setUnexpectedErrorHandler(origErrorHandler);
|
||||
setUnexpectedErrorHandler(origErrorHandler);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -390,7 +391,7 @@ suite('PausableEmitter', function () {
|
||||
emitter.fire(1);
|
||||
emitter.fire(2);
|
||||
|
||||
assert.deepEqual(data, [1, 2]);
|
||||
assert.deepStrictEqual(data, [1, 2]);
|
||||
});
|
||||
|
||||
test('pause/resume - no merge', function () {
|
||||
@@ -400,17 +401,17 @@ suite('PausableEmitter', function () {
|
||||
emitter.event(e => data.push(e));
|
||||
emitter.fire(1);
|
||||
emitter.fire(2);
|
||||
assert.deepEqual(data, [1, 2]);
|
||||
assert.deepStrictEqual(data, [1, 2]);
|
||||
|
||||
emitter.pause();
|
||||
emitter.fire(3);
|
||||
emitter.fire(4);
|
||||
assert.deepEqual(data, [1, 2]);
|
||||
assert.deepStrictEqual(data, [1, 2]);
|
||||
|
||||
emitter.resume();
|
||||
assert.deepEqual(data, [1, 2, 3, 4]);
|
||||
assert.deepStrictEqual(data, [1, 2, 3, 4]);
|
||||
emitter.fire(5);
|
||||
assert.deepEqual(data, [1, 2, 3, 4, 5]);
|
||||
assert.deepStrictEqual(data, [1, 2, 3, 4, 5]);
|
||||
});
|
||||
|
||||
test('pause/resume - merge', function () {
|
||||
@@ -420,18 +421,18 @@ suite('PausableEmitter', function () {
|
||||
emitter.event(e => data.push(e));
|
||||
emitter.fire(1);
|
||||
emitter.fire(2);
|
||||
assert.deepEqual(data, [1, 2]);
|
||||
assert.deepStrictEqual(data, [1, 2]);
|
||||
|
||||
emitter.pause();
|
||||
emitter.fire(3);
|
||||
emitter.fire(4);
|
||||
assert.deepEqual(data, [1, 2]);
|
||||
assert.deepStrictEqual(data, [1, 2]);
|
||||
|
||||
emitter.resume();
|
||||
assert.deepEqual(data, [1, 2, 7]);
|
||||
assert.deepStrictEqual(data, [1, 2, 7]);
|
||||
|
||||
emitter.fire(5);
|
||||
assert.deepEqual(data, [1, 2, 7, 5]);
|
||||
assert.deepStrictEqual(data, [1, 2, 7, 5]);
|
||||
});
|
||||
|
||||
test('double pause/resume', function () {
|
||||
@@ -441,22 +442,22 @@ suite('PausableEmitter', function () {
|
||||
emitter.event(e => data.push(e));
|
||||
emitter.fire(1);
|
||||
emitter.fire(2);
|
||||
assert.deepEqual(data, [1, 2]);
|
||||
assert.deepStrictEqual(data, [1, 2]);
|
||||
|
||||
emitter.pause();
|
||||
emitter.pause();
|
||||
emitter.fire(3);
|
||||
emitter.fire(4);
|
||||
assert.deepEqual(data, [1, 2]);
|
||||
assert.deepStrictEqual(data, [1, 2]);
|
||||
|
||||
emitter.resume();
|
||||
assert.deepEqual(data, [1, 2]);
|
||||
assert.deepStrictEqual(data, [1, 2]);
|
||||
|
||||
emitter.resume();
|
||||
assert.deepEqual(data, [1, 2, 3, 4]);
|
||||
assert.deepStrictEqual(data, [1, 2, 3, 4]);
|
||||
|
||||
emitter.resume();
|
||||
assert.deepEqual(data, [1, 2, 3, 4]);
|
||||
assert.deepStrictEqual(data, [1, 2, 3, 4]);
|
||||
});
|
||||
|
||||
test('resume, no pause', function () {
|
||||
@@ -466,11 +467,11 @@ suite('PausableEmitter', function () {
|
||||
emitter.event(e => data.push(e));
|
||||
emitter.fire(1);
|
||||
emitter.fire(2);
|
||||
assert.deepEqual(data, [1, 2]);
|
||||
assert.deepStrictEqual(data, [1, 2]);
|
||||
|
||||
emitter.resume();
|
||||
emitter.fire(3);
|
||||
assert.deepEqual(data, [1, 2, 3]);
|
||||
assert.deepStrictEqual(data, [1, 2, 3]);
|
||||
});
|
||||
|
||||
test('nested pause', function () {
|
||||
@@ -493,16 +494,16 @@ suite('PausableEmitter', function () {
|
||||
emitter.pause();
|
||||
emitter.fire(1);
|
||||
emitter.fire(2);
|
||||
assert.deepEqual(data, []);
|
||||
assert.deepStrictEqual(data, []);
|
||||
|
||||
emitter.resume();
|
||||
assert.deepEqual(data, [1, 1]); // paused after first event
|
||||
assert.deepStrictEqual(data, [1, 1]); // paused after first event
|
||||
|
||||
emitter.resume();
|
||||
assert.deepEqual(data, [1, 1, 2, 2]); // remaing event delivered
|
||||
assert.deepStrictEqual(data, [1, 1, 2, 2]); // remaing event delivered
|
||||
|
||||
emitter.fire(3);
|
||||
assert.deepEqual(data, [1, 1, 2, 2, 3, 3]);
|
||||
assert.deepStrictEqual(data, [1, 1, 2, 2, 3, 3]);
|
||||
|
||||
});
|
||||
});
|
||||
@@ -518,13 +519,13 @@ suite('Event utils', () => {
|
||||
const event = bufferer.wrapEvent(emitter.event);
|
||||
const listener = event(counter.onEvent, counter);
|
||||
|
||||
assert.equal(counter.count, 0);
|
||||
assert.strictEqual(counter.count, 0);
|
||||
emitter.fire();
|
||||
assert.equal(counter.count, 1);
|
||||
assert.strictEqual(counter.count, 1);
|
||||
emitter.fire();
|
||||
assert.equal(counter.count, 2);
|
||||
assert.strictEqual(counter.count, 2);
|
||||
emitter.fire();
|
||||
assert.equal(counter.count, 3);
|
||||
assert.strictEqual(counter.count, 3);
|
||||
|
||||
listener.dispose();
|
||||
});
|
||||
@@ -536,20 +537,20 @@ suite('Event utils', () => {
|
||||
const event = bufferer.wrapEvent(emitter.event);
|
||||
const listener = event(counter.onEvent, counter);
|
||||
|
||||
assert.equal(counter.count, 0);
|
||||
assert.strictEqual(counter.count, 0);
|
||||
emitter.fire();
|
||||
assert.equal(counter.count, 1);
|
||||
assert.strictEqual(counter.count, 1);
|
||||
|
||||
bufferer.bufferEvents(() => {
|
||||
emitter.fire();
|
||||
assert.equal(counter.count, 1);
|
||||
assert.strictEqual(counter.count, 1);
|
||||
emitter.fire();
|
||||
assert.equal(counter.count, 1);
|
||||
assert.strictEqual(counter.count, 1);
|
||||
});
|
||||
|
||||
assert.equal(counter.count, 3);
|
||||
assert.strictEqual(counter.count, 3);
|
||||
emitter.fire();
|
||||
assert.equal(counter.count, 4);
|
||||
assert.strictEqual(counter.count, 4);
|
||||
|
||||
listener.dispose();
|
||||
});
|
||||
@@ -563,20 +564,20 @@ suite('Event utils', () => {
|
||||
const listener2 = Event.once(emitter.event)(() => counter2++);
|
||||
const listener3 = Event.once(emitter.event)(() => counter3++);
|
||||
|
||||
assert.equal(counter1, 0);
|
||||
assert.equal(counter2, 0);
|
||||
assert.equal(counter3, 0);
|
||||
assert.strictEqual(counter1, 0);
|
||||
assert.strictEqual(counter2, 0);
|
||||
assert.strictEqual(counter3, 0);
|
||||
|
||||
listener3.dispose();
|
||||
emitter.fire();
|
||||
assert.equal(counter1, 1);
|
||||
assert.equal(counter2, 1);
|
||||
assert.equal(counter3, 0);
|
||||
assert.strictEqual(counter1, 1);
|
||||
assert.strictEqual(counter2, 1);
|
||||
assert.strictEqual(counter3, 0);
|
||||
|
||||
emitter.fire();
|
||||
assert.equal(counter1, 2);
|
||||
assert.equal(counter2, 1);
|
||||
assert.equal(counter3, 0);
|
||||
assert.strictEqual(counter1, 2);
|
||||
assert.strictEqual(counter2, 1);
|
||||
assert.strictEqual(counter3, 0);
|
||||
|
||||
listener1.dispose();
|
||||
listener2.dispose();
|
||||
@@ -591,10 +592,10 @@ suite('Event utils', () => {
|
||||
const event = Event.fromPromise(Promise.resolve(null));
|
||||
event(() => count++);
|
||||
|
||||
assert.equal(count, 0);
|
||||
assert.strictEqual(count, 0);
|
||||
|
||||
await timeout(10);
|
||||
assert.equal(count, 1);
|
||||
assert.strictEqual(count, 1);
|
||||
});
|
||||
|
||||
test('should emit when done - setTimeout', async () => {
|
||||
@@ -604,9 +605,9 @@ suite('Event utils', () => {
|
||||
const event = Event.fromPromise(promise);
|
||||
event(() => count++);
|
||||
|
||||
assert.equal(count, 0);
|
||||
assert.strictEqual(count, 0);
|
||||
await promise;
|
||||
assert.equal(count, 1);
|
||||
assert.strictEqual(count, 1);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -646,14 +647,14 @@ suite('Event utils', () => {
|
||||
assert.deepEqual(result, []);
|
||||
|
||||
const listener = bufferedEvent(num => result.push(num));
|
||||
assert.deepEqual(result, [1, 2, 3]);
|
||||
assert.deepStrictEqual(result, [1, 2, 3]);
|
||||
|
||||
emitter.fire(4);
|
||||
assert.deepEqual(result, [1, 2, 3, 4]);
|
||||
assert.deepStrictEqual(result, [1, 2, 3, 4]);
|
||||
|
||||
listener.dispose();
|
||||
emitter.fire(5);
|
||||
assert.deepEqual(result, [1, 2, 3, 4]);
|
||||
assert.deepStrictEqual(result, [1, 2, 3, 4]);
|
||||
});
|
||||
|
||||
test('should buffer events on next tick', async () => {
|
||||
@@ -668,14 +669,14 @@ suite('Event utils', () => {
|
||||
assert.deepEqual(result, []);
|
||||
|
||||
const listener = bufferedEvent(num => result.push(num));
|
||||
assert.deepEqual(result, []);
|
||||
assert.deepStrictEqual(result, []);
|
||||
|
||||
await timeout(10);
|
||||
emitter.fire(4);
|
||||
assert.deepEqual(result, [1, 2, 3, 4]);
|
||||
assert.deepStrictEqual(result, [1, 2, 3, 4]);
|
||||
listener.dispose();
|
||||
emitter.fire(5);
|
||||
assert.deepEqual(result, [1, 2, 3, 4]);
|
||||
assert.deepStrictEqual(result, [1, 2, 3, 4]);
|
||||
});
|
||||
|
||||
test('should fire initial buffer events', () => {
|
||||
@@ -690,7 +691,7 @@ suite('Event utils', () => {
|
||||
assert.deepEqual(result, []);
|
||||
|
||||
bufferedEvent(num => result.push(num));
|
||||
assert.deepEqual(result, [-2, -1, 0, 1, 2, 3]);
|
||||
assert.deepStrictEqual(result, [-2, -1, 0, 1, 2, 3]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -704,10 +705,10 @@ suite('Event utils', () => {
|
||||
const e1 = new Emitter<number>();
|
||||
m.add(e1.event);
|
||||
|
||||
assert.deepEqual(result, []);
|
||||
assert.deepStrictEqual(result, []);
|
||||
|
||||
e1.fire(0);
|
||||
assert.deepEqual(result, [0]);
|
||||
assert.deepStrictEqual(result, [0]);
|
||||
});
|
||||
|
||||
test('multiplexer dispose works', () => {
|
||||
@@ -718,16 +719,16 @@ suite('Event utils', () => {
|
||||
const e1 = new Emitter<number>();
|
||||
m.add(e1.event);
|
||||
|
||||
assert.deepEqual(result, []);
|
||||
assert.deepStrictEqual(result, []);
|
||||
|
||||
e1.fire(0);
|
||||
assert.deepEqual(result, [0]);
|
||||
assert.deepStrictEqual(result, [0]);
|
||||
|
||||
m.dispose();
|
||||
assert.deepEqual(result, [0]);
|
||||
assert.deepStrictEqual(result, [0]);
|
||||
|
||||
e1.fire(0);
|
||||
assert.deepEqual(result, [0]);
|
||||
assert.deepStrictEqual(result, [0]);
|
||||
});
|
||||
|
||||
test('event dispose works', () => {
|
||||
@@ -738,16 +739,16 @@ suite('Event utils', () => {
|
||||
const e1 = new Emitter<number>();
|
||||
m.add(e1.event);
|
||||
|
||||
assert.deepEqual(result, []);
|
||||
assert.deepStrictEqual(result, []);
|
||||
|
||||
e1.fire(0);
|
||||
assert.deepEqual(result, [0]);
|
||||
assert.deepStrictEqual(result, [0]);
|
||||
|
||||
e1.dispose();
|
||||
assert.deepEqual(result, [0]);
|
||||
assert.deepStrictEqual(result, [0]);
|
||||
|
||||
e1.fire(0);
|
||||
assert.deepEqual(result, [0]);
|
||||
assert.deepStrictEqual(result, [0]);
|
||||
});
|
||||
|
||||
test('mutliplexer event dispose works', () => {
|
||||
@@ -758,16 +759,16 @@ suite('Event utils', () => {
|
||||
const e1 = new Emitter<number>();
|
||||
const l1 = m.add(e1.event);
|
||||
|
||||
assert.deepEqual(result, []);
|
||||
assert.deepStrictEqual(result, []);
|
||||
|
||||
e1.fire(0);
|
||||
assert.deepEqual(result, [0]);
|
||||
assert.deepStrictEqual(result, [0]);
|
||||
|
||||
l1.dispose();
|
||||
assert.deepEqual(result, [0]);
|
||||
assert.deepStrictEqual(result, [0]);
|
||||
|
||||
e1.fire(0);
|
||||
assert.deepEqual(result, [0]);
|
||||
assert.deepStrictEqual(result, [0]);
|
||||
});
|
||||
|
||||
test('hot start works', () => {
|
||||
@@ -785,7 +786,7 @@ suite('Event utils', () => {
|
||||
e1.fire(1);
|
||||
e2.fire(2);
|
||||
e3.fire(3);
|
||||
assert.deepEqual(result, [1, 2, 3]);
|
||||
assert.deepStrictEqual(result, [1, 2, 3]);
|
||||
});
|
||||
|
||||
test('cold start works', () => {
|
||||
@@ -804,7 +805,7 @@ suite('Event utils', () => {
|
||||
e1.fire(1);
|
||||
e2.fire(2);
|
||||
e3.fire(3);
|
||||
assert.deepEqual(result, [1, 2, 3]);
|
||||
assert.deepStrictEqual(result, [1, 2, 3]);
|
||||
});
|
||||
|
||||
test('late add works', () => {
|
||||
@@ -825,7 +826,7 @@ suite('Event utils', () => {
|
||||
m.add(e3.event);
|
||||
e3.fire(3);
|
||||
|
||||
assert.deepEqual(result, [1, 2, 3]);
|
||||
assert.deepStrictEqual(result, [1, 2, 3]);
|
||||
});
|
||||
|
||||
test('add dispose works', () => {
|
||||
@@ -845,15 +846,15 @@ suite('Event utils', () => {
|
||||
const e3 = new Emitter<number>();
|
||||
const l3 = m.add(e3.event);
|
||||
e3.fire(3);
|
||||
assert.deepEqual(result, [1, 2, 3]);
|
||||
assert.deepStrictEqual(result, [1, 2, 3]);
|
||||
|
||||
l3.dispose();
|
||||
e3.fire(4);
|
||||
assert.deepEqual(result, [1, 2, 3]);
|
||||
assert.deepStrictEqual(result, [1, 2, 3]);
|
||||
|
||||
e2.fire(4);
|
||||
e1.fire(5);
|
||||
assert.deepEqual(result, [1, 2, 3, 4, 5]);
|
||||
assert.deepStrictEqual(result, [1, 2, 3, 4, 5]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -864,31 +865,31 @@ suite('Event utils', () => {
|
||||
const result: number[] = [];
|
||||
const listener = event(num => result.push(num));
|
||||
|
||||
assert.deepEqual(result, []);
|
||||
assert.deepStrictEqual(result, []);
|
||||
|
||||
emitter.fire(1);
|
||||
assert.deepEqual(result, [1]);
|
||||
assert.deepStrictEqual(result, [1]);
|
||||
|
||||
emitter.fire(2);
|
||||
assert.deepEqual(result, [1, 2]);
|
||||
assert.deepStrictEqual(result, [1, 2]);
|
||||
|
||||
emitter.fire(2);
|
||||
assert.deepEqual(result, [1, 2]);
|
||||
assert.deepStrictEqual(result, [1, 2]);
|
||||
|
||||
emitter.fire(1);
|
||||
assert.deepEqual(result, [1, 2, 1]);
|
||||
assert.deepStrictEqual(result, [1, 2, 1]);
|
||||
|
||||
emitter.fire(1);
|
||||
assert.deepEqual(result, [1, 2, 1]);
|
||||
assert.deepStrictEqual(result, [1, 2, 1]);
|
||||
|
||||
emitter.fire(3);
|
||||
assert.deepEqual(result, [1, 2, 1, 3]);
|
||||
assert.deepStrictEqual(result, [1, 2, 1, 3]);
|
||||
|
||||
emitter.fire(3);
|
||||
assert.deepEqual(result, [1, 2, 1, 3]);
|
||||
assert.deepStrictEqual(result, [1, 2, 1, 3]);
|
||||
|
||||
emitter.fire(3);
|
||||
assert.deepEqual(result, [1, 2, 1, 3]);
|
||||
assert.deepStrictEqual(result, [1, 2, 1, 3]);
|
||||
|
||||
listener.dispose();
|
||||
});
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import * as assert from 'assert';
|
||||
import * as extpath from 'vs/base/common/extpath';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
import { isWindows } from 'vs/base/common/platform';
|
||||
import { CharCode } from 'vs/base/common/charCode';
|
||||
|
||||
suite('Paths', () => {
|
||||
@@ -32,7 +32,7 @@ suite('Paths', () => {
|
||||
assert.strictEqual(extpath.getRoot('file://foo'), '');
|
||||
});
|
||||
|
||||
(!platform.isWindows ? test.skip : test)('isUNC', () => {
|
||||
(!isWindows ? test.skip : test)('isUNC', () => {
|
||||
assert.ok(!extpath.isUNC('foo'));
|
||||
assert.ok(!extpath.isUNC('/foo'));
|
||||
assert.ok(!extpath.isUNC('\\foo'));
|
||||
@@ -51,7 +51,7 @@ suite('Paths', () => {
|
||||
assert.ok(!extpath.isValidBasename('/test.txt'));
|
||||
assert.ok(!extpath.isValidBasename('\\test.txt'));
|
||||
|
||||
if (platform.isWindows) {
|
||||
if (isWindows) {
|
||||
assert.ok(!extpath.isValidBasename('aux'));
|
||||
assert.ok(!extpath.isValidBasename('Aux'));
|
||||
assert.ok(!extpath.isValidBasename('LPT0'));
|
||||
@@ -72,7 +72,7 @@ suite('Paths', () => {
|
||||
});
|
||||
|
||||
test('sanitizeFilePath', () => {
|
||||
if (platform.isWindows) {
|
||||
if (isWindows) {
|
||||
assert.strictEqual(extpath.sanitizeFilePath('.', 'C:\\the\\cwd'), 'C:\\the\\cwd');
|
||||
assert.strictEqual(extpath.sanitizeFilePath('', 'C:\\the\\cwd'), 'C:\\the\\cwd');
|
||||
|
||||
@@ -108,7 +108,7 @@ suite('Paths', () => {
|
||||
});
|
||||
|
||||
test('isRootOrDriveLetter', () => {
|
||||
if (platform.isWindows) {
|
||||
if (isWindows) {
|
||||
assert.ok(extpath.isRootOrDriveLetter('c:'));
|
||||
assert.ok(extpath.isRootOrDriveLetter('D:'));
|
||||
assert.ok(extpath.isRootOrDriveLetter('D:/'));
|
||||
@@ -122,7 +122,7 @@ suite('Paths', () => {
|
||||
});
|
||||
|
||||
test('hasDriveLetter', () => {
|
||||
if (platform.isWindows) {
|
||||
if (isWindows) {
|
||||
assert.ok(extpath.hasDriveLetter('c:'));
|
||||
assert.ok(extpath.hasDriveLetter('D:'));
|
||||
assert.ok(extpath.hasDriveLetter('D:/'));
|
||||
@@ -136,7 +136,7 @@ suite('Paths', () => {
|
||||
});
|
||||
|
||||
test('getDriveLetter', () => {
|
||||
if (platform.isWindows) {
|
||||
if (isWindows) {
|
||||
assert.strictEqual(extpath.getDriveLetter('c:'), 'c');
|
||||
assert.strictEqual(extpath.getDriveLetter('D:'), 'D');
|
||||
assert.strictEqual(extpath.getDriveLetter('D:/'), 'D');
|
||||
|
||||
@@ -4,13 +4,13 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import * as scorer from 'vs/base/common/fuzzyScorer';
|
||||
import { IItemAccessor, FuzzyScore, FuzzyScore2, IItemScore, prepareQuery, scoreFuzzy, scoreFuzzy2, scoreItemFuzzy, compareItemsByFuzzyScore, pieceToQuery } from 'vs/base/common/fuzzyScorer';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { basename, dirname, sep, posix, win32 } from 'vs/base/common/path';
|
||||
import { isWindows } from 'vs/base/common/platform';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
|
||||
class ResourceAccessorClass implements scorer.IItemAccessor<URI> {
|
||||
class ResourceAccessorClass implements IItemAccessor<URI> {
|
||||
|
||||
getItemLabel(resource: URI): string {
|
||||
return basename(resource.fsPath);
|
||||
@@ -27,7 +27,7 @@ class ResourceAccessorClass implements scorer.IItemAccessor<URI> {
|
||||
|
||||
const ResourceAccessor = new ResourceAccessorClass();
|
||||
|
||||
class ResourceWithSlashAccessorClass implements scorer.IItemAccessor<URI> {
|
||||
class ResourceWithSlashAccessorClass implements IItemAccessor<URI> {
|
||||
|
||||
getItemLabel(resource: URI): string {
|
||||
return basename(resource.fsPath);
|
||||
@@ -44,7 +44,7 @@ class ResourceWithSlashAccessorClass implements scorer.IItemAccessor<URI> {
|
||||
|
||||
const ResourceWithSlashAccessor = new ResourceWithSlashAccessorClass();
|
||||
|
||||
class ResourceWithBackslashAccessorClass implements scorer.IItemAccessor<URI> {
|
||||
class ResourceWithBackslashAccessorClass implements IItemAccessor<URI> {
|
||||
|
||||
getItemLabel(resource: URI): string {
|
||||
return basename(resource.fsPath);
|
||||
@@ -61,7 +61,7 @@ class ResourceWithBackslashAccessorClass implements scorer.IItemAccessor<URI> {
|
||||
|
||||
const ResourceWithBackslashAccessor = new ResourceWithBackslashAccessorClass();
|
||||
|
||||
class NullAccessorClass implements scorer.IItemAccessor<URI> {
|
||||
class NullAccessorClass implements IItemAccessor<URI> {
|
||||
|
||||
getItemLabel(resource: URI): string {
|
||||
return undefined!;
|
||||
@@ -76,24 +76,24 @@ class NullAccessorClass implements scorer.IItemAccessor<URI> {
|
||||
}
|
||||
}
|
||||
|
||||
function _doScore(target: string, query: string, fuzzy: boolean): scorer.FuzzyScore {
|
||||
const preparedQuery = scorer.prepareQuery(query);
|
||||
function _doScore(target: string, query: string, fuzzy: boolean): FuzzyScore {
|
||||
const preparedQuery = prepareQuery(query);
|
||||
|
||||
return scorer.scoreFuzzy(target, preparedQuery.normalized, preparedQuery.normalizedLowercase, fuzzy);
|
||||
return scoreFuzzy(target, preparedQuery.normalized, preparedQuery.normalizedLowercase, fuzzy);
|
||||
}
|
||||
|
||||
function _doScore2(target: string, query: string, matchOffset: number = 0): scorer.FuzzyScore2 {
|
||||
const preparedQuery = scorer.prepareQuery(query);
|
||||
function _doScore2(target: string, query: string, matchOffset: number = 0): FuzzyScore2 {
|
||||
const preparedQuery = prepareQuery(query);
|
||||
|
||||
return scorer.scoreFuzzy2(target, preparedQuery, 0, matchOffset);
|
||||
return scoreFuzzy2(target, preparedQuery, 0, matchOffset);
|
||||
}
|
||||
|
||||
function scoreItem<T>(item: T, query: string, fuzzy: boolean, accessor: scorer.IItemAccessor<T>): scorer.IItemScore {
|
||||
return scorer.scoreItemFuzzy(item, scorer.prepareQuery(query), fuzzy, accessor, Object.create(null));
|
||||
function scoreItem<T>(item: T, query: string, fuzzy: boolean, accessor: IItemAccessor<T>): IItemScore {
|
||||
return scoreItemFuzzy(item, prepareQuery(query), fuzzy, accessor, Object.create(null));
|
||||
}
|
||||
|
||||
function compareItemsByScore<T>(itemA: T, itemB: T, query: string, fuzzy: boolean, accessor: scorer.IItemAccessor<T>): number {
|
||||
return scorer.compareItemsByFuzzyScore(itemA, itemB, scorer.prepareQuery(query), fuzzy, accessor, Object.create(null));
|
||||
function compareItemsByScore<T>(itemA: T, itemB: T, query: string, fuzzy: boolean, accessor: IItemAccessor<T>): number {
|
||||
return compareItemsByFuzzyScore(itemA, itemB, prepareQuery(query), fuzzy, accessor, Object.create(null));
|
||||
}
|
||||
|
||||
const NullAccessor = new NullAccessorClass();
|
||||
@@ -103,7 +103,7 @@ suite('Fuzzy Scorer', () => {
|
||||
test('score (fuzzy)', function () {
|
||||
const target = 'HeLlo-World';
|
||||
|
||||
const scores: scorer.FuzzyScore[] = [];
|
||||
const scores: FuzzyScore[] = [];
|
||||
scores.push(_doScore(target, 'HelLo-World', true)); // direct case match
|
||||
scores.push(_doScore(target, 'hello-world', true)); // direct mix-case match
|
||||
scores.push(_doScore(target, 'HW', true)); // direct case prefix (multiple)
|
||||
@@ -1071,16 +1071,16 @@ suite('Fuzzy Scorer', () => {
|
||||
});
|
||||
|
||||
test('prepareQuery', () => {
|
||||
assert.strictEqual(scorer.prepareQuery(' f*a ').normalized, 'fa');
|
||||
assert.strictEqual(scorer.prepareQuery('model Tester.ts').original, 'model Tester.ts');
|
||||
assert.strictEqual(scorer.prepareQuery('model Tester.ts').originalLowercase, 'model Tester.ts'.toLowerCase());
|
||||
assert.strictEqual(scorer.prepareQuery('model Tester.ts').normalized, 'modelTester.ts');
|
||||
assert.strictEqual(scorer.prepareQuery('Model Tester.ts').normalizedLowercase, 'modeltester.ts');
|
||||
assert.strictEqual(scorer.prepareQuery('ModelTester.ts').containsPathSeparator, false);
|
||||
assert.strictEqual(scorer.prepareQuery('Model' + sep + 'Tester.ts').containsPathSeparator, true);
|
||||
assert.strictEqual(prepareQuery(' f*a ').normalized, 'fa');
|
||||
assert.strictEqual(prepareQuery('model Tester.ts').original, 'model Tester.ts');
|
||||
assert.strictEqual(prepareQuery('model Tester.ts').originalLowercase, 'model Tester.ts'.toLowerCase());
|
||||
assert.strictEqual(prepareQuery('model Tester.ts').normalized, 'modelTester.ts');
|
||||
assert.strictEqual(prepareQuery('Model Tester.ts').normalizedLowercase, 'modeltester.ts');
|
||||
assert.strictEqual(prepareQuery('ModelTester.ts').containsPathSeparator, false);
|
||||
assert.strictEqual(prepareQuery('Model' + sep + 'Tester.ts').containsPathSeparator, true);
|
||||
|
||||
// with spaces
|
||||
let query = scorer.prepareQuery('He*llo World');
|
||||
let query = prepareQuery('He*llo World');
|
||||
assert.strictEqual(query.original, 'He*llo World');
|
||||
assert.strictEqual(query.normalized, 'HelloWorld');
|
||||
assert.strictEqual(query.normalizedLowercase, 'HelloWorld'.toLowerCase());
|
||||
@@ -1092,13 +1092,13 @@ suite('Fuzzy Scorer', () => {
|
||||
assert.strictEqual(query.values?.[1].normalized, 'World');
|
||||
assert.strictEqual(query.values?.[1].normalizedLowercase, 'World'.toLowerCase());
|
||||
|
||||
let restoredQuery = scorer.pieceToQuery(query.values!);
|
||||
let restoredQuery = pieceToQuery(query.values!);
|
||||
assert.strictEqual(restoredQuery.original, query.original);
|
||||
assert.strictEqual(restoredQuery.values?.length, query.values?.length);
|
||||
assert.strictEqual(restoredQuery.containsPathSeparator, query.containsPathSeparator);
|
||||
|
||||
// with spaces that are empty
|
||||
query = scorer.prepareQuery(' Hello World ');
|
||||
query = prepareQuery(' Hello World ');
|
||||
assert.strictEqual(query.original, ' Hello World ');
|
||||
assert.strictEqual(query.originalLowercase, ' Hello World '.toLowerCase());
|
||||
assert.strictEqual(query.normalized, 'HelloWorld');
|
||||
@@ -1115,19 +1115,19 @@ suite('Fuzzy Scorer', () => {
|
||||
|
||||
// Path related
|
||||
if (isWindows) {
|
||||
assert.strictEqual(scorer.prepareQuery('C:\\some\\path').pathNormalized, 'C:\\some\\path');
|
||||
assert.strictEqual(scorer.prepareQuery('C:\\some\\path').normalized, 'C:\\some\\path');
|
||||
assert.strictEqual(scorer.prepareQuery('C:\\some\\path').containsPathSeparator, true);
|
||||
assert.strictEqual(scorer.prepareQuery('C:/some/path').pathNormalized, 'C:\\some\\path');
|
||||
assert.strictEqual(scorer.prepareQuery('C:/some/path').normalized, 'C:\\some\\path');
|
||||
assert.strictEqual(scorer.prepareQuery('C:/some/path').containsPathSeparator, true);
|
||||
assert.strictEqual(prepareQuery('C:\\some\\path').pathNormalized, 'C:\\some\\path');
|
||||
assert.strictEqual(prepareQuery('C:\\some\\path').normalized, 'C:\\some\\path');
|
||||
assert.strictEqual(prepareQuery('C:\\some\\path').containsPathSeparator, true);
|
||||
assert.strictEqual(prepareQuery('C:/some/path').pathNormalized, 'C:\\some\\path');
|
||||
assert.strictEqual(prepareQuery('C:/some/path').normalized, 'C:\\some\\path');
|
||||
assert.strictEqual(prepareQuery('C:/some/path').containsPathSeparator, true);
|
||||
} else {
|
||||
assert.strictEqual(scorer.prepareQuery('/some/path').pathNormalized, '/some/path');
|
||||
assert.strictEqual(scorer.prepareQuery('/some/path').normalized, '/some/path');
|
||||
assert.strictEqual(scorer.prepareQuery('/some/path').containsPathSeparator, true);
|
||||
assert.strictEqual(scorer.prepareQuery('\\some\\path').pathNormalized, '/some/path');
|
||||
assert.strictEqual(scorer.prepareQuery('\\some\\path').normalized, '/some/path');
|
||||
assert.strictEqual(scorer.prepareQuery('\\some\\path').containsPathSeparator, true);
|
||||
assert.strictEqual(prepareQuery('/some/path').pathNormalized, '/some/path');
|
||||
assert.strictEqual(prepareQuery('/some/path').normalized, '/some/path');
|
||||
assert.strictEqual(prepareQuery('/some/path').containsPathSeparator, true);
|
||||
assert.strictEqual(prepareQuery('\\some\\path').pathNormalized, '/some/path');
|
||||
assert.strictEqual(prepareQuery('\\some\\path').normalized, '/some/path');
|
||||
assert.strictEqual(prepareQuery('\\some\\path').containsPathSeparator, true);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -2,9 +2,10 @@
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import * as path from 'vs/base/common/path';
|
||||
import * as glob from 'vs/base/common/glob';
|
||||
import { sep } from 'vs/base/common/path';
|
||||
import { isWindows } from 'vs/base/common/platform';
|
||||
|
||||
suite('Glob', () => {
|
||||
@@ -952,7 +953,7 @@ suite('Glob', () => {
|
||||
}
|
||||
|
||||
function nativeSep(slashPath: string): string {
|
||||
return slashPath.replace(/\//g, path.sep);
|
||||
return slashPath.replace(/\//g, sep);
|
||||
}
|
||||
|
||||
test('relative pattern - glob star', function () {
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { IMatch } from 'vs/base/common/filters';
|
||||
import { matchesFuzzyIconAware, parseLabelWithIcons, IParsedLabelWithIcons, stripIcons } from 'vs/base/common/iconLabels';
|
||||
import { matchesFuzzyIconAware, parseLabelWithIcons, IParsedLabelWithIcons, stripIcons, escapeIcons, markdownEscapeEscapedIcons } from 'vs/base/common/iconLabels';
|
||||
|
||||
export interface IIconFilter {
|
||||
// Returns null if word doesn't match.
|
||||
@@ -66,9 +66,23 @@ suite('Icon Labels', () => {
|
||||
});
|
||||
|
||||
test('stripIcons', () => {
|
||||
assert.equal(stripIcons('Hello World'), 'Hello World');
|
||||
assert.equal(stripIcons('$(Hello World'), '$(Hello World');
|
||||
assert.equal(stripIcons('$(Hello) World'), ' World');
|
||||
assert.equal(stripIcons('$(Hello) W$(oi)rld'), ' Wrld');
|
||||
assert.strictEqual(stripIcons('Hello World'), 'Hello World');
|
||||
assert.strictEqual(stripIcons('$(Hello World'), '$(Hello World');
|
||||
assert.strictEqual(stripIcons('$(Hello) World'), ' World');
|
||||
assert.strictEqual(stripIcons('$(Hello) W$(oi)rld'), ' Wrld');
|
||||
});
|
||||
|
||||
|
||||
test('escapeIcons', () => {
|
||||
assert.strictEqual(escapeIcons('Hello World'), 'Hello World');
|
||||
assert.strictEqual(escapeIcons('$(Hello World'), '$(Hello World');
|
||||
assert.strictEqual(escapeIcons('$(Hello) World'), '\\$(Hello) World');
|
||||
assert.strictEqual(escapeIcons('\\$(Hello) W$(oi)rld'), '\\$(Hello) W\\$(oi)rld');
|
||||
});
|
||||
|
||||
test('markdownEscapeEscapedIcons', () => {
|
||||
assert.strictEqual(markdownEscapeEscapedIcons('Hello World'), 'Hello World');
|
||||
assert.strictEqual(markdownEscapeEscapedIcons('$(Hello) World'), '$(Hello) World');
|
||||
assert.strictEqual(markdownEscapeEscapedIcons('\\$(Hello) World'), '\\\\$(Hello) World');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -25,4 +25,11 @@ suite('Iterable', function () {
|
||||
assert.equal(Iterable.first(customIterable), 'one'); // fresh
|
||||
});
|
||||
|
||||
test('equals', () => {
|
||||
assert.strictEqual(Iterable.equals([1, 2], [1, 2]), true);
|
||||
assert.strictEqual(Iterable.equals([1, 2], [1]), false);
|
||||
assert.strictEqual(Iterable.equals([1], [1, 2]), false);
|
||||
assert.strictEqual(Iterable.equals([2, 1], [1, 2]), false);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user