Merge commit 'be3e8236086165e5e45a5a10783823874b3f3ebd' as 'lib/vscode'

This commit is contained in:
Joe Previte
2020-12-15 15:52:33 -07:00
4649 changed files with 1311795 additions and 0 deletions

View File

@@ -0,0 +1,104 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as path from 'vs/base/common/path';
import * as pfs from 'vs/base/node/pfs';
import { IStringDictionary } from 'vs/base/common/collections';
import product from 'vs/platform/product/common/product';
import { Disposable, toDisposable } from 'vs/base/common/lifecycle';
import { onUnexpectedError } from 'vs/base/common/errors';
import { ILogService } from 'vs/platform/log/common/log';
import { INativeEnvironmentService } from 'vs/platform/environment/common/environment';
interface ExtensionEntry {
version: string;
extensionIdentifier: {
id: string;
uuid: string;
};
}
interface LanguagePackEntry {
hash: string;
extensions: ExtensionEntry[];
}
interface LanguagePackFile {
[locale: string]: LanguagePackEntry;
}
export class LanguagePackCachedDataCleaner extends Disposable {
constructor(
@INativeEnvironmentService private readonly _environmentService: INativeEnvironmentService,
@ILogService private readonly _logService: ILogService
) {
super();
// We have no Language pack support for dev version (run from source)
// So only cleanup when we have a build version.
if (this._environmentService.isBuilt) {
this._manageCachedDataSoon();
}
}
private _manageCachedDataSoon(): void {
let handle: any = setTimeout(async () => {
handle = undefined;
this._logService.info('Starting to clean up unused language packs.');
const maxAge = product.nameLong.indexOf('Insiders') >= 0
? 1000 * 60 * 60 * 24 * 7 // roughly 1 week
: 1000 * 60 * 60 * 24 * 30 * 3; // roughly 3 months
try {
const installed: IStringDictionary<boolean> = Object.create(null);
const metaData: LanguagePackFile = JSON.parse(await pfs.readFile(path.join(this._environmentService.userDataPath, 'languagepacks.json'), 'utf8'));
for (let locale of Object.keys(metaData)) {
const entry = metaData[locale];
installed[`${entry.hash}.${locale}`] = true;
}
// Cleanup entries for language packs that aren't installed anymore
const cacheDir = path.join(this._environmentService.userDataPath, 'clp');
const exists = await pfs.exists(cacheDir);
if (!exists) {
return;
}
for (let entry of await pfs.readdir(cacheDir)) {
if (installed[entry]) {
this._logService.info(`Skipping directory ${entry}. Language pack still in use.`);
continue;
}
this._logService.info('Removing unused language pack:', entry);
await pfs.rimraf(path.join(cacheDir, entry));
}
const now = Date.now();
for (let packEntry of Object.keys(installed)) {
const folder = path.join(cacheDir, packEntry);
for (let entry of await pfs.readdir(folder)) {
if (entry === 'tcf.json') {
continue;
}
const candidate = path.join(folder, entry);
const stat = await pfs.stat(candidate);
if (stat.isDirectory()) {
const diff = now - stat.mtime.getTime();
if (diff > maxAge) {
this._logService.info('Removing language pack cache entry: ', path.join(packEntry, entry));
await pfs.rimraf(candidate);
}
}
}
}
} catch (error) {
onUnexpectedError(error);
}
}, 40 * 1000);
this._register(toDisposable(() => {
if (handle !== undefined) {
clearTimeout(handle);
}
}));
}
}

View File

@@ -0,0 +1,45 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { join, dirname, basename } from 'vs/base/common/path';
import { readdir, rimraf } from 'vs/base/node/pfs';
import { onUnexpectedError } from 'vs/base/common/errors';
import { Disposable, toDisposable } from 'vs/base/common/lifecycle';
export class LogsDataCleaner extends Disposable {
constructor(
@IEnvironmentService private readonly environmentService: IEnvironmentService
) {
super();
this.cleanUpOldLogsSoon();
}
private cleanUpOldLogsSoon(): void {
let handle: NodeJS.Timeout | undefined = setTimeout(() => {
handle = undefined;
const currentLog = basename(this.environmentService.logsPath);
const logsRoot = dirname(this.environmentService.logsPath);
readdir(logsRoot).then(children => {
const allSessions = children.filter(name => /^\d{8}T\d{6}$/.test(name));
const oldSessions = allSessions.sort().filter((d, i) => d !== currentLog);
const toDelete = oldSessions.slice(0, Math.max(0, oldSessions.length - 9));
return Promise.all(toDelete.map(name => rimraf(join(logsRoot, name))));
}).then(null, onUnexpectedError);
}, 10 * 1000);
this._register(toDisposable(() => {
if (handle) {
clearTimeout(handle);
handle = undefined;
}
}));
}
}

View File

@@ -0,0 +1,85 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { basename, dirname, join } from 'vs/base/common/path';
import { onUnexpectedError } from 'vs/base/common/errors';
import { toDisposable, DisposableStore } from 'vs/base/common/lifecycle';
import { readdir, rimraf, stat } from 'vs/base/node/pfs';
import product from 'vs/platform/product/common/product';
export class NodeCachedDataCleaner {
private static readonly _DataMaxAge = product.nameLong.indexOf('Insiders') >= 0
? 1000 * 60 * 60 * 24 * 7 // roughly 1 week
: 1000 * 60 * 60 * 24 * 30 * 3; // roughly 3 months
private readonly _disposables = new DisposableStore();
constructor(
private readonly nodeCachedDataDir: string | undefined
) {
this._manageCachedDataSoon();
}
dispose(): void {
this._disposables.dispose();
}
private _manageCachedDataSoon(): void {
// Cached data is stored as user data and we run a cleanup task everytime
// the editor starts. The strategy is to delete all files that are older than
// 3 months (1 week respectively)
if (!this.nodeCachedDataDir) {
return;
}
// The folder which contains folders of cached data. Each of these folder is per
// version
const nodeCachedDataRootDir = dirname(this.nodeCachedDataDir);
const nodeCachedDataCurrent = basename(this.nodeCachedDataDir);
let handle: NodeJS.Timeout | undefined = setTimeout(() => {
handle = undefined;
readdir(nodeCachedDataRootDir).then(entries => {
const now = Date.now();
const deletes: Promise<unknown>[] = [];
entries.forEach(entry => {
// name check
// * not the current cached data folder
if (entry !== nodeCachedDataCurrent) {
const path = join(nodeCachedDataRootDir, entry);
deletes.push(stat(path).then(stats => {
// stat check
// * only directories
// * only when old enough
if (stats.isDirectory()) {
const diff = now - stats.mtime.getTime();
if (diff > NodeCachedDataCleaner._DataMaxAge) {
return rimraf(path);
}
}
return undefined;
}));
}
});
return Promise.all(deletes);
}).then(undefined, onUnexpectedError);
}, 30 * 1000);
this._disposables.add(toDisposable(() => {
if (handle) {
clearTimeout(handle);
handle = undefined;
}
}));
}
}

View File

@@ -0,0 +1,68 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { INativeEnvironmentService } from 'vs/platform/environment/common/environment';
import { join } from 'vs/base/common/path';
import { readdir, readFile, rimraf } from 'vs/base/node/pfs';
import { onUnexpectedError } from 'vs/base/common/errors';
import { Disposable, toDisposable } from 'vs/base/common/lifecycle';
import { IBackupWorkspacesFormat } from 'vs/platform/backup/node/backup';
export class StorageDataCleaner extends Disposable {
// Workspace/Folder storage names are MD5 hashes (128bits / 4 due to hex presentation)
private static readonly NON_EMPTY_WORKSPACE_ID_LENGTH = 128 / 4;
constructor(
private readonly backupWorkspacesPath: string,
@INativeEnvironmentService private readonly environmentService: INativeEnvironmentService
) {
super();
this.cleanUpStorageSoon();
}
private cleanUpStorageSoon(): void {
let handle: NodeJS.Timeout | undefined = setTimeout(() => {
handle = undefined;
(async () => {
try {
// Leverage the backup workspace file to find out which empty workspace is currently in use to
// determine which empty workspace storage can safely be deleted
const contents = await readFile(this.backupWorkspacesPath, 'utf8');
const workspaces = JSON.parse(contents) as IBackupWorkspacesFormat;
const emptyWorkspaces = workspaces.emptyWorkspaceInfos.map(info => info.backupFolder);
// Read all workspace storage folders that exist
const storageFolders = await readdir(this.environmentService.workspaceStorageHome.fsPath);
const deletes: Promise<void>[] = [];
storageFolders.forEach(storageFolder => {
if (storageFolder.length === StorageDataCleaner.NON_EMPTY_WORKSPACE_ID_LENGTH) {
return;
}
if (emptyWorkspaces.indexOf(storageFolder) === -1) {
deletes.push(rimraf(join(this.environmentService.workspaceStorageHome.fsPath, storageFolder)));
}
});
await Promise.all(deletes);
} catch (error) {
onUnexpectedError(error);
}
})();
}, 30 * 1000);
this._register(toDisposable(() => {
if (handle) {
clearTimeout(handle);
handle = undefined;
}
}));
}
}