Continue work on opener service

- Add error notification if opener throws an exception
- Add public facing id to openers. This is used in settings
- Add intellisense for the opener id setting
This commit is contained in:
Matt Bierner
2021-01-12 23:12:10 -08:00
parent d6936dd524
commit 47aa3ad09a
6 changed files with 106 additions and 62 deletions

View File

@@ -4,11 +4,15 @@
*--------------------------------------------------------------------------------------------*/
import { CancellationToken } from 'vs/base/common/cancellation';
import { isPromiseCanceledError } from 'vs/base/common/errors';
import { Disposable } from 'vs/base/common/lifecycle';
import { Schemas } from 'vs/base/common/network';
import { URI } from 'vs/base/common/uri';
import { localize } from 'vs/nls';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { ExtHostContext, ExtHostUriOpenersShape, IExtHostContext, MainContext, MainThreadUriOpenersShape } from 'vs/workbench/api/common/extHost.protocol';
import { externalUriOpenerIdSchemaAddition } from 'vs/workbench/contrib/externalUriOpener/common/configuration';
import { ExternalOpenerEntry, IExternalOpenerProvider, IExternalUriOpenerService } from 'vs/workbench/contrib/externalUriOpener/common/externalUriOpenerService';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { extHostNamedCustomer } from '../common/extHostCustomers';
@@ -23,12 +27,13 @@ interface RegisteredOpenerMetadata {
export class MainThreadUriOpeners extends Disposable implements MainThreadUriOpenersShape, IExternalOpenerProvider {
private readonly proxy: ExtHostUriOpenersShape;
private readonly registeredOpeners = new Map<number, RegisteredOpenerMetadata>();
private readonly _registeredOpeners = new Map<string, RegisteredOpenerMetadata>();
constructor(
context: IExtHostContext,
@IExternalUriOpenerService private readonly externalUriOpenerService: IExternalUriOpenerService,
@IExtensionService private readonly extensionService: IExtensionService,
@INotificationService private readonly notificationService: INotificationService,
) {
super();
this.proxy = context.getProxy(ExtHostContext.ExtHostUriOpeners);
@@ -47,48 +52,59 @@ export class MainThreadUriOpeners extends Disposable implements MainThreadUriOpe
await this.extensionService.activateByEvent(`onUriOpen:${targetUri.scheme}`);
// If there are no handlers there is no point in making a round trip
const hasHandler = Array.from(this.registeredOpeners.values()).some(x => x.schemes.has(targetUri.scheme));
const hasHandler = Array.from(this._registeredOpeners.values()).some(x => x.schemes.has(targetUri.scheme));
if (!hasHandler) {
return [];
}
const openerHandles = await this.proxy.$getOpenersForUri(targetUri, CancellationToken.None);
return openerHandles.map(handle => this.openerForCommand(handle, targetUri));
const openerIds = await this.proxy.$getOpenersForUri(targetUri, CancellationToken.None);
return openerIds.map(id => this.createOpener(id, targetUri));
}
private openerForCommand(openerHandle: number, sourceUri: URI): ExternalOpenerEntry {
const metadata = this.registeredOpeners.get(openerHandle)!;
private createOpener(openerId: string, sourceUri: URI): ExternalOpenerEntry {
const metadata = this._registeredOpeners.get(openerId)!;
return {
id: metadata.extensionId.value,
id: openerId,
label: metadata.label,
openExternal: async (href) => {
const resolveUri = URI.parse(href);
await this.proxy.$openUri(openerHandle, { resolveUri, sourceUri }, CancellationToken.None);
try {
await this.proxy.$openUri(openerId, { resolveUri, sourceUri }, CancellationToken.None);
} catch (e) {
if (!isPromiseCanceledError(e)) {
this.notificationService.error(localize('openerFailedMessage', "Could not open uri: {0}", e.toString()));
}
}
return true;
},
};
}
async $registerUriOpener(
handle: number,
id: string,
schemes: readonly string[],
extensionId: ExtensionIdentifier,
label: string,
): Promise<void> {
this.registeredOpeners.set(handle, {
if (this._registeredOpeners.has(id)) {
throw new Error(`Opener with id already registered: '${id}'`);
}
this._registeredOpeners.set(id, {
schemes: new Set(schemes),
label,
extensionId,
});
externalUriOpenerIdSchemaAddition.enum?.push(id);
}
async $unregisterUriOpener(handle: number): Promise<void> {
this.registeredOpeners.delete(handle);
async $unregisterUriOpener(id: string): Promise<void> {
this._registeredOpeners.delete(id);
}
dispose(): void {
super.dispose();
this.registeredOpeners.clear();
this._registeredOpeners.clear();
}
}