提交 5a27354d 编写于 作者: J Joao Moreno

Merge branch 'url-service'

......@@ -30,10 +30,9 @@ import { resolveCommonProperties } from 'vs/platform/telemetry/node/commonProper
import { TelemetryAppenderChannel } from 'vs/platform/telemetry/common/telemetryIpc';
import { TelemetryService, ITelemetryServiceConfig } from 'vs/platform/telemetry/common/telemetryService';
import { AppInsightsAppender } from 'vs/platform/telemetry/node/appInsightsAppender';
import { IWindowsService } from 'vs/platform/windows/common/windows';
import { IWindowsService, ActiveWindowManager } from 'vs/platform/windows/common/windows';
import { WindowsChannelClient } from 'vs/platform/windows/common/windowsIpc';
import { ipcRenderer } from 'electron';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { createSharedProcessContributions } from 'vs/code/electron-browser/sharedProcess/contrib/contributions';
import { createSpdLogService } from 'vs/platform/log/node/spdlogService';
import { ILogService, LogLevel } from 'vs/platform/log/common/log';
......@@ -58,28 +57,6 @@ interface ISharedProcessInitData {
logLevel: LogLevel;
}
class ActiveWindowManager implements IDisposable {
private disposables: IDisposable[] = [];
private _activeWindowId: number;
constructor(@IWindowsService windowsService: IWindowsService) {
windowsService.onWindowOpen(this.setActiveWindow, this, this.disposables);
windowsService.onWindowFocus(this.setActiveWindow, this, this.disposables);
}
private setActiveWindow(windowId: number) {
this._activeWindowId = windowId;
}
public get activeClientId(): string {
return `window:${this._activeWindowId}`;
}
public dispose() {
this.disposables = dispose(this.disposables);
}
}
const eventPrefix = 'monacoworkbench';
function main(server: Server, initData: ISharedProcessInitData, configuration: ISharedProcessConfiguration): void {
......
......@@ -8,7 +8,7 @@
import { app, ipcMain as ipc, BrowserWindow } from 'electron';
import * as platform from 'vs/base/common/platform';
import { WindowsManager } from 'vs/code/electron-main/windows';
import { IWindowsService, OpenContext } from 'vs/platform/windows/common/windows';
import { IWindowsService, OpenContext, ActiveWindowManager } from 'vs/platform/windows/common/windows';
import { WindowsChannel } from 'vs/platform/windows/common/windowsIpc';
import { WindowsService } from 'vs/platform/windows/electron-main/windowsService';
import { ILifecycleService } from 'vs/platform/lifecycle/electron-main/lifecycleMain';
......@@ -29,7 +29,7 @@ import { IStateService } from 'vs/platform/state/common/state';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IURLService } from 'vs/platform/url/common/url';
import { URLChannel } from 'vs/platform/url/common/urlIpc';
import { URLHandlerChannelClient, URLServiceChannel } from 'vs/platform/url/common/urlIpc';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils';
import { ITelemetryAppenderChannel, TelemetryAppenderClient } from 'vs/platform/telemetry/common/telemetryIpc';
......@@ -59,6 +59,7 @@ import { IssueChannel } from 'vs/platform/issue/common/issueIpc';
import { IssueService } from 'vs/platform/issue/electron-main/issueService';
import { LogLevelSetterChannel } from 'vs/platform/log/common/logIpc';
import { setUnexpectedErrorHandler } from 'vs/base/common/errors';
import { ElectronURLListener } from 'vs/platform/url/electron-main/electronUrlListener';
export class CodeApplication {
......@@ -355,10 +356,6 @@ export class CodeApplication {
const updateChannel = new UpdateChannel(updateService);
this.electronIpcServer.registerChannel('update', updateChannel);
const urlService = accessor.get(IURLService);
const urlChannel = appInstantiationService.createInstance(URLChannel, urlService);
this.electronIpcServer.registerChannel('url', urlChannel);
const issueService = accessor.get(IIssueService);
const issueChannel = new IssueChannel(issueService);
this.electronIpcServer.registerChannel('issue', issueChannel);
......@@ -372,6 +369,10 @@ export class CodeApplication {
this.electronIpcServer.registerChannel('windows', windowsChannel);
this.sharedProcessClient.done(client => client.registerChannel('windows', windowsChannel));
const urlService = accessor.get(IURLService);
const urlChannel = new URLServiceChannel(urlService);
this.electronIpcServer.registerChannel('url', urlChannel);
// Log level management
const logLevelChannel = new LogLevelSetterChannel(accessor.get(ILogService));
this.electronIpcServer.registerChannel('loglevel', logLevelChannel);
......@@ -382,10 +383,25 @@ export class CodeApplication {
// Propagate to clients
this.windowsMainService = accessor.get(IWindowsMainService); // TODO@Joao: unfold this
const args = this.environmentService.args;
// Create a URL handler which forwards to the last active window
const activeWindowManager = new ActiveWindowManager(windowsService);
const urlHandlerChannel = this.electronIpcServer.getChannel('urlHandler', { route: () => activeWindowManager.activeClientId });
const multiplexURLHandler = new URLHandlerChannelClient(urlHandlerChannel);
// Register the multiple URL handker
urlService.registerHandler(multiplexURLHandler);
// Watch Electron URLs and forward them to the UrlService
const urls = args['open-url'] ? args._urls : [];
const urlListener = new ElectronURLListener(urls, urlService, this.windowsMainService);
this.toDispose.push(urlListener);
this.windowsMainService.ready(this.userEnv);
// Open our first window
const args = this.environmentService.args;
const context = !!process.env['VSCODE_CLI'] ? OpenContext.CLI : OpenContext.DESKTOP;
if (args['new-window'] && args._.length === 0) {
this.windowsMainService.open({ context, cli: args, forceNewWindow: true, forceEmpty: true, initialStartup: true }); // new window if "-n" was used without paths
......
......@@ -18,6 +18,7 @@ import { whenDeleted } from 'vs/base/node/pfs';
import { IWorkspacesMainService } from 'vs/platform/workspaces/common/workspaces';
import { Schemas } from 'vs/base/common/network';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import URI from '../../base/common/uri';
export const ID = 'launchService';
export const ILaunchService = createDecorator<ILaunchService>(ID);
......@@ -39,6 +40,24 @@ export interface IMainProcessInfo {
windows: IWindowInfo[];
}
function parseOpenUrl(args: ParsedArgs): URI[] {
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 args._urls
.map(url => {
try {
return URI.parse(url);
} catch (err) {
return null;
}
})
.filter(uri => !!uri);
}
return [];
}
export interface ILaunchService {
_serviceBrand: any;
start(args: ParsedArgs, userEnv: IProcessEnvironment): TPromise<void>;
......@@ -118,25 +137,30 @@ export class LaunchService implements ILaunchService {
public start(args: ParsedArgs, userEnv: IProcessEnvironment): TPromise<void> {
this.logService.trace('Received data from other instance: ', args, userEnv);
const urlsToOpen = parseOpenUrl(args);
// Check early for open-url which is handled in URL service
if (this.shouldOpenUrl(args)) {
return TPromise.as(null);
}
if (urlsToOpen.length) {
let whenWindowReady = TPromise.as<any>(null);
// Otherwise handle in windows service
return this.startOpenWindow(args, userEnv);
}
// Create a window if there is none
if (this.windowsMainService.getWindowCount() === 0) {
const window = this.windowsMainService.openNewWindow(OpenContext.DESKTOP)[0];
whenWindowReady = window.ready();
}
private shouldOpenUrl(args: ParsedArgs): boolean {
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
args._urls.forEach(url => this.urlService.open(url));
// Make sure a window is open, ready to receive the url event
whenWindowReady.then(() => {
for (const url of urlsToOpen) {
this.urlService.open(url);
}
});
return true;
return TPromise.as(null);
}
return false;
// Otherwise handle in windows service
return this.startOpenWindow(args, userEnv);
}
private startOpenWindow(args: ParsedArgs, userEnv: IProcessEnvironment): TPromise<void> {
......
......@@ -34,7 +34,7 @@ import { ConfigurationService } from 'vs/platform/configuration/node/configurati
import { IRequestService } from 'vs/platform/request/node/request';
import { RequestService } from 'vs/platform/request/electron-main/requestService';
import { IURLService } from 'vs/platform/url/common/url';
import { URLService } from 'vs/platform/url/electron-main/urlService';
import { URLService } from 'vs/platform/url/common/urlService';
import * as fs from 'original-fs';
import { CodeApplication } from 'vs/code/electron-main/app';
import { HistoryMainService } from 'vs/platform/history/electron-main/historyMainService';
......@@ -71,7 +71,7 @@ function createServices(args: ParsedArgs, bufferLogService: BufferLogService): I
services.set(IStateService, new SyncDescriptor(StateService));
services.set(IConfigurationService, new SyncDescriptor(ConfigurationService));
services.set(IRequestService, new SyncDescriptor(RequestService));
services.set(IURLService, new SyncDescriptor(URLService, args['open-url'] ? args._urls : []));
services.set(IURLService, new SyncDescriptor(URLService));
services.set(IBackupMainService, new SyncDescriptor(BackupMainService));
services.set(IDialogService, new SyncDescriptor(CommandLineDialogService));
......
......@@ -1375,8 +1375,8 @@ export class WindowsManager implements IWindowsMainService {
return getLastActiveWindow(WindowsManager.WINDOWS);
}
public openNewWindow(context: OpenContext): void {
this.open({ context, cli: this.environmentService.args, forceNewWindow: true, forceEmpty: true });
public openNewWindow(context: OpenContext): ICodeWindow[] {
return this.open({ context, cli: this.environmentService.args, forceNewWindow: true, forceEmpty: true });
}
public waitForWindowCloseOrLoad(windowId: number): TPromise<void> {
......
......@@ -5,15 +5,21 @@
'use strict';
import { Event } from 'vs/base/common/event';
import URI from 'vs/base/common/uri';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IDisposable } from 'vs/base/common/lifecycle';
import { TPromise } from 'vs/base/common/winjs.base';
export const ID = 'urlService';
export const IURLService = createDecorator<IURLService>(ID);
export interface IURLHandler {
handleURL(uri: URI): TPromise<boolean>;
}
export interface IURLService {
_serviceBrand: any;
open(url: string): void;
onOpenURL: Event<URI>;
open(url: URI): TPromise<boolean>;
registerHandler(handler: IURLHandler): IDisposable;
}
......@@ -6,61 +6,65 @@
'use strict';
import { TPromise } from 'vs/base/common/winjs.base';
import { IChannel, eventToCall, eventFromCall, Serializer, Deserializer } from 'vs/base/parts/ipc/common/ipc';
import { IURLService } from './url';
import { Event, filterEvent } from 'vs/base/common/event';
import { IWindowsService } from 'vs/platform/windows/common/windows';
import { IChannel } from 'vs/base/parts/ipc/common/ipc';
import { IURLHandler, IURLService } from './url';
import URI from 'vs/base/common/uri';
import { IDisposable } from 'vs/base/common/lifecycle';
const URISerializer: Serializer<URI, any> = uri => uri.toJSON();
const URIDeserializer: Deserializer<URI, any> = raw => URI.revive(raw);
export interface IURLChannel extends IChannel {
call(command: 'event:onOpenURL'): TPromise<void>;
export interface IURLServiceChannel extends IChannel {
call(command: 'open', url: string): TPromise<boolean>;
call(command: string, arg?: any): TPromise<any>;
}
export class URLChannel implements IURLChannel {
private focusedWindowId: number;
export class URLServiceChannel implements IURLServiceChannel {
constructor(
private service: IURLService,
@IWindowsService windowsService: IWindowsService
) {
windowsService.onWindowFocus(id => this.focusedWindowId = id);
}
constructor(private service: IURLService) { }
call(command: string, arg?: any): TPromise<any> {
switch (command) {
case 'event:onOpenURL': return eventToCall(filterEvent(this.service.onOpenURL, () => this.isWindowFocused(arg)), URISerializer);
case 'open': return this.service.open(URI.revive(arg));
}
return undefined;
}
}
export class URLServiceChannelClient implements IURLService {
/**
* We only want the focused window to get pinged with the onOpenUrl event.
* The idea here is to filter the onOpenUrl event with the knowledge of which
* was the last window to be focused. When first listening to the event,
* each client sends its window ID via the arguments to `call(...)`.
* When the event fires, the server has enough knowledge to filter the event
* and fire it only to the focused window.
*/
private isWindowFocused(windowID: number): boolean {
return this.focusedWindowId === windowID;
_serviceBrand: any;
constructor(private channel: IChannel) { }
open(url: URI): TPromise<boolean, any> {
return this.channel.call('open', url.toJSON());
}
registerHandler(handler: IURLHandler): IDisposable {
throw new Error('Not implemented.');
}
}
export class URLChannelClient implements IURLService {
export interface IURLHandlerChannel extends IChannel {
call(command: 'handleURL', arg: any): TPromise<boolean>;
call(command: string, arg?: any): TPromise<any>;
}
_serviceBrand: any;
export class URLHandlerChannel implements IURLHandlerChannel {
constructor(private handler: IURLHandler) { }
call(command: string, arg?: any): TPromise<any> {
switch (command) {
case 'handleURL': return this.handler.handleURL(URI.revive(arg));
}
return undefined;
}
}
constructor(private channel: IChannel, private windowID: number) { }
export class URLHandlerChannelClient implements IURLHandler {
private _onOpenURL = eventFromCall<URI>(this.channel, 'event:onOpenURL', this.windowID, URIDeserializer);
get onOpenURL(): Event<URI> { return this._onOpenURL; }
constructor(private channel: IChannel) { }
open(url: string): void {
return; // not implemented
handleURL(uri: URI): TPromise<boolean> {
return this.channel.call('handleURL', uri.toJSON());
}
}
\ No newline at end of file
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { IURLService, IURLHandler } from 'vs/platform/url/common/url';
import URI from 'vs/base/common/uri';
import { IDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { TPromise } from 'vs/base/common/winjs.base';
declare module Array {
function from<T>(set: Set<T>): T[];
}
export class URLService implements IURLService {
_serviceBrand: any;
private handlers = new Set<IURLHandler>();
async open(uri: URI): TPromise<boolean> {
const handlers = Array.from(this.handlers);
for (const handler of handlers) {
if (await handler.handleURL(uri)) {
return true;
}
}
return false;
}
registerHandler(handler: IURLHandler): IDisposable {
this.handlers.add(handler);
return toDisposable(() => this.handlers.delete(handler));
}
}
export class RelayURLService extends URLService implements IURLHandler {
constructor(private urlService: IURLService) {
super();
}
async open(uri: URI): TPromise<boolean> {
return this.urlService.open(uri);
}
handleURL(uri: URI): TPromise<boolean> {
return super.open(uri);
}
}
\ No newline at end of file
......@@ -5,57 +5,84 @@
'use strict';
import { Event, mapEvent, chain, echo, Emitter, anyEvent, fromNodeEventEmitter } from 'vs/base/common/event';
import { mapEvent, fromNodeEventEmitter, filterEvent } from 'vs/base/common/event';
import { IURLService } from 'vs/platform/url/common/url';
import product from 'vs/platform/node/product';
import { app } from 'electron';
import URI from 'vs/base/common/uri';
import { ILogService } from 'vs/platform/log/common/log';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { IWindowsMainService } from 'vs/platform/windows/electron-main/windows';
import { ReadyState } from 'vs/platform/windows/common/windows';
export class URLService implements IURLService {
function uriFromRawUrl(url: string): URI | null {
try {
return URI.parse(url);
} catch (e) {
return null;
}
}
_serviceBrand: any;
export class ElectronURLListener {
private readonly openUrlEmitter: Emitter<string> = new Emitter<string>();
onOpenURL: Event<URI>;
private buffer: URI[] = [];
private disposables: IDisposable[] = [];
constructor(
initial: string | string[],
@ILogService private logService: ILogService
@IURLService private urlService: IURLService,
@IWindowsMainService private windowsService: IWindowsMainService
) {
const globalBuffer = (global.getOpenUrls() || []) as string[];
const initialBuffer = [
const rawBuffer = [
...(typeof initial === 'string' ? [initial] : initial),
...globalBuffer
];
this.buffer = rawBuffer.map(uriFromRawUrl).filter(uri => !!uri);
app.setAsDefaultProtocolClient(product.urlProtocol, process.execPath, ['--open-url', '--']);
const rawOnOpenUrl = fromNodeEventEmitter(app, 'open-url', (event: Electron.Event, url: string) => ({ event, url }));
const onOpenElectronUrl = mapEvent(
fromNodeEventEmitter(app, 'open-url', (event: Electron.Event, url: string) => ({ event, url })),
({ event, url }) => {
// always prevent default and return the url as string
event.preventDefault();
return url;
});
// always prevent default and return the url as string
const preventedOnOpenUrl = mapEvent(rawOnOpenUrl, ({ event, url }) => {
event.preventDefault();
return url;
});
const onOpenUrl = filterEvent(mapEvent(onOpenElectronUrl, uriFromRawUrl), uri => !!uri);
onOpenUrl(this.open, this, this.disposables);
this.windowsService.onWindowReady(this.flushBuffer, this, this.disposables);
this.flushBuffer();
}
// echo all `onOpenUrl` events to each listener
const bufferedOnOpenUrl = echo(preventedOnOpenUrl, true, initialBuffer);
private open(uri: URI): void {
const shouldBuffer = this.windowsService.getWindows()
.filter(w => w.readyState === ReadyState.READY)
.length === 0;
this.onOpenURL = chain(anyEvent(bufferedOnOpenUrl, this.openUrlEmitter.event))
.map(url => {
try {
return URI.parse(url);
} catch (e) {
return null;
if (shouldBuffer) {
this.buffer.push(uri);
} else {
this.urlService.open(uri).then(handled => {
if (!handled) {
this.buffer.push(uri);
}
})
.filter(uri => !!uri)
.event;
});
}
}
open(url: string): void {
this.logService.trace('urlService#open', url);
this.openUrlEmitter.fire(url);
private flushBuffer(): void {
const buffer = this.buffer;
this.buffer = [];
for (const uri of buffer) {
this.open(uri);
}
}
}
dispose(): void {
this.disposables = dispose(this.disposables);
}
}
\ No newline at end of file
......@@ -16,6 +16,7 @@ import { IRecentlyOpened } from 'vs/platform/history/common/history';
import { ICommandAction } from 'vs/platform/actions/common/actions';
import { PerformanceEntry } from 'vs/base/common/performance';
import { LogLevel } from 'vs/platform/log/common/log';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
export const IWindowsService = createDecorator<IWindowsService>('windowsService');
......@@ -333,3 +334,26 @@ export interface IRunActionInWindowRequest {
id: string;
from: 'menu' | 'touchbar' | 'mouse';
}
export class ActiveWindowManager implements IDisposable {
private disposables: IDisposable[] = [];
private _activeWindowId: number;
constructor(@IWindowsService windowsService: IWindowsService) {
windowsService.onWindowOpen(this.setActiveWindow, this, this.disposables);
windowsService.onWindowFocus(this.setActiveWindow, this, this.disposables);
}
private setActiveWindow(windowId: number) {
this._activeWindowId = windowId;
}
get activeClientId(): string {
return `window:${this._activeWindowId}`;
}
dispose() {
this.disposables = dispose(this.disposables);
}
}
\ No newline at end of file
......@@ -107,7 +107,7 @@ export interface IWindowsMainService {
focusLastActive(cli: ParsedArgs, context: OpenContext): ICodeWindow;
getLastActiveWindow(): ICodeWindow;
waitForWindowCloseOrLoad(windowId: number): TPromise<void>;
openNewWindow(context: OpenContext): void;
openNewWindow(context: OpenContext): ICodeWindow[];
sendToFocused(channel: string, ...args: any[]): void;
sendToAll(channel: string, payload: any, windowIdsToIgnore?: number[]): void;
getFocusedWindow(): ICodeWindow;
......
......@@ -14,8 +14,8 @@ import product from 'vs/platform/node/product';
import { IWindowsService, OpenContext, INativeOpenDialogOptions, IEnterWorkspaceResult, IMessageBoxResult } from 'vs/platform/windows/common/windows';
import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/environment';
import { shell, crashReporter, app, Menu, clipboard } from 'electron';
import { Event, chain, fromNodeEventEmitter } from 'vs/base/common/event';
import { IURLService } from 'vs/platform/url/common/url';
import { Event, fromNodeEventEmitter } from 'vs/base/common/event';
import { IURLService, IURLHandler } from 'vs/platform/url/common/url';
import { ILifecycleService } from 'vs/platform/lifecycle/electron-main/lifecycleMain';
import { IWindowsMainService, ISharedProcess } from 'vs/platform/windows/electron-main/windows';
import { IHistoryMainService, IRecentlyOpened } from 'vs/platform/history/common/history';
......@@ -26,7 +26,7 @@ import { mnemonicButtonLabel } from 'vs/base/common/labels';
import { isWindows } from 'vs/base/common/platform';
import { ILogService } from 'vs/platform/log/common/log';
export class WindowsService implements IWindowsService, IDisposable {
export class WindowsService implements IWindowsService, IURLHandler, IDisposable {
_serviceBrand: any;
......@@ -45,17 +45,7 @@ export class WindowsService implements IWindowsService, IDisposable {
@IHistoryMainService private historyService: IHistoryMainService,
@ILogService private logService: ILogService
) {
// Catch file URLs
chain(urlService.onOpenURL)
.filter(uri => uri.authority === Schemas.file && !!uri.path)
.map(uri => URI.file(uri.fsPath))
.on(this.openFileForURI, this, this.disposables);
// Catch extension URLs when there are no windows open
chain(urlService.onOpenURL)
.filter(uri => /^extension/.test(uri.path))
.filter(() => this.windowsMainService.getWindowCount() === 0)
.on(this.openExtensionForURI, this, this.disposables);
urlService.registerHandler(this);
}
pickFileFolderAndOpen(options: INativeOpenDialogOptions): TPromise<void> {
......@@ -496,27 +486,21 @@ export class WindowsService implements IWindowsService, IDisposable {
return TPromise.as(null);
}
private openFileForURI(uri: URI): TPromise<void> {
const cli = assign(Object.create(null), this.environmentService.args, { goto: true });
const pathsToOpen = [uri.fsPath];
async handleURL(uri: URI): TPromise<boolean> {
// Catch file URLs
if (uri.authority === Schemas.file && !!uri.path) {
return this.openFileForURI(URI.file(uri.fsPath));
}
this.windowsMainService.open({ context: OpenContext.API, cli, pathsToOpen });
return TPromise.as(null);
return false;
}
/**
* This should only fire whenever an extension URL is open
* and there are no windows to handle it.
*/
private async openExtensionForURI(uri: URI): TPromise<void> {
const cli = assign(Object.create(null), this.environmentService.args);
const window = await this.windowsMainService.open({ context: OpenContext.API, cli })[0];
if (!window) {
return;
}
private async openFileForURI(uri: URI): TPromise<boolean> {
const cli = assign(Object.create(null), this.environmentService.args, { goto: true });
const pathsToOpen = [uri.fsPath];
window.win.show();
this.windowsMainService.open({ context: OpenContext.API, cli, pathsToOpen });
return true;
}
dispose(): void {
......
......@@ -36,17 +36,18 @@ import { Client as ElectronIPCClient } from 'vs/base/parts/ipc/electron-browser/
import { webFrame } from 'electron';
import { UpdateChannelClient } from 'vs/platform/update/common/updateIpc';
import { IUpdateService } from 'vs/platform/update/common/update';
import { URLChannelClient } from 'vs/platform/url/common/urlIpc';
import { URLHandlerChannel, URLServiceChannelClient } from 'vs/platform/url/common/urlIpc';
import { IURLService } from 'vs/platform/url/common/url';
import { WorkspacesChannelClient } from 'vs/platform/workspaces/common/workspacesIpc';
import { IWorkspacesService } from 'vs/platform/workspaces/common/workspaces';
import { createSpdLogService } from 'vs/platform/log/node/spdlogService';
import * as fs from 'fs';
import { ConsoleLogService, MultiplexLogService, ILogService } from 'vs/platform/log/common/log';
import { IssueChannelClient } from 'vs/platform/issue/common/issueIpc';
import { IIssueService } from 'vs/platform/issue/common/issue';
import { LogLevelSetterChannelClient, FollowerLogService } from 'vs/platform/log/common/logIpc';
import { RelayURLService } from 'vs/platform/url/common/urlService';
gracefulFs.gracefulify(fs); // enable gracefulFs
export function startup(configuration: IWindowConfiguration): TPromise<void> {
......@@ -72,7 +73,7 @@ export function startup(configuration: IWindowConfiguration): TPromise<void> {
}
function openWorkbench(configuration: IWindowConfiguration): TPromise<void> {
const mainProcessClient = new ElectronIPCClient(String(`window${configuration.windowId}`));
const mainProcessClient = new ElectronIPCClient(`window:${configuration.windowId}`);
const mainServices = createMainProcessServices(mainProcessClient, configuration);
const environmentService = new EnvironmentService(configuration, configuration.execPath);
......@@ -216,7 +217,12 @@ function createMainProcessServices(mainProcessClient: ElectronIPCClient, configu
serviceCollection.set(IUpdateService, new SyncDescriptor(UpdateChannelClient, updateChannel));
const urlChannel = mainProcessClient.getChannel('url');
serviceCollection.set(IURLService, new SyncDescriptor(URLChannelClient, urlChannel, configuration.windowId));
const mainUrlService = new URLServiceChannelClient(urlChannel);
const urlService = new RelayURLService(mainUrlService);
serviceCollection.set(IURLService, urlService);
const urlHandlerChannel = new URLHandlerChannel(urlService);
mainProcessClient.registerChannel('urlHandler', urlHandlerChannel);
const issueChannel = mainProcessClient.getChannel('issue');
serviceCollection.set(IIssueService, new SyncDescriptor(IssueChannelClient, issueChannel));
......
......@@ -9,7 +9,7 @@ import * as nls from 'vs/nls';
import { readFile } from 'vs/base/node/pfs';
import * as semver from 'semver';
import * as path from 'path';
import { Event, Emitter, chain } from 'vs/base/common/event';
import { Event, Emitter } from 'vs/base/common/event';
import { index } from 'vs/base/common/arrays';
import { assign } from 'vs/base/common/objects';
import { ThrottledDelayer } from 'vs/base/common/async';
......@@ -30,7 +30,7 @@ import Severity from 'vs/base/common/severity';
import URI from 'vs/base/common/uri';
import { IExtension, IExtensionDependencies, ExtensionState, IExtensionsWorkbenchService, AutoUpdateConfigurationKey } from 'vs/workbench/parts/extensions/common/extensions';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IURLService } from 'vs/platform/url/common/url';
import { IURLService, IURLHandler } from 'vs/platform/url/common/url';
import { ExtensionsInput } from 'vs/workbench/parts/extensions/common/extensionsInput';
import product from 'vs/platform/node/product';
import { ILogService } from 'vs/platform/log/common/log';
......@@ -345,7 +345,7 @@ function toTelemetryEventName(operation: Operation) {
return '';
}
export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService {
export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, IURLHandler {
private static readonly SyncPeriod = 1000 * 60 * 60 * 12; // 12 hours
......@@ -389,10 +389,7 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService {
this.syncDelayer = new ThrottledDelayer<void>(ExtensionsWorkbenchService.SyncPeriod);
this.autoUpdateDelayer = new ThrottledDelayer<void>(1000);
chain(urlService.onOpenURL)
.filter(uri => /^extension/.test(uri.path))
.on(this.onOpenExtensionUrl, this, this.disposables);
urlService.registerHandler(this);
this.configurationService.onDidChangeConfiguration(e => {
if (e.affectsConfiguration(AutoUpdateConfigurationKey)) {
......@@ -970,6 +967,15 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService {
this.notificationService.error(err);
}
async handleURL(uri: URI): TPromise<boolean> {
if (!/^extension/.test(uri.path)) {
return false;
}
this.onOpenExtensionUrl(uri);
return true;
}
private onOpenExtensionUrl(uri: URI): void {
const match = /^extension\/([^/]+)$/.exec(uri.path);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册