@sussudio/platform
Version:
Internal APIs for VS Code's service injection the base services.
210 lines (209 loc) • 7.66 kB
JavaScript
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
var __decorate =
(this && this.__decorate) ||
function (decorators, target, key, desc) {
var c = arguments.length,
r = c < 3 ? target : desc === null ? (desc = Object.getOwnPropertyDescriptor(target, key)) : desc,
d;
if (typeof Reflect === 'object' && typeof Reflect.decorate === 'function')
r = Reflect.decorate(decorators, target, key, desc);
else
for (var i = decorators.length - 1; i >= 0; i--)
if ((d = decorators[i])) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __param =
(this && this.__param) ||
function (paramIndex, decorator) {
return function (target, key) {
decorator(target, key, paramIndex);
};
};
import { app } from 'electron';
import { coalesce, firstOrDefault } from '@sussudio/base/common/arrays.mjs';
import { isMacintosh } from '@sussudio/base/common/platform.mjs';
import { URI } from '@sussudio/base/common/uri.mjs';
import { whenDeleted } from '@sussudio/base/node/pfs.mjs';
import { IConfigurationService } from '../../configuration/common/configuration.mjs';
import { isLaunchedFromCli } from '../../environment/node/argvHelper.mjs';
import { createDecorator } from '../../instantiation/common/instantiation.mjs';
import { ILogService } from '../../log/common/log.mjs';
import { IURLService } from '../../url/common/url.mjs';
import { IWindowsMainService } from '../../windows/electron-main/windows.mjs';
export const ID = 'launchMainService';
export const ILaunchMainService = createDecorator(ID);
let LaunchMainService = class LaunchMainService {
logService;
windowsMainService;
urlService;
configurationService;
constructor(logService, windowsMainService, urlService, configurationService) {
this.logService = logService;
this.windowsMainService = windowsMainService;
this.urlService = urlService;
this.configurationService = configurationService;
}
async start(args, userEnv) {
this.logService.trace('Received data from other instance: ', args, userEnv);
// macOS: Electron > 7.x changed its behaviour to not
// bring the application to the foreground when a window
// is focused programmatically. Only via `app.focus` and
// the option `steal: true` can you get the previous
// behaviour back. The only reason to use this option is
// when a window is getting focused while the application
// is not in the foreground and since we got instructed
// to open a new window from another instance, we ensure
// that the app has focus.
if (isMacintosh) {
app.focus({ steal: true });
}
// Check early for open-url which is handled in URL service
const urlsToOpen = this.parseOpenUrl(args);
if (urlsToOpen.length) {
let whenWindowReady = Promise.resolve();
// Create a window if there is none
if (this.windowsMainService.getWindowCount() === 0) {
const window = firstOrDefault(
await this.windowsMainService.openEmptyWindow({ context: 4 /* OpenContext.DESKTOP */ }),
);
if (window) {
whenWindowReady = window.ready();
}
}
// Make sure a window is open, ready to receive the url event
whenWindowReady.then(() => {
for (const { uri, url } of urlsToOpen) {
this.urlService.open(uri, { originalUrl: url });
}
});
}
// Otherwise handle in windows service
else {
return this.startOpenWindow(args, userEnv);
}
}
parseOpenUrl(args) {
if (args['open-url'] && args._urls && args._urls.length > 0) {
// --open-url must contain -- followed by the url(s)
// process.argv is used over args._ as args._ are resolved to file paths at this point
return coalesce(
args._urls.map((url) => {
try {
return { uri: URI.parse(url), url };
} catch (err) {
return null;
}
}),
);
}
return [];
}
async startOpenWindow(args, userEnv) {
const context = isLaunchedFromCli(userEnv) ? 0 /* OpenContext.CLI */ : 4; /* OpenContext.DESKTOP */
let usedWindows = [];
const waitMarkerFileURI = args.wait && args.waitMarkerFilePath ? URI.file(args.waitMarkerFilePath) : undefined;
const remoteAuthority = args.remote || undefined;
const baseConfig = {
context,
cli: args,
userEnv,
waitMarkerFileURI,
remoteAuthority,
forceProfile: args.profile,
forceTempProfile: args['profile-temp'],
};
// Special case extension development
if (!!args.extensionDevelopmentPath) {
await this.windowsMainService.openExtensionDevelopmentHostWindow(args.extensionDevelopmentPath, baseConfig);
}
// Start without file/folder arguments
else if (!args._.length && !args['folder-uri'] && !args['file-uri']) {
let openNewWindow = false;
// Force new window
if (args['new-window'] || args['unity-launch'] || baseConfig.forceProfile || baseConfig.forceTempProfile) {
openNewWindow = true;
}
// Force reuse window
else if (args['reuse-window']) {
openNewWindow = false;
}
// Otherwise check for settings
else {
const windowConfig = this.configurationService.getValue('window');
const openWithoutArgumentsInNewWindowConfig =
windowConfig?.openWithoutArgumentsInNewWindow || 'default'; /* default */
switch (openWithoutArgumentsInNewWindowConfig) {
case 'on':
openNewWindow = true;
break;
case 'off':
openNewWindow = false;
break;
default:
openNewWindow = !isMacintosh; // prefer to restore running instance on macOS
}
}
// Open new Window
if (openNewWindow) {
usedWindows = await this.windowsMainService.open({
...baseConfig,
forceNewWindow: true,
forceEmpty: true,
});
}
// Focus existing window or open if none opened
else {
const lastActive = this.windowsMainService.getLastActiveWindow();
if (lastActive) {
this.windowsMainService.openExistingWindow(lastActive, baseConfig);
usedWindows = [lastActive];
} else {
usedWindows = await this.windowsMainService.open({
...baseConfig,
forceEmpty: true,
});
}
}
}
// Start with file/folder arguments
else {
usedWindows = await this.windowsMainService.open({
...baseConfig,
forceNewWindow: args['new-window'],
preferNewWindow: !args['reuse-window'] && !args.wait,
forceReuseWindow: args['reuse-window'],
diffMode: args.diff,
mergeMode: args.merge,
addMode: args.add,
noRecentEntry: !!args['skip-add-to-recently-opened'],
gotoLineMode: args.goto,
});
}
// If the other instance is waiting to be killed, we hook up a window listener if one window
// is being used and only then resolve the startup promise which will kill this second instance.
// In addition, we poll for the wait marker file to be deleted to return.
if (waitMarkerFileURI && usedWindows.length === 1 && usedWindows[0]) {
return Promise.race([usedWindows[0].whenClosedOrLoaded, whenDeleted(waitMarkerFileURI.fsPath)]).then(
() => undefined,
() => undefined,
);
}
}
async getMainProcessId() {
this.logService.trace('Received request for process ID from other instance.');
return process.pid;
}
};
LaunchMainService = __decorate(
[
__param(0, ILogService),
__param(1, IWindowsMainService),
__param(2, IURLService),
__param(3, IConfigurationService),
],
LaunchMainService,
);
export { LaunchMainService };