Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement previously stubbed window#registerURIhandler (#13169) #13306

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

rschnekenbu
Copy link
Contributor

What it does

This PR implements the stubbed API window#registerUriHandler(). This was not marked properly as '@stubbed', but the implementation returned nothing.
A detailed explanation on how this is supposed to be used in extensions is located here: https://github.com/microsoft/vscode/blob/1.85.1/src/vscode-dts/vscode.d.ts#L10146.

Fixes #13169

Contributed on behalf of STMicroelectronics

How to test

  1. install the following extension on theia browser example, main branch:
  1. trigger the "Open External: Open external URI", then select "uri handler" menu. Nothing will happened
    13169-before-patch

  2. Update to this PR

  3. The same sequence of actions will display a notification, as implemented by the uri handler provided by the test extension.
    13169-after-patch

Follow-ups

no follow-up

Review checklist

Reminder for reviewers

fixes eclipse-theia#13169

contributed on behalf of STMicroelectronics

Signed-off-by: Remi Schnekenburger <rschnekenburger@eclipsesource.com>
@rschnekenbu
Copy link
Contributor Author

@tsmaeder, would you have time to review this one?

@tsmaeder tsmaeder self-requested a review January 24, 2024 10:09
@JonasHelming
Copy link
Contributor

@tsmaeder This would be good to have for the release

@rschnekenbu rschnekenbu changed the title Implement stubbed window#registerURIhandler (#13169) Implement previously stubbed window#registerURIhandler (#13169) Jan 24, 2024
}

@injectable()
export class ExtensionOpenHandler implements OpenHandler {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmh...this is an extension mechanism to the OpenerService, which itself already has an extension mechanism. IMO, it would make more sense to make OpenerService properly extensible.


readonly id = 'extensionsURIHandlers';

private providers = new Map<string, UriHandler>();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it stores handlers, it should be called handlers ideally handlersByAuthority.

if (provider) {
return provider.handleUri(uri);
}
return Promise.reject(`Impossible to handle ${uri}.`);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should reject with an error. This should not happen if canHandle returns true.

const authority = uri.authority;
const provider = this.providers.get(authority);
if (provider) {
return provider.handleUri(uri);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The documentation of OpenHandler requires an opened widget or undefined as the return value, not a boolean.

}

public registerHandler(extensionId: string, handler: UriHandler): void {
this.providers.set(extensionId, handler);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can't talk about "extensionId" in the core package: plugin support might not be included in any particular Theia application. This handling needs to go into the UriMain service.


}

class ExtensionUriHandler implements UriHandler {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We must not talk about "Extensions" when we mean plugins. Extensions are NPM modules that extend Theia at compile time. This is confusing enough without mixing the terms in our source code.

const handler = this.handlers.get(handle);

if (!handler) {
return Promise.resolve(undefined);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can remove the undefined.

try {
handler.handleUri(URI.revive(uri));
} catch (err) {
console.log(`error while handling external uri: ${uri}`);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not let the error propagate?

@tsmaeder
Copy link
Contributor

@rschnekenbu what I don't understand is where we hook up theia to be the system-wide URI handler for some uri's. Is this already hooked up somewhere?

}

@injectable()
export class DefaultPluginUriHandlerService implements PluginUriHandlerService {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not clear to me why we need this class. Can't this code just live in the UriMainImpl class?

@jfaltermeier
Copy link
Contributor

I would like to build the release soon, what is the status here?

@rschnekenbu
Copy link
Contributor Author

The PR is under review again, not sure when it will be accepted.

Copy link
Contributor

@tsmaeder tsmaeder left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still a bunch of "extensions" left.

this.handlers.clear();
}

async $registerUriHandler(handle: number, extensionId: string, extensionDisplayName: string): Promise<void> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"plugin"! It's probably just easiest to do a "find" on this PR page to find all occurrences of "extension".

}

async open(uri: URI, options?: OpenerOptions | undefined): Promise<undefined> {
if (this.canHandle(uri) === 0) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this check is necessary: if we do not return an O.K.-value from canHandle(), this method should never be called.

if (this.openerService.addHandler?.(extensionUriHandler)) {
this.handlers.set(handle, extensionUriHandler);
}
return Promise.resolve();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need this?

if (this.canHandle(uri) === 0) {
throw new Error(`Extension ${this.pluginName} is not supposed to handle this URI: ${uri}`);
}
await Promise.resolve(this.proxy.$handleExternalUri(this.handle, uri.toComponents()));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can just write await this.proxy.handle.... as the last line. No need to return a promise from an async method.

export class UriExtImpl implements UriExt {

private static handle = 0;
private handles = new Set<string>();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This set does not store handles, it stores the ids of the extensions that have already registered a handler. registeredExtensions maybe?


$handleExternalUri(handle: number, uri: UriComponents): Promise<void> {
const handler = this.handlers.get(handle);
if (!handler) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this throw an error? It sounds like this should never happen. In general, it's not a good idea to fail silently.

return Promise.resolve();
}
handler.handleUri(URI.revive(uri));
return Promise.resolve();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line is not necessary.

@tsmaeder
Copy link
Contributor

@rschnekenbu when I open the "run" dialog from the Windows start menu and type "theia://ryanleonhardt.open-external" Windows tells me there is not handler in the system for the "theia" scheme. Isn't this what the "sytem-wide" url handler (https://code.visualstudio.com/api/references/vscode-api#window) is supposed to do? When I open a "vscode://foo.bar" uri, VS Code handles it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: Waiting on author
Development

Successfully merging this pull request may close these issues.

registerUriHandler is not implemented
4 participants