diff --git a/src/vs/editor/browser/services/openerService.ts b/src/vs/editor/browser/services/openerService.ts index 4ecc73effdf..1c4f38ef491 100644 --- a/src/vs/editor/browser/services/openerService.ts +++ b/src/vs/editor/browser/services/openerService.ts @@ -10,13 +10,17 @@ import * as resources from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/commands'; -import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { IOpenerService, IOpener } from 'vs/platform/opener/common/opener'; import { equalsIgnoreCase } from 'vs/base/common/strings'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { LinkedList } from 'vs/base/common/linkedList'; export class OpenerService implements IOpenerService { _serviceBrand: any; + private readonly _opener = new LinkedList(); + constructor( @ICodeEditorService private readonly _editorService: ICodeEditorService, @ICommandService private readonly _commandService: ICommandService, @@ -24,14 +28,30 @@ export class OpenerService implements IOpenerService { // } - open(resource: URI, options?: { openToSide?: boolean }): Promise { + registerOpener(opener: IOpener): IDisposable { + const remove = this._opener.push(opener); + return { dispose: remove }; + } - const { scheme, path, query, fragment } = resource; - - if (!scheme) { - // no scheme ?!? + async open(resource: URI, options?: { openToSide?: boolean }): Promise { + // no scheme ?!? + if (!resource.scheme) { return Promise.resolve(false); } + // check with contributed openers + for (const opener of this._opener.toArray()) { + const handled = await opener.open(resource, options); + if (handled) { + return true; + } + } + // use default openers + return this._doOpen(resource, options); + } + + private _doOpen(resource: URI, options?: { openToSide?: boolean }): Promise { + + const { scheme, path, query, fragment } = resource; if (equalsIgnoreCase(scheme, Schemas.http) || equalsIgnoreCase(scheme, Schemas.https) || equalsIgnoreCase(scheme, Schemas.mailto)) { // open http or default mail application diff --git a/src/vs/platform/opener/common/opener.ts b/src/vs/platform/opener/common/opener.ts index 043c4e392e8..3c999d51b30 100644 --- a/src/vs/platform/opener/common/opener.ts +++ b/src/vs/platform/opener/common/opener.ts @@ -5,13 +5,21 @@ import { URI } from 'vs/base/common/uri'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { IDisposable } from 'vs/base/common/lifecycle'; export const IOpenerService = createDecorator('openerService'); + +export interface IOpener { + open(resource: URI, options?: { openToSide?: boolean }): Promise; +} + export interface IOpenerService { _serviceBrand: any; + registerOpener(opener: IOpener): IDisposable; + /** * Opens a resource, like a webadress, a document uri, or executes command. * @@ -23,5 +31,6 @@ export interface IOpenerService { export const NullOpenerService: IOpenerService = Object.freeze({ _serviceBrand: undefined, + registerOpener() { return { dispose() { } }; }, open() { return Promise.resolve(false); } }); diff --git a/src/vs/platform/url/electron-browser/urlService.ts b/src/vs/platform/url/electron-browser/urlService.ts index 814ad021e59..11853cb0eeb 100644 --- a/src/vs/platform/url/electron-browser/urlService.ts +++ b/src/vs/platform/url/electron-browser/urlService.ts @@ -8,16 +8,21 @@ import { URI } from 'vs/base/common/uri'; import { IMainProcessService } from 'vs/platform/ipc/electron-browser/mainProcessService'; import { URLServiceChannelClient, URLHandlerChannel } from 'vs/platform/url/node/urlIpc'; import { URLService } from 'vs/platform/url/common/urlService'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; export class RelayURLService extends URLService implements IURLHandler { private urlService: IURLService; - constructor(@IMainProcessService mainProcessService: IMainProcessService) { + constructor( + @IMainProcessService mainProcessService: IMainProcessService, + @IOpenerService openerService: IOpenerService + ) { super(); this.urlService = new URLServiceChannelClient(mainProcessService.getChannel('url')); mainProcessService.registerChannel('urlHandler', new URLHandlerChannel(this)); + openerService.registerOpener(this); } open(uri: URI): Promise { diff --git a/src/vs/workbench/api/electron-browser/mainThreadEditors.ts b/src/vs/workbench/api/electron-browser/mainThreadEditors.ts index a3e64bc9600..56c22bed761 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadEditors.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadEditors.ts @@ -24,8 +24,6 @@ import { ExtHostContext, ExtHostEditorsShape, IApplyEditsOptions, IExtHostContex import { EditorViewColumn, editorGroupToViewColumn, viewColumnToEditorGroup } from 'vs/workbench/api/common/shared/editor'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; -import { IURLService } from 'vs/platform/url/common/url'; -import product from 'vs/platform/product/node/product'; export class MainThreadTextEditors implements MainThreadTextEditorsShape { @@ -270,7 +268,6 @@ CommandsRegistry.registerCommand('_workbench.open', function (accessor: Services const editorService = accessor.get(IEditorService); const editorGroupService = accessor.get(IEditorGroupsService); const openerService = accessor.get(IOpenerService); - const urlService = accessor.get(IURLService); const [resource, options, position, label] = args; @@ -282,12 +279,8 @@ CommandsRegistry.registerCommand('_workbench.open', function (accessor: Services if (resource && resource.scheme === 'command') { // do not allow to execute commands from here return Promise.resolve(undefined); - } - if (resource && (resource.scheme === product.urlProtocol || /^vscode/.test(resource.scheme))) { - return urlService.open(resource).then(_ => undefined); } - // finally, delegate to opener service return openerService.open(resource).then(_ => undefined); });