mirror of
https://github.com/coder/code-server.git
synced 2026-05-15 08:47:26 +02:00
Merge commit 'be3e8236086165e5e45a5a10783823874b3f3ebd' as 'lib/vscode'
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<!-- Copyright (C) Microsoft Corporation. All rights reserved. -->
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'self'; connect-src 'self' https:;">
|
||||
</head>
|
||||
|
||||
<body aria-label="">
|
||||
Shared Process
|
||||
</body>
|
||||
|
||||
<!-- Init Bootstrap Helpers -->
|
||||
<script src="../../../../bootstrap.js"></script>
|
||||
<script src="../../../../vs/loader.js"></script>
|
||||
<script src="../../../../bootstrap-window.js"></script>
|
||||
|
||||
<!-- Startup via sharedProcess.js -->
|
||||
<script src="sharedProcess.js"></script>
|
||||
|
||||
</html>
|
||||
@@ -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.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
//@ts-check
|
||||
'use strict';
|
||||
|
||||
(function () {
|
||||
const bootstrap = bootstrapLib();
|
||||
const bootstrapWindow = bootstrapWindowLib();
|
||||
|
||||
// Avoid Monkey Patches from Application Insights
|
||||
bootstrap.avoidMonkeyPatchFromAppInsights();
|
||||
|
||||
// Load shared process into window
|
||||
bootstrapWindow.load(['vs/code/electron-browser/sharedProcess/sharedProcessMain'], function (sharedProcess, configuration) {
|
||||
sharedProcess.startup({
|
||||
machineId: configuration.machineId,
|
||||
windowId: configuration.windowId
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
//#region Globals
|
||||
|
||||
/**
|
||||
* @returns {{ avoidMonkeyPatchFromAppInsights: () => void; }}
|
||||
*/
|
||||
function bootstrapLib() {
|
||||
// @ts-ignore (defined in bootstrap.js)
|
||||
return window.MonacoBootstrap;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {{ load: (modules: string[], resultCallback: (result, configuration: object) => any, options?: object) => unknown }}
|
||||
*/
|
||||
function bootstrapWindowLib() {
|
||||
// @ts-ignore (defined in bootstrap-window.js)
|
||||
return window.MonacoBootstrapWindow;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
}());
|
||||
@@ -0,0 +1,334 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as fs from 'fs';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
import product from 'vs/platform/product/common/product';
|
||||
import { serve, Server, connect } from 'vs/base/parts/ipc/node/ipc.net';
|
||||
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
|
||||
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
|
||||
import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService';
|
||||
import { IEnvironmentService, INativeEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { NativeParsedArgs } from 'vs/platform/environment/common/argv';
|
||||
import { NativeEnvironmentService } from 'vs/platform/environment/node/environmentService';
|
||||
import { ExtensionManagementChannel, ExtensionTipsChannel } from 'vs/platform/extensionManagement/common/extensionManagementIpc';
|
||||
import { IExtensionManagementService, IExtensionGalleryService, IGlobalExtensionEnablementService, IExtensionTipsService } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService';
|
||||
import { ExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionGalleryService';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { ConfigurationService } from 'vs/platform/configuration/common/configurationService';
|
||||
import { IRequestService } from 'vs/platform/request/common/request';
|
||||
import { RequestService } from 'vs/platform/request/browser/requestService';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { combinedAppender, NullTelemetryService, ITelemetryAppender, NullAppender } from 'vs/platform/telemetry/common/telemetryUtils';
|
||||
import { resolveCommonProperties } from 'vs/platform/telemetry/node/commonProperties';
|
||||
import { TelemetryAppenderChannel } from 'vs/platform/telemetry/node/telemetryIpc';
|
||||
import { TelemetryService, ITelemetryServiceConfig } from 'vs/platform/telemetry/common/telemetryService';
|
||||
import { AppInsightsAppender } from 'vs/platform/telemetry/node/appInsightsAppender';
|
||||
import { ipcRenderer } from 'vs/base/parts/sandbox/electron-sandbox/globals';
|
||||
import { ILogService, LogLevel, ILoggerService } from 'vs/platform/log/common/log';
|
||||
import { LoggerChannelClient, FollowerLogService } from 'vs/platform/log/common/logIpc';
|
||||
import { LocalizationsService } from 'vs/platform/localizations/node/localizations';
|
||||
import { ILocalizationsService } from 'vs/platform/localizations/common/localizations';
|
||||
import { combinedDisposable, DisposableStore, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { DownloadService } from 'vs/platform/download/common/downloadService';
|
||||
import { IDownloadService } from 'vs/platform/download/common/download';
|
||||
import { IChannel, IServerChannel, StaticRouter, createChannelSender, createChannelReceiver } from 'vs/base/parts/ipc/common/ipc';
|
||||
import { NodeCachedDataCleaner } from 'vs/code/electron-browser/sharedProcess/contrib/nodeCachedDataCleaner';
|
||||
import { LanguagePackCachedDataCleaner } from 'vs/code/electron-browser/sharedProcess/contrib/languagePackCachedDataCleaner';
|
||||
import { StorageDataCleaner } from 'vs/code/electron-browser/sharedProcess/contrib/storageDataCleaner';
|
||||
import { LogsDataCleaner } from 'vs/code/electron-browser/sharedProcess/contrib/logsDataCleaner';
|
||||
import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/mainProcessService';
|
||||
import { SpdLogService } from 'vs/platform/log/node/spdlogService';
|
||||
import { DiagnosticsService, IDiagnosticsService } from 'vs/platform/diagnostics/node/diagnosticsService';
|
||||
import { FileService } from 'vs/platform/files/common/fileService';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemProvider';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { IProductService } from 'vs/platform/product/common/productService';
|
||||
import { IUserDataSyncService, IUserDataSyncStoreService, registerConfiguration, IUserDataSyncLogService, IUserDataSyncUtilService, IUserDataSyncResourceEnablementService, IUserDataSyncBackupStoreService, IUserDataSyncStoreManagementService, IUserDataAutoSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSync';
|
||||
import { UserDataSyncService } from 'vs/platform/userDataSync/common/userDataSyncService';
|
||||
import { UserDataSyncStoreService, UserDataSyncStoreManagementService } from 'vs/platform/userDataSync/common/userDataSyncStoreService';
|
||||
import { UserDataSyncChannel, UserDataSyncUtilServiceClient, UserDataAutoSyncChannel, UserDataSyncMachinesServiceChannel, UserDataSyncAccountServiceChannel, UserDataSyncStoreManagementServiceChannel, StorageKeysSyncRegistryChannel } from 'vs/platform/userDataSync/common/userDataSyncIpc';
|
||||
import { INativeHostService } from 'vs/platform/native/electron-sandbox/native';
|
||||
import { LoggerService } from 'vs/platform/log/node/loggerService';
|
||||
import { UserDataSyncLogService } from 'vs/platform/userDataSync/common/userDataSyncLog';
|
||||
import { UserDataAutoSyncService } from 'vs/platform/userDataSync/electron-sandbox/userDataAutoSyncService';
|
||||
import { NativeStorageService } from 'vs/platform/storage/node/storageService';
|
||||
import { GlobalStorageDatabaseChannelClient } from 'vs/platform/storage/node/storageIpc';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { GlobalExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionEnablementService';
|
||||
import { UserDataSyncResourceEnablementService } from 'vs/platform/userDataSync/common/userDataSyncResourceEnablementService';
|
||||
import { IUserDataSyncAccountService, UserDataSyncAccountService } from 'vs/platform/userDataSync/common/userDataSyncAccount';
|
||||
import { UserDataSyncBackupStoreService } from 'vs/platform/userDataSync/common/userDataSyncBackupStoreService';
|
||||
import { IStorageKeysSyncRegistryService, StorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys';
|
||||
import { ExtensionTipsService } from 'vs/platform/extensionManagement/electron-sandbox/extensionTipsService';
|
||||
import { UserDataSyncMachinesService, IUserDataSyncMachinesService } from 'vs/platform/userDataSync/common/userDataSyncMachines';
|
||||
import { IExtensionRecommendationNotificationService } from 'vs/platform/extensionRecommendations/common/extensionRecommendations';
|
||||
import { ExtensionRecommendationNotificationServiceChannelClient } from 'vs/platform/extensionRecommendations/electron-sandbox/extensionRecommendationsIpc';
|
||||
import { ActiveWindowManager } from 'vs/platform/windows/common/windowTracker';
|
||||
import { TelemetryLogAppender } from 'vs/platform/telemetry/common/telemetryLogAppender';
|
||||
import { UserDataAutoSyncEnablementService } from 'vs/platform/userDataSync/common/userDataAutoSyncService';
|
||||
import { IgnoredExtensionsManagementService, IIgnoredExtensionsManagementService } from 'vs/platform/userDataSync/common/ignoredExtensions';
|
||||
|
||||
export interface ISharedProcessConfiguration {
|
||||
readonly machineId: string;
|
||||
readonly windowId: number;
|
||||
}
|
||||
|
||||
export function startup(configuration: ISharedProcessConfiguration) {
|
||||
handshake(configuration);
|
||||
}
|
||||
|
||||
interface ISharedProcessInitData {
|
||||
sharedIPCHandle: string;
|
||||
args: NativeParsedArgs;
|
||||
logLevel: LogLevel;
|
||||
nodeCachedDataDir?: string;
|
||||
backupWorkspacesPath: string;
|
||||
}
|
||||
|
||||
const eventPrefix = 'monacoworkbench';
|
||||
|
||||
class MainProcessService implements IMainProcessService {
|
||||
|
||||
constructor(
|
||||
private server: Server,
|
||||
private mainRouter: StaticRouter
|
||||
) { }
|
||||
|
||||
declare readonly _serviceBrand: undefined;
|
||||
|
||||
getChannel(channelName: string): IChannel {
|
||||
return this.server.getChannel(channelName, this.mainRouter);
|
||||
}
|
||||
|
||||
registerChannel(channelName: string, channel: IServerChannel<string>): void {
|
||||
this.server.registerChannel(channelName, channel);
|
||||
}
|
||||
}
|
||||
|
||||
async function main(server: Server, initData: ISharedProcessInitData, configuration: ISharedProcessConfiguration): Promise<void> {
|
||||
const services = new ServiceCollection();
|
||||
|
||||
const disposables = new DisposableStore();
|
||||
|
||||
const onExit = () => disposables.dispose();
|
||||
process.once('exit', onExit);
|
||||
ipcRenderer.once('vscode:electron-main->shared-process=exit', onExit);
|
||||
|
||||
disposables.add(server);
|
||||
|
||||
const environmentService = new NativeEnvironmentService(initData.args);
|
||||
|
||||
const mainRouter = new StaticRouter(ctx => ctx === 'main');
|
||||
const loggerClient = new LoggerChannelClient(server.getChannel('logger', mainRouter));
|
||||
const logService = new FollowerLogService(loggerClient, new SpdLogService('sharedprocess', environmentService.logsPath, initData.logLevel));
|
||||
disposables.add(logService);
|
||||
logService.info('main', JSON.stringify(configuration));
|
||||
|
||||
const mainProcessService = new MainProcessService(server, mainRouter);
|
||||
services.set(IMainProcessService, mainProcessService);
|
||||
|
||||
// Files
|
||||
const fileService = new FileService(logService);
|
||||
services.set(IFileService, fileService);
|
||||
disposables.add(fileService);
|
||||
const diskFileSystemProvider = new DiskFileSystemProvider(logService);
|
||||
disposables.add(diskFileSystemProvider);
|
||||
fileService.registerProvider(Schemas.file, diskFileSystemProvider);
|
||||
|
||||
// Configuration
|
||||
const configurationService = new ConfigurationService(environmentService.settingsResource, fileService);
|
||||
disposables.add(configurationService);
|
||||
await configurationService.initialize();
|
||||
|
||||
// Storage
|
||||
const storageService = new NativeStorageService(new GlobalStorageDatabaseChannelClient(mainProcessService.getChannel('storage')), logService, environmentService);
|
||||
await storageService.initialize();
|
||||
services.set(IStorageService, storageService);
|
||||
disposables.add(toDisposable(() => storageService.flush()));
|
||||
|
||||
services.set(IStorageKeysSyncRegistryService, new SyncDescriptor(StorageKeysSyncRegistryService));
|
||||
services.set(IEnvironmentService, environmentService);
|
||||
services.set(INativeEnvironmentService, environmentService);
|
||||
|
||||
services.set(IProductService, { _serviceBrand: undefined, ...product });
|
||||
services.set(ILogService, logService);
|
||||
services.set(IConfigurationService, configurationService);
|
||||
services.set(IRequestService, new SyncDescriptor(RequestService));
|
||||
services.set(ILoggerService, new SyncDescriptor(LoggerService));
|
||||
|
||||
const nativeHostService = createChannelSender<INativeHostService>(mainProcessService.getChannel('nativeHost'), { context: configuration.windowId });
|
||||
services.set(INativeHostService, nativeHostService);
|
||||
const activeWindowManager = new ActiveWindowManager(nativeHostService);
|
||||
const activeWindowRouter = new StaticRouter(ctx => activeWindowManager.getActiveClientId().then(id => ctx === id));
|
||||
|
||||
services.set(IDownloadService, new SyncDescriptor(DownloadService));
|
||||
services.set(IExtensionRecommendationNotificationService, new ExtensionRecommendationNotificationServiceChannelClient(server.getChannel('IExtensionRecommendationNotificationService', activeWindowRouter)));
|
||||
|
||||
const instantiationService = new InstantiationService(services);
|
||||
|
||||
let telemetryService: ITelemetryService;
|
||||
instantiationService.invokeFunction(accessor => {
|
||||
const services = new ServiceCollection();
|
||||
const { appRoot, extensionsPath, extensionDevelopmentLocationURI, isBuilt, installSourcePath } = environmentService;
|
||||
|
||||
let telemetryAppender: ITelemetryAppender = NullAppender;
|
||||
if (!extensionDevelopmentLocationURI && !environmentService.disableTelemetry && product.enableTelemetry) {
|
||||
telemetryAppender = new TelemetryLogAppender(accessor.get(ILoggerService), environmentService);
|
||||
if (product.aiConfig && product.aiConfig.asimovKey && isBuilt) {
|
||||
const appInsightsAppender = new AppInsightsAppender(eventPrefix, null, product.aiConfig.asimovKey);
|
||||
disposables.add(toDisposable(() => appInsightsAppender!.flush())); // Ensure the AI appender is disposed so that it flushes remaining data
|
||||
telemetryAppender = combinedAppender(appInsightsAppender, telemetryAppender);
|
||||
}
|
||||
const config: ITelemetryServiceConfig = {
|
||||
appender: telemetryAppender,
|
||||
commonProperties: resolveCommonProperties(product.commit, product.version, configuration.machineId, product.msftInternalDomains, installSourcePath),
|
||||
sendErrorTelemetry: true,
|
||||
piiPaths: extensionsPath ? [appRoot, extensionsPath] : [appRoot]
|
||||
};
|
||||
|
||||
telemetryService = new TelemetryService(config, configurationService);
|
||||
services.set(ITelemetryService, telemetryService);
|
||||
} else {
|
||||
telemetryService = NullTelemetryService;
|
||||
services.set(ITelemetryService, NullTelemetryService);
|
||||
}
|
||||
server.registerChannel('telemetryAppender', new TelemetryAppenderChannel(telemetryAppender));
|
||||
|
||||
const storageKeysSyncRegistryService = accessor.get(IStorageKeysSyncRegistryService);
|
||||
const storageKeysSyncChannel = new StorageKeysSyncRegistryChannel(storageKeysSyncRegistryService);
|
||||
server.registerChannel('storageKeysSyncRegistryService', storageKeysSyncChannel);
|
||||
|
||||
services.set(IExtensionManagementService, new SyncDescriptor(ExtensionManagementService));
|
||||
services.set(IExtensionGalleryService, new SyncDescriptor(ExtensionGalleryService));
|
||||
services.set(ILocalizationsService, new SyncDescriptor(LocalizationsService));
|
||||
services.set(IDiagnosticsService, new SyncDescriptor(DiagnosticsService));
|
||||
services.set(IExtensionTipsService, new SyncDescriptor(ExtensionTipsService));
|
||||
|
||||
services.set(IUserDataSyncAccountService, new SyncDescriptor(UserDataSyncAccountService));
|
||||
services.set(IUserDataSyncLogService, new SyncDescriptor(UserDataSyncLogService));
|
||||
services.set(IUserDataSyncUtilService, new UserDataSyncUtilServiceClient(server.getChannel('userDataSyncUtil', client => client.ctx !== 'main')));
|
||||
services.set(IGlobalExtensionEnablementService, new SyncDescriptor(GlobalExtensionEnablementService));
|
||||
services.set(IIgnoredExtensionsManagementService, new SyncDescriptor(IgnoredExtensionsManagementService));
|
||||
services.set(IUserDataSyncStoreManagementService, new SyncDescriptor(UserDataSyncStoreManagementService));
|
||||
services.set(IUserDataSyncStoreService, new SyncDescriptor(UserDataSyncStoreService));
|
||||
services.set(IUserDataSyncMachinesService, new SyncDescriptor(UserDataSyncMachinesService));
|
||||
services.set(IUserDataSyncBackupStoreService, new SyncDescriptor(UserDataSyncBackupStoreService));
|
||||
services.set(IUserDataAutoSyncEnablementService, new SyncDescriptor(UserDataAutoSyncEnablementService));
|
||||
services.set(IUserDataSyncResourceEnablementService, new SyncDescriptor(UserDataSyncResourceEnablementService));
|
||||
services.set(IUserDataSyncService, new SyncDescriptor(UserDataSyncService));
|
||||
registerConfiguration();
|
||||
|
||||
const instantiationService2 = instantiationService.createChild(services);
|
||||
|
||||
instantiationService2.invokeFunction(accessor => {
|
||||
|
||||
const extensionManagementService = accessor.get(IExtensionManagementService);
|
||||
const channel = new ExtensionManagementChannel(extensionManagementService, () => null);
|
||||
server.registerChannel('extensions', channel);
|
||||
|
||||
const localizationsService = accessor.get(ILocalizationsService);
|
||||
const localizationsChannel = createChannelReceiver(localizationsService);
|
||||
server.registerChannel('localizations', localizationsChannel);
|
||||
|
||||
const diagnosticsService = accessor.get(IDiagnosticsService);
|
||||
const diagnosticsChannel = createChannelReceiver(diagnosticsService);
|
||||
server.registerChannel('diagnostics', diagnosticsChannel);
|
||||
|
||||
const extensionTipsService = accessor.get(IExtensionTipsService);
|
||||
const extensionTipsChannel = new ExtensionTipsChannel(extensionTipsService);
|
||||
server.registerChannel('extensionTipsService', extensionTipsChannel);
|
||||
|
||||
const userDataSyncMachinesService = accessor.get(IUserDataSyncMachinesService);
|
||||
const userDataSyncMachineChannel = new UserDataSyncMachinesServiceChannel(userDataSyncMachinesService);
|
||||
server.registerChannel('userDataSyncMachines', userDataSyncMachineChannel);
|
||||
|
||||
const authTokenService = accessor.get(IUserDataSyncAccountService);
|
||||
const authTokenChannel = new UserDataSyncAccountServiceChannel(authTokenService);
|
||||
server.registerChannel('userDataSyncAccount', authTokenChannel);
|
||||
|
||||
const userDataSyncStoreManagementService = accessor.get(IUserDataSyncStoreManagementService);
|
||||
const userDataSyncStoreManagementChannel = new UserDataSyncStoreManagementServiceChannel(userDataSyncStoreManagementService);
|
||||
server.registerChannel('userDataSyncStoreManagement', userDataSyncStoreManagementChannel);
|
||||
|
||||
const userDataSyncService = accessor.get(IUserDataSyncService);
|
||||
const userDataSyncChannel = new UserDataSyncChannel(server, userDataSyncService, logService);
|
||||
server.registerChannel('userDataSync', userDataSyncChannel);
|
||||
|
||||
const userDataAutoSync = instantiationService2.createInstance(UserDataAutoSyncService);
|
||||
const userDataAutoSyncChannel = new UserDataAutoSyncChannel(userDataAutoSync);
|
||||
server.registerChannel('userDataAutoSync', userDataAutoSyncChannel);
|
||||
|
||||
// clean up deprecated extensions
|
||||
(extensionManagementService as ExtensionManagementService).removeDeprecatedExtensions();
|
||||
// update localizations cache
|
||||
(localizationsService as LocalizationsService).update();
|
||||
// cache clean ups
|
||||
disposables.add(combinedDisposable(
|
||||
new NodeCachedDataCleaner(initData.nodeCachedDataDir),
|
||||
instantiationService2.createInstance(LanguagePackCachedDataCleaner),
|
||||
instantiationService2.createInstance(StorageDataCleaner, initData.backupWorkspacesPath),
|
||||
instantiationService2.createInstance(LogsDataCleaner),
|
||||
userDataAutoSync
|
||||
));
|
||||
disposables.add(extensionManagementService as ExtensionManagementService);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function setupIPC(hook: string): Promise<Server> {
|
||||
function setup(retry: boolean): Promise<Server> {
|
||||
return serve(hook).then(null, err => {
|
||||
if (!retry || platform.isWindows || err.code !== 'EADDRINUSE') {
|
||||
return Promise.reject(err);
|
||||
}
|
||||
|
||||
// should retry, not windows and eaddrinuse
|
||||
|
||||
return connect(hook, '').then(
|
||||
client => {
|
||||
// we could connect to a running instance. this is not good, abort
|
||||
client.dispose();
|
||||
return Promise.reject(new Error('There is an instance already running.'));
|
||||
},
|
||||
err => {
|
||||
// it happens on Linux and OS X that the pipe is left behind
|
||||
// let's delete it, since we can't connect to it
|
||||
// and the retry the whole thing
|
||||
try {
|
||||
fs.unlinkSync(hook);
|
||||
} catch (e) {
|
||||
return Promise.reject(new Error('Error deleting the shared ipc hook.'));
|
||||
}
|
||||
|
||||
return setup(false);
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
return setup(true);
|
||||
}
|
||||
|
||||
async function handshake(configuration: ISharedProcessConfiguration): Promise<void> {
|
||||
|
||||
// receive payload from electron-main to start things
|
||||
const data = await new Promise<ISharedProcessInitData>(c => {
|
||||
ipcRenderer.once('vscode:electron-main->shared-process=payload', (event: unknown, r: ISharedProcessInitData) => c(r));
|
||||
|
||||
// tell electron-main we are ready to receive payload
|
||||
ipcRenderer.send('vscode:shared-process->electron-main=ready-for-payload');
|
||||
});
|
||||
|
||||
// await IPC connection and signal this back to electron-main
|
||||
const server = await setupIPC(data.sharedIPCHandle);
|
||||
ipcRenderer.send('vscode:shared-process->electron-main=ipc-ready');
|
||||
|
||||
// await initialization and signal this back to electron-main
|
||||
await main(server, data, configuration);
|
||||
ipcRenderer.send('vscode:shared-process->electron-main=init-done');
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<!-- Copyright (C) Microsoft Corporation. All rights reserved. -->
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src 'self' https: data: blob: vscode-remote-resource:; media-src 'none'; frame-src 'self' vscode-webview: https://*.vscode-webview-test.com; object-src 'self'; script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; connect-src 'self' https:; font-src 'self' https: vscode-remote-resource:;">
|
||||
</head>
|
||||
<body aria-label="">
|
||||
</body>
|
||||
|
||||
<!-- Init Bootstrap Helpers -->
|
||||
<script src="../../../../bootstrap.js"></script>
|
||||
<script src="../../../../vs/loader.js"></script>
|
||||
<script src="../../../../bootstrap-window.js"></script>
|
||||
|
||||
<!-- Startup via workbench.js -->
|
||||
<script src="workbench.js"></script>
|
||||
</html>
|
||||
191
lib/vscode/src/vs/code/electron-browser/workbench/workbench.js
Normal file
191
lib/vscode/src/vs/code/electron-browser/workbench/workbench.js
Normal file
@@ -0,0 +1,191 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
/// <reference path="../../../../typings/require.d.ts" />
|
||||
|
||||
//@ts-check
|
||||
'use strict';
|
||||
|
||||
(function () {
|
||||
|
||||
// Add a perf entry right from the top
|
||||
const perf = perfLib();
|
||||
perf.mark('renderer/started');
|
||||
|
||||
// Load environment in parallel to workbench loading to avoid waterfall
|
||||
const bootstrapWindow = bootstrapWindowLib();
|
||||
const whenEnvResolved = bootstrapWindow.globals().process.whenEnvResolved();
|
||||
|
||||
// Load workbench main JS, CSS and NLS all in parallel. This is an
|
||||
// optimization to prevent a waterfall of loading to happen, because
|
||||
// we know for a fact that workbench.desktop.main will depend on
|
||||
// the related CSS and NLS counterparts.
|
||||
bootstrapWindow.load([
|
||||
'vs/workbench/workbench.desktop.main',
|
||||
'vs/nls!vs/workbench/workbench.desktop.main',
|
||||
'vs/css!vs/workbench/workbench.desktop.main'
|
||||
],
|
||||
async function (workbench, configuration) {
|
||||
|
||||
// Mark start of workbench
|
||||
perf.mark('didLoadWorkbenchMain');
|
||||
performance.mark('workbench-start');
|
||||
|
||||
// Wait for process environment being fully resolved
|
||||
await whenEnvResolved;
|
||||
|
||||
perf.mark('main/startup');
|
||||
|
||||
// @ts-ignore
|
||||
return require('vs/workbench/electron-browser/desktop.main').main(configuration);
|
||||
},
|
||||
{
|
||||
removeDeveloperKeybindingsAfterLoad: true,
|
||||
canModifyDOM: function (windowConfig) {
|
||||
showPartsSplash(windowConfig);
|
||||
},
|
||||
beforeLoaderConfig: function (windowConfig, loaderConfig) {
|
||||
loaderConfig.recordStats = true;
|
||||
},
|
||||
beforeRequire: function () {
|
||||
perf.mark('willLoadWorkbenchMain');
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
//region Helpers
|
||||
|
||||
function perfLib() {
|
||||
globalThis.MonacoPerformanceMarks = globalThis.MonacoPerformanceMarks || [];
|
||||
|
||||
return {
|
||||
/**
|
||||
* @param {string} name
|
||||
*/
|
||||
mark(name) {
|
||||
globalThis.MonacoPerformanceMarks.push(name, Date.now());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {{
|
||||
* load: (modules: string[], resultCallback: (result, configuration: object) => any, options: object) => unknown,
|
||||
* globals: () => typeof import('../../../base/parts/sandbox/electron-sandbox/globals')
|
||||
* }}
|
||||
*/
|
||||
function bootstrapWindowLib() {
|
||||
// @ts-ignore (defined in bootstrap-window.js)
|
||||
return window.MonacoBootstrapWindow;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {{
|
||||
* partsSplashPath?: string,
|
||||
* colorScheme: ('light' | 'dark' | 'hc'),
|
||||
* autoDetectHighContrast?: boolean,
|
||||
* extensionDevelopmentPath?: string[],
|
||||
* folderUri?: object,
|
||||
* workspace?: object
|
||||
* }} configuration
|
||||
*/
|
||||
function showPartsSplash(configuration) {
|
||||
perf.mark('willShowPartsSplash');
|
||||
|
||||
let data;
|
||||
if (typeof configuration.partsSplashPath === 'string') {
|
||||
try {
|
||||
data = JSON.parse(require.__$__nodeRequire('fs').readFileSync(configuration.partsSplashPath, 'utf8'));
|
||||
} catch (e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
// high contrast mode has been turned on from the outside, e.g. OS -> ignore stored colors and layouts
|
||||
const isHighContrast = configuration.colorScheme === 'hc' /* ColorScheme.HIGH_CONTRAST */ && configuration.autoDetectHighContrast;
|
||||
if (data && isHighContrast && data.baseTheme !== 'hc-black') {
|
||||
data = undefined;
|
||||
}
|
||||
|
||||
// developing an extension -> ignore stored layouts
|
||||
if (data && configuration.extensionDevelopmentPath) {
|
||||
data.layoutInfo = undefined;
|
||||
}
|
||||
|
||||
// minimal color configuration (works with or without persisted data)
|
||||
let baseTheme, shellBackground, shellForeground;
|
||||
if (data) {
|
||||
baseTheme = data.baseTheme;
|
||||
shellBackground = data.colorInfo.editorBackground;
|
||||
shellForeground = data.colorInfo.foreground;
|
||||
} else if (isHighContrast) {
|
||||
baseTheme = 'hc-black';
|
||||
shellBackground = '#000000';
|
||||
shellForeground = '#FFFFFF';
|
||||
} else {
|
||||
baseTheme = 'vs-dark';
|
||||
shellBackground = '#1E1E1E';
|
||||
shellForeground = '#CCCCCC';
|
||||
}
|
||||
const style = document.createElement('style');
|
||||
style.className = 'initialShellColors';
|
||||
document.head.appendChild(style);
|
||||
style.textContent = `body { background-color: ${shellBackground}; color: ${shellForeground}; margin: 0; padding: 0; }`;
|
||||
|
||||
if (data && data.layoutInfo) {
|
||||
// restore parts if possible (we might not always store layout info)
|
||||
const { id, layoutInfo, colorInfo } = data;
|
||||
const splash = document.createElement('div');
|
||||
splash.id = id;
|
||||
splash.className = baseTheme;
|
||||
|
||||
if (layoutInfo.windowBorder) {
|
||||
splash.style.position = 'relative';
|
||||
splash.style.height = 'calc(100vh - 2px)';
|
||||
splash.style.width = 'calc(100vw - 2px)';
|
||||
splash.style.border = '1px solid var(--window-border-color)';
|
||||
splash.style.setProperty('--window-border-color', colorInfo.windowBorder);
|
||||
|
||||
if (layoutInfo.windowBorderRadius) {
|
||||
splash.style.borderRadius = layoutInfo.windowBorderRadius;
|
||||
}
|
||||
}
|
||||
|
||||
// ensure there is enough space
|
||||
layoutInfo.sideBarWidth = Math.min(layoutInfo.sideBarWidth, window.innerWidth - (layoutInfo.activityBarWidth + layoutInfo.editorPartMinWidth));
|
||||
|
||||
// part: title
|
||||
const titleDiv = document.createElement('div');
|
||||
titleDiv.setAttribute('style', `position: absolute; width: 100%; left: 0; top: 0; height: ${layoutInfo.titleBarHeight}px; background-color: ${colorInfo.titleBarBackground}; -webkit-app-region: drag;`);
|
||||
splash.appendChild(titleDiv);
|
||||
|
||||
// part: activity bar
|
||||
const activityDiv = document.createElement('div');
|
||||
activityDiv.setAttribute('style', `position: absolute; height: calc(100% - ${layoutInfo.titleBarHeight}px); top: ${layoutInfo.titleBarHeight}px; ${layoutInfo.sideBarSide}: 0; width: ${layoutInfo.activityBarWidth}px; background-color: ${colorInfo.activityBarBackground};`);
|
||||
splash.appendChild(activityDiv);
|
||||
|
||||
// part: side bar (only when opening workspace/folder)
|
||||
if (configuration.folderUri || configuration.workspace) {
|
||||
// folder or workspace -> status bar color, sidebar
|
||||
const sideDiv = document.createElement('div');
|
||||
sideDiv.setAttribute('style', `position: absolute; height: calc(100% - ${layoutInfo.titleBarHeight}px); top: ${layoutInfo.titleBarHeight}px; ${layoutInfo.sideBarSide}: ${layoutInfo.activityBarWidth}px; width: ${layoutInfo.sideBarWidth}px; background-color: ${colorInfo.sideBarBackground};`);
|
||||
splash.appendChild(sideDiv);
|
||||
}
|
||||
|
||||
// part: statusbar
|
||||
const statusDiv = document.createElement('div');
|
||||
statusDiv.setAttribute('style', `position: absolute; width: 100%; bottom: 0; left: 0; height: ${layoutInfo.statusBarHeight}px; background-color: ${configuration.folderUri || configuration.workspace ? colorInfo.statusBarBackground : colorInfo.statusBarNoFolderBackground};`);
|
||||
splash.appendChild(statusDiv);
|
||||
|
||||
document.body.appendChild(splash);
|
||||
}
|
||||
|
||||
perf.mark('didShowPartsSplash');
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
}());
|
||||
Reference in New Issue
Block a user